From a8814983a6dad2142e616e6646a99b2ffd5134c6 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Wed, 6 Jan 2021 17:01:32 -0300 Subject: [PATCH 001/221] changes --- .../ProductCard/FUTURE_ProductCard.tsx | 57 +++++++++++++++++++ framework/types.d.ts | 25 ++++++++ 2 files changed, 82 insertions(+) create mode 100644 components/product/ProductCard/FUTURE_ProductCard.tsx create mode 100644 framework/types.d.ts diff --git a/components/product/ProductCard/FUTURE_ProductCard.tsx b/components/product/ProductCard/FUTURE_ProductCard.tsx new file mode 100644 index 000000000..0b5c3aece --- /dev/null +++ b/components/product/ProductCard/FUTURE_ProductCard.tsx @@ -0,0 +1,57 @@ +import { FC } from 'react' +import cn from 'classnames' +import Image from 'next/image' +import s from './ProductCard.module.css' +// import WishlistButton from '@components/wishlist/WishlistButton' + +interface Props { + className?: string + product: Product + variant?: 'slim' | 'simple' +} + +const ProductCard: FC<Props> = ({ className, product, variant }) => { + const defaultImageProps = { + layout: 'responsive', + } + + return ( + <a className={cn(s.root, { [s.simple]: variant === 'simple' }, className)}> + {variant === 'slim' ? ( + <div className="relative overflow-hidden box-border"> + <div className="absolute inset-0 flex items-center justify-end mr-8 z-20"> + <span className="bg-black text-white inline-block p-3 font-bold text-xl break-words"> + {product.name} + </span> + {/* Image */} + </div> + </div> + ) : ( + <> + <div className={s.squareBg} /> + <div className="flex flex-row justify-between box-border w-full z-20 absolute"> + <div className="absolute top-0 left-0 pr-16 max-w-full"> + <h3 className={s.productTitle}> + <span>{product.name}</span> + </h3> + <span className={s.productPrice}>{product.price}</span> + </div> + </div> + <div className={s.imageContainer}> + {/* Image */} + + <Image + quality="85" + src={product.images[0].src} + alt={product.name} + className={s.productImage} + {...defaultImageProps} + /> + </div> + </> + )} + </a> + ) +} + +export default ProductCard diff --git a/framework/types.d.ts b/framework/types.d.ts new file mode 100644 index 000000000..40d7390a7 --- /dev/null +++ b/framework/types.d.ts @@ -0,0 +1,25 @@ +interface Product { + id: string | number + name: string + description: string + images: Images[] + slug: string + price: string + variantId: string +} + +interface Images { + src: string + alt?: string +} + +interface NextImage { + src: string + width: number | string + height: number | string + layout?: 'fixed' | 'intrinsic' | 'responsive' | undefined + priority?: boolean + loading?: 'eager' | 'lazy' + sizes?: string + alt?: string +} From 36396e23c10110c29d125b1daae555b04ebefffd Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Wed, 6 Jan 2021 21:24:09 -0300 Subject: [PATCH 002/221] Progress --- .../ProductCard/FUTURE_ProductCard.tsx | 19 +-- framework/types.d.ts | 20 +-- pages/index copy.tsx | 152 ++++++++++++++++++ pages/index.tsx | 32 ++-- 4 files changed, 188 insertions(+), 35 deletions(-) create mode 100644 pages/index copy.tsx diff --git a/components/product/ProductCard/FUTURE_ProductCard.tsx b/components/product/ProductCard/FUTURE_ProductCard.tsx index 0b5c3aece..7ef41b0be 100644 --- a/components/product/ProductCard/FUTURE_ProductCard.tsx +++ b/components/product/ProductCard/FUTURE_ProductCard.tsx @@ -1,20 +1,18 @@ import { FC } from 'react' import cn from 'classnames' -import Image from 'next/image' +import Image, { ImageProps } from 'next/image' import s from './ProductCard.module.css' +// Restore Wishlist func // import WishlistButton from '@components/wishlist/WishlistButton' interface Props { className?: string product: Product variant?: 'slim' | 'simple' + imgProps?: Omit<ImageProps, 'src'> } -const ProductCard: FC<Props> = ({ className, product, variant }) => { - const defaultImageProps = { - layout: 'responsive', - } - +const ProductCard: FC<Props> = ({ className, product, variant, imgProps }) => { return ( <a className={cn(s.root, { [s.simple]: variant === 'simple' }, className)}> {variant === 'slim' ? ( @@ -38,14 +36,13 @@ const ProductCard: FC<Props> = ({ className, product, variant }) => { </div> </div> <div className={s.imageContainer}> - {/* Image */} - <Image - quality="85" - src={product.images[0].src} alt={product.name} className={s.productImage} - {...defaultImageProps} + src={product.images[0].src} + height={540} + width={540} + {...imgProps} /> </div> </> diff --git a/framework/types.d.ts b/framework/types.d.ts index 40d7390a7..a05f03a69 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -13,13 +13,13 @@ interface Images { alt?: string } -interface NextImage { - src: string - width: number | string - height: number | string - layout?: 'fixed' | 'intrinsic' | 'responsive' | undefined - priority?: boolean - loading?: 'eager' | 'lazy' - sizes?: string - alt?: string -} +// interface NextImageProps { +// src: string +// width: number | string +// height: number | string +// layout?: 'fixed' | 'intrinsic' | 'responsive' | undefined +// priority?: boolean +// loading?: 'eager' | 'lazy' +// sizes?: string +// alt?: string +// } diff --git a/pages/index copy.tsx b/pages/index copy.tsx new file mode 100644 index 000000000..ef1c0a96e --- /dev/null +++ b/pages/index copy.tsx @@ -0,0 +1,152 @@ +import rangeMap from '@lib/range-map' +import { Layout } from '@components/common' +import { ProductCard } from '@components/product' +import { Grid, Marquee, Hero } from '@components/ui' +import HomeAllProductsGrid from '@components/common/HomeAllProductsGrid' +import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next' + +import { getConfig } from '@framework/api' +import getAllProducts from '@framework/api/operations/get-all-products' +import getSiteInfo from '@framework/api/operations/get-site-info' +import getAllPages from '@framework/api/operations/get-all-pages' + +export async function getStaticProps({ + preview, + locale, +}: GetStaticPropsContext) { + const config = getConfig({ locale }) + + // Get Featured Products + const { products: featuredProducts } = await getAllProducts({ + variables: { field: 'featuredProducts', first: 6 }, + config, + preview, + }) + + // Get Best Selling Products + const { products: bestSellingProducts } = await getAllProducts({ + variables: { field: 'bestSellingProducts', first: 6 }, + config, + preview, + }) + + // Get Best Newest Products + const { products: newestProducts } = await getAllProducts({ + variables: { field: 'newestProducts', first: 12 }, + config, + preview, + }) + + const { categories, brands } = await getSiteInfo({ config, preview }) + const { pages } = await getAllPages({ config, preview }) + + // These are the products that are going to be displayed in the landing. + // We prefer to do the computation at buildtime/servertime + const { featured, bestSelling } = (() => { + // Create a copy of products that we can mutate + const products = [...newestProducts] + // If the lists of featured and best selling products don't have enough + // products, then fill them with products from the products list, this + // is useful for new commerce sites that don't have a lot of products + return { + featured: rangeMap(6, (i) => featuredProducts[i] ?? products.shift()) + .filter(nonNullable) + .sort((a, b) => a.node.prices.price.value - b.node.prices.price.value) + .reverse(), + bestSelling: rangeMap( + 6, + (i) => bestSellingProducts[i] ?? products.shift() + ).filter(nonNullable), + } + })() + + return { + props: { + featured, + bestSelling, + newestProducts, + categories, + brands, + pages, + }, + revalidate: 14400, + } +} + +const nonNullable = (v: any) => v + +export default function Home({ + featured, + bestSelling, + brands, + categories, + newestProducts, +}: InferGetStaticPropsType<typeof getStaticProps>) { + return ( + <div> + <Grid> + {featured.slice(0, 3).map(({ node }, i) => ( + <ProductCard + key={node.path} + product={node} + imgWidth={i === 0 ? 1080 : 540} + imgHeight={i === 0 ? 1080 : 540} + imgPriority + imgLoading="eager" + /> + ))} + </Grid> + <Marquee variant="secondary"> + {bestSelling.slice(3, 6).map(({ node }) => ( + <ProductCard + key={node.path} + product={node} + variant="slim" + imgWidth={320} + imgHeight={320} + imgLayout="fixed" + /> + ))} + </Marquee> + <Hero + headline="Release Details: The Yeezy BOOST 350 V2 ‘Natural'" + description=" + The Yeezy BOOST 350 V2 lineup continues to grow. We recently had the + ‘Carbon’ iteration, and now release details have been locked in for + this ‘Natural’ joint. Revealed by Yeezy Mafia earlier this year, the + shoe was originally called ‘Abez’, which translated to ‘Tin’ in + Hebrew. It’s now undergone a name change, and will be referred to as + ‘Natural’." + /> + <Grid layout="B"> + {featured.slice(3, 6).map(({ node }, i) => ( + <ProductCard + key={node.path} + product={node} + imgWidth={i === 1 ? 1080 : 540} + imgHeight={i === 1 ? 1080 : 540} + /> + ))} + </Grid> + <Marquee> + {bestSelling.slice(0, 3).map(({ node }) => ( + <ProductCard + key={node.path} + product={node} + variant="slim" + imgWidth={320} + imgHeight={320} + imgLayout="fixed" + /> + ))} + </Marquee> + <HomeAllProductsGrid + categories={categories} + brands={brands} + newestProducts={newestProducts} + /> + </div> + ) +} + +Home.Layout = Layout diff --git a/pages/index.tsx b/pages/index.tsx index ef1c0a96e..3266bc053 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,6 +1,6 @@ import rangeMap from '@lib/range-map' import { Layout } from '@components/common' -import { ProductCard } from '@components/product' +import ProductCard from '@components/product/ProductCard/FUTURE_ProductCard' import { Grid, Marquee, Hero } from '@components/ui' import HomeAllProductsGrid from '@components/common/HomeAllProductsGrid' import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next' @@ -89,10 +89,10 @@ export default function Home({ <ProductCard key={node.path} product={node} - imgWidth={i === 0 ? 1080 : 540} - imgHeight={i === 0 ? 1080 : 540} - imgPriority - imgLoading="eager" + imgProps={{ + width: i === 0 ? 1080 : 540, + height: i === 0 ? 1080 : 540, + }} /> ))} </Grid> @@ -102,9 +102,10 @@ export default function Home({ key={node.path} product={node} variant="slim" - imgWidth={320} - imgHeight={320} - imgLayout="fixed" + imgProps={{ + width: 320, + height: 320, + }} /> ))} </Marquee> @@ -123,8 +124,10 @@ export default function Home({ <ProductCard key={node.path} product={node} - imgWidth={i === 1 ? 1080 : 540} - imgHeight={i === 1 ? 1080 : 540} + imgProps={{ + width: i === 1 ? 1080 : 540, + height: i === 1 ? 1080 : 540, + }} /> ))} </Grid> @@ -134,16 +137,17 @@ export default function Home({ key={node.path} product={node} variant="slim" - imgWidth={320} - imgHeight={320} - imgLayout="fixed" + imgProps={{ + width: 320, + height: 320, + }} /> ))} </Marquee> <HomeAllProductsGrid + newestProducts={newestProducts} categories={categories} brands={brands} - newestProducts={newestProducts} /> </div> ) From f7956f8d01d11bef183eb3a605bdac6403a679ae Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Thu, 7 Jan 2021 11:01:47 -0300 Subject: [PATCH 003/221] Normalized Products output --- pages/index.tsx | 80 +++++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 50 deletions(-) diff --git a/pages/index.tsx b/pages/index.tsx index 3266bc053..6ebbc8d40 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -10,61 +10,45 @@ import getAllProducts from '@framework/api/operations/get-all-products' import getSiteInfo from '@framework/api/operations/get-site-info' import getAllPages from '@framework/api/operations/get-all-pages' +// Outputs from providers should already be normalized +// TODO (bc) move this to the provider +function normalize(arr: any[]) { + // Normalizes products arr response and flattens node edges + return arr.map( + ({ + node: { entityId: id, images, variants, productOptions, ...rest }, + }) => ({ + id, + images: images.edges, + variants: variants.edges, + productOptions: productOptions.edges, + ...rest, + }) + ) +} + export async function getStaticProps({ preview, locale, }: GetStaticPropsContext) { const config = getConfig({ locale }) - // Get Featured Products - const { products: featuredProducts } = await getAllProducts({ - variables: { field: 'featuredProducts', first: 6 }, + const { products: rawProducts } = await getAllProducts({ + variables: { first: 12 }, config, preview, }) - // Get Best Selling Products - const { products: bestSellingProducts } = await getAllProducts({ - variables: { field: 'bestSellingProducts', first: 6 }, - config, - preview, - }) + const products = normalize(rawProducts) - // Get Best Newest Products - const { products: newestProducts } = await getAllProducts({ - variables: { field: 'newestProducts', first: 12 }, - config, - preview, - }) + console.log(products) const { categories, brands } = await getSiteInfo({ config, preview }) const { pages } = await getAllPages({ config, preview }) - // These are the products that are going to be displayed in the landing. - // We prefer to do the computation at buildtime/servertime - const { featured, bestSelling } = (() => { - // Create a copy of products that we can mutate - const products = [...newestProducts] - // If the lists of featured and best selling products don't have enough - // products, then fill them with products from the products list, this - // is useful for new commerce sites that don't have a lot of products - return { - featured: rangeMap(6, (i) => featuredProducts[i] ?? products.shift()) - .filter(nonNullable) - .sort((a, b) => a.node.prices.price.value - b.node.prices.price.value) - .reverse(), - bestSelling: rangeMap( - 6, - (i) => bestSellingProducts[i] ?? products.shift() - ).filter(nonNullable), - } - })() - return { props: { - featured, - bestSelling, - newestProducts, + products, categories, brands, pages, @@ -73,22 +57,18 @@ export async function getStaticProps({ } } -const nonNullable = (v: any) => v - export default function Home({ - featured, - bestSelling, + products, brands, categories, - newestProducts, }: InferGetStaticPropsType<typeof getStaticProps>) { return ( <div> <Grid> - {featured.slice(0, 3).map(({ node }, i) => ( + {products.slice(0, 3).map(({ p }, i) => ( <ProductCard - key={node.path} - product={node} + key={p.id} + product={p} imgProps={{ width: i === 0 ? 1080 : 540, height: i === 0 ? 1080 : 540, @@ -96,7 +76,7 @@ export default function Home({ /> ))} </Grid> - <Marquee variant="secondary"> + {/* <Marquee variant="secondary"> {bestSelling.slice(3, 6).map(({ node }) => ( <ProductCard key={node.path} @@ -143,12 +123,12 @@ export default function Home({ }} /> ))} - </Marquee> - <HomeAllProductsGrid + </Marquee> */} + {/* <HomeAllProductsGrid newestProducts={newestProducts} categories={categories} brands={brands} - /> + /> */} </div> ) } From 812535caffef9938051c8e0d4de85467c249404e Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Thu, 7 Jan 2021 11:19:28 -0300 Subject: [PATCH 004/221] Progress --- .../ProductCard/FUTURE_ProductCard.tsx | 19 ++++++++++------- framework/types.d.ts | 4 ++-- pages/index.tsx | 21 ++++++++++++------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/components/product/ProductCard/FUTURE_ProductCard.tsx b/components/product/ProductCard/FUTURE_ProductCard.tsx index 7ef41b0be..9ca615d90 100644 --- a/components/product/ProductCard/FUTURE_ProductCard.tsx +++ b/components/product/ProductCard/FUTURE_ProductCard.tsx @@ -13,6 +13,7 @@ interface Props { } const ProductCard: FC<Props> = ({ className, product, variant, imgProps }) => { + const firstImage = product.images[0] return ( <a className={cn(s.root, { [s.simple]: variant === 'simple' }, className)}> {variant === 'slim' ? ( @@ -36,14 +37,16 @@ const ProductCard: FC<Props> = ({ className, product, variant, imgProps }) => { </div> </div> <div className={s.imageContainer}> - <Image - alt={product.name} - className={s.productImage} - src={product.images[0].src} - height={540} - width={540} - {...imgProps} - /> + {firstImage.src && ( + <Image + alt={product.name} + className={s.productImage} + src={firstImage.src} + height={540} + width={540} + {...imgProps} + /> + )} </div> </> )} diff --git a/framework/types.d.ts b/framework/types.d.ts index a05f03a69..de5deb080 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -2,13 +2,13 @@ interface Product { id: string | number name: string description: string - images: Images[] + images: Image[] slug: string price: string variantId: string } -interface Images { +interface Image { src: string alt?: string } diff --git a/pages/index.tsx b/pages/index.tsx index 6ebbc8d40..8406b333c 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -12,6 +12,7 @@ import getAllPages from '@framework/api/operations/get-all-pages' // Outputs from providers should already be normalized // TODO (bc) move this to the provider + function normalize(arr: any[]) { // Normalizes products arr response and flattens node edges return arr.map( @@ -19,9 +20,15 @@ function normalize(arr: any[]) { node: { entityId: id, images, variants, productOptions, ...rest }, }) => ({ id, - images: images.edges, - variants: variants.edges, - productOptions: productOptions.edges, + images: images.edges.map( + ({ node: { urlOriginal, altText, ...rest } }: any) => ({ + url: urlOriginal, + alt: altText, + ...rest, + }) + ), + variants: variants.edges.map(({ node }: any) => node), + productOptions: productOptions.edges.map(({ node }: any) => node), ...rest, }) ) @@ -41,7 +48,7 @@ export async function getStaticProps({ const products = normalize(rawProducts) - console.log(products) + // console.log(products) const { categories, brands } = await getSiteInfo({ config, preview }) const { pages } = await getAllPages({ config, preview }) @@ -65,10 +72,10 @@ export default function Home({ return ( <div> <Grid> - {products.slice(0, 3).map(({ p }, i) => ( + {products.slice(0, 3).map((product, i) => ( <ProductCard - key={p.id} - product={p} + key={product.id} + product={product} imgProps={{ width: i === 0 ? 1080 : 540, height: i === 0 ? 1080 : 540, From e9dfda1e86bb1dda4050c6887b433542235d76d2 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Thu, 7 Jan 2021 11:58:00 -0300 Subject: [PATCH 005/221] Restored Index Agnostic --- .../ProductCard/FUTURE_ProductCard.tsx | 80 ++++++++++++------- components/ui/Marquee/Marquee.module.css | 11 +-- framework/types.d.ts | 29 +++---- pages/index.tsx | 52 +++++++----- 4 files changed, 101 insertions(+), 71 deletions(-) diff --git a/components/product/ProductCard/FUTURE_ProductCard.tsx b/components/product/ProductCard/FUTURE_ProductCard.tsx index 9ca615d90..604b91460 100644 --- a/components/product/ProductCard/FUTURE_ProductCard.tsx +++ b/components/product/ProductCard/FUTURE_ProductCard.tsx @@ -1,5 +1,6 @@ import { FC } from 'react' import cn from 'classnames' +import Link from 'next/link' import Image, { ImageProps } from 'next/image' import s from './ProductCard.module.css' // Restore Wishlist func @@ -13,44 +14,63 @@ interface Props { } const ProductCard: FC<Props> = ({ className, product, variant, imgProps }) => { - const firstImage = product.images[0] return ( - <a className={cn(s.root, { [s.simple]: variant === 'simple' }, className)}> - {variant === 'slim' ? ( - <div className="relative overflow-hidden box-border"> - <div className="absolute inset-0 flex items-center justify-end mr-8 z-20"> - <span className="bg-black text-white inline-block p-3 font-bold text-xl break-words"> - {product.name} - </span> - {/* Image */} - </div> - </div> - ) : ( - <> - <div className={s.squareBg} /> - <div className="flex flex-row justify-between box-border w-full z-20 absolute"> - <div className="absolute top-0 left-0 pr-16 max-w-full"> - <h3 className={s.productTitle}> - <span>{product.name}</span> - </h3> - <span className={s.productPrice}>{product.price}</span> + <Link href={`product/${product.slug}`}> + <a + className={cn(s.root, { [s.simple]: variant === 'simple' }, className)} + > + {variant === 'slim' ? ( + <div className="relative overflow-hidden box-border"> + <div className="absolute inset-0 flex items-center justify-end mr-8 z-20"> + <span className="bg-black text-white inline-block p-3 font-bold text-xl break-words"> + {product.name} + </span> </div> - </div> - <div className={s.imageContainer}> - {firstImage.src && ( + {product.images[0] && ( <Image + quality="85" alt={product.name} - className={s.productImage} - src={firstImage.src} - height={540} - width={540} + src={product.images[0].url} + height={320} + width={320} + layout="fixed" {...imgProps} /> )} </div> - </> - )} - </a> + ) : ( + <> + <div className={s.squareBg} /> + <div className="flex flex-row justify-between box-border w-full z-20 absolute"> + <div className="absolute top-0 left-0 pr-16 max-w-full"> + <h3 className={s.productTitle}> + <span>{product.name}</span> + </h3> + <span className={s.productPrice}> + {product.prices[0].value} + + {product.prices[0].currencyCode} + </span> + </div> + </div> + <div className={s.imageContainer}> + {product.images[0] && ( + <Image + alt={product.name} + className={s.productImage} + src={product.images[0].url} + height={540} + width={540} + quality="85" + layout="responsive" + {...imgProps} + /> + )} + </div> + </> + )} + </a> + </Link> ) } diff --git a/components/ui/Marquee/Marquee.module.css b/components/ui/Marquee/Marquee.module.css index 32601a54e..1fabc2ca8 100644 --- a/components/ui/Marquee/Marquee.module.css +++ b/components/ui/Marquee/Marquee.module.css @@ -1,15 +1,16 @@ .root { - @apply w-full; + @apply w-full relative; + height: 320px; min-width: 100%; } .container { @apply flex flex-row items-center; +} - & > * { - @apply flex-1 px-16 py-4; - width: 430px; - } +.container > * { + @apply relative flex-1 px-16 py-4 h-full; + min-height: 320px; } .primary { diff --git a/framework/types.d.ts b/framework/types.d.ts index de5deb080..4098e5c7e 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -1,25 +1,20 @@ +interface ProductImage { + url: string + alt?: string +} + interface Product { id: string | number name: string description: string - images: Image[] + images: ProductImage[] + prices: ProductPrice[] slug: string - price: string - variantId: string + path?: string } -interface Image { - src: string - alt?: string +interface ProductPrice { + value: number | string + currencyCode: 'USD' | 'ARS' + type?: 'price' | 'retail' | 'sale' | string } - -// interface NextImageProps { -// src: string -// width: number | string -// height: number | string -// layout?: 'fixed' | 'intrinsic' | 'responsive' | undefined -// priority?: boolean -// loading?: 'eager' | 'lazy' -// sizes?: string -// alt?: string -// } diff --git a/pages/index.tsx b/pages/index.tsx index 8406b333c..186da09f6 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -13,13 +13,23 @@ import getAllPages from '@framework/api/operations/get-all-pages' // Outputs from providers should already be normalized // TODO (bc) move this to the provider -function normalize(arr: any[]) { +function productsNormalizer(arr: any[]) { // Normalizes products arr response and flattens node edges return arr.map( ({ - node: { entityId: id, images, variants, productOptions, ...rest }, + node: { + entityId: id, + images, + variants, + productOptions, + prices, + path, + ...rest + }, }) => ({ id, + path, + slug: path.slice(1, -1), images: images.edges.map( ({ node: { urlOriginal, altText, ...rest } }: any) => ({ url: urlOriginal, @@ -29,6 +39,12 @@ function normalize(arr: any[]) { ), variants: variants.edges.map(({ node }: any) => node), productOptions: productOptions.edges.map(({ node }: any) => node), + prices: [ + { + value: prices.price.value, + currencyCode: prices.price.currencyCode, + }, + ], ...rest, }) ) @@ -46,10 +62,8 @@ export async function getStaticProps({ preview, }) - const products = normalize(rawProducts) - - // console.log(products) - + // Remove normalizer and send to framework provider. + const products = productsNormalizer(rawProducts) const { categories, brands } = await getSiteInfo({ config, preview }) const { pages } = await getAllPages({ config, preview }) @@ -83,11 +97,11 @@ export default function Home({ /> ))} </Grid> - {/* <Marquee variant="secondary"> - {bestSelling.slice(3, 6).map(({ node }) => ( + <Marquee variant="secondary"> + {products.slice(0, 3).map((product, i) => ( <ProductCard - key={node.path} - product={node} + key={product.id} + product={product} variant="slim" imgProps={{ width: 320, @@ -107,22 +121,22 @@ export default function Home({ ‘Natural’." /> <Grid layout="B"> - {featured.slice(3, 6).map(({ node }, i) => ( + {products.slice(0, 3).map((product, i) => ( <ProductCard - key={node.path} - product={node} + key={product.id} + product={product} imgProps={{ - width: i === 1 ? 1080 : 540, - height: i === 1 ? 1080 : 540, + width: i === 0 ? 1080 : 540, + height: i === 0 ? 1080 : 540, }} /> ))} </Grid> <Marquee> - {bestSelling.slice(0, 3).map(({ node }) => ( + {products.slice(0, 3).map((product, i) => ( <ProductCard - key={node.path} - product={node} + key={product.id} + product={product} variant="slim" imgProps={{ width: 320, @@ -130,7 +144,7 @@ export default function Home({ }} /> ))} - </Marquee> */} + </Marquee> {/* <HomeAllProductsGrid newestProducts={newestProducts} categories={categories} From c780852fbb70a1e95d4ed85dfe12617da703642e Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Thu, 7 Jan 2021 16:28:50 -0300 Subject: [PATCH 006/221] Progress --- README.md | 22 +++++++++++++--- .../ProductCard/FUTURE_ProductCard.tsx | 10 ++++--- .../WishlistButton/WishlistButton.tsx | 26 +++++++++---------- framework/bigcommerce/api/wishlist/index.ts | 4 +-- framework/bigcommerce/wishlist/index.ts | 4 +++ framework/types.d.ts | 14 ++++++---- 6 files changed, 53 insertions(+), 27 deletions(-) create mode 100644 framework/bigcommerce/wishlist/index.ts diff --git a/README.md b/README.md index 935c433cf..de1452b75 100644 --- a/README.md +++ b/README.md @@ -84,10 +84,10 @@ Our commitment to Open Source can be found [here](https://vercel.com/oss). ## Goals -* **Next.js Commerce** should have a completely data **agnostic** UI -* **Aware of schema**: should ship with the right data schemas and types. -* All providers should return the right data types and schemas to blend correctly with Next.js Commerce. -* `@framework` will be the alias utilized in commerce and it will map to the ecommerce provider of preference- e.g BigCommerce, Shopify, Swell. All providers should expose the same standardized functions. _Note that the same applies for recipes using a CMS + an ecommerce provider._ +- **Next.js Commerce** should have a completely data **agnostic** UI +- **Aware of schema**: should ship with the right data schemas and types. +- All providers should return the right data types and schemas to blend correctly with Next.js Commerce. +- `@framework` will be the alias utilized in commerce and it will map to the ecommerce provider of preference- e.g BigCommerce, Shopify, Swell. All providers should expose the same standardized functions. _Note that the same applies for recipes using a CMS + an ecommerce provider._ There is a `framework` folder in the root folder that will contain multiple ecommerce providers. @@ -95,5 +95,19 @@ Additionally, we need to ensure feature parity (not all providers have e.g. wish People actively working on this project: @okbel & @lfades. +## Framework +Framework is where the data comes from. Contains mostly hooks and functions. +## Structure + +- ## product +- wishlist + - useWishlist + - addWishlistItem + - removeWishlistItem +- auth + +- config.json + +## Wishlist diff --git a/components/product/ProductCard/FUTURE_ProductCard.tsx b/components/product/ProductCard/FUTURE_ProductCard.tsx index 604b91460..6ccc79fb9 100644 --- a/components/product/ProductCard/FUTURE_ProductCard.tsx +++ b/components/product/ProductCard/FUTURE_ProductCard.tsx @@ -1,10 +1,9 @@ import { FC } from 'react' import cn from 'classnames' import Link from 'next/link' -import Image, { ImageProps } from 'next/image' import s from './ProductCard.module.css' -// Restore Wishlist func -// import WishlistButton from '@components/wishlist/WishlistButton' +import Image, { ImageProps } from 'next/image' +import WishlistButton from '@components/wishlist/WishlistButton' interface Props { className?: string @@ -52,6 +51,11 @@ const ProductCard: FC<Props> = ({ className, product, variant, imgProps }) => { {product.prices[0].currencyCode} </span> </div> + <WishlistButton + className={s.wishlistButton} + productId={product.id} + variant={product.variants[0]!} + /> </div> <div className={s.imageContainer}> {product.images[0] && ( diff --git a/components/wishlist/WishlistButton/WishlistButton.tsx b/components/wishlist/WishlistButton/WishlistButton.tsx index b8b41c90d..fda78a4bf 100644 --- a/components/wishlist/WishlistButton/WishlistButton.tsx +++ b/components/wishlist/WishlistButton/WishlistButton.tsx @@ -1,16 +1,17 @@ import React, { FC, useState } from 'react' import cn from 'classnames' -import type { ProductNode } from '@framework/api/operations/get-all-products' -import useAddItem from '@framework/wishlist/use-add-item' -import useRemoveItem from '@framework/wishlist/use-remove-item' -import useWishlist from '@framework/wishlist/use-wishlist' -import useCustomer from '@framework/use-customer' import { Heart } from '@components/icons' import { useUI } from '@components/ui/context' +import type { ProductNode } from '@framework/api/operations/get-all-products' +import useCustomer from '@framework/use-customer' +import useAddItem from '@framework/wishlist/use-add-item' +import useWishlist from '@framework/wishlist/use-wishlist' +import useRemoveItem from '@framework/wishlist/use-remove-item' + type Props = { - productId: number - variant: NonNullable<ProductNode['variants']['edges']>[0] + productId: Product['id'] + variant: ProductVariant } & React.ButtonHTMLAttributes<HTMLButtonElement> const WishlistButton: FC<Props> = ({ @@ -19,16 +20,15 @@ const WishlistButton: FC<Props> = ({ className, ...props }) => { + const { data } = useWishlist() const addItem = useAddItem() const removeItem = useRemoveItem() - const { data } = useWishlist() const { data: customer } = useCustomer() - const [loading, setLoading] = useState(false) const { openModal, setModalView } = useUI() + const [loading, setLoading] = useState(false) + const itemInWishlist = data?.items?.find( - (item) => - item.product_id === productId && - item.variant_id === variant?.node.entityId + (item) => item.product_id === productId && item.variant_id === variant.id ) const handleWishlistChange = async (e: any) => { @@ -50,7 +50,7 @@ const WishlistButton: FC<Props> = ({ } else { await addItem({ productId, - variantId: variant?.node.entityId!, + variantId: variant?.id, }) } diff --git a/framework/bigcommerce/api/wishlist/index.ts b/framework/bigcommerce/api/wishlist/index.ts index 94194dd41..e78f0d9b9 100644 --- a/framework/bigcommerce/api/wishlist/index.ts +++ b/framework/bigcommerce/api/wishlist/index.ts @@ -15,8 +15,8 @@ import removeItem from './handlers/remove-item' export type { Wishlist, WishlistItem } export type ItemBody = { - productId: number - variantId: number + productId: Product['id'] + variantId: ProductVariant['id'] } export type AddItemBody = { item: ItemBody } diff --git a/framework/bigcommerce/wishlist/index.ts b/framework/bigcommerce/wishlist/index.ts new file mode 100644 index 000000000..9ea28291c --- /dev/null +++ b/framework/bigcommerce/wishlist/index.ts @@ -0,0 +1,4 @@ +export { default as useAddItem } from './use-add-item' +export { default as useWishlist } from './use-wishlist' +export { default as useRemoveItem } from './use-remove-item' +export { default as useWishlistActions } from './use-wishlist-actions' diff --git a/framework/types.d.ts b/framework/types.d.ts index 4098e5c7e..5d171b5b0 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -1,17 +1,21 @@ -interface ProductImage { - url: string - alt?: string -} - interface Product { id: string | number name: string description: string images: ProductImage[] + variants: ProductVariant[] prices: ProductPrice[] slug: string path?: string } +interface ProductImage { + url: string + alt?: string +} + +interface ProductVariant { + id: string | number +} interface ProductPrice { value: number | string From 4499f33f13d43385979c101229e278b9f8497e37 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Thu, 7 Jan 2021 16:49:26 -0300 Subject: [PATCH 007/221] Reordering --- README.md | 25 ++++++++++++++----- components/auth/LoginView.tsx | 2 +- components/auth/SignUpView.tsx | 2 +- components/cart/CartItem/CartItem.tsx | 2 +- .../cart/CartSidebarView/CartSidebarView.tsx | 2 +- components/common/UserNav/DropdownMenu.tsx | 2 +- components/common/UserNav/UserNav.tsx | 2 +- .../product/ProductCard/ProductCard.tsx | 2 +- .../product/ProductView/ProductView.tsx | 2 +- .../WishlistButton/WishlistButton.tsx | 2 +- .../wishlist/WishlistCard/WishlistCard.tsx | 2 +- framework/bigcommerce/auth/index.ts | 3 +++ .../bigcommerce/{ => auth}/use-login.tsx | 4 +-- .../bigcommerce/{ => auth}/use-logout.tsx | 2 +- .../bigcommerce/{ => auth}/use-signup.tsx | 4 +-- framework/bigcommerce/cart/index.ts | 5 ++++ framework/bigcommerce/customer/index.ts | 1 + .../{ => customer}/use-customer.tsx | 2 +- framework/bigcommerce/product/index.ts | 2 ++ .../bigcommerce/{ => product}/use-price.tsx | 0 .../{products => product}/use-search.tsx | 0 .../bigcommerce/wishlist/use-add-item.tsx | 2 +- .../bigcommerce/wishlist/use-remove-item.tsx | 2 +- .../bigcommerce/wishlist/use-wishlist.tsx | 2 +- pages/cart.tsx | 2 +- pages/profile.tsx | 2 +- pages/search.tsx | 2 +- 27 files changed, 52 insertions(+), 28 deletions(-) create mode 100644 framework/bigcommerce/auth/index.ts rename framework/bigcommerce/{ => auth}/use-login.tsx (91%) rename framework/bigcommerce/{ => auth}/use-logout.tsx (94%) rename framework/bigcommerce/{ => auth}/use-signup.tsx (91%) create mode 100644 framework/bigcommerce/cart/index.ts create mode 100644 framework/bigcommerce/customer/index.ts rename framework/bigcommerce/{ => customer}/use-customer.tsx (93%) create mode 100644 framework/bigcommerce/product/index.ts rename framework/bigcommerce/{ => product}/use-price.tsx (100%) rename framework/bigcommerce/{products => product}/use-search.tsx (100%) diff --git a/README.md b/README.md index de1452b75..54f021f74 100644 --- a/README.md +++ b/README.md @@ -97,17 +97,30 @@ People actively working on this project: @okbel & @lfades. ## Framework -Framework is where the data comes from. Contains mostly hooks and functions. +Framework is where the data comes from. It contains mostly hooks and functions. ## Structure -- ## product -- wishlist +Main folder and its exposed functions + +- `product` + - usePrice + - useSearch +- `wishlist` - useWishlist - addWishlistItem - removeWishlistItem -- auth +- `auth` + - useLogin + - useLogout + - useSignup +- `cart` -- config.json + - useCart + - useAddItem + - useRemoveItem + - useCartActions + - useUpdateItem -## Wishlist +- `config.json` +- README.md diff --git a/components/auth/LoginView.tsx b/components/auth/LoginView.tsx index 9102a53c6..89d5bf893 100644 --- a/components/auth/LoginView.tsx +++ b/components/auth/LoginView.tsx @@ -1,6 +1,6 @@ import { FC, useEffect, useState, useCallback } from 'react' import { Logo, Button, Input } from '@components/ui' -import useLogin from '@framework/use-login' +import useLogin from '@framework/auth/use-login' import { useUI } from '@components/ui/context' import { validate } from 'email-validator' diff --git a/components/auth/SignUpView.tsx b/components/auth/SignUpView.tsx index c49637d47..1b619828b 100644 --- a/components/auth/SignUpView.tsx +++ b/components/auth/SignUpView.tsx @@ -3,7 +3,7 @@ import { validate } from 'email-validator' import { Info } from '@components/icons' import { useUI } from '@components/ui/context' import { Logo, Button, Input } from '@components/ui' -import useSignup from '@framework/use-signup' +import useSignup from '@framework/auth/use-signup' interface Props {} diff --git a/components/cart/CartItem/CartItem.tsx b/components/cart/CartItem/CartItem.tsx index 283f3fa40..ac78d1849 100644 --- a/components/cart/CartItem/CartItem.tsx +++ b/components/cart/CartItem/CartItem.tsx @@ -3,7 +3,7 @@ import cn from 'classnames' import Image from 'next/image' import Link from 'next/link' import { Trash, Plus, Minus } from '@components/icons' -import usePrice from '@framework/use-price' +import usePrice from '@framework/product/use-price' import useUpdateItem from '@framework/cart/use-update-item' import useRemoveItem from '@framework/cart/use-remove-item' import s from './CartItem.module.css' diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index b313bdea9..2a58fd74f 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -5,7 +5,7 @@ import { Button } from '@components/ui' import { Bag, Cross, Check } from '@components/icons' import { useUI } from '@components/ui/context' import useCart from '@framework/cart/use-cart' -import usePrice from '@framework/use-price' +import usePrice from '@framework/product/use-price' import CartItem from '../CartItem' import s from './CartSidebarView.module.css' diff --git a/components/common/UserNav/DropdownMenu.tsx b/components/common/UserNav/DropdownMenu.tsx index 7b02c863a..a5bc5fd86 100644 --- a/components/common/UserNav/DropdownMenu.tsx +++ b/components/common/UserNav/DropdownMenu.tsx @@ -15,7 +15,7 @@ import { clearAllBodyScrollLocks, } from 'body-scroll-lock' -import useLogout from '@framework/use-logout' +import useLogout from '@framework/auth/use-logout' interface DropdownMenuProps { open?: boolean diff --git a/components/common/UserNav/UserNav.tsx b/components/common/UserNav/UserNav.tsx index 31852f658..7b912422d 100644 --- a/components/common/UserNav/UserNav.tsx +++ b/components/common/UserNav/UserNav.tsx @@ -2,7 +2,7 @@ import { FC } from 'react' import Link from 'next/link' import cn from 'classnames' import useCart from '@framework/cart/use-cart' -import useCustomer from '@framework/use-customer' +import useCustomer from '@framework/customer/use-customer' import { Heart, Bag } from '@components/icons' import { useUI } from '@components/ui/context' import DropdownMenu from './DropdownMenu' diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index 85e69b3f2..58a201e8b 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -5,7 +5,7 @@ import type { FC } from 'react' import s from './ProductCard.module.css' import WishlistButton from '@components/wishlist/WishlistButton' -import usePrice from '@framework/use-price' +import usePrice from '@framework/product/use-price' import type { ProductNode } from '@framework/api/operations/get-all-products' interface Props { diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index 9703156a1..97342242d 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -8,7 +8,7 @@ import { useUI } from '@components/ui/context' import { Swatch, ProductSlider } from '@components/product' import { Button, Container, Text } from '@components/ui' -import usePrice from '@framework/use-price' +import usePrice from '@framework/product/use-price' import useAddItem from '@framework/cart/use-add-item' import type { ProductNode } from '@framework/api/operations/get-product' import { diff --git a/components/wishlist/WishlistButton/WishlistButton.tsx b/components/wishlist/WishlistButton/WishlistButton.tsx index fda78a4bf..ea4abbb51 100644 --- a/components/wishlist/WishlistButton/WishlistButton.tsx +++ b/components/wishlist/WishlistButton/WishlistButton.tsx @@ -4,7 +4,7 @@ import { Heart } from '@components/icons' import { useUI } from '@components/ui/context' import type { ProductNode } from '@framework/api/operations/get-all-products' -import useCustomer from '@framework/use-customer' +import useCustomer from '@framework/customer/use-customer' import useAddItem from '@framework/wishlist/use-add-item' import useWishlist from '@framework/wishlist/use-wishlist' import useRemoveItem from '@framework/wishlist/use-remove-item' diff --git a/components/wishlist/WishlistCard/WishlistCard.tsx b/components/wishlist/WishlistCard/WishlistCard.tsx index 8cbd38bc7..b9b1a2f3e 100644 --- a/components/wishlist/WishlistCard/WishlistCard.tsx +++ b/components/wishlist/WishlistCard/WishlistCard.tsx @@ -3,7 +3,7 @@ import cn from 'classnames' import Link from 'next/link' import Image from 'next/image' import type { WishlistItem } from '@framework/api/wishlist' -import usePrice from '@framework/use-price' +import usePrice from '@framework/product/use-price' import useRemoveItem from '@framework/wishlist/use-remove-item' import useAddItem from '@framework/cart/use-add-item' import { useUI } from '@components/ui/context' diff --git a/framework/bigcommerce/auth/index.ts b/framework/bigcommerce/auth/index.ts new file mode 100644 index 000000000..36e757a89 --- /dev/null +++ b/framework/bigcommerce/auth/index.ts @@ -0,0 +1,3 @@ +export { default as useLogin } from './use-login' +export { default as useLogout } from './use-logout' +export { default as useSignup } from './use-signup' diff --git a/framework/bigcommerce/use-login.tsx b/framework/bigcommerce/auth/use-login.tsx similarity index 91% rename from framework/bigcommerce/use-login.tsx rename to framework/bigcommerce/auth/use-login.tsx index 62d4ecbd7..fa2294666 100644 --- a/framework/bigcommerce/use-login.tsx +++ b/framework/bigcommerce/auth/use-login.tsx @@ -2,8 +2,8 @@ import { useCallback } from 'react' import type { HookFetcher } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' import useCommerceLogin from '@commerce/use-login' -import type { LoginBody } from './api/customers/login' -import useCustomer from './use-customer' +import type { LoginBody } from '../api/customers/login' +import useCustomer from '../customer/use-customer' const defaultOpts = { url: '/api/bigcommerce/customers/login', diff --git a/framework/bigcommerce/use-logout.tsx b/framework/bigcommerce/auth/use-logout.tsx similarity index 94% rename from framework/bigcommerce/use-logout.tsx rename to framework/bigcommerce/auth/use-logout.tsx index a9131ed3a..6aaee29f9 100644 --- a/framework/bigcommerce/use-logout.tsx +++ b/framework/bigcommerce/auth/use-logout.tsx @@ -1,7 +1,7 @@ import { useCallback } from 'react' import type { HookFetcher } from '@commerce/utils/types' import useCommerceLogout from '@commerce/use-logout' -import useCustomer from './use-customer' +import useCustomer from '../customer/use-customer' const defaultOpts = { url: '/api/bigcommerce/customers/logout', diff --git a/framework/bigcommerce/use-signup.tsx b/framework/bigcommerce/auth/use-signup.tsx similarity index 91% rename from framework/bigcommerce/use-signup.tsx rename to framework/bigcommerce/auth/use-signup.tsx index 4d907c5ac..c68ce7b7a 100644 --- a/framework/bigcommerce/use-signup.tsx +++ b/framework/bigcommerce/auth/use-signup.tsx @@ -2,8 +2,8 @@ import { useCallback } from 'react' import type { HookFetcher } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' import useCommerceSignup from '@commerce/use-signup' -import type { SignupBody } from './api/customers/signup' -import useCustomer from './use-customer' +import type { SignupBody } from '../api/customers/signup' +import useCustomer from '../customer/use-customer' const defaultOpts = { url: '/api/bigcommerce/customers/signup', diff --git a/framework/bigcommerce/cart/index.ts b/framework/bigcommerce/cart/index.ts new file mode 100644 index 000000000..43c6db2b7 --- /dev/null +++ b/framework/bigcommerce/cart/index.ts @@ -0,0 +1,5 @@ +export { default as useCart } from './use-cart' +export { default as useAddItem } from './use-add-item' +export { default as useRemoveItem } from './use-remove-item' +export { default as useWishlistActions } from './use-cart-actions' +export { default as useUpdateItem } from './use-cart-actions' diff --git a/framework/bigcommerce/customer/index.ts b/framework/bigcommerce/customer/index.ts new file mode 100644 index 000000000..6c903ecc5 --- /dev/null +++ b/framework/bigcommerce/customer/index.ts @@ -0,0 +1 @@ +export { default as useCustomer } from './use-customer' diff --git a/framework/bigcommerce/use-customer.tsx b/framework/bigcommerce/customer/use-customer.tsx similarity index 93% rename from framework/bigcommerce/use-customer.tsx rename to framework/bigcommerce/customer/use-customer.tsx index 4a08a0330..f44f16c1f 100644 --- a/framework/bigcommerce/use-customer.tsx +++ b/framework/bigcommerce/customer/use-customer.tsx @@ -1,7 +1,7 @@ import type { HookFetcher } from '@commerce/utils/types' import type { SwrOptions } from '@commerce/utils/use-data' import useCommerceCustomer from '@commerce/use-customer' -import type { Customer, CustomerData } from './api/customers' +import type { Customer, CustomerData } from '../api/customers' const defaultOpts = { url: '/api/bigcommerce/customers', diff --git a/framework/bigcommerce/product/index.ts b/framework/bigcommerce/product/index.ts new file mode 100644 index 000000000..426a3edcd --- /dev/null +++ b/framework/bigcommerce/product/index.ts @@ -0,0 +1,2 @@ +export { default as usePrice } from './use-price' +export { default as useSearch } from './use-search' diff --git a/framework/bigcommerce/use-price.tsx b/framework/bigcommerce/product/use-price.tsx similarity index 100% rename from framework/bigcommerce/use-price.tsx rename to framework/bigcommerce/product/use-price.tsx diff --git a/framework/bigcommerce/products/use-search.tsx b/framework/bigcommerce/product/use-search.tsx similarity index 100% rename from framework/bigcommerce/products/use-search.tsx rename to framework/bigcommerce/product/use-search.tsx diff --git a/framework/bigcommerce/wishlist/use-add-item.tsx b/framework/bigcommerce/wishlist/use-add-item.tsx index 0c355c181..6e7d9de41 100644 --- a/framework/bigcommerce/wishlist/use-add-item.tsx +++ b/framework/bigcommerce/wishlist/use-add-item.tsx @@ -3,7 +3,7 @@ import { HookFetcher } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' import useWishlistAddItem from '@commerce/wishlist/use-add-item' import type { ItemBody, AddItemBody } from '../api/wishlist' -import useCustomer from '../use-customer' +import useCustomer from '../customer/use-customer' import useWishlist, { UseWishlistOptions, Wishlist } from './use-wishlist' const defaultOpts = { diff --git a/framework/bigcommerce/wishlist/use-remove-item.tsx b/framework/bigcommerce/wishlist/use-remove-item.tsx index 5ff55b5f2..86614a21a 100644 --- a/framework/bigcommerce/wishlist/use-remove-item.tsx +++ b/framework/bigcommerce/wishlist/use-remove-item.tsx @@ -3,7 +3,7 @@ import { HookFetcher } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' import useWishlistRemoveItem from '@commerce/wishlist/use-remove-item' import type { RemoveItemBody } from '../api/wishlist' -import useCustomer from '../use-customer' +import useCustomer from '../customer/use-customer' import useWishlist, { UseWishlistOptions, Wishlist } from './use-wishlist' const defaultOpts = { diff --git a/framework/bigcommerce/wishlist/use-wishlist.tsx b/framework/bigcommerce/wishlist/use-wishlist.tsx index 4ab581155..6ebc8459d 100644 --- a/framework/bigcommerce/wishlist/use-wishlist.tsx +++ b/framework/bigcommerce/wishlist/use-wishlist.tsx @@ -2,7 +2,7 @@ import { HookFetcher } from '@commerce/utils/types' import { SwrOptions } from '@commerce/utils/use-data' import useCommerceWishlist from '@commerce/wishlist/use-wishlist' import type { Wishlist } from '../api/wishlist' -import useCustomer from '../use-customer' +import useCustomer from '../customer/use-customer' const defaultOpts = { url: '/api/bigcommerce/wishlist', diff --git a/pages/cart.tsx b/pages/cart.tsx index 7e6d17b21..fc06c4ced 100644 --- a/pages/cart.tsx +++ b/pages/cart.tsx @@ -2,7 +2,7 @@ import type { GetStaticPropsContext } from 'next' import { getConfig } from '@framework/api' import getAllPages from '@framework/api/operations/get-all-pages' import useCart from '@framework/cart/use-cart' -import usePrice from '@framework/use-price' +import usePrice from '@framework/product/use-price' import { Layout } from '@components/common' import { Button } from '@components/ui' import { Bag, Cross, Check } from '@components/icons' diff --git a/pages/profile.tsx b/pages/profile.tsx index 63de0b234..8b8c1a3b1 100644 --- a/pages/profile.tsx +++ b/pages/profile.tsx @@ -1,7 +1,7 @@ import type { GetStaticPropsContext } from 'next' import { getConfig } from '@framework/api' import getAllPages from '@framework/api/operations/get-all-pages' -import useCustomer from '@framework/use-customer' +import useCustomer from '@framework/customer/use-customer' import { Layout } from '@components/common' import { Container, Text } from '@components/ui' diff --git a/pages/search.tsx b/pages/search.tsx index 5110aba55..18b0d9c1a 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -6,7 +6,7 @@ import { useRouter } from 'next/router' import { getConfig } from '@framework/api' import getAllPages from '@framework/api/operations/get-all-pages' import getSiteInfo from '@framework/api/operations/get-site-info' -import useSearch from '@framework/products/use-search' +import useSearch from '@framework/product/use-search' import { Layout } from '@components/common' import { ProductCard } from '@components/product' import { Container, Grid, Skeleton } from '@components/ui' From 27dd4bfb69c35256cef1607da559aa57067097b9 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Thu, 7 Jan 2021 16:58:58 -0300 Subject: [PATCH 008/221] Moved normalizer to BC function --- .../api/operations/get-all-products.ts | 43 +++++++++++++++++-- pages/index.tsx | 42 +----------------- 2 files changed, 41 insertions(+), 44 deletions(-) diff --git a/framework/bigcommerce/api/operations/get-all-products.ts b/framework/bigcommerce/api/operations/get-all-products.ts index 0cd9737c2..036602639 100644 --- a/framework/bigcommerce/api/operations/get-all-products.ts +++ b/framework/bigcommerce/api/operations/get-all-products.ts @@ -8,6 +8,43 @@ import setProductLocaleMeta from '../utils/set-product-locale-meta' import { productConnectionFragment } from '../fragments/product' import { BigcommerceConfig, getConfig } from '..' +function productsNormalizer(arr: any[]): Product[] { + // Normalizes products arr response and flattens node edges + return arr.map( + ({ + node: { + entityId: id, + images, + variants, + productOptions, + prices, + path, + ...rest + }, + }) => ({ + id, + path, + slug: path.slice(1, -1), + images: images.edges.map( + ({ node: { urlOriginal, altText, ...rest } }: any) => ({ + url: urlOriginal, + alt: altText, + ...rest, + }) + ), + variants: variants.edges.map(({ node }: any) => node), + productOptions: productOptions.edges.map(({ node }: any) => node), + prices: [ + { + value: prices.price.value, + currencyCode: prices.price.currencyCode, + }, + ], + ...rest, + }) + ) +} + export const getAllProductsQuery = /* GraphQL */ ` query getAllProducts( $hasLocale: Boolean = false @@ -72,7 +109,7 @@ async function getAllProducts(opts?: { variables?: ProductVariables config?: BigcommerceConfig preview?: boolean -}): Promise<GetAllProductsResult> +}): Promise<{ products: Product[] }> async function getAllProducts< T extends Record<keyof GetAllProductsResult, any[]>, @@ -93,7 +130,7 @@ async function getAllProducts({ variables?: ProductVariables config?: BigcommerceConfig preview?: boolean -} = {}): Promise<GetAllProductsResult> { +} = {}): Promise<{ products: Product[] }> { config = getConfig(config) const locale = vars.locale || config.locale @@ -126,7 +163,7 @@ async function getAllProducts({ }) } - return { products } + return { products: productsNormalizer(products) } } export default getAllProducts diff --git a/pages/index.tsx b/pages/index.tsx index 186da09f6..9bb1e09a6 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -11,44 +11,6 @@ import getSiteInfo from '@framework/api/operations/get-site-info' import getAllPages from '@framework/api/operations/get-all-pages' // Outputs from providers should already be normalized -// TODO (bc) move this to the provider - -function productsNormalizer(arr: any[]) { - // Normalizes products arr response and flattens node edges - return arr.map( - ({ - node: { - entityId: id, - images, - variants, - productOptions, - prices, - path, - ...rest - }, - }) => ({ - id, - path, - slug: path.slice(1, -1), - images: images.edges.map( - ({ node: { urlOriginal, altText, ...rest } }: any) => ({ - url: urlOriginal, - alt: altText, - ...rest, - }) - ), - variants: variants.edges.map(({ node }: any) => node), - productOptions: productOptions.edges.map(({ node }: any) => node), - prices: [ - { - value: prices.price.value, - currencyCode: prices.price.currencyCode, - }, - ], - ...rest, - }) - ) -} export async function getStaticProps({ preview, @@ -56,14 +18,12 @@ export async function getStaticProps({ }: GetStaticPropsContext) { const config = getConfig({ locale }) - const { products: rawProducts } = await getAllProducts({ + const { products } = await getAllProducts({ variables: { first: 12 }, config, preview, }) - // Remove normalizer and send to framework provider. - const products = productsNormalizer(rawProducts) const { categories, brands } = await getSiteInfo({ config, preview }) const { pages } = await getAllPages({ config, preview }) From 4259136983c461db7887f34a1da0af72dfdecfb2 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Thu, 7 Jan 2021 17:10:27 -0300 Subject: [PATCH 009/221] Removed Futures --- .../ProductCard/FUTURE_ProductCard.tsx | 81 ---------------- .../product/ProductCard/ProductCard.tsx | 97 ++++++++----------- pages/index.tsx | 3 +- 3 files changed, 39 insertions(+), 142 deletions(-) delete mode 100644 components/product/ProductCard/FUTURE_ProductCard.tsx diff --git a/components/product/ProductCard/FUTURE_ProductCard.tsx b/components/product/ProductCard/FUTURE_ProductCard.tsx deleted file mode 100644 index 6ccc79fb9..000000000 --- a/components/product/ProductCard/FUTURE_ProductCard.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { FC } from 'react' -import cn from 'classnames' -import Link from 'next/link' -import s from './ProductCard.module.css' -import Image, { ImageProps } from 'next/image' -import WishlistButton from '@components/wishlist/WishlistButton' - -interface Props { - className?: string - product: Product - variant?: 'slim' | 'simple' - imgProps?: Omit<ImageProps, 'src'> -} - -const ProductCard: FC<Props> = ({ className, product, variant, imgProps }) => { - return ( - <Link href={`product/${product.slug}`}> - <a - className={cn(s.root, { [s.simple]: variant === 'simple' }, className)} - > - {variant === 'slim' ? ( - <div className="relative overflow-hidden box-border"> - <div className="absolute inset-0 flex items-center justify-end mr-8 z-20"> - <span className="bg-black text-white inline-block p-3 font-bold text-xl break-words"> - {product.name} - </span> - </div> - {product.images[0] && ( - <Image - quality="85" - alt={product.name} - src={product.images[0].url} - height={320} - width={320} - layout="fixed" - {...imgProps} - /> - )} - </div> - ) : ( - <> - <div className={s.squareBg} /> - <div className="flex flex-row justify-between box-border w-full z-20 absolute"> - <div className="absolute top-0 left-0 pr-16 max-w-full"> - <h3 className={s.productTitle}> - <span>{product.name}</span> - </h3> - <span className={s.productPrice}> - {product.prices[0].value} - - {product.prices[0].currencyCode} - </span> - </div> - <WishlistButton - className={s.wishlistButton} - productId={product.id} - variant={product.variants[0]!} - /> - </div> - <div className={s.imageContainer}> - {product.images[0] && ( - <Image - alt={product.name} - className={s.productImage} - src={product.images[0].url} - height={540} - width={540} - quality="85" - layout="responsive" - {...imgProps} - /> - )} - </div> - </> - )} - </a> - </Link> - ) -} - -export default ProductCard diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index 58a201e8b..6ccc79fb9 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -1,45 +1,20 @@ +import { FC } from 'react' import cn from 'classnames' import Link from 'next/link' -import Image from 'next/image' -import type { FC } from 'react' import s from './ProductCard.module.css' +import Image, { ImageProps } from 'next/image' import WishlistButton from '@components/wishlist/WishlistButton' -import usePrice from '@framework/product/use-price' -import type { ProductNode } from '@framework/api/operations/get-all-products' - interface Props { className?: string - product: ProductNode + product: Product variant?: 'slim' | 'simple' - imgWidth: number | string - imgHeight: number | string - imgLayout?: 'fixed' | 'intrinsic' | 'responsive' | undefined - imgPriority?: boolean - imgLoading?: 'eager' | 'lazy' - imgSizes?: string + imgProps?: Omit<ImageProps, 'src'> } -const ProductCard: FC<Props> = ({ - className, - product: p, - variant, - imgWidth, - imgHeight, - imgPriority, - imgLoading, - imgSizes, - imgLayout = 'responsive', -}) => { - const src = p.images.edges?.[0]?.node?.urlOriginal! - const { price } = usePrice({ - amount: p.prices?.price?.value, - baseAmount: p.prices?.retailPrice?.value, - currencyCode: p.prices?.price?.currencyCode!, - }) - +const ProductCard: FC<Props> = ({ className, product, variant, imgProps }) => { return ( - <Link href={`/product${p.path}`}> + <Link href={`product/${product.slug}`}> <a className={cn(s.root, { [s.simple]: variant === 'simple' }, className)} > @@ -47,20 +22,20 @@ const ProductCard: FC<Props> = ({ <div className="relative overflow-hidden box-border"> <div className="absolute inset-0 flex items-center justify-end mr-8 z-20"> <span className="bg-black text-white inline-block p-3 font-bold text-xl break-words"> - {p.name} + {product.name} </span> </div> - <Image - quality="85" - width={imgWidth} - sizes={imgSizes} - height={imgHeight} - layout={imgLayout} - loading={imgLoading} - priority={imgPriority} - src={p.images.edges?.[0]?.node.urlOriginal!} - alt={p.images.edges?.[0]?.node.altText || 'Product Image'} - /> + {product.images[0] && ( + <Image + quality="85" + alt={product.name} + src={product.images[0].url} + height={320} + width={320} + layout="fixed" + {...imgProps} + /> + )} </div> ) : ( <> @@ -68,29 +43,33 @@ const ProductCard: FC<Props> = ({ <div className="flex flex-row justify-between box-border w-full z-20 absolute"> <div className="absolute top-0 left-0 pr-16 max-w-full"> <h3 className={s.productTitle}> - <span>{p.name}</span> + <span>{product.name}</span> </h3> - <span className={s.productPrice}>{price}</span> + <span className={s.productPrice}> + {product.prices[0].value} + + {product.prices[0].currencyCode} + </span> </div> <WishlistButton className={s.wishlistButton} - productId={p.entityId} - variant={p.variants.edges?.[0]!} + productId={product.id} + variant={product.variants[0]!} /> </div> <div className={s.imageContainer}> - <Image - quality="85" - src={src} - alt={p.name} - className={s.productImage} - width={imgWidth} - sizes={imgSizes} - height={imgHeight} - layout={imgLayout} - loading={imgLoading} - priority={imgPriority} - /> + {product.images[0] && ( + <Image + alt={product.name} + className={s.productImage} + src={product.images[0].url} + height={540} + width={540} + quality="85" + layout="responsive" + {...imgProps} + /> + )} </div> </> )} diff --git a/pages/index.tsx b/pages/index.tsx index 9bb1e09a6..76299673d 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,7 +1,6 @@ -import rangeMap from '@lib/range-map' import { Layout } from '@components/common' -import ProductCard from '@components/product/ProductCard/FUTURE_ProductCard' import { Grid, Marquee, Hero } from '@components/ui' +import { ProductCard } from '@components/product' import HomeAllProductsGrid from '@components/common/HomeAllProductsGrid' import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next' From 4f0898281ddc4ca98daf95e8551c177f4a77d764 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Sat, 9 Jan 2021 12:05:40 -0300 Subject: [PATCH 010/221] More Types --- framework/types.d.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/framework/types.d.ts b/framework/types.d.ts index 5d171b5b0..f15bde593 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -22,3 +22,17 @@ interface ProductPrice { currencyCode: 'USD' | 'ARS' type?: 'price' | 'retail' | 'sale' | string } + +interface Wishlist { + id: string + products: Pick<Product, ['id', 'name']>[] +} + +interface Customer { + id: string + name: string +} + +interface Category {} + +interface Brand {} From c3c1ac7cf595344973b5486a1b16f1ab49bda41e Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Sat, 9 Jan 2021 12:07:17 -0300 Subject: [PATCH 011/221] More Types --- framework/types.d.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/framework/types.d.ts b/framework/types.d.ts index f15bde593..b804b68a5 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -25,7 +25,12 @@ interface ProductPrice { interface Wishlist { id: string - products: Pick<Product, ['id', 'name']>[] + products: Pick<Product, 'id' | 'name' | 'prices'>[] +} + +interface Wishlist { + id: string + products: Pick<Product, 'id' | 'name' | 'prices'>[] } interface Customer { From ab16960ddb0fa7efaeffa2147a3224e09be0252b Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Sat, 9 Jan 2021 12:17:31 -0300 Subject: [PATCH 012/221] More Types --- framework/types.d.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/framework/types.d.ts b/framework/types.d.ts index b804b68a5..2f1329071 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -23,7 +23,7 @@ interface ProductPrice { type?: 'price' | 'retail' | 'sale' | string } -interface Wishlist { +interface Cart { id: string products: Pick<Product, 'id' | 'name' | 'prices'>[] } @@ -33,11 +33,22 @@ interface Wishlist { products: Pick<Product, 'id' | 'name' | 'prices'>[] } +interface Order {} + interface Customer { id: string name: string + email: string } -interface Category {} +interface Category { + id: string + name: string +} -interface Brand {} +interface Brand { + id: string + name: string +} + +type Features = 'wishlist' | 'customer' From e593eab9cc130257e067f5bd5da49248d899fcf5 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Sat, 9 Jan 2021 12:39:18 -0300 Subject: [PATCH 013/221] Fix useCallback --- components/ui/Modal/Modal.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/components/ui/Modal/Modal.tsx b/components/ui/Modal/Modal.tsx index c3b0c8df2..300a99192 100644 --- a/components/ui/Modal/Modal.tsx +++ b/components/ui/Modal/Modal.tsx @@ -19,12 +19,14 @@ interface Props { const Modal: FC<Props> = ({ children, open, onClose, onEnter = null }) => { const ref = useRef() as React.MutableRefObject<HTMLDivElement> - const handleKey = (e: KeyboardEvent) => - useCallback(() => { + const handleKey = useCallback( + (e: KeyboardEvent) => { if (e.key === 'Escape') { return onClose() } - }, [onClose]) + }, + [onClose] + ) useEffect(() => { if (ref.current) { From 0d4355b43195834d3caee6470fecba5efc90f4e3 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Sun, 10 Jan 2021 13:08:34 -0300 Subject: [PATCH 014/221] Progress, Changes types, readme and restoring functionality --- .gitignore | 1 + README.md | 8 ++++++++ components/ui/index.ts | 1 + components/wishlist/WishlistButton/WishlistButton.tsx | 9 +++------ framework/bigcommerce/api/wishlist/index.ts | 4 ++-- .../bigcommerce/{scripts => lib}/generate-definitions.js | 0 framework/bigcommerce/wishlist/use-wishlist.tsx | 2 +- framework/types.d.ts | 4 ++-- package.json | 2 +- pages/index.tsx | 2 -- tsconfig.json | 2 +- 11 files changed, 20 insertions(+), 15 deletions(-) rename framework/bigcommerce/{scripts => lib}/generate-definitions.js (100%) diff --git a/.gitignore b/.gitignore index bcbf6047a..50d4285ba 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ yarn-debug.log* yarn-error.log* # local env files +.env .env.local .env.development.local .env.test.local diff --git a/README.md b/README.md index 54f021f74..e944c7569 100644 --- a/README.md +++ b/README.md @@ -124,3 +124,11 @@ Main folder and its exposed functions - `config.json` - README.md + +#### Example of correct usage of Commece Framework + +```js +import { useUI } from '@components/ui' +import { useCustomer } from '@framework/customer' +import { useAddItem, useWishlist, useRemoveItem } from '@framework/wishlist' +``` diff --git a/components/ui/index.ts b/components/ui/index.ts index 581c12d53..f2a293200 100644 --- a/components/ui/index.ts +++ b/components/ui/index.ts @@ -10,3 +10,4 @@ export { default as Skeleton } from './Skeleton' export { default as Modal } from './Modal' export { default as Text } from './Text' export { default as Input } from './Input' +export { useUI } from './context' diff --git a/components/wishlist/WishlistButton/WishlistButton.tsx b/components/wishlist/WishlistButton/WishlistButton.tsx index ea4abbb51..db9ca4b67 100644 --- a/components/wishlist/WishlistButton/WishlistButton.tsx +++ b/components/wishlist/WishlistButton/WishlistButton.tsx @@ -1,13 +1,10 @@ import React, { FC, useState } from 'react' import cn from 'classnames' import { Heart } from '@components/icons' -import { useUI } from '@components/ui/context' -import type { ProductNode } from '@framework/api/operations/get-all-products' -import useCustomer from '@framework/customer/use-customer' -import useAddItem from '@framework/wishlist/use-add-item' -import useWishlist from '@framework/wishlist/use-wishlist' -import useRemoveItem from '@framework/wishlist/use-remove-item' +import { useUI } from '@components/ui' +import { useCustomer } from '@framework/customer' +import { useAddItem, useWishlist, useRemoveItem } from '@framework/wishlist' type Props = { productId: Product['id'] diff --git a/framework/bigcommerce/api/wishlist/index.ts b/framework/bigcommerce/api/wishlist/index.ts index e78f0d9b9..b50c5e97f 100644 --- a/framework/bigcommerce/api/wishlist/index.ts +++ b/framework/bigcommerce/api/wishlist/index.ts @@ -21,10 +21,10 @@ export type ItemBody = { export type AddItemBody = { item: ItemBody } -export type RemoveItemBody = { itemId: string } +export type RemoveItemBody = { itemId: Product['id'] } export type WishlistBody = { - customer_id: number + customer_id: Customer['id'] is_public: number name: string items: any[] diff --git a/framework/bigcommerce/scripts/generate-definitions.js b/framework/bigcommerce/lib/generate-definitions.js similarity index 100% rename from framework/bigcommerce/scripts/generate-definitions.js rename to framework/bigcommerce/lib/generate-definitions.js diff --git a/framework/bigcommerce/wishlist/use-wishlist.tsx b/framework/bigcommerce/wishlist/use-wishlist.tsx index 6ebc8459d..455fdc9ff 100644 --- a/framework/bigcommerce/wishlist/use-wishlist.tsx +++ b/framework/bigcommerce/wishlist/use-wishlist.tsx @@ -16,7 +16,7 @@ export interface UseWishlistOptions { } export interface UseWishlistInput extends UseWishlistOptions { - customerId?: number + customerId?: Customer['id'] } export const fetcher: HookFetcher<Wishlist | null, UseWishlistInput> = ( diff --git a/framework/types.d.ts b/framework/types.d.ts index 2f1329071..881218ca8 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -2,11 +2,11 @@ interface Product { id: string | number name: string description: string + slug: string + path?: string images: ProductImage[] variants: ProductVariant[] prices: ProductPrice[] - slug: string - path?: string } interface ProductImage { url: string diff --git a/package.json b/package.json index 1030e8479..35e8d5cb5 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "analyze": "BUNDLE_ANALYZE=both yarn build", "find:unused": "next-unused", "generate": "graphql-codegen", - "generate:definitions": "node framework/bigcommerce/scripts/generate-definitions.js" + "generate:definitions": "node framework/bigcommerce/lib/generate-definitions.js" }, "prettier": { "semi": false, diff --git a/pages/index.tsx b/pages/index.tsx index 76299673d..e194366c4 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -9,8 +9,6 @@ import getAllProducts from '@framework/api/operations/get-all-products' import getSiteInfo from '@framework/api/operations/get-site-info' import getAllPages from '@framework/api/operations/get-all-pages' -// Outputs from providers should already be normalized - export async function getStaticProps({ preview, locale, diff --git a/tsconfig.json b/tsconfig.json index 98639f61e..43dfd2a27 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,6 +26,6 @@ "@framework": ["framework/bigcommerce"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], + "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], "exclude": ["node_modules"] } From 3ba60fcafcf2433cb0db39dad5bf631f24c4dc5f Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Sun, 10 Jan 2021 14:11:00 -0300 Subject: [PATCH 015/221] Changes --- framework/bigcommerce/wishlist/use-wishlist.tsx | 2 +- framework/commerce/wishlist/index.ts | 3 +++ framework/types.d.ts | 9 ++++++--- pages/wishlist.tsx | 4 +++- 4 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 framework/commerce/wishlist/index.ts diff --git a/framework/bigcommerce/wishlist/use-wishlist.tsx b/framework/bigcommerce/wishlist/use-wishlist.tsx index 455fdc9ff..6ebc8459d 100644 --- a/framework/bigcommerce/wishlist/use-wishlist.tsx +++ b/framework/bigcommerce/wishlist/use-wishlist.tsx @@ -16,7 +16,7 @@ export interface UseWishlistOptions { } export interface UseWishlistInput extends UseWishlistOptions { - customerId?: Customer['id'] + customerId?: number } export const fetcher: HookFetcher<Wishlist | null, UseWishlistInput> = ( diff --git a/framework/commerce/wishlist/index.ts b/framework/commerce/wishlist/index.ts new file mode 100644 index 000000000..241af3c7e --- /dev/null +++ b/framework/commerce/wishlist/index.ts @@ -0,0 +1,3 @@ +export { default as useAddItem } from './use-add-item' +export { default as useWishlist } from './use-wishlist' +export { default as useRemoveItem } from './use-remove-item' diff --git a/framework/types.d.ts b/framework/types.d.ts index 881218ca8..8958a9a04 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -36,11 +36,14 @@ interface Wishlist { interface Order {} interface Customer { - id: string - name: string - email: string + id: string | number | undefined + [prop: string]: any } +type UseCustomerResponse = { + customer: Customer +} | null + interface Category { id: string name: string diff --git a/pages/wishlist.tsx b/pages/wishlist.tsx index b52c78d47..8f957280f 100644 --- a/pages/wishlist.tsx +++ b/pages/wishlist.tsx @@ -7,6 +7,7 @@ import { Heart } from '@components/icons' import { Text, Container } from '@components/ui' import { WishlistCard } from '@components/wishlist' import { defatultPageProps } from '@lib/defaults' +import { useCustomer } from '@framework/customer' export async function getStaticProps({ preview, @@ -20,7 +21,8 @@ export async function getStaticProps({ } export default function Wishlist() { - const { data, isEmpty } = useWishlist({ includeProducts: true }) + const { data: customer } = useCustomer() + const { data, isEmpty } = useWishlist() return ( <Container> From 92a2388bd11459fdb32faafafb4d8565ae531bf9 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Sun, 10 Jan 2021 14:13:46 -0300 Subject: [PATCH 016/221] TS Issues --- .../common/HomeAllProductsGrid/HomeAllProductsGrid.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx index f19e1586a..d292d0d6b 100644 --- a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx +++ b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx @@ -53,8 +53,10 @@ const Head: FC<Props> = ({ categories, brands, newestProducts }) => { key={node.path} product={node} variant="simple" - imgWidth={480} - imgHeight={480} + imgProps={{ + width: 480, + height: 480, + }} /> ))} </Grid> From 0a05182f3b9445ef434aecc6016ce7b015532d0e Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Sun, 10 Jan 2021 15:55:11 -0300 Subject: [PATCH 017/221] Changes --- .../HomeAllProductsGrid.tsx | 10 ++-- .../product/ProductCard/ProductCard.tsx | 4 +- .../product/ProductView/ProductView.tsx | 47 +++++++++---------- .../api/operations/get-all-products.ts | 10 ++-- framework/types.d.ts | 7 +-- pages/index.tsx | 10 ++-- 6 files changed, 43 insertions(+), 45 deletions(-) diff --git a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx index d292d0d6b..d55f51f7a 100644 --- a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx +++ b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx @@ -8,10 +8,10 @@ import { getCategoryPath, getDesignerPath } from '@lib/search' interface Props { categories?: any brands?: any - newestProducts?: any + products?: Product[] } -const Head: FC<Props> = ({ categories, brands, newestProducts }) => { +const Head: FC<Props> = ({ categories, brands, products = [] }) => { return ( <div className={s.root}> <div className={s.asideWrapper}> @@ -48,10 +48,10 @@ const Head: FC<Props> = ({ categories, brands, newestProducts }) => { </div> <div className="flex-1"> <Grid layout="normal"> - {newestProducts.map(({ node }: any) => ( + {products.map((product) => ( <ProductCard - key={node.path} - product={node} + key={product.path} + product={product} variant="simple" imgProps={{ width: 480, diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index 6ccc79fb9..47c000032 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -46,9 +46,9 @@ const ProductCard: FC<Props> = ({ className, product, variant, imgProps }) => { <span>{product.name}</span> </h3> <span className={s.productPrice}> - {product.prices[0].value} + {product.price.value} - {product.prices[0].currencyCode} + {product.price.currencyCode} </span> </div> <WishlistButton diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index 97342242d..34bae1f69 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -1,16 +1,16 @@ -import { FC, useState } from 'react' import cn from 'classnames' import Image from 'next/image' import { NextSeo } from 'next-seo' - +import { FC, useState } from 'react' import s from './ProductView.module.css' -import { useUI } from '@components/ui/context' + +import { useUI } from '@components/ui' import { Swatch, ProductSlider } from '@components/product' import { Button, Container, Text } from '@components/ui' -import usePrice from '@framework/product/use-price' -import useAddItem from '@framework/cart/use-add-item' -import type { ProductNode } from '@framework/api/operations/get-product' +import { usePrice } from '@framework/product' +import { useAddItem } from '@framework/cart' + import { getCurrentVariant, getProductOptions, @@ -21,15 +21,15 @@ import WishlistButton from '@components/wishlist/WishlistButton' interface Props { className?: string children?: any - product: ProductNode + product: Product } const ProductView: FC<Props> = ({ product }) => { const addItem = useAddItem() const { price } = usePrice({ - amount: product.prices?.price?.value, - baseAmount: product.prices?.retailPrice?.value, - currencyCode: product.prices?.price?.currencyCode!, + amount: product.price.value, + baseAmount: product.price.retailValue, + currencyCode: product.price.currencyCode!, }) const { openSidebar } = useUI() const options = getProductOptions(product) @@ -38,15 +38,15 @@ const ProductView: FC<Props> = ({ product }) => { size: null, color: null, }) - const variant = - getCurrentVariant(product, choices) || product.variants.edges?.[0] + + const variant = getCurrentVariant(product, choices) || product.variants[0] const addToCart = async () => { setLoading(true) try { await addItem({ - productId: product.entityId, - variantId: product.variants.edges?.[0]?.node.entityId!, + productId: Number(product.id), + variantId: Number(product.variants[0].id), }) openSidebar() setLoading(false) @@ -66,7 +66,7 @@ const ProductView: FC<Props> = ({ product }) => { description: product.description, images: [ { - url: product.images.edges?.[0]?.node.urlOriginal!, + url: product.images[0]?.url!, width: 800, height: 600, alt: product.name, @@ -81,18 +81,18 @@ const ProductView: FC<Props> = ({ product }) => { <div className={s.price}> {price} {` `} - {product.prices?.price.currencyCode} + {product.price?.currencyCode} </div> </div> <div className={s.sliderContainer}> - <ProductSlider key={product.entityId}> - {product.images.edges?.map((image, i) => ( - <div key={image?.node.urlOriginal} className={s.imageContainer}> + <ProductSlider key={product.id}> + {product.images.map((image, i) => ( + <div key={image.url} className={s.imageContainer}> <Image className={s.img} - src={image?.node.urlOriginal!} - alt={image?.node.altText || 'Product Image'} + src={image.url!} + alt={image.alt || 'Product Image'} width={1050} height={1050} priority={i === 0} @@ -152,11 +152,10 @@ const ProductView: FC<Props> = ({ product }) => { </Button> </div> </div> - <WishlistButton className={s.wishlistButton} - productId={product.entityId} - variant={product.variants.edges?.[0]!} + productId={product.id} + variant={product.variants[0]!} /> </div> </Container> diff --git a/framework/bigcommerce/api/operations/get-all-products.ts b/framework/bigcommerce/api/operations/get-all-products.ts index 036602639..7c91c4f1d 100644 --- a/framework/bigcommerce/api/operations/get-all-products.ts +++ b/framework/bigcommerce/api/operations/get-all-products.ts @@ -34,12 +34,10 @@ function productsNormalizer(arr: any[]): Product[] { ), variants: variants.edges.map(({ node }: any) => node), productOptions: productOptions.edges.map(({ node }: any) => node), - prices: [ - { - value: prices.price.value, - currencyCode: prices.price.currencyCode, - }, - ], + price: { + value: prices.price.value, + currencyCode: prices.price.currencyCode, + }, ...rest, }) ) diff --git a/framework/types.d.ts b/framework/types.d.ts index 8958a9a04..87347da7b 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -6,7 +6,7 @@ interface Product { path?: string images: ProductImage[] variants: ProductVariant[] - prices: ProductPrice[] + price: ProductPrice } interface ProductImage { url: string @@ -18,9 +18,10 @@ interface ProductVariant { } interface ProductPrice { - value: number | string + value: number currencyCode: 'USD' | 'ARS' - type?: 'price' | 'retail' | 'sale' | string + retailValue?: number + saleValue?: number } interface Cart { diff --git a/pages/index.tsx b/pages/index.tsx index e194366c4..4b76134c7 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -41,7 +41,7 @@ export default function Home({ categories, }: InferGetStaticPropsType<typeof getStaticProps>) { return ( - <div> + <> <Grid> {products.slice(0, 3).map((product, i) => ( <ProductCard @@ -102,12 +102,12 @@ export default function Home({ /> ))} </Marquee> - {/* <HomeAllProductsGrid - newestProducts={newestProducts} + <HomeAllProductsGrid + newestProducts={products} categories={categories} brands={brands} - /> */} - </div> + /> + </> ) } From ac58e4a35108b5312a133ec5404109453e32844e Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Sun, 10 Jan 2021 16:16:49 -0300 Subject: [PATCH 018/221] Normalizer --- .../api/operations/get-all-products.ts | 38 +------------------ .../bigcommerce/api/operations/get-product.ts | 6 ++- framework/bigcommerce/lib/normalize.ts | 33 ++++++++++++++++ framework/types.d.ts | 24 ++++++------ pages/product/[slug].tsx | 2 - 5 files changed, 52 insertions(+), 51 deletions(-) create mode 100644 framework/bigcommerce/lib/normalize.ts diff --git a/framework/bigcommerce/api/operations/get-all-products.ts b/framework/bigcommerce/api/operations/get-all-products.ts index 7c91c4f1d..599109a7d 100644 --- a/framework/bigcommerce/api/operations/get-all-products.ts +++ b/framework/bigcommerce/api/operations/get-all-products.ts @@ -7,41 +7,7 @@ import filterEdges from '../utils/filter-edges' import setProductLocaleMeta from '../utils/set-product-locale-meta' import { productConnectionFragment } from '../fragments/product' import { BigcommerceConfig, getConfig } from '..' - -function productsNormalizer(arr: any[]): Product[] { - // Normalizes products arr response and flattens node edges - return arr.map( - ({ - node: { - entityId: id, - images, - variants, - productOptions, - prices, - path, - ...rest - }, - }) => ({ - id, - path, - slug: path.slice(1, -1), - images: images.edges.map( - ({ node: { urlOriginal, altText, ...rest } }: any) => ({ - url: urlOriginal, - alt: altText, - ...rest, - }) - ), - variants: variants.edges.map(({ node }: any) => node), - productOptions: productOptions.edges.map(({ node }: any) => node), - price: { - value: prices.price.value, - currencyCode: prices.price.currencyCode, - }, - ...rest, - }) - ) -} +import { normalizeProduct } from '../../lib/normalize' export const getAllProductsQuery = /* GraphQL */ ` query getAllProducts( @@ -161,7 +127,7 @@ async function getAllProducts({ }) } - return { products: productsNormalizer(products) } + return { products: products.map(normalizeProduct) } } export default getAllProducts diff --git a/framework/bigcommerce/api/operations/get-product.ts b/framework/bigcommerce/api/operations/get-product.ts index e75e87607..50efb97aa 100644 --- a/framework/bigcommerce/api/operations/get-product.ts +++ b/framework/bigcommerce/api/operations/get-product.ts @@ -2,6 +2,7 @@ import type { GetProductQuery, GetProductQueryVariables } from '../../schema' import setProductLocaleMeta from '../utils/set-product-locale-meta' import { productInfoFragment } from '../fragments/product' import { BigcommerceConfig, getConfig } from '..' +import { normalizeProduct } from '@framework/lib/normalize' export const getProductQuery = /* GraphQL */ ` query getProduct( @@ -92,7 +93,7 @@ async function getProduct({ variables: ProductVariables config?: BigcommerceConfig preview?: boolean -}): Promise<GetProductResult> { +}): Promise<Product | {}> { config = getConfig(config) const locale = vars.locale || config.locale @@ -109,7 +110,8 @@ async function getProduct({ if (locale && config.applyLocale) { setProductLocaleMeta(product) } - return { product } + + return { product: normalizeProduct(product as any) } } return {} diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts new file mode 100644 index 000000000..f3a316319 --- /dev/null +++ b/framework/bigcommerce/lib/normalize.ts @@ -0,0 +1,33 @@ +import { ProductNode } from '@framework/api/operations/get-all-products' + +export function normalizeProduct(productNode: ProductNode | any): Product { + const { + entityId: id, + images, + variants, + productOptions, + prices, + path, + ...rest + } = productNode + + return { + id, + path, + slug: path.slice(1, -1), + images: images.edges?.map( + ({ node: { urlOriginal, altText, ...rest } }: any) => ({ + url: urlOriginal, + alt: altText, + ...rest, + }) + ), + variants: variants?.edges?.map(({ node }: any) => node), + productOptions: productOptions?.edges?.map(({ node }: any) => node), + price: { + value: prices?.price.value, + currencyCode: prices?.price.currencyCode, + }, + ...rest, + } +} diff --git a/framework/types.d.ts b/framework/types.d.ts index 87347da7b..6d4e573fc 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -1,11 +1,15 @@ -interface Product { +interface Entity { id: string | number + [prop: string]: any +} + +interface Product extends Entity { name: string description: string slug: string path?: string - images: ProductImage[] - variants: ProductVariant[] + images: ProductImage[] | any[] | undefined + variants: ProductVariant[] | any[] | undefined price: ProductPrice } interface ProductImage { @@ -19,25 +23,23 @@ interface ProductVariant { interface ProductPrice { value: number - currencyCode: 'USD' | 'ARS' + currencyCode: 'USD' | 'ARS' | string | undefined retailValue?: number saleValue?: number } -interface Cart { +interface Cart extends Entity { id: string products: Pick<Product, 'id' | 'name' | 'prices'>[] } -interface Wishlist { - id: string +interface Wishlist extends Entity { products: Pick<Product, 'id' | 'name' | 'prices'>[] } interface Order {} -interface Customer { - id: string | number | undefined +interface Customer extends Entity { [prop: string]: any } @@ -45,12 +47,12 @@ type UseCustomerResponse = { customer: Customer } | null -interface Category { +interface Category extends Entity { id: string name: string } -interface Brand { +interface Brand extends Entity { id: string name: string } diff --git a/pages/product/[slug].tsx b/pages/product/[slug].tsx index 008cd95d6..4f17dcbad 100644 --- a/pages/product/[slug].tsx +++ b/pages/product/[slug].tsx @@ -7,8 +7,6 @@ import { useRouter } from 'next/router' import { Layout } from '@components/common' import { ProductView } from '@components/product' -// Data - import { getConfig } from '@framework/api' import getProduct from '@framework/api/operations/get-product' import getAllPages from '@framework/api/operations/get-all-pages' From def1cf07787c64bd6e793d3b3daa947860ca2117 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Sun, 10 Jan 2021 16:21:09 -0300 Subject: [PATCH 019/221] Normalizing more operations --- README.md | 2 ++ framework/bigcommerce/api/operations/get-product.ts | 2 +- framework/bigcommerce/product/index.ts | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e944c7569..8410f5641 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,8 @@ Main folder and its exposed functions - `product` - usePrice - useSearch + - getProduct + - getAllProducts - `wishlist` - useWishlist - addWishlistItem diff --git a/framework/bigcommerce/api/operations/get-product.ts b/framework/bigcommerce/api/operations/get-product.ts index 50efb97aa..2476d1398 100644 --- a/framework/bigcommerce/api/operations/get-product.ts +++ b/framework/bigcommerce/api/operations/get-product.ts @@ -111,7 +111,7 @@ async function getProduct({ setProductLocaleMeta(product) } - return { product: normalizeProduct(product as any) } + return { product: normalizeProduct(product) } } return {} diff --git a/framework/bigcommerce/product/index.ts b/framework/bigcommerce/product/index.ts index 426a3edcd..83d507a2c 100644 --- a/framework/bigcommerce/product/index.ts +++ b/framework/bigcommerce/product/index.ts @@ -1,2 +1,4 @@ export { default as usePrice } from './use-price' export { default as useSearch } from './use-search' +export { default as getProduct } from '../api/operations/get-product' +export { default as getAllProducts } from '../api/operations/get-all-products' From 1742ae8ea617755b2ac553fd8cfa073dbbfcba32 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Sun, 10 Jan 2021 16:25:25 -0300 Subject: [PATCH 020/221] Normalizing more operations --- components/product/ProductCard/ProductCard.tsx | 6 +++--- framework/types.d.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index 47c000032..d60f3e937 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -25,7 +25,7 @@ const ProductCard: FC<Props> = ({ className, product, variant, imgProps }) => { {product.name} </span> </div> - {product.images[0] && ( + {product.images?[0] && ( <Image quality="85" alt={product.name} @@ -54,11 +54,11 @@ const ProductCard: FC<Props> = ({ className, product, variant, imgProps }) => { <WishlistButton className={s.wishlistButton} productId={product.id} - variant={product.variants[0]!} + variant={product.variants?[0]} /> </div> <div className={s.imageContainer}> - {product.images[0] && ( + {product.images?[0] && ( <Image alt={product.name} className={s.productImage} diff --git a/framework/types.d.ts b/framework/types.d.ts index 6d4e573fc..1342071dd 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -9,7 +9,7 @@ interface Product extends Entity { slug: string path?: string images: ProductImage[] | any[] | undefined - variants: ProductVariant[] | any[] | undefined + variants: ProductVariant[] | any[] | null | undefined price: ProductPrice } interface ProductImage { From e42c511e3dd020502653db4f26ea22522ec5546a Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Mon, 11 Jan 2021 12:12:51 -0300 Subject: [PATCH 021/221] changes --- .../product/ProductCard/ProductCard.tsx | 18 +- components/ui/Marquee/Marquee.tsx | 4 +- .../WishlistButton/WishlistButton.tsx | 2 +- framework/bigcommerce/lib/normalize.ts | 26 +- .../scripts/generate-definitions.js | 49 + package.json | 8 +- pages/index.tsx | 4 +- pages/{index copy.tsx => index2.tsx} | 0 yarn.lock | 1233 +++++++---------- 9 files changed, 570 insertions(+), 774 deletions(-) create mode 100644 framework/bigcommerce/scripts/generate-definitions.js rename pages/{index copy.tsx => index2.tsx} (100%) diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index d60f3e937..863a3b2b8 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -12,9 +12,15 @@ interface Props { imgProps?: Omit<ImageProps, 'src'> } -const ProductCard: FC<Props> = ({ className, product, variant, imgProps }) => { +const ProductCard: FC<Props> = ({ + className, + product, + variant, + imgProps, + ...props +}) => { return ( - <Link href={`product/${product.slug}`}> + <Link href={`product/${product.slug}`} {...props}> <a className={cn(s.root, { [s.simple]: variant === 'simple' }, className)} > @@ -25,11 +31,11 @@ const ProductCard: FC<Props> = ({ className, product, variant, imgProps }) => { {product.name} </span> </div> - {product.images?[0] && ( + {product?.images && ( <Image quality="85" alt={product.name} - src={product.images[0].url} + src={product.images[0].url!} height={320} width={320} layout="fixed" @@ -54,11 +60,11 @@ const ProductCard: FC<Props> = ({ className, product, variant, imgProps }) => { <WishlistButton className={s.wishlistButton} productId={product.id} - variant={product.variants?[0]} + variant={product.variant[0]!} /> </div> <div className={s.imageContainer}> - {product.images?[0] && ( + {product?.images && ( <Image alt={product.name} className={s.productImage} diff --git a/components/ui/Marquee/Marquee.tsx b/components/ui/Marquee/Marquee.tsx index 09f562011..163f29a34 100644 --- a/components/ui/Marquee/Marquee.tsx +++ b/components/ui/Marquee/Marquee.tsx @@ -9,7 +9,7 @@ interface Props { variant?: 'primary' | 'secondary' } -const Maquee: FC<Props> = ({ +const Marquee: FC<Props> = ({ className = '', children, variant = 'primary', @@ -32,4 +32,4 @@ const Maquee: FC<Props> = ({ ) } -export default Maquee +export default Marquee diff --git a/components/wishlist/WishlistButton/WishlistButton.tsx b/components/wishlist/WishlistButton/WishlistButton.tsx index db9ca4b67..aca0ad148 100644 --- a/components/wishlist/WishlistButton/WishlistButton.tsx +++ b/components/wishlist/WishlistButton/WishlistButton.tsx @@ -47,7 +47,7 @@ const WishlistButton: FC<Props> = ({ } else { await addItem({ productId, - variantId: variant?.id, + variantId: variant?.id!, }) } diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index f3a316319..7d938c643 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -1,21 +1,23 @@ -import { ProductNode } from '@framework/api/operations/get-all-products' +import { ProductEdge } from '@framework/schema' -export function normalizeProduct(productNode: ProductNode | any): Product { +export function normalizeProduct(productNode: ProductEdge): Product { + // console.log(productNode) const { - entityId: id, - images, - variants, - productOptions, - prices, - path, - ...rest + node: { + entityId: id, + images, + variants, + productOptions, + prices, + path, + ...rest + }, } = productNode return { - id, path, - slug: path.slice(1, -1), - images: images.edges?.map( + slug: path?.slice(1, -1), + images: images?.edges?.map( ({ node: { urlOriginal, altText, ...rest } }: any) => ({ url: urlOriginal, alt: altText, diff --git a/framework/bigcommerce/scripts/generate-definitions.js b/framework/bigcommerce/scripts/generate-definitions.js new file mode 100644 index 000000000..a2b830d3b --- /dev/null +++ b/framework/bigcommerce/scripts/generate-definitions.js @@ -0,0 +1,49 @@ +/** + * Generates definitions for REST API endpoints that are being + * used by ../api using https://github.com/drwpow/swagger-to-ts + */ +const { readFileSync, promises } = require('fs') +const path = require('path') +const fetch = require('node-fetch') +const swaggerToTS = require('@manifoldco/swagger-to-ts').default + +async function getSchema(filename) { + const url = `https://next-api.stoplight.io/projects/8433/files/${filename}` + const res = await fetch(url) + + if (!res.ok) { + throw new Error(`Request failed with ${res.status}: ${res.statusText}`) + } + + return res.json() +} + +const schemas = Object.entries({ + '../api/definitions/catalog.ts': + 'BigCommerce_Catalog_API.oas2.yml?ref=version%2F20.930', + '../api/definitions/store-content.ts': + 'BigCommerce_Store_Content_API.oas2.yml?ref=version%2F20.930', + '../api/definitions/wishlist.ts': + 'BigCommerce_Wishlist_API.oas2.yml?ref=version%2F20.930', + // swagger-to-ts is not working for the schema of the cart API + // '../api/definitions/cart.ts': + // 'BigCommerce_Server_to_Server_Cart_API.oas2.yml', +}) + +async function writeDefinitions() { + const ops = schemas.map(async ([dest, filename]) => { + const destination = path.join(__dirname, dest) + const schema = await getSchema(filename) + const definition = swaggerToTS(schema.content, { + prettierConfig: 'package.json', + }) + + await promises.writeFile(destination, definition) + + console.log(`✔️ Added definitions for: ${dest}`) + }) + + await Promise.all(ops) +} + +writeDefinitions() diff --git a/package.json b/package.json index 35e8d5cb5..dd78af9ba 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "analyze": "BUNDLE_ANALYZE=both yarn build", "find:unused": "next-unused", "generate": "graphql-codegen", - "generate:definitions": "node framework/bigcommerce/lib/generate-definitions.js" + "generate:definitions": "node framework/bigcommerce/scripts/generate-definitions.js" }, "prettier": { "semi": false, @@ -46,7 +46,9 @@ "dependencies": { "@reach/portal": "^0.11.2", "@tailwindcss/ui": "^0.6.2", + "@types/node-fetch": "2", "@vercel/fetch": "^6.1.0", + "@vercel/fetch-cached-dns": "^2.0.1", "body-scroll-lock": "^3.1.5", "bowser": "^2.11.0", "classnames": "^2.2.6", @@ -60,12 +62,14 @@ "next": "^10.0.5-canary.11", "next-seo": "^4.11.0", "next-themes": "^0.0.4", + "node-fetch": "^2.6.1", "postcss-nesting": "^7.0.1", "react": "^16.14.0", "react-dom": "^16.14.0", "react-merge-refs": "^1.1.0", "react-ticker": "^1.2.2", - "swr": "^0.3.11", + "swr": "^0.4.0", + "tabbable": "^5.1.5", "tailwindcss": "^1.9" }, "devDependencies": { diff --git a/pages/index.tsx b/pages/index.tsx index 4b76134c7..d3f3f6ba5 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -102,11 +102,11 @@ export default function Home({ /> ))} </Marquee> - <HomeAllProductsGrid + {/* <HomeAllProductsGrid newestProducts={products} categories={categories} brands={brands} - /> + /> */} </> ) } diff --git a/pages/index copy.tsx b/pages/index2.tsx similarity index 100% rename from pages/index copy.tsx rename to pages/index2.tsx diff --git a/yarn.lock b/yarn.lock index 2cd6e3ee4..29227fbb8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -88,7 +88,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.12.1", "@babel/generator@^7.12.10", "@babel/generator@^7.12.11", "@babel/generator@^7.5.0": +"@babel/generator@^7.12.10", "@babel/generator@^7.12.11", "@babel/generator@^7.5.0": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.11.tgz#98a7df7b8c358c9a37ab07a24056853016aba3af" integrity sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA== @@ -212,12 +212,7 @@ dependencies: "@babel/types" "^7.12.11" -"@babel/helper-validator-identifier@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" - integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== - -"@babel/helper-validator-identifier@^7.12.11": +"@babel/helper-validator-identifier@^7.10.4", "@babel/helper-validator-identifier@^7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== @@ -240,17 +235,7 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@7.11.5": - version "7.11.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" - integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== - -"@babel/parser@^7.0.0": - version "7.12.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.5.tgz#b4af32ddd473c0bfa643bd7ff0728b8e71b81ea0" - integrity sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ== - -"@babel/parser@^7.12.1", "@babel/parser@^7.12.10", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7": +"@babel/parser@7.12.11", "@babel/parser@^7.0.0", "@babel/parser@^7.12.0", "@babel/parser@^7.12.10", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.11.tgz#9ce3595bcd74bc5c466905e86c535b8b25011e79" integrity sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg== @@ -474,22 +459,7 @@ "@babel/parser" "^7.12.7" "@babel/types" "^7.12.7" -"@babel/traverse@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.1.tgz#941395e0c5cc86d5d3e75caa095d3924526f0c1e" - integrity sha512-MA3WPoRt1ZHo2ZmoGKNqi20YnPt0B1S0GTZEPhhd+hw2KGUzBlHuVunj6K4sNuK+reEvyiPwtp0cpaqLzJDmAw== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.12.1" - "@babel/helper-function-name" "^7.10.4" - "@babel/helper-split-export-declaration" "^7.11.0" - "@babel/parser" "^7.12.1" - "@babel/types" "^7.12.1" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.19" - -"@babel/traverse@^7.0.0", "@babel/traverse@^7.12.1", "@babel/traverse@^7.12.10", "@babel/traverse@^7.12.5": +"@babel/traverse@7.12.12", "@babel/traverse@^7.0.0", "@babel/traverse@^7.12.1", "@babel/traverse@^7.12.10", "@babel/traverse@^7.12.5": version "7.12.12" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.12.tgz#d0cd87892704edd8da002d674bc811ce64743376" integrity sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w== @@ -504,12 +474,12 @@ globals "^11.1.0" lodash "^4.17.19" -"@babel/types@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.1.tgz#e109d9ab99a8de735be287ee3d6a9947a190c4ae" - integrity sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA== +"@babel/types@7.12.12", "@babel/types@^7.0.0", "@babel/types@^7.10.5", "@babel/types@^7.12.0", "@babel/types@^7.12.1", "@babel/types@^7.12.10", "@babel/types@^7.12.11", "@babel/types@^7.12.12", "@babel/types@^7.12.5", "@babel/types@^7.12.7": + version "7.12.12" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.12.tgz#4608a6ec313abbd87afa55004d373ad04a96c299" + integrity sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ== dependencies: - "@babel/helper-validator-identifier" "^7.10.4" + "@babel/helper-validator-identifier" "^7.12.11" lodash "^4.17.19" to-fast-properties "^2.0.0" @@ -522,15 +492,6 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@babel/types@^7.0.0", "@babel/types@^7.10.5", "@babel/types@^7.12.1", "@babel/types@^7.12.10", "@babel/types@^7.12.11", "@babel/types@^7.12.12", "@babel/types@^7.12.5", "@babel/types@^7.12.7": - version "7.12.12" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.12.tgz#4608a6ec313abbd87afa55004d373ad04a96c299" - integrity sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ== - dependencies: - "@babel/helper-validator-identifier" "^7.12.11" - lodash "^4.17.19" - to-fast-properties "^2.0.0" - "@csstools/convert-colors@^1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7" @@ -653,19 +614,19 @@ tslib "~2.0.1" "@graphql-codegen/typescript@^1.18.1", "@graphql-codegen/typescript@^1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-1.19.0.tgz#05b1b4502b91dee53ec4f0ae39e9537eff004c5c" - integrity sha512-ThpMdtb6LmEKzLNxlotpk33eIMfX1i1aAlYDaqctIhFjZ2Rbvkgdb8q/5/my7yeH33BLnuqHDKrdkDsAAvCHcw== + version "1.20.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-1.20.0.tgz#f9a17b869e5691276965a56c7a1efe4eb938b6e7" + integrity sha512-7xW+n0USNpr6iZ4Et17ZbPzBLNe/LrSgrQ6V/8Mlgp1reQWAZtoVw13Oq4GnxHCzAYio8nFindLl+emW9ZBeew== dependencies: "@graphql-codegen/plugin-helpers" "^1.18.2" - "@graphql-codegen/visitor-plugin-common" "^1.17.21" + "@graphql-codegen/visitor-plugin-common" "^1.18.0" auto-bind "~4.0.0" tslib "~2.0.1" -"@graphql-codegen/visitor-plugin-common@^1.17.21", "@graphql-codegen/visitor-plugin-common@^1.17.22": - version "1.17.22" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.17.22.tgz#5321077ea54b423397b659406a2b60d1a3b44156" - integrity sha512-5+fkcET7ftqexyEkGWvmalL+RTi1ApfAt9k9FEmM2WdRXi/uYwwYb+qHwIoaMVD0pXKeUAB5+Qgs9+Md4VIM7Q== +"@graphql-codegen/visitor-plugin-common@^1.17.22", "@graphql-codegen/visitor-plugin-common@^1.18.0": + version "1.18.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.18.0.tgz#f4366ec1093c01e752e85f8bd30d09c58b1d6951" + integrity sha512-OR/Cm9nVaqMLe94Ski60UDhaAf/4+QeV/CQRYrCmxFzEsm03U41VplcNgJBXIAB3EgP/LEIZtgkRc1H4N9u9+Q== dependencies: "@graphql-codegen/plugin-helpers" "^1.18.2" "@graphql-tools/optimize" "^1.0.1" @@ -748,17 +709,17 @@ tslib "~2.0.1" "@graphql-tools/graphql-tag-pluck@^6.2.6": - version "6.3.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-6.3.0.tgz#b1c178fe6e8c4823ca611cf1392f530ad0490dd9" - integrity sha512-wdXE6iKTD/ePvhPaukhXm6M8FcsiR9rrwFvkYN96sx2UjDjXzU6vS1QUniNuwjRPaQuSe635vqfaUSN9JuNHvA== + version "6.4.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-6.4.0.tgz#f8303606f717174a1068501294805b3304417145" + integrity sha512-qBQ0ITgMkUzxz5QsZk9HsSeOQo6KlXd/Ex5cePzPuRT72odPEQXNL9hCsxaCvZO9r3in8bmG6OTWs4ms/GevXA== dependencies: - "@babel/parser" "7.11.5" - "@babel/traverse" "7.12.1" - "@babel/types" "7.12.1" + "@babel/parser" "7.12.11" + "@babel/traverse" "7.12.12" + "@babel/types" "7.12.12" "@graphql-tools/utils" "^7.0.0" - tslib "~2.0.1" + tslib "~2.1.0" optionalDependencies: - vue-template-compiler "^2.6.12" + "@vue/compiler-sfc" "^3.0.4" "@graphql-tools/import@^6.2.5": version "6.2.5" @@ -886,13 +847,13 @@ tslib "~2.0.1" "@graphql-tools/utils@^7.0.0", "@graphql-tools/utils@^7.1.0", "@graphql-tools/utils@^7.1.2", "@graphql-tools/utils@^7.1.5", "@graphql-tools/utils@^7.1.6", "@graphql-tools/utils@^7.2.1": - version "7.2.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-7.2.3.tgz#4bb2ae0bef62df1f342f2a769434fbb105dd0d84" - integrity sha512-9MvSKeo+8DM72706FvrUP8figQjRzSwBswWrXviyWyt3wSkk6MU2cURQKfMpc0I6nswZvkDSqYoQQ/6mazoXxA== + version "7.2.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-7.2.4.tgz#1164cf268988254f281b4cfbbc0e8f7ca24a8a41" + integrity sha512-EDSb98dTWX8FngvayWejip1DutOl0wGtNbXC7a3CZf5fiJS7bGHQ/8cSlMhe9XaHwpLJCbAk/Ijnp/dYbXk33w== dependencies: "@ardatan/aggregate-error" "0.0.6" camel-case "4.1.2" - tslib "~2.0.1" + tslib "~2.1.0" "@graphql-tools/wrap@^7.0.4": version "7.0.5" @@ -914,16 +875,16 @@ "@hapi/hoek" "9.x.x" "@hapi/boom@9.x.x": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-9.1.0.tgz#0d9517657a56ff1e0b42d0aca9da1b37706fec56" - integrity sha512-4nZmpp4tXbm162LaZT45P7F7sgiem8dwAh2vHWT6XX24dozNjGMg6BvKCRvtCUcmcXqeMIUqWN8Rc5X8yKuROQ== + version "9.1.1" + resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-9.1.1.tgz#89e6f0e01637c2a4228da0d113e8157c93677b04" + integrity sha512-VNR8eDbBrOxBgbkddRYIe7+8DZ+vSbV6qlmaN2x7eWjsUjy2VmQgChkOKcVZIeupEZYj+I0dqNg430OhwzagjA== dependencies: "@hapi/hoek" "9.x.x" "@hapi/hoek@9.x.x": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.1.0.tgz#6c9eafc78c1529248f8f4d92b0799a712b6052c6" - integrity sha512-i9YbZPN3QgfighY/1X1Pu118VUz2Fmmhd6b2n0/O8YVgGGfw0FbUYoA97k7FkpGJ+pLCFEDLUmAPPV4D1kpeFw== + version "9.1.1" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.1.1.tgz#9daf5745156fd84b8e9889a2dc721f0c58e894aa" + integrity sha512-CAEbWH7OIur6jEOzaai83jq3FmKmv4PmX1JYfs9IrYcGEVI/lyL1EXJGCj7eFVJ0bg5QR8LMxBlEtA+xKiLpFw== "@iarna/toml@^2.2.5": version "2.2.5" @@ -941,26 +902,26 @@ prettier "^2.0.5" "@next/bundle-analyzer@^10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-10.0.1.tgz#4bcb376c1163b9e6e2866e1112bc848194648dce" - integrity sha512-1UMWnaeZSm+HQTqkje5ToqzCO3kXh7tqeJQBRjWz4+bbTdZEWPO8JPB/JFNpckU3FzYcnG6oeeYmUzHufZp3sg== + version "10.0.5" + resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-10.0.5.tgz#02eda5d88bc1ee8efff03902083bcb2c537e787d" + integrity sha512-fDhursKrqycV7u6crESINKQKp5/Q17Xd9mI1n0BFhIvpfp8br/gSqLHeaN2DXfcOS/Rb0/FmY4pdIFjXnwdZbg== dependencies: - webpack-bundle-analyzer "3.6.1" + webpack-bundle-analyzer "4.3.0" -"@next/env@10.0.5-canary.11": - version "10.0.5-canary.11" - resolved "https://registry.yarnpkg.com/@next/env/-/env-10.0.5-canary.11.tgz#76daeda2bd0c9f040892f299864091c37c946bb9" - integrity sha512-afx6ECY9pqPLvVOteA1gi0pZ6CuLPSSx5QNyKX+m9v0sp+idEIjgFtghI+HU+hz7S9C7EP0kGw3wnY1/qdK0bw== +"@next/env@10.0.5": + version "10.0.5" + resolved "https://registry.yarnpkg.com/@next/env/-/env-10.0.5.tgz#446e59ee7a8d05061be784b24732c369653038ab" + integrity sha512-dw6Q7PALIo7nTFfaB4OnRDte3EikrApGtjX/4cRmYXLh+uudDI50aS39MaGeKKZ2mxPKoNiFcY6Slv1f6KIPOw== -"@next/polyfill-module@10.0.5-canary.11": - version "10.0.5-canary.11" - resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-10.0.5-canary.11.tgz#e6e53351b1bdd2ce619a4e29df191b1c8633d232" - integrity sha512-MWsioa2Kyx/p8kDsB0Y6fCley7/S8Z+vUG4ixUteR+OWibZSQrtqGs47lCJyfvdu69s75uzSlXNybbTWzcMguA== +"@next/polyfill-module@10.0.5": + version "10.0.5" + resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-10.0.5.tgz#f2de3deee8694cc75094fea4e91225724b3a21e1" + integrity sha512-R6ySTTIl6yqyO3Xft7h+QR9Z4e6epEw+AGO125CrwPmCDQ8ASLGltsHYvSHBP7Eae7xaorkXHW0jeI8NewLpew== -"@next/react-dev-overlay@10.0.5-canary.11": - version "10.0.5-canary.11" - resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-10.0.5-canary.11.tgz#c897d12eb0b1c8df065dc2a72ba7aeff5049b566" - integrity sha512-6Zt3wWhEVzGr9i8GUzCvDeY7qTb/u6Svnaan3uvY1cVtkghMa8qvGYqG45URecD8VhGBo9dI8Bnl9vYE4yj2wA== +"@next/react-dev-overlay@10.0.5": + version "10.0.5" + resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-10.0.5.tgz#f0006e56d8c8b4269f341ff22233e4a7e1176b52" + integrity sha512-olqIaaRvFezzi02V/AYwvmrGbShUNrJDvyZTGNahxXEkiYsuu67llY5IKFM5Oya93/eRqaCCKMn89vpvYtvDcw== dependencies: "@babel/code-frame" "7.12.11" anser "1.4.9" @@ -974,10 +935,10 @@ stacktrace-parser "0.1.10" strip-ansi "6.0.0" -"@next/react-refresh-utils@10.0.5-canary.11": - version "10.0.5-canary.11" - resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-10.0.5-canary.11.tgz#d3e885bfb0175f887adf4d927df0f6852a066a77" - integrity sha512-W/JNmDVnGeziaRkcCa94Oj0izOCWv4XGIKRNkTnQrtAWlZ1sIq5eijkJSJRphfBt9sShKUc90LOWCjtoEPpBwQ== +"@next/react-refresh-utils@10.0.5": + version "10.0.5" + resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-10.0.5.tgz#c1ca664c4ffe2f79d14383323d783368833d503b" + integrity sha512-Eo+nvW1ZhdkuxBWSsKHGDLNspUaJJQClld9R+H+EtiIuAQtTa8f2rhcQeyUOtvUNQoPyq7Em2PwUqOQD6LOOMA== "@nodelib/fs.scandir@2.1.4": version "2.1.4" @@ -1012,6 +973,11 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/context-base/-/context-base-0.14.0.tgz#c67fc20a4d891447ca1a855d7d70fa79a3533001" integrity sha512-sDOAZcYwynHFTbLo6n8kIbLiVF3a3BLkrmehJUyEbT9F+Smbi47kLGS2gG2g0fjBLR/Lr1InPD7kXL7FaTqEkw== +"@polka/url@^1.0.0-next.9": + version "1.0.0-next.11" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.11.tgz#aeb16f50649a91af79dbe36574b66d0f9e4d9f71" + integrity sha512-3NsZsJIA/22P3QUyrEDNA2D133H4j224twJrdipXN38dpnIOzAbUDtOwkcJ5pXmn75w7LSQDjA4tO9dm1XlqlA== + "@reach/portal@^0.11.2": version "0.11.2" resolved "https://registry.yarnpkg.com/@reach/portal/-/portal-0.11.2.tgz#19a671be9ff010a345892b81e710cb6e4d9f9762" @@ -1102,9 +1068,9 @@ "@types/node" "*" "@types/classnames@^2.2.10": - version "2.2.10" - resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.10.tgz#cc658ca319b6355399efc1f5b9e818f1a24bf999" - integrity sha512-1UzDldn9GfYYEsWWnn/P4wkTlkZDH7lDb0wBMGbtIQc9zXEQq7FlKBdZUn6OBqD8sKZZ2RQO2mAjGpXiDGoRmQ== + version "2.2.11" + resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.11.tgz#2521cc86f69d15c5b90664e4829d84566052c1cf" + integrity sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw== "@types/cookie@^0.4.0": version "0.4.0" @@ -1120,9 +1086,9 @@ "@types/estree" "*" "@types/eslint@*": - version "7.2.4" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.4.tgz#d12eeed7741d2491b69808576ac2d20c14f74c41" - integrity sha512-YCY4kzHMsHoyKspQH+nwSe+70Kep7Vjt2X+dZe5Vs2vkRudqtoFoUIv1RlJmZB8Hbp7McneupoZij4PadxsK5Q== + version "7.2.6" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.6.tgz#5e9aff555a975596c03a98b59ecd103decc70c3c" + integrity sha512-I+1sYH+NPQ3/tVqCeUSBwTE/0heyvtXqpIopUUArlBm0Kpocb8FbMa3AZ/ASKIFpN3rnEx932TTXDbt9OXsNDw== dependencies: "@types/estree" "*" "@types/json-schema" "*" @@ -1145,9 +1111,9 @@ integrity sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw== "@types/js-yaml@^3.12.5": - version "3.12.5" - resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.5.tgz#136d5e6a57a931e1cce6f9d8126aa98a9c92a6bb" - integrity sha512-JCcp6J0GV66Y4ZMDAQCXot4xprYB+Zfd3meK9+INSJeVZwJmHAW30BBEEkPzXswMXuiyReUGOP3GxrADc9wPww== + version "3.12.6" + resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.6.tgz#7f10c926aa41e189a2755c4c7fcf8e4573bd7ac1" + integrity sha512-cK4XqrLvP17X6c0C8n4iTbT59EixqyXL3Fk8/Rsk4dF3oX4dg70gYUXrXVUUHpnsGMPNlTQMqf+TVmNPX6FmSQ== "@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6": version "7.0.6" @@ -1188,9 +1154,9 @@ "@types/lodash" "*" "@types/lodash@*": - version "4.14.162" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.162.tgz#65d78c397e0d883f44afbf1f7ba9867022411470" - integrity sha512-alvcho1kRUnnD1Gcl4J+hK0eencvzq9rmzvFPRmP5rPHx9VVsJj6bKLTATPVf9ktgv4ujzh7T+XWKp+jhuODig== + version "4.14.167" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.167.tgz#ce7d78553e3c886d4ea643c37ec7edc20f16765e" + integrity sha512-w7tQPjARrvdeBkX/Rwg95S592JwxqOjmms3zWQ0XZgSyxSLdzWaYH3vErBhdVS/lRBX7F8aBYcYJYTr5TMGOzw== "@types/lru-cache@4.1.1": version "4.1.1" @@ -1202,6 +1168,14 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.1.tgz#283f669ff76d7b8260df8ab7a4262cc83d988256" integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg== +"@types/node-fetch@2": + version "2.5.7" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c" + integrity sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + "@types/node-fetch@2.3.2": version "2.3.2" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.3.2.tgz#e01893b176c6fa1367743726380d65bce5d6576b" @@ -1209,21 +1183,16 @@ dependencies: "@types/node" "*" -"@types/node@*": - version "14.14.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.2.tgz#d25295f9e4ca5989a2c610754dc02a9721235eeb" - integrity sha512-jeYJU2kl7hL9U5xuI/BhKPZ4vqGM/OmK6whiFAXVhlstzZhVamWhDSmHyGLIp+RVyuF9/d0dqr2P85aFj4BvJg== +"@types/node@*", "@types/node@^14.14.16": + version "14.14.20" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.20.tgz#f7974863edd21d1f8a494a73e8e2b3658615c340" + integrity sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A== "@types/node@10.12.18": version "10.12.18" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== -"@types/node@^14.14.16": - version "14.14.16" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.16.tgz#3cc351f8d48101deadfed4c9e4f116048d437b4b" - integrity sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw== - "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" @@ -1299,6 +1268,60 @@ agentkeepalive "3.4.1" debug "3.1.0" +"@vue/compiler-core@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.0.5.tgz#a6e54cabe9536e74c6513acd2649f311af1d43ac" + integrity sha512-iFXwk2gmU/GGwN4hpBwDWWMLvpkIejf/AybcFtlQ5V1ur+5jwfBaV0Y1RXoR6ePfBPJixtKZ3PmN+M+HgMAtfQ== + dependencies: + "@babel/parser" "^7.12.0" + "@babel/types" "^7.12.0" + "@vue/shared" "3.0.5" + estree-walker "^2.0.1" + source-map "^0.6.1" + +"@vue/compiler-dom@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.0.5.tgz#7885a13e6d18f64dde8ebceec052ed2c102696c2" + integrity sha512-HSOSe2XSPuCkp20h4+HXSiPH9qkhz6YbW9z9ZtL5vef2T2PMugH7/osIFVSrRZP/Ul5twFZ7MIRlp8tPX6e4/g== + dependencies: + "@vue/compiler-core" "3.0.5" + "@vue/shared" "3.0.5" + +"@vue/compiler-sfc@^3.0.4": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.0.5.tgz#3ae08e60244a72faf9598361874fb7bdb5b1d37c" + integrity sha512-uOAC4X0Gx3SQ9YvDC7YMpbDvoCmPvP0afVhJoxRotDdJ+r8VO3q4hFf/2f7U62k4Vkdftp6DVni8QixrfYzs+w== + dependencies: + "@babel/parser" "^7.12.0" + "@babel/types" "^7.12.0" + "@vue/compiler-core" "3.0.5" + "@vue/compiler-dom" "3.0.5" + "@vue/compiler-ssr" "3.0.5" + "@vue/shared" "3.0.5" + consolidate "^0.16.0" + estree-walker "^2.0.1" + hash-sum "^2.0.0" + lru-cache "^5.1.1" + magic-string "^0.25.7" + merge-source-map "^1.1.0" + postcss "^7.0.32" + postcss-modules "^3.2.2" + postcss-selector-parser "^6.0.4" + source-map "^0.6.1" + +"@vue/compiler-ssr@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.0.5.tgz#7661ad891a0be948726c7f7ad1e425253c587b83" + integrity sha512-Wm//Kuxa1DpgjE4P9W0coZr8wklOfJ35Jtq61CbU+t601CpPTK4+FL2QDBItaG7aoUUDCWL5nnxMkuaOgzTBKg== + dependencies: + "@vue/compiler-dom" "3.0.5" + "@vue/shared" "3.0.5" + +"@vue/shared@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.0.5.tgz#c131d88bd6713cc4d93b3bb1372edb1983225ff0" + integrity sha512-gYsNoGkWejBxNO6SNRjOh/xKeZ0H0V+TFzaPzODfBjkAIb0aQgBuixC1brandC/CDJy1wYPwSoYrXpvul7m6yw== + "@webassemblyjs/ast@1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.1.tgz#76c6937716d68bf1484c15139f5ed30b9abc8bb4" @@ -1472,14 +1495,6 @@ abort-controller@3.0.0: dependencies: event-target-shim "^5.0.0" -accepts@~1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" - integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== - dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" - acorn-node@^1.6.1: version "1.8.2" resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" @@ -1489,12 +1504,17 @@ acorn-node@^1.6.1: acorn-walk "^7.0.0" xtend "^4.0.2" -acorn-walk@^7.0.0, acorn-walk@^7.1.1: +acorn-walk@^7.0.0: version "7.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn@^7.0.0, acorn@^7.1.1: +acorn-walk@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.0.1.tgz#d265d35db6940a656c715806a448456ee4fa3b7f" + integrity sha512-zn/7dYtoTVkG4EoMU55QlQU4F+m+T7Kren6Vj3C2DapWPnakG/DL9Ns5aPAPW5Ixd3uxXrV/BoMKKVFIazPcdg== + +acorn@^7.0.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== @@ -1640,11 +1660,6 @@ arity-n@^1.0.4: resolved "https://registry.yarnpkg.com/arity-n/-/arity-n-1.0.4.tgz#d9e76b11733e08569c0847ae7b39b2860b30b745" integrity sha1-2edrEXM+CFacCEeuezmyhgswt0U= -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= - array-flatten@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-3.0.0.tgz#6428ca2ee52c7b823192ec600fa3ed2f157cd541" @@ -1707,11 +1722,6 @@ ast-types@0.13.2: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48" integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA== -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - async-retry@1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.2.3.tgz#a6521f338358d322b1a0012b79030c6f411d1ce0" @@ -1833,9 +1843,9 @@ balanced-match@^1.0.0: integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= base64-js@^1.0.2, base64-js@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" - integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== bcrypt-pbkdf@^1.0.0: version "1.0.2" @@ -1844,25 +1854,15 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -bfj@^6.1.1: - version "6.1.2" - resolved "https://registry.yarnpkg.com/bfj/-/bfj-6.1.2.tgz#325c861a822bcb358a41c78a33b8e6e2086dde7f" - integrity sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw== - dependencies: - bluebird "^3.5.5" - check-types "^8.0.3" - hoopy "^0.1.4" - tryer "^1.0.1" - big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== binary-extensions@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" - integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== bl@^4.0.3: version "4.0.3" @@ -1873,7 +1873,7 @@ bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" -bluebird@^3.5.5, bluebird@^3.7.2: +bluebird@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -1883,27 +1883,11 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.4.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== -bn.js@^5.1.1: +bn.js@^5.0.0, bn.js@^5.1.1: version "5.1.3" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b" integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ== -body-parser@1.19.0: - version "1.19.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== - dependencies: - bytes "3.1.0" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "1.7.2" - iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" - body-scroll-lock@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/body-scroll-lock/-/body-scroll-lock-3.1.5.tgz#c1392d9217ed2c3e237fee1e910f6cdd80b7aaec" @@ -1966,11 +1950,11 @@ browserify-des@^1.0.0: safe-buffer "^5.1.2" browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" - integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== dependencies: - bn.js "^4.1.0" + bn.js "^5.0.0" randombytes "^2.0.1" browserify-sign@^4.0.0: @@ -1999,14 +1983,15 @@ browserslist@4.14.6: node-releases "^1.1.65" browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.6.4: - version "4.14.5" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.5.tgz#1c751461a102ddc60e40993639b709be7f2c4015" - integrity sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA== + version "4.16.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.1.tgz#bf757a2da376b3447b800a16f0f1c96358138766" + integrity sha512-UXhDrwqsNcpTYJBTZsbGATDxZbiVDsx6UjpmRUmtnP10pr8wAYr5LgFoEFw9ixriQH2mv/NX2SfGzE/o8GndLA== dependencies: - caniuse-lite "^1.0.30001135" - electron-to-chromium "^1.3.571" - escalade "^3.1.0" - node-releases "^1.1.61" + caniuse-lite "^1.0.30001173" + colorette "^1.2.1" + electron-to-chromium "^1.3.634" + escalade "^3.1.1" + node-releases "^1.1.69" bser@2.1.1: version "2.1.1" @@ -2038,15 +2023,7 @@ buffer@5.6.0: base64-js "^1.0.2" ieee754 "^1.1.4" -buffer@^5.5.0: - version "5.6.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.1.tgz#b99419405f4290a7a1f20b51037cee9f1fbd7f6a" - integrity sha512-2z15UUHpS9/3tk9mY/q+Rl3rydOi7yMp5XWNQnRvoz+mJwiv8brqYwp9a+nOCtma6dwuEIxljD8W3ysVBZ05Vg== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -buffer@^5.7.0: +buffer@^5.5.0, buffer@^5.7.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -2060,9 +2037,9 @@ bunyan-prettystream@^0.1.3: integrity sha1-bDtxMmb2rTIAfHtqsemYokU0nZg= bunyan@^1.8.14: - version "1.8.14" - resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.14.tgz#3d8c1afea7de158a5238c7cb8a66ab6b38dd45b4" - integrity sha512-LlahJUxXzZLuw/hetUQJmRgZ1LF6+cr5TPpRj6jf327AsiIq2jhYEH4oqUUkVKTor+9w2BT3oxVwhzE5lw9tcg== + version "1.8.15" + resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.15.tgz#8ce34ca908a17d0776576ca1b2f6cbd916e93b46" + integrity sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig== optionalDependencies: dtrace-provider "~0.8" moment "^2.19.3" @@ -2095,12 +2072,12 @@ cacheable-request@^6.0.0: responselike "^1.0.2" call-bind@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce" - integrity sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w== + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.1.tgz#29aca9151f8ddcfd5b9b786898f005f425e88567" + integrity sha512-tvAvUwNcRikl3RVF20X9lsYmmepsovzTWeJiXjO0PkJp15uy/6xKFZOQtuiSULwYW+6ToZBprphCgWXC2dSgcQ== dependencies: function-bind "^1.1.1" - get-intrinsic "^1.0.0" + get-intrinsic "^1.0.2" callsites@^3.0.0: version "3.1.0" @@ -2143,19 +2120,14 @@ camelcase@5.3.1, camelcase@^5.0.0, camelcase@^5.3.1: integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.1.0.tgz#27dc176173725fb0adf8a48b647f4d7871944d78" - integrity sha512-WCMml9ivU60+8rEJgELlFp1gxFcEGxwYleE3bziHEDeqsqAWGHdimB7beBFGjLzVNgPGyDsfgXLQEYMpmIFnVQ== + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== -caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001093, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001113, caniuse-lite@^1.0.30001135: - version "1.0.30001151" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001151.tgz#1ddfde5e6fff02aad7940b4edb7d3ac76b0cb00b" - integrity sha512-Zh3sHqskX6mHNrqUerh+fkf0N72cMxrmflzje/JyVImfpknscMnkeJrlFGJcqTmaa0iszdYptGpWMJCRQDkBVw== - -caniuse-lite@^1.0.30001154: - version "1.0.30001161" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001161.tgz#64f7ffe79ee780b8c92843ff34feb36cea4651e0" - integrity sha512-JharrCDxOqPLBULF9/SPa6yMcBRTjZARJ6sc3cuKrPfyIk64JN6kuMINWqA99Xc8uElMFcROliwtz0n9pYej+g== +caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001093, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001113, caniuse-lite@^1.0.30001154, caniuse-lite@^1.0.30001173: + version "1.0.30001174" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001174.tgz#0f2aca2153fd88ceb07a2bb982fc2acb787623c4" + integrity sha512-tqClL/4ThQq6cfFXH3oJL4rifFBeM6gTkphjao5kgwMaW9yn0tKgQLAEfKzDwj6HQWCB/aWo8kTFlSvIN8geEA== caseless@~0.12.0: version "0.12.0" @@ -2203,12 +2175,7 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -check-types@^8.0.3: - version "8.0.3" - resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552" - integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ== - -chokidar@3.4.3, chokidar@^3.4.3: +chokidar@3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b" integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ== @@ -2223,6 +2190,21 @@ chokidar@3.4.3, chokidar@^3.4.3: optionalDependencies: fsevents "~2.1.2" +chokidar@^3.4.3: + version "3.5.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.0.tgz#458a4816a415e9d3b3caa4faec2b96a6935a9e65" + integrity sha512-JgQM9JS92ZbFR4P90EvmzNpSGhpPBGBSj10PILeDyYFwp4h2/D9OM03wsJ4zW1fEp4ka2DGrnUeD7FuvQ2aZ2Q== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.3.1" + chownr@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" @@ -2262,7 +2244,7 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-spinners@^2.4.0: +cli-spinners@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.5.0.tgz#12763e47251bf951cb75c201dfa58ff1bcb2d047" integrity sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ== @@ -2367,7 +2349,7 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.13.0, commander@^2.16.0, commander@^2.18.0, commander@^2.20.0, commander@^2.20.3, commander@^2.8.1: +commander@^2.13.0, commander@^2.16.0, commander@^2.20.0, commander@^2.20.3, commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -2377,6 +2359,11 @@ commander@^5.0.0, commander@^5.1.0: resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== +commander@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + common-tags@1.8.0, common-tags@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" @@ -2404,6 +2391,13 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= +consolidate@^0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.16.0.tgz#a11864768930f2f19431660a65906668f5fbdc16" + integrity sha512-Nhl1wzCslqXYTJVDyJCu3ODohy9OfBMB5uD2BiBTzd7w+QY0lBzafkR8y8755yMYHAaMD4NuzbAw03/xzfw+eQ== + dependencies: + bluebird "^3.7.2" + constant-case@3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.3.tgz#ac910a99caf3926ac5112f352e3af599d8c5fc0a" @@ -2422,18 +2416,6 @@ constant-case@^3.0.3: tslib "^2.0.3" upper-case "^2.0.2" -content-disposition@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== - dependencies: - safe-buffer "5.1.2" - -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - convert-source-map@1.7.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" @@ -2446,16 +2428,6 @@ convert-source-map@^0.3.3: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190" integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA= -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= - -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" - integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== - cookie@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" @@ -2647,9 +2619,9 @@ cssnano-simple@1.2.1: postcss "^7.0.32" csstype@^3.0.2: - version "3.0.4" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.4.tgz#b156d7be03b84ff425c9a0a4b1e5f4da9c5ca888" - integrity sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA== + version "3.0.6" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.6.tgz#865d0b5833d7d8d40f4e5b8a6d76aea3de4725ef" + integrity sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw== d@1, d@^1.0.1: version "1.0.1" @@ -2681,23 +2653,11 @@ date-fns@^1.27.2: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== -de-indent@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" - integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0= - debounce@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131" integrity sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg== -debug@2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - debug@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -2705,33 +2665,19 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" - integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== - dependencies: - ms "2.1.2" - -debug@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - -debug@^4.2.0: +debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.2.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: ms "2.1.2" -debug@^4.2.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.0.tgz#efa41cbf14fc9448075367fdaaddf82376da211e" - integrity sha512-jjO6JD2rKfiZQnBoRzhRTbXjHLGLfH+UtGkWLc/UXAh/rzZMyjbgn0NcfFpqT8nd1kTtFnDiJcrIFkq4UKeJVg== +debug@^3.1.0: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: - ms "2.1.2" + ms "^2.1.1" decamelize-keys@^1.1.0: version "1.1.0" @@ -2857,11 +2803,6 @@ des.js@^1.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - detect-indent@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd" @@ -2986,7 +2927,7 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -dom-serializer@1.1.0, dom-serializer@^1.0.1: +dom-serializer@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.1.0.tgz#5f7c828f1bfc44887dc2a315ab5c45691d544b58" integrity sha512-ox7bvGXt2n+uLWtCRLybYx60IrOlWL/aCebWJk1T0d4m3y2tzf4U3ij9wBMUb6YJZpz06HCCYuyCDveE2xXmzQ== @@ -2995,12 +2936,16 @@ dom-serializer@1.1.0, dom-serializer@^1.0.1: domhandler "^3.0.0" entities "^2.0.0" -domelementtype@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.2.tgz#f3b6e549201e46f588b59463dd77187131fe6971" - integrity sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA== +dom-serializer@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.2.0.tgz#3433d9136aeb3c627981daa385fc7f32d27c48f1" + integrity sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + entities "^2.0.0" -domelementtype@^2.1.0: +domelementtype@^2.0.1, domelementtype@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== @@ -3062,7 +3007,7 @@ duplexer3@^0.1.4: resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= -duplexer@^0.1.1: +duplexer@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== @@ -3082,25 +3027,10 @@ ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer "^5.0.1" -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -ejs@^2.6.1: - version "2.7.4" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" - integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== - -electron-to-chromium@^1.3.571: - version "1.3.583" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.583.tgz#47a9fde74740b1205dba96db2e433132964ba3ee" - integrity sha512-L9BwLwJohjZW9mQESI79HRzhicPk1DFgM+8hOCfGgGCFEcA3Otpv7QK6SGtYoZvfQfE3wKLh0Hd5ptqUFv3gvQ== - -electron-to-chromium@^1.3.585: - version "1.3.607" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.607.tgz#1bff13f1cf89f2fee0d244b8c64a7138f80f3a3b" - integrity sha512-h2SYNaBnlplGS0YyXl8oJWokfcNxVjJANQfMCsQefG6OSuAuNIeW+A8yGT/ci+xRoBb3k2zq1FrOvkgoKBol8g== +electron-to-chromium@^1.3.585, electron-to-chromium@^1.3.634: + version "1.3.635" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.635.tgz#8d1591eeca6b257d380061a2c04f0b3cc6c9e33b" + integrity sha512-RRriZOLs9CpW6KTLmgBqyUdnY0QNqqWs0HOtuQGGEMizOTNNn1P7sGRBxARnUeLejOsgwjDyRqT3E/CSst02ZQ== elegant-spinner@^1.0.1: version "1.0.1" @@ -3140,11 +3070,6 @@ emojis-list@^3.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -3153,18 +3078,18 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: once "^1.4.0" enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" - integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ== + version "4.5.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" + integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== dependencies: graceful-fs "^4.1.2" memory-fs "^0.5.0" tapable "^1.0.0" enhanced-resolve@^5.3.1: - version "5.4.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.4.1.tgz#c89b0c34f17f931902ef2913a125d4b825b49b6f" - integrity sha512-4GbyIMzYktTFoRSmkbgZ1LU+RXwf4AQ8Z+rSuuh1dC8plp0PPeaWvx6+G4hh4KnUJ48VoxKbNyA1QQQIUpXjYA== + version "5.5.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.5.0.tgz#5f2ca7d511076541e2a30dc364a40c4f6c027bcd" + integrity sha512-b4a6BasBCoLzri4MdaeOlDMpls2oioI28CF17csMiav9dq46yvQaKPFNUrCHB6VqQokBDG2VIEEL81jMiQ6Wtw== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -3175,9 +3100,9 @@ entities@^2.0.0: integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== errno@^0.1.3: - version "0.1.7" - resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" - integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== dependencies: prr "~1.0.1" @@ -3241,16 +3166,11 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.3: d "^1.0.1" ext "^1.1.2" -escalade@^3.1.0, escalade@^3.1.1: +escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -3303,12 +3223,17 @@ estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== +estree-walker@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -etag@1.8.1, etag@~1.8.1: +etag@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= @@ -3343,42 +3268,6 @@ expand-template@^2.0.3: resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== -express@^4.16.3: - version "4.17.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" - integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== - dependencies: - accepts "~1.3.7" - array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" - content-type "~1.0.4" - cookie "0.4.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "~1.1.2" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" - range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" - statuses "~1.5.0" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - ext@^1.1.2: version "1.4.0" resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" @@ -3501,11 +3390,6 @@ file-exists-dazinatorfork@^1.0.2: resolved "https://registry.yarnpkg.com/file-exists-dazinatorfork/-/file-exists-dazinatorfork-1.0.2.tgz#cd8d0d85f63e39dc81eceb0b687c44a2cca95c47" integrity sha512-r70c72ln2YHzQINNfxDp02hAhbGkt1HffZ+Du8oetWDLjDtFja/Lm10lUaSh9e+wD+7VDvPee0b0C9SAy8pWZg== -filesize@^3.6.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" - integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== - filing-cabinet@^2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/filing-cabinet/-/filing-cabinet-2.6.0.tgz#3d4d5093a98b6fae84cf282e8bded1b8ad5f9c0c" @@ -3532,19 +3416,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" - find-cache-dir@3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" @@ -3614,16 +3485,6 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" -forwarded@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" - integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= - fs-capacitor@^6.1.0: version "6.2.0" resolved "https://registry.yarnpkg.com/fs-capacitor/-/fs-capacitor-6.2.0.tgz#fa79ac6576629163cb84561995602d8999afb7f5" @@ -3653,6 +3514,11 @@ fsevents@~2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== +fsevents@~2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.1.tgz#b209ab14c61012636c8863507edf7fb68cc54e9f" + integrity sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -3672,6 +3538,13 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" +generic-names@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/generic-names/-/generic-names-2.0.1.tgz#f8a378ead2ccaa7a34f0317b05554832ae41b872" + integrity sha512-kPCHWa1m9wGG/OwQpeweTwM/PYiQLrUIxXbt/P4Nic3LbGjCP0YwrALHW1uNLKZ0LIMg+RF+XRlj2ekT9ZlZAQ== + dependencies: + loader-utils "^1.1.0" + gensync@^1.0.0-beta.1: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -3690,7 +3563,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.0: +get-intrinsic@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.2.tgz#6820da226e50b24894e08859469dc68361545d49" integrity sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg== @@ -3871,13 +3744,12 @@ graphviz@0.0.9: dependencies: temp "~0.4.0" -gzip-size@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" - integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== +gzip-size@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" + integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== dependencies: - duplexer "^0.1.1" - pify "^4.0.1" + duplexer "^0.1.2" har-schema@^2.0.0: version "2.0.0" @@ -3940,6 +3812,11 @@ hash-base@^3.0.0: readable-stream "^3.6.0" safe-buffer "^5.2.0" +hash-sum@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a" + integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg== + hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" @@ -3948,7 +3825,7 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" -he@1.2.0, he@^1.1.0: +he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== @@ -3967,11 +3844,6 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoopy@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" - integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ== - hosted-git-info@^2.1.4: version "2.8.8" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" @@ -3997,18 +3869,7 @@ http-cache-semantics@^4.0.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== -http-errors@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-errors@1.7.3, http-errors@~1.7.2: +http-errors@1.7.3: version "1.7.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== @@ -4070,6 +3931,11 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +icss-replace-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" + integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= + icss-utils@^4.0.0, icss-utils@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" @@ -4078,9 +3944,9 @@ icss-utils@^4.0.0, icss-utils@^4.1.1: postcss "^7.0.14" ieee754@^1.1.13, ieee754@^1.1.4: - version "1.1.13" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" - integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore@^5.1.4: version "5.1.8" @@ -4135,11 +4001,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, i resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" @@ -4164,11 +4025,6 @@ inquirer@^7.3.3: strip-ansi "^6.0.0" through "^2.3.6" -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - is-absolute@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" @@ -4199,13 +4055,6 @@ is-callable@^1.1.4, is-callable@^1.2.2: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== -is-core-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.0.0.tgz#58531b70aed1db7c0e8d4eb1a0a2d1ddd64bd12d" - integrity sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw== - dependencies: - has "^1.0.3" - is-core-module@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" @@ -4397,10 +4246,10 @@ jest-worker@24.9.0: merge-stream "^2.0.0" supports-color "^6.1.0" -jest-worker@^26.6.1: - version "26.6.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.1.tgz#c2ae8cde6802cc14056043f997469ec170d9c32a" - integrity sha512-R5IE3qSGz+QynJx8y+ICEkdI2OJ3RJjRQVEyCcFAd3yVhQSEtquziPO29Mlzgn07LOVE8u8jhJ1FqcwegiXWOw== +jest-worker@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== dependencies: "@types/node" "*" merge-stream "^2.0.0" @@ -4549,9 +4398,9 @@ jws@^3.2.2: safe-buffer "^5.0.1" keen-slider@^5.2.4: - version "5.2.4" - resolved "https://registry.yarnpkg.com/keen-slider/-/keen-slider-5.2.4.tgz#9e2a889c63c02a651c81caa438f3691e9a3bc0a8" - integrity sha512-z39afyASW63B+1FzWGzBkvXAnzJl3gAD8M+32TmhJAPJqjckCaKYm7YBjpSba04AoVMQw8y9U1LVcUucVVIQkQ== + version "5.3.5" + resolved "https://registry.yarnpkg.com/keen-slider/-/keen-slider-5.3.5.tgz#29ac24fd7dbf1f1435bd448e0e583cded36b4b91" + integrity sha512-3QYvrFK7HbtkiC98/G+RM44y9WBe9cpHDHtP9bI1J/Vrc54IHLuTjOjyX4E81ukRc5GwiGfijOwStxEhisFnPg== keyv@^3.0.0: version "3.1.0" @@ -4643,9 +4492,9 @@ listr@^0.14.3: rxjs "^6.3.3" loader-runner@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.1.0.tgz#f70bc0c29edbabdf2043e7ee73ccc3fe1c96b42d" - integrity sha512-oR4lB4WvwFoC70ocraKhn5nkKSs23t57h9udUgw8o0iH8hMXeEoRuUgfcvgUwAJ1ZpRqBvcou4N2SMvM1DwMrA== + version "4.2.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384" + integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== loader-utils@1.2.3: version "1.2.3" @@ -4665,7 +4514,7 @@ loader-utils@2.0.0, loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" -loader-utils@^1.0.2: +loader-utils@^1.0.2, loader-utils@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== @@ -4693,6 +4542,11 @@ lodash._reinterpolate@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -4832,14 +4686,14 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== -lru-cache@5.1.1: +lru-cache@5.1.1, lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: yallist "^3.0.2" -lru-cache@6.0.0: +lru-cache@6.0.0, lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== @@ -4874,6 +4728,13 @@ madge@^3.8.0: typescript "^3.9.5" walkdir "^0.4.1" +magic-string@^0.25.7: + version "0.25.7" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" + integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== + dependencies: + sourcemap-codec "^1.4.4" + make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -4918,11 +4779,6 @@ md5.js@^1.3.4: inherits "^2.0.1" safe-buffer "^5.1.2" -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - memory-fs@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" @@ -4948,10 +4804,12 @@ meow@^7.0.0: type-fest "^0.13.1" yargs-parser "^18.1.3" -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= +merge-source-map@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" + integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== + dependencies: + source-map "^0.6.1" merge-stream@^2.0.0: version "2.0.0" @@ -4963,11 +4821,6 @@ merge2@^1.3.0: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - micromatch@^4.0.0, micromatch@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" @@ -4984,22 +4837,22 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.44.0: - version "1.44.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" - integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== +mime-db@1.45.0: + version "1.45.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" + integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== -mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.27" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" - integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19: + version "2.1.28" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" + integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ== dependencies: - mime-db "1.44.0" + mime-db "1.45.0" -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mime@^2.3.1: + version "2.4.7" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.7.tgz#962aed9be0ed19c91fd7dc2ece5d7f4e89a90d74" + integrity sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA== mimic-fn@^1.0.0: version "1.2.0" @@ -5072,18 +4925,18 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@^0.5.1, mkdirp@~0.5.1: +mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" -mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - module-definition@^3.0.0, module-definition@^3.3.0: version "3.3.1" resolved "https://registry.yarnpkg.com/module-definition/-/module-definition-3.3.1.tgz#fedef71667713e36988b93d0626a4fe7b35aebfc" @@ -5114,16 +4967,16 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - -ms@2.1.2, ms@^2.0.0, ms@^2.1.1: +ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@^2.0.0, ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + mute-stream@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" @@ -5144,9 +4997,9 @@ nan@^2.14.0: integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== nanoid@^3.1.16: - version "3.1.18" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.18.tgz#0680db22ab01c372e89209f5d18283d98de3e96d" - integrity sha512-rndlDjbbHbcV3xi+R2fpJ+PbGMdfBxz5v1fATIQFq0DP64FsicQdwnKLy47K4kZHdRpmQXtz24eGsxQqamzYTA== + version "3.1.20" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" + integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== napi-build-utils@^1.0.1: version "1.0.2" @@ -5165,20 +5018,15 @@ ncp@~2.0.0: resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M= -negotiator@0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" - integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== - neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== next-seo@^4.11.0: - version "4.14.0" - resolved "https://registry.yarnpkg.com/next-seo/-/next-seo-4.14.0.tgz#8a3243286f65606da81f6fdb0f8b1673ab5dd5a6" - integrity sha512-HHL82n2bx1Aj0z8kpy31LoJxzWpZwC/RlXleoO5zNNjJjEGtNS7uZ+wrvAbFGUqC4V54I1sZa4f0WZV+mpOx6w== + version "4.17.0" + resolved "https://registry.yarnpkg.com/next-seo/-/next-seo-4.17.0.tgz#fb3dbe0ed7414aa9d83872ef5d8510980a6dae7e" + integrity sha512-/hnb3oq7bhi8s7bup7+Lm6VzzgRucj+JrWtp+dJiYs/04H0N/NhmE9MpepixQ16fFj6G/39JLYxhzsO/QZG/Kw== next-themes@^0.0.4: version "0.0.4" @@ -5199,17 +5047,17 @@ next-unused@^0.0.3: ts-loader "^7.0.0" next@^10.0.5-canary.11: - version "10.0.5-canary.11" - resolved "https://registry.yarnpkg.com/next/-/next-10.0.5-canary.11.tgz#f6c32b451a8fe179f917f19c1b5315501584d47f" - integrity sha512-RryLV/az0m+SyD1plzeRsE8G66gec2wp8jJoyaOUJCUjQdQK6sHgjwJB26KJcJFZskdbtUqbFvr05rw4eY0SAQ== + version "10.0.5" + resolved "https://registry.yarnpkg.com/next/-/next-10.0.5.tgz#8071e0aa1883266c91943aa7c6b73deadb064793" + integrity sha512-yr7ap2TLugf0aMHz+3JoKFP9CCkFE+k6jCfdUymORhptjLYZbD3YGlTcUC1CRl+b5Phlbl7m/WUIPde0VcguiA== dependencies: "@ampproject/toolbox-optimizer" "2.7.1-alpha.0" "@babel/runtime" "7.12.5" "@hapi/accept" "5.0.1" - "@next/env" "10.0.5-canary.11" - "@next/polyfill-module" "10.0.5-canary.11" - "@next/react-dev-overlay" "10.0.5-canary.11" - "@next/react-refresh-utils" "10.0.5-canary.11" + "@next/env" "10.0.5" + "@next/polyfill-module" "10.0.5" + "@next/react-dev-overlay" "10.0.5" + "@next/react-refresh-utils" "10.0.5" "@opentelemetry/api" "0.14.0" ast-types "0.13.2" babel-plugin-transform-define "2.0.0" @@ -5261,16 +5109,16 @@ no-case@^3.0.3, no-case@^3.0.4: tslib "^2.0.3" node-abi@^2.7.0: - version "2.19.1" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.19.1.tgz#6aa32561d0a5e2fdb6810d8c25641b657a8cea85" - integrity sha512-HbtmIuByq44yhAzK7b9j/FelKlHYISKQn0mtvcBrU5QBkhoCMp5bu8Hv5AI34DcKfOAcJBcOEMwLlwO62FFu9A== + version "2.19.3" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.19.3.tgz#252f5dcab12dad1b5503b2d27eddd4733930282d" + integrity sha512-9xZrlyfvKhWme2EXFKQhZRp1yNWT/uI1luYPr3sFl+H4keYY4xR+1jO7mvTTijIsHf1M+QDe9uWuKeEpLInIlg== dependencies: semver "^5.4.1" node-addon-api@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.0.2.tgz#04bc7b83fd845ba785bb6eae25bc857e1ef75681" - integrity sha512-+D4s2HCnxPd5PjjI0STKwncjXTUKKqm74MDMz9OPXavjsGmjkvwgLtA5yoxJUdmpj52+2u+RrXgPipahKczMKg== + version "3.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.1.0.tgz#98b21931557466c6729e51cb77cd39c965f42239" + integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw== node-emoji@^1.8.1: version "1.10.0" @@ -5296,15 +5144,10 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-releases@^1.1.61: - version "1.1.64" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.64.tgz#71b4ae988e9b1dd7c1ffce58dd9e561752dfebc5" - integrity sha512-Iec8O9166/x2HRMJyLLLWkd0sFFLrFNy+Xf+JQfSQsdBJzPcHpNl3JQ9gD4j+aJxmCa25jNsIbM4bmACtSbkSg== - -node-releases@^1.1.65: - version "1.1.67" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.67.tgz#28ebfcccd0baa6aad8e8d4d8fe4cbc49ae239c12" - integrity sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg== +node-releases@^1.1.65, node-releases@^1.1.69: + version "1.1.69" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.69.tgz#3149dbde53b781610cd8b486d62d86e26c3725f6" + integrity sha512-DGIjo79VDEyAnRlfSqYTsy+yoHd2IOjJiKUozD2MV2D85Vso6Bug56mb9tT/fY5Urt0iqk01H7x+llAruDR2zA== node-source-walk@^4.0.0, node-source-walk@^4.2.0: version "4.2.0" @@ -5396,9 +5239,9 @@ object-assign@^4.1.0, object-assign@^4.1.1: integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-hash@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.3.tgz#d12db044e03cd2ca3d77c0570d87225b02e1e6ea" - integrity sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg== + version "2.1.1" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.1.1.tgz#9447d0279b4fcf80cff3259bf66a1dc73afabe09" + integrity sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ== object-inspect@^1.8.0: version "1.9.0" @@ -5425,13 +5268,6 @@ object.assign@^4.1.0, object.assign@^4.1.1: has-symbols "^1.0.1" object-keys "^1.1.1" -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= - dependencies: - ee-first "1.1.1" - once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -5453,7 +5289,7 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" -opener@^1.5.1: +opener@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== @@ -5471,16 +5307,16 @@ optionator@^0.8.1: word-wrap "~1.2.3" ora@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-5.1.0.tgz#b188cf8cd2d4d9b13fd25383bc3e5cba352c94f8" - integrity sha512-9tXIMPvjZ7hPTbk8DFq1f7Kow/HU/pQYB60JbNq+QnGwcyhWVZaQ4hM9zQDEsPxw/muLpgiHSaumUZxCAmod/w== + version "5.2.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.2.0.tgz#de10bfd2d15514384af45f3fa9d9b1aaf344fda1" + integrity sha512-+wG2v8TUU8EgzPHun1k/n45pXquQ9fHnbXVetl9rRgO6kjZszGGbraF3XPTIdgeA+s1lbRjSEftAnyT0w8ZMvQ== dependencies: + bl "^4.0.3" chalk "^4.1.0" cli-cursor "^3.1.0" - cli-spinners "^2.4.0" + cli-spinners "^2.5.0" is-interactive "^1.0.0" log-symbols "^4.0.0" - mute-stream "0.0.8" strip-ansi "^6.0.0" wcwidth "^1.0.1" @@ -5501,14 +5337,14 @@ p-cancelable@^1.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== -p-limit@3.0.2, p-limit@^3.0.2: +p-limit@3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.2.tgz#1664e010af3cadc681baafd3e2a437be7b0fb5fe" integrity sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg== dependencies: p-try "^2.0.0" -p-limit@3.1.0: +p-limit@3.1.0, p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -5606,11 +5442,6 @@ parse-ms@^2.1.0: resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== -parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - pascal-case@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.1.tgz#5ac1975133ed619281e88920973d2cd1f279de5f" @@ -5659,11 +5490,6 @@ path-root@^0.1.1: dependencies: path-root-regex "^0.1.0" -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= - path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -5845,9 +5671,9 @@ postcss-focus-within@^3.0.0: postcss "^7.0.2" postcss-font-variant@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-4.0.0.tgz#71dd3c6c10a0d846c5eda07803439617bbbabacc" - integrity sha512-M8BFYKOvCrI2aITzDad7kWuXXTm0YhGdP9Q8HanmN4EF1Hmcgs1KK5rSHylt/lUJe8yLxiSwWAHdScoEiIxztg== + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-4.0.1.tgz#42d4c0ab30894f60f98b17561eb5c0321f502641" + integrity sha512-I3ADQSTNtLTTd8uxZhtSOrTCQ9G4qUVKPjHiDk0bV75QSxXjVWiJVJ2VLdspGUi9fbW9BcjKJoRvxAH1pckqmA== dependencies: postcss "^7.0.2" @@ -5922,7 +5748,7 @@ postcss-modules-extract-imports@^2.0.0: dependencies: postcss "^7.0.5" -postcss-modules-local-by-default@^3.0.3: +postcss-modules-local-by-default@^3.0.2, postcss-modules-local-by-default@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0" integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw== @@ -5948,6 +5774,21 @@ postcss-modules-values@^3.0.0: icss-utils "^4.0.0" postcss "^7.0.6" +postcss-modules@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/postcss-modules/-/postcss-modules-3.2.2.tgz#ee390de0f9f18e761e1778dfb9be26685c02c51f" + integrity sha512-JQ8IAqHELxC0N6tyCg2UF40pACY5oiL6UpiqqcIFRWqgDYO8B0jnxzoQ0EOpPrWXvcpu6BSbQU/3vSiq7w8Nhw== + dependencies: + generic-names "^2.0.1" + icss-replace-symbols "^1.1.0" + lodash.camelcase "^4.3.0" + postcss "^7.0.32" + postcss-modules-extract-imports "^2.0.0" + postcss-modules-local-by-default "^3.0.2" + postcss-modules-scope "^2.2.0" + postcss-modules-values "^3.0.0" + string-hash "^1.1.1" + postcss-nested@^4.1.1: version "4.2.3" resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-4.2.3.tgz#c6f255b0a720549776d220d00c4b70cd244136f6" @@ -6059,9 +5900,9 @@ postcss-selector-matches@^4.0.0: postcss "^7.0.2" postcss-selector-not@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-4.0.0.tgz#c68ff7ba96527499e832724a2674d65603b645c0" - integrity sha512-W+bkBZRhqJaYN8XAnbbZPLWMvZD1wKTu0UxtFKdhtGjWYmxhkUneoeOhRJKdAE5V7ZTlnbHfCR+6bNwK9e1dTQ== + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-4.0.1.tgz#263016eef1cf219e0ade9a913780fc1f48204cbf" + integrity sha512-YolvBgInEK5/79C+bdFMyzqTg6pkYqDbzZIST/PDMqa/o3qtXenD05apBG2jLgT0/BQ77d4U2UK12jWpilqMAQ== dependencies: balanced-match "^1.0.0" postcss "^7.0.2" @@ -6075,7 +5916,7 @@ postcss-selector-parser@^5.0.0-rc.3, postcss-selector-parser@^5.0.0-rc.4: indexes-of "^1.0.1" uniq "^1.0.1" -postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: +postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: version "6.0.4" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3" integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw== @@ -6209,16 +6050,11 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= -prettier@^2.0.5: +prettier@^2.0.5, prettier@^2.1.2: version "2.2.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== -prettier@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.2.tgz#3050700dae2e4c8b67c4c3f666cdb8af405e1ce5" - integrity sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg== - pretty-hrtime@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" @@ -6257,14 +6093,6 @@ prop-types@15.7.2, prop-types@^15.6.2: object-assign "^4.1.1" react-is "^16.8.1" -proxy-addr@~2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" - integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== - dependencies: - forwarded "~0.1.2" - ipaddr.js "1.9.1" - prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -6310,11 +6138,6 @@ purgecss@^2.3.0: postcss "7.0.32" postcss-selector-parser "^6.0.2" -qs@6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== - qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -6350,21 +6173,6 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== - dependencies: - bytes "3.1.0" - http-errors "1.7.2" - iconv-lite "0.4.24" - unpipe "1.0.0" - raw-body@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" @@ -6481,9 +6289,9 @@ redent@^3.0.0: strip-indent "^3.0.0" reduce-css-calc@^2.1.6: - version "2.1.7" - resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.7.tgz#1ace2e02c286d78abcd01fd92bfe8097ab0602c2" - integrity sha512-fDnlZ+AybAS3C7Q9xDq5y8A2z+lT63zLbynew/lur/IR24OQF5x98tfNwf79mzEdfywZ0a2wpM860FhFfMxZlA== + version "2.1.8" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz#7ef8761a28d614980dc0c982f772c93f7a99de03" + integrity sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg== dependencies: css-unit-converter "^1.1.1" postcss-value-parser "^3.3.0" @@ -6653,7 +6461,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.10.0, resolve@^1.11.1, resolve@^1.14.2: version "1.19.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== @@ -6661,14 +6469,6 @@ resolve@^1.10.0: is-core-module "^2.1.0" path-parse "^1.0.6" -resolve@^1.11.1, resolve@^1.14.2: - version "1.18.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.18.1.tgz#018fcb2c5b207d2a6424aee361c5a266da8f4130" - integrity sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA== - dependencies: - is-core-module "^2.0.0" - path-parse "^1.0.6" - responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" @@ -6747,16 +6547,16 @@ rxjs@^6.3.3, rxjs@^6.6.0: dependencies: tslib "^1.9.0" -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + safe-json-stringify@~1: version "1.2.0" resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz#356e44bc98f1f93ce45df14bcd7c01cda86e0afd" @@ -6827,28 +6627,11 @@ semver@^6.0.0, semver@^6.2.0: integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== semver@^7.3.2: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== - -send@0.17.1: - version "0.17.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.7.2" - mime "1.6.0" - ms "2.1.1" - on-finished "~2.3.0" - range-parser "~1.2.1" - statuses "~1.5.0" + lru-cache "^6.0.0" serialize-javascript@^5.0.1: version "5.0.1" @@ -6857,16 +6640,6 @@ serialize-javascript@^5.0.1: dependencies: randombytes "^2.1.0" -serve-static@1.14.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.17.1" - set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -6956,6 +6729,15 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" +sirv@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.10.tgz#3e591f5a9ae2520f50d5830f5fae38d97e7be194" + integrity sha512-H5EZCoZaggEUQy8ocKsF7WAToGuZhjJlLvM3XOef46CbdIgbNeQ1p32N1PCuCjkVYwrAVOSMacN6CXXgIzuspg== + dependencies: + "@polka/url" "^1.0.0-next.9" + mime "^2.3.1" + totalist "^1.0.0" + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -7017,6 +6799,11 @@ source-map@^0.5.0: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= +sourcemap-codec@^1.4.4: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + spdx-correct@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" @@ -7075,7 +6862,7 @@ stacktrace-parser@0.1.10: dependencies: type-fest "^0.7.1" -"statuses@>= 1.5.0 < 2", statuses@~1.5.0: +"statuses@>= 1.5.0 < 2": version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= @@ -7098,7 +6885,7 @@ string-env-interpolation@1.0.1, string-env-interpolation@^1.0.1: resolved "https://registry.yarnpkg.com/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz#ad4397ae4ac53fe6c91d1402ad6f6a52862c7152" integrity sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg== -string-hash@1.1.3: +string-hash@1.1.3, string-hash@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b" integrity sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs= @@ -7267,10 +7054,10 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -swr@^0.3.11: - version "0.3.11" - resolved "https://registry.yarnpkg.com/swr/-/swr-0.3.11.tgz#f7f50ed26c06afea4249482cec504768a2272664" - integrity sha512-ya30LuRGK2R7eDlttnb7tU5EmJYJ+N6ytIOM2j0Hqs0qauJcDjVLDOGy7KmFeH5ivOwLHalFaIyYl2K+SGa7HQ== +swr@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/swr/-/swr-0.4.0.tgz#e76da9f981fe6dee0e133289e9b582fc80d9c41d" + integrity sha512-70qd1FHYHwIdYXW0jTpm5ktitzvPBCtyKz8ZzynWlY/rMqe4drYPgcl/H9Ipuh+Xv6ZW5viNx13ro8EKIWZcoQ== dependencies: dequal "2.0.2" @@ -7287,6 +7074,11 @@ sync-fetch@0.3.0: buffer "^5.7.0" node-fetch "^2.6.1" +tabbable@^5.1.5: + version "5.1.5" + resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-5.1.5.tgz#efec48ede268d511c261e3b81facbb4782a35147" + integrity sha512-oVAPrWgLLqrbvQE8XqcU7CVBq6SQbaIbHkhOca3u7/jzuQvyZycrUKPCGr04qpEIUslmUlULbSeN+m3QrKEykA== + tailwindcss@^1.9: version "1.9.6" resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-1.9.6.tgz#0c5089911d24e1e98e592a31bfdb3d8f34ecf1a0" @@ -7325,17 +7117,7 @@ tapable@^2.1.1, tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== -tar-fs@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.0.tgz#d1cdd121ab465ee0eb9ccde2d35049d3f3daf0d5" - integrity sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg== - dependencies: - chownr "^1.1.1" - mkdirp-classic "^0.5.2" - pump "^3.0.0" - tar-stream "^2.0.0" - -tar-fs@^2.1.1: +tar-fs@^2.0.0, tar-fs@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== @@ -7345,17 +7127,6 @@ tar-fs@^2.1.1: pump "^3.0.0" tar-stream "^2.1.4" -tar-stream@^2.0.0: - version "2.1.4" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.4.tgz#c4fb1a11eb0da29b893a5b25476397ba2d053bfa" - integrity sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - tar-stream@^2.1.4: version "2.2.0" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" @@ -7373,18 +7144,18 @@ temp@~0.4.0: integrity sha1-ZxrWPVe+D+nXKUZks/xABjZnimA= terser-webpack-plugin@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.0.3.tgz#ec60542db2421f45735c719d2e17dabfbb2e3e42" - integrity sha512-zFdGk8Lh9ZJGPxxPE6jwysOlATWB8GMW8HcfGULWA/nPal+3VdATflQvSBSLQJRCmYZnfFJl6vkRTiwJGNgPiQ== + version "5.1.1" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.1.tgz#7effadee06f7ecfa093dbbd3e9ab23f5f3ed8673" + integrity sha512-5XNNXZiR8YO6X6KhSGXfY0QrGrCRlSwAEjIIrlRQR4W8nP69TaJUlh3bkuac6zzgspiGPfKEHcY295MMVExl5Q== dependencies: - jest-worker "^26.6.1" - p-limit "^3.0.2" + jest-worker "^26.6.2" + p-limit "^3.1.0" schema-utils "^3.0.0" serialize-javascript "^5.0.1" source-map "^0.6.1" - terser "^5.3.8" + terser "^5.5.1" -terser@5.5.1: +terser@5.5.1, terser@^5.5.1: version "5.5.1" resolved "https://registry.yarnpkg.com/terser/-/terser-5.5.1.tgz#540caa25139d6f496fdea056e414284886fb2289" integrity sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ== @@ -7393,15 +7164,6 @@ terser@5.5.1: source-map "~0.7.2" source-map-support "~0.5.19" -terser@^5.3.8: - version "5.3.8" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.8.tgz#991ae8ba21a3d990579b54aa9af11586197a75dd" - integrity sha512-zVotuHoIfnYjtlurOouTazciEfL7V38QMAOhGqpXDEg6yT13cF4+fEP9b0rrCEQTn+tT46uxgFsTZzhygk+CzQ== - dependencies: - commander "^2.20.0" - source-map "~0.7.2" - source-map-support "~0.5.19" - through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -7436,6 +7198,11 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +totalist@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" + integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== + tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -7466,11 +7233,6 @@ trim-newlines@^3.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30" integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA== -tryer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" - integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== - ts-loader@^7.0.0: version "7.0.5" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-7.0.5.tgz#789338fb01cb5dc0a33c54e50558b34a73c9c4c5" @@ -7509,15 +7271,20 @@ tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2, tslib@^2.0.0, tslib@^2.0.3, tslib@~2.0.1: +tslib@^2, tslib@^2.0.0, tslib@^2.0.3, tslib@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" + integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== + +tslib@~2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== tsutils@^3.17.1: - version "3.17.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" - integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== + version "3.19.1" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.19.1.tgz#d8566e0c51c82f32f9c25a4d367cd62409a547a9" + integrity sha512-GEdoBf5XI324lu7ycad7s6laADfnAqCw6wLGI+knxvw9vsIYBaJfYdmeCEG3FMMUiSm3OGgNb+m6utsWf5h9Vw== dependencies: tslib "^1.8.1" @@ -7565,14 +7332,6 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -type-is@~1.6.17, type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - type@^1.0.1: version "1.2.0" resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" @@ -7589,9 +7348,9 @@ typescript@^3.0.3, typescript@^3.8.3, typescript@^3.9.5, typescript@^3.9.7: integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== typescript@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5" - integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg== + version "4.1.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" + integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== ua-parser-js@^0.7.18: version "0.7.23" @@ -7620,7 +7379,7 @@ unixify@1.0.0: dependencies: normalize-path "^2.1.1" -unpipe@1.0.0, unpipe@~1.0.0: +unpipe@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= @@ -7640,9 +7399,9 @@ upper-case@^2.0.1, upper-case@^2.0.2: tslib "^2.0.3" uri-js@^4.2.2: - version "4.4.0" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" - integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" @@ -7678,11 +7437,6 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= - uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" @@ -7701,11 +7455,6 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= - verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" @@ -7720,14 +7469,6 @@ vm-browserify@1.1.2: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -vue-template-compiler@^2.6.12: - version "2.6.12" - resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz#947ed7196744c8a5285ebe1233fe960437fcc57e" - integrity sha512-OzzZ52zS41YUbkCBfdXShQTe69j1gQDZ9HIX8miuC9C3rBCk9wIRjLiZZLrmX9V+Ftq/YEyv1JaVr5Y/hNtByg== - dependencies: - de-indent "^1.0.2" - he "^1.1.0" - walkdir@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39" @@ -7749,9 +7490,9 @@ watchpack@2.0.0-beta.13: graceful-fs "^4.1.2" watchpack@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.0.0.tgz#b12248f32f0fd4799b7be0802ad1f6573a45955c" - integrity sha512-xSdCxxYZWNk3VK13bZRYhsQpfa8Vg63zXG+3pyU8ouqSLRCv4IGXIp9Kr226q6GBkGRlZrST2wwKtjfKz2m7Cg== + version "2.1.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.1.0.tgz#e63194736bf3aa22026f7b191cd57907b0f9f696" + integrity sha512-UjgD1mqjkG99+3lgG36at4wPnUXNvis2v1utwTgQ43C22c4LD71LsYMExdWXh4HZ+RmW+B0t1Vrg2GpXAkTOQw== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" @@ -7768,24 +7509,20 @@ webidl-conversions@^4.0.2: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== -webpack-bundle-analyzer@3.6.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.6.1.tgz#bdb637c2304424f2fbff9a950c7be42a839ae73b" - integrity sha512-Nfd8HDwfSx1xBwC+P8QMGvHAOITxNBSvu/J/mCJvOwv+G4VWkU7zir9SSenTtyCi0LnVtmsc7G5SZo1uV+bxRw== +webpack-bundle-analyzer@4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.3.0.tgz#2f3c0ca9041d5ee47fa418693cf56b4a518b578b" + integrity sha512-J3TPm54bPARx6QG8z4cKBszahnUglcv70+N+8gUqv2I5KOFHJbzBiLx+pAp606so0X004fxM7hqRu10MLjJifA== dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - bfj "^6.1.1" - chalk "^2.4.1" - commander "^2.18.0" - ejs "^2.6.1" - express "^4.16.3" - filesize "^3.6.1" - gzip-size "^5.0.0" - lodash "^4.17.15" - mkdirp "^0.5.1" - opener "^1.5.1" - ws "^6.0.0" + acorn "^8.0.4" + acorn-walk "^8.0.0" + chalk "^4.1.0" + commander "^6.2.0" + gzip-size "^6.0.0" + lodash "^4.17.20" + opener "^1.5.2" + sirv "^1.0.7" + ws "^7.3.1" webpack-sources@1.4.3: version "1.4.3" @@ -7905,12 +7642,10 @@ ws@7.4.1: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.1.tgz#a333be02696bd0e54cea0434e21dcc8a9ac294bb" integrity sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ== -ws@^6.0.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" - integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== - dependencies: - async-limiter "~1.0.0" +ws@^7.3.1: + version "7.4.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.2.tgz#782100048e54eb36fe9843363ab1c68672b261dd" + integrity sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA== xtend@^4.0.2: version "4.0.2" From 72b5fb0c1a5a04b8f1c9f3927c2e81dd3081d140 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Mon, 11 Jan 2021 12:17:55 -0300 Subject: [PATCH 022/221] Merge Issues --- README.md | 26 ++++++-------------------- pages/wishlist.tsx | 6 +----- 2 files changed, 7 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index ebaf190d7..fbbbbf26b 100644 --- a/README.md +++ b/README.md @@ -22,18 +22,19 @@ This project is currently <b>under development</b>. - Dark Mode Support ## Work in progress + We're using Github Projects to keep track of issues in progress and todo's. Here is our [Board](https://github.com/vercel/commerce/projects/1) ## Integrations -Next.js Commerce integrates out-of-the-box with BigCommerce. We plan to support all major ecommerce backends. +Next.js Commerce integrates out-of-the-box with BigCommerce. We plan to support all major ecommerce backends. ## Goals -* **Next.js Commerce** should have a completely data **agnostic** UI -* **Aware of schema**: should ship with the right data schemas and types. -* All providers should return the right data types and schemas to blend correctly with Next.js Commerce. -* `@framework` will be the alias utilized in commerce and it will map to the ecommerce provider of preference- e.g BigCommerce, Shopify, Swell. All providers should expose the same standardized functions. _Note that the same applies for recipes using a CMS + an ecommerce provider._ +- **Next.js Commerce** should have a completely data **agnostic** UI +- **Aware of schema**: should ship with the right data schemas and types. +- All providers should return the right data types and schemas to blend correctly with Next.js Commerce. +- `@framework` will be the alias utilized in commerce and it will map to the ecommerce provider of preference- e.g BigCommerce, Shopify, Swell. All providers should expose the same standardized functions. _Note that the same applies for recipes using a CMS + an ecommerce provider._ There is a `framework` folder in the root folder that will contain multiple ecommerce providers. @@ -92,21 +93,6 @@ Our commitment to Open Source can be found [here](https://vercel.com/oss). 8. The development branch is `development` (this is the branch pull requests should be made against). On a release, `develop` branch is rebased into `master`. - -<<<<<<< HEAD -- **Next.js Commerce** should have a completely data **agnostic** UI -- **Aware of schema**: should ship with the right data schemas and types. -- All providers should return the right data types and schemas to blend correctly with Next.js Commerce. -- `@framework` will be the alias utilized in commerce and it will map to the ecommerce provider of preference- e.g BigCommerce, Shopify, Swell. All providers should expose the same standardized functions. _Note that the same applies for recipes using a CMS + an ecommerce provider._ - -There is a `framework` folder in the root folder that will contain multiple ecommerce providers. - -Additionally, we need to ensure feature parity (not all providers have e.g. wishlist) we will also have to build a feature API to disable/enable features in the UI. - -People actively working on this project: @okbel & @lfades. -======= ->>>>>>> master - ## Framework Framework is where the data comes from. It contains mostly hooks and functions. diff --git a/pages/wishlist.tsx b/pages/wishlist.tsx index 680848e35..b4410a58c 100644 --- a/pages/wishlist.tsx +++ b/pages/wishlist.tsx @@ -6,12 +6,8 @@ import { Layout } from '@components/common' import { Heart } from '@components/icons' import { Text, Container } from '@components/ui' import { WishlistCard } from '@components/wishlist' -<<<<<<< HEAD -import { defatultPageProps } from '@lib/defaults' -import { useCustomer } from '@framework/customer' -======= import { defaultPageProps } from '@lib/defaults' ->>>>>>> master +import { useCustomer } from '@framework/customer' export async function getStaticProps({ preview, From deadb1675b3f9c7496f075340e83af1159fe7ddd Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Mon, 11 Jan 2021 12:22:18 -0300 Subject: [PATCH 023/221] Cleanup --- components/auth/LoginView.tsx | 2 +- components/auth/SignUpView.tsx | 2 +- components/common/UserNav/DropdownMenu.tsx | 3 +- .../bigcommerce/lib/generate-definitions.js | 49 ------ package.json | 3 - pages/index2.tsx | 152 ------------------ tsconfig.json | 8 +- 7 files changed, 10 insertions(+), 209 deletions(-) delete mode 100644 framework/bigcommerce/lib/generate-definitions.js delete mode 100644 pages/index2.tsx diff --git a/components/auth/LoginView.tsx b/components/auth/LoginView.tsx index 89d5bf893..7b402e6d7 100644 --- a/components/auth/LoginView.tsx +++ b/components/auth/LoginView.tsx @@ -1,6 +1,6 @@ import { FC, useEffect, useState, useCallback } from 'react' import { Logo, Button, Input } from '@components/ui' -import useLogin from '@framework/auth/use-login' +import { useLogin } from '@framework/auth' import { useUI } from '@components/ui/context' import { validate } from 'email-validator' diff --git a/components/auth/SignUpView.tsx b/components/auth/SignUpView.tsx index 1b619828b..49351bfe9 100644 --- a/components/auth/SignUpView.tsx +++ b/components/auth/SignUpView.tsx @@ -3,7 +3,7 @@ import { validate } from 'email-validator' import { Info } from '@components/icons' import { useUI } from '@components/ui/context' import { Logo, Button, Input } from '@components/ui' -import useSignup from '@framework/auth/use-signup' +import { useSignup } from '@framework/auth' interface Props {} diff --git a/components/common/UserNav/DropdownMenu.tsx b/components/common/UserNav/DropdownMenu.tsx index a5bc5fd86..f8cedc332 100644 --- a/components/common/UserNav/DropdownMenu.tsx +++ b/components/common/UserNav/DropdownMenu.tsx @@ -8,6 +8,7 @@ import { Avatar } from '@components/common' import { Moon, Sun } from '@components/icons' import { useUI } from '@components/ui/context' import ClickOutside from '@lib/click-outside' +import { useLogout } from '@framework/auth' import { disableBodyScroll, @@ -15,8 +16,6 @@ import { clearAllBodyScrollLocks, } from 'body-scroll-lock' -import useLogout from '@framework/auth/use-logout' - interface DropdownMenuProps { open?: boolean } diff --git a/framework/bigcommerce/lib/generate-definitions.js b/framework/bigcommerce/lib/generate-definitions.js deleted file mode 100644 index a2b830d3b..000000000 --- a/framework/bigcommerce/lib/generate-definitions.js +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Generates definitions for REST API endpoints that are being - * used by ../api using https://github.com/drwpow/swagger-to-ts - */ -const { readFileSync, promises } = require('fs') -const path = require('path') -const fetch = require('node-fetch') -const swaggerToTS = require('@manifoldco/swagger-to-ts').default - -async function getSchema(filename) { - const url = `https://next-api.stoplight.io/projects/8433/files/${filename}` - const res = await fetch(url) - - if (!res.ok) { - throw new Error(`Request failed with ${res.status}: ${res.statusText}`) - } - - return res.json() -} - -const schemas = Object.entries({ - '../api/definitions/catalog.ts': - 'BigCommerce_Catalog_API.oas2.yml?ref=version%2F20.930', - '../api/definitions/store-content.ts': - 'BigCommerce_Store_Content_API.oas2.yml?ref=version%2F20.930', - '../api/definitions/wishlist.ts': - 'BigCommerce_Wishlist_API.oas2.yml?ref=version%2F20.930', - // swagger-to-ts is not working for the schema of the cart API - // '../api/definitions/cart.ts': - // 'BigCommerce_Server_to_Server_Cart_API.oas2.yml', -}) - -async function writeDefinitions() { - const ops = schemas.map(async ([dest, filename]) => { - const destination = path.join(__dirname, dest) - const schema = await getSchema(filename) - const definition = swaggerToTS(schema.content, { - prettierConfig: 'package.json', - }) - - await promises.writeFile(destination, definition) - - console.log(`✔️ Added definitions for: ${dest}`) - }) - - await Promise.all(ops) -} - -writeDefinitions() diff --git a/package.json b/package.json index dd78af9ba..b90bf4cf8 100644 --- a/package.json +++ b/package.json @@ -46,9 +46,7 @@ "dependencies": { "@reach/portal": "^0.11.2", "@tailwindcss/ui": "^0.6.2", - "@types/node-fetch": "2", "@vercel/fetch": "^6.1.0", - "@vercel/fetch-cached-dns": "^2.0.1", "body-scroll-lock": "^3.1.5", "bowser": "^2.11.0", "classnames": "^2.2.6", @@ -62,7 +60,6 @@ "next": "^10.0.5-canary.11", "next-seo": "^4.11.0", "next-themes": "^0.0.4", - "node-fetch": "^2.6.1", "postcss-nesting": "^7.0.1", "react": "^16.14.0", "react-dom": "^16.14.0", diff --git a/pages/index2.tsx b/pages/index2.tsx deleted file mode 100644 index ef1c0a96e..000000000 --- a/pages/index2.tsx +++ /dev/null @@ -1,152 +0,0 @@ -import rangeMap from '@lib/range-map' -import { Layout } from '@components/common' -import { ProductCard } from '@components/product' -import { Grid, Marquee, Hero } from '@components/ui' -import HomeAllProductsGrid from '@components/common/HomeAllProductsGrid' -import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next' - -import { getConfig } from '@framework/api' -import getAllProducts from '@framework/api/operations/get-all-products' -import getSiteInfo from '@framework/api/operations/get-site-info' -import getAllPages from '@framework/api/operations/get-all-pages' - -export async function getStaticProps({ - preview, - locale, -}: GetStaticPropsContext) { - const config = getConfig({ locale }) - - // Get Featured Products - const { products: featuredProducts } = await getAllProducts({ - variables: { field: 'featuredProducts', first: 6 }, - config, - preview, - }) - - // Get Best Selling Products - const { products: bestSellingProducts } = await getAllProducts({ - variables: { field: 'bestSellingProducts', first: 6 }, - config, - preview, - }) - - // Get Best Newest Products - const { products: newestProducts } = await getAllProducts({ - variables: { field: 'newestProducts', first: 12 }, - config, - preview, - }) - - const { categories, brands } = await getSiteInfo({ config, preview }) - const { pages } = await getAllPages({ config, preview }) - - // These are the products that are going to be displayed in the landing. - // We prefer to do the computation at buildtime/servertime - const { featured, bestSelling } = (() => { - // Create a copy of products that we can mutate - const products = [...newestProducts] - // If the lists of featured and best selling products don't have enough - // products, then fill them with products from the products list, this - // is useful for new commerce sites that don't have a lot of products - return { - featured: rangeMap(6, (i) => featuredProducts[i] ?? products.shift()) - .filter(nonNullable) - .sort((a, b) => a.node.prices.price.value - b.node.prices.price.value) - .reverse(), - bestSelling: rangeMap( - 6, - (i) => bestSellingProducts[i] ?? products.shift() - ).filter(nonNullable), - } - })() - - return { - props: { - featured, - bestSelling, - newestProducts, - categories, - brands, - pages, - }, - revalidate: 14400, - } -} - -const nonNullable = (v: any) => v - -export default function Home({ - featured, - bestSelling, - brands, - categories, - newestProducts, -}: InferGetStaticPropsType<typeof getStaticProps>) { - return ( - <div> - <Grid> - {featured.slice(0, 3).map(({ node }, i) => ( - <ProductCard - key={node.path} - product={node} - imgWidth={i === 0 ? 1080 : 540} - imgHeight={i === 0 ? 1080 : 540} - imgPriority - imgLoading="eager" - /> - ))} - </Grid> - <Marquee variant="secondary"> - {bestSelling.slice(3, 6).map(({ node }) => ( - <ProductCard - key={node.path} - product={node} - variant="slim" - imgWidth={320} - imgHeight={320} - imgLayout="fixed" - /> - ))} - </Marquee> - <Hero - headline="Release Details: The Yeezy BOOST 350 V2 ‘Natural'" - description=" - The Yeezy BOOST 350 V2 lineup continues to grow. We recently had the - ‘Carbon’ iteration, and now release details have been locked in for - this ‘Natural’ joint. Revealed by Yeezy Mafia earlier this year, the - shoe was originally called ‘Abez’, which translated to ‘Tin’ in - Hebrew. It’s now undergone a name change, and will be referred to as - ‘Natural’." - /> - <Grid layout="B"> - {featured.slice(3, 6).map(({ node }, i) => ( - <ProductCard - key={node.path} - product={node} - imgWidth={i === 1 ? 1080 : 540} - imgHeight={i === 1 ? 1080 : 540} - /> - ))} - </Grid> - <Marquee> - {bestSelling.slice(0, 3).map(({ node }) => ( - <ProductCard - key={node.path} - product={node} - variant="slim" - imgWidth={320} - imgHeight={320} - imgLayout="fixed" - /> - ))} - </Marquee> - <HomeAllProductsGrid - categories={categories} - brands={brands} - newestProducts={newestProducts} - /> - </div> - ) -} - -Home.Layout = Layout diff --git a/tsconfig.json b/tsconfig.json index 43dfd2a27..480cc2cb4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,6 +26,12 @@ "@framework": ["framework/bigcommerce"] } }, - "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], + "include": [ + "next-env.d.ts", + "framework/*.d.ts", + "**/*.ts", + "**/*.tsx", + "**/*.js" + ], "exclude": ["node_modules"] } From a3a2c15f70d238332afbeea34ac520cc457ff930 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Mon, 11 Jan 2021 12:26:34 -0300 Subject: [PATCH 024/221] change --- framework/bigcommerce/lib/normalize.ts | 4 +- yarn.lock | 7728 ++++++++++++++++++++++++ 2 files changed, 7729 insertions(+), 3 deletions(-) create mode 100644 yarn.lock diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index 7d938c643..51b508edd 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -1,6 +1,4 @@ -import { ProductEdge } from '@framework/schema' - -export function normalizeProduct(productNode: ProductEdge): Product { +export function normalizeProduct(productNode: any): Product { // console.log(productNode) const { node: { diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 000000000..8e9ded267 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,7728 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ampproject/toolbox-core@2.7.4", "@ampproject/toolbox-core@^2.7.1-alpha.0": + version "2.7.4" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-core/-/toolbox-core-2.7.4.tgz#8355136f16301458ce942acf6c55952c9a415627" + integrity sha512-qpBhcS4urB7IKc+jx2kksN7BuvvwCo7Y3IstapWo+EW+COY5EYAUwb2pil37v3TsaqHKgX//NloFP1SKzGZAnw== + dependencies: + cross-fetch "3.0.6" + lru-cache "6.0.0" + +"@ampproject/toolbox-optimizer@2.7.1-alpha.0": + version "2.7.1-alpha.0" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-optimizer/-/toolbox-optimizer-2.7.1-alpha.0.tgz#1571dcd02608223ff68f6b7223102a123e381197" + integrity sha512-WGPZKVQvHgNYJk1XVJCCmY+NVGTGJtvn0OALDyiegN4FJWOcilQUhCIcjMkZN59u1flz/u+sEKccM5qsROqVyg== + dependencies: + "@ampproject/toolbox-core" "^2.7.1-alpha.0" + "@ampproject/toolbox-runtime-version" "^2.7.1-alpha.0" + "@ampproject/toolbox-script-csp" "^2.5.4" + "@ampproject/toolbox-validator-rules" "^2.7.1-alpha.0" + abort-controller "3.0.0" + cross-fetch "3.0.6" + cssnano-simple "1.2.1" + dom-serializer "1.1.0" + domhandler "3.3.0" + domutils "2.4.2" + htmlparser2 "5.0.1" + https-proxy-agent "5.0.0" + lru-cache "6.0.0" + node-fetch "2.6.1" + normalize-html-whitespace "1.0.0" + postcss "7.0.32" + postcss-safe-parser "4.0.2" + terser "5.5.1" + +"@ampproject/toolbox-runtime-version@^2.7.1-alpha.0": + version "2.7.4" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-runtime-version/-/toolbox-runtime-version-2.7.4.tgz#f49da0dab122101ef75ed3caed3a0142487b73e1" + integrity sha512-SAdOUOERp42thVNWaBJlnFvFVvnacMVnz5z9LyUZHSnoL1EqrAW5Sz5jv+Ly+gkA8NYsEaUxAdSCBAzE9Uzb4Q== + dependencies: + "@ampproject/toolbox-core" "2.7.4" + +"@ampproject/toolbox-script-csp@^2.5.4": + version "2.5.4" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-script-csp/-/toolbox-script-csp-2.5.4.tgz#d8b7b91a678ae8f263cb36d9b74e441b7d633aad" + integrity sha512-+knTYetI5nWllRZ9wFcj7mYxelkiiFVRAAW/hl0ad8EnKHMH82tRlk40CapEnUHhp6Er5sCYkumQ8dngs3Q4zQ== + +"@ampproject/toolbox-validator-rules@^2.7.1-alpha.0": + version "2.7.4" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-validator-rules/-/toolbox-validator-rules-2.7.4.tgz#a58b5eca723f6c3557ac83b696de0247f5f03ce4" + integrity sha512-z3JRcpIZLLdVC9XVe7YTZuB3a/eR9s2SjElYB9AWRdyJyL5Jt7+pGNv4Uwh1uHVoBXsWEVQzNOWSNtrO3mSwZA== + dependencies: + cross-fetch "3.0.6" + +"@ardatan/aggregate-error@0.0.6": + version "0.0.6" + resolved "https://registry.yarnpkg.com/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz#fe6924771ea40fc98dc7a7045c2e872dc8527609" + integrity sha512-vyrkEHG1jrukmzTPtyWB4NLPauUw5bQeg4uhn8f+1SSynmrOcyvlb1GKQjjgoBzElLdfXCRYX8UnBlhklOHYRQ== + dependencies: + tslib "~2.0.1" + +"@babel/code-frame@7.12.11", "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/core@^7.0.0": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.10.tgz#b79a2e1b9f70ed3d84bbfb6d8c4ef825f606bccd" + integrity sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.10" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.5" + "@babel/parser" "^7.12.10" + "@babel/template" "^7.12.7" + "@babel/traverse" "^7.12.10" + "@babel/types" "^7.12.10" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.12.10", "@babel/generator@^7.12.11", "@babel/generator@^7.5.0": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.11.tgz#98a7df7b8c358c9a37ab07a24056853016aba3af" + integrity sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA== + dependencies: + "@babel/types" "^7.12.11" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-annotate-as-pure@^7.10.4", "@babel/helper-annotate-as-pure@^7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.10.tgz#54ab9b000e60a93644ce17b3f37d313aaf1d115d" + integrity sha512-XplmVbC1n+KY6jL8/fgLVXXUauDIB+lD5+GsQEh6F6GBF1dq1qy4DP4yXWzDKcoqXB3X58t61e85Fitoww4JVQ== + dependencies: + "@babel/types" "^7.12.10" + +"@babel/helper-create-class-features-plugin@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz#3c45998f431edd4a9214c5f1d3ad1448a6137f6e" + integrity sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-member-expression-to-functions" "^7.12.1" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.10.4" + +"@babel/helper-define-map@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz#b53c10db78a640800152692b13393147acb9bb30" + integrity sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/types" "^7.10.5" + lodash "^4.17.19" + +"@babel/helper-function-name@^7.10.4", "@babel/helper-function-name@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz#1fd7738aee5dcf53c3ecff24f1da9c511ec47b42" + integrity sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA== + dependencies: + "@babel/helper-get-function-arity" "^7.12.10" + "@babel/template" "^7.12.7" + "@babel/types" "^7.12.11" + +"@babel/helper-get-function-arity@^7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz#b158817a3165b5faa2047825dfa61970ddcc16cf" + integrity sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag== + dependencies: + "@babel/types" "^7.12.10" + +"@babel/helper-member-expression-to-functions@^7.12.1", "@babel/helper-member-expression-to-functions@^7.12.7": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz#aa77bd0396ec8114e5e30787efa78599d874a855" + integrity sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw== + dependencies: + "@babel/types" "^7.12.7" + +"@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz#1bfc0229f794988f76ed0a4d4e90860850b54dfb" + integrity sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA== + dependencies: + "@babel/types" "^7.12.5" + +"@babel/helper-module-transforms@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz#7954fec71f5b32c48e4b303b437c34453fd7247c" + integrity sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w== + dependencies: + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-simple-access" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/helper-validator-identifier" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.1" + "@babel/types" "^7.12.1" + lodash "^4.17.19" + +"@babel/helper-optimise-call-expression@^7.10.4", "@babel/helper-optimise-call-expression@^7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz#94ca4e306ee11a7dd6e9f42823e2ac6b49881e2d" + integrity sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ== + dependencies: + "@babel/types" "^7.12.10" + +"@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.8.0": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + +"@babel/helper-replace-supers@^7.12.1": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz#ea511658fc66c7908f923106dd88e08d1997d60d" + integrity sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.12.7" + "@babel/helper-optimise-call-expression" "^7.12.10" + "@babel/traverse" "^7.12.10" + "@babel/types" "^7.12.11" + +"@babel/helper-simple-access@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz#32427e5aa61547d38eb1e6eaf5fd1426fdad9136" + integrity sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA== + dependencies: + "@babel/types" "^7.12.1" + +"@babel/helper-skip-transparent-expression-wrappers@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf" + integrity sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA== + dependencies: + "@babel/types" "^7.12.1" + +"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0", "@babel/helper-split-export-declaration@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz#1b4cc424458643c47d37022223da33d76ea4603a" + integrity sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g== + dependencies: + "@babel/types" "^7.12.11" + +"@babel/helper-validator-identifier@^7.10.4", "@babel/helper-validator-identifier@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" + integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== + +"@babel/helpers@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.12.5.tgz#1a1ba4a768d9b58310eda516c449913fe647116e" + integrity sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.5" + "@babel/types" "^7.12.5" + +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@7.12.11", "@babel/parser@^7.0.0", "@babel/parser@^7.12.0", "@babel/parser@^7.12.10", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.11.tgz#9ce3595bcd74bc5c466905e86c535b8b25011e79" + integrity sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg== + +"@babel/plugin-proposal-class-properties@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz#a082ff541f2a29a4821065b8add9346c0c16e5de" + integrity sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz#def9bd03cea0f9b72283dac0ec22d289c7691069" + integrity sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.12.1" + +"@babel/plugin-syntax-class-properties@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz#bcb297c5366e79bebadef509549cd93b04f19978" + integrity sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.12.1.tgz#a77670d9abe6d63e8acadf4c31bb1eb5a506bbdd" + integrity sha512-1lBLLmtxrwpm4VKmtVFselI/P3pX+G63fAtUUt6b2Nzgao77KNDwyuRt90Mj2/9pKobtt68FdvjfqohZjg/FCA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz#9d9d357cc818aa7ae7935917c1257f67677a0926" + integrity sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-transform-arrow-functions@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz#8083ffc86ac8e777fbe24b5967c4b2521f3cb2b3" + integrity sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-block-scoped-functions@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz#f2a1a365bde2b7112e0a6ded9067fdd7c07905d9" + integrity sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-block-scoping@^7.0.0": + version "7.12.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.12.tgz#d93a567a152c22aea3b1929bb118d1d0a175cdca" + integrity sha512-VOEPQ/ExOVqbukuP7BYJtI5ZxxsmegTwzZ04j1aF0dkSypGo9XpDHuOrABsJu+ie+penpSJheDJ11x1BEZNiyQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-classes@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz#65e650fcaddd3d88ddce67c0f834a3d436a32db6" + integrity sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-define-map" "^7.10.4" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.10.4" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz#d68cf6c9b7f838a8a4144badbe97541ea0904852" + integrity sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-destructuring@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz#b9a570fe0d0a8d460116413cb4f97e8e08b2f847" + integrity sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-flow-strip-types@^7.0.0": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.12.10.tgz#d85e30ecfa68093825773b7b857e5085bbd32c95" + integrity sha512-0ti12wLTLeUIzu9U7kjqIn4MyOL7+Wibc7avsHhj4o1l5C0ATs8p2IMHrVYjm9t9wzhfEO6S3kxax0Rpdo8LTg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-flow" "^7.12.1" + +"@babel/plugin-transform-for-of@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz#07640f28867ed16f9511c99c888291f560921cfa" + integrity sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-function-name@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz#2ec76258c70fe08c6d7da154003a480620eba667" + integrity sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-literals@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz#d73b803a26b37017ddf9d3bb8f4dc58bfb806f57" + integrity sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-member-expression-literals@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz#496038602daf1514a64d43d8e17cbb2755e0c3ad" + integrity sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-modules-commonjs@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz#fa403124542636c786cf9b460a0ffbb48a86e648" + integrity sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag== + dependencies: + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-simple-access" "^7.12.1" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-object-super@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz#4ea08696b8d2e65841d0c7706482b048bed1066e" + integrity sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" + +"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz#d2e963b038771650c922eff593799c96d853255d" + integrity sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-property-literals@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz#41bc81200d730abb4456ab8b3fbd5537b59adecd" + integrity sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-react-display-name@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.12.1.tgz#1cbcd0c3b1d6648c55374a22fc9b6b7e5341c00d" + integrity sha512-cAzB+UzBIrekfYxyLlFqf/OagTvHLcVBb5vpouzkYkBclRPraiygVnafvAoipErZLI8ANv8Ecn6E/m5qPXD26w== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-react-jsx@^7.0.0": + version "7.12.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.12.tgz#b0da51ffe5f34b9a900e9f1f5fb814f9e512d25e" + integrity sha512-JDWGuzGNWscYcq8oJVCtSE61a5+XAOos+V0HrxnDieUus4UMnBEosDnY1VJqU5iZ4pA04QY7l0+JvHL1hZEfsw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.12.10" + "@babel/helper-module-imports" "^7.12.5" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-jsx" "^7.12.1" + "@babel/types" "^7.12.12" + +"@babel/plugin-transform-shorthand-properties@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz#0bf9cac5550fce0cfdf043420f661d645fdc75e3" + integrity sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-spread@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz#527f9f311be4ec7fdc2b79bb89f7bf884b3e1e1e" + integrity sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" + +"@babel/plugin-transform-template-literals@^7.0.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz#b43ece6ed9a79c0c71119f576d299ef09d942843" + integrity sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/runtime@7.12.5", "@babel/runtime@^7.0.0": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" + integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.10.4", "@babel/template@^7.12.7": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.7.tgz#c817233696018e39fbb6c491d2fb684e05ed43bc" + integrity sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.12.7" + "@babel/types" "^7.12.7" + +"@babel/traverse@7.12.12", "@babel/traverse@^7.0.0", "@babel/traverse@^7.12.1", "@babel/traverse@^7.12.10", "@babel/traverse@^7.12.5": + version "7.12.12" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.12.tgz#d0cd87892704edd8da002d674bc811ce64743376" + integrity sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w== + dependencies: + "@babel/code-frame" "^7.12.11" + "@babel/generator" "^7.12.11" + "@babel/helper-function-name" "^7.12.11" + "@babel/helper-split-export-declaration" "^7.12.11" + "@babel/parser" "^7.12.11" + "@babel/types" "^7.12.12" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + +"@babel/types@7.12.12", "@babel/types@^7.0.0", "@babel/types@^7.10.5", "@babel/types@^7.12.0", "@babel/types@^7.12.1", "@babel/types@^7.12.10", "@babel/types@^7.12.11", "@babel/types@^7.12.12", "@babel/types@^7.12.5", "@babel/types@^7.12.7": + version "7.12.12" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.12.tgz#4608a6ec313abbd87afa55004d373ad04a96c299" + integrity sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + lodash "^4.17.19" + 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" + integrity sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg== + dependencies: + esutils "^2.0.2" + lodash "^4.17.13" + to-fast-properties "^2.0.0" + +"@csstools/convert-colors@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7" + integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw== + +"@endemolshinegroup/cosmiconfig-typescript-loader@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz#eea4635828dde372838b0909693ebd9aafeec22d" + integrity sha512-QRVtqJuS1mcT56oHpVegkKBlgtWjXw/gHNWO3eL9oyB5Sc7HBoc2OLG/nYpVfT/Jejvo3NUrD0Udk7XgoyDKkA== + dependencies: + lodash.get "^4" + make-error "^1" + ts-node "^9" + tslib "^2" + +"@fullhuman/postcss-purgecss@^2.1.2": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@fullhuman/postcss-purgecss/-/postcss-purgecss-2.3.0.tgz#50a954757ec78696615d3e118e3fee2d9291882e" + integrity sha512-qnKm5dIOyPGJ70kPZ5jiz0I9foVOic0j+cOzNDoo8KoCf6HjicIZ99UfO2OmE7vCYSKAAepEwJtNzpiiZAh9xw== + dependencies: + postcss "7.0.32" + purgecss "^2.3.0" + +"@graphql-codegen/cli@^1.20.0": + version "1.20.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-1.20.0.tgz#e1bb62fce07caaf1395ca6e94ffc0f2ba1f57938" + integrity sha512-5pLtZoaqEmEui6PR7IArmD23VLD3++UQby6iNe4NFG4eMcRai2raIM0E4a/MSn7SjyfSRguekYMMC5JKS1VgQw== + dependencies: + "@graphql-codegen/core" "1.17.9" + "@graphql-codegen/plugin-helpers" "^1.18.2" + "@graphql-tools/apollo-engine-loader" "^6" + "@graphql-tools/code-file-loader" "^6" + "@graphql-tools/git-loader" "^6" + "@graphql-tools/github-loader" "^6" + "@graphql-tools/graphql-file-loader" "^6" + "@graphql-tools/json-file-loader" "^6" + "@graphql-tools/load" "^6" + "@graphql-tools/prisma-loader" "^6" + "@graphql-tools/url-loader" "^6" + "@graphql-tools/utils" "^7.0.0" + ansi-escapes "^4.3.1" + camel-case "^4.1.2" + chalk "^4.1.0" + chokidar "^3.4.3" + common-tags "^1.8.0" + constant-case "^3.0.3" + cosmiconfig "^7.0.0" + debounce "^1.2.0" + dependency-graph "^0.9.0" + detect-indent "^6.0.0" + glob "^7.1.6" + graphql-config "^3.2.0" + indent-string "^4.0.0" + inquirer "^7.3.3" + is-glob "^4.0.1" + json-to-pretty-yaml "^1.2.2" + latest-version "5.1.0" + listr "^0.14.3" + listr-update-renderer "^0.5.0" + log-symbols "^4.0.0" + lower-case "^2.0.1" + minimatch "^3.0.4" + mkdirp "^1.0.4" + pascal-case "^3.1.1" + request "^2.88.2" + string-env-interpolation "^1.0.1" + ts-log "^2.2.3" + tslib "~2.0.1" + upper-case "^2.0.2" + valid-url "^1.0.9" + wrap-ansi "^7.0.0" + yaml "^1.10.0" + yargs "^16.1.1" + +"@graphql-codegen/core@1.17.9": + version "1.17.9" + resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-1.17.9.tgz#c03e71018ff04d26f5139a2d90a32b31d3bb2b43" + integrity sha512-7nwy+bMWqb0iYJ2DKxA9UiE16meeJ2Ch2XWS/N/ZnA0snTR+GZ20USI8z6YqP1Fuist7LvGO1MbitO2qBT8raA== + dependencies: + "@graphql-codegen/plugin-helpers" "^1.18.2" + "@graphql-tools/merge" "^6" + "@graphql-tools/utils" "^6" + tslib "~2.0.1" + +"@graphql-codegen/plugin-helpers@^1.18.2": + version "1.18.2" + resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-1.18.2.tgz#57011076cb8b8f5d04d37d226a5eda300c01be94" + integrity sha512-SvX+Ryq2naLcoD6jJNxtzc/moWTgHJ+X0KRfvhGWTa+xtFTS02i+PWOR89YYPcD8+LF6GmyFBjx2FmLCx4JwMg== + dependencies: + "@graphql-tools/utils" "^6" + camel-case "4.1.1" + common-tags "1.8.0" + constant-case "3.0.3" + import-from "3.0.0" + lodash "~4.17.20" + lower-case "2.0.1" + param-case "3.0.3" + pascal-case "3.1.1" + tslib "~2.0.1" + upper-case "2.0.1" + +"@graphql-codegen/schema-ast@^1.18.1": + version "1.18.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-1.18.1.tgz#4081741b940944f883eec26f031840283f877210" + integrity sha512-uBVPqDZVvV1i1mBTp+DQldBO5AO4PAetZrAJJhQk5gaYxvKlf7YZWRT2WFwRdH1GEdU35tRxUX8XKSkxaKct/w== + dependencies: + "@graphql-codegen/plugin-helpers" "^1.18.2" + "@graphql-tools/utils" "^6" + tslib "~2.0.1" + +"@graphql-codegen/typescript-operations@^1.17.13": + version "1.17.13" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-1.17.13.tgz#a5b08c1573b9507ca5a9e66e795aecc40ddc5305" + integrity sha512-Wm/S4pmPy+KPvFVpygNwC4pd9zKtGIwnS+2rlMUBZVSpv4fxjX2YDvYHP/gucck+SiS0RRxB7hW65bTwCns46g== + dependencies: + "@graphql-codegen/plugin-helpers" "^1.18.2" + "@graphql-codegen/typescript" "^1.18.1" + "@graphql-codegen/visitor-plugin-common" "^1.17.22" + auto-bind "~4.0.0" + tslib "~2.0.1" + +"@graphql-codegen/typescript@^1.18.1", "@graphql-codegen/typescript@^1.19.0": + version "1.20.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-1.20.0.tgz#f9a17b869e5691276965a56c7a1efe4eb938b6e7" + integrity sha512-7xW+n0USNpr6iZ4Et17ZbPzBLNe/LrSgrQ6V/8Mlgp1reQWAZtoVw13Oq4GnxHCzAYio8nFindLl+emW9ZBeew== + dependencies: + "@graphql-codegen/plugin-helpers" "^1.18.2" + "@graphql-codegen/visitor-plugin-common" "^1.18.0" + auto-bind "~4.0.0" + tslib "~2.0.1" + +"@graphql-codegen/visitor-plugin-common@^1.17.22", "@graphql-codegen/visitor-plugin-common@^1.18.0": + version "1.18.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.18.0.tgz#f4366ec1093c01e752e85f8bd30d09c58b1d6951" + integrity sha512-OR/Cm9nVaqMLe94Ski60UDhaAf/4+QeV/CQRYrCmxFzEsm03U41VplcNgJBXIAB3EgP/LEIZtgkRc1H4N9u9+Q== + dependencies: + "@graphql-codegen/plugin-helpers" "^1.18.2" + "@graphql-tools/optimize" "^1.0.1" + "@graphql-tools/relay-operation-optimizer" "^6" + array.prototype.flatmap "^1.2.4" + auto-bind "~4.0.0" + dependency-graph "^0.9.0" + graphql-tag "^2.11.0" + parse-filepath "^1.0.2" + pascal-case "^3.1.1" + tslib "~2.0.1" + +"@graphql-tools/apollo-engine-loader@^6": + version "6.2.5" + resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-6.2.5.tgz#b9e65744f522bb9f6ca50651e5622820c4f059a8" + integrity sha512-CE4uef6PyxtSG+7OnLklIr2BZZDgjO89ZXK47EKdY7jQy/BQD/9o+8SxPsgiBc+2NsDJH2I6P/nqoaJMOEat6g== + dependencies: + "@graphql-tools/utils" "^7.0.0" + cross-fetch "3.0.6" + tslib "~2.0.1" + +"@graphql-tools/batch-execute@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-7.0.0.tgz#e79d11bd5b39f29172f6ec2eafa71103c6a6c85b" + integrity sha512-+ywPfK6N2Ddna6oOa5Qb1Mv7EA8LOwRNOAPP9dL37FEhksJM9pYqPSceUcqMqg7S9b0+Cgr78s408rgvurV3/Q== + dependencies: + "@graphql-tools/utils" "^7.0.0" + dataloader "2.0.0" + is-promise "4.0.0" + tslib "~2.0.1" + +"@graphql-tools/code-file-loader@^6": + version "6.2.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-6.2.6.tgz#f89ffb1a5ca48c67dcf2cff97e1a5d06eabc81c2" + integrity sha512-oDuMiXy1Rj1KszY7no+PFNzw2H25PVJKg9K/deK+IHL1631Q+VLK6/czBIw4TMEsbYhlKErgWDI+XBzK73VZSQ== + dependencies: + "@graphql-tools/graphql-tag-pluck" "^6.2.6" + "@graphql-tools/utils" "^7.0.0" + tslib "~2.0.1" + +"@graphql-tools/delegate@^7.0.1", "@graphql-tools/delegate@^7.0.7": + version "7.0.8" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-7.0.8.tgz#72254c92d4254c4f8477a36f7e6c61b48265dcd5" + integrity sha512-pS1wci7ZxzdCITRrMI66UA+6/E0Z1Yczd3QxJBDb4Kp0nTGy1xy7enGa0+i55EmCvKvuwyx+tzXzwA1fNGRJzg== + dependencies: + "@ardatan/aggregate-error" "0.0.6" + "@graphql-tools/batch-execute" "^7.0.0" + "@graphql-tools/schema" "^7.0.0" + "@graphql-tools/utils" "^7.1.6" + dataloader "2.0.0" + is-promise "4.0.0" + tslib "~2.0.1" + +"@graphql-tools/git-loader@^6": + version "6.2.5" + resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-6.2.5.tgz#a4b3e8826964e1752a3d3a5a33a44b70b9694353" + integrity sha512-WOQDSzazyPZMZUvymHBv5oZ80/mS7tc8XUNy2GmU5My8YRny5zu4fEgP4vQeFcD1trG3uoHUaJPGF7Mmvp6Yhg== + dependencies: + "@graphql-tools/graphql-tag-pluck" "^6.2.6" + "@graphql-tools/utils" "^7.0.0" + tslib "~2.0.1" + +"@graphql-tools/github-loader@^6": + version "6.2.5" + resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-6.2.5.tgz#460dff6f5bbaa26957a5ea3be4f452b89cc6a44b" + integrity sha512-DLuQmYeNNdPo8oWus8EePxWCfCAyUXPZ/p1PWqjrX/NGPyH2ZObdqtDAfRHztljt0F/qkBHbGHCEk2TKbRZTRw== + dependencies: + "@graphql-tools/graphql-tag-pluck" "^6.2.6" + "@graphql-tools/utils" "^7.0.0" + cross-fetch "3.0.6" + tslib "~2.0.1" + +"@graphql-tools/graphql-file-loader@^6", "@graphql-tools/graphql-file-loader@^6.0.0": + version "6.2.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-6.2.6.tgz#5b907d21b0f947df892ed837db74cd3f6d771c34" + integrity sha512-L+RdYl5C6+X0zdOTUotY0K5zwqvSGpqI/qcZpVvCDenoAcVTyaNLmnd/ViErwedhCaGqAAV0wI1nPtyKFPlMUg== + dependencies: + "@graphql-tools/import" "^6.2.5" + "@graphql-tools/utils" "^7.0.0" + tslib "~2.0.1" + +"@graphql-tools/graphql-tag-pluck@^6.2.6": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-6.4.0.tgz#f8303606f717174a1068501294805b3304417145" + integrity sha512-qBQ0ITgMkUzxz5QsZk9HsSeOQo6KlXd/Ex5cePzPuRT72odPEQXNL9hCsxaCvZO9r3in8bmG6OTWs4ms/GevXA== + dependencies: + "@babel/parser" "7.12.11" + "@babel/traverse" "7.12.12" + "@babel/types" "7.12.12" + "@graphql-tools/utils" "^7.0.0" + tslib "~2.1.0" + optionalDependencies: + "@vue/compiler-sfc" "^3.0.4" + +"@graphql-tools/import@^6.2.5": + version "6.2.5" + resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-6.2.5.tgz#5f279815229320128a07cad188c4860be18cb422" + integrity sha512-ZGXT5tDod7m+LO38fc+o0JzR1LstL0RF35HKEWoUdxRIVaaeYH9VMuan9Gn+9M9RDME3RnzEa9aGzf9ATj8bTA== + dependencies: + resolve-from "5.0.0" + tslib "~2.0.1" + +"@graphql-tools/json-file-loader@^6", "@graphql-tools/json-file-loader@^6.0.0": + version "6.2.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-6.2.6.tgz#830482cfd3721a0799cbf2fe5b09959d9332739a" + integrity sha512-CnfwBSY5926zyb6fkDBHnlTblHnHI4hoBALFYXnrg0Ev4yWU8B04DZl/pBRUc459VNgO2x8/mxGIZj2hPJG1EA== + dependencies: + "@graphql-tools/utils" "^7.0.0" + tslib "~2.0.1" + +"@graphql-tools/load@^6", "@graphql-tools/load@^6.0.0": + version "6.2.5" + resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-6.2.5.tgz#7dd0d34c8ce2cfb24f61c6beba2817d9afdd7f2b" + integrity sha512-TpDgp+id0hhD1iMhdFSgWgWumdI/IpFWwouJeaEhEEAEBkdvH4W9gbBiJBSbPQwMPRNWx8/AZtry0cYKLW4lHg== + dependencies: + "@graphql-tools/merge" "^6.2.5" + "@graphql-tools/utils" "^7.0.0" + globby "11.0.1" + import-from "3.0.0" + is-glob "4.0.1" + p-limit "3.0.2" + tslib "~2.0.1" + unixify "1.0.0" + valid-url "1.0.9" + +"@graphql-tools/merge@^6", "@graphql-tools/merge@^6.0.0", "@graphql-tools/merge@^6.2.5": + version "6.2.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-6.2.6.tgz#f10b8958523687440536ecf166f2959d2f094d0f" + integrity sha512-G6x0QlIzFHoJ3dyF9a4gxmBtaEYJ+EoAAGqXHsE/drRr58K1jscQdfKZdF1wZWZgxkgakHqgt1+oFMeQg/O6ug== + dependencies: + "@graphql-tools/schema" "^7.0.0" + "@graphql-tools/utils" "^7.0.0" + tslib "~2.0.1" + +"@graphql-tools/optimize@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-1.0.1.tgz#9933fffc5a3c63f95102b1cb6076fb16ac7bb22d" + integrity sha512-cRlUNsbErYoBtzzS6zXahXeTBZGPVlPHXCpnEZ0XiK/KY/sQL96cyzak0fM/Gk6qEI9/l32MYEICjasiBQrl5w== + dependencies: + tslib "~2.0.1" + +"@graphql-tools/prisma-loader@^6": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@graphql-tools/prisma-loader/-/prisma-loader-6.2.7.tgz#0a9aa8f40c79a926f2d4f157dc282478bccafaca" + integrity sha512-o0QHl767uaLZVjb9NlupZjCzjfC5Zo79G6QLnK0Rbi0Ldk5Lf05HDZIfMhiyd9tsw73d0GQY7yIPvQJFE2S5Tw== + dependencies: + "@graphql-tools/url-loader" "^6.3.1" + "@graphql-tools/utils" "^7.0.0" + "@types/http-proxy-agent" "^2.0.2" + "@types/js-yaml" "^3.12.5" + "@types/json-stable-stringify" "^1.0.32" + "@types/jsonwebtoken" "^8.5.0" + ajv "^6.12.6" + bluebird "^3.7.2" + chalk "^4.1.0" + debug "^4.2.0" + dotenv "^8.2.0" + graphql-request "^3.3.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + isomorphic-fetch "^3.0.0" + js-yaml "^3.14.0" + json-stable-stringify "^1.0.1" + jsonwebtoken "^8.5.1" + lodash "^4.17.20" + replaceall "^0.1.6" + scuid "^1.1.0" + tslib "~2.0.1" + yaml-ast-parser "^0.0.43" + +"@graphql-tools/relay-operation-optimizer@^6": + version "6.3.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.3.0.tgz#f8c7f6c8aa4a9cf50ab151fbc5db4f4282a79532" + integrity sha512-Or3UgRvkY9Fq1AAx7q38oPqFmTepLz7kp6wDHKyR0ceG7AvHv5En22R12mAeISInbhff4Rpwgf6cE8zHRu6bCw== + dependencies: + "@graphql-tools/utils" "^7.1.0" + relay-compiler "10.1.0" + tslib "~2.0.1" + +"@graphql-tools/schema@^7.0.0", "@graphql-tools/schema@^7.1.2": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-7.1.2.tgz#5084eaef893719ad01329f77673d102e7710542e" + integrity sha512-GabNT51ErVHE2riDH4EQdRusUsI+nMElT8LdFHyuP53v8gwtleAj+LePQ9jif4NYUe/JQVqO8V28vPcHrA7gfQ== + dependencies: + "@graphql-tools/utils" "^7.1.2" + tslib "~2.0.1" + +"@graphql-tools/url-loader@^6", "@graphql-tools/url-loader@^6.0.0", "@graphql-tools/url-loader@^6.3.1": + version "6.7.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-6.7.1.tgz#ce4d2284b702a360d928e74e7f989d8579f0d9f6" + integrity sha512-7NJ1G5diJAuWYZszQf0mNwPipVMOjIIMteNkutdExBq4CgN0V1xa3/iC25CUrI7sZiq+D367zZNONmKf+3bA2Q== + dependencies: + "@graphql-tools/delegate" "^7.0.1" + "@graphql-tools/utils" "^7.1.5" + "@graphql-tools/wrap" "^7.0.4" + "@types/websocket" "1.0.1" + cross-fetch "3.0.6" + eventsource "1.0.7" + extract-files "9.0.0" + graphql-upload "^11.0.0" + graphql-ws "3.1.0" + is-promise "4.0.0" + isomorphic-form-data "2.0.0" + isomorphic-ws "4.0.1" + sse-z "0.3.0" + sync-fetch "0.3.0" + tslib "~2.0.1" + valid-url "1.0.9" + ws "7.4.1" + +"@graphql-tools/utils@^6", "@graphql-tools/utils@^6.0.0": + version "6.2.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-6.2.4.tgz#38a2314d2e5e229ad4f78cca44e1199e18d55856" + integrity sha512-ybgZ9EIJE3JMOtTrTd2VcIpTXtDrn2q6eiYkeYMKRVh3K41+LZa6YnR2zKERTXqTWqhobROwLt4BZbw2O3Aeeg== + dependencies: + "@ardatan/aggregate-error" "0.0.6" + camel-case "4.1.1" + tslib "~2.0.1" + +"@graphql-tools/utils@^7.0.0", "@graphql-tools/utils@^7.1.0", "@graphql-tools/utils@^7.1.2", "@graphql-tools/utils@^7.1.5", "@graphql-tools/utils@^7.1.6", "@graphql-tools/utils@^7.2.1": + version "7.2.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-7.2.4.tgz#1164cf268988254f281b4cfbbc0e8f7ca24a8a41" + integrity sha512-EDSb98dTWX8FngvayWejip1DutOl0wGtNbXC7a3CZf5fiJS7bGHQ/8cSlMhe9XaHwpLJCbAk/Ijnp/dYbXk33w== + dependencies: + "@ardatan/aggregate-error" "0.0.6" + camel-case "4.1.2" + tslib "~2.1.0" + +"@graphql-tools/wrap@^7.0.4": + version "7.0.5" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-7.0.5.tgz#8659a119abef11754f712b0c202e41a484951e0b" + integrity sha512-KCWBXsDfvG46GNUawRltJL4j9BMGoOG7oo3WEyCQP+SByWXiTe5cBF45SLDVQgdjljGNZhZ4Lq/7avIkF7/zDQ== + dependencies: + "@graphql-tools/delegate" "^7.0.7" + "@graphql-tools/schema" "^7.1.2" + "@graphql-tools/utils" "^7.2.1" + is-promise "4.0.0" + tslib "~2.0.1" + +"@hapi/accept@5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@hapi/accept/-/accept-5.0.1.tgz#068553e867f0f63225a506ed74e899441af53e10" + integrity sha512-fMr4d7zLzsAXo28PRRQPXR1o2Wmu+6z+VY1UzDp0iFo13Twj8WePakwXBiqn3E1aAlTpSNzCXdnnQXFhst8h8Q== + dependencies: + "@hapi/boom" "9.x.x" + "@hapi/hoek" "9.x.x" + +"@hapi/boom@9.x.x": + version "9.1.1" + resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-9.1.1.tgz#89e6f0e01637c2a4228da0d113e8157c93677b04" + integrity sha512-VNR8eDbBrOxBgbkddRYIe7+8DZ+vSbV6qlmaN2x7eWjsUjy2VmQgChkOKcVZIeupEZYj+I0dqNg430OhwzagjA== + dependencies: + "@hapi/hoek" "9.x.x" + +"@hapi/hoek@9.x.x": + version "9.1.1" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.1.1.tgz#9daf5745156fd84b8e9889a2dc721f0c58e894aa" + integrity sha512-CAEbWH7OIur6jEOzaai83jq3FmKmv4PmX1JYfs9IrYcGEVI/lyL1EXJGCj7eFVJ0bg5QR8LMxBlEtA+xKiLpFw== + +"@iarna/toml@^2.2.5": + version "2.2.5" + resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" + integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== + +"@manifoldco/swagger-to-ts@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@manifoldco/swagger-to-ts/-/swagger-to-ts-2.1.0.tgz#b52a429e5b4ab3627571d3e9ae7399cf5cd4c454" + integrity sha512-IH0FAHhwWHR3Gs3rnVHNEscZujGn+K6/2Zu5cWfZre3Vz2tx1SvvJKEbSM89MztfDDRjOpb+6pQD/vqdEoTBVg== + dependencies: + chalk "^4.0.0" + js-yaml "^3.13.1" + meow "^7.0.0" + prettier "^2.0.5" + +"@next/bundle-analyzer@^10.0.1": + version "10.0.5" + resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-10.0.5.tgz#02eda5d88bc1ee8efff03902083bcb2c537e787d" + integrity sha512-fDhursKrqycV7u6crESINKQKp5/Q17Xd9mI1n0BFhIvpfp8br/gSqLHeaN2DXfcOS/Rb0/FmY4pdIFjXnwdZbg== + dependencies: + webpack-bundle-analyzer "4.3.0" + +"@next/env@10.0.5": + version "10.0.5" + resolved "https://registry.yarnpkg.com/@next/env/-/env-10.0.5.tgz#446e59ee7a8d05061be784b24732c369653038ab" + integrity sha512-dw6Q7PALIo7nTFfaB4OnRDte3EikrApGtjX/4cRmYXLh+uudDI50aS39MaGeKKZ2mxPKoNiFcY6Slv1f6KIPOw== + +"@next/polyfill-module@10.0.5": + version "10.0.5" + resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-10.0.5.tgz#f2de3deee8694cc75094fea4e91225724b3a21e1" + integrity sha512-R6ySTTIl6yqyO3Xft7h+QR9Z4e6epEw+AGO125CrwPmCDQ8ASLGltsHYvSHBP7Eae7xaorkXHW0jeI8NewLpew== + +"@next/react-dev-overlay@10.0.5": + version "10.0.5" + resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-10.0.5.tgz#f0006e56d8c8b4269f341ff22233e4a7e1176b52" + integrity sha512-olqIaaRvFezzi02V/AYwvmrGbShUNrJDvyZTGNahxXEkiYsuu67llY5IKFM5Oya93/eRqaCCKMn89vpvYtvDcw== + 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.0.5": + version "10.0.5" + resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-10.0.5.tgz#c1ca664c4ffe2f79d14383323d783368833d503b" + integrity sha512-Eo+nvW1ZhdkuxBWSsKHGDLNspUaJJQClld9R+H+EtiIuAQtTa8f2rhcQeyUOtvUNQoPyq7Em2PwUqOQD6LOOMA== + +"@nodelib/fs.scandir@2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" + integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA== + dependencies: + "@nodelib/fs.stat" "2.0.4" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655" + integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063" + integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow== + dependencies: + "@nodelib/fs.scandir" "2.1.4" + fastq "^1.6.0" + +"@opentelemetry/api@0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-0.14.0.tgz#4e17d8d2f1da72b19374efa7b6526aa001267cae" + integrity sha512-L7RMuZr5LzMmZiQSQDy9O1jo0q+DaLy6XpYJfIGfYSfoJA5qzYwUP3sP1uMIQ549DvxAgM3ng85EaPTM/hUHwQ== + dependencies: + "@opentelemetry/context-base" "^0.14.0" + +"@opentelemetry/context-base@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/context-base/-/context-base-0.14.0.tgz#c67fc20a4d891447ca1a855d7d70fa79a3533001" + integrity sha512-sDOAZcYwynHFTbLo6n8kIbLiVF3a3BLkrmehJUyEbT9F+Smbi47kLGS2gG2g0fjBLR/Lr1InPD7kXL7FaTqEkw== + +"@polka/url@^1.0.0-next.9": + version "1.0.0-next.11" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.11.tgz#aeb16f50649a91af79dbe36574b66d0f9e4d9f71" + integrity sha512-3NsZsJIA/22P3QUyrEDNA2D133H4j224twJrdipXN38dpnIOzAbUDtOwkcJ5pXmn75w7LSQDjA4tO9dm1XlqlA== + +"@reach/portal@^0.11.2": + version "0.11.2" + resolved "https://registry.yarnpkg.com/@reach/portal/-/portal-0.11.2.tgz#19a671be9ff010a345892b81e710cb6e4d9f9762" + integrity sha512-/53A/rY5oX2Y7D5TpvsP+V5cSd+4MPY6f21mAmVn4DCVwpkCFOlJ059ZL7ixS85M0Jz48YQnnvBJUqwkxqUG/g== + dependencies: + "@reach/utils" "0.11.2" + tslib "^2.0.0" + +"@reach/utils@0.11.2": + version "0.11.2" + resolved "https://registry.yarnpkg.com/@reach/utils/-/utils-0.11.2.tgz#be1f03650db56fd67a16d3fc70e5262cdb139cec" + integrity sha512-fBTolYj+rKTROXmf0zHO0rCWSvw7J0ALmYj5QxW4DmITMOH5uyRuWDWOfqohIGFbOtF/sum50WTB3tvx76d+Aw== + dependencies: + "@types/warning" "^3.0.0" + tslib "^2.0.0" + warning "^4.0.3" + +"@samverschueren/stream-to-observable@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz#a21117b19ee9be70c379ec1877537ef2e1c63301" + integrity sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ== + dependencies: + any-observable "^0.3.0" + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@tailwindcss/custom-forms@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@tailwindcss/custom-forms/-/custom-forms-0.2.1.tgz#40e5ed1fff6d29d8ed1c508a0b2aaf8da96962e0" + integrity sha512-XdP5XY6kxo3x5o50mWUyoYWxOPV16baagLoZ5uM41gh6IhXzhz/vJYzqrTb/lN58maGIKlpkxgVsQUNSsbAS3Q== + dependencies: + lodash "^4.17.11" + mini-svg-data-uri "^1.0.3" + traverse "^0.6.6" + +"@tailwindcss/typography@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.2.0.tgz#b597c83502e3c3c6641a8aaabda223cd494ab349" + integrity sha512-aPgMH+CjQiScLZculoDNOQUrrK2ktkbl3D6uCLYp1jgYRlNDrMONu9nMu8LfwAeetYNpVNeIGx7WzHSu0kvECg== + +"@tailwindcss/ui@^0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@tailwindcss/ui/-/ui-0.6.2.tgz#4144aba86543bf79fefe0ea14a78a12fb315810e" + integrity sha512-i0sWpAgnF4VitNqaf4JVDuiaQ3MmREwn7gmNYR6lvX29avYLLNOHf0DgzhVEfwfB+CJ6WjZvPoJuDYExZgWlwQ== + dependencies: + "@tailwindcss/custom-forms" "^0.2.1" + "@tailwindcss/typography" "^0.2.0" + hex-rgb "^4.1.0" + postcss-selector-parser "^6.0.2" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@types/async-retry@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@types/async-retry/-/async-retry-1.2.1.tgz#fa9ac165907a8ee78f4924f4e393b656c65b5bb4" + integrity sha512-yMQ6CVgICWtyFNBqJT3zqOc+TnqqEPLo4nKJNPFwcialiylil38Ie6q1ENeFTjvaLOkVim9K5LisHgAKJWidGQ== + +"@types/body-scroll-lock@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@types/body-scroll-lock/-/body-scroll-lock-2.6.1.tgz#0dbd2b6ad2f4cfcece7102d6cf8630ce95508ee0" + integrity sha512-PPFm/2A6LfKmSpvMg58gHtSqwwMChbcKKGhSCRIhY4MyFzhY8moAN6HrTCpOeZQUqkFdTFfMqr7njeqGLKt72Q== + +"@types/bunyan-prettystream@^0.1.31": + version "0.1.31" + resolved "https://registry.yarnpkg.com/@types/bunyan-prettystream/-/bunyan-prettystream-0.1.31.tgz#3864836abb907ab151f7edf7c64c323c9609e1d1" + integrity sha512-NE7fq2ZcX7OSMK+VhTNJkVEHlo+hm0uVXpuLeH1ifGm52Qwuo/kLD2GHo7UcEXMFu3duKver/AFo8C4TME93zw== + dependencies: + "@types/node" "*" + +"@types/bunyan@^1.8.6": + version "1.8.6" + resolved "https://registry.yarnpkg.com/@types/bunyan/-/bunyan-1.8.6.tgz#6527641cca30bedec5feb9ab527b7803b8000582" + integrity sha512-YiozPOOsS6bIuz31ilYqR5SlLif4TBWsousN2aCWLi5233nZSX19tFbcQUPdR7xJ8ypPyxkCGNxg0CIV5n9qxQ== + dependencies: + "@types/node" "*" + +"@types/classnames@^2.2.10": + version "2.2.11" + resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.11.tgz#2521cc86f69d15c5b90664e4829d84566052c1cf" + integrity sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw== + +"@types/cookie@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.0.tgz#14f854c0f93d326e39da6e3b6f34f7d37513d108" + integrity sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg== + +"@types/eslint-scope@^3.7.0": + version "3.7.0" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.0.tgz#4792816e31119ebd506902a482caec4951fabd86" + integrity sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "7.2.6" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.6.tgz#5e9aff555a975596c03a98b59ecd103decc70c3c" + integrity sha512-I+1sYH+NPQ3/tVqCeUSBwTE/0heyvtXqpIopUUArlBm0Kpocb8FbMa3AZ/ASKIFpN3rnEx932TTXDbt9OXsNDw== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^0.0.45": + version "0.0.45" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884" + integrity sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g== + +"@types/http-proxy-agent@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@types/http-proxy-agent/-/http-proxy-agent-2.0.2.tgz#942c1f35c7e1f0edd1b6ffae5d0f9051cfb32be1" + integrity sha512-2S6IuBRhqUnH1/AUx9k8KWtY3Esg4eqri946MnxTG5HwehF1S5mqLln8fcyMiuQkY72p2gH3W+rIPqp5li0LyQ== + dependencies: + "@types/node" "*" + +"@types/js-cookie@^2.2.6": + version "2.2.6" + resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.6.tgz#f1a1cb35aff47bc5cfb05cb0c441ca91e914c26f" + integrity sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw== + +"@types/js-yaml@^3.12.5": + version "3.12.6" + resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.6.tgz#7f10c926aa41e189a2755c4c7fcf8e4573bd7ac1" + integrity sha512-cK4XqrLvP17X6c0C8n4iTbT59EixqyXL3Fk8/Rsk4dF3oX4dg70gYUXrXVUUHpnsGMPNlTQMqf+TVmNPX6FmSQ== + +"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" + integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== + +"@types/json-stable-stringify@^1.0.32": + version "1.0.32" + resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.32.tgz#121f6917c4389db3923640b2e68de5fa64dda88e" + integrity sha512-q9Q6+eUEGwQkv4Sbst3J4PNgDOvpuVuKj79Hl/qnmBMEIPzB5QoFRUtjcgcg2xNUZyYUGXBk5wYIBKHt0A+Mxw== + +"@types/jsonwebtoken@^8.5.0": + version "8.5.0" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz#2531d5e300803aa63279b232c014acf780c981c5" + integrity sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg== + dependencies: + "@types/node" "*" + +"@types/lodash.debounce@^4.0.6": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/lodash.debounce/-/lodash.debounce-4.0.6.tgz#c5a2326cd3efc46566c47e4c0aa248dc0ee57d60" + integrity sha512-4WTmnnhCfDvvuLMaF3KV4Qfki93KebocUF45msxhYyjMttZDQYzHkO639ohhk8+oco2cluAFL3t5+Jn4mleylQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash.random@^3.2.6": + version "3.2.6" + resolved "https://registry.yarnpkg.com/@types/lodash.random/-/lodash.random-3.2.6.tgz#64b08abad168dca39c778ed40cce75b2f9e168eb" + integrity sha512-RRr0pKm+3USvG/HTkuRKA8v2EqXu19VXC09j4VL2UQec8Yx8Fn6wYTPGjYdmX4UFd23ykS7SLFkiULS/rv8kTA== + dependencies: + "@types/lodash" "*" + +"@types/lodash.throttle@^4.1.6": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@types/lodash.throttle/-/lodash.throttle-4.1.6.tgz#f5ba2c22244ee42ff6c2c49e614401a870c1009c" + integrity sha512-/UIH96i/sIRYGC60NoY72jGkCJtFN5KVPhEMMMTjol65effe1gPn0tycJqV5tlSwMTzX8FqzB5yAj0rfGHTPNg== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.14.167" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.167.tgz#ce7d78553e3c886d4ea643c37ec7edc20f16765e" + integrity sha512-w7tQPjARrvdeBkX/Rwg95S592JwxqOjmms3zWQ0XZgSyxSLdzWaYH3vErBhdVS/lRBX7F8aBYcYJYTr5TMGOzw== + +"@types/lru-cache@4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-4.1.1.tgz#b2d87a5e3df8d4b18ca426c5105cd701c2306d40" + integrity sha512-8mNEUG6diOrI6pMqOHrHPDBB1JsrpedeMK9AWGzVCQ7StRRribiT9BRvUmF8aUws9iBbVlgVekOT5Sgzc1MTKw== + +"@types/minimist@^1.2.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.1.tgz#283f669ff76d7b8260df8ab7a4262cc83d988256" + integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg== + +"@types/node-fetch@2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.3.2.tgz#e01893b176c6fa1367743726380d65bce5d6576b" + integrity sha512-yW0EOebSsQme9yKu09XbdDfle4/SmWZMK4dfteWcSLCYNQQcF+YOv0kIrvm+9pO11/ghA4E6A+RNQqvYj4Nr3A== + dependencies: + "@types/node" "*" + +"@types/node@*", "@types/node@^14.14.16": + version "14.14.20" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.20.tgz#f7974863edd21d1f8a494a73e8e2b3658615c340" + integrity sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A== + +"@types/node@10.12.18": + version "10.12.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" + integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== + +"@types/normalize-package-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" + integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== + +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + +"@types/prop-types@*": + version "15.7.3" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" + integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== + +"@types/react@^17.0.0": + version "17.0.0" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.0.tgz#5af3eb7fad2807092f0046a1302b7823e27919b8" + integrity sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + +"@types/warning@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52" + integrity sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI= + +"@types/websocket@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.1.tgz#039272c196c2c0e4868a0d8a1a27bbb86e9e9138" + integrity sha512-f5WLMpezwVxCLm1xQe/kdPpQIOmL0TXYx2O15VYfYzc7hTIdxiOoOvez+McSIw3b7z/1zGovew9YSL7+h4h7/Q== + dependencies: + "@types/node" "*" + +"@typescript-eslint/typescript-estree@^2.29.0": + version "2.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5" + integrity sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg== + dependencies: + debug "^4.1.1" + eslint-visitor-keys "^1.1.0" + glob "^7.1.6" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + +"@vercel/fetch-cached-dns@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@vercel/fetch-cached-dns/-/fetch-cached-dns-2.0.1.tgz#b929ba5b4b6f7108abf49adaf03309159047c134" + integrity sha512-4a2IoekfGUgV/dinAB7Tx5oqA+Pg9I/6x/t8n/yduHmdclP5EdWTN4gPrwOKVECKVn2pV1VxAT8q4toSzwa2Eg== + dependencies: + "@types/node-fetch" "2.3.2" + "@zeit/dns-cached-resolve" "2.1.0" + +"@vercel/fetch-retry@^5.0.2": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@vercel/fetch-retry/-/fetch-retry-5.0.3.tgz#cce5d23f6e64f6f525c24e2ac7c78f65d6c5b1f4" + integrity sha512-DIIoBY92r+sQ6iHSf5WjKiYvkdsDIMPWKYATlE0KcUAj2RV6SZK9UWpUzBRKsofXqedOqpVjrI0IE6AWL7JRtg== + dependencies: + async-retry "^1.3.1" + debug "^3.1.0" + +"@vercel/fetch@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@vercel/fetch/-/fetch-6.1.0.tgz#4959cd264d25e811b46491818a9d9ca5d752a2a9" + integrity sha512-xR0GQggKhPvwEWrqcrobsQFjyR/bDDbX24BkSaRyLzW+8SydKhkBc/mBCUV8h4SBZSlJMJnqhrxjFCZ1uJcqNg== + dependencies: + "@types/async-retry" "1.2.1" + "@vercel/fetch-cached-dns" "^2.0.1" + "@vercel/fetch-retry" "^5.0.2" + agentkeepalive "3.4.1" + debug "3.1.0" + +"@vue/compiler-core@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.0.5.tgz#a6e54cabe9536e74c6513acd2649f311af1d43ac" + integrity sha512-iFXwk2gmU/GGwN4hpBwDWWMLvpkIejf/AybcFtlQ5V1ur+5jwfBaV0Y1RXoR6ePfBPJixtKZ3PmN+M+HgMAtfQ== + dependencies: + "@babel/parser" "^7.12.0" + "@babel/types" "^7.12.0" + "@vue/shared" "3.0.5" + estree-walker "^2.0.1" + source-map "^0.6.1" + +"@vue/compiler-dom@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.0.5.tgz#7885a13e6d18f64dde8ebceec052ed2c102696c2" + integrity sha512-HSOSe2XSPuCkp20h4+HXSiPH9qkhz6YbW9z9ZtL5vef2T2PMugH7/osIFVSrRZP/Ul5twFZ7MIRlp8tPX6e4/g== + dependencies: + "@vue/compiler-core" "3.0.5" + "@vue/shared" "3.0.5" + +"@vue/compiler-sfc@^3.0.4": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.0.5.tgz#3ae08e60244a72faf9598361874fb7bdb5b1d37c" + integrity sha512-uOAC4X0Gx3SQ9YvDC7YMpbDvoCmPvP0afVhJoxRotDdJ+r8VO3q4hFf/2f7U62k4Vkdftp6DVni8QixrfYzs+w== + dependencies: + "@babel/parser" "^7.12.0" + "@babel/types" "^7.12.0" + "@vue/compiler-core" "3.0.5" + "@vue/compiler-dom" "3.0.5" + "@vue/compiler-ssr" "3.0.5" + "@vue/shared" "3.0.5" + consolidate "^0.16.0" + estree-walker "^2.0.1" + hash-sum "^2.0.0" + lru-cache "^5.1.1" + magic-string "^0.25.7" + merge-source-map "^1.1.0" + postcss "^7.0.32" + postcss-modules "^3.2.2" + postcss-selector-parser "^6.0.4" + source-map "^0.6.1" + +"@vue/compiler-ssr@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.0.5.tgz#7661ad891a0be948726c7f7ad1e425253c587b83" + integrity sha512-Wm//Kuxa1DpgjE4P9W0coZr8wklOfJ35Jtq61CbU+t601CpPTK4+FL2QDBItaG7aoUUDCWL5nnxMkuaOgzTBKg== + dependencies: + "@vue/compiler-dom" "3.0.5" + "@vue/shared" "3.0.5" + +"@vue/shared@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.0.5.tgz#c131d88bd6713cc4d93b3bb1372edb1983225ff0" + integrity sha512-gYsNoGkWejBxNO6SNRjOh/xKeZ0H0V+TFzaPzODfBjkAIb0aQgBuixC1brandC/CDJy1wYPwSoYrXpvul7m6yw== + +"@webassemblyjs/ast@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.1.tgz#76c6937716d68bf1484c15139f5ed30b9abc8bb4" + integrity sha512-uMu1nCWn2Wxyy126LlGqRVlhdTOsO/bsBRI4dNq3+6SiSuRKRQX6ejjKgh82LoGAPSq72lDUiQ4FWVaf0PecYw== + dependencies: + "@webassemblyjs/helper-module-context" "1.9.1" + "@webassemblyjs/helper-wasm-bytecode" "1.9.1" + "@webassemblyjs/wast-parser" "1.9.1" + +"@webassemblyjs/floating-point-hex-parser@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.1.tgz#9eb0ff90a1cdeef51f36ba533ed9f06b5cdadd09" + integrity sha512-5VEKu024RySmLKTTBl9q1eO/2K5jk9ZS+2HXDBLA9s9p5IjkaXxWiDb/+b7wSQp6FRdLaH1IVGIfOex58Na2pg== + +"@webassemblyjs/helper-api-error@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.1.tgz#ad89015c4246cd7f5ed0556700237f8b9c2c752f" + integrity sha512-y1lGmfm38djrScwpeL37rRR9f1D6sM8RhMpvM7CYLzOlHVboouZokXK/G88BpzW0NQBSvCCOnW5BFhten4FPfA== + +"@webassemblyjs/helper-buffer@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.1.tgz#186e67ac25f9546ea7939759413987f157524133" + integrity sha512-uS6VSgieHbk/m4GSkMU5cqe/5TekdCzQso4revCIEQ3vpGZgqSSExi4jWpTWwDpAHOIAb1Jfrs0gUB9AA4n71w== + +"@webassemblyjs/helper-code-frame@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.1.tgz#aab177b7cc87a318a8f8664ad68e2c3828ebc42b" + integrity sha512-ZQ2ZT6Evk4DPIfD+92AraGYaFIqGm4U20e7FpXwl7WUo2Pn1mZ1v8VGH8i+Y++IQpxPbQo/UyG0Khs7eInskzA== + dependencies: + "@webassemblyjs/wast-printer" "1.9.1" + +"@webassemblyjs/helper-fsm@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.1.tgz#527e91628e84d13d3573884b3dc4c53a81dcb911" + integrity sha512-J32HGpveEqqcKFS0YbgicB0zAlpfIxJa5MjxDxhu3i5ltPcVfY5EPvKQ1suRguFPehxiUs+/hfkwPEXom/l0lw== + +"@webassemblyjs/helper-module-context@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.1.tgz#778670b3d471f7cf093d1e7c0dde431b54310e16" + integrity sha512-IEH2cMmEQKt7fqelLWB5e/cMdZXf2rST1JIrzWmf4XBt3QTxGdnnLvV4DYoN8pJjOx0VYXsWg+yF16MmJtolZg== + dependencies: + "@webassemblyjs/ast" "1.9.1" + +"@webassemblyjs/helper-wasm-bytecode@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.1.tgz#563f59bcf409ccf469edde168b9426961ffbf6df" + integrity sha512-i2rGTBqFUcSXxyjt2K4vm/3kkHwyzG6o427iCjcIKjOqpWH8SEem+xe82jUk1iydJO250/CvE5o7hzNAMZf0dQ== + +"@webassemblyjs/helper-wasm-section@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.1.tgz#f7988f94c12b01b99a16120cb01dc099b00e4798" + integrity sha512-FetqzjtXZr2d57IECK+aId3D0IcGweeM0CbAnJHkYJkcRTHP+YcMb7Wmc0j21h5UWBpwYGb9dSkK/93SRCTrGg== + dependencies: + "@webassemblyjs/ast" "1.9.1" + "@webassemblyjs/helper-buffer" "1.9.1" + "@webassemblyjs/helper-wasm-bytecode" "1.9.1" + "@webassemblyjs/wasm-gen" "1.9.1" + +"@webassemblyjs/ieee754@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.1.tgz#3b715871ca7d75784717cf9ceca9d7b81374b8af" + integrity sha512-EvTG9M78zP1MmkBpUjGQHZc26DzPGZSLIPxYHCjQsBMo60Qy2W34qf8z0exRDtxBbRIoiKa5dFyWer/7r1aaSQ== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.1.tgz#b2ecaa39f9e8277cc9c707c1ca8b2aa7b27d0b72" + integrity sha512-Oc04ub0vFfLnF+2/+ki3AE+anmW4sv9uNBqb+79fgTaPv6xJsOT0dhphNfL3FrME84CbX/D1T9XT8tjFo0IIiw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.1.tgz#d02d9daab85cda3211e43caf31dca74c260a73b0" + integrity sha512-llkYtppagjCodFjo0alWOUhAkfOiQPQDIc5oA6C9sFAXz7vC9QhZf/f8ijQIX+A9ToM3c9Pq85X0EX7nx9gVhg== + +"@webassemblyjs/wasm-edit@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.1.tgz#e27a6bdbf78e5c72fa812a2fc3cbaad7c3e37578" + integrity sha512-S2IaD6+x9B2Xi8BCT0eGsrXXd8UxAh2LVJpg1ZMtHXnrDcsTtIX2bDjHi40Hio6Lc62dWHmKdvksI+MClCYbbw== + dependencies: + "@webassemblyjs/ast" "1.9.1" + "@webassemblyjs/helper-buffer" "1.9.1" + "@webassemblyjs/helper-wasm-bytecode" "1.9.1" + "@webassemblyjs/helper-wasm-section" "1.9.1" + "@webassemblyjs/wasm-gen" "1.9.1" + "@webassemblyjs/wasm-opt" "1.9.1" + "@webassemblyjs/wasm-parser" "1.9.1" + "@webassemblyjs/wast-printer" "1.9.1" + +"@webassemblyjs/wasm-gen@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.1.tgz#56a0787d1fa7994fdc7bea59004e5bec7189c5fc" + integrity sha512-bqWI0S4lBQsEN5FTZ35vYzfKUJvtjNnBobB1agCALH30xNk1LToZ7Z8eiaR/Z5iVECTlBndoRQV3F6mbEqE/fg== + dependencies: + "@webassemblyjs/ast" "1.9.1" + "@webassemblyjs/helper-wasm-bytecode" "1.9.1" + "@webassemblyjs/ieee754" "1.9.1" + "@webassemblyjs/leb128" "1.9.1" + "@webassemblyjs/utf8" "1.9.1" + +"@webassemblyjs/wasm-opt@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.1.tgz#fbdf8943a825e6dcc4cd69c3e092289fa4aec96c" + integrity sha512-gSf7I7YWVXZ5c6XqTEqkZjVs8K1kc1k57vsB6KBQscSagDNbAdxt6MwuJoMjsE1yWY1tsuL+pga268A6u+Fdkg== + dependencies: + "@webassemblyjs/ast" "1.9.1" + "@webassemblyjs/helper-buffer" "1.9.1" + "@webassemblyjs/wasm-gen" "1.9.1" + "@webassemblyjs/wasm-parser" "1.9.1" + +"@webassemblyjs/wasm-parser@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.1.tgz#5e8352a246d3f605312c8e414f7990de55aaedfa" + integrity sha512-ImM4N2T1MEIond0MyE3rXvStVxEmivQrDKf/ggfh5pP6EHu3lL/YTAoSrR7shrbKNPpeKpGesW1LIK/L4kqduw== + dependencies: + "@webassemblyjs/ast" "1.9.1" + "@webassemblyjs/helper-api-error" "1.9.1" + "@webassemblyjs/helper-wasm-bytecode" "1.9.1" + "@webassemblyjs/ieee754" "1.9.1" + "@webassemblyjs/leb128" "1.9.1" + "@webassemblyjs/utf8" "1.9.1" + +"@webassemblyjs/wast-parser@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.1.tgz#e25ef13585c060073c1db0d6bd94340fdeee7596" + integrity sha512-2xVxejXSvj3ls/o2TR/zI6p28qsGupjHhnHL6URULQRcXmryn3w7G83jQMcT7PHqUfyle65fZtWLukfdLdE7qw== + dependencies: + "@webassemblyjs/ast" "1.9.1" + "@webassemblyjs/floating-point-hex-parser" "1.9.1" + "@webassemblyjs/helper-api-error" "1.9.1" + "@webassemblyjs/helper-code-frame" "1.9.1" + "@webassemblyjs/helper-fsm" "1.9.1" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/wast-printer@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.1.tgz#b9f38e93652037d4f3f9c91584635af4191ed7c1" + integrity sha512-tDV8V15wm7mmbAH6XvQRU1X+oPGmeOzYsd6h7hlRLz6QpV4Ec/KKxM8OpLtFmQPLCreGxTp+HuxtH4pRIZyL9w== + dependencies: + "@webassemblyjs/ast" "1.9.1" + "@webassemblyjs/wast-parser" "1.9.1" + "@xtuc/long" "4.2.2" + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +"@zeit/dns-cached-resolve@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@zeit/dns-cached-resolve/-/dns-cached-resolve-2.1.0.tgz#78583010df1683fdb7b05949b75593c9a8641bc1" + integrity sha512-KD2zyRZEBNs9PJ3/ob7zx0CvR4wM0oV4G5s5gFfPwmM74GpFbUN2pAAivP2AXnUrJ14Nkh8NumNKOzOyc4LbFQ== + dependencies: + "@types/async-retry" "1.2.1" + "@types/lru-cache" "4.1.1" + "@types/node" "10.12.18" + async-retry "1.2.3" + lru-cache "5.1.1" + +abort-controller@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +acorn-node@^1.6.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" + integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== + dependencies: + acorn "^7.0.0" + acorn-walk "^7.0.0" + xtend "^4.0.2" + +acorn-walk@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn-walk@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.0.1.tgz#d265d35db6940a656c715806a448456ee4fa3b7f" + integrity sha512-zn/7dYtoTVkG4EoMU55QlQU4F+m+T7Kren6Vj3C2DapWPnakG/DL9Ns5aPAPW5Ixd3uxXrV/BoMKKVFIazPcdg== + +acorn@^7.0.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.0.4: + version "8.0.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.0.4.tgz#7a3ae4191466a6984eee0fe3407a4f3aa9db8354" + integrity sha512-XNP0PqF1XD19ZlLKvB7cMmnZswW4C/03pRHgirB30uSJTaS3A3V1/P4sS3HPvFmjoriPCJQs+JDSbm4bL1TxGQ== + +adjust-sourcemap-loader@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz#5ae12fb5b7b1c585e80bbb5a63ec163a1a45e61e" + integrity sha512-YBrGyT2/uVQ/c6Rr+t6ZJXniY03YtHGMJQYal368burRGYKqhx9qGTWqcBU5s1CwYY9E/ri63RYyG1IacMZtqw== + dependencies: + loader-utils "^2.0.0" + regex-parser "^2.2.11" + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +agentkeepalive@3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.4.1.tgz#aa95aebc3a749bca5ed53e3880a09f5235b48f0c" + integrity sha512-MPIwsZU9PP9kOrZpyu2042kYA8Fdt/AedQYkYXucHgF9QoD9dXVp0ypuGnHXSR0hTstBxdt85Xkh4JolYfK5wg== + dependencies: + humanize-ms "^1.2.1" + +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.12.3, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.12.6: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +anser@1.4.9: + version "1.4.9" + resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.9.tgz#1f85423a5dcf8da4631a341665ff675b96845760" + integrity sha512-AI+BjTeGt2+WFk4eWcqbQ7snZpDBt8SaLlj0RT2h5xfdWaiy51OjYvqwMrNzJLGy8iOAL6nKDITWO+rd4MkYEA== + +ansi-escapes@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-escapes@^4.2.1, ansi-escapes@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +any-observable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" + integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog== + +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +app-module-path@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" + integrity sha1-ZBqlXft9am8KgUHEucCqULbCTdU= + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +arity-n@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/arity-n/-/arity-n-1.0.4.tgz#d9e76b11733e08569c0847ae7b39b2860b30b745" + integrity sha1-2edrEXM+CFacCEeuezmyhgswt0U= + +array-flatten@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-3.0.0.tgz#6428ca2ee52c7b823192ec600fa3ed2f157cd541" + integrity sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA== + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array.prototype.flatmap@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9" + integrity sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + function-bind "^1.1.1" + +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= + +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +ast-module-types@^2.3.2, ast-module-types@^2.4.0, ast-module-types@^2.6.0, ast-module-types@^2.7.0, ast-module-types@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-2.7.1.tgz#3f7989ef8dfa1fdb82dfe0ab02bdfc7c77a57dd3" + integrity sha512-Rnnx/4Dus6fn7fTqdeLEAn5vUll5w7/vts0RN608yFa6si/rDOUonlIIiwugHBFWjylHjxm9owoSZn71KwG4gw== + +ast-types@0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48" + integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA== + +async-retry@1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.2.3.tgz#a6521f338358d322b1a0012b79030c6f411d1ce0" + integrity sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q== + dependencies: + retry "0.12.0" + +async-retry@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.1.tgz#139f31f8ddce50c0870b0ba558a6079684aaed55" + integrity sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA== + dependencies: + retry "0.12.0" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +auto-bind@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" + integrity sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ== + +autoprefixer@^9.4.5, autoprefixer@^9.6.1: + version "9.8.6" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" + integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== + dependencies: + browserslist "^4.12.0" + caniuse-lite "^1.0.30001109" + colorette "^1.2.1" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^7.0.32" + postcss-value-parser "^4.1.0" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== + dependencies: + object.assign "^4.1.0" + +babel-plugin-syntax-jsx@6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" + integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= + +babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: + version "7.0.0-beta.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" + integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== + +babel-plugin-transform-define@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-define/-/babel-plugin-transform-define-2.0.0.tgz#79c3536635f899aabaf830b194b25519465675a4" + integrity sha512-0dv5RNRUlUKxGYIIErl01lpvi8b7W2R04Qcl1mCj70ahwZcgiklfXnFlh4FGnRh6aayCfSZKdhiMryVzcq5Dmg== + dependencies: + lodash "^4.17.11" + traverse "0.6.6" + +babel-plugin-transform-react-remove-prop-types@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a" + integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA== + +babel-preset-fbjs@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/babel-preset-fbjs/-/babel-preset-fbjs-3.3.0.tgz#a6024764ea86c8e06a22d794ca8b69534d263541" + integrity sha512-7QTLTCd2gwB2qGoi5epSULMHugSVgpcVt5YAeiFO9ABLrutDQzKfGwzxgZHLpugq8qMdg/DhRZDZ5CLKxBkEbw== + dependencies: + "@babel/plugin-proposal-class-properties" "^7.0.0" + "@babel/plugin-proposal-object-rest-spread" "^7.0.0" + "@babel/plugin-syntax-class-properties" "^7.0.0" + "@babel/plugin-syntax-flow" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.0.0" + "@babel/plugin-syntax-object-rest-spread" "^7.0.0" + "@babel/plugin-transform-arrow-functions" "^7.0.0" + "@babel/plugin-transform-block-scoped-functions" "^7.0.0" + "@babel/plugin-transform-block-scoping" "^7.0.0" + "@babel/plugin-transform-classes" "^7.0.0" + "@babel/plugin-transform-computed-properties" "^7.0.0" + "@babel/plugin-transform-destructuring" "^7.0.0" + "@babel/plugin-transform-flow-strip-types" "^7.0.0" + "@babel/plugin-transform-for-of" "^7.0.0" + "@babel/plugin-transform-function-name" "^7.0.0" + "@babel/plugin-transform-literals" "^7.0.0" + "@babel/plugin-transform-member-expression-literals" "^7.0.0" + "@babel/plugin-transform-modules-commonjs" "^7.0.0" + "@babel/plugin-transform-object-super" "^7.0.0" + "@babel/plugin-transform-parameters" "^7.0.0" + "@babel/plugin-transform-property-literals" "^7.0.0" + "@babel/plugin-transform-react-display-name" "^7.0.0" + "@babel/plugin-transform-react-jsx" "^7.0.0" + "@babel/plugin-transform-shorthand-properties" "^7.0.0" + "@babel/plugin-transform-spread" "^7.0.0" + "@babel/plugin-transform-template-literals" "^7.0.0" + babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base64-js@^1.0.2, base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +bl@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489" + integrity sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.4.0: + version "4.11.9" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" + integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== + +bn.js@^5.0.0, bn.js@^5.1.1: + version "5.1.3" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b" + integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ== + +body-scroll-lock@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/body-scroll-lock/-/body-scroll-lock-3.1.5.tgz#c1392d9217ed2c3e237fee1e910f6cdd80b7aaec" + integrity sha512-Yi1Xaml0EvNA0OYWxXiYNqY24AfWkbA6w5vxE7GWxtKfzIbZM+Qw+aSmkgsbWzbHiy/RCSkUZBplVxTA+E4jJg== + +bowser@^2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" + integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.1, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +browserslist@4.14.6: + version "4.14.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.6.tgz#97702a9c212e0c6b6afefad913d3a1538e348457" + integrity sha512-zeFYcUo85ENhc/zxHbiIp0LGzzTrE2Pv2JhxvS7kpUb9Q9D38kUX6Bie7pGutJ/5iF5rOxE7CepAuWD56xJ33A== + dependencies: + caniuse-lite "^1.0.30001154" + electron-to-chromium "^1.3.585" + escalade "^3.1.1" + node-releases "^1.1.65" + +browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.6.4: + version "4.16.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.1.tgz#bf757a2da376b3447b800a16f0f1c96358138766" + integrity sha512-UXhDrwqsNcpTYJBTZsbGATDxZbiVDsx6UjpmRUmtnP10pr8wAYr5LgFoEFw9ixriQH2mv/NX2SfGzE/o8GndLA== + dependencies: + caniuse-lite "^1.0.30001173" + colorette "^1.2.1" + electron-to-chromium "^1.3.634" + escalade "^3.1.1" + node-releases "^1.1.69" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer@5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + +buffer@^5.5.0, buffer@^5.7.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +bunyan-prettystream@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/bunyan-prettystream/-/bunyan-prettystream-0.1.3.tgz#6c3b713266f6ad32007c7b6ab1e998a245349d98" + integrity sha1-bDtxMmb2rTIAfHtqsemYokU0nZg= + +bunyan@^1.8.14: + version "1.8.15" + resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.15.tgz#8ce34ca908a17d0776576ca1b2f6cbd916e93b46" + integrity sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig== + optionalDependencies: + dtrace-provider "~0.8" + moment "^2.19.3" + mv "~2" + safe-json-stringify "~1" + +busboy@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.3.1.tgz#170899274c5bf38aae27d5c62b71268cd585fd1b" + integrity sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw== + dependencies: + dicer "0.3.0" + +bytes@3.1.0, bytes@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +call-bind@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.1.tgz#29aca9151f8ddcfd5b9b786898f005f425e88567" + integrity sha512-tvAvUwNcRikl3RVF20X9lsYmmepsovzTWeJiXjO0PkJp15uy/6xKFZOQtuiSULwYW+6ToZBprphCgWXC2dSgcQ== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.1.tgz#1fc41c854f00e2f7d0139dfeba1542d6896fe547" + integrity sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q== + dependencies: + pascal-case "^3.1.1" + tslib "^1.10.0" + +camel-case@4.1.2, camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + +camelcase-css@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +camelcase-keys@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" + integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== + dependencies: + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" + +camelcase@5.3.1, camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.0.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + +caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001093, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001113, caniuse-lite@^1.0.30001154, caniuse-lite@^1.0.30001173: + version "1.0.30001174" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001174.tgz#0f2aca2153fd88ceb07a2bb982fc2acb787623c4" + integrity sha512-tqClL/4ThQq6cfFXH3oJL4rifFBeM6gTkphjao5kgwMaW9yn0tKgQLAEfKzDwj6HQWCB/aWo8kTFlSvIN8geEA== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +chalk@2.4.2, chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.0.0.tgz#6e98081ed2d17faab615eb52ac66ec1fe6209e72" + integrity sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^1.0.0, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +"chalk@^3.0.0 || ^4.0.0", chalk@^4.0.0, chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +chokidar@3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b" + integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.1.2" + +chokidar@^3.4.3: + version "3.5.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.0.tgz#458a4816a415e9d3b3caa4faec2b96a6935a9e65" + integrity sha512-JgQM9JS92ZbFR4P90EvmzNpSGhpPBGBSj10PILeDyYFwp4h2/D9OM03wsJ4zW1fEp4ka2DGrnUeD7FuvQ2aZ2Q== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.3.1" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +chrome-trace-event@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" + integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== + dependencies: + tslib "^1.9.0" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +classnames@2.2.6, classnames@^2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" + integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== + +cli-cursor@^2.0.0, cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.5.0.tgz#12763e47251bf951cb75c201dfa58ff1bcb2d047" + integrity sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ== + +cli-truncate@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" + integrity sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ= + dependencies: + slice-ansi "0.0.4" + string-width "^1.0.1" + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +color-convert@^1.9.0, color-convert@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.4.tgz#dd51cd25cfee953d138fe4002372cc3d0e504cb6" + integrity sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.1.2, color@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/color/-/color-3.1.3.tgz#ca67fb4e7b97d611dcde39eceed422067d91596e" + integrity sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ== + dependencies: + color-convert "^1.9.1" + color-string "^1.5.4" + +colorette@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" + integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== + +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^2.13.0, commander@^2.16.0, commander@^2.20.0, commander@^2.20.3, commander@^2.8.1: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^5.0.0, commander@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== + +commander@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + +common-tags@1.8.0, common-tags@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" + integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + +compose-function@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/compose-function/-/compose-function-3.0.3.tgz#9ed675f13cc54501d30950a486ff6a7ba3ab185f" + integrity sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8= + dependencies: + arity-n "^1.0.4" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + +consolidate@^0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.16.0.tgz#a11864768930f2f19431660a65906668f5fbdc16" + integrity sha512-Nhl1wzCslqXYTJVDyJCu3ODohy9OfBMB5uD2BiBTzd7w+QY0lBzafkR8y8755yMYHAaMD4NuzbAw03/xzfw+eQ== + dependencies: + bluebird "^3.7.2" + +constant-case@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.3.tgz#ac910a99caf3926ac5112f352e3af599d8c5fc0a" + integrity sha512-FXtsSnnrFYpzDmvwDGQW+l8XK3GV1coLyBN0eBz16ZUzGaZcT2ANVCJmLeuw2GQgxKHQIe9e0w2dzkSfaRlUmA== + dependencies: + no-case "^3.0.3" + tslib "^1.10.0" + upper-case "^2.0.1" + +constant-case@^3.0.3: + version "3.0.4" + resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" + integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + upper-case "^2.0.2" + +convert-source-map@1.7.0, convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +convert-source-map@^0.3.3: + version "0.3.5" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190" + integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA= + +cookie@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" + integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cosmiconfig-toml-loader@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig-toml-loader/-/cosmiconfig-toml-loader-1.0.0.tgz#0681383651cceff918177debe9084c0d3769509b" + integrity sha512-H/2gurFWVi7xXvCyvsWRLCMekl4tITJcX0QEsDMpzxtuxDyM59xLatYNg4s/k9AA/HdtCYfj2su8mgA0GSDLDA== + dependencies: + "@iarna/toml" "^2.2.5" + +cosmiconfig@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" + integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.7.2" + +cosmiconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" + integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-fetch@3.0.6, cross-fetch@^3.0.4, cross-fetch@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c" + integrity sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ== + dependencies: + node-fetch "2.6.1" + +crypto-browserify@3.12.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +css-blank-pseudo@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz#dfdefd3254bf8a82027993674ccf35483bfcb3c5" + integrity sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w== + dependencies: + postcss "^7.0.5" + +css-has-pseudo@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz#3c642ab34ca242c59c41a125df9105841f6966ee" + integrity sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^5.0.0-rc.4" + +css-loader@4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-4.3.0.tgz#c888af64b2a5b2e85462c72c0f4a85c7e2e0821e" + integrity sha512-rdezjCjScIrsL8BSYszgT4s476IcNKt6yX69t0pHjJVnPUTDpn4WfIpDQTN3wCJvUvfsz/mFjuGOekf3PY3NUg== + dependencies: + camelcase "^6.0.0" + cssesc "^3.0.0" + icss-utils "^4.1.1" + loader-utils "^2.0.0" + postcss "^7.0.32" + postcss-modules-extract-imports "^2.0.0" + postcss-modules-local-by-default "^3.0.3" + postcss-modules-scope "^2.2.0" + postcss-modules-values "^3.0.0" + postcss-value-parser "^4.1.0" + schema-utils "^2.7.1" + semver "^7.3.2" + +css-prefers-color-scheme@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz#6f830a2714199d4f0d0d0bb8a27916ed65cff1f4" + integrity sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg== + dependencies: + postcss "^7.0.5" + +css-unit-converter@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.2.tgz#4c77f5a1954e6dbff60695ecb214e3270436ab21" + integrity sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA== + +css.escape@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s= + +css@^2.0.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" + integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== + dependencies: + inherits "^2.0.3" + source-map "^0.6.1" + source-map-resolve "^0.5.2" + urix "^0.1.0" + +cssdb@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-4.4.0.tgz#3bf2f2a68c10f5c6a08abd92378331ee803cddb0" + integrity sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ== + +cssesc@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" + integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssnano-preset-simple@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/cssnano-preset-simple/-/cssnano-preset-simple-1.2.1.tgz#8976013114b1fc4718253d30f21aaed1780fb80e" + integrity sha512-B2KahOIFTV6dw5Ioy9jHshTh/vAYNnUB2enyWRgnAEg3oJBjI/035ExpePaMqS2SwpbH7gCgvQqwpMBH6hTJSw== + dependencies: + caniuse-lite "^1.0.30001093" + postcss "^7.0.32" + +cssnano-simple@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/cssnano-simple/-/cssnano-simple-1.2.1.tgz#6de5d9dd75774bc8f31767573410a952c7dd8a12" + integrity sha512-9vOyjw8Dj/T12kIOnXPZ5VnEIo6F3YMaIn0wqJXmn277R58cWpI3AvtdlCBtohX7VAUNYcyk2d0dKcXXkb5I6Q== + dependencies: + cssnano-preset-simple "1.2.1" + postcss "^7.0.32" + +csstype@^3.0.2: + version "3.0.6" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.6.tgz#865d0b5833d7d8d40f4e5b8a6d76aea3de4725ef" + integrity sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw== + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +data-uri-to-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" + integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== + +dataloader@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.0.0.tgz#41eaf123db115987e21ca93c005cd7753c55fe6f" + integrity sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ== + +date-fns@^1.27.2: + version "1.30.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" + integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== + +debounce@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131" + integrity sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg== + +debug@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.2.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + +debug@^3.1.0: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +decamelize-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" + integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +decamelize@^1.1.0, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +decomment@^0.9.2: + version "0.9.3" + resolved "https://registry.yarnpkg.com/decomment/-/decomment-0.9.3.tgz#b913f32e5fe1113848f516caa5c7afefa9544d38" + integrity sha512-5skH5BfUL3n09RDmMVaHS1QGCiZRnl2nArUwmsE9JRY93Ueh3tihYl5wIrDdAuXnoFhxVis/DmRWREO2c6DG3w== + dependencies: + esprima "4.0.1" + +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +decompress-response@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986" + integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== + dependencies: + mimic-response "^2.0.0" + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + dependencies: + clone "^1.0.2" + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +defined@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +dependency-graph@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.9.0.tgz#11aed7e203bc8b00f48356d92db27b265c445318" + integrity sha512-9YLIBURXj4DJMFALxXw9K3Y3rwb5Fk0X5/8ipCzaN84+gKxoHK43tVKRNakCQbiEx07E8Uwhuq21BpUagFhZ8w== + +dependency-tree@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/dependency-tree/-/dependency-tree-7.2.2.tgz#2366c96c0a905adfc19e9ed8dcdbfcb93476dfb5" + integrity sha512-WWZJpNuMWqEM97CGykmyKLjjUWGVGkRRMSIEBWk5izmugxmivnItg4MMHkDzuvmB/7vglhudEoc5wyMp5ODD+Q== + dependencies: + commander "^2.20.3" + debug "^4.2.1" + filing-cabinet "^2.6.0" + precinct "^6.3.1" + typescript "^3.9.7" + +dequal@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.2.tgz#85ca22025e3a87e65ef75a7a437b35284a7e319d" + integrity sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug== + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +detect-indent@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd" + integrity sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA== + +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + +detective-amd@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/detective-amd/-/detective-amd-3.0.1.tgz#aca8eddb1f405821953faf4a893d9b9e0430b09e" + integrity sha512-vJgluSKkPyo+/McW9hzwmZwY1VPA3BS0VS1agdpPAWAhr65HwC1ox4Ig82rVfGYDYCa4GcKQON5JWBk++2Kf1Q== + dependencies: + ast-module-types "^2.7.0" + escodegen "^1.8.0" + get-amd-module-type "^3.0.0" + node-source-walk "^4.0.0" + +detective-cjs@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/detective-cjs/-/detective-cjs-3.1.1.tgz#18da3e39a002d2098a1123d45ce1de1b0d9045a0" + integrity sha512-JQtNTBgFY6h8uT6pgph5QpV3IyxDv+z3qPk/FZRDT9TlFfm5dnRtpH39WtQEr1khqsUxVqXzKjZHpdoQvQbllg== + dependencies: + ast-module-types "^2.4.0" + node-source-walk "^4.0.0" + +detective-es6@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/detective-es6/-/detective-es6-2.2.0.tgz#8f2baba3f8cd90a5cfd748f5ac436f0158ed2585" + integrity sha512-fSpNY0SLER7/sVgQZ1NxJPwmc9uCTzNgdkQDhAaj8NPYwr7Qji9QBcmbNvtMCnuuOGMuKn3O7jv0An+/WRWJZQ== + dependencies: + node-source-walk "^4.0.0" + +detective-less@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/detective-less/-/detective-less-1.0.2.tgz#a68af9ca5f69d74b7d0aa190218b211d83b4f7e3" + integrity sha512-Rps1xDkEEBSq3kLdsdnHZL1x2S4NGDcbrjmd4q+PykK5aJwDdP5MBgrJw1Xo+kyUHuv3JEzPqxr+Dj9ryeDRTA== + dependencies: + debug "^4.0.0" + gonzales-pe "^4.2.3" + node-source-walk "^4.0.0" + +detective-postcss@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/detective-postcss/-/detective-postcss-3.0.1.tgz#511921951f66135e17d0ece2e7604c6e4966c9c6" + integrity sha512-tfTS2GdpUal5NY0aCqI4dpEy8Xfr88AehYKB0iBIZvo8y2g3UsrcDnrp9PR2FbzoW7xD5Rip3NJW7eCSvtqdUw== + dependencies: + debug "^4.1.1" + is-url "^1.2.4" + postcss "^7.0.2" + postcss-values-parser "^1.5.0" + +detective-sass@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/detective-sass/-/detective-sass-3.0.1.tgz#496b819efd1f5c4dd3f0e19b43a8634bdd6927c4" + integrity sha512-oSbrBozRjJ+QFF4WJFbjPQKeakoaY1GiR380NPqwdbWYd5wfl5cLWv0l6LsJVqrgWfFN1bjFqSeo32Nxza8Lbw== + dependencies: + debug "^4.1.1" + gonzales-pe "^4.2.3" + node-source-walk "^4.0.0" + +detective-scss@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/detective-scss/-/detective-scss-2.0.1.tgz#06f8c21ae6dedad1fccc26d544892d968083eaf8" + integrity sha512-VveyXW4WQE04s05KlJ8K0bG34jtHQVgTc9InspqoQxvnelj/rdgSAy7i2DXAazyQNFKlWSWbS+Ro2DWKFOKTPQ== + dependencies: + debug "^4.1.1" + gonzales-pe "^4.2.3" + node-source-walk "^4.0.0" + +detective-stylus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/detective-stylus/-/detective-stylus-1.0.0.tgz#50aee7db8babb990381f010c63fabba5b58e54cd" + integrity sha1-UK7n24uruZA4HwEMY/q7pbWOVM0= + +detective-typescript@^5.8.0: + version "5.8.0" + resolved "https://registry.yarnpkg.com/detective-typescript/-/detective-typescript-5.8.0.tgz#c46776571e26bad6c9ada020cb3cb4e5625d1311" + integrity sha512-SrsUCfCaDTF64QVMHMidRal+kmkbIc5zP8cxxZPsomWx9vuEUjBlSJNhf7/ypE5cLdJJDI4qzKDmyzqQ+iz/xg== + dependencies: + "@typescript-eslint/typescript-estree" "^2.29.0" + ast-module-types "^2.6.0" + node-source-walk "^4.2.0" + typescript "^3.8.3" + +detective@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b" + integrity sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg== + dependencies: + acorn-node "^1.6.1" + defined "^1.0.0" + minimist "^1.1.1" + +dicer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.3.0.tgz#eacd98b3bfbf92e8ab5c2fdb71aaac44bb06b872" + integrity sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA== + dependencies: + streamsearch "0.1.2" + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dom-serializer@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.1.0.tgz#5f7c828f1bfc44887dc2a315ab5c45691d544b58" + integrity sha512-ox7bvGXt2n+uLWtCRLybYx60IrOlWL/aCebWJk1T0d4m3y2tzf4U3ij9wBMUb6YJZpz06HCCYuyCDveE2xXmzQ== + dependencies: + domelementtype "^2.0.1" + domhandler "^3.0.0" + entities "^2.0.0" + +dom-serializer@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.2.0.tgz#3433d9136aeb3c627981daa385fc7f32d27c48f1" + integrity sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + entities "^2.0.0" + +domelementtype@^2.0.1, domelementtype@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" + integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== + +domhandler@3.3.0, domhandler@^3.0.0, domhandler@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a" + integrity sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA== + dependencies: + domelementtype "^2.0.1" + +domhandler@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.0.0.tgz#01ea7821de996d85f69029e81fa873c21833098e" + integrity sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA== + dependencies: + domelementtype "^2.1.0" + +domutils@2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.4.2.tgz#7ee5be261944e1ad487d9aa0616720010123922b" + integrity sha512-NKbgaM8ZJOecTZsIzW5gSuplsX2IWW2mIK7xVr8hTQF2v1CJWTmLZ1HOCh5sH+IzVPAGE5IucooOkvwBRAdowA== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.0.1" + domhandler "^3.3.0" + +domutils@^2.4.2: + version "2.4.4" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.4.4.tgz#282739c4b150d022d34699797369aad8d19bbbd3" + integrity sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.0.1" + domhandler "^4.0.0" + +dot-case@^3.0.3: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +dotenv@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + +dtrace-provider@~0.8: + version "0.8.8" + resolved "https://registry.yarnpkg.com/dtrace-provider/-/dtrace-provider-0.8.8.tgz#2996d5490c37e1347be263b423ed7b297fb0d97e" + integrity sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg== + dependencies: + nan "^2.14.0" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +duplexer@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +electron-to-chromium@^1.3.585, electron-to-chromium@^1.3.634: + version "1.3.635" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.635.tgz#8d1591eeca6b257d380061a2c04f0b3cc6c9e33b" + integrity sha512-RRriZOLs9CpW6KTLmgBqyUdnY0QNqqWs0HOtuQGGEMizOTNNn1P7sGRBxARnUeLejOsgwjDyRqT3E/CSst02ZQ== + +elegant-spinner@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" + integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= + +elliptic@^6.5.3: + version "6.5.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +email-validator@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/email-validator/-/email-validator-2.0.4.tgz#b8dfaa5d0dae28f1b03c95881d904d4e40bfe7ed" + integrity sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" + integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.5.0" + tapable "^1.0.0" + +enhanced-resolve@^5.3.1: + version "5.5.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.5.0.tgz#5f2ca7d511076541e2a30dc364a40c4f6c027bcd" + integrity sha512-b4a6BasBCoLzri4MdaeOlDMpls2oioI28CF17csMiav9dq46yvQaKPFNUrCHB6VqQokBDG2VIEEL81jMiQ6Wtw== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +entities@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" + integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== + +errno@^0.1.3: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.18.0-next.1: + version "1.18.0-next.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" + integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.2" + is-negative-zero "^2.0.0" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.1" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@2.0.3, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escodegen@^1.8.0: + version "1.14.3" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" + integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== + dependencies: + esprima "^4.0.1" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +esprima@4.0.1, esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1, estraverse@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + +estree-walker@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +events@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" + integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== + +eventsource@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" + integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ== + dependencies: + original "^1.0.0" + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extract-files@9.0.0, extract-files@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-9.0.0.tgz#8a7744f2437f81f5ed3250ed9f1550de902fe54a" + integrity sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ== + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.1.1: + version "3.2.4" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" + integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + picomatch "^2.2.1" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fastq@^1.6.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.10.0.tgz#74dbefccade964932cdf500473ef302719c652bb" + integrity sha512-NL2Qc5L3iQEsyYzweq7qfgy5OtXCmGzGvhElGEd/SoFWEMOEczNh5s5ocaF01HDetxz+p8ecjNPA6cZxxIHmzA== + dependencies: + reusify "^1.0.4" + +fb-watchman@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + dependencies: + bser "2.1.1" + +fbjs-css-vars@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" + integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== + +fbjs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.0.tgz#0907067fb3f57a78f45d95f1eacffcacd623c165" + integrity sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg== + dependencies: + cross-fetch "^3.0.4" + fbjs-css-vars "^1.0.0" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.18" + +figures@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + dependencies: + escape-string-regexp "^1.0.5" + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +file-exists-dazinatorfork@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/file-exists-dazinatorfork/-/file-exists-dazinatorfork-1.0.2.tgz#cd8d0d85f63e39dc81eceb0b687c44a2cca95c47" + integrity sha512-r70c72ln2YHzQINNfxDp02hAhbGkt1HffZ+Du8oetWDLjDtFja/Lm10lUaSh9e+wD+7VDvPee0b0C9SAy8pWZg== + +filing-cabinet@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/filing-cabinet/-/filing-cabinet-2.6.0.tgz#3d4d5093a98b6fae84cf282e8bded1b8ad5f9c0c" + integrity sha512-7kSlTScEkxoYKXCix7tAQ52ZeIHcx7ZWWArEZgXY+eTMe6yDYFdDhHdkXm9rSmvrrpzdZeR1wiufS1rUt4OzMA== + dependencies: + app-module-path "^2.2.0" + commander "^2.13.0" + debug "^4.1.1" + decomment "^0.9.2" + enhanced-resolve "^4.1.0" + is-relative-path "^1.0.2" + module-definition "^3.0.0" + module-lookup-amd "^6.1.0" + resolve "^1.11.1" + resolve-dependency-path "^2.0.0" + sass-lookup "^3.0.0" + stylus-lookup "^3.0.1" + typescript "^3.0.3" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-cache-dir@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" + integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/find/-/find-0.3.0.tgz#4082e8fc8d8320f1a382b5e4f521b9bc50775cb8" + integrity sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw== + dependencies: + traverse-chain "~0.1.0" + +flatten@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b" + integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg== + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@^2.3.2: + version "2.5.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" + integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +form-data@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" + integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +fs-capacitor@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/fs-capacitor/-/fs-capacitor-6.2.0.tgz#fa79ac6576629163cb84561995602d8999afb7f5" + integrity sha512-nKcE1UduoSKX27NSZlg879LdQc94OtbOsEmKMN2MBNudXREvijRKx2GEBsTMTfws+BrbkJoEuynbGSVRSpauvw== + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + +fsevents@~2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.1.tgz#b209ab14c61012636c8863507edf7fb68cc54e9f" + integrity sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +generic-names@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/generic-names/-/generic-names-2.0.1.tgz#f8a378ead2ccaa7a34f0317b05554832ae41b872" + integrity sha512-kPCHWa1m9wGG/OwQpeweTwM/PYiQLrUIxXbt/P4Nic3LbGjCP0YwrALHW1uNLKZ0LIMg+RF+XRlj2ekT9ZlZAQ== + dependencies: + loader-utils "^1.1.0" + +gensync@^1.0.0-beta.1: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-amd-module-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-amd-module-type/-/get-amd-module-type-3.0.0.tgz#bb334662fa04427018c937774570de495845c288" + integrity sha512-99Q7COuACPfVt18zH9N4VAMyb81S6TUgJm2NgV6ERtkh9VIkAaByZkW530wl3lLN5KTtSrK9jVLxYsoP5hQKsw== + dependencies: + ast-module-types "^2.3.2" + node-source-walk "^4.0.0" + +get-caller-file@^2.0.1, get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.2.tgz#6820da226e50b24894e08859469dc68361545d49" + integrity sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== + +get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= + +glob-parent@^5.1.0, glob-parent@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^6.0.1: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + integrity sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI= + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.1.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== + +globby@11.0.1: + version "11.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" + integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + +gonzales-pe@^4.2.3: + version "4.3.0" + resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3" + integrity sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ== + dependencies: + minimist "^1.2.5" + +got@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + +graphql-config@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-3.2.0.tgz#3ec3a7e319792086b80e54db4b37372ad4a79a32" + integrity sha512-ygEKDeQNZKpm4137560n2oY3bGM0D5zyRsQVaJntKkufWdgPg6sb9/4J1zJW2y/yC1ortAbhNho09qmeJeLa9g== + dependencies: + "@endemolshinegroup/cosmiconfig-typescript-loader" "3.0.2" + "@graphql-tools/graphql-file-loader" "^6.0.0" + "@graphql-tools/json-file-loader" "^6.0.0" + "@graphql-tools/load" "^6.0.0" + "@graphql-tools/merge" "^6.0.0" + "@graphql-tools/url-loader" "^6.0.0" + "@graphql-tools/utils" "^6.0.0" + cosmiconfig "6.0.0" + cosmiconfig-toml-loader "1.0.0" + minimatch "3.0.4" + string-env-interpolation "1.0.1" + tslib "^2.0.0" + +graphql-request@^3.3.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-3.4.0.tgz#3a400cd5511eb3c064b1873afb059196bbea9c2b" + integrity sha512-acrTzidSlwAj8wBNO7Q/UQHS8T+z5qRGquCQRv9J1InwR01BBWV9ObnoE+JS5nCCEj8wSGS0yrDXVDoRiKZuOg== + dependencies: + cross-fetch "^3.0.6" + extract-files "^9.0.0" + form-data "^3.0.0" + +graphql-tag@^2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.11.0.tgz#1deb53a01c46a7eb401d6cb59dec86fa1cccbffd" + integrity sha512-VmsD5pJqWJnQZMUeRwrDhfgoyqcfwEkvtpANqcoUG8/tOLkwNgU9mzub/Mc78OJMhHjx7gfAMTxzdG43VGg3bA== + +graphql-upload@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/graphql-upload/-/graphql-upload-11.0.0.tgz#24b245ff18f353bab6715e8a055db9fd73035e10" + integrity sha512-zsrDtu5gCbQFDWsNa5bMB4nf1LpKX9KDgh+f8oL1288ijV4RxeckhVozAjqjXAfRpxOHD1xOESsh6zq8SjdgjA== + dependencies: + busboy "^0.3.1" + fs-capacitor "^6.1.0" + http-errors "^1.7.3" + isobject "^4.0.0" + object-path "^0.11.4" + +graphql-ws@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-3.1.0.tgz#cd09d385a21ab88af4c226da79c19351df9b27e8" + integrity sha512-zbex3FSiFz0iRgfkzDNWpOY/sYWoX+iZ5XUhakaDwOh99HSuk8rPt5suuxdXUVzEg5TGQ9rwzNaz/+mTPtS0yg== + +graphql@^15.4.0: + version "15.4.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.4.0.tgz#e459dea1150da5a106486ba7276518b5295a4347" + integrity sha512-EB3zgGchcabbsU9cFe1j+yxdzKQKAbGUWRb13DsrsMN1yyfmmIq+2+L5MqVWcDCE4V89R5AyUOi7sMOGxdsYtA== + +graphviz@0.0.9: + version "0.0.9" + resolved "https://registry.yarnpkg.com/graphviz/-/graphviz-0.0.9.tgz#0bbf1df588c6a92259282da35323622528c4bbc4" + integrity sha512-SmoY2pOtcikmMCqCSy2NO1YsRfu9OO0wpTlOYW++giGjfX1a6gax/m1Fo8IdUd0/3H15cTOfR1SMKwohj4LKsg== + dependencies: + temp "~0.4.0" + +gzip-size@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" + integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== + dependencies: + duplexer "^0.1.2" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +hard-rejection@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash-sum@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a" + integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg== + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hex-rgb@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/hex-rgb/-/hex-rgb-4.2.0.tgz#fb377f2e5658fc924f1efa189685922e56ecaf0f" + integrity sha512-I7DkKeQ2kR2uyqgbxPgNgClH/rfs1ioKZhZW8VTIAirsxCR5EyhYeywgZbhMScgUbKCkgo6bb6JwA0CLTn9beA== + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hosted-git-info@^2.1.4: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + +html-tags@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" + integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== + +htmlparser2@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-5.0.1.tgz#7daa6fc3e35d6107ac95a4fc08781f091664f6e7" + integrity sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ== + dependencies: + domelementtype "^2.0.1" + domhandler "^3.3.0" + domutils "^2.4.2" + entities "^2.0.0" + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-errors@1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@^1.7.3: + version "1.8.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.0.tgz#75d1bbe497e1044f51e4ee9e704a62f28d336507" + integrity sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-proxy-agent@5.0.0, https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= + dependencies: + ms "^2.0.0" + +iconv-lite@0.4.24, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +icss-replace-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" + integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= + +icss-utils@^4.0.0, icss-utils@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" + integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== + dependencies: + postcss "^7.0.14" + +ieee754@^1.1.13, ieee754@^1.1.4: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + +immutable@~3.7.6: + version "3.7.6" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b" + integrity sha1-E7TTyxK++hVIKib+Gy665kAHHks= + +import-fresh@^3.1.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-from@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-3.0.0.tgz#055cfec38cd5a27d8057ca51376d7d3bf0891966" + integrity sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ== + dependencies: + resolve-from "^5.0.0" + +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== + +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +inquirer@^7.3.3: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.19" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + +is-absolute@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" + integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== + dependencies: + is-relative "^1.0.0" + is-windows "^1.0.1" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-callable@^1.1.4, is-callable@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" + integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== + +is-core-module@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" + integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== + dependencies: + has "^1.0.3" + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@4.0.1, is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + +is-negative-zero@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= + +is-observable@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e" + integrity sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA== + dependencies: + symbol-observable "^1.1.0" + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-promise@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" + integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + +is-promise@^2.1.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + +is-regex@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + dependencies: + has-symbols "^1.0.1" + +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= + +is-relative-path@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-relative-path/-/is-relative-path-1.0.2.tgz#091b46a0d67c1ed0fe85f1f8cfdde006bb251d46" + integrity sha1-CRtGoNZ8HtD+hfH4z93gBrslHUY= + +is-relative@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" + integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== + dependencies: + is-unc-path "^1.0.0" + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-unc-path@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" + integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== + dependencies: + unc-path-regex "^0.1.2" + +is-url@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== + +is-windows@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" + integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== + +isomorphic-fetch@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" + integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA== + dependencies: + node-fetch "^2.6.1" + whatwg-fetch "^3.4.1" + +isomorphic-form-data@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isomorphic-form-data/-/isomorphic-form-data-2.0.0.tgz#9f6adf1c4c61ae3aefd8f110ab60fb9b143d6cec" + integrity sha512-TYgVnXWeESVmQSg4GLVbalmQ+B4NPi/H4eWxqALKj63KsUrcu301YDjBqaOw3h+cbak7Na4Xyps3BiptHtxTfg== + dependencies: + form-data "^2.3.2" + +isomorphic-ws@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" + integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +jest-worker@24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" + integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== + dependencies: + merge-stream "^2.0.0" + supports-color "^6.1.0" + +jest-worker@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +js-cookie@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" + integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1, js-yaml@^3.14.0: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +json-parse-better-errors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json-to-pretty-yaml@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz#f4cd0bd0a5e8fe1df25aaf5ba118b099fd992d5b" + integrity sha1-9M0L0KXo/h3yWq9boRiwmf2ZLVs= + dependencies: + remedial "^1.0.7" + remove-trailing-spaces "^1.0.6" + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +json5@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + dependencies: + minimist "^1.2.5" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + +jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +keen-slider@^5.2.4: + version "5.3.5" + resolved "https://registry.yarnpkg.com/keen-slider/-/keen-slider-5.3.5.tgz#29ac24fd7dbf1f1435bd448e0e583cded36b4b91" + integrity sha512-3QYvrFK7HbtkiC98/G+RM44y9WBe9cpHDHtP9bI1J/Vrc54IHLuTjOjyX4E81ukRc5GwiGfijOwStxEhisFnPg== + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +kind-of@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +klona@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" + integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== + +latest-version@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" + integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== + dependencies: + package-json "^6.3.0" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +line-column@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/line-column/-/line-column-1.0.2.tgz#d25af2936b6f4849172b312e4792d1d987bc34a2" + integrity sha1-0lryk2tvSEkXKzEuR5LR2Ye8NKI= + dependencies: + isarray "^1.0.0" + isobject "^2.0.0" + +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + +listr-silent-renderer@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" + integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4= + +listr-update-renderer@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz#4ea8368548a7b8aecb7e06d8c95cb45ae2ede6a2" + integrity sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA== + dependencies: + chalk "^1.1.3" + cli-truncate "^0.2.1" + elegant-spinner "^1.0.1" + figures "^1.7.0" + indent-string "^3.0.0" + log-symbols "^1.0.2" + log-update "^2.3.0" + strip-ansi "^3.0.1" + +listr-verbose-renderer@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz#f1132167535ea4c1261102b9f28dac7cba1e03db" + integrity sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw== + dependencies: + chalk "^2.4.1" + cli-cursor "^2.1.0" + date-fns "^1.27.2" + figures "^2.0.0" + +listr@^0.14.3: + version "0.14.3" + resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" + integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA== + dependencies: + "@samverschueren/stream-to-observable" "^0.3.0" + is-observable "^1.1.0" + is-promise "^2.1.0" + is-stream "^1.1.0" + listr-silent-renderer "^1.1.1" + listr-update-renderer "^0.5.0" + listr-verbose-renderer "^0.5.0" + p-map "^2.0.0" + rxjs "^6.3.3" + +loader-runner@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384" + integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== + +loader-utils@1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" + integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== + dependencies: + big.js "^5.2.2" + emojis-list "^2.0.0" + json5 "^1.0.1" + +loader-utils@2.0.0, loader-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" + integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +loader-utils@^1.0.2, loader-utils@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + +lodash.get@^4: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + +lodash.random@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash.random/-/lodash.random-3.2.0.tgz#96e24e763333199130d2c9e2fd57f91703cc262d" + integrity sha1-luJOdjMzGZEw0sni/Vf5FwPMJi0= + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= + +lodash.template@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" + integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.templatesettings@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" + integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== + dependencies: + lodash._reinterpolate "^3.0.0" + +lodash.throttle@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" + integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= + +lodash.toarray@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" + integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= + +lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@~4.17.20: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + +log-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" + integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg= + dependencies: + chalk "^1.0.0" + +log-symbols@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== + dependencies: + chalk "^4.0.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" + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lower-case@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7" + integrity sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ== + dependencies: + tslib "^1.10.0" + +lower-case@^2.0.1, lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@5.1.1, lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@6.0.0, lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +madge@^3.8.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/madge/-/madge-3.12.0.tgz#727c1ebb268150d52e6d0c3ccd382b4c7bd0bf19" + integrity sha512-9kA2W5RIbvH25CWc8tzPNn1X47AOcHEEwZJxWAMxhEOKEziVR1iMCbGCFUea5tWXs/A+xgJF59o/oSbNkOXpeg== + dependencies: + chalk "^4.1.0" + commander "^5.1.0" + commondir "^1.0.1" + debug "^4.0.1" + dependency-tree "^7.2.2" + detective-amd "^3.0.0" + detective-cjs "^3.1.1" + detective-es6 "^2.1.0" + detective-less "^1.0.2" + detective-postcss "^3.0.1" + detective-sass "^3.0.1" + detective-scss "^2.0.1" + detective-stylus "^1.0.0" + detective-typescript "^5.8.0" + graphviz "0.0.9" + ora "^5.1.0" + pluralize "^8.0.0" + precinct "^6.3.1" + pretty-ms "^7.0.0" + rc "^1.2.7" + typescript "^3.9.5" + walkdir "^0.4.1" + +magic-string@^0.25.7: + version "0.25.7" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" + integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== + dependencies: + sourcemap-codec "^1.4.4" + +make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +make-dir@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +make-error@^1, make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +map-cache@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + +map-obj@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.1.0.tgz#b91221b542734b9f14256c0132c897c5d7256fd5" + integrity sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g== + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +memory-fs@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" + integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +meow@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/meow/-/meow-7.1.1.tgz#7c01595e3d337fcb0ec4e8eed1666ea95903d306" + integrity sha512-GWHvA5QOcS412WCo8vwKDlTelGLsCGBVevQB5Kva961rmNfun0PCbv5+xta2kUMFJyR8/oWnn7ddeKdosbAPbA== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^2.5.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.13.1" + yargs-parser "^18.1.3" + +merge-source-map@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" + integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== + dependencies: + source-map "^0.6.1" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.0, micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.45.0: + version "1.45.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" + integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== + +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19: + version "2.1.28" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" + integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ== + dependencies: + mime-db "1.45.0" + +mime@^2.3.1: + version "2.4.7" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.7.tgz#962aed9be0ed19c91fd7dc2ece5d7f4e89a90d74" + integrity sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA== + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mimic-response@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" + integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +mini-svg-data-uri@^1.0.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/mini-svg-data-uri/-/mini-svg-data-uri-1.2.3.tgz#e16baa92ad55ddaa1c2c135759129f41910bc39f" + integrity sha512-zd6KCAyXgmq6FV1mR10oKXYtvmA9vRoB6xPSTUJTbFApCtkefDnYueVR1gkof3KcdLZo1Y8mjF2DFmQMIxsHNQ== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist-options@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" + integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + kind-of "^6.0.3" + +minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@~0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +module-definition@^3.0.0, module-definition@^3.3.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/module-definition/-/module-definition-3.3.1.tgz#fedef71667713e36988b93d0626a4fe7b35aebfc" + integrity sha512-kLidGPwQ2yq484nSD+D3JoJp4Etc0Ox9P0L34Pu/cU4X4HcG7k7p62XI5BBuvURWMRX3RPyuhOcBHbKus+UH4A== + dependencies: + ast-module-types "^2.7.1" + node-source-walk "^4.0.0" + +module-lookup-amd@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/module-lookup-amd/-/module-lookup-amd-6.2.0.tgz#70600008b3f26630fde9ef9ae6165ac69de6ecbb" + integrity sha512-uxHCj5Pw9psZiC1znjU2qPsubt6haCSsN9m7xmIdoTciEgfxUkE1vhtDvjHPuOXEZrVJhjKgkmkP+w73rRuelQ== + dependencies: + commander "^2.8.1" + debug "^4.1.0" + file-exists-dazinatorfork "^1.0.2" + find "^0.3.0" + requirejs "^2.3.5" + requirejs-config-file "^3.1.1" + +moment@^2.19.3: + version "2.29.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" + integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.0.0, ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +mv@~2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2" + integrity sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI= + dependencies: + mkdirp "~0.5.1" + ncp "~2.0.0" + rimraf "~2.4.0" + +nan@^2.14.0: + version "2.14.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" + integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== + +nanoid@^3.1.16: + version "3.1.20" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" + integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== + +napi-build-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" + integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== + +native-url@0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/native-url/-/native-url-0.3.4.tgz#29c943172aed86c63cee62c8c04db7f5756661f8" + integrity sha512-6iM8R99ze45ivyH8vybJ7X0yekIcPf5GgLV5K0ENCbmRcaRIDoj37BC8iLEmaaBfqqb8enuZ5p0uhY+lVAbAcA== + dependencies: + querystring "^0.2.0" + +ncp@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" + integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M= + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +next-seo@^4.11.0: + version "4.17.0" + resolved "https://registry.yarnpkg.com/next-seo/-/next-seo-4.17.0.tgz#fb3dbe0ed7414aa9d83872ef5d8510980a6dae7e" + integrity sha512-/hnb3oq7bhi8s7bup7+Lm6VzzgRucj+JrWtp+dJiYs/04H0N/NhmE9MpepixQ16fFj6G/39JLYxhzsO/QZG/Kw== + +next-themes@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.0.4.tgz#fb06a9d03887201dd8fdd75fc1c84f406988f61e" + integrity sha512-j0bJJ6INanFsniCL31j1ZhjNaoqIFOaTeiUakbGpGxDz4+318Zp2ZfaorSjpUhzDWbXBKA3ZDE0DSUVWJ/8EsA== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +next-unused@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/next-unused/-/next-unused-0.0.3.tgz#e7560b30c64b91a4143450eee1cdb535eb85e51b" + integrity sha512-LrjyGL6gq3v7eOL2KNkmciDZCpJjt9QjEftQEr4oZ8o5zwoEvLqfn7mgv+9Qv+i39fDrEYlF436zfwpBHp7ahg== + dependencies: + madge "^3.8.0" + ts-loader "^7.0.0" + +next@^10.0.5-canary.11: + version "10.0.5" + resolved "https://registry.yarnpkg.com/next/-/next-10.0.5.tgz#8071e0aa1883266c91943aa7c6b73deadb064793" + integrity sha512-yr7ap2TLugf0aMHz+3JoKFP9CCkFE+k6jCfdUymORhptjLYZbD3YGlTcUC1CRl+b5Phlbl7m/WUIPde0VcguiA== + dependencies: + "@ampproject/toolbox-optimizer" "2.7.1-alpha.0" + "@babel/runtime" "7.12.5" + "@hapi/accept" "5.0.1" + "@next/env" "10.0.5" + "@next/polyfill-module" "10.0.5" + "@next/react-dev-overlay" "10.0.5" + "@next/react-refresh-utils" "10.0.5" + "@opentelemetry/api" "0.14.0" + ast-types "0.13.2" + babel-plugin-transform-define "2.0.0" + babel-plugin-transform-react-remove-prop-types "0.4.24" + browserslist "4.14.6" + buffer "5.6.0" + caniuse-lite "^1.0.30001113" + chalk "2.4.2" + chokidar "3.4.3" + crypto-browserify "3.12.0" + css-loader "4.3.0" + cssnano-simple "1.2.1" + etag "1.8.1" + find-cache-dir "3.3.1" + jest-worker "24.9.0" + loader-utils "2.0.0" + native-url "0.3.4" + node-fetch "2.6.1" + node-html-parser "1.4.9" + p-limit "3.1.0" + path-browserify "1.0.1" + pnp-webpack-plugin "1.6.4" + postcss "8.1.7" + process "0.11.10" + prop-types "15.7.2" + raw-body "2.4.1" + react-is "16.13.1" + react-refresh "0.8.3" + resolve-url-loader "3.1.2" + sass-loader "10.0.5" + schema-utils "2.7.1" + stream-browserify "3.0.0" + style-loader "1.2.1" + styled-jsx "3.3.2" + use-subscription "1.5.1" + vm-browserify "1.1.2" + watchpack "2.0.0-beta.13" + webpack "4.44.1" + webpack-sources "1.4.3" + optionalDependencies: + sharp "0.26.3" + +no-case@^3.0.3, no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +node-abi@^2.7.0: + version "2.19.3" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.19.3.tgz#252f5dcab12dad1b5503b2d27eddd4733930282d" + integrity sha512-9xZrlyfvKhWme2EXFKQhZRp1yNWT/uI1luYPr3sFl+H4keYY4xR+1jO7mvTTijIsHf1M+QDe9uWuKeEpLInIlg== + dependencies: + semver "^5.4.1" + +node-addon-api@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.1.0.tgz#98b21931557466c6729e51cb77cd39c965f42239" + integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw== + +node-emoji@^1.8.1: + version "1.10.0" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" + integrity sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw== + dependencies: + lodash.toarray "^4.4.0" + +node-fetch@2.6.1, node-fetch@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +node-html-parser@1.4.9: + version "1.4.9" + resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-1.4.9.tgz#3c8f6cac46479fae5800725edb532e9ae8fd816c" + integrity sha512-UVcirFD1Bn0O+TSmloHeHqZZCxHjvtIeGdVdGMhyZ8/PWlEiZaZ5iJzR189yKZr8p0FXN58BUeC7RHRkf/KYGw== + dependencies: + he "1.2.0" + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + +node-releases@^1.1.65, node-releases@^1.1.69: + version "1.1.69" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.69.tgz#3149dbde53b781610cd8b486d62d86e26c3725f6" + integrity sha512-DGIjo79VDEyAnRlfSqYTsy+yoHd2IOjJiKUozD2MV2D85Vso6Bug56mb9tT/fY5Urt0iqk01H7x+llAruDR2zA== + +node-source-walk@^4.0.0, node-source-walk@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/node-source-walk/-/node-source-walk-4.2.0.tgz#c2efe731ea8ba9c03c562aa0a9d984e54f27bc2c" + integrity sha512-hPs/QMe6zS94f5+jG3kk9E7TNm4P2SulrKiLWMzKszBfNZvL/V6wseHlTd7IvfW0NZWqPtK3+9yYNr+3USGteA== + dependencies: + "@babel/parser" "^7.0.0" + +noop-logger@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" + integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= + +normalize-html-whitespace@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/normalize-html-whitespace/-/normalize-html-whitespace-1.0.0.tgz#5e3c8e192f1b06c3b9eee4b7e7f28854c7601e34" + integrity sha512-9ui7CGtOOlehQu0t/OhhlmDyc71mKVlv+4vF+me4iZLPrNtRL2xoquEdfZxasC/bdQi/Hr3iTrpyRKIG+ocabA== + +normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= + +normalize-url@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + +normalize.css@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3" + integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg== + +npmlog@^4.0.1, npmlog@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +nullthrows@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" + integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-hash@^2.0.3: + version "2.1.1" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.1.1.tgz#9447d0279b4fcf80cff3259bf66a1dc73afabe09" + integrity sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ== + +object-inspect@^1.8.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" + integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== + +object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-path@^0.11.4: + version "0.11.5" + resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.5.tgz#d4e3cf19601a5140a55a16ad712019a9c50b577a" + integrity sha512-jgSbThcoR/s+XumvGMTMf81QVBmah+/Q7K7YduKeKVWL7N111unR2d6pZZarSk6kY/caeNxUDyxOvMWyzoU2eg== + +object.assign@^4.1.0, object.assign@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +opener@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" + integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +ora@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.2.0.tgz#de10bfd2d15514384af45f3fa9d9b1aaf344fda1" + integrity sha512-+wG2v8TUU8EgzPHun1k/n45pXquQ9fHnbXVetl9rRgO6kjZszGGbraF3XPTIdgeA+s1lbRjSEftAnyT0w8ZMvQ== + dependencies: + bl "^4.0.3" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + log-symbols "^4.0.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + +original@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" + integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== + dependencies: + url-parse "^1.4.3" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-limit@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.2.tgz#1664e010af3cadc681baafd3e2a437be7b0fb5fe" + integrity sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg== + dependencies: + p-try "^2.0.0" + +p-limit@3.1.0, p-limit@^3.0.2, p-limit@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + 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-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +package-json@^6.3.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== + dependencies: + got "^9.6.0" + registry-auth-token "^4.0.0" + registry-url "^5.0.0" + semver "^6.2.0" + +param-case@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.3.tgz#4be41f8399eff621c56eebb829a5e451d9801238" + integrity sha512-VWBVyimc1+QrzappRs7waeN2YmoZFCGXWASRYX1/rGHtXqEcrGEIDm+jqIwFa2fRXNgQEwrxaYuIrX0WcAguTA== + dependencies: + dot-case "^3.0.3" + tslib "^1.10.0" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-filepath@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" + integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE= + dependencies: + is-absolute "^1.0.0" + map-cache "^0.2.0" + path-root "^0.1.1" + +parse-json@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz#f96088cdf24a8faa9aea9a009f2d9d942c999646" + integrity sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse-ms@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" + integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== + +pascal-case@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.1.tgz#5ac1975133ed619281e88920973d2cd1f279de5f" + integrity sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA== + dependencies: + no-case "^3.0.3" + tslib "^1.10.0" + +pascal-case@^3.1.1, pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +path-browserify@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-root-regex@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" + integrity sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0= + +path-root@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" + integrity sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc= + dependencies: + path-root-regex "^0.1.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pbkdf2@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94" + integrity sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pkg-dir@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pkg-dir@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-5.0.0.tgz#a02d6aebe6ba133a928f74aec20bafdfe6b8e760" + integrity sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA== + dependencies: + find-up "^5.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== + +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + +pnp-webpack-plugin@1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" + integrity sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg== + dependencies: + ts-pnp "^1.1.6" + +postcss-attribute-case-insensitive@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880" + integrity sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA== + dependencies: + postcss "^7.0.2" + postcss-selector-parser "^6.0.2" + +postcss-color-functional-notation@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz#5efd37a88fbabeb00a2966d1e53d98ced93f74e0" + integrity sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-color-gray@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz#532a31eb909f8da898ceffe296fdc1f864be8547" + integrity sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw== + dependencies: + "@csstools/convert-colors" "^1.4.0" + postcss "^7.0.5" + postcss-values-parser "^2.0.0" + +postcss-color-hex-alpha@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz#a8d9ca4c39d497c9661e374b9c51899ef0f87388" + integrity sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw== + dependencies: + postcss "^7.0.14" + postcss-values-parser "^2.0.1" + +postcss-color-mod-function@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz#816ba145ac11cc3cb6baa905a75a49f903e4d31d" + integrity sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ== + dependencies: + "@csstools/convert-colors" "^1.4.0" + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-color-rebeccapurple@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz#c7a89be872bb74e45b1e3022bfe5748823e6de77" + integrity sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-custom-media@^7.0.8: + version "7.0.8" + resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz#fffd13ffeffad73621be5f387076a28b00294e0c" + integrity sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg== + dependencies: + postcss "^7.0.14" + +postcss-custom-properties@^8.0.11: + version "8.0.11" + resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz#2d61772d6e92f22f5e0d52602df8fae46fa30d97" + integrity sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA== + dependencies: + postcss "^7.0.17" + postcss-values-parser "^2.0.1" + +postcss-custom-selectors@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz#64858c6eb2ecff2fb41d0b28c9dd7b3db4de7fba" + integrity sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w== + dependencies: + postcss "^7.0.2" + postcss-selector-parser "^5.0.0-rc.3" + +postcss-dir-pseudo-class@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz#6e3a4177d0edb3abcc85fdb6fbb1c26dabaeaba2" + integrity sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw== + dependencies: + postcss "^7.0.2" + postcss-selector-parser "^5.0.0-rc.3" + +postcss-double-position-gradients@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz#fc927d52fddc896cb3a2812ebc5df147e110522e" + integrity sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA== + dependencies: + postcss "^7.0.5" + postcss-values-parser "^2.0.0" + +postcss-env-function@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-env-function/-/postcss-env-function-2.0.2.tgz#0f3e3d3c57f094a92c2baf4b6241f0b0da5365d7" + integrity sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-flexbugs-fixes@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.2.1.tgz#9218a65249f30897deab1033aced8578562a6690" + integrity sha512-9SiofaZ9CWpQWxOwRh1b/r85KD5y7GgvsNt1056k6OYLvWUun0czCvogfJgylC22uJTwW1KzY3Gz65NZRlvoiQ== + dependencies: + postcss "^7.0.26" + +postcss-focus-visible@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz#477d107113ade6024b14128317ade2bd1e17046e" + integrity sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g== + dependencies: + postcss "^7.0.2" + +postcss-focus-within@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz#763b8788596cee9b874c999201cdde80659ef680" + integrity sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w== + dependencies: + postcss "^7.0.2" + +postcss-font-variant@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-4.0.1.tgz#42d4c0ab30894f60f98b17561eb5c0321f502641" + integrity sha512-I3ADQSTNtLTTd8uxZhtSOrTCQ9G4qUVKPjHiDk0bV75QSxXjVWiJVJ2VLdspGUi9fbW9BcjKJoRvxAH1pckqmA== + dependencies: + postcss "^7.0.2" + +postcss-functions@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-functions/-/postcss-functions-3.0.0.tgz#0e94d01444700a481de20de4d55fb2640564250e" + integrity sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4= + dependencies: + glob "^7.1.2" + object-assign "^4.1.1" + postcss "^6.0.9" + postcss-value-parser "^3.3.0" + +postcss-gap-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz#431c192ab3ed96a3c3d09f2ff615960f902c1715" + integrity sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg== + dependencies: + postcss "^7.0.2" + +postcss-image-set-function@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz#28920a2f29945bed4c3198d7df6496d410d3f288" + integrity sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-initial@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-3.0.2.tgz#f018563694b3c16ae8eaabe3c585ac6319637b2d" + integrity sha512-ugA2wKonC0xeNHgirR4D3VWHs2JcU08WAi1KFLVcnb7IN89phID6Qtg2RIctWbnvp1TM2BOmDtX8GGLCKdR8YA== + dependencies: + lodash.template "^4.5.0" + postcss "^7.0.2" + +postcss-js@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-2.0.3.tgz#a96f0f23ff3d08cec7dc5b11bf11c5f8077cdab9" + integrity sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w== + dependencies: + camelcase-css "^2.0.1" + postcss "^7.0.18" + +postcss-lab-function@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz#bb51a6856cd12289ab4ae20db1e3821ef13d7d2e" + integrity sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg== + dependencies: + "@csstools/convert-colors" "^1.4.0" + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-logical@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-logical/-/postcss-logical-3.0.0.tgz#2495d0f8b82e9f262725f75f9401b34e7b45d5b5" + integrity sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA== + dependencies: + postcss "^7.0.2" + +postcss-media-minmax@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz#b75bb6cbc217c8ac49433e12f22048814a4f5ed5" + integrity sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw== + dependencies: + postcss "^7.0.2" + +postcss-modules-extract-imports@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" + integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== + dependencies: + postcss "^7.0.5" + +postcss-modules-local-by-default@^3.0.2, postcss-modules-local-by-default@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0" + integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw== + dependencies: + icss-utils "^4.1.1" + postcss "^7.0.32" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" + integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^6.0.0" + +postcss-modules-values@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10" + integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg== + dependencies: + icss-utils "^4.0.0" + postcss "^7.0.6" + +postcss-modules@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/postcss-modules/-/postcss-modules-3.2.2.tgz#ee390de0f9f18e761e1778dfb9be26685c02c51f" + integrity sha512-JQ8IAqHELxC0N6tyCg2UF40pACY5oiL6UpiqqcIFRWqgDYO8B0jnxzoQ0EOpPrWXvcpu6BSbQU/3vSiq7w8Nhw== + dependencies: + generic-names "^2.0.1" + icss-replace-symbols "^1.1.0" + lodash.camelcase "^4.3.0" + postcss "^7.0.32" + postcss-modules-extract-imports "^2.0.0" + postcss-modules-local-by-default "^3.0.2" + postcss-modules-scope "^2.2.0" + postcss-modules-values "^3.0.0" + string-hash "^1.1.1" + +postcss-nested@^4.1.1: + version "4.2.3" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-4.2.3.tgz#c6f255b0a720549776d220d00c4b70cd244136f6" + integrity sha512-rOv0W1HquRCamWy2kFl3QazJMMe1ku6rCFoAAH+9AcxdbpDeBr6k968MLWuLjvjMcGEip01ak09hKOEgpK9hvw== + dependencies: + postcss "^7.0.32" + postcss-selector-parser "^6.0.2" + +postcss-nesting@^7.0.0, postcss-nesting@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-7.0.1.tgz#b50ad7b7f0173e5b5e3880c3501344703e04c052" + integrity sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg== + dependencies: + postcss "^7.0.2" + +postcss-overflow-shorthand@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz#31ecf350e9c6f6ddc250a78f0c3e111f32dd4c30" + integrity sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g== + dependencies: + postcss "^7.0.2" + +postcss-page-break@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-page-break/-/postcss-page-break-2.0.0.tgz#add52d0e0a528cabe6afee8b46e2abb277df46bf" + integrity sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ== + dependencies: + postcss "^7.0.2" + +postcss-place@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-place/-/postcss-place-4.0.1.tgz#e9f39d33d2dc584e46ee1db45adb77ca9d1dcc62" + integrity sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-preset-env@^6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz#c34ddacf8f902383b35ad1e030f178f4cdf118a5" + integrity sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg== + dependencies: + autoprefixer "^9.6.1" + browserslist "^4.6.4" + caniuse-lite "^1.0.30000981" + css-blank-pseudo "^0.1.4" + css-has-pseudo "^0.10.0" + css-prefers-color-scheme "^3.1.1" + cssdb "^4.4.0" + postcss "^7.0.17" + postcss-attribute-case-insensitive "^4.0.1" + postcss-color-functional-notation "^2.0.1" + postcss-color-gray "^5.0.0" + postcss-color-hex-alpha "^5.0.3" + postcss-color-mod-function "^3.0.3" + postcss-color-rebeccapurple "^4.0.1" + postcss-custom-media "^7.0.8" + postcss-custom-properties "^8.0.11" + postcss-custom-selectors "^5.1.2" + postcss-dir-pseudo-class "^5.0.0" + postcss-double-position-gradients "^1.0.0" + postcss-env-function "^2.0.2" + postcss-focus-visible "^4.0.0" + postcss-focus-within "^3.0.0" + postcss-font-variant "^4.0.0" + postcss-gap-properties "^2.0.0" + postcss-image-set-function "^3.0.1" + postcss-initial "^3.0.0" + postcss-lab-function "^2.0.1" + postcss-logical "^3.0.0" + postcss-media-minmax "^4.0.0" + postcss-nesting "^7.0.0" + postcss-overflow-shorthand "^2.0.0" + postcss-page-break "^2.0.0" + postcss-place "^4.0.1" + postcss-pseudo-class-any-link "^6.0.0" + postcss-replace-overflow-wrap "^3.0.0" + postcss-selector-matches "^4.0.0" + postcss-selector-not "^4.0.0" + +postcss-pseudo-class-any-link@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz#2ed3eed393b3702879dec4a87032b210daeb04d1" + integrity sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew== + dependencies: + postcss "^7.0.2" + postcss-selector-parser "^5.0.0-rc.3" + +postcss-replace-overflow-wrap@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz#61b360ffdaedca84c7c918d2b0f0d0ea559ab01c" + integrity sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw== + dependencies: + postcss "^7.0.2" + +postcss-safe-parser@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz#a6d4e48f0f37d9f7c11b2a581bf00f8ba4870b96" + integrity sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g== + dependencies: + postcss "^7.0.26" + +postcss-selector-matches@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz#71c8248f917ba2cc93037c9637ee09c64436fcff" + integrity sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww== + dependencies: + balanced-match "^1.0.0" + postcss "^7.0.2" + +postcss-selector-not@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-4.0.1.tgz#263016eef1cf219e0ade9a913780fc1f48204cbf" + integrity sha512-YolvBgInEK5/79C+bdFMyzqTg6pkYqDbzZIST/PDMqa/o3qtXenD05apBG2jLgT0/BQ77d4U2UK12jWpilqMAQ== + dependencies: + balanced-match "^1.0.0" + postcss "^7.0.2" + +postcss-selector-parser@^5.0.0-rc.3, postcss-selector-parser@^5.0.0-rc.4: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz#249044356697b33b64f1a8f7c80922dddee7195c" + integrity sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ== + dependencies: + cssesc "^2.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3" + integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw== + dependencies: + cssesc "^3.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + util-deprecate "^1.0.2" + +postcss-value-parser@^3.3.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" + integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== + +postcss-value-parser@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" + integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== + +postcss-values-parser@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-1.5.0.tgz#5d9fa63e2bcb0179ce48f3235303765eb89f3047" + integrity sha512-3M3p+2gMp0AH3da530TlX8kiO1nxdTnc3C6vr8dMxRLIlh8UYkz0/wcwptSXjhtx2Fr0TySI7a+BHDQ8NL7LaQ== + dependencies: + flatten "^1.0.2" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz#da8b472d901da1e205b47bdc98637b9e9e550e5f" + integrity sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg== + dependencies: + flatten "^1.0.2" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss@7.0.21: + version "7.0.21" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.21.tgz#06bb07824c19c2021c5d056d5b10c35b989f7e17" + integrity sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + +postcss@7.0.32: + version "7.0.32" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" + integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + +postcss@8.1.7: + version "8.1.7" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.1.7.tgz#ff6a82691bd861f3354fd9b17b2332f88171233f" + integrity sha512-llCQW1Pz4MOPwbZLmOddGM9eIJ8Bh7SZ2Oj5sxZva77uVaotYDsYTch1WBTNu7fUY0fpWp0fdt7uW40D4sRiiQ== + dependencies: + colorette "^1.2.1" + line-column "^1.0.2" + nanoid "^3.1.16" + source-map "^0.6.1" + +postcss@^6.0.9: + version "6.0.23" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" + integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag== + dependencies: + chalk "^2.4.1" + source-map "^0.6.1" + supports-color "^5.4.0" + +postcss@^7.0.11, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.18, postcss@^7.0.2, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: + version "7.0.35" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24" + integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + +prebuild-install@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.0.0.tgz#669022bcde57c710a869e39c5ca6bf9cd207f316" + integrity sha512-h2ZJ1PXHKWZpp1caLw0oX9sagVpL2YTk+ZwInQbQ3QqNd4J03O6MpFNmMTJlkfgPENWqe5kP0WjQLqz5OjLfsw== + dependencies: + detect-libc "^1.0.3" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^2.7.0" + noop-logger "^0.1.1" + npmlog "^4.0.1" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^3.0.3" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + which-pm-runs "^1.0.0" + +precinct@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/precinct/-/precinct-6.3.1.tgz#8ad735a8afdfc48b56ed39c9ad3bf999b6b928dc" + integrity sha512-JAwyLCgTylWminoD7V0VJwMElWmwrVSR6r9HaPWCoswkB4iFzX7aNtO7VBfAVPy+NhmjKb8IF8UmlWJXzUkOIQ== + dependencies: + commander "^2.20.3" + debug "^4.1.1" + detective-amd "^3.0.0" + detective-cjs "^3.1.1" + detective-es6 "^2.1.0" + detective-less "^1.0.2" + detective-postcss "^3.0.1" + detective-sass "^3.0.1" + detective-scss "^2.0.1" + detective-stylus "^1.0.0" + detective-typescript "^5.8.0" + module-definition "^3.3.0" + node-source-walk "^4.2.0" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +prettier@^2.0.5, prettier@^2.1.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" + integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== + +pretty-hrtime@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" + integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= + +pretty-ms@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" + integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== + dependencies: + parse-ms "^2.1.0" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + +prop-types@15.7.2, prop-types@^15.6.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +purgecss@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-2.3.0.tgz#5327587abf5795e6541517af8b190a6fb5488bb3" + integrity sha512-BE5CROfVGsx2XIhxGuZAT7rTH9lLeQx/6M0P7DTXQH4IUc3BBzs9JUzt4yzGf3JrH9enkeq6YJBe9CTtkm1WmQ== + dependencies: + commander "^5.0.0" + glob "^7.0.0" + postcss "7.0.32" + postcss-selector-parser "^6.0.2" + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +querystring@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +raw-body@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" + integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== + dependencies: + bytes "3.1.0" + http-errors "1.7.3" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc@^1.2.7, rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +react-dom@^16.14.0: + version "16.14.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89" + integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.19.1" + +react-is@16.13.1, react-is@^16.8.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-merge-refs@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/react-merge-refs/-/react-merge-refs-1.1.0.tgz#73d88b892c6c68cbb7a66e0800faa374f4c38b06" + integrity sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ== + +react-refresh@0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f" + integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg== + +react-ticker@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/react-ticker/-/react-ticker-1.2.2.tgz#12cda5ff8266c6fe90ffcd8c58e12ba1596ddf24" + integrity sha512-PXUujoPJvajxwOfosuuujlrBUrjgGp4FB4haWFOI25ujhMppW4SuLkiOdQ9AylrWN3yTHWdk2kbQWe3n9SjFGA== + +react@^16.14.0: + version "16.14.0" + resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" + integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + +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== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +readable-stream@^2.0.1, readable-stream@^2.0.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== + dependencies: + picomatch "^2.2.1" + +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + +reduce-css-calc@^2.1.6: + version "2.1.8" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz#7ef8761a28d614980dc0c982f772c93f7a99de03" + integrity sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg== + dependencies: + css-unit-converter "^1.1.1" + postcss-value-parser "^3.3.0" + +regenerator-runtime@^0.13.4: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + +regex-parser@^2.2.11: + version "2.2.11" + resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58" + integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q== + +registry-auth-token@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" + integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== + dependencies: + rc "^1.2.8" + +registry-url@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== + dependencies: + rc "^1.2.8" + +relay-compiler@10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/relay-compiler/-/relay-compiler-10.1.0.tgz#fb4672cdbe9b54869a3a79759edd8c2d91609cbe" + integrity sha512-HPqc3N3tNgEgUH5+lTr5lnLbgnsZMt+MRiyS0uAVNhuPY2It0X1ZJG+9qdA3L9IqKFUNwVn6zTO7RArjMZbARQ== + dependencies: + "@babel/core" "^7.0.0" + "@babel/generator" "^7.5.0" + "@babel/parser" "^7.0.0" + "@babel/runtime" "^7.0.0" + "@babel/traverse" "^7.0.0" + "@babel/types" "^7.0.0" + babel-preset-fbjs "^3.3.0" + chalk "^4.0.0" + fb-watchman "^2.0.0" + fbjs "^3.0.0" + glob "^7.1.1" + immutable "~3.7.6" + nullthrows "^1.1.1" + relay-runtime "10.1.0" + signedsource "^1.0.0" + yargs "^15.3.1" + +relay-runtime@10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/relay-runtime/-/relay-runtime-10.1.0.tgz#4753bf36e95e8d862cef33608e3d98b4ed730d16" + integrity sha512-bxznLnQ1ST6APN/cFi7l0FpjbZVchWQjjhj9mAuJBuUqNNCh9uV+UTRhpQF7Q8ycsPp19LHTpVyGhYb0ustuRQ== + dependencies: + "@babel/runtime" "^7.0.0" + fbjs "^3.0.0" + +remedial@^1.0.7: + version "1.0.8" + resolved "https://registry.yarnpkg.com/remedial/-/remedial-1.0.8.tgz#a5e4fd52a0e4956adbaf62da63a5a46a78c578a0" + integrity sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg== + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +remove-trailing-spaces@^1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz#4354d22f3236374702f58ee373168f6d6887ada7" + integrity sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA== + +replaceall@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/replaceall/-/replaceall-0.1.6.tgz#81d81ac7aeb72d7f5c4942adf2697a3220688d8e" + integrity sha1-gdgax663LX9cSUKt8ml6MiBojY4= + +request@^2.88.2: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +requirejs-config-file@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/requirejs-config-file/-/requirejs-config-file-3.1.2.tgz#de8c0b3eebdf243511c994a8a24b006f8b825997" + integrity sha512-sdLWywcDuNz7EIOhenSbRfT4YF84nItDv90coN2htbokjmU2QeyQuSBZILQUKNksepl8UPVU+hgYySFaDxbJPQ== + dependencies: + esprima "^4.0.0" + make-dir "^2.1.0" + stringify-object "^3.2.1" + +requirejs@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.6.tgz#e5093d9601c2829251258c0b9445d4d19fa9e7c9" + integrity sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +resolve-dependency-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-dependency-path/-/resolve-dependency-path-2.0.0.tgz#11700e340717b865d216c66cabeb4a2a3c696736" + integrity sha512-DIgu+0Dv+6v2XwRaNWnumKu7GPufBBOr5I1gRPJHkvghrfCGOooJODFvgFimX/KRxk9j0whD2MnKHzM1jYvk9w== + +resolve-from@5.0.0, resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-url-loader@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-3.1.2.tgz#235e2c28e22e3e432ba7a5d4e305c59a58edfc08" + integrity sha512-QEb4A76c8Mi7I3xNKXlRKQSlLBwjUV/ULFMP+G7n3/7tJZ8MG5wsZ3ucxP1Jz8Vevn6fnJsxDx9cIls+utGzPQ== + dependencies: + adjust-sourcemap-loader "3.0.0" + camelcase "5.3.1" + compose-function "3.0.3" + convert-source-map "1.7.0" + es6-iterator "2.0.3" + loader-utils "1.2.3" + postcss "7.0.21" + rework "1.0.1" + rework-visit "1.0.0" + source-map "0.6.1" + +resolve-url@^0.2.1: + version "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.11.1, resolve@^1.14.2: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== + dependencies: + is-core-module "^2.1.0" + path-parse "^1.0.6" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +retry@0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rework-visit@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rework-visit/-/rework-visit-1.0.0.tgz#9945b2803f219e2f7aca00adb8bc9f640f842c9a" + integrity sha1-mUWygD8hni96ygCtuLyfZA+ELJo= + +rework@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rework/-/rework-1.0.1.tgz#30806a841342b54510aa4110850cd48534144aa7" + integrity sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc= + dependencies: + convert-source-map "^0.3.3" + css "^2.0.0" + +rimraf@~2.4.0: + version "2.4.5" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da" + integrity sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto= + dependencies: + glob "^6.0.1" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +run-parallel@^1.1.9: + version "1.1.10" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef" + integrity sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw== + +rxjs@^6.3.3, rxjs@^6.6.0: + version "6.6.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" + integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== + dependencies: + tslib "^1.9.0" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-json-stringify@~1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz#356e44bc98f1f93ce45df14bcd7c01cda86e0afd" + integrity sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg== + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sass-loader@10.0.5: + version "10.0.5" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.0.5.tgz#f53505b5ddbedf43797470ceb34066ded82bb769" + integrity sha512-2LqoNPtKkZq/XbXNQ4C64GFEleSEHKv6NPSI+bMC/l+jpEXGJhiRYkAQToO24MR7NU4JRY2RpLpJ/gjo2Uf13w== + dependencies: + klona "^2.0.4" + loader-utils "^2.0.0" + neo-async "^2.6.2" + schema-utils "^3.0.0" + semver "^7.3.2" + +sass-lookup@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/sass-lookup/-/sass-lookup-3.0.0.tgz#3b395fa40569738ce857bc258e04df2617c48cac" + integrity sha512-TTsus8CfFRn1N44bvdEai1no6PqdmDiQUiqW5DlpmtT+tYnIt1tXtDIph5KA1efC+LmioJXSnCtUVpcK9gaKIg== + dependencies: + commander "^2.16.0" + +scheduler@^0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" + integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +schema-utils@2.7.1, schema-utils@^2.6.6, schema-utils@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== + dependencies: + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" + +schema-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" + integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== + dependencies: + "@types/json-schema" "^7.0.6" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +scuid@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/scuid/-/scuid-1.1.0.tgz#d3f9f920956e737a60f72d0e4ad280bf324d5dab" + integrity sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg== + +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.0.0, semver@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.2: + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + dependencies: + lru-cache "^6.0.0" + +serialize-javascript@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== + dependencies: + randombytes "^2.1.0" + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +sharp@0.26.3: + version "0.26.3" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.26.3.tgz#9de8577a986b22538e6e12ced1f7e8a53f9728de" + integrity sha512-NdEJ9S6AMr8Px0zgtFo1TJjMK/ROMU92MkDtYn2BBrDjIx3YfH9TUyGdzPC+I/L619GeYQc690Vbaxc5FPCCWg== + dependencies: + array-flatten "^3.0.0" + color "^3.1.3" + detect-libc "^1.0.3" + node-addon-api "^3.0.2" + npmlog "^4.1.2" + prebuild-install "^6.0.0" + semver "^7.3.2" + simple-get "^4.0.0" + tar-fs "^2.1.1" + tunnel-agent "^0.6.0" + +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== + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +signedsource@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/signedsource/-/signedsource-1.0.0.tgz#1ddace4981798f93bd833973803d80d52e93ad6a" + integrity sha1-HdrOSYF5j5O9gzlzgD2A1S6TrWo= + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.0.tgz#b45be062435e50d159540b576202ceec40b9c6b3" + integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA== + dependencies: + decompress-response "^4.2.0" + once "^1.3.1" + simple-concat "^1.0.0" + +simple-get@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.0.tgz#73fa628278d21de83dadd5512d2cc1f4872bd675" + integrity sha512-ZalZGexYr3TA0SwySsr5HlgOOinS4Jsa8YB2GJ6lUNAazyAu4KG/VmzMTwAt2YVXzzVj8QmefmAonZIK2BSGcQ== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= + dependencies: + is-arrayish "^0.3.1" + +sirv@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.10.tgz#3e591f5a9ae2520f50d5830f5fae38d97e7be194" + integrity sha512-H5EZCoZaggEUQy8ocKsF7WAToGuZhjJlLvM3XOef46CbdIgbNeQ1p32N1PCuCjkVYwrAVOSMacN6CXXgIzuspg== + dependencies: + "@polka/url" "^1.0.0-next.9" + mime "^2.3.1" + totalist "^1.0.0" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= + +source-list-map@^2.0.0, source-list-map@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map-resolve@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.5.17, source-map-support@~0.5.19: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@0.7.3, source-map@~0.7.2: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + +source-map@0.8.0-beta.0: + version "0.8.0-beta.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11" + integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA== + dependencies: + whatwg-url "^7.0.0" + +source-map@^0.5.0: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +sourcemap-codec@^1.4.4: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.7" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" + integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sse-z@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/sse-z/-/sse-z-0.3.0.tgz#e215db7c303d6c4a4199d80cb63811cc28fa55b9" + integrity sha512-jfcXynl9oAOS9YJ7iqS2JMUEHOlvrRAD+54CENiWnc4xsuVLQVSgmwf7cwOTcBd/uq3XkQKBGojgvEtVXcJ/8w== + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stacktrace-parser@0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" + integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== + dependencies: + type-fest "^0.7.1" + +"statuses@>= 1.5.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +stream-browserify@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" + integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA== + dependencies: + inherits "~2.0.4" + readable-stream "^3.5.0" + +streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= + +string-env-interpolation@1.0.1, string-env-interpolation@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz#ad4397ae4ac53fe6c91d1402ad6f6a52862c7152" + integrity sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg== + +string-hash@1.1.3, string-hash@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b" + integrity sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs= + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + 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.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.trimend@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz#a22bd53cca5c7cf44d7c9d5c732118873d6cd18b" + integrity sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa" + integrity sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +stringify-object@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + +strip-ansi@6.0.0, strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +style-loader@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.2.1.tgz#c5cbbfbf1170d076cfdd86e0109c5bba114baa1a" + integrity sha512-ByHSTQvHLkWE9Ir5+lGbVOXhxX10fbprhLvdg96wedFZb4NDekDPxVKv5Fwmio+QcMlkkNfuK+5W1peQ5CUhZg== + dependencies: + loader-utils "^2.0.0" + schema-utils "^2.6.6" + +styled-jsx@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-3.3.2.tgz#2474601a26670a6049fb4d3f94bd91695b3ce018" + integrity sha512-daAkGd5mqhbBhLd6jYAjYBa9LpxYCzsgo/f6qzPdFxVB8yoGbhxvzQgkC0pfmCVvW3JuAEBn0UzFLBfkHVZG1g== + dependencies: + "@babel/types" "7.8.3" + babel-plugin-syntax-jsx "6.18.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" + integrity sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw== + +stylis@3.5.4: + version "3.5.4" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe" + integrity sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q== + +stylus-lookup@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/stylus-lookup/-/stylus-lookup-3.0.2.tgz#c9eca3ff799691020f30b382260a67355fefdddd" + integrity sha512-oEQGHSjg/AMaWlKe7gqsnYzan8DLcGIHe0dUaFkucZZ14z4zjENRlQMCHT4FNsiWnJf17YN9OvrCfCoi7VvOyg== + dependencies: + commander "^2.8.1" + debug "^4.1.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +supports-color@^5.3.0, supports-color@^5.4.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +swr@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/swr/-/swr-0.4.0.tgz#e76da9f981fe6dee0e133289e9b582fc80d9c41d" + integrity sha512-70qd1FHYHwIdYXW0jTpm5ktitzvPBCtyKz8ZzynWlY/rMqe4drYPgcl/H9Ipuh+Xv6ZW5viNx13ro8EKIWZcoQ== + dependencies: + dequal "2.0.2" + +symbol-observable@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== + +sync-fetch@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/sync-fetch/-/sync-fetch-0.3.0.tgz#77246da949389310ad978ab26790bb05f88d1335" + integrity sha512-dJp4qg+x4JwSEW1HibAuMi0IIrBI3wuQr2GimmqB7OXR50wmwzfdusG+p39R9w3R6aFtZ2mzvxvWKQ3Bd/vx3g== + dependencies: + buffer "^5.7.0" + node-fetch "^2.6.1" + +tabbable@^5.1.5: + version "5.1.5" + resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-5.1.5.tgz#efec48ede268d511c261e3b81facbb4782a35147" + integrity sha512-oVAPrWgLLqrbvQE8XqcU7CVBq6SQbaIbHkhOca3u7/jzuQvyZycrUKPCGr04qpEIUslmUlULbSeN+m3QrKEykA== + +tailwindcss@^1.9: + version "1.9.6" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-1.9.6.tgz#0c5089911d24e1e98e592a31bfdb3d8f34ecf1a0" + integrity sha512-nY8WYM/RLPqGsPEGEV2z63riyQPcHYZUJpAwdyBzVpxQHOHqHE+F/fvbCeXhdF1+TA5l72vSkZrtYCB9hRcwkQ== + dependencies: + "@fullhuman/postcss-purgecss" "^2.1.2" + autoprefixer "^9.4.5" + browserslist "^4.12.0" + bytes "^3.0.0" + chalk "^3.0.0 || ^4.0.0" + color "^3.1.2" + detective "^5.2.0" + fs-extra "^8.0.0" + html-tags "^3.1.0" + lodash "^4.17.20" + node-emoji "^1.8.1" + normalize.css "^8.0.1" + object-hash "^2.0.3" + postcss "^7.0.11" + postcss-functions "^3.0.0" + postcss-js "^2.0.0" + postcss-nested "^4.1.1" + postcss-selector-parser "^6.0.0" + postcss-value-parser "^4.1.0" + pretty-hrtime "^1.0.3" + reduce-css-calc "^2.1.6" + resolve "^1.14.2" + +tapable@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" + integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== + +tar-fs@^2.0.0, tar-fs@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +temp@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.4.0.tgz#671ad63d57be0fe9d7294664b3fc400636678a60" + integrity sha1-ZxrWPVe+D+nXKUZks/xABjZnimA= + +terser-webpack-plugin@^5.0.3: + version "5.1.1" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.1.tgz#7effadee06f7ecfa093dbbd3e9ab23f5f3ed8673" + integrity sha512-5XNNXZiR8YO6X6KhSGXfY0QrGrCRlSwAEjIIrlRQR4W8nP69TaJUlh3bkuac6zzgspiGPfKEHcY295MMVExl5Q== + dependencies: + jest-worker "^26.6.2" + p-limit "^3.1.0" + schema-utils "^3.0.0" + serialize-javascript "^5.0.1" + source-map "^0.6.1" + terser "^5.5.1" + +terser@5.5.1, terser@^5.5.1: + version "5.5.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.5.1.tgz#540caa25139d6f496fdea056e414284886fb2289" + integrity sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ== + dependencies: + commander "^2.20.0" + source-map "~0.7.2" + source-map-support "~0.5.19" + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +totalist@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" + integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= + dependencies: + punycode "^2.1.0" + +traverse-chain@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" + integrity sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE= + +traverse@0.6.6, traverse@^0.6.6: + version "0.6.6" + resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" + integrity sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc= + +trim-newlines@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30" + integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA== + +ts-loader@^7.0.0: + version "7.0.5" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-7.0.5.tgz#789338fb01cb5dc0a33c54e50558b34a73c9c4c5" + integrity sha512-zXypEIT6k3oTc+OZNx/cqElrsbBtYqDknf48OZos0NQ3RTt045fBIU8RRSu+suObBzYB355aIPGOe/3kj9h7Ig== + dependencies: + chalk "^2.3.0" + enhanced-resolve "^4.0.0" + loader-utils "^1.0.2" + micromatch "^4.0.0" + semver "^6.0.0" + +ts-log@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-2.2.3.tgz#4da5640fe25a9fb52642cd32391c886721318efb" + integrity sha512-XvB+OdKSJ708Dmf9ore4Uf/q62AYDTzFcAdxc8KNML1mmAWywRFVt/dn1KYJH8Agt5UJNujfM3znU5PxgAzA2w== + +ts-node@^9: + version "9.1.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" + integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== + dependencies: + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + +ts-pnp@^1.1.6: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" + integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== + +tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2, tslib@^2.0.0, tslib@^2.0.3, tslib@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" + integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== + +tslib@~2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" + integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== + +tsutils@^3.17.1: + version "3.19.1" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.19.1.tgz#d8566e0c51c82f32f9c25a4d367cd62409a547a9" + integrity sha512-GEdoBf5XI324lu7ycad7s6laADfnAqCw6wLGI+knxvw9vsIYBaJfYdmeCEG3FMMUiSm3OGgNb+m6utsWf5h9Vw== + dependencies: + tslib "^1.8.1" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + +type-fest@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" + integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" + integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.1.0.tgz#9bdc22c648cf8cf86dd23d32336a41cfb6475e3f" + integrity sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA== + +typescript@^3.0.3, typescript@^3.8.3, typescript@^3.9.5, typescript@^3.9.7: + version "3.9.7" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" + integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== + +typescript@^4.0.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" + integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== + +ua-parser-js@^0.7.18: + version "0.7.23" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.23.tgz#704d67f951e13195fbcd3d78818577f5bc1d547b" + integrity sha512-m4hvMLxgGHXG3O3fQVAyyAQpZzDOvwnhOTjYz5Xmr7r/+LpkNy3vJXdVRWgd1TkAb7NGROZuSy96CrlNVjA7KA== + +unc-path-regex@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" + integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unixify@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unixify/-/unixify-1.0.0.tgz#3a641c8c2ffbce4da683a5c70f03a462940c2090" + integrity sha1-OmQcjC/7zk2mg6XHDwOkYpQMIJA= + dependencies: + normalize-path "^2.1.1" + +unpipe@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +upper-case@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.1.tgz#6214d05e235dc817822464ccbae85822b3d8665f" + integrity sha512-laAsbea9SY5osxrv7S99vH9xAaJKrw5Qpdh4ENRLcaxipjKsiaBwiAsxfa8X5mObKNTQPsupSq0J/VIxsSJe3A== + dependencies: + tslib "^1.10.0" + +upper-case@^2.0.1, upper-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.2.tgz#d89810823faab1df1549b7d97a76f8662bae6f7a" + integrity sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg== + dependencies: + tslib "^2.0.3" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +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" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +url-parse@^1.4.3: + version "1.4.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +use-subscription@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1" + integrity sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA== + dependencies: + object-assign "^4.1.1" + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +valid-url@1.0.9, valid-url@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" + integrity sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA= + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vm-browserify@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== + +walkdir@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39" + integrity sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ== + +warning@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + +watchpack@2.0.0-beta.13: + version "2.0.0-beta.13" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.0.0-beta.13.tgz#9d9b0c094b8402139333e04eb6194643c8384f55" + integrity sha512-ZEFq2mx/k5qgQwgi6NOm+2ImICb8ngAkA/rZ6oyXZ7SgPn3pncf+nfhYTCrs3lmHwOxnPtGLTOuFLfpSMh1VMA== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +watchpack@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.1.0.tgz#e63194736bf3aa22026f7b191cd57907b0f9f696" + integrity sha512-UjgD1mqjkG99+3lgG36at4wPnUXNvis2v1utwTgQ43C22c4LD71LsYMExdWXh4HZ+RmW+B0t1Vrg2GpXAkTOQw== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= + dependencies: + defaults "^1.0.3" + +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + +webpack-bundle-analyzer@4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.3.0.tgz#2f3c0ca9041d5ee47fa418693cf56b4a518b578b" + integrity sha512-J3TPm54bPARx6QG8z4cKBszahnUglcv70+N+8gUqv2I5KOFHJbzBiLx+pAp606so0X004fxM7hqRu10MLjJifA== + dependencies: + acorn "^8.0.4" + acorn-walk "^8.0.0" + chalk "^4.1.0" + commander "^6.2.0" + gzip-size "^6.0.0" + lodash "^4.17.20" + opener "^1.5.2" + sirv "^1.0.7" + ws "^7.3.1" + +webpack-sources@1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack-sources@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac" + integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w== + dependencies: + source-list-map "^2.0.1" + source-map "^0.6.1" + +webpack@4.44.1, webpack@5.11.1: + version "5.11.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.11.1.tgz#39b2b9daeb5c6c620e03b7556ec674eaed4016b4" + integrity sha512-tNUIdAmYJv+nupRs/U/gqmADm6fgrf5xE+rSlSsf2PgsGO7j2WG7ccU6AWNlOJlHFl+HnmXlBmHIkiLf+XA9mQ== + dependencies: + "@types/eslint-scope" "^3.7.0" + "@types/estree" "^0.0.45" + "@webassemblyjs/ast" "1.9.1" + "@webassemblyjs/helper-module-context" "1.9.1" + "@webassemblyjs/wasm-edit" "1.9.1" + "@webassemblyjs/wasm-parser" "1.9.1" + acorn "^8.0.4" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.3.1" + eslint-scope "^5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.4" + json-parse-better-errors "^1.0.2" + loader-runner "^4.1.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + pkg-dir "^5.0.0" + schema-utils "^3.0.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.0.3" + watchpack "^2.0.0" + webpack-sources "^2.1.1" + +whatwg-fetch@^3.4.1: + version "3.5.0" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz#605a2cd0a7146e5db141e29d1c62ab84c0c4c868" + integrity sha512-jXkLtsR42xhXg7akoDKvKWE40eJeI+2KZqcp2h3NsOrRnDvtWX36KcKl30dy+hxECivdk2BVUHVNrPtoMBUx6A== + +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which-pm-runs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" + integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrap-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba" + integrity sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo= + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +ws@7.4.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.1.tgz#a333be02696bd0e54cea0434e21dcc8a9ac294bb" + integrity sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ== + +ws@^7.3.1: + version "7.4.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.2.tgz#782100048e54eb36fe9843363ab1c68672b261dd" + integrity sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA== + +xtend@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" + integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== + +y18n@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" + integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml-ast-parser@^0.0.43: + version "0.0.43" + resolved "https://registry.yarnpkg.com/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz#e8a23e6fb4c38076ab92995c5dca33f3d3d7c9bb" + integrity sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A== + +yaml@^1.10.0, yaml@^1.7.2: + version "1.10.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" + integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== + +yargs-parser@^18.1.2, yargs-parser@^18.1.3: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^20.2.2: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs@^15.3.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + +yargs@^16.1.1: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 28b244760afb5fee9ba71779a9f1f5c38ec99fc7 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Mon, 11 Jan 2021 13:25:25 -0300 Subject: [PATCH 025/221] changes --- framework/bigcommerce/api/operations/get-all-products.ts | 4 ++-- framework/bigcommerce/api/operations/get-product.ts | 4 ++-- package.json | 2 +- pages/index2.tsx | 7 +++++++ yarn.lock | 2 +- 5 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 pages/index2.tsx diff --git a/framework/bigcommerce/api/operations/get-all-products.ts b/framework/bigcommerce/api/operations/get-all-products.ts index 599109a7d..534c20bfb 100644 --- a/framework/bigcommerce/api/operations/get-all-products.ts +++ b/framework/bigcommerce/api/operations/get-all-products.ts @@ -94,7 +94,7 @@ async function getAllProducts({ variables?: ProductVariables config?: BigcommerceConfig preview?: boolean -} = {}): Promise<{ products: Product[] }> { +} = {}): Promise<{ products: Product[] | any[] }> { config = getConfig(config) const locale = vars.locale || config.locale @@ -127,7 +127,7 @@ async function getAllProducts({ }) } - return { products: products.map(normalizeProduct) } + return { products } } export default getAllProducts diff --git a/framework/bigcommerce/api/operations/get-product.ts b/framework/bigcommerce/api/operations/get-product.ts index 2476d1398..403208264 100644 --- a/framework/bigcommerce/api/operations/get-product.ts +++ b/framework/bigcommerce/api/operations/get-product.ts @@ -93,7 +93,7 @@ async function getProduct({ variables: ProductVariables config?: BigcommerceConfig preview?: boolean -}): Promise<Product | {}> { +}): Promise<Product | {} | any> { config = getConfig(config) const locale = vars.locale || config.locale @@ -111,7 +111,7 @@ async function getProduct({ setProductLocaleMeta(product) } - return { product: normalizeProduct(product) } + return { product } } return {} diff --git a/package.json b/package.json index b90bf4cf8..d1f669a36 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "lodash.debounce": "^4.0.8", "lodash.random": "^3.2.0", "lodash.throttle": "^4.1.1", - "next": "^10.0.5-canary.11", + "next": "^10.0.5", "next-seo": "^4.11.0", "next-themes": "^0.0.4", "postcss-nesting": "^7.0.1", diff --git a/pages/index2.tsx b/pages/index2.tsx new file mode 100644 index 000000000..9b94444e1 --- /dev/null +++ b/pages/index2.tsx @@ -0,0 +1,7 @@ +import { Layout } from '@components/common' + +export default function Home() { + return <>Hello</> +} + +Home.Layout = Layout diff --git a/yarn.lock b/yarn.lock index 8e9ded267..769c2e077 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5038,7 +5038,7 @@ next-unused@^0.0.3: madge "^3.8.0" ts-loader "^7.0.0" -next@^10.0.5-canary.11: +next@^10.0.5: version "10.0.5" resolved "https://registry.yarnpkg.com/next/-/next-10.0.5.tgz#8071e0aa1883266c91943aa7c6b73deadb064793" integrity sha512-yr7ap2TLugf0aMHz+3JoKFP9CCkFE+k6jCfdUymORhptjLYZbD3YGlTcUC1CRl+b5Phlbl7m/WUIPde0VcguiA== From 8f9bbe19cae4af57815850c46fd450941c9667fa Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Mon, 11 Jan 2021 13:55:45 -0300 Subject: [PATCH 026/221] index.ts broke my tree shaking --- components/auth/LoginView.tsx | 2 +- components/auth/SignUpView.tsx | 2 +- components/common/UserNav/DropdownMenu.tsx | 2 +- .../product/ProductCard/ProductCard.tsx | 4 ++-- .../product/ProductView/ProductView.tsx | 2 +- .../WishlistButton/WishlistButton.tsx | 6 ++++-- .../api/operations/get-all-products.ts | 2 +- .../bigcommerce/api/operations/get-product.ts | 2 +- framework/bigcommerce/lib/normalize.ts | 21 +++++++++---------- next.config.js | 8 ++----- package.json | 3 +++ tsconfig.json | 8 +------ 12 files changed, 28 insertions(+), 34 deletions(-) diff --git a/components/auth/LoginView.tsx b/components/auth/LoginView.tsx index 7b402e6d7..89d5bf893 100644 --- a/components/auth/LoginView.tsx +++ b/components/auth/LoginView.tsx @@ -1,6 +1,6 @@ import { FC, useEffect, useState, useCallback } from 'react' import { Logo, Button, Input } from '@components/ui' -import { useLogin } from '@framework/auth' +import useLogin from '@framework/auth/use-login' import { useUI } from '@components/ui/context' import { validate } from 'email-validator' diff --git a/components/auth/SignUpView.tsx b/components/auth/SignUpView.tsx index 49351bfe9..1b619828b 100644 --- a/components/auth/SignUpView.tsx +++ b/components/auth/SignUpView.tsx @@ -3,7 +3,7 @@ import { validate } from 'email-validator' import { Info } from '@components/icons' import { useUI } from '@components/ui/context' import { Logo, Button, Input } from '@components/ui' -import { useSignup } from '@framework/auth' +import useSignup from '@framework/auth/use-signup' interface Props {} diff --git a/components/common/UserNav/DropdownMenu.tsx b/components/common/UserNav/DropdownMenu.tsx index f8cedc332..43f842009 100644 --- a/components/common/UserNav/DropdownMenu.tsx +++ b/components/common/UserNav/DropdownMenu.tsx @@ -8,7 +8,7 @@ import { Avatar } from '@components/common' import { Moon, Sun } from '@components/icons' import { useUI } from '@components/ui/context' import ClickOutside from '@lib/click-outside' -import { useLogout } from '@framework/auth' +import useLogout from '@framework/auth/use-logout' import { disableBodyScroll, diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index 863a3b2b8..b3c061a5c 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -57,11 +57,11 @@ const ProductCard: FC<Props> = ({ {product.price.currencyCode} </span> </div> - <WishlistButton + {/* <WishlistButton className={s.wishlistButton} productId={product.id} variant={product.variant[0]!} - /> + /> */} </div> <div className={s.imageContainer}> {product?.images && ( diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index 34bae1f69..b5aafd0b1 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -8,7 +8,7 @@ import { useUI } from '@components/ui' import { Swatch, ProductSlider } from '@components/product' import { Button, Container, Text } from '@components/ui' -import { usePrice } from '@framework/product' +import usePrice from '@framework/product/use-price' import { useAddItem } from '@framework/cart' import { diff --git a/components/wishlist/WishlistButton/WishlistButton.tsx b/components/wishlist/WishlistButton/WishlistButton.tsx index aca0ad148..dced18a89 100644 --- a/components/wishlist/WishlistButton/WishlistButton.tsx +++ b/components/wishlist/WishlistButton/WishlistButton.tsx @@ -3,8 +3,10 @@ import cn from 'classnames' import { Heart } from '@components/icons' import { useUI } from '@components/ui' -import { useCustomer } from '@framework/customer' -import { useAddItem, useWishlist, useRemoveItem } from '@framework/wishlist' +import useCustomer from '@framework/customer/use-customer' +import useAddItem from '@framework/wishlist/use-add-item' +import useRemoveItem from '@framework/wishlist/use-remove-item' +import useWishlist from '@framework/wishlist/use-add-item' type Props = { productId: Product['id'] diff --git a/framework/bigcommerce/api/operations/get-all-products.ts b/framework/bigcommerce/api/operations/get-all-products.ts index 534c20bfb..60aa197cc 100644 --- a/framework/bigcommerce/api/operations/get-all-products.ts +++ b/framework/bigcommerce/api/operations/get-all-products.ts @@ -127,7 +127,7 @@ async function getAllProducts({ }) } - return { products } + return { products: products.map(({ node }) => normalizeProduct(node)) } } export default getAllProducts diff --git a/framework/bigcommerce/api/operations/get-product.ts b/framework/bigcommerce/api/operations/get-product.ts index 403208264..aa480ac5e 100644 --- a/framework/bigcommerce/api/operations/get-product.ts +++ b/framework/bigcommerce/api/operations/get-product.ts @@ -111,7 +111,7 @@ async function getProduct({ setProductLocaleMeta(product) } - return { product } + return { product: normalizeProduct(product) } } return {} diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index 51b508edd..0e3dbc128 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -1,15 +1,14 @@ -export function normalizeProduct(productNode: any): Product { - // console.log(productNode) +import { Product as BCProduct } from '@framework/schema' + +export function normalizeProduct(productNode: BCProduct): Product { const { - node: { - entityId: id, - images, - variants, - productOptions, - prices, - path, - ...rest - }, + entityId: id, + images, + variants, + productOptions, + prices, + path, + ...rest } = productNode return { diff --git a/next.config.js b/next.config.js index ee2db68bc..e732ef78a 100644 --- a/next.config.js +++ b/next.config.js @@ -1,8 +1,4 @@ -const bundleAnalyzer = require('@next/bundle-analyzer')({ - enabled: !!process.env.BUNDLE_ANALYZE, -}) - -module.exports = bundleAnalyzer({ +module.exports = { images: { domains: ['cdn11.bigcommerce.com'], }, @@ -38,4 +34,4 @@ module.exports = bundleAnalyzer({ }, ] }, -}) +} diff --git a/package.json b/package.json index d1f669a36..a30af5b97 100644 --- a/package.json +++ b/package.json @@ -99,5 +99,8 @@ "resolutions": { "webpack": "5.11.1" }, + "node": { + "net": "empty" + }, "license": "MIT" } diff --git a/tsconfig.json b/tsconfig.json index 480cc2cb4..98639f61e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,12 +26,6 @@ "@framework": ["framework/bigcommerce"] } }, - "include": [ - "next-env.d.ts", - "framework/*.d.ts", - "**/*.ts", - "**/*.tsx", - "**/*.js" - ], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], "exclude": ["node_modules"] } From dccc5ef430b4191aee9ce6cd55289e9264b37d64 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Mon, 11 Jan 2021 14:13:29 -0300 Subject: [PATCH 027/221] slug --- .../product/ProductCard/ProductCard.tsx | 6 ++-- .../product/ProductView/ProductView.tsx | 15 ++++++---- framework/bigcommerce/lib/normalize.ts | 29 ++++++++++++------- framework/types.d.ts | 4 +-- 4 files changed, 34 insertions(+), 20 deletions(-) diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index b3c061a5c..658476e7d 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -57,11 +57,11 @@ const ProductCard: FC<Props> = ({ {product.price.currencyCode} </span> </div> - {/* <WishlistButton + <WishlistButton className={s.wishlistButton} productId={product.id} - variant={product.variant[0]!} - /> */} + variant={product.variants[0]} + /> </div> <div className={s.imageContainer}> {product?.images && ( diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index b5aafd0b1..4f9844fcc 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -25,21 +25,26 @@ interface Props { } const ProductView: FC<Props> = ({ product }) => { + console.log(product) + const addItem = useAddItem() const { price } = usePrice({ amount: product.price.value, baseAmount: product.price.retailValue, currencyCode: product.price.currencyCode!, }) + const { openSidebar } = useUI() - const options = getProductOptions(product) + + // const options = getProductOptions(product) + const [loading, setLoading] = useState(false) const [choices, setChoices] = useState<SelectedOptions>({ size: null, color: null, }) - const variant = getCurrentVariant(product, choices) || product.variants[0] + // const variant = getCurrentVariant(product, choices) || product.variants[0] const addToCart = async () => { setLoading(true) @@ -106,7 +111,7 @@ const ProductView: FC<Props> = ({ product }) => { <div className={s.sidebar}> <section> - {options?.map((opt: any) => ( + {/* {options?.map((opt: any) => ( <div className="pb-4" key={opt.displayName}> <h2 className="uppercase font-medium">{opt.displayName}</h2> <div className="flex flex-row py-4"> @@ -133,7 +138,7 @@ const ProductView: FC<Props> = ({ product }) => { })} </div> </div> - ))} + ))} */} <div className="pb-14 break-words w-full max-w-xl"> <Text html={product.description} /> @@ -146,7 +151,7 @@ const ProductView: FC<Props> = ({ product }) => { className={s.button} onClick={addToCart} loading={loading} - disabled={!variant} + // disabled={!variant} > Add to Cart </Button> diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index 0e3dbc128..73c477d9a 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -13,16 +13,25 @@ export function normalizeProduct(productNode: BCProduct): Product { return { path, - slug: path?.slice(1, -1), - images: images?.edges?.map( - ({ node: { urlOriginal, altText, ...rest } }: any) => ({ - url: urlOriginal, - alt: altText, - ...rest, - }) - ), - variants: variants?.edges?.map(({ node }: any) => node), - productOptions: productOptions?.edges?.map(({ node }: any) => node), + slug: path?.replace(/^\/+|\/+$/g, ''), + images: images.edges + ? images.edges.map( + ({ node: { urlOriginal, altText, ...rest } }: any) => ({ + url: urlOriginal, + alt: altText, + ...rest, + }) + ) + : [], + variants: variants.edges + ? variants.edges.map(({ node: { entityId, ...rest } }: any) => ({ + id: entityId, + ...rest, + })) + : [], + productOptions: productOptions.edges + ? productOptions.edges.map(({ node }: any) => node) + : [], price: { value: prices?.price.value, currencyCode: prices?.price.currencyCode, diff --git a/framework/types.d.ts b/framework/types.d.ts index 1342071dd..b189d26b6 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -8,8 +8,8 @@ interface Product extends Entity { description: string slug: string path?: string - images: ProductImage[] | any[] | undefined - variants: ProductVariant[] | any[] | null | undefined + images: ProductImage[] + variants: ProductVariant[] price: ProductPrice } interface ProductImage { From 4fdaae2197238b695ab5627dca6694c5d2d3618c Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Mon, 11 Jan 2021 14:54:05 -0300 Subject: [PATCH 028/221] Normalized Options and Swatches --- components/product/ProductView/ProductView.tsx | 12 ++++-------- framework/bigcommerce/lib/normalize.ts | 17 +++++++++++++++-- framework/types.d.ts | 12 ++++++++++++ tsconfig.json | 8 +++++++- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index 4f9844fcc..290af8b68 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -25,8 +25,6 @@ interface Props { } const ProductView: FC<Props> = ({ product }) => { - console.log(product) - const addItem = useAddItem() const { price } = usePrice({ amount: product.price.value, @@ -36,8 +34,6 @@ const ProductView: FC<Props> = ({ product }) => { const { openSidebar } = useUI() - // const options = getProductOptions(product) - const [loading, setLoading] = useState(false) const [choices, setChoices] = useState<SelectedOptions>({ size: null, @@ -111,16 +107,16 @@ const ProductView: FC<Props> = ({ product }) => { <div className={s.sidebar}> <section> - {/* {options?.map((opt: any) => ( + {product.options?.map((opt) => ( <div className="pb-4" key={opt.displayName}> <h2 className="uppercase font-medium">{opt.displayName}</h2> <div className="flex flex-row py-4"> - {opt.values.map((v: any, i: number) => { + {opt.values.map((v, i: number) => { const active = (choices as any)[opt.displayName] return ( <Swatch - key={`${v.entityId}-${i}`} + key={`${opt.id}-${i}`} active={v.label === active} variant={opt.displayName} color={v.hexColors ? v.hexColors[0] : ''} @@ -138,7 +134,7 @@ const ProductView: FC<Props> = ({ product }) => { })} </div> </div> - ))} */} + ))} <div className="pb-14 break-words w-full max-w-xl"> <Text html={product.description} /> diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index 73c477d9a..a2be86478 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -8,6 +8,7 @@ export function normalizeProduct(productNode: BCProduct): Product { productOptions, prices, path, + options: _, ...rest } = productNode @@ -29,8 +30,20 @@ export function normalizeProduct(productNode: BCProduct): Product { ...rest, })) : [], - productOptions: productOptions.edges - ? productOptions.edges.map(({ node }: any) => node) + options: productOptions.edges + ? productOptions.edges.map( + ({ + node: { + entityId, + values: { edges }, + ...rest + }, + }: any) => ({ + id: entityId, + values: edges.map(({ node }: any) => node), + ...rest, + }) + ) : [], price: { value: prices?.price.value, diff --git a/framework/types.d.ts b/framework/types.d.ts index b189d26b6..e44f23bbb 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -11,7 +11,19 @@ interface Product extends Entity { images: ProductImage[] variants: ProductVariant[] price: ProductPrice + options: ProductOption[] } + +interface ProductOption extends Entity { + displayName: string + values: ProductOptionValues[] +} + +interface ProductOptionValues { + label: string + hexColors?: string[] +} + interface ProductImage { url: string alt?: string diff --git a/tsconfig.json b/tsconfig.json index 98639f61e..c45e16c4d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,6 +26,12 @@ "@framework": ["framework/bigcommerce"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], + "include": [ + "next-env.d.ts", + "./framework/types.d.ts", + "**/*.ts", + "**/*.tsx", + "**/*.js" + ], "exclude": ["node_modules"] } From 9bbd7feed083d5bd7a055dd665a78f4c905f9000 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Mon, 11 Jan 2021 15:03:04 -0300 Subject: [PATCH 029/221] Restored Add to cart --- components/product/ProductView/ProductView.tsx | 12 +++--------- components/product/Swatch/Swatch.tsx | 2 +- framework/bigcommerce/lib/normalize.ts | 4 +++- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index 290af8b68..2c8a456d9 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -11,11 +11,7 @@ import { Button, Container, Text } from '@components/ui' import usePrice from '@framework/product/use-price' import { useAddItem } from '@framework/cart' -import { - getCurrentVariant, - getProductOptions, - SelectedOptions, -} from '../helpers' +import { getCurrentVariant, SelectedOptions } from '../helpers' import WishlistButton from '@components/wishlist/WishlistButton' interface Props { @@ -41,13 +37,13 @@ const ProductView: FC<Props> = ({ product }) => { }) // const variant = getCurrentVariant(product, choices) || product.variants[0] - + console.log('PRODUCT VIEW', product) const addToCart = async () => { setLoading(true) try { await addItem({ productId: Number(product.id), - variantId: Number(product.variants[0].id), + variantId: Number(product.variants[0].id), // TODO(bc) send the correct variant }) openSidebar() setLoading(false) @@ -113,7 +109,6 @@ const ProductView: FC<Props> = ({ product }) => { <div className="flex flex-row py-4"> {opt.values.map((v, i: number) => { const active = (choices as any)[opt.displayName] - return ( <Swatch key={`${opt.id}-${i}`} @@ -147,7 +142,6 @@ const ProductView: FC<Props> = ({ product }) => { className={s.button} onClick={addToCart} loading={loading} - // disabled={!variant} > Add to Cart </Button> diff --git a/components/product/Swatch/Swatch.tsx b/components/product/Swatch/Swatch.tsx index 30c9be7c3..34244321f 100644 --- a/components/product/Swatch/Swatch.tsx +++ b/components/product/Swatch/Swatch.tsx @@ -13,7 +13,7 @@ interface Props { color?: string } -const Swatch: FC<Props & ButtonProps> = ({ +const Swatch: FC<Omit<ButtonProps, 'variant'> & Props> = ({ className, color = '', label, diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index a2be86478..ad6302c2e 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -8,11 +8,13 @@ export function normalizeProduct(productNode: BCProduct): Product { productOptions, prices, path, - options: _, + id: _, + options: _0, ...rest } = productNode return { + id, path, slug: path?.replace(/^\/+|\/+$/g, ''), images: images.edges From 287e6904954e6ea8c8b501d1c04f8f38d57332ab Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Mon, 11 Jan 2021 16:16:00 -0300 Subject: [PATCH 030/221] Correct Variant Added to Cart --- .../product/ProductView/ProductView.tsx | 23 ++++++----- components/product/helpers.ts | 38 +++--------------- .../wishlist/WishlistCard/WishlistCard.tsx | 29 +++++++------- framework/bigcommerce/api/catalog/products.ts | 2 +- framework/bigcommerce/lib/normalize.ts | 40 ++++++++++++------- .../bigcommerce/wishlist/use-wishlist.tsx | 2 +- framework/types.d.ts | 7 +--- 7 files changed, 63 insertions(+), 78 deletions(-) diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index 2c8a456d9..7151208ac 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -11,7 +11,7 @@ import { Button, Container, Text } from '@components/ui' import usePrice from '@framework/product/use-price' import { useAddItem } from '@framework/cart' -import { getCurrentVariant, SelectedOptions } from '../helpers' +import { getVariant, SelectedOptions } from '../helpers' import WishlistButton from '@components/wishlist/WishlistButton' interface Props { @@ -27,23 +27,24 @@ const ProductView: FC<Props> = ({ product }) => { baseAmount: product.price.retailValue, currencyCode: product.price.currencyCode!, }) - const { openSidebar } = useUI() - const [loading, setLoading] = useState(false) const [choices, setChoices] = useState<SelectedOptions>({ size: null, color: null, }) - // const variant = getCurrentVariant(product, choices) || product.variants[0] - console.log('PRODUCT VIEW', product) + // Select the correct variant based on choices + const variant = getVariant(product, choices) + const addToCart = async () => { setLoading(true) try { await addItem({ productId: Number(product.id), - variantId: Number(product.variants[0].id), // TODO(bc) send the correct variant + variantId: variant + ? Number(variant.id) + : Number(product.variants[0].id), }) openSidebar() setLoading(false) @@ -108,11 +109,14 @@ const ProductView: FC<Props> = ({ product }) => { <h2 className="uppercase font-medium">{opt.displayName}</h2> <div className="flex flex-row py-4"> {opt.values.map((v, i: number) => { - const active = (choices as any)[opt.displayName] + const active = (choices as any)[ + opt.displayName.toLowerCase() + ] + return ( <Swatch key={`${opt.id}-${i}`} - active={v.label === active} + active={v.label.toLowerCase() === active} variant={opt.displayName} color={v.hexColors ? v.hexColors[0] : ''} label={v.label} @@ -120,7 +124,7 @@ const ProductView: FC<Props> = ({ product }) => { setChoices((choices) => { return { ...choices, - [opt.displayName]: v.label, + [opt.displayName.toLowerCase()]: v.label.toLowerCase(), } }) }} @@ -142,6 +146,7 @@ const ProductView: FC<Props> = ({ product }) => { className={s.button} onClick={addToCart} loading={loading} + disabled={!variant} > Add to Cart </Button> diff --git a/components/product/helpers.ts b/components/product/helpers.ts index eda8d434a..57c37dbc8 100644 --- a/components/product/helpers.ts +++ b/components/product/helpers.ts @@ -1,5 +1,3 @@ -import type { ProductNode } from '@framework/api/operations/get-product' - export type SelectedOptions = { size: string | null color: string | null @@ -10,42 +8,18 @@ export type ProductOption = { values: any } -// Returns the available options of a product -export function getProductOptions(product: ProductNode) { - const options = product.productOptions.edges?.reduce<ProductOption[]>( - (arr, edge) => { - if (edge?.node.__typename === 'MultipleChoiceOption') { - arr.push({ - displayName: edge.node.displayName.toLowerCase(), - values: edge.node.values.edges?.map((edge) => edge?.node), - }) - } - return arr - }, - [] - ) - - return options -} - -// Finds a variant in the product that matches the selected options -export function getCurrentVariant(product: ProductNode, opts: SelectedOptions) { - const variant = product.variants.edges?.find((edge) => { - const { node } = edge ?? {} - +export function getVariant(product: Product, opts: SelectedOptions) { + const variant = product.variants.find((variant) => { return Object.entries(opts).every(([key, value]) => - node?.productOptions.edges?.find((edge) => { + variant.options.find((option) => { if ( - edge?.node.__typename === 'MultipleChoiceOption' && - edge.node.displayName.toLowerCase() === key + option.__typename === 'MultipleChoiceOption' && + option.displayName.toLowerCase() === key.toLowerCase() ) { - return edge.node.values.edges?.find( - (valueEdge) => valueEdge?.node.label === value - ) + return option.values.find((v) => v.label.toLowerCase() === value) } }) ) }) - return variant } diff --git a/components/wishlist/WishlistCard/WishlistCard.tsx b/components/wishlist/WishlistCard/WishlistCard.tsx index b9b1a2f3e..82147f575 100644 --- a/components/wishlist/WishlistCard/WishlistCard.tsx +++ b/components/wishlist/WishlistCard/WishlistCard.tsx @@ -2,21 +2,20 @@ import { FC, useState } from 'react' import cn from 'classnames' import Link from 'next/link' import Image from 'next/image' -import type { WishlistItem } from '@framework/api/wishlist' -import usePrice from '@framework/product/use-price' -import useRemoveItem from '@framework/wishlist/use-remove-item' -import useAddItem from '@framework/cart/use-add-item' -import { useUI } from '@components/ui/context' -import { Button, Text } from '@components/ui' -import { Trash } from '@components/icons' import s from './WishlistCard.module.css' +import { Trash } from '@components/icons' +import { Button, Text } from '@components/ui' + +import { useUI } from '@components/ui/context' +import usePrice from '@framework/product/use-price' +import useAddItem from '@framework/cart/use-add-item' +import useRemoveItem from '@framework/wishlist/use-remove-item' interface Props { - item: WishlistItem + product: Product } -const WishlistCard: FC<Props> = ({ item }) => { - const product = item.product! +const WishlistCard: FC<Props> = ({ product }) => { const { price } = usePrice({ amount: product.prices?.price?.value, baseAmount: product.prices?.retailPrice?.value, @@ -34,7 +33,7 @@ const WishlistCard: FC<Props> = ({ item }) => { try { // If this action succeeds then there's no need to do `setRemoving(true)` // because the component will be removed from the view - await removeItem({ id: item.id! }) + await removeItem({ id: product.id! }) } catch (error) { setRemoving(false) } @@ -43,8 +42,8 @@ const WishlistCard: FC<Props> = ({ item }) => { setLoading(true) try { await addItem({ - productId: product.entityId, - variantId: product.variants.edges?.[0]?.node.entityId!, + productId: Number(product.id), + variantId: Number(product.variants[0].id), }) openSidebar() setLoading(false) @@ -57,10 +56,10 @@ const WishlistCard: FC<Props> = ({ item }) => { <div className={cn(s.root, { 'opacity-75 pointer-events-none': removing })}> <div className={`col-span-3 ${s.productBg}`}> <Image - src={product.images.edges?.[0]?.node.urlOriginal!} + src={product.images[0].url} width={400} height={400} - alt={product.images.edges?.[0]?.node.altText || 'Product Image'} + alt={product.images[0].alt || 'Product Image'} /> </div> diff --git a/framework/bigcommerce/api/catalog/products.ts b/framework/bigcommerce/api/catalog/products.ts index 0e3690e55..7456fc3ac 100644 --- a/framework/bigcommerce/api/catalog/products.ts +++ b/framework/bigcommerce/api/catalog/products.ts @@ -21,7 +21,7 @@ export type ProductsHandlers = { const METHODS = ['GET'] -// TODO: a complete implementation should have schema validation for `req.body` +// TODO(lf): a complete implementation should have schema validation for `req.body` const productsApi: BigcommerceApiHandler< SearchProductsData, ProductsHandlers diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index ad6302c2e..5e3992d4b 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -1,5 +1,19 @@ import { Product as BCProduct } from '@framework/schema' +function productOptionNormalize({ + node: { + entityId, + values: { edges }, + ...rest + }, +}: any) { + return { + id: entityId, + values: edges.map(({ node }: any) => node), + ...rest, + } +} + export function normalizeProduct(productNode: BCProduct): Product { const { entityId: id, @@ -10,6 +24,7 @@ export function normalizeProduct(productNode: BCProduct): Product { path, id: _, options: _0, + brand, ...rest } = productNode @@ -27,26 +42,21 @@ export function normalizeProduct(productNode: BCProduct): Product { ) : [], variants: variants.edges - ? variants.edges.map(({ node: { entityId, ...rest } }: any) => ({ - id: entityId, - ...rest, - })) - : [], - options: productOptions.edges - ? productOptions.edges.map( - ({ - node: { - entityId, - values: { edges }, - ...rest - }, - }: any) => ({ + ? variants.edges.map( + ({ node: { entityId, productOptions, ...rest } }: any) => ({ id: entityId, - values: edges.map(({ node }: any) => node), + options: productOptions.edges.map(productOptionNormalize), ...rest, }) ) : [], + options: productOptions.edges + ? productOptions.edges.map(productOptionNormalize) + : [], + brand: { + id: brand?.entityId, + ...brand, + }, price: { value: prices?.price.value, currencyCode: prices?.price.currencyCode, diff --git a/framework/bigcommerce/wishlist/use-wishlist.tsx b/framework/bigcommerce/wishlist/use-wishlist.tsx index 6ebc8459d..0e1671039 100644 --- a/framework/bigcommerce/wishlist/use-wishlist.tsx +++ b/framework/bigcommerce/wishlist/use-wishlist.tsx @@ -46,7 +46,7 @@ export function extendHook( const response = useCommerceWishlist( defaultOpts, [ - ['customerId', customer?.entityId], + ['customerId', customer?.id], ['includeProducts', includeProducts], ], customFetcher, diff --git a/framework/types.d.ts b/framework/types.d.ts index e44f23bbb..e020ba893 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -31,6 +31,7 @@ interface ProductImage { interface ProductVariant { id: string | number + options: ProductOption[] } interface ProductPrice { @@ -51,21 +52,17 @@ interface Wishlist extends Entity { interface Order {} -interface Customer extends Entity { - [prop: string]: any -} +interface Customer extends Entity {} type UseCustomerResponse = { customer: Customer } | null interface Category extends Entity { - id: string name: string } interface Brand extends Entity { - id: string name: string } From fc34856e506a4d8186e792b6845617968e34f4c6 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Tue, 12 Jan 2021 16:59:07 -0300 Subject: [PATCH 031/221] Normalizing Cart Responses --- .../cart/CartSidebarView/CartSidebarView.tsx | 1 + components/common/Layout/Layout.tsx | 5 ++- components/product/helpers.ts | 5 --- framework/bigcommerce/cart/use-cart.tsx | 3 +- framework/bigcommerce/lib/normalize.ts | 22 +++++++--- framework/commerce/cart/use-cart.tsx | 2 +- framework/types.d.ts | 5 ++- pages/cart.tsx | 41 ++++++++++++------- 8 files changed, 54 insertions(+), 30 deletions(-) diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index ca73a6ec3..466ef15b6 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -12,6 +12,7 @@ import s from './CartSidebarView.module.css' const CartSidebarView: FC = () => { const { closeSidebar } = useUI() const { data, isEmpty } = useCart() + const { price: subTotal } = usePrice( data && { amount: data.base_amount, diff --git a/components/common/Layout/Layout.tsx b/components/common/Layout/Layout.tsx index ff8467a23..f4dac69ac 100644 --- a/components/common/Layout/Layout.tsx +++ b/components/common/Layout/Layout.tsx @@ -7,13 +7,12 @@ import { useUI } from '@components/ui/context' import { Navbar, Footer } from '@components/common' import { useAcceptCookies } from '@lib/hooks/useAcceptCookies' import { Sidebar, Button, Modal, LoadingDots } from '@components/ui' -import { CartSidebarView } from '@components/cart' +import CartSidebarView from '@components/cart/CartSidebarView' import LoginView from '@components/auth/LoginView' import { CommerceProvider } from '@framework' import type { Page } from '@framework/api/operations/get-all-pages' - const Loading = () => ( <div className="w-80 h-80 flex items-center text-center justify-center p-3"> <LoadingDots /> @@ -28,10 +27,12 @@ const SignUpView = dynamic( () => import('@components/auth/SignUpView'), dynamicProps ) + const ForgotPassword = dynamic( () => import('@components/auth/ForgotPassword'), dynamicProps ) + const FeatureBar = dynamic( () => import('@components/common/FeatureBar'), dynamicProps diff --git a/components/product/helpers.ts b/components/product/helpers.ts index 57c37dbc8..ae0c43530 100644 --- a/components/product/helpers.ts +++ b/components/product/helpers.ts @@ -3,11 +3,6 @@ export type SelectedOptions = { color: string | null } -export type ProductOption = { - displayName: string - values: any -} - export function getVariant(product: Product, opts: SelectedOptions) { const variant = product.variants.find((variant) => { return Object.entries(opts).every(([key, value]) => diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index 83743096e..54582372f 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -2,6 +2,7 @@ import type { HookFetcher } from '@commerce/utils/types' import type { SwrOptions } from '@commerce/utils/use-data' import useCommerceCart, { CartInput } from '@commerce/cart/use-cart' import type { Cart } from '../api/cart' +import { normalizeCart } from '../lib/normalize' const defaultOpts = { url: '/api/bigcommerce/cart', @@ -39,7 +40,7 @@ export function extendHook( set: (x) => x, }) - return response + return normalizeCart(response) } useCart.extend = extendHook diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index 5e3992d4b..686581b9f 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -1,6 +1,6 @@ import { Product as BCProduct } from '@framework/schema' -function productOptionNormalize({ +function normalizeProductOption({ node: { entityId, values: { edges }, @@ -9,7 +9,7 @@ function productOptionNormalize({ }: any) { return { id: entityId, - values: edges.map(({ node }: any) => node), + values: edges?.map(({ node }: any) => node), ...rest, } } @@ -45,16 +45,18 @@ export function normalizeProduct(productNode: BCProduct): Product { ? variants.edges.map( ({ node: { entityId, productOptions, ...rest } }: any) => ({ id: entityId, - options: productOptions.edges.map(productOptionNormalize), + options: productOptions?.edges + ? productOptions.edges.map(normalizeProductOption) + : [], ...rest, }) ) : [], options: productOptions.edges - ? productOptions.edges.map(productOptionNormalize) + ? productOptions?.edges.map(normalizeProductOption) : [], brand: { - id: brand?.entityId, + id: brand?.entityId ? brand?.entityId : null, ...brand, }, price: { @@ -64,3 +66,13 @@ export function normalizeProduct(productNode: BCProduct): Product { ...rest, } } + +export function normalizeCart({ data, ...rest }: any) { + return { + ...rest, + data: { + products: data?.line_items?.physical_items ?? [], + ...data, + }, + } +} diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index 8aefc3e68..f280923a6 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -9,7 +9,7 @@ export type CartResponse<Result> = responseInterface<Result, Error> & { } export type CartInput = { - cartId: string | undefined + cartId: Cart['id'] } export default function useCart<Result>( diff --git a/framework/types.d.ts b/framework/types.d.ts index e020ba893..7d79b6c80 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -42,7 +42,10 @@ interface ProductPrice { } interface Cart extends Entity { - id: string + id: string | undefined + currency: { code: string } + taxIncluded?: boolean + totalAmmount: number | string products: Pick<Product, 'id' | 'name' | 'prices'>[] } diff --git a/pages/cart.tsx b/pages/cart.tsx index fc06c4ced..a9e24edf2 100644 --- a/pages/cart.tsx +++ b/pages/cart.tsx @@ -22,23 +22,34 @@ export async function getStaticProps({ export default function Cart() { const { data, isEmpty } = useCart() - const { price: subTotal } = usePrice( - data && { - amount: data.base_amount, - currencyCode: data.currency.code, - } - ) - const { price: total } = usePrice( - data && { - amount: data.cart_amount, - currencyCode: data.currency.code, - } - ) + const loading = !data - const items = data?.line_items.physical_items ?? [] + if (loading) { + // Load skeleton + return <div>Loading</div> + } - const error = null - const success = null + console.log('Cart Data', data) + + // const { price: subTotal } = usePrice( + // data && { + // amount: data.base_amount, + // currencyCode: data.currency.code, + // } + // ) + // const { price: total } = usePrice( + // data && { + // amount: data.cart_amount, + // currencyCode: data.currency.code, + // } + // ) + + // const items = data?.line_items.physical_items ?? [] + + // const error = null + // const success = null + + return <div>hola</div> return ( <div className="grid lg:grid-cols-12"> From 7f70cfd868c6224e293061e8ba4158d9bd92fe28 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Thu, 14 Jan 2021 12:58:41 -0300 Subject: [PATCH 032/221] Changes --- components/cart/CartItem/CartItem.tsx | 15 ++++--- .../cart/CartSidebarView/CartSidebarView.tsx | 8 ++-- .../product/ProductView/ProductView.tsx | 2 +- .../api/catalog/handlers/get-products.ts | 12 +++-- framework/bigcommerce/api/catalog/products.ts | 4 +- .../api/operations/get-all-products.ts | 2 +- .../bigcommerce/api/operations/get-product.ts | 2 +- framework/bigcommerce/lib/normalize.ts | 44 +++++++++++++++++-- framework/types.d.ts | 19 ++++++-- package.json | 1 + pages/cart.tsx | 26 +++++------ pages/index2.tsx | 7 --- pages/search.tsx | 42 ++++++++++-------- tsconfig.json | 2 +- yarn.lock | 5 +++ 15 files changed, 124 insertions(+), 67 deletions(-) delete mode 100644 pages/index2.tsx diff --git a/components/cart/CartItem/CartItem.tsx b/components/cart/CartItem/CartItem.tsx index ac78d1849..2337c871c 100644 --- a/components/cart/CartItem/CartItem.tsx +++ b/components/cart/CartItem/CartItem.tsx @@ -12,7 +12,7 @@ const CartItem = ({ item, currencyCode, }: { - item: any + item: CartItem currencyCode: string }) => { const { price } = usePrice({ @@ -55,7 +55,7 @@ const CartItem = ({ try { // If this action succeeds then there's no need to do `setRemoving(true)` // because the component will be removed from the view - await removeItem({ id: item.id }) + await removeItem({ id: String(item.id) }) } catch (error) { setRemoving(false) } @@ -77,16 +77,14 @@ const CartItem = ({ <div className="w-16 h-16 bg-violet relative overflow-hidden"> <Image className={s.productImage} - src={item.image_url} width={150} height={150} - alt="Product Image" - // The cart item image is already optimized and very small in size + src={item.images[0].url} + alt={item.images[0].alt} unoptimized /> </div> <div className="flex-1 flex flex-col text-base"> - {/** TODO: Replace this. No `path` found at Cart */} <Link href={`/product/${item.url.split('/')[3]}`}> <span className="font-bold mb-5 text-lg cursor-pointer"> {item.name} @@ -115,7 +113,10 @@ const CartItem = ({ </div> <div className="flex flex-col justify-between space-y-2 text-base"> <span>{price}</span> - <button className="flex justify-end" onClick={handleRemove}> + <button + className="flex justify-end outline-none" + onClick={handleRemove} + > <Trash /> </button> </div> diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index 466ef15b6..19c6964d7 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -16,19 +16,17 @@ const CartSidebarView: FC = () => { const { price: subTotal } = usePrice( data && { amount: data.base_amount, - currencyCode: data.currency.code, + currencyCode: data.currency?.code || 'USD', } ) const { price: total } = usePrice( data && { amount: data.cart_amount, - currencyCode: data.currency.code, + currencyCode: data.currency?.code || 'USD', } ) const handleClose = () => closeSidebar() - const items = data?.line_items.physical_items ?? [] - const error = null const success = null @@ -95,7 +93,7 @@ const CartSidebarView: FC = () => { My Cart </h2> <ul className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accents-3 border-t border-accents-3"> - {items.map((item: any) => ( + {data.products.map((item: any) => ( <CartItem key={item.id} item={item} diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index 7151208ac..d55e52611 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -146,7 +146,7 @@ const ProductView: FC<Props> = ({ product }) => { className={s.button} onClick={addToCart} loading={loading} - disabled={!variant} + disabled={!variant && product.options.length > 0} > Add to Cart </Button> diff --git a/framework/bigcommerce/api/catalog/handlers/get-products.ts b/framework/bigcommerce/api/catalog/handlers/get-products.ts index b05548e40..f99d82729 100644 --- a/framework/bigcommerce/api/catalog/handlers/get-products.ts +++ b/framework/bigcommerce/api/catalog/handlers/get-products.ts @@ -1,3 +1,4 @@ +import { Product } from 'framework/types' import getAllProducts, { ProductEdge } from '../../operations/get-all-products' import type { ProductsHandlers } from '../products' @@ -6,6 +7,7 @@ const SORT: { [key: string]: string | undefined } = { trending: 'total_sold', price: 'price', } + const LIMIT = 12 // Return current cart info @@ -44,21 +46,25 @@ const getProducts: ProductsHandlers['getProducts'] = async ({ const { data } = await config.storeApiFetch<{ data: { id: number }[] }>( url.pathname + url.search ) + const entityIds = data.map((p) => p.id) const found = entityIds.length > 0 + // We want the GraphQL version of each product const graphqlData = await getAllProducts({ variables: { first: LIMIT, entityIds }, config, }) + // Put the products in an object that we can use to get them by id const productsById = graphqlData.products.reduce<{ - [k: number]: ProductEdge + [k: number]: Product }>((prods, p) => { - prods[p.node.entityId] = p + prods[p.id] = p return prods }, {}) - const products: ProductEdge[] = found ? [] : graphqlData.products + + const products: Product[] = found ? [] : graphqlData.products // Populate the products array with the graphql products, in the order // assigned by the list of entity ids diff --git a/framework/bigcommerce/api/catalog/products.ts b/framework/bigcommerce/api/catalog/products.ts index 7456fc3ac..fe5b807c6 100644 --- a/framework/bigcommerce/api/catalog/products.ts +++ b/framework/bigcommerce/api/catalog/products.ts @@ -4,11 +4,11 @@ import createApiHandler, { BigcommerceHandler, } from '../utils/create-api-handler' import { BigcommerceApiError } from '../utils/errors' -import type { ProductEdge } from '../operations/get-all-products' import getProducts from './handlers/get-products' +import { Product } from 'framework/types' export type SearchProductsData = { - products: ProductEdge[] + products: Product[] found: boolean } diff --git a/framework/bigcommerce/api/operations/get-all-products.ts b/framework/bigcommerce/api/operations/get-all-products.ts index 60aa197cc..3cfb77b7c 100644 --- a/framework/bigcommerce/api/operations/get-all-products.ts +++ b/framework/bigcommerce/api/operations/get-all-products.ts @@ -127,7 +127,7 @@ async function getAllProducts({ }) } - return { products: products.map(({ node }) => normalizeProduct(node)) } + return { products: products.map(({ node }) => normalizeProduct(node as any)) } } export default getAllProducts diff --git a/framework/bigcommerce/api/operations/get-product.ts b/framework/bigcommerce/api/operations/get-product.ts index aa480ac5e..4d34d86a0 100644 --- a/framework/bigcommerce/api/operations/get-product.ts +++ b/framework/bigcommerce/api/operations/get-product.ts @@ -111,7 +111,7 @@ async function getProduct({ setProductLocaleMeta(product) } - return { product: normalizeProduct(product) } + return { product: normalizeProduct(product as any) } } return {} diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index 686581b9f..950177e73 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -1,4 +1,5 @@ -import { Product as BCProduct } from '@framework/schema' +import { Cart, CartItem, Product } from '../../types' +import { Product as BigCommerceProduct } from '@framework/schema' function normalizeProductOption({ node: { @@ -14,7 +15,7 @@ function normalizeProductOption({ } } -export function normalizeProduct(productNode: BCProduct): Product { +export function normalizeProduct(productNode: BigCommerceProduct): Product { const { entityId: id, images, @@ -67,12 +68,47 @@ export function normalizeProduct(productNode: BCProduct): Product { } } -export function normalizeCart({ data, ...rest }: any) { +export function normalizeCart({ data, ...rest }: any): Cart { return { ...rest, data: { - products: data?.line_items?.physical_items ?? [], + products: data?.line_items?.physical_items.map(itemsToProducts) ?? [], ...data, }, } } + +function itemsToProducts({ + id, + name, + quantity, + product_id, + variant_id, + image_url, + list_price, + sale_price, + extended_list_price, + extended_sale_price, + ...rest +}: any): CartItem { + return { + id, + name, + prices: { + listPrice: list_price, + salePrice: sale_price, + extendedListPrice: extended_list_price, + extendedSalePrice: extended_sale_price, + }, + images: [ + { + alt: name, + url: image_url, + }, + ], + productId: product_id, + variantId: variant_id, + quantity, + ...rest, + } +} diff --git a/framework/types.d.ts b/framework/types.d.ts index 7d79b6c80..7a4c45359 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -1,3 +1,5 @@ +import { CartItem } from '@components/cart' + interface Entity { id: string | number [prop: string]: any @@ -12,6 +14,7 @@ interface Product extends Entity { variants: ProductVariant[] price: ProductPrice options: ProductOption[] + sku?: string } interface ProductOption extends Entity { @@ -37,8 +40,11 @@ interface ProductVariant { interface ProductPrice { value: number currencyCode: 'USD' | 'ARS' | string | undefined - retailValue?: number - saleValue?: number + retailPrice?: number + salePrice?: number + listPrice?: number + extendedSalePrice?: number + extendedListPrice?: number } interface Cart extends Entity { @@ -46,7 +52,14 @@ interface Cart extends Entity { currency: { code: string } taxIncluded?: boolean totalAmmount: number | string - products: Pick<Product, 'id' | 'name' | 'prices'>[] + products: Pick<Product, 'id' | 'name' | 'prices'> & CartItem[] +} + +interface CartItem extends Entity { + quantity: number + productId: Product['id'] + variantId: ProductVariant['id'] + images: ProductImage[] } interface Wishlist extends Entity { diff --git a/package.json b/package.json index a30af5b97..edabd234f 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "next": "^10.0.5", "next-seo": "^4.11.0", "next-themes": "^0.0.4", + "normalizr": "^3.6.1", "postcss-nesting": "^7.0.1", "react": "^16.14.0", "react-dom": "^16.14.0", diff --git a/pages/cart.tsx b/pages/cart.tsx index a9e24edf2..57b92db27 100644 --- a/pages/cart.tsx +++ b/pages/cart.tsx @@ -29,20 +29,18 @@ export default function Cart() { return <div>Loading</div> } - console.log('Cart Data', data) - - // const { price: subTotal } = usePrice( - // data && { - // amount: data.base_amount, - // currencyCode: data.currency.code, - // } - // ) - // const { price: total } = usePrice( - // data && { - // amount: data.cart_amount, - // currencyCode: data.currency.code, - // } - // ) + const { price: subTotal } = usePrice( + data && { + amount: data.base_amount, + currencyCode: data.currency.code, + } + ) + const { price: total } = usePrice( + data && { + amount: data.cart_amount, + currencyCode: data.currency.code, + } + ) // const items = data?.line_items.physical_items ?? [] diff --git a/pages/index2.tsx b/pages/index2.tsx deleted file mode 100644 index 9b94444e1..000000000 --- a/pages/index2.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { Layout } from '@components/common' - -export default function Home() { - return <>Hello</> -} - -Home.Layout = Layout diff --git a/pages/search.tsx b/pages/search.tsx index 18b0d9c1a..d9367239f 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -3,16 +3,29 @@ import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next' import Link from 'next/link' import { useState } from 'react' import { useRouter } from 'next/router' -import { getConfig } from '@framework/api' -import getAllPages from '@framework/api/operations/get-all-pages' -import getSiteInfo from '@framework/api/operations/get-site-info' -import useSearch from '@framework/product/use-search' + import { Layout } from '@components/common' import { ProductCard } from '@components/product' import { Container, Grid, Skeleton } from '@components/ui' +import { getConfig } from '@framework/api' +import getAllPages from '@framework/api/operations/get-all-pages' +import getSiteInfo from '@framework/api/operations/get-site-info' +import useSearch from '@framework/product/use-search' + import rangeMap from '@lib/range-map' + +// TODO(bc) Remove this. This should come from the API import getSlug from '@lib/get-slug' + +// TODO (bc) : Remove or standarize this. +const SORT = Object.entries({ + 'latest-desc': 'Latest arrivals', + 'trending-desc': 'Trending', + 'price-asc': 'Price: Low to high', + 'price-desc': 'Price: High to low', +}) + import { filterQuery, getCategoryPath, @@ -27,19 +40,11 @@ export async function getStaticProps({ const config = getConfig({ locale }) const { pages } = await getAllPages({ config, preview }) const { categories, brands } = await getSiteInfo({ config, preview }) - return { props: { pages, categories, brands }, } } -const SORT = Object.entries({ - 'latest-desc': 'Latest arrivals', - 'trending-desc': 'Trending', - 'price-asc': 'Price: Low to high', - 'price-desc': 'Price: High to low', -}) - export default function Search({ categories, brands, @@ -76,7 +81,6 @@ export default function Search({ } else { setToggleFilter(!toggleFilter) } - setActiveFilter(filter) } @@ -333,14 +337,16 @@ export default function Search({ {data ? ( <Grid layout="normal"> - {data.products.map(({ node }) => ( + {data.products.map((product) => ( <ProductCard variant="simple" - key={node.path} + key={product.path} className="animated fadeIn" - product={node} - imgWidth={480} - imgHeight={480} + product={product} + imgProps={{ + width: 480, + height: 480, + }} /> ))} </Grid> diff --git a/tsconfig.json b/tsconfig.json index c45e16c4d..d80fe8ba8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,7 +28,7 @@ }, "include": [ "next-env.d.ts", - "./framework/types.d.ts", + "framework/types.d.ts", "**/*.ts", "**/*.tsx", "**/*.js" diff --git a/yarn.lock b/yarn.lock index 769c2e077..4db1aba12 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5195,6 +5195,11 @@ normalize.css@^8.0.1: resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3" integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg== +normalizr@^3.6.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/normalizr/-/normalizr-3.6.1.tgz#d367ab840e031ff382141b8d81ce279292ff69fe" + integrity sha512-8iEmqXmPtll8PwbEFrbPoDxVw7MKnNvt3PZzR2Xvq9nggEEOgBlNICPXYzyZ4w4AkHUzCU998mdatER3n2VaMA== + npmlog@^4.0.1, npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" From de0ba8cee8108794521a58c3579fc56b7a7663d3 Mon Sep 17 00:00:00 2001 From: Belen Curcio <curciobelen@gmail.com> Date: Sun, 17 Jan 2021 12:53:34 -0300 Subject: [PATCH 033/221] changes breaking --- components/cart/CartItem/CartItem.tsx | 9 +++++---- components/common/Footer/Footer.tsx | 2 +- components/common/Layout/Layout.tsx | 2 +- .../api/catalog/handlers/get-products.ts | 2 +- .../bigcommerce/api/customers/handlers/login.ts | 2 +- .../bigcommerce/api/customers/handlers/signup.ts | 2 +- .../api/utils/set-product-locale-meta.ts | 2 +- .../bigcommerce/api/wishlist/handlers/add-item.ts | 4 ++-- .../api/wishlist/handlers/get-wishlist.ts | 4 ++-- .../api/wishlist/handlers/remove-item.ts | 4 ++-- framework/bigcommerce/api/wishlist/index.ts | 2 +- .../bigcommerce/{api/operations => auth}/login.ts | 8 ++++---- .../{api/operations => common}/get-all-pages.ts | 6 +++--- .../{api/operations => common}/get-page.ts | 6 +++--- .../{api/operations => common}/get-site-info.ts | 10 +++++----- .../operations => customer}/get-customer-id.ts | 4 ++-- .../get-customer-wishlist.ts | 8 ++++---- .../get-all-product-paths.ts | 8 ++++---- .../operations => product}/get-all-products.ts | 14 +++++++------- .../{api/operations => product}/get-product.ts | 8 ++++---- framework/bigcommerce/product/index.ts | 4 ++-- package.json | 5 +++-- pages/[...pages].tsx | 4 ++-- pages/blog.tsx | 2 +- pages/cart.tsx | 2 +- pages/index.tsx | 6 +++--- pages/orders.tsx | 2 +- pages/product/[slug].tsx | 6 +++--- pages/profile.tsx | 2 +- pages/search.tsx | 4 ++-- pages/wishlist.tsx | 2 +- tsconfig.json | 2 +- yarn.lock | 15 ++++++++++++++- 33 files changed, 89 insertions(+), 74 deletions(-) rename framework/bigcommerce/{api/operations => auth}/login.ts (88%) rename framework/bigcommerce/{api/operations => common}/get-all-pages.ts (83%) rename framework/bigcommerce/{api/operations => common}/get-page.ts (85%) rename framework/bigcommerce/{api/operations => common}/get-site-info.ts (89%) rename framework/bigcommerce/{api/operations => customer}/get-customer-id.ts (84%) rename framework/bigcommerce/{api/operations => customer}/get-customer-wishlist.ts (89%) rename framework/bigcommerce/{api/operations => product}/get-all-product-paths.ts (89%) rename framework/bigcommerce/{api/operations => product}/get-all-products.ts (88%) rename framework/bigcommerce/{api/operations => product}/get-product.ts (93%) diff --git a/components/cart/CartItem/CartItem.tsx b/components/cart/CartItem/CartItem.tsx index 2337c871c..c5a48c8e6 100644 --- a/components/cart/CartItem/CartItem.tsx +++ b/components/cart/CartItem/CartItem.tsx @@ -2,13 +2,14 @@ import { ChangeEvent, useEffect, useState } from 'react' import cn from 'classnames' import Image from 'next/image' import Link from 'next/link' +import s from './CartItem.module.css' import { Trash, Plus, Minus } from '@components/icons' import usePrice from '@framework/product/use-price' import useUpdateItem from '@framework/cart/use-update-item' import useRemoveItem from '@framework/cart/use-remove-item' -import s from './CartItem.module.css' +import { CartItem } from 'framework/types' -const CartItem = ({ +const Item = ({ item, currencyCode, }: { @@ -31,7 +32,7 @@ const CartItem = ({ const val = Number(e.target.value) if (Number.isInteger(val) && val >= 0) { - setQuantity(e.target.value) + setQuantity(Number(e.target.value)) } } const handleBlur = () => { @@ -124,4 +125,4 @@ const CartItem = ({ ) } -export default CartItem +export default Item diff --git a/components/common/Footer/Footer.tsx b/components/common/Footer/Footer.tsx index c90b4886f..75b2806ef 100644 --- a/components/common/Footer/Footer.tsx +++ b/components/common/Footer/Footer.tsx @@ -2,7 +2,7 @@ import { FC } from 'react' import cn from 'classnames' import Link from 'next/link' import { useRouter } from 'next/router' -import type { Page } from '@framework/api/operations/get-all-pages' +import type { Page } from '@framework/common/get-all-pages' import getSlug from '@lib/get-slug' import { Github, Vercel } from '@components/icons' import { Logo, Container } from '@components/ui' diff --git a/components/common/Layout/Layout.tsx b/components/common/Layout/Layout.tsx index f4dac69ac..0490dbb19 100644 --- a/components/common/Layout/Layout.tsx +++ b/components/common/Layout/Layout.tsx @@ -11,7 +11,7 @@ import CartSidebarView from '@components/cart/CartSidebarView' import LoginView from '@components/auth/LoginView' import { CommerceProvider } from '@framework' -import type { Page } from '@framework/api/operations/get-all-pages' +import type { Page } from '@framework/common/get-all-pages' const Loading = () => ( <div className="w-80 h-80 flex items-center text-center justify-center p-3"> diff --git a/framework/bigcommerce/api/catalog/handlers/get-products.ts b/framework/bigcommerce/api/catalog/handlers/get-products.ts index f99d82729..894dc5cf3 100644 --- a/framework/bigcommerce/api/catalog/handlers/get-products.ts +++ b/framework/bigcommerce/api/catalog/handlers/get-products.ts @@ -1,5 +1,5 @@ import { Product } from 'framework/types' -import getAllProducts, { ProductEdge } from '../../operations/get-all-products' +import getAllProducts, { ProductEdge } from '../../../product/get-all-products' import type { ProductsHandlers } from '../products' const SORT: { [key: string]: string | undefined } = { diff --git a/framework/bigcommerce/api/customers/handlers/login.ts b/framework/bigcommerce/api/customers/handlers/login.ts index c4062f6cc..9e019f3a0 100644 --- a/framework/bigcommerce/api/customers/handlers/login.ts +++ b/framework/bigcommerce/api/customers/handlers/login.ts @@ -1,5 +1,5 @@ import { FetcherError } from '@commerce/utils/errors' -import login from '../../operations/login' +import login from '../../../auth/login' import type { LoginHandlers } from '../login' const invalidCredentials = /invalid credentials/i diff --git a/framework/bigcommerce/api/customers/handlers/signup.ts b/framework/bigcommerce/api/customers/handlers/signup.ts index ff45adadb..1b24db0cc 100644 --- a/framework/bigcommerce/api/customers/handlers/signup.ts +++ b/framework/bigcommerce/api/customers/handlers/signup.ts @@ -1,5 +1,5 @@ import { BigcommerceApiError } from '../../utils/errors' -import login from '../../operations/login' +import login from '../../../auth/login' import { SignupHandlers } from '../signup' const signup: SignupHandlers['signup'] = async ({ diff --git a/framework/bigcommerce/api/utils/set-product-locale-meta.ts b/framework/bigcommerce/api/utils/set-product-locale-meta.ts index 767286477..974a197bd 100644 --- a/framework/bigcommerce/api/utils/set-product-locale-meta.ts +++ b/framework/bigcommerce/api/utils/set-product-locale-meta.ts @@ -1,4 +1,4 @@ -import type { ProductNode } from '../operations/get-all-products' +import type { ProductNode } from '../../product/get-all-products' import type { RecursivePartial } from './types' export default function setProductLocaleMeta( diff --git a/framework/bigcommerce/api/wishlist/handlers/add-item.ts b/framework/bigcommerce/api/wishlist/handlers/add-item.ts index a02ef4434..00d7b06bd 100644 --- a/framework/bigcommerce/api/wishlist/handlers/add-item.ts +++ b/framework/bigcommerce/api/wishlist/handlers/add-item.ts @@ -1,6 +1,6 @@ import type { WishlistHandlers } from '..' -import getCustomerId from '../../operations/get-customer-id' -import getCustomerWishlist from '../../operations/get-customer-wishlist' +import getCustomerId from '../../../customer/get-customer-id' +import getCustomerWishlist from '../../../customer/get-customer-wishlist' import { parseWishlistItem } from '../../utils/parse-item' // Returns the wishlist of the signed customer diff --git a/framework/bigcommerce/api/wishlist/handlers/get-wishlist.ts b/framework/bigcommerce/api/wishlist/handlers/get-wishlist.ts index 3eb3000cc..3737c033a 100644 --- a/framework/bigcommerce/api/wishlist/handlers/get-wishlist.ts +++ b/framework/bigcommerce/api/wishlist/handlers/get-wishlist.ts @@ -1,5 +1,5 @@ -import getCustomerId from '../../operations/get-customer-id' -import getCustomerWishlist from '../../operations/get-customer-wishlist' +import getCustomerId from '../../../customer/get-customer-id' +import getCustomerWishlist from '../../../customer/get-customer-wishlist' import type { Wishlist, WishlistHandlers } from '..' // Return wishlist info diff --git a/framework/bigcommerce/api/wishlist/handlers/remove-item.ts b/framework/bigcommerce/api/wishlist/handlers/remove-item.ts index 29b6eff60..a9cfd9db5 100644 --- a/framework/bigcommerce/api/wishlist/handlers/remove-item.ts +++ b/framework/bigcommerce/api/wishlist/handlers/remove-item.ts @@ -1,7 +1,7 @@ -import getCustomerId from '../../operations/get-customer-id' +import getCustomerId from '../../../customer/get-customer-id' import getCustomerWishlist, { Wishlist, -} from '../../operations/get-customer-wishlist' +} from '../../../customer/get-customer-wishlist' import type { WishlistHandlers } from '..' // Return current wishlist info diff --git a/framework/bigcommerce/api/wishlist/index.ts b/framework/bigcommerce/api/wishlist/index.ts index b50c5e97f..e892d2e78 100644 --- a/framework/bigcommerce/api/wishlist/index.ts +++ b/framework/bigcommerce/api/wishlist/index.ts @@ -7,7 +7,7 @@ import { BigcommerceApiError } from '../utils/errors' import type { Wishlist, WishlistItem, -} from '../operations/get-customer-wishlist' +} from '../../customer/get-customer-wishlist' import getWishlist from './handlers/get-wishlist' import addItem from './handlers/add-item' import removeItem from './handlers/remove-item' diff --git a/framework/bigcommerce/api/operations/login.ts b/framework/bigcommerce/auth/login.ts similarity index 88% rename from framework/bigcommerce/api/operations/login.ts rename to framework/bigcommerce/auth/login.ts index 8a8502705..adfa2d467 100644 --- a/framework/bigcommerce/api/operations/login.ts +++ b/framework/bigcommerce/auth/login.ts @@ -1,8 +1,8 @@ import type { ServerResponse } from 'http' -import type { LoginMutation, LoginMutationVariables } from '../../schema' -import type { RecursivePartial } from '../utils/types' -import concatHeader from '../utils/concat-cookie' -import { BigcommerceConfig, getConfig } from '..' +import type { LoginMutation, LoginMutationVariables } from '../schema' +import type { RecursivePartial } from '../api/utils/types' +import concatHeader from '../api/utils/concat-cookie' +import { BigcommerceConfig, getConfig } from '../api' export const loginMutation = /* GraphQL */ ` mutation login($email: String!, $password: String!) { diff --git a/framework/bigcommerce/api/operations/get-all-pages.ts b/framework/bigcommerce/common/get-all-pages.ts similarity index 83% rename from framework/bigcommerce/api/operations/get-all-pages.ts rename to framework/bigcommerce/common/get-all-pages.ts index 9fe83f2a1..dc5eb15a5 100644 --- a/framework/bigcommerce/api/operations/get-all-pages.ts +++ b/framework/bigcommerce/common/get-all-pages.ts @@ -1,6 +1,6 @@ -import type { RecursivePartial, RecursiveRequired } from '../utils/types' -import { BigcommerceConfig, getConfig } from '..' -import { definitions } from '../definitions/store-content' +import type { RecursivePartial, RecursiveRequired } from '../api/utils/types' +import { BigcommerceConfig, getConfig } from '../api' +import { definitions } from '../api/definitions/store-content' export type Page = definitions['page_Full'] diff --git a/framework/bigcommerce/api/operations/get-page.ts b/framework/bigcommerce/common/get-page.ts similarity index 85% rename from framework/bigcommerce/api/operations/get-page.ts rename to framework/bigcommerce/common/get-page.ts index 3010dd34c..b65e340e1 100644 --- a/framework/bigcommerce/api/operations/get-page.ts +++ b/framework/bigcommerce/common/get-page.ts @@ -1,6 +1,6 @@ -import type { RecursivePartial, RecursiveRequired } from '../utils/types' -import { BigcommerceConfig, getConfig } from '..' -import { definitions } from '../definitions/store-content' +import type { RecursivePartial, RecursiveRequired } from '../api/utils/types' +import { BigcommerceConfig, getConfig } from '../api' +import { definitions } from '../api/definitions/store-content' export type Page = definitions['page_Full'] diff --git a/framework/bigcommerce/api/operations/get-site-info.ts b/framework/bigcommerce/common/get-site-info.ts similarity index 89% rename from framework/bigcommerce/api/operations/get-site-info.ts rename to framework/bigcommerce/common/get-site-info.ts index 44c0cfeb5..80cde8d82 100644 --- a/framework/bigcommerce/api/operations/get-site-info.ts +++ b/framework/bigcommerce/common/get-site-info.ts @@ -1,8 +1,8 @@ -import type { GetSiteInfoQuery, GetSiteInfoQueryVariables } from '../../schema' -import type { RecursivePartial, RecursiveRequired } from '../utils/types' -import filterEdges from '../utils/filter-edges' -import { BigcommerceConfig, getConfig } from '..' -import { categoryTreeItemFragment } from '../fragments/category-tree' +import type { GetSiteInfoQuery, GetSiteInfoQueryVariables } from '../schema' +import type { RecursivePartial, RecursiveRequired } from '../api/utils/types' +import filterEdges from '../api/utils/filter-edges' +import { BigcommerceConfig, getConfig } from '../api' +import { categoryTreeItemFragment } from '../api/fragments/category-tree' // Get 3 levels of categories export const getSiteInfoQuery = /* GraphQL */ ` diff --git a/framework/bigcommerce/api/operations/get-customer-id.ts b/framework/bigcommerce/customer/get-customer-id.ts similarity index 84% rename from framework/bigcommerce/api/operations/get-customer-id.ts rename to framework/bigcommerce/customer/get-customer-id.ts index 7fe84093e..65ce5a6a8 100644 --- a/framework/bigcommerce/api/operations/get-customer-id.ts +++ b/framework/bigcommerce/customer/get-customer-id.ts @@ -1,5 +1,5 @@ -import { GetCustomerIdQuery } from '../../schema' -import { BigcommerceConfig, getConfig } from '..' +import { GetCustomerIdQuery } from '../schema' +import { BigcommerceConfig, getConfig } from '../api' export const getCustomerIdQuery = /* GraphQL */ ` query getCustomerId { diff --git a/framework/bigcommerce/api/operations/get-customer-wishlist.ts b/framework/bigcommerce/customer/get-customer-wishlist.ts similarity index 89% rename from framework/bigcommerce/api/operations/get-customer-wishlist.ts rename to framework/bigcommerce/customer/get-customer-wishlist.ts index 2c1299b46..a3c7413cc 100644 --- a/framework/bigcommerce/api/operations/get-customer-wishlist.ts +++ b/framework/bigcommerce/customer/get-customer-wishlist.ts @@ -1,7 +1,7 @@ -import type { RecursivePartial, RecursiveRequired } from '../utils/types' -import { definitions } from '../definitions/wishlist' -import { BigcommerceConfig, getConfig } from '..' -import getAllProducts, { ProductEdge } from './get-all-products' +import type { RecursivePartial, RecursiveRequired } from '../api/utils/types' +import { definitions } from '../api/definitions/wishlist' +import { BigcommerceConfig, getConfig } from '../api' +import getAllProducts, { ProductEdge } from '../product/get-all-products' export type Wishlist = Omit<definitions['wishlist_Full'], 'items'> & { items?: WishlistItem[] diff --git a/framework/bigcommerce/api/operations/get-all-product-paths.ts b/framework/bigcommerce/product/get-all-product-paths.ts similarity index 89% rename from framework/bigcommerce/api/operations/get-all-product-paths.ts rename to framework/bigcommerce/product/get-all-product-paths.ts index 71522be35..c1b23b38d 100644 --- a/framework/bigcommerce/api/operations/get-all-product-paths.ts +++ b/framework/bigcommerce/product/get-all-product-paths.ts @@ -1,10 +1,10 @@ import type { GetAllProductPathsQuery, GetAllProductPathsQueryVariables, -} from '../../schema' -import type { RecursivePartial, RecursiveRequired } from '../utils/types' -import filterEdges from '../utils/filter-edges' -import { BigcommerceConfig, getConfig } from '..' +} from '../schema' +import type { RecursivePartial, RecursiveRequired } from '../api/utils/types' +import filterEdges from '../api/utils/filter-edges' +import { BigcommerceConfig, getConfig } from '../api' export const getAllProductPathsQuery = /* GraphQL */ ` query getAllProductPaths($first: Int = 100) { diff --git a/framework/bigcommerce/api/operations/get-all-products.ts b/framework/bigcommerce/product/get-all-products.ts similarity index 88% rename from framework/bigcommerce/api/operations/get-all-products.ts rename to framework/bigcommerce/product/get-all-products.ts index 3cfb77b7c..b7d728c4a 100644 --- a/framework/bigcommerce/api/operations/get-all-products.ts +++ b/framework/bigcommerce/product/get-all-products.ts @@ -1,13 +1,13 @@ import type { GetAllProductsQuery, GetAllProductsQueryVariables, -} from '../../schema' -import type { RecursivePartial, RecursiveRequired } from '../utils/types' -import filterEdges from '../utils/filter-edges' -import setProductLocaleMeta from '../utils/set-product-locale-meta' -import { productConnectionFragment } from '../fragments/product' -import { BigcommerceConfig, getConfig } from '..' -import { normalizeProduct } from '../../lib/normalize' +} from '../schema' +import type { RecursivePartial, RecursiveRequired } from '../api/utils/types' +import filterEdges from '../api/utils/filter-edges' +import setProductLocaleMeta from '../api/utils/set-product-locale-meta' +import { productConnectionFragment } from '../api/fragments/product' +import { BigcommerceConfig, getConfig } from '../api' +import { normalizeProduct } from '../lib/normalize' export const getAllProductsQuery = /* GraphQL */ ` query getAllProducts( diff --git a/framework/bigcommerce/api/operations/get-product.ts b/framework/bigcommerce/product/get-product.ts similarity index 93% rename from framework/bigcommerce/api/operations/get-product.ts rename to framework/bigcommerce/product/get-product.ts index 4d34d86a0..3624a9cca 100644 --- a/framework/bigcommerce/api/operations/get-product.ts +++ b/framework/bigcommerce/product/get-product.ts @@ -1,7 +1,7 @@ -import type { GetProductQuery, GetProductQueryVariables } from '../../schema' -import setProductLocaleMeta from '../utils/set-product-locale-meta' -import { productInfoFragment } from '../fragments/product' -import { BigcommerceConfig, getConfig } from '..' +import type { GetProductQuery, GetProductQueryVariables } from '../schema' +import setProductLocaleMeta from '../api/utils/set-product-locale-meta' +import { productInfoFragment } from '../api/fragments/product' +import { BigcommerceConfig, getConfig } from '../api' import { normalizeProduct } from '@framework/lib/normalize' export const getProductQuery = /* GraphQL */ ` diff --git a/framework/bigcommerce/product/index.ts b/framework/bigcommerce/product/index.ts index 83d507a2c..b290c189f 100644 --- a/framework/bigcommerce/product/index.ts +++ b/framework/bigcommerce/product/index.ts @@ -1,4 +1,4 @@ export { default as usePrice } from './use-price' export { default as useSearch } from './use-search' -export { default as getProduct } from '../api/operations/get-product' -export { default as getAllProducts } from '../api/operations/get-all-products' +export { default as getProduct } from './get-product' +export { default as getAllProducts } from './get-all-products' diff --git a/package.json b/package.json index edabd234f..aba8b4b5d 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "bowser": "^2.11.0", "classnames": "^2.2.6", "cookie": "^0.4.1", + "dot-object": "^2.1.4", "email-validator": "^2.0.4", "js-cookie": "^2.2.1", "keen-slider": "^5.2.4", @@ -100,8 +101,8 @@ "resolutions": { "webpack": "5.11.1" }, - "node": { - "net": "empty" + "engines": { + "node": ">=14" }, "license": "MIT" } diff --git a/pages/[...pages].tsx b/pages/[...pages].tsx index 6caac1720..98d7697cd 100644 --- a/pages/[...pages].tsx +++ b/pages/[...pages].tsx @@ -8,8 +8,8 @@ import { Layout } from '@components/common' import getSlug from '@lib/get-slug' import { missingLocaleInPages } from '@lib/usage-warns' import { getConfig } from '@framework/api' -import getPage from '@framework/api/operations/get-page' -import getAllPages from '@framework/api/operations/get-all-pages' +import getPage from '@framework/common/get-page' +import getAllPages from '@framework/common/get-all-pages' import { defaultPageProps } from '@lib/defaults' export async function getStaticProps({ diff --git a/pages/blog.tsx b/pages/blog.tsx index a3a020bca..eca3b2295 100644 --- a/pages/blog.tsx +++ b/pages/blog.tsx @@ -1,6 +1,6 @@ import type { GetStaticPropsContext } from 'next' import { getConfig } from '@framework/api' -import getAllPages from '@framework/api/operations/get-all-pages' +import getAllPages from '@framework/common/get-all-pages' import { Layout } from '@components/common' import { Container } from '@components/ui' diff --git a/pages/cart.tsx b/pages/cart.tsx index 57b92db27..e78beed57 100644 --- a/pages/cart.tsx +++ b/pages/cart.tsx @@ -1,6 +1,6 @@ import type { GetStaticPropsContext } from 'next' import { getConfig } from '@framework/api' -import getAllPages from '@framework/api/operations/get-all-pages' +import getAllPages from '@framework/common/get-all-pages' import useCart from '@framework/cart/use-cart' import usePrice from '@framework/product/use-price' import { Layout } from '@components/common' diff --git a/pages/index.tsx b/pages/index.tsx index d3f3f6ba5..811472674 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -5,9 +5,9 @@ import HomeAllProductsGrid from '@components/common/HomeAllProductsGrid' import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next' import { getConfig } from '@framework/api' -import getAllProducts from '@framework/api/operations/get-all-products' -import getSiteInfo from '@framework/api/operations/get-site-info' -import getAllPages from '@framework/api/operations/get-all-pages' +import getAllProducts from '@framework/product/get-all-products' +import getSiteInfo from '@framework/common/get-site-info' +import getAllPages from '@framework/common/get-all-pages' export async function getStaticProps({ preview, diff --git a/pages/orders.tsx b/pages/orders.tsx index 973a021eb..08e32c2b2 100644 --- a/pages/orders.tsx +++ b/pages/orders.tsx @@ -1,6 +1,6 @@ import type { GetStaticPropsContext } from 'next' import { getConfig } from '@framework/api' -import getAllPages from '@framework/api/operations/get-all-pages' +import getAllPages from '@framework/common/get-all-pages' import { Layout } from '@components/common' import { Container, Text } from '@components/ui' import { Bag } from '@components/icons' diff --git a/pages/product/[slug].tsx b/pages/product/[slug].tsx index 4f17dcbad..3d2971eed 100644 --- a/pages/product/[slug].tsx +++ b/pages/product/[slug].tsx @@ -8,9 +8,9 @@ import { Layout } from '@components/common' import { ProductView } from '@components/product' import { getConfig } from '@framework/api' -import getProduct from '@framework/api/operations/get-product' -import getAllPages from '@framework/api/operations/get-all-pages' -import getAllProductPaths from '@framework/api/operations/get-all-product-paths' +import getProduct from '@framework/product/get-product' +import getAllPages from '@framework/common/get-all-pages' +import getAllProductPaths from '@framework/product/get-all-product-paths' export async function getStaticProps({ params, diff --git a/pages/profile.tsx b/pages/profile.tsx index 8b8c1a3b1..ec845c879 100644 --- a/pages/profile.tsx +++ b/pages/profile.tsx @@ -1,6 +1,6 @@ import type { GetStaticPropsContext } from 'next' import { getConfig } from '@framework/api' -import getAllPages from '@framework/api/operations/get-all-pages' +import getAllPages from '@framework/common/get-all-pages' import useCustomer from '@framework/customer/use-customer' import { Layout } from '@components/common' import { Container, Text } from '@components/ui' diff --git a/pages/search.tsx b/pages/search.tsx index d9367239f..e84569db3 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -9,9 +9,9 @@ import { ProductCard } from '@components/product' import { Container, Grid, Skeleton } from '@components/ui' import { getConfig } from '@framework/api' -import getAllPages from '@framework/api/operations/get-all-pages' -import getSiteInfo from '@framework/api/operations/get-site-info' import useSearch from '@framework/product/use-search' +import getAllPages from '@framework/common/get-all-pages' +import getSiteInfo from '@framework/common/get-site-info' import rangeMap from '@lib/range-map' diff --git a/pages/wishlist.tsx b/pages/wishlist.tsx index b4410a58c..9df111dc6 100644 --- a/pages/wishlist.tsx +++ b/pages/wishlist.tsx @@ -1,6 +1,6 @@ import type { GetStaticPropsContext } from 'next' import { getConfig } from '@framework/api' -import getAllPages from '@framework/api/operations/get-all-pages' +import getAllPages from '@framework/common/get-all-pages' import useWishlist from '@framework/wishlist/use-wishlist' import { Layout } from '@components/common' import { Heart } from '@components/icons' diff --git a/tsconfig.json b/tsconfig.json index d80fe8ba8..e6201f640 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,8 +27,8 @@ } }, "include": [ + "/framework/types.d.ts", "next-env.d.ts", - "framework/types.d.ts", "**/*.ts", "**/*.tsx", "**/*.js" diff --git a/yarn.lock b/yarn.lock index 4db1aba12..441f924fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2346,6 +2346,11 @@ commander@^2.13.0, commander@^2.16.0, commander@^2.20.0, commander@^2.20.3, comm resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + commander@^5.0.0, commander@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" @@ -2982,6 +2987,14 @@ dot-case@^3.0.3: no-case "^3.0.4" tslib "^2.0.3" +dot-object@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/dot-object/-/dot-object-2.1.4.tgz#c6c54e9fca510b4d0ea4d65acf33726963843b5f" + integrity sha512-7FXnyyCLFawNYJ+NhkqyP9Wd2yzuo+7n9pGiYpkmXCTYa8Ci2U0eUNDVg5OuO5Pm6aFXI2SWN8/N/w7SJWu1WA== + dependencies: + commander "^4.0.0" + glob "^7.1.5" + dotenv@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" @@ -3618,7 +3631,7 @@ glob@^6.0.1: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.6: +glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.5, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== From 64b25ef70bfa98ad6bd2d10deab330d98cd59533 Mon Sep 17 00:00:00 2001 From: bc <bc@bcs-MacBook-Pro.fibertel.com.ar> Date: Mon, 18 Jan 2021 13:23:37 -0300 Subject: [PATCH 034/221] Adding immutable normalizer for Product --- framework/bigcommerce/lib/normalize.ts | 93 +++++++++++++------------- framework/types.d.ts | 2 +- package.json | 1 + yarn.lock | 5 ++ 4 files changed, 54 insertions(+), 47 deletions(-) diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index 950177e73..d9e0274c7 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -1,13 +1,16 @@ import { Cart, CartItem, Product } from '../../types' import { Product as BigCommerceProduct } from '@framework/schema' +import update from "immutability-helper" + +function normalizeProductOption(productOption:any) { + const { + node: { + entityId, + values: { edges }, + ...rest + }, + } = productOption; -function normalizeProductOption({ - node: { - entityId, - values: { edges }, - ...rest - }, -}: any) { return { id: entityId, values: edges?.map(({ node }: any) => node), @@ -15,57 +18,55 @@ function normalizeProductOption({ } } -export function normalizeProduct(productNode: BigCommerceProduct): Product { +export function normalizeProduct(productNode: any): Product { const { entityId: id, - images, - variants, productOptions, prices, path, id: _, options: _0, - brand, - ...rest } = productNode - return { - id, - path, - slug: path?.replace(/^\/+|\/+$/g, ''), - images: images.edges - ? images.edges.map( - ({ node: { urlOriginal, altText, ...rest } }: any) => ({ - url: urlOriginal, - alt: altText, - ...rest, - }) - ) - : [], - variants: variants.edges - ? variants.edges.map( - ({ node: { entityId, productOptions, ...rest } }: any) => ({ - id: entityId, - options: productOptions?.edges - ? productOptions.edges.map(normalizeProductOption) - : [], - ...rest, - }) - ) - : [], - options: productOptions.edges - ? productOptions?.edges.map(normalizeProductOption) - : [], + return update(productNode, { + id: { $set: String(id) }, + images: { + $apply: ({edges} : any) => edges?.map( + ({ node: { urlOriginal, altText, ...rest } }: any) => ({ + url: urlOriginal, + alt: altText, + ...rest, + }) + ) + }, + variants: { + $apply: ({edges} : any) => edges?.map( + ({ node: { entityId, productOptions, ...rest } }: any) => ({ + id: entityId, + options: productOptions?.edges + ? productOptions.edges.map(normalizeProductOption) + : [], + ...rest, + }) + ) + }, + options: { + $set: productOptions.edges ? productOptions?.edges.map(normalizeProductOption) : [] + }, brand: { - id: brand?.entityId ? brand?.entityId : null, - ...brand, + $apply:(brand : any) => brand?.entityId ? brand?.entityId : null, + }, + slug: { + $set: path?.replace(/^\/+|\/+$/g, '') }, price: { - value: prices?.price.value, - currencyCode: prices?.price.currencyCode, - }, - ...rest, - } + $set: { + value: prices?.price.value, + currencyCode: prices?.price.currencyCode, + } + }, + $unset: ['entityId'] + }) } export function normalizeCart({ data, ...rest }: any): Cart { diff --git a/framework/types.d.ts b/framework/types.d.ts index 7a4c45359..e68b6fd17 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -8,7 +8,7 @@ interface Entity { interface Product extends Entity { name: string description: string - slug: string + slug?: string path?: string images: ProductImage[] variants: ProductVariant[] diff --git a/package.json b/package.json index aba8b4b5d..d8229071d 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "cookie": "^0.4.1", "dot-object": "^2.1.4", "email-validator": "^2.0.4", + "immutability-helper": "^3.1.1", "js-cookie": "^2.2.1", "keen-slider": "^5.2.4", "lodash.debounce": "^4.0.8", diff --git a/yarn.lock b/yarn.lock index 441f924fc..bc77775f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3958,6 +3958,11 @@ ignore@^5.1.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== +immutability-helper@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/immutability-helper/-/immutability-helper-3.1.1.tgz#2b86b2286ed3b1241c9e23b7b21e0444f52f77b7" + integrity sha512-Q0QaXjPjwIju/28TsugCHNEASwoCcJSyJV3uO1sOIQGI0jKgm9f41Lvz0DZj3n46cNCyAZTsEYoY4C2bVRUzyQ== + immutable@~3.7.6: version "3.7.6" resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b" From d03df631cef305ef3400ee948a3c535721a62803 Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Mon, 18 Jan 2021 16:24:22 -0300 Subject: [PATCH 035/221] Cart Normalized --- CHANGELOG.md | 4 ++ components/cart/CartItem/CartItem.tsx | 8 ++- framework/bigcommerce/cart/use-cart.tsx | 5 +- framework/bigcommerce/lib/normalize.ts | 83 +++++++++++++------------ framework/types.d.ts | 2 - tsconfig.json | 2 +- 6 files changed, 60 insertions(+), 44 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..b2d1092e3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +## Changelog + +- Select Variants Working +- Click on cart item title, closes the sidebar \ No newline at end of file diff --git a/components/cart/CartItem/CartItem.tsx b/components/cart/CartItem/CartItem.tsx index c5a48c8e6..47389aee4 100644 --- a/components/cart/CartItem/CartItem.tsx +++ b/components/cart/CartItem/CartItem.tsx @@ -4,6 +4,7 @@ import Image from 'next/image' import Link from 'next/link' import s from './CartItem.module.css' import { Trash, Plus, Minus } from '@components/icons' +import {useUI} from '@components/ui/context' import usePrice from '@framework/product/use-price' import useUpdateItem from '@framework/cart/use-update-item' import useRemoveItem from '@framework/cart/use-remove-item' @@ -16,18 +17,23 @@ const Item = ({ item: CartItem currencyCode: string }) => { + const { closeSidebarIfPresent } = useUI() + const { price } = usePrice({ amount: item.extended_sale_price, baseAmount: item.extended_list_price, currencyCode, }) + const updateItem = useUpdateItem(item) const removeItem = useRemoveItem() const [quantity, setQuantity] = useState(item.quantity) const [removing, setRemoving] = useState(false) + const updateQuantity = async (val: number) => { await updateItem({ quantity: val }) } + const handleQuantity = (e: ChangeEvent<HTMLInputElement>) => { const val = Number(e.target.value) @@ -87,7 +93,7 @@ const Item = ({ </div> <div className="flex-1 flex flex-col text-base"> <Link href={`/product/${item.url.split('/')[3]}`}> - <span className="font-bold mb-5 text-lg cursor-pointer"> + <span className="font-bold mb-5 text-lg cursor-pointer" onClick={() => closeSidebarIfPresent()}> {item.name} </span> </Link> diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index 54582372f..a1ace483d 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -3,6 +3,7 @@ import type { SwrOptions } from '@commerce/utils/use-data' import useCommerceCart, { CartInput } from '@commerce/cart/use-cart' import type { Cart } from '../api/cart' import { normalizeCart } from '../lib/normalize' +import update from 'immutability-helper' const defaultOpts = { url: '/api/bigcommerce/cart', @@ -40,7 +41,9 @@ export function extendHook( set: (x) => x, }) - return normalizeCart(response) + return update(response, { + data: { $set: normalizeCart(response.data) } + }) } useCart.extend = extendHook diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index d9e0274c7..cd7763c6e 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -1,6 +1,12 @@ import { Cart, CartItem, Product } from '../../types' -import { Product as BigCommerceProduct } from '@framework/schema' -import update from "immutability-helper" +import update, { extend } from "immutability-helper" + +// Allows auto creation when needed +extend('$auto', function(value, object) { + return object ? + update(object, value): + update({}, value); +}); function normalizeProductOption(productOption:any) { const { @@ -69,47 +75,46 @@ export function normalizeProduct(productNode: any): Product { }) } -export function normalizeCart({ data, ...rest }: any): Cart { - return { - ...rest, - data: { - products: data?.line_items?.physical_items.map(itemsToProducts) ?? [], - ...data, +export function normalizeCart(cart: any): Cart { + return update(cart, { + $auto: { + products: { $set: cart?.line_items?.physical_items?.map(itemsToProducts)} }, - } + $unset: ['created_time', 'coupons', 'line_items'] + }) } -function itemsToProducts({ - id, - name, - quantity, - product_id, - variant_id, - image_url, - list_price, - sale_price, - extended_list_price, - extended_sale_price, - ...rest -}: any): CartItem { - return { +function itemsToProducts(item: any): CartItem { + const { id, name, - prices: { - listPrice: list_price, - salePrice: sale_price, - extendedListPrice: extended_list_price, - extendedSalePrice: extended_sale_price, - }, - images: [ - { - alt: name, - url: image_url, - }, - ], - productId: product_id, - variantId: variant_id, quantity, - ...rest, - } + product_id, + variant_id, + image_url, + list_price, + sale_price, + extended_list_price, + extended_sale_price, + ...rest + } = item; + + return update(item, { + $auto: { + prices: { + $auto: { + listPrice: { $set: list_price }, + salePrice: { $set: sale_price } , + extendedListPrice: { $set: extended_list_price }, + extendedSalePrice: { $set: extended_sale_price }, + } + }, + images: { $set: [{ + alt: name, + url: image_url + }]}, + productId: { $set: product_id }, + variantId: { $set: variant_id } + } + }) } diff --git a/framework/types.d.ts b/framework/types.d.ts index e68b6fd17..9ee622c4b 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -1,5 +1,3 @@ -import { CartItem } from '@components/cart' - interface Entity { id: string | number [prop: string]: any diff --git a/tsconfig.json b/tsconfig.json index e6201f640..856ebbff5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,7 +27,7 @@ } }, "include": [ - "/framework/types.d.ts", + "framework/types.d.ts", "next-env.d.ts", "**/*.ts", "**/*.tsx", From 49b9efa395de7a5330e963fd5363cbf4eee2802e Mon Sep 17 00:00:00 2001 From: Bel Curcio <bel@vercel.com> Date: Mon, 18 Jan 2021 20:17:10 -0300 Subject: [PATCH 036/221] changes --- components/cart/CartItem/CartItem.tsx | 8 +++++--- components/cart/CartSidebarView/CartSidebarView.tsx | 2 +- components/product/ProductCard/ProductCard.tsx | 6 +++--- components/product/ProductView/ProductView.tsx | 8 ++++---- components/wishlist/WishlistButton/index.ts | 1 - package.json | 2 +- tsconfig.json | 2 +- 7 files changed, 15 insertions(+), 14 deletions(-) delete mode 100644 components/wishlist/WishlistButton/index.ts diff --git a/components/cart/CartItem/CartItem.tsx b/components/cart/CartItem/CartItem.tsx index 47389aee4..14bd10c94 100644 --- a/components/cart/CartItem/CartItem.tsx +++ b/components/cart/CartItem/CartItem.tsx @@ -4,11 +4,10 @@ import Image from 'next/image' import Link from 'next/link' import s from './CartItem.module.css' import { Trash, Plus, Minus } from '@components/icons' -import {useUI} from '@components/ui/context' +import { useUI } from '@components/ui/context' import usePrice from '@framework/product/use-price' import useUpdateItem from '@framework/cart/use-update-item' import useRemoveItem from '@framework/cart/use-remove-item' -import { CartItem } from 'framework/types' const Item = ({ item, @@ -93,7 +92,10 @@ const Item = ({ </div> <div className="flex-1 flex flex-col text-base"> <Link href={`/product/${item.url.split('/')[3]}`}> - <span className="font-bold mb-5 text-lg cursor-pointer" onClick={() => closeSidebarIfPresent()}> + <span + className="font-bold mb-5 text-lg cursor-pointer" + onClick={() => closeSidebarIfPresent()} + > {item.name} </span> </Link> diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index 19c6964d7..ed4ce915a 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -93,7 +93,7 @@ const CartSidebarView: FC = () => { My Cart </h2> <ul className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accents-3 border-t border-accents-3"> - {data.products.map((item: any) => ( + {data?.products?.map((item) => ( <CartItem key={item.id} item={item} diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index 658476e7d..d3484a970 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -3,7 +3,7 @@ import cn from 'classnames' import Link from 'next/link' import s from './ProductCard.module.css' import Image, { ImageProps } from 'next/image' -import WishlistButton from '@components/wishlist/WishlistButton' +// import WishlistButton from '@components/wishlist/WishlistButton' interface Props { className?: string @@ -57,11 +57,11 @@ const ProductCard: FC<Props> = ({ {product.price.currencyCode} </span> </div> - <WishlistButton + {/* <WishlistButton className={s.wishlistButton} productId={product.id} variant={product.variants[0]} - /> + /> */} </div> <div className={s.imageContainer}> {product?.images && ( diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index d55e52611..d3d11ea16 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -12,7 +12,7 @@ import usePrice from '@framework/product/use-price' import { useAddItem } from '@framework/cart' import { getVariant, SelectedOptions } from '../helpers' -import WishlistButton from '@components/wishlist/WishlistButton' +// import WishlistButton from '@components/wishlist/WishlistButton' interface Props { className?: string @@ -24,7 +24,7 @@ const ProductView: FC<Props> = ({ product }) => { const addItem = useAddItem() const { price } = usePrice({ amount: product.price.value, - baseAmount: product.price.retailValue, + baseAmount: product.price.retailPrice, currencyCode: product.price.currencyCode!, }) const { openSidebar } = useUI() @@ -152,11 +152,11 @@ const ProductView: FC<Props> = ({ product }) => { </Button> </div> </div> - <WishlistButton + {/* <WishlistButton className={s.wishlistButton} productId={product.id} variant={product.variants[0]!} - /> + /> */} </div> </Container> ) diff --git a/components/wishlist/WishlistButton/index.ts b/components/wishlist/WishlistButton/index.ts deleted file mode 100644 index 66e88074b..000000000 --- a/components/wishlist/WishlistButton/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './WishlistButton' diff --git a/package.json b/package.json index d8229071d..9848a4674 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "webpack": "5.11.1" }, "engines": { - "node": ">=14" + "node": "12.x" }, "license": "MIT" } diff --git a/tsconfig.json b/tsconfig.json index 856ebbff5..0992a8cd6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -33,5 +33,5 @@ "**/*.tsx", "**/*.js" ], - "exclude": ["node_modules"] + "exclude": ["node_modules", "components/wishlist"] } From 017218c155896898d0ddcd6803c1bffccb2ae1af Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Tue, 19 Jan 2021 11:07:40 -0300 Subject: [PATCH 037/221] Progress --- components/cart/CartSidebarView/CartSidebarView.tsx | 5 +++-- framework/bigcommerce/lib/normalize.ts | 8 +++++--- framework/types.d.ts | 3 ++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index 19c6964d7..dd3b62f29 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -15,13 +15,14 @@ const CartSidebarView: FC = () => { const { price: subTotal } = usePrice( data && { - amount: data.base_amount, + amount: data.subTotal, currencyCode: data.currency?.code || 'USD', } ) + const { price: total } = usePrice( data && { - amount: data.cart_amount, + amount: data.total, currencyCode: data.currency?.code || 'USD', } ) diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index cd7763c6e..35c8d48b2 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -75,10 +75,12 @@ export function normalizeProduct(productNode: any): Product { }) } -export function normalizeCart(cart: any): Cart { - return update(cart, { +export function normalizeCart(data: any): Cart { + return update(data, { $auto: { - products: { $set: cart?.line_items?.physical_items?.map(itemsToProducts)} + products: { $set: data?.line_items?.physical_items?.map(itemsToProducts)}, + subTotal: { $set: data.base_amount }, + total: { $set: data.cart_amount } }, $unset: ['created_time', 'coupons', 'line_items'] }) diff --git a/framework/types.d.ts b/framework/types.d.ts index 9ee622c4b..31d5aedf3 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -49,8 +49,9 @@ interface Cart extends Entity { id: string | undefined currency: { code: string } taxIncluded?: boolean - totalAmmount: number | string products: Pick<Product, 'id' | 'name' | 'prices'> & CartItem[] + subTotal: number | string + total: number | string } interface CartItem extends Entity { From 48b484011dc1bf406f8239ecf53749ff9a939928 Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Tue, 19 Jan 2021 12:33:50 -0300 Subject: [PATCH 038/221] More updates --- components/cart/CartItem/CartItem.tsx | 2 ++ .../cart/CartSidebarView/CartSidebarView.tsx | 6 ++--- .../product/ProductCard/ProductCard.tsx | 2 +- framework/bigcommerce/cart/use-cart.tsx | 21 ++++++++------- framework/bigcommerce/lib/immutability.ts | 18 +++++++++++++ framework/bigcommerce/lib/normalize.ts | 17 ++++-------- framework/types.d.ts | 3 ++- pages/cart.tsx | 26 ++++++------------- pages/search.tsx | 12 ++++----- 9 files changed, 57 insertions(+), 50 deletions(-) create mode 100644 framework/bigcommerce/lib/immutability.ts diff --git a/components/cart/CartItem/CartItem.tsx b/components/cart/CartItem/CartItem.tsx index 14bd10c94..91a70af0b 100644 --- a/components/cart/CartItem/CartItem.tsx +++ b/components/cart/CartItem/CartItem.tsx @@ -12,6 +12,7 @@ import useRemoveItem from '@framework/cart/use-remove-item' const Item = ({ item, currencyCode, + ...rest }: { item: CartItem currencyCode: string @@ -79,6 +80,7 @@ const Item = ({ className={cn('flex flex-row space-x-8 py-8', { 'opacity-75 pointer-events-none': removing, })} + {...rest} > <div className="w-16 h-16 bg-violet relative overflow-hidden"> <Image diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index 01ce64850..1f54b96f9 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -15,14 +15,14 @@ const CartSidebarView: FC = () => { const { price: subTotal } = usePrice( data && { - amount: data.subTotal, + amount: Number(data.subTotal), currencyCode: data.currency?.code || 'USD', } ) const { price: total } = usePrice( data && { - amount: data.total, + amount: Number(data.total), currencyCode: data.currency?.code || 'USD', } ) @@ -94,7 +94,7 @@ const CartSidebarView: FC = () => { My Cart </h2> <ul className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accents-3 border-t border-accents-3"> - {data?.products?.map((item) => ( + {data?.items?.map((item) => ( <CartItem key={item.id} item={item} diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index d3484a970..58ba11e10 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -20,7 +20,7 @@ const ProductCard: FC<Props> = ({ ...props }) => { return ( - <Link href={`product/${product.slug}`} {...props}> + <Link href={`/product/${product.slug}`} {...props}> <a className={cn(s.root, { [s.simple]: variant === 'simple' }, className)} > diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index a1ace483d..204ef0fa1 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -1,18 +1,19 @@ + +import { normalizeCart } from '../lib/normalize' import type { HookFetcher } from '@commerce/utils/types' import type { SwrOptions } from '@commerce/utils/use-data' import useCommerceCart, { CartInput } from '@commerce/cart/use-cart' -import type { Cart } from '../api/cart' -import { normalizeCart } from '../lib/normalize' -import update from 'immutability-helper' +import type { Cart as BigCommerceCart } from '../api/cart' +import update from "@framework/lib/immutability" const defaultOpts = { url: '/api/bigcommerce/cart', method: 'GET', } -export type { Cart } +type UseCartResponse = BigCommerceCart & Cart -export const fetcher: HookFetcher<Cart | null, CartInput> = ( +export const fetcher: HookFetcher<UseCartResponse | null , CartInput> = ( options, { cartId }, fetch @@ -22,7 +23,7 @@ export const fetcher: HookFetcher<Cart | null, CartInput> = ( export function extendHook( customFetcher: typeof fetcher, - swrOptions?: SwrOptions<Cart | null, CartInput> + swrOptions?: SwrOptions<UseCartResponse | null, CartInput> ) { const useCart = () => { const response = useCommerceCart(defaultOpts, [], customFetcher, { @@ -41,9 +42,11 @@ export function extendHook( set: (x) => x, }) - return update(response, { - data: { $set: normalizeCart(response.data) } - }) + + + return response.data ? update(response, { + data: { $set: normalizeCart(response.data ) } + }) : response } useCart.extend = extendHook diff --git a/framework/bigcommerce/lib/immutability.ts b/framework/bigcommerce/lib/immutability.ts new file mode 100644 index 000000000..dc0177f8a --- /dev/null +++ b/framework/bigcommerce/lib/immutability.ts @@ -0,0 +1,18 @@ + +import update, { Context } from 'immutability-helper'; + +const c = new Context(); + +c.extend('$auto', function(value, object) { + return object ? + c.update(object, value): + c.update({}, value); +}); + +c.extend('$autoArray', function(value, object) { + return object ? + c.update(object, value): + c.update([], value); +}); + +export default c.update \ No newline at end of file diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index 35c8d48b2..83ac0a2e6 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -1,12 +1,5 @@ +import update from "@framework/lib/immutability" import { Cart, CartItem, Product } from '../../types' -import update, { extend } from "immutability-helper" - -// Allows auto creation when needed -extend('$auto', function(value, object) { - return object ? - update(object, value): - update({}, value); -}); function normalizeProductOption(productOption:any) { const { @@ -78,11 +71,11 @@ export function normalizeProduct(productNode: any): Product { export function normalizeCart(data: any): Cart { return update(data, { $auto: { - products: { $set: data?.line_items?.physical_items?.map(itemsToProducts)}, - subTotal: { $set: data.base_amount }, - total: { $set: data.cart_amount } + items: { $set: data?.line_items?.physical_items?.map(itemsToProducts)}, + subTotal: { $set: data?.base_amount }, + total: { $set: data?.cart_amount } }, - $unset: ['created_time', 'coupons', 'line_items'] + $unset: ['created_time', 'coupons', 'line_items', 'email'] }) } diff --git a/framework/types.d.ts b/framework/types.d.ts index 31d5aedf3..7943cfaef 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -49,9 +49,10 @@ interface Cart extends Entity { id: string | undefined currency: { code: string } taxIncluded?: boolean - products: Pick<Product, 'id' | 'name' | 'prices'> & CartItem[] + items: Pick<Product, 'id' | 'name' | 'prices'> & CartItem[] subTotal: number | string total: number | string + customerId: Customer['id'] } interface CartItem extends Entity { diff --git a/pages/cart.tsx b/pages/cart.tsx index e78beed57..2021291a1 100644 --- a/pages/cart.tsx +++ b/pages/cart.tsx @@ -8,6 +8,7 @@ import { Button } from '@components/ui' import { Bag, Cross, Check } from '@components/icons' import { CartItem } from '@components/cart' import { Text } from '@components/ui' +import products from '@framework/api/catalog/products' export async function getStaticProps({ preview, @@ -21,34 +22,23 @@ export async function getStaticProps({ } export default function Cart() { + const error = null + const success = null const { data, isEmpty } = useCart() - const loading = !data - - if (loading) { - // Load skeleton - return <div>Loading</div> - } const { price: subTotal } = usePrice( data && { - amount: data.base_amount, - currencyCode: data.currency.code, + amount: Number(data.subTotal), + currencyCode: data.currency.code || 'USD', } ) const { price: total } = usePrice( data && { - amount: data.cart_amount, - currencyCode: data.currency.code, + amount: Number(data.total), + currencyCode: data.currency?.code || 'USD', } ) - // const items = data?.line_items.physical_items ?? [] - - // const error = null - // const success = null - - return <div>hola</div> - return ( <div className="grid lg:grid-cols-12"> <div className="lg:col-span-8"> @@ -88,7 +78,7 @@ export default function Cart() { <Text variant="pageHeading">My Cart</Text> <Text variant="sectionHeading">Review your Order</Text> <ul className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accents-2 border-b border-accents-2"> - {items.map((item) => ( + {data?.items.map((item) => ( <CartItem key={item.id} item={item} diff --git a/pages/search.tsx b/pages/search.tsx index e84569db3..97bee34d4 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -110,9 +110,9 @@ export default function Search({ fill="currentColor" > <path - fill-rule="evenodd" + fillRule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" - clip-rule="evenodd" + clipRule="evenodd" /> </svg> </button> @@ -209,9 +209,9 @@ export default function Search({ fill="currentColor" > <path - fill-rule="evenodd" + fillRule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" - clip-rule="evenodd" + clipRule="evenodd" /> </svg> </button> @@ -384,9 +384,9 @@ export default function Search({ fill="currentColor" > <path - fill-rule="evenodd" + fillRule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" - clip-rule="evenodd" + clipRule="evenodd" /> </svg> </button> From 4a268e738d0dc818c4d4174fbb360510b908c806 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Tue, 19 Jan 2021 17:36:09 -0500 Subject: [PATCH 039/221] Removed some comments --- framework/bigcommerce/api/cart/handlers/add-item.ts | 1 - framework/bigcommerce/api/cart/handlers/remove-item.ts | 1 - framework/bigcommerce/api/cart/handlers/update-item.ts | 1 - framework/commerce/api/index.ts | 2 -- 4 files changed, 5 deletions(-) diff --git a/framework/bigcommerce/api/cart/handlers/add-item.ts b/framework/bigcommerce/api/cart/handlers/add-item.ts index 25ae3880e..f58a6c43c 100644 --- a/framework/bigcommerce/api/cart/handlers/add-item.ts +++ b/framework/bigcommerce/api/cart/handlers/add-item.ts @@ -2,7 +2,6 @@ import { parseCartItem } from '../../utils/parse-item' import getCartCookie from '../../utils/get-cart-cookie' import type { CartHandlers } from '..' -// Return current cart info const addItem: CartHandlers['addItem'] = async ({ res, body: { cartId, item }, diff --git a/framework/bigcommerce/api/cart/handlers/remove-item.ts b/framework/bigcommerce/api/cart/handlers/remove-item.ts index 2ee5dbe16..c980cbb42 100644 --- a/framework/bigcommerce/api/cart/handlers/remove-item.ts +++ b/framework/bigcommerce/api/cart/handlers/remove-item.ts @@ -1,7 +1,6 @@ import getCartCookie from '../../utils/get-cart-cookie' import type { CartHandlers } from '..' -// Return current cart info const removeItem: CartHandlers['removeItem'] = async ({ res, body: { cartId, itemId }, diff --git a/framework/bigcommerce/api/cart/handlers/update-item.ts b/framework/bigcommerce/api/cart/handlers/update-item.ts index c64c111df..df9ccaee8 100644 --- a/framework/bigcommerce/api/cart/handlers/update-item.ts +++ b/framework/bigcommerce/api/cart/handlers/update-item.ts @@ -2,7 +2,6 @@ import { parseCartItem } from '../../utils/parse-item' import getCartCookie from '../../utils/get-cart-cookie' import type { CartHandlers } from '..' -// Return current cart info const updateItem: CartHandlers['updateItem'] = async ({ res, body: { cartId, itemId, item }, diff --git a/framework/commerce/api/index.ts b/framework/commerce/api/index.ts index b1ef09a62..77b2eeb7e 100644 --- a/framework/commerce/api/index.ts +++ b/framework/commerce/api/index.ts @@ -32,5 +32,3 @@ export interface CommerceAPIFetchOptions<Variables> { variables?: Variables preview?: boolean } - -// TODO: define interfaces for all the available operations and API endpoints From 10318b2fab9f658fada14381febb9f31c02d6cd5 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Tue, 19 Jan 2021 17:40:27 -0500 Subject: [PATCH 040/221] Add loading state for data hooks --- framework/bigcommerce/cart/use-cart.tsx | 15 ++--- framework/bigcommerce/lib/normalize.ts | 69 ++++++++++---------- framework/commerce/cart/use-cart.tsx | 10 ++- framework/commerce/utils/define-property.ts | 37 +++++++++++ framework/commerce/utils/use-data.tsx | 14 +++- framework/commerce/wishlist/use-wishlist.tsx | 8 +-- 6 files changed, 101 insertions(+), 52 deletions(-) create mode 100644 framework/commerce/utils/define-property.ts diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index 204ef0fa1..aab4c2e6f 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -1,10 +1,9 @@ - import { normalizeCart } from '../lib/normalize' import type { HookFetcher } from '@commerce/utils/types' import type { SwrOptions } from '@commerce/utils/use-data' import useCommerceCart, { CartInput } from '@commerce/cart/use-cart' import type { Cart as BigCommerceCart } from '../api/cart' -import update from "@framework/lib/immutability" +import update from '@framework/lib/immutability' const defaultOpts = { url: '/api/bigcommerce/cart', @@ -13,7 +12,7 @@ const defaultOpts = { type UseCartResponse = BigCommerceCart & Cart -export const fetcher: HookFetcher<UseCartResponse | null , CartInput> = ( +export const fetcher: HookFetcher<UseCartResponse | null, CartInput> = ( options, { cartId }, fetch @@ -42,11 +41,11 @@ export function extendHook( set: (x) => x, }) - - - return response.data ? update(response, { - data: { $set: normalizeCart(response.data ) } - }) : response + return response.data + ? update(response, { + data: { $set: normalizeCart(response.data) }, + }) + : response } useCart.extend = extendHook diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index 83ac0a2e6..bbce17214 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -1,14 +1,13 @@ -import update from "@framework/lib/immutability" -import { Cart, CartItem, Product } from '../../types' +import update from '@framework/lib/immutability' -function normalizeProductOption(productOption:any) { +function normalizeProductOption(productOption: any) { const { node: { entityId, values: { edges }, ...rest }, - } = productOption; + } = productOption return { id: entityId, @@ -30,52 +29,52 @@ export function normalizeProduct(productNode: any): Product { return update(productNode, { id: { $set: String(id) }, images: { - $apply: ({edges} : any) => edges?.map( - ({ node: { urlOriginal, altText, ...rest } }: any) => ({ + $apply: ({ edges }: any) => + edges?.map(({ node: { urlOriginal, altText, ...rest } }: any) => ({ url: urlOriginal, alt: altText, ...rest, - }) - ) - }, + })), + }, variants: { - $apply: ({edges} : any) => edges?.map( - ({ node: { entityId, productOptions, ...rest } }: any) => ({ + $apply: ({ edges }: any) => + edges?.map(({ node: { entityId, productOptions, ...rest } }: any) => ({ id: entityId, options: productOptions?.edges ? productOptions.edges.map(normalizeProductOption) : [], ...rest, - }) - ) + })), }, options: { - $set: productOptions.edges ? productOptions?.edges.map(normalizeProductOption) : [] + $set: productOptions.edges + ? productOptions?.edges.map(normalizeProductOption) + : [], }, brand: { - $apply:(brand : any) => brand?.entityId ? brand?.entityId : null, - }, - slug: { - $set: path?.replace(/^\/+|\/+$/g, '') + $apply: (brand: any) => (brand?.entityId ? brand?.entityId : null), + }, + slug: { + $set: path?.replace(/^\/+|\/+$/g, ''), }, price: { $set: { value: prices?.price.value, currencyCode: prices?.price.currencyCode, - } - }, - $unset: ['entityId'] + }, + }, + $unset: ['entityId'], }) } export function normalizeCart(data: any): Cart { return update(data, { $auto: { - items: { $set: data?.line_items?.physical_items?.map(itemsToProducts)}, + items: { $set: data?.line_items?.physical_items?.map(itemsToProducts) }, subTotal: { $set: data?.base_amount }, - total: { $set: data?.cart_amount } + total: { $set: data?.cart_amount }, }, - $unset: ['created_time', 'coupons', 'line_items', 'email'] + $unset: ['created_time', 'coupons', 'line_items', 'email'], }) } @@ -92,24 +91,28 @@ function itemsToProducts(item: any): CartItem { extended_list_price, extended_sale_price, ...rest - } = item; + } = item return update(item, { $auto: { prices: { $auto: { listPrice: { $set: list_price }, - salePrice: { $set: sale_price } , + salePrice: { $set: sale_price }, extendedListPrice: { $set: extended_list_price }, extendedSalePrice: { $set: extended_sale_price }, - } + }, + }, + images: { + $set: [ + { + alt: name, + url: image_url, + }, + ], }, - images: { $set: [{ - alt: name, - url: image_url - }]}, productId: { $set: product_id }, - variantId: { $set: variant_id } - } + variantId: { $set: variant_id }, + }, }) } diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index f280923a6..af472244d 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -1,12 +1,10 @@ import type { responseInterface } from 'swr' import Cookies from 'js-cookie' import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types' -import useData, { SwrOptions } from '../utils/use-data' +import useData, { ResponseState, SwrOptions } from '../utils/use-data' import { useCommerce } from '..' -export type CartResponse<Result> = responseInterface<Result, Error> & { - isEmpty: boolean -} +export type CartResponse<Result> = ResponseState<Result> & { isEmpty: boolean } export type CartInput = { cartId: Cart['id'] @@ -17,7 +15,7 @@ export default function useCart<Result>( input: HookInput, fetcherFn: HookFetcher<Result, CartInput>, swrOptions?: SwrOptions<Result, CartInput> -) { +): CartResponse<Result> { const { cartCookie } = useCommerce() const fetcher: typeof fetcherFn = (options, input, fetch) => { @@ -27,5 +25,5 @@ export default function useCart<Result>( const response = useData(options, input, fetcher, swrOptions) - return Object.assign(response, { isEmpty: true }) as CartResponse<Result> + return Object.assign(response, { isEmpty: true }) } diff --git a/framework/commerce/utils/define-property.ts b/framework/commerce/utils/define-property.ts new file mode 100644 index 000000000..e89735226 --- /dev/null +++ b/framework/commerce/utils/define-property.ts @@ -0,0 +1,37 @@ +// Taken from https://fettblog.eu/typescript-assertion-signatures/ + +type InferValue<Prop extends PropertyKey, Desc> = Desc extends { + get(): any + value: any +} + ? never + : Desc extends { value: infer T } + ? Record<Prop, T> + : Desc extends { get(): infer T } + ? Record<Prop, T> + : never + +type DefineProperty< + Prop extends PropertyKey, + Desc extends PropertyDescriptor +> = Desc extends { writable: any; set(val: any): any } + ? never + : Desc extends { writable: any; get(): any } + ? never + : Desc extends { writable: false } + ? Readonly<InferValue<Prop, Desc>> + : Desc extends { writable: true } + ? InferValue<Prop, Desc> + : Readonly<InferValue<Prop, Desc>> + +export default function defineProperty< + Obj extends object, + Key extends PropertyKey, + PDesc extends PropertyDescriptor +>( + obj: Obj, + prop: Key, + val: PDesc +): asserts obj is Obj & DefineProperty<Key, PDesc> { + Object.defineProperty(obj, prop, val) +} diff --git a/framework/commerce/utils/use-data.tsx b/framework/commerce/utils/use-data.tsx index dd5917ccb..d7f8ac5d6 100644 --- a/framework/commerce/utils/use-data.tsx +++ b/framework/commerce/utils/use-data.tsx @@ -1,5 +1,6 @@ import useSWR, { ConfigInterface, responseInterface } from 'swr' import type { HookInput, HookFetcher, HookFetcherOptions } from './types' +import defineProperty from './define-property' import { CommerceError } from './errors' import { useCommerce } from '..' @@ -9,12 +10,16 @@ export type SwrOptions<Result, Input = null> = ConfigInterface< HookFetcher<Result, Input> > +export type ResponseState<Result> = responseInterface<Result, CommerceError> & { + isLoading: boolean +} + export type UseData = <Result = any, Input = null>( options: HookFetcherOptions | (() => HookFetcherOptions | null), input: HookInput, fetcherFn: HookFetcher<Result, Input>, swrOptions?: SwrOptions<Result, Input> -) => responseInterface<Result, CommerceError> +) => ResponseState<Result> const useData: UseData = (options, input, fetcherFn, swrOptions) => { const { fetcherRef } = useCommerce() @@ -54,6 +59,13 @@ const useData: UseData = (options, input, fetcherFn, swrOptions) => { swrOptions ) + defineProperty(response, 'isLoading', { + get() { + return response.data === undefined + }, + set: (x) => x, + }) + return response } diff --git a/framework/commerce/wishlist/use-wishlist.tsx b/framework/commerce/wishlist/use-wishlist.tsx index 7b2981412..66199e380 100644 --- a/framework/commerce/wishlist/use-wishlist.tsx +++ b/framework/commerce/wishlist/use-wishlist.tsx @@ -1,8 +1,8 @@ import type { responseInterface } from 'swr' import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types' -import useData, { SwrOptions } from '../utils/use-data' +import useData, { ResponseState, SwrOptions } from '../utils/use-data' -export type WishlistResponse<Result> = responseInterface<Result, Error> & { +export type WishlistResponse<Result> = ResponseState<Result> & { isEmpty: boolean } @@ -11,7 +11,7 @@ export default function useWishlist<Result, Input = null>( input: HookInput, fetcherFn: HookFetcher<Result, Input>, swrOptions?: SwrOptions<Result, Input> -) { +): WishlistResponse<Result> { const response = useData(options, input, fetcherFn, swrOptions) - return Object.assign(response, { isEmpty: true }) as WishlistResponse<Result> + return Object.assign(response, { isEmpty: true }) } From a7baff45fc378975edb9d8103a60b7ed6d91f7e3 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Tue, 19 Jan 2021 18:45:50 -0500 Subject: [PATCH 041/221] Bug fix --- framework/commerce/utils/use-data.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/framework/commerce/utils/use-data.tsx b/framework/commerce/utils/use-data.tsx index d7f8ac5d6..00d7c8c31 100644 --- a/framework/commerce/utils/use-data.tsx +++ b/framework/commerce/utils/use-data.tsx @@ -59,12 +59,14 @@ const useData: UseData = (options, input, fetcherFn, swrOptions) => { swrOptions ) - defineProperty(response, 'isLoading', { - get() { - return response.data === undefined - }, - set: (x) => x, - }) + if (!('isLoading' in response)) { + defineProperty(response, 'isLoading', { + get() { + return response.data === undefined + }, + set: (x) => x, + }) + } return response } From 1db974dabdcd6f089f9961bcbb00a0dda06bcbee Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Wed, 20 Jan 2021 15:29:02 -0500 Subject: [PATCH 042/221] Changed the way isEmpty works --- .../cart/CartSidebarView/CartSidebarView.tsx | 10 ++++------ framework/bigcommerce/cart/use-cart.tsx | 19 +++++++++++-------- .../bigcommerce/wishlist/use-wishlist.tsx | 15 +++++++++------ framework/commerce/cart/use-cart.tsx | 4 ++-- framework/commerce/wishlist/use-wishlist.tsx | 4 ++-- pages/cart.tsx | 4 ++-- pages/wishlist.tsx | 4 ++-- 7 files changed, 32 insertions(+), 28 deletions(-) diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index 1f54b96f9..5cd409559 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -11,7 +11,7 @@ import s from './CartSidebarView.module.css' const CartSidebarView: FC = () => { const { closeSidebar } = useUI() - const { data, isEmpty } = useCart() + const { data, isLoading, isEmpty } = useCart() const { price: subTotal } = usePrice( data && { @@ -19,7 +19,7 @@ const CartSidebarView: FC = () => { currencyCode: data.currency?.code || 'USD', } ) - + const { price: total } = usePrice( data && { amount: Number(data.total), @@ -34,9 +34,7 @@ const CartSidebarView: FC = () => { return ( <div className={cn(s.root, { - [s.empty]: error, - [s.empty]: success, - [s.empty]: isEmpty, + [s.empty]: error || success || isLoading || isEmpty, })} > <header className="px-4 pt-6 pb-4 sm:px-6"> @@ -56,7 +54,7 @@ const CartSidebarView: FC = () => { </div> </header> - {isEmpty ? ( + {isLoading || isEmpty ? ( <div className="flex-1 px-4 flex flex-col justify-center items-center"> <span className="border border-dashed border-primary rounded-full flex items-center justify-center w-16 h-16 p-12 bg-secondary text-secondary"> <Bag className="absolute" /> diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index aab4c2e6f..708db68c5 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -1,6 +1,7 @@ import { normalizeCart } from '../lib/normalize' import type { HookFetcher } from '@commerce/utils/types' import type { SwrOptions } from '@commerce/utils/use-data' +import defineProperty from '@commerce/utils/define-property' import useCommerceCart, { CartInput } from '@commerce/cart/use-cart' import type { Cart as BigCommerceCart } from '../api/cart' import update from '@framework/lib/immutability' @@ -32,14 +33,16 @@ export function extendHook( // Uses a getter to only calculate the prop when required // response.data is also a getter and it's better to not trigger it early - Object.defineProperty(response, 'isEmpty', { - get() { - return Object.values(response.data?.line_items ?? {}).every( - (items) => !items.length - ) - }, - set: (x) => x, - }) + if (!('isEmpty' in response)) { + defineProperty(response, 'isEmpty', { + get() { + return Object.values(response.data?.line_items ?? {}).every( + (items) => !items.length + ) + }, + set: (x) => x, + }) + } return response.data ? update(response, { diff --git a/framework/bigcommerce/wishlist/use-wishlist.tsx b/framework/bigcommerce/wishlist/use-wishlist.tsx index 0e1671039..870b90019 100644 --- a/framework/bigcommerce/wishlist/use-wishlist.tsx +++ b/framework/bigcommerce/wishlist/use-wishlist.tsx @@ -1,5 +1,6 @@ import { HookFetcher } from '@commerce/utils/types' import { SwrOptions } from '@commerce/utils/use-data' +import defineProperty from '@commerce/utils/define-property' import useCommerceWishlist from '@commerce/wishlist/use-wishlist' import type { Wishlist } from '../api/wishlist' import useCustomer from '../customer/use-customer' @@ -58,12 +59,14 @@ export function extendHook( // Uses a getter to only calculate the prop when required // response.data is also a getter and it's better to not trigger it early - Object.defineProperty(response, 'isEmpty', { - get() { - return (response.data?.items?.length || 0) <= 0 - }, - set: (x) => x, - }) + if (!('isEmpty' in response)) { + defineProperty(response, 'isEmpty', { + get() { + return (response.data?.items?.length || 0) <= 0 + }, + set: (x) => x, + }) + } return response } diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index af472244d..513c041ea 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -4,7 +4,7 @@ import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types' import useData, { ResponseState, SwrOptions } from '../utils/use-data' import { useCommerce } from '..' -export type CartResponse<Result> = ResponseState<Result> & { isEmpty: boolean } +export type CartResponse<Result> = ResponseState<Result> & { isEmpty?: boolean } export type CartInput = { cartId: Cart['id'] @@ -25,5 +25,5 @@ export default function useCart<Result>( const response = useData(options, input, fetcher, swrOptions) - return Object.assign(response, { isEmpty: true }) + return response } diff --git a/framework/commerce/wishlist/use-wishlist.tsx b/framework/commerce/wishlist/use-wishlist.tsx index 66199e380..62b1c8702 100644 --- a/framework/commerce/wishlist/use-wishlist.tsx +++ b/framework/commerce/wishlist/use-wishlist.tsx @@ -3,7 +3,7 @@ import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types' import useData, { ResponseState, SwrOptions } from '../utils/use-data' export type WishlistResponse<Result> = ResponseState<Result> & { - isEmpty: boolean + isEmpty?: boolean } export default function useWishlist<Result, Input = null>( @@ -13,5 +13,5 @@ export default function useWishlist<Result, Input = null>( swrOptions?: SwrOptions<Result, Input> ): WishlistResponse<Result> { const response = useData(options, input, fetcherFn, swrOptions) - return Object.assign(response, { isEmpty: true }) + return response } diff --git a/pages/cart.tsx b/pages/cart.tsx index 2021291a1..b7f2027d3 100644 --- a/pages/cart.tsx +++ b/pages/cart.tsx @@ -24,7 +24,7 @@ export async function getStaticProps({ export default function Cart() { const error = null const success = null - const { data, isEmpty } = useCart() + const { data, isLoading, isEmpty } = useCart() const { price: subTotal } = usePrice( data && { @@ -42,7 +42,7 @@ export default function Cart() { return ( <div className="grid lg:grid-cols-12"> <div className="lg:col-span-8"> - {isEmpty ? ( + {isLoading || isEmpty ? ( <div className="flex-1 px-12 py-24 flex flex-col justify-center items-center "> <span className="border border-dashed border-secondary flex items-center justify-center w-16 h-16 bg-primary p-12 rounded-lg text-primary"> <Bag className="absolute" /> diff --git a/pages/wishlist.tsx b/pages/wishlist.tsx index 9df111dc6..a3f25d0e7 100644 --- a/pages/wishlist.tsx +++ b/pages/wishlist.tsx @@ -22,14 +22,14 @@ export async function getStaticProps({ export default function Wishlist() { const { data: customer } = useCustomer() - const { data, isEmpty } = useWishlist() + const { data, isLoading, isEmpty } = useWishlist() return ( <Container> <div className="mt-3 mb-20"> <Text variant="pageHeading">My Wishlist</Text> <div className="group flex flex-col"> - {isEmpty ? ( + {isLoading || isEmpty ? ( <div className="flex-1 px-12 py-24 flex flex-col justify-center items-center "> <span className="border border-dashed border-secondary flex items-center justify-center w-16 h-16 bg-primary p-12 rounded-lg text-primary"> <Heart className="absolute" /> From 2613a5cec770ad47c41edc5841ad853006773e92 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 21 Jan 2021 10:44:59 -0500 Subject: [PATCH 043/221] Improve navbar performance --- components/common/Navbar/Navbar.tsx | 93 ++++++++++--------------- components/common/Navbar/NavbarRoot.tsx | 33 +++++++++ 2 files changed, 70 insertions(+), 56 deletions(-) create mode 100644 components/common/Navbar/NavbarRoot.tsx diff --git a/components/common/Navbar/Navbar.tsx b/components/common/Navbar/Navbar.tsx index d2d2b2d79..b2a372f66 100644 --- a/components/common/Navbar/Navbar.tsx +++ b/components/common/Navbar/Navbar.tsx @@ -1,66 +1,47 @@ -import { FC, useState, useEffect } from 'react' +import { FC } from 'react' import Link from 'next/link' -import s from './Navbar.module.css' import { Logo, Container } from '@components/ui' import { Searchbar, UserNav } from '@components/common' -import cn from 'classnames' -import throttle from 'lodash.throttle' +import NavbarRoot from './NavbarRoot' +import s from './Navbar.module.css' -const Navbar: FC = () => { - const [hasScrolled, setHasScrolled] = useState(false) - - useEffect(() => { - const handleScroll = throttle(() => { - const offset = 0 - const { scrollTop } = document.documentElement - const scrolled = scrollTop > offset - setHasScrolled(scrolled) - }, 200) - - document.addEventListener('scroll', handleScroll) - return () => { - document.removeEventListener('scroll', handleScroll) - } - }, []) - - return ( - <div className={cn(s.root, { 'shadow-magical': hasScrolled })}> - <Container> - <div className="relative flex flex-row justify-between py-4 align-center md:py-6"> - <div className="flex items-center flex-1"> - <Link href="/"> - <a className={s.logo} aria-label="Logo"> - <Logo /> - </a> +const Navbar: FC = () => ( + <NavbarRoot> + <Container> + <div className="relative flex flex-row justify-between py-4 align-center md:py-6"> + <div className="flex items-center flex-1"> + <Link href="/"> + <a className={s.logo} aria-label="Logo"> + <Logo /> + </a> + </Link> + <nav className="hidden ml-6 space-x-4 lg:block"> + <Link href="/search"> + <a className={s.link}>All</a> </Link> - <nav className="hidden ml-6 space-x-4 lg:block"> - <Link href="/search"> - <a className={s.link}>All</a> - </Link> - <Link href="/search?q=clothes"> - <a className={s.link}>Clothes</a> - </Link> - <Link href="/search?q=accessories"> - <a className={s.link}>Accessories</a> - </Link> - </nav> - </div> - - <div className="justify-center flex-1 hidden lg:flex"> - <Searchbar /> - </div> - - <div className="flex justify-end flex-1 space-x-8"> - <UserNav /> - </div> + <Link href="/search?q=clothes"> + <a className={s.link}>Clothes</a> + </Link> + <Link href="/search?q=accessories"> + <a className={s.link}>Accessories</a> + </Link> + </nav> </div> - <div className="flex pb-4 lg:px-6 lg:hidden"> - <Searchbar id="mobile-search" /> + <div className="justify-center flex-1 hidden lg:flex"> + <Searchbar /> </div> - </Container> - </div> - ) -} + + <div className="flex justify-end flex-1 space-x-8"> + <UserNav /> + </div> + </div> + + <div className="flex pb-4 lg:px-6 lg:hidden"> + <Searchbar id="mobile-search" /> + </div> + </Container> + </NavbarRoot> +) export default Navbar diff --git a/components/common/Navbar/NavbarRoot.tsx b/components/common/Navbar/NavbarRoot.tsx new file mode 100644 index 000000000..2eb8c5429 --- /dev/null +++ b/components/common/Navbar/NavbarRoot.tsx @@ -0,0 +1,33 @@ +import { FC, useState, useEffect } from 'react' +import throttle from 'lodash.throttle' +import cn from 'classnames' +import s from './Navbar.module.css' + +const NavbarRoot: FC = ({ children }) => { + const [hasScrolled, setHasScrolled] = useState(false) + + useEffect(() => { + const handleScroll = throttle(() => { + const offset = 0 + const { scrollTop } = document.documentElement + const scrolled = scrollTop > offset + + if (hasScrolled !== scrolled) { + setHasScrolled(scrolled) + } + }, 200) + + document.addEventListener('scroll', handleScroll) + return () => { + document.removeEventListener('scroll', handleScroll) + } + }, [hasScrolled]) + + return ( + <div className={cn(s.root, { 'shadow-magical': hasScrolled })}> + {children} + </div> + ) +} + +export default NavbarRoot From bafb8a4479ee65b8cd5cb9c40b1ebc5caed98aae Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 21 Jan 2021 21:19:53 -0500 Subject: [PATCH 044/221] Added useResponse hook --- framework/bigcommerce/cart/use-cart.tsx | 33 +++++++--------- framework/commerce/cart/use-cart.tsx | 3 -- framework/commerce/utils/types.ts | 2 + framework/commerce/utils/use-data.tsx | 2 +- framework/commerce/utils/use-response.tsx | 40 ++++++++++++++++++++ framework/commerce/wishlist/use-wishlist.tsx | 1 - 6 files changed, 57 insertions(+), 24 deletions(-) create mode 100644 framework/commerce/utils/use-response.tsx diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index 708db68c5..c1a9bed7a 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -1,10 +1,9 @@ import { normalizeCart } from '../lib/normalize' import type { HookFetcher } from '@commerce/utils/types' import type { SwrOptions } from '@commerce/utils/use-data' -import defineProperty from '@commerce/utils/define-property' +import useResponse from '@commerce/utils/use-response' import useCommerceCart, { CartInput } from '@commerce/cart/use-cart' import type { Cart as BigCommerceCart } from '../api/cart' -import update from '@framework/lib/immutability' const defaultOpts = { url: '/api/bigcommerce/cart', @@ -30,25 +29,21 @@ export function extendHook( revalidateOnFocus: false, ...swrOptions, }) - - // Uses a getter to only calculate the prop when required - // response.data is also a getter and it's better to not trigger it early - if (!('isEmpty' in response)) { - defineProperty(response, 'isEmpty', { - get() { - return Object.values(response.data?.line_items ?? {}).every( - (items) => !items.length - ) + const res = useResponse(response, { + normalizer: normalizeCart, + descriptors: { + isEmpty: { + get() { + return Object.values(response.data?.line_items ?? {}).every( + (items) => !items.length + ) + }, + enumerable: true, }, - set: (x) => x, - }) - } + }, + }) - return response.data - ? update(response, { - data: { $set: normalizeCart(response.data) }, - }) - : response + return res } useCart.extend = extendHook diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index 513c041ea..c0fd10a5e 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -1,4 +1,3 @@ -import type { responseInterface } from 'swr' import Cookies from 'js-cookie' import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types' import useData, { ResponseState, SwrOptions } from '../utils/use-data' @@ -17,12 +16,10 @@ export default function useCart<Result>( swrOptions?: SwrOptions<Result, CartInput> ): CartResponse<Result> { const { cartCookie } = useCommerce() - const fetcher: typeof fetcherFn = (options, input, fetch) => { input.cartId = Cookies.get(cartCookie) return fetcherFn(options, input, fetch) } - const response = useData(options, input, fetcher, swrOptions) return response diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index b983d3391..2cd838964 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -22,3 +22,5 @@ export type HookFetcherOptions = { } export type HookInput = [string, string | number | boolean | undefined][] + +export type Override<T, K> = Omit<T, keyof K> & K diff --git a/framework/commerce/utils/use-data.tsx b/framework/commerce/utils/use-data.tsx index 00d7c8c31..a921eb529 100644 --- a/framework/commerce/utils/use-data.tsx +++ b/framework/commerce/utils/use-data.tsx @@ -64,7 +64,7 @@ const useData: UseData = (options, input, fetcherFn, swrOptions) => { get() { return response.data === undefined }, - set: (x) => x, + enumerable: true, }) } diff --git a/framework/commerce/utils/use-response.tsx b/framework/commerce/utils/use-response.tsx new file mode 100644 index 000000000..f3a1ed071 --- /dev/null +++ b/framework/commerce/utils/use-response.tsx @@ -0,0 +1,40 @@ +import { useMemo } from 'react' +import { responseInterface } from 'swr' +import { CommerceError } from './errors' +import { Override } from './types' + +export type UseResponseOptions< + D, + R extends responseInterface<any, CommerceError> +> = { + descriptors?: PropertyDescriptorMap + normalizer?: (data: R['data']) => D +} + +export type UseResponse = <D, R extends responseInterface<any, CommerceError>>( + response: R, + options: UseResponseOptions<D, R> +) => D extends object ? Override<R, { data?: D }> : R + +const useResponse: UseResponse = (response, { descriptors, normalizer }) => { + const memoizedResponse = useMemo( + () => + Object.create(response, { + ...descriptors, + ...(normalizer + ? { + data: { + get() { + return normalizer(response.data) + }, + enumerable: true, + }, + } + : {}), + }), + [response] + ) + return memoizedResponse +} + +export default useResponse diff --git a/framework/commerce/wishlist/use-wishlist.tsx b/framework/commerce/wishlist/use-wishlist.tsx index 62b1c8702..df8bebe5c 100644 --- a/framework/commerce/wishlist/use-wishlist.tsx +++ b/framework/commerce/wishlist/use-wishlist.tsx @@ -1,4 +1,3 @@ -import type { responseInterface } from 'swr' import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types' import useData, { ResponseState, SwrOptions } from '../utils/use-data' From fc104c6caf9e04fb00558bcba8271d814808423a Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 22 Jan 2021 10:51:49 -0500 Subject: [PATCH 045/221] added useResponse to useWhishlist --- .../bigcommerce/wishlist/use-wishlist.tsx | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/framework/bigcommerce/wishlist/use-wishlist.tsx b/framework/bigcommerce/wishlist/use-wishlist.tsx index 870b90019..730bf19dc 100644 --- a/framework/bigcommerce/wishlist/use-wishlist.tsx +++ b/framework/bigcommerce/wishlist/use-wishlist.tsx @@ -1,6 +1,6 @@ import { HookFetcher } from '@commerce/utils/types' import { SwrOptions } from '@commerce/utils/use-data' -import defineProperty from '@commerce/utils/define-property' +import useResponse from '@commerce/utils/use-response' import useCommerceWishlist from '@commerce/wishlist/use-wishlist' import type { Wishlist } from '../api/wishlist' import useCustomer from '../customer/use-customer' @@ -56,19 +56,18 @@ export function extendHook( ...swrOptions, } ) - - // Uses a getter to only calculate the prop when required - // response.data is also a getter and it's better to not trigger it early - if (!('isEmpty' in response)) { - defineProperty(response, 'isEmpty', { - get() { - return (response.data?.items?.length || 0) <= 0 + const res = useResponse(response, { + descriptors: { + isEmpty: { + get() { + return (response.data?.items?.length || 0) <= 0 + }, + set: (x) => x, }, - set: (x) => x, - }) - } + }, + }) - return response + return res } useWishlist.extend = extendHook From bccef99c35c26615acb494e9e53e46fed2a1901e Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 22 Jan 2021 12:13:34 -0500 Subject: [PATCH 046/221] Added husky and lint-staged --- package.json | 82 +++++++++------- yarn.lock | 261 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 305 insertions(+), 38 deletions(-) diff --git a/package.json b/package.json index 9848a4674..e9dc98176 100644 --- a/package.json +++ b/package.json @@ -10,39 +10,14 @@ "generate": "graphql-codegen", "generate:definitions": "node framework/bigcommerce/scripts/generate-definitions.js" }, + "license": "MIT", + "engines": { + "node": "12.x" + }, "prettier": { "semi": false, "singleQuote": true }, - "next-unused": { - "alias": { - "@lib/*": [ - "lib/*" - ], - "@assets/*": [ - "assets/*" - ], - "@config/*": [ - "config/*" - ], - "@components/*": [ - "components/*" - ], - "@utils/*": [ - "utils/*" - ] - }, - "debug": true, - "include": [ - "components", - "lib", - "pages" - ], - "exclude": [], - "entrypoints": [ - "pages" - ] - }, "dependencies": { "@reach/portal": "^0.11.2", "@tailwindcss/ui": "^0.6.2", @@ -93,6 +68,8 @@ "bunyan": "^1.8.14", "bunyan-prettystream": "^0.1.3", "graphql": "^15.4.0", + "husky": "^4.3.8", + "lint-staged": "^10.5.3", "next-unused": "^0.0.3", "postcss-flexbugs-fixes": "^4.2.1", "postcss-preset-env": "^6.7.0", @@ -102,8 +79,49 @@ "resolutions": { "webpack": "5.11.1" }, - "engines": { - "node": "12.x" + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } }, - "license": "MIT" + "lint-staged": { + "**/*.{js,jsx,ts,tsx}": [ + "eslint", + "prettier --write", + "git add" + ], + "**/*.{md,mdx,json}": [ + "prettier --write", + "git add" + ] + }, + "next-unused": { + "alias": { + "@lib/*": [ + "lib/*" + ], + "@assets/*": [ + "assets/*" + ], + "@config/*": [ + "config/*" + ], + "@components/*": [ + "components/*" + ], + "@utils/*": [ + "utils/*" + ] + }, + "debug": true, + "include": [ + "components", + "lib", + "pages" + ], + "exclude": [], + "entrypoints": [ + "pages" + ] + } } diff --git a/yarn.lock b/yarn.lock index bc77775f6..b46f7acce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1538,6 +1538,14 @@ agentkeepalive@3.4.1: dependencies: humanize-ms "^1.2.1" +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + 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" @@ -1558,12 +1566,17 @@ anser@1.4.9: resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.9.tgz#1f85423a5dcf8da4631a341665ff675b96845760" integrity sha512-AI+BjTeGt2+WFk4eWcqbQ7snZpDBt8SaLlj0RT2h5xfdWaiy51OjYvqwMrNzJLGy8iOAL6nKDITWO+rd4MkYEA== +ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + ansi-escapes@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== -ansi-escapes@^4.2.1, ansi-escapes@^4.3.1: +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0, ansi-escapes@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== @@ -1714,6 +1727,11 @@ ast-types@0.13.2: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48" integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA== +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + async-retry@1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.2.3.tgz#a6521f338358d322b1a0012b79030c6f411d1ce0" @@ -2209,6 +2227,11 @@ chrome-trace-event@^1.0.2: dependencies: tslib "^1.9.0" +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" @@ -2222,6 +2245,11 @@ classnames@2.2.6, classnames@^2.2.6: resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + cli-cursor@^2.0.0, cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -2249,6 +2277,14 @@ 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" + cli-width@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" @@ -2371,6 +2407,11 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= +compare-versions@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" + integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== + compose-function@3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/compose-function/-/compose-function-3.0.3.tgz#9ed675f13cc54501d30950a486ff6a7ba3ab185f" @@ -2507,6 +2548,15 @@ cross-fetch@3.0.6, cross-fetch@^3.0.4, cross-fetch@^3.0.6: dependencies: node-fetch "2.6.1" +cross-spawn@^7.0.0: + 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: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -2722,6 +2772,11 @@ decompress-response@^6.0.0: dependencies: mimic-response "^3.1.0" +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= + deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -3099,6 +3154,13 @@ enhanced-resolve@^5.3.1: graceful-fs "^4.2.4" tapable "^2.2.0" +enquirer@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + entities@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" @@ -3268,6 +3330,21 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" +execa@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + expand-template@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" @@ -3383,7 +3460,7 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" -figures@^3.0.0: +figures@^3.0.0, figures@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== @@ -3446,6 +3523,13 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" +find-versions@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-4.0.0.tgz#3c57e573bf97769b8cb8df16934b627915da4965" + integrity sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ== + dependencies: + semver-regex "^3.1.2" + find@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/find/-/find-0.3.0.tgz#4082e8fc8d8320f1a382b5e4f521b9bc50775cb8" @@ -3589,7 +3673,7 @@ get-stream@^4.1.0: dependencies: pump "^3.0.0" -get-stream@^5.1.0: +get-stream@^5.0.0, get-stream@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== @@ -3922,6 +4006,11 @@ https-proxy-agent@5.0.0, https-proxy-agent@^5.0.0: agent-base "6" debug "4" +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -3929,6 +4018,22 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" +husky@^4.3.8: + version "4.3.8" + resolved "https://registry.yarnpkg.com/husky/-/husky-4.3.8.tgz#31144060be963fd6850e5cc8f019a1dfe194296d" + integrity sha512-LCqqsB0PzJQ/AlCgfrfzRe3e3+NvmefAdKQhRYpxS4u6clblBoDdzzvHi8fmxKRzvMxPY/1WZWzomPZww0Anow== + dependencies: + chalk "^4.0.0" + ci-info "^2.0.0" + compare-versions "^3.6.0" + cosmiconfig "^7.0.0" + find-versions "^4.0.0" + opencollective-postinstall "^2.0.2" + pkg-dir "^5.0.0" + please-upgrade-node "^3.2.0" + slash "^3.0.0" + which-pm-runs "^1.0.0" + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -4177,6 +4282,11 @@ is-stream@^1.1.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + is-symbol@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" @@ -4211,6 +4321,11 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" @@ -4457,6 +4572,27 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= +lint-staged@^10.5.3: + version "10.5.3" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.5.3.tgz#c682838b3eadd4c864d1022da05daa0912fb1da5" + integrity sha512-TanwFfuqUBLufxCc3RUtFEkFraSPNR3WzWcGF39R3f2J7S9+iF9W0KTVLfSy09lYGmZS5NDCxjNvhGMSJyFCWg== + dependencies: + chalk "^4.1.0" + cli-truncate "^2.1.0" + commander "^6.2.0" + cosmiconfig "^7.0.0" + debug "^4.2.0" + dedent "^0.7.0" + enquirer "^2.3.6" + execa "^4.1.0" + listr2 "^3.2.2" + log-symbols "^4.0.0" + micromatch "^4.0.2" + normalize-path "^3.0.0" + please-upgrade-node "^3.2.0" + string-argv "0.3.1" + stringify-object "^3.3.0" + listr-silent-renderer@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" @@ -4486,6 +4622,21 @@ listr-verbose-renderer@^0.5.0: date-fns "^1.27.2" figures "^2.0.0" +listr2@^3.2.2: + version "3.3.0" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.3.0.tgz#fab51211e4152d88bab7d91e4f7f896b0d9e5ba1" + integrity sha512-G9IFI/m65icgVlifS0wMQnvn35/8VJGzEb3crpE4NnaegQYQOn/wP7yqi9TTJQ/eoxme4UaPbffBK1XqKP/DOg== + dependencies: + chalk "^4.1.0" + cli-truncate "^2.1.0" + figures "^3.2.0" + indent-string "^4.0.0" + log-update "^4.0.0" + p-map "^4.0.0" + rxjs "^6.6.3" + through "^2.3.8" + wrap-ansi "^7.0.0" + listr@^0.14.3: version "0.14.3" resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" @@ -4665,6 +4816,16 @@ log-update@^2.3.0: cli-cursor "^2.0.0" wrap-ansi "^3.0.1" +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== + dependencies: + ansi-escapes "^4.3.0" + cli-cursor "^3.1.0" + slice-ansi "^4.0.0" + wrap-ansi "^6.2.0" + loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -5218,6 +5379,13 @@ normalizr@^3.6.1: resolved "https://registry.yarnpkg.com/normalizr/-/normalizr-3.6.1.tgz#d367ab840e031ff382141b8d81ce279292ff69fe" integrity sha512-8iEmqXmPtll8PwbEFrbPoDxVw7MKnNvt3PZzR2Xvq9nggEEOgBlNICPXYzyZ4w4AkHUzCU998mdatER3n2VaMA== +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + npmlog@^4.0.1, npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" @@ -5304,6 +5472,11 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" +opencollective-postinstall@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" + integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== + opener@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" @@ -5392,6 +5565,13 @@ p-map@^2.0.0: resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== +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-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -5488,6 +5668,11 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" @@ -5555,6 +5740,13 @@ platform@1.3.6: resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg== +please-upgrade-node@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942" + integrity sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg== + dependencies: + semver-compare "^1.0.0" + pluralize@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" @@ -6555,7 +6747,7 @@ run-parallel@^1.1.9: resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef" integrity sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw== -rxjs@^6.3.3, rxjs@^6.6.0: +rxjs@^6.3.3, rxjs@^6.6.0, rxjs@^6.6.3: version "6.6.3" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== @@ -6631,6 +6823,16 @@ scuid@^1.1.0: resolved "https://registry.yarnpkg.com/scuid/-/scuid-1.1.0.tgz#d3f9f920956e737a60f72d0e4ad280bf324d5dab" integrity sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg== +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= + +semver-regex@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-3.1.2.tgz#34b4c0d361eef262e07199dbef316d0f2ab11807" + integrity sha512-bXWyL6EAKOJa81XG1OZ/Yyuq+oT0b2YLlxx7c+mrdYPaPbnj6WgVULXhinMIeZGufuUBu/eVRqXEhiv4imfwxA== + "semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" @@ -6699,6 +6901,18 @@ sharp@0.26.3: tar-fs "^2.1.1" tunnel-agent "^0.6.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "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: version "1.7.2" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" @@ -6763,6 +6977,24 @@ slice-ansi@0.0.4: resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + source-list-map@^2.0.0, source-list-map@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" @@ -6895,6 +7127,11 @@ streamsearch@0.1.2: resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= +string-argv@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" + integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== + string-env-interpolation@1.0.1, string-env-interpolation@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz#ad4397ae4ac53fe6c91d1402ad6f6a52862c7152" @@ -6961,7 +7198,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringify-object@^3.2.1: +stringify-object@^3.2.1, stringify-object@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== @@ -6991,6 +7228,11 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -7179,7 +7421,7 @@ terser@5.5.1, terser@^5.5.1: source-map "~0.7.2" source-map-support "~0.5.19" -through@^2.3.6: +through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -7609,6 +7851,13 @@ which-pm-runs@^1.0.0: resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" From 8784e05183771a129cc4e9b95415aa45a9e06537 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 22 Jan 2021 12:17:39 -0500 Subject: [PATCH 047/221] Ran prettier fix --- CHANGELOG.md | 4 +- components/common/I18nWidget/I18nWidget.tsx | 2 +- components/icons/Vercel.tsx | 46 +++++++++++++---- .../product/ProductSlider/ProductSlider.tsx | 26 +++++++--- components/ui/Container/Container.tsx | 6 +-- framework/bigcommerce/README.md | 48 +++++++++--------- framework/bigcommerce/api/cart/index.ts | 8 +-- framework/bigcommerce/api/utils/parse-item.ts | 2 +- framework/bigcommerce/auth/login.ts | 2 +- framework/bigcommerce/common/get-page.ts | 6 +-- framework/bigcommerce/lib/immutability.ts | 23 ++++----- lib/click-outside/click-outside.tsx | 50 ++++++++++--------- package.json | 2 +- 13 files changed, 129 insertions(+), 96 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2d1092e3..7d1d95638 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Changelog +## Changelog - Select Variants Working -- Click on cart item title, closes the sidebar \ No newline at end of file +- Click on cart item title, closes the sidebar diff --git a/components/common/I18nWidget/I18nWidget.tsx b/components/common/I18nWidget/I18nWidget.tsx index 9bfc7b63c..fbe67a4c1 100644 --- a/components/common/I18nWidget/I18nWidget.tsx +++ b/components/common/I18nWidget/I18nWidget.tsx @@ -43,7 +43,7 @@ const I18nWidget: FC = () => { const currentLocale = locale || defaultLocale return ( - <ClickOutside active={display} onClick={() => setDisplay(false)} > + <ClickOutside active={display} onClick={() => setDisplay(false)}> <nav className={s.root}> <div className="flex items-center relative" diff --git a/components/icons/Vercel.tsx b/components/icons/Vercel.tsx index 8e8f6457d..96e619fdf 100644 --- a/components/icons/Vercel.tsx +++ b/components/icons/Vercel.tsx @@ -1,15 +1,39 @@ const Vercel = ({ ...props }) => { - return ( - <svg width="89" height="20" viewBox="0 0 89 20" fill="none" xmlns="http://www.w3.org/2000/svg" { ...props }> - <path d="M11.5625 0L23.125 20H0L11.5625 0Z" fill="currentColor"/> - <path d="M49.875 10.625C49.875 7.40625 47.5 5.15625 44.0937 5.15625C40.6875 5.15625 38.3125 7.40625 38.3125 10.625C38.3125 13.7812 40.875 16.0937 44.4062 16.0937C46.3438 16.0937 48.0938 15.375 49.2188 14.0625L47.0938 12.8437C46.4375 13.5 45.4688 13.9062 44.4062 13.9062C42.8438 13.9062 41.5 13.0625 41.0312 11.7812L40.9375 11.5625H49.7812C49.8438 11.25 49.875 10.9375 49.875 10.625ZM40.9062 9.6875L40.9688 9.5C41.375 8.15625 42.5625 7.34375 44.0625 7.34375C45.5938 7.34375 46.75 8.15625 47.1562 9.5L47.2188 9.6875H40.9062Z" fill="currentColor"/> - <path d="M83.5313 10.625C83.5313 7.40625 81.1563 5.15625 77.75 5.15625C74.3438 5.15625 71.9688 7.40625 71.9688 10.625C71.9688 13.7812 74.5313 16.0937 78.0625 16.0937C80 16.0937 81.75 15.375 82.875 14.0625L80.75 12.8437C80.0938 13.5 79.125 13.9062 78.0625 13.9062C76.5 13.9062 75.1563 13.0625 74.6875 11.7812L74.5938 11.5625H83.4375C83.5 11.25 83.5313 10.9375 83.5313 10.625ZM74.5625 9.6875L74.625 9.5C75.0313 8.15625 76.2188 7.34375 77.7188 7.34375C79.25 7.34375 80.4063 8.15625 80.8125 9.5L80.875 9.6875H74.5625Z" fill="currentColor"/> - <path d="M68.5313 8.84374L70.6563 7.62499C69.6563 6.06249 67.875 5.18749 65.7188 5.18749C62.3125 5.18749 59.9375 7.43749 59.9375 10.6562C59.9375 13.875 62.3125 16.125 65.7188 16.125C67.875 16.125 69.6563 15.25 70.6563 13.6875L68.5313 12.4687C67.9688 13.4062 66.9688 13.9375 65.7188 13.9375C63.75 13.9375 62.4375 12.625 62.4375 10.6562C62.4375 8.68749 63.75 7.37499 65.7188 7.37499C66.9375 7.37499 67.9688 7.90624 68.5313 8.84374Z" fill="currentColor"/> - <path d="M88.2188 1.75H85.7188V15.8125H88.2188V1.75Z" fill="currentColor"/> - <path d="M40.1563 1.75H37.2813L31.7813 11.25L26.2813 1.75H23.375L31.7813 16.25L40.1563 1.75Z" fill="currentColor"/> - <path d="M57.8438 8.0625C58.125 8.0625 58.4062 8.09375 58.6875 8.15625V5.5C56.5625 5.5625 54.5625 6.75 54.5625 8.21875V5.5H52.0625V15.8125H54.5625V11.3437C54.5625 9.40625 55.9062 8.0625 57.8438 8.0625Z" fill="currentColor"/> - </svg> - + return ( + <svg + width="89" + height="20" + viewBox="0 0 89 20" + fill="none" + xmlns="http://www.w3.org/2000/svg" + {...props} + > + <path d="M11.5625 0L23.125 20H0L11.5625 0Z" fill="currentColor" /> + <path + d="M49.875 10.625C49.875 7.40625 47.5 5.15625 44.0937 5.15625C40.6875 5.15625 38.3125 7.40625 38.3125 10.625C38.3125 13.7812 40.875 16.0937 44.4062 16.0937C46.3438 16.0937 48.0938 15.375 49.2188 14.0625L47.0938 12.8437C46.4375 13.5 45.4688 13.9062 44.4062 13.9062C42.8438 13.9062 41.5 13.0625 41.0312 11.7812L40.9375 11.5625H49.7812C49.8438 11.25 49.875 10.9375 49.875 10.625ZM40.9062 9.6875L40.9688 9.5C41.375 8.15625 42.5625 7.34375 44.0625 7.34375C45.5938 7.34375 46.75 8.15625 47.1562 9.5L47.2188 9.6875H40.9062Z" + fill="currentColor" + /> + <path + d="M83.5313 10.625C83.5313 7.40625 81.1563 5.15625 77.75 5.15625C74.3438 5.15625 71.9688 7.40625 71.9688 10.625C71.9688 13.7812 74.5313 16.0937 78.0625 16.0937C80 16.0937 81.75 15.375 82.875 14.0625L80.75 12.8437C80.0938 13.5 79.125 13.9062 78.0625 13.9062C76.5 13.9062 75.1563 13.0625 74.6875 11.7812L74.5938 11.5625H83.4375C83.5 11.25 83.5313 10.9375 83.5313 10.625ZM74.5625 9.6875L74.625 9.5C75.0313 8.15625 76.2188 7.34375 77.7188 7.34375C79.25 7.34375 80.4063 8.15625 80.8125 9.5L80.875 9.6875H74.5625Z" + fill="currentColor" + /> + <path + d="M68.5313 8.84374L70.6563 7.62499C69.6563 6.06249 67.875 5.18749 65.7188 5.18749C62.3125 5.18749 59.9375 7.43749 59.9375 10.6562C59.9375 13.875 62.3125 16.125 65.7188 16.125C67.875 16.125 69.6563 15.25 70.6563 13.6875L68.5313 12.4687C67.9688 13.4062 66.9688 13.9375 65.7188 13.9375C63.75 13.9375 62.4375 12.625 62.4375 10.6562C62.4375 8.68749 63.75 7.37499 65.7188 7.37499C66.9375 7.37499 67.9688 7.90624 68.5313 8.84374Z" + fill="currentColor" + /> + <path + d="M88.2188 1.75H85.7188V15.8125H88.2188V1.75Z" + fill="currentColor" + /> + <path + d="M40.1563 1.75H37.2813L31.7813 11.25L26.2813 1.75H23.375L31.7813 16.25L40.1563 1.75Z" + fill="currentColor" + /> + <path + d="M57.8438 8.0625C58.125 8.0625 58.4062 8.09375 58.6875 8.15625V5.5C56.5625 5.5625 54.5625 6.75 54.5625 8.21875V5.5H52.0625V15.8125H54.5625V11.3437C54.5625 9.40625 55.9062 8.0625 57.8438 8.0625Z" + fill="currentColor" + /> + </svg> ) } diff --git a/components/product/ProductSlider/ProductSlider.tsx b/components/product/ProductSlider/ProductSlider.tsx index 0756edcda..4ea7d2ec4 100644 --- a/components/product/ProductSlider/ProductSlider.tsx +++ b/components/product/ProductSlider/ProductSlider.tsx @@ -1,5 +1,12 @@ import { useKeenSlider } from 'keen-slider/react' -import React, { Children, FC, isValidElement, useState, useRef, useEffect } from 'react' +import React, { + Children, + FC, + isValidElement, + useState, + useRef, + useEffect, +} from 'react' import cn from 'classnames' import s from './ProductSlider.module.css' @@ -25,7 +32,7 @@ const ProductSlider: FC = ({ children }) => { const touchXPosition = event.touches[0].pageX // Size of the touch area const touchXRadius = event.touches[0].radiusX || 0 - + // We set a threshold (10px) on both sizes of the screen, // if the touch area overlaps with the screen edges // it's likely to trigger the navigation. We prevent the @@ -33,15 +40,20 @@ const ProductSlider: FC = ({ children }) => { if ( touchXPosition - touchXRadius < 10 || touchXPosition + touchXRadius > window.innerWidth - 10 - ) event.preventDefault() + ) + event.preventDefault() } - sliderContainerRef.current! - .addEventListener('touchstart', preventNavigation) + sliderContainerRef.current!.addEventListener( + 'touchstart', + preventNavigation + ) return () => { - sliderContainerRef.current! - .removeEventListener('touchstart', preventNavigation) + sliderContainerRef.current!.removeEventListener( + 'touchstart', + preventNavigation + ) } }, []) diff --git a/components/ui/Container/Container.tsx b/components/ui/Container/Container.tsx index 1897a4825..7b281a2e4 100644 --- a/components/ui/Container/Container.tsx +++ b/components/ui/Container/Container.tsx @@ -13,9 +13,9 @@ const Container: FC<Props> = ({ children, className, el = 'div', clean }) => { 'mx-auto max-w-8xl px-6': !clean, }) - let Component: React.ComponentType<React.HTMLAttributes< - HTMLDivElement - >> = el as any + let Component: React.ComponentType< + React.HTMLAttributes<HTMLDivElement> + > = el as any return <Component className={rootClassName}>{children}</Component> } diff --git a/framework/bigcommerce/README.md b/framework/bigcommerce/README.md index 7beee6c14..a847fb36d 100644 --- a/framework/bigcommerce/README.md +++ b/framework/bigcommerce/README.md @@ -1,27 +1,25 @@ +# Table of Contents -Table of Contents -================= - - * [BigCommerce Storefront Data Hooks](#bigcommerce-storefront-data-hooks) - * [Installation](#installation) - * [General Usage](#general-usage) - * [CommerceProvider](#commerceprovider) - * [useLogin hook](#uselogin-hook) - * [useLogout](#uselogout) - * [useCustomer](#usecustomer) - * [useSignup](#usesignup) - * [usePrice](#useprice) - * [Cart Hooks](#cart-hooks) - * [useCart](#usecart) - * [useAddItem](#useadditem) - * [useUpdateItem](#useupdateitem) - * [useRemoveItem](#useremoveitem) - * [Wishlist Hooks](#wishlist-hooks) - * [Product Hooks and API](#product-hooks-and-api) - * [useSearch](#usesearch) - * [getAllProducts](#getallproducts) - * [getProduct](#getproduct) - * [More](#more) +- [BigCommerce Storefront Data Hooks](#bigcommerce-storefront-data-hooks) + - [Installation](#installation) + - [General Usage](#general-usage) + - [CommerceProvider](#commerceprovider) + - [useLogin hook](#uselogin-hook) + - [useLogout](#uselogout) + - [useCustomer](#usecustomer) + - [useSignup](#usesignup) + - [usePrice](#useprice) + - [Cart Hooks](#cart-hooks) + - [useCart](#usecart) + - [useAddItem](#useadditem) + - [useUpdateItem](#useupdateitem) + - [useRemoveItem](#useremoveitem) + - [Wishlist Hooks](#wishlist-hooks) + - [Product Hooks and API](#product-hooks-and-api) + - [useSearch](#usesearch) + - [getAllProducts](#getallproducts) + - [getProduct](#getproduct) + - [More](#more) # BigCommerce Storefront Data Hooks @@ -235,7 +233,7 @@ import useUpdateItem from '@bigcommerce/storefront-data-hooks/cart/use-update-it const CartItem = ({ item }) => { const [quantity, setQuantity] = useState(item.quantity) const updateItem = useUpdateItem(item) - + const updateQuantity = async (e) => { const val = e.target.value await updateItem({ quantity: val }) @@ -264,7 +262,7 @@ import useRemoveItem from '@bigcommerce/storefront-data-hooks/cart/use-remove-it const RemoveButton = ({ item }) => { const removeItem = useRemoveItem() - + const handleRemove = async () => { await removeItem({ id: item.id }) } diff --git a/framework/bigcommerce/api/cart/index.ts b/framework/bigcommerce/api/cart/index.ts index 5ff2d975b..715480bd8 100644 --- a/framework/bigcommerce/api/cart/index.ts +++ b/framework/bigcommerce/api/cart/index.ts @@ -10,15 +10,15 @@ import updateItem from './handlers/update-item' import removeItem from './handlers/remove-item' type OptionSelections = { - option_id: Number - option_value: Number|String + option_id: Number + option_value: Number | String } export type ItemBody = { productId: number variantId: number - quantity?: number - optionSelections?: OptionSelections + quantity?: number + optionSelections?: OptionSelections } export type AddItemBody = { item: ItemBody } diff --git a/framework/bigcommerce/api/utils/parse-item.ts b/framework/bigcommerce/api/utils/parse-item.ts index 132f269f6..8a50881b1 100644 --- a/framework/bigcommerce/api/utils/parse-item.ts +++ b/framework/bigcommerce/api/utils/parse-item.ts @@ -10,5 +10,5 @@ export const parseCartItem = (item: ItemBody) => ({ quantity: item.quantity, product_id: item.productId, variant_id: item.variantId, - option_selections: item.optionSelections + option_selections: item.optionSelections, }) diff --git a/framework/bigcommerce/auth/login.ts b/framework/bigcommerce/auth/login.ts index adfa2d467..3fef29879 100644 --- a/framework/bigcommerce/auth/login.ts +++ b/framework/bigcommerce/auth/login.ts @@ -54,7 +54,7 @@ async function login({ if (process.env.NODE_ENV !== 'production') { cookie = cookie.replace('; Secure', '') // SameSite=none can't be set unless the cookie is Secure - // bc seems to sometimes send back SameSite=None rather than none so make + // bc seems to sometimes send back SameSite=None rather than none so make // this case insensitive cookie = cookie.replace(/; SameSite=none/gi, '; SameSite=lax') } diff --git a/framework/bigcommerce/common/get-page.ts b/framework/bigcommerce/common/get-page.ts index b65e340e1..932032efb 100644 --- a/framework/bigcommerce/common/get-page.ts +++ b/framework/bigcommerce/common/get-page.ts @@ -38,9 +38,9 @@ async function getPage({ config = getConfig(config) // RecursivePartial forces the method to check for every prop in the data, which is // required in case there's a custom `url` - const { data } = await config.storeApiFetch<RecursivePartial<{ data: Page[] }>>( - url || `/v3/content/pages?id=${variables.id}&include=body` - ) + const { data } = await config.storeApiFetch< + RecursivePartial<{ data: Page[] }> + >(url || `/v3/content/pages?id=${variables.id}&include=body`) const firstPage = data?.[0] const page = firstPage as RecursiveRequired<typeof firstPage> diff --git a/framework/bigcommerce/lib/immutability.ts b/framework/bigcommerce/lib/immutability.ts index dc0177f8a..488d3570f 100644 --- a/framework/bigcommerce/lib/immutability.ts +++ b/framework/bigcommerce/lib/immutability.ts @@ -1,18 +1,13 @@ +import update, { Context } from 'immutability-helper' -import update, { Context } from 'immutability-helper'; +const c = new Context() -const c = new Context(); +c.extend('$auto', function (value, object) { + return object ? c.update(object, value) : c.update({}, value) +}) -c.extend('$auto', function(value, object) { - return object ? - c.update(object, value): - c.update({}, value); -}); +c.extend('$autoArray', function (value, object) { + return object ? c.update(object, value) : c.update([], value) +}) -c.extend('$autoArray', function(value, object) { - return object ? - c.update(object, value): - c.update([], value); -}); - -export default c.update \ No newline at end of file +export default c.update diff --git a/lib/click-outside/click-outside.tsx b/lib/click-outside/click-outside.tsx index 268fc13d9..3c6b0ef28 100644 --- a/lib/click-outside/click-outside.tsx +++ b/lib/click-outside/click-outside.tsx @@ -7,32 +7,36 @@ interface ClickOutsideProps { children: any } -const ClickOutside = ({ active = true, onClick, children }: ClickOutsideProps) => { - const innerRef = useRef() +const ClickOutside = ({ + active = true, + onClick, + children, +}: ClickOutsideProps) => { + const innerRef = useRef() - const handleClick = (event: any) => { - if (!hasParent(event.target, innerRef?.current)) { - if (typeof onClick === 'function') { - onClick(event) - } + const handleClick = (event: any) => { + if (!hasParent(event.target, innerRef?.current)) { + if (typeof onClick === 'function') { + onClick(event) } } - - useEffect(() => { - if (active) { - document.addEventListener('mousedown', handleClick) - document.addEventListener('touchstart', handleClick) - } - - return () => { - if (active) { - document.removeEventListener('mousedown', handleClick) - document.removeEventListener('touchstart', handleClick) - } - } - }) - - return React.cloneElement(children, { ref: innerRef }) } + useEffect(() => { + if (active) { + document.addEventListener('mousedown', handleClick) + document.addEventListener('touchstart', handleClick) + } + + return () => { + if (active) { + document.removeEventListener('mousedown', handleClick) + document.removeEventListener('touchstart', handleClick) + } + } + }) + + return React.cloneElement(children, { ref: innerRef }) +} + export default ClickOutside diff --git a/package.json b/package.json index e9dc98176..ff492a35e 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "build": "next build", "start": "next start", "analyze": "BUNDLE_ANALYZE=both yarn build", + "prettier-fix": "prettier --write .", "find:unused": "next-unused", "generate": "graphql-codegen", "generate:definitions": "node framework/bigcommerce/scripts/generate-definitions.js" @@ -86,7 +87,6 @@ }, "lint-staged": { "**/*.{js,jsx,ts,tsx}": [ - "eslint", "prettier --write", "git add" ], From a49d0a18f8c182eeb40c77b2bb76b8c0ca7867c2 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 25 Jan 2021 13:51:15 -0500 Subject: [PATCH 048/221] Added more cart types --- framework/bigcommerce/api/cart/index.ts | 2 + framework/bigcommerce/api/catalog/products.ts | 2 +- framework/bigcommerce/cart/use-cart.tsx | 4 +- framework/bigcommerce/lib/normalize.ts | 23 +++++- framework/types.d.ts | 82 +++++++++++++++++++ 5 files changed, 107 insertions(+), 6 deletions(-) diff --git a/framework/bigcommerce/api/cart/index.ts b/framework/bigcommerce/api/cart/index.ts index 715480bd8..8988f3606 100644 --- a/framework/bigcommerce/api/cart/index.ts +++ b/framework/bigcommerce/api/cart/index.ts @@ -45,6 +45,8 @@ export type Cart = { gift_certificates: any[] physical_items: any[] } + created_time: string + discounts?: { id: number; discounted_amount: number }[] // TODO: add missing fields } diff --git a/framework/bigcommerce/api/catalog/products.ts b/framework/bigcommerce/api/catalog/products.ts index fe5b807c6..d13dcd3c3 100644 --- a/framework/bigcommerce/api/catalog/products.ts +++ b/framework/bigcommerce/api/catalog/products.ts @@ -15,7 +15,7 @@ export type SearchProductsData = { export type ProductsHandlers = { getProducts: BigcommerceHandler< SearchProductsData, - { search?: 'string'; category?: string; brand?: string; sort?: string } + { search?: string; category?: string; brand?: string; sort?: string } > } diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index c1a9bed7a..0d2070f63 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -3,14 +3,14 @@ import type { HookFetcher } from '@commerce/utils/types' import type { SwrOptions } from '@commerce/utils/use-data' import useResponse from '@commerce/utils/use-response' import useCommerceCart, { CartInput } from '@commerce/cart/use-cart' -import type { Cart as BigCommerceCart } from '../api/cart' +import type { Cart as BigcommerceCart } from '../api/cart' const defaultOpts = { url: '/api/bigcommerce/cart', method: 'GET', } -type UseCartResponse = BigCommerceCart & Cart +type UseCartResponse = BigcommerceCart & Cart export const fetcher: HookFetcher<UseCartResponse | null, CartInput> = ( options, diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index bbce17214..535e77501 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -1,4 +1,5 @@ -import update from '@framework/lib/immutability' +import type { Cart as BigCommerceCart } from '../api/cart' +import update from './immutability' function normalizeProductOption(productOption: any) { const { @@ -67,8 +68,24 @@ export function normalizeProduct(productNode: any): Product { }) } -export function normalizeCart(data: any): Cart { - return update(data, { +export function normalizeCart(data: BigCommerceCart): Cart { + const d: BaseCart = data && { + id: data.id, + customerId: String(data.customer_id), + email: data.email, + createdAt: data.created_time, + currency: data.currency, + taxesIncluded: data.tax_included, + lineItems: data.line_items as any, + lineItemsSubtotalPrice: data.base_amount, + subtotalPrice: data.base_amount + data.discount_amount, + totalPrice: data.cart_amount, + discounts: data.discounts?.map((discount) => ({ + value: discount.discounted_amount, + })), + } + + return update(data as any, { $auto: { items: { $set: data?.line_items?.physical_items?.map(itemsToProducts) }, subTotal: { $set: data?.base_amount }, diff --git a/framework/types.d.ts b/framework/types.d.ts index 7943cfaef..e5b1ed7c1 100644 --- a/framework/types.d.ts +++ b/framework/types.d.ts @@ -45,6 +45,88 @@ interface ProductPrice { extendedListPrice?: number } +interface DiscountBase { + // The value of the discount, can be an amount or percentage + value: number +} + +interface BaseLineItem { + id: string + variantId: string + name: string + quantity: number + discounts: DiscountBase[] + variant: BaseProductVariant +} + +interface Measurement { + value: number + unit: 'KILOGRAMS' | 'GRAMS' | 'POUNDS' | 'OUNCES' +} + +interface Image { + url: string + altText?: string + width?: number + height?: number +} + +interface BaseProductVariant { + id: string + // The SKU (stock keeping unit) associated with the product variant. + sku: string + // The product variant’s title, or the product's name. + name: string + // Indicates whether this product variant is in stock. + isInStock: boolean + // Indicates if the product variant is available for sale. + availableForSale: boolean + // Whether a customer needs to provide a shipping address when placing + // an order for the product variant. + requiresShipping: boolean + // Image associated with the product variant. Falls back to the product image + // if no image is available. + image: Image + // The variant's weight. If a weight was not explicitly specified on the + // variant this will be the product's weight. + weight?: Measurement + // The variant's height. If a height was not explicitly specified on the + // variant, this will be the product's height. + height?: Measurement + // The variant's width. If a width was not explicitly specified on the + // variant, this will be the product's width. + width?: Measurement + // The variant's depth. If a depth was not explicitly specified on the + // variant, this will be the product's depth. + depth?: Measurement +} + +// Shopping cart, a.k.a Checkout +interface BaseCart { + id: string + // ID of the customer to which the cart belongs. + customerId?: string + // The email assigned to this cart + email?: string + // The date and time when the cart was created. + createdAt: string + // The currency used for this cart + currency: { code: string } + // Specifies if taxes are included in the line items. + taxesIncluded: boolean + lineItems: Pick<Product, 'id' | 'name' | 'prices'> & CartItem[] + // The sum of all the prices of all the items in the cart. + // Duties, taxes, shipping and discounts excluded. + lineItemsSubtotalPrice: number + // Price of the cart before duties, shipping and taxes. + subtotalPrice: number + // The sum of all the prices of all the items in the cart. + // Duties, taxes and discounts included. + totalPrice: number + // Discounts that have been applied on the cart. + discounts?: DiscountBase[] +} + interface Cart extends Entity { id: string | undefined currency: { code: string } From 905d0324f0f7110a25fafad3a10ebe398d94da3c Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 25 Jan 2021 14:50:04 -0500 Subject: [PATCH 049/221] Moved types.d.ts to the commerce folder --- framework/{ => commerce}/types.d.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename framework/{ => commerce}/types.d.ts (100%) diff --git a/framework/types.d.ts b/framework/commerce/types.d.ts similarity index 100% rename from framework/types.d.ts rename to framework/commerce/types.d.ts From 1588fcf6bbba807deeddfcbc232344570a616ff0 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 25 Jan 2021 15:03:59 -0500 Subject: [PATCH 050/221] Minor changes --- framework/bigcommerce/cart/use-cart.tsx | 6 ++---- framework/bigcommerce/lib/normalize.ts | 4 ++-- framework/bigcommerce/types.d.ts | 1 + framework/commerce/utils/use-response.tsx | 2 +- tsconfig.json | 8 +------- 5 files changed, 7 insertions(+), 14 deletions(-) create mode 100644 framework/bigcommerce/types.d.ts diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index 0d2070f63..b3ea897c9 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -10,9 +10,7 @@ const defaultOpts = { method: 'GET', } -type UseCartResponse = BigcommerceCart & Cart - -export const fetcher: HookFetcher<UseCartResponse | null, CartInput> = ( +export const fetcher: HookFetcher<BigcommerceCart | null, CartInput> = ( options, { cartId }, fetch @@ -22,7 +20,7 @@ export const fetcher: HookFetcher<UseCartResponse | null, CartInput> = ( export function extendHook( customFetcher: typeof fetcher, - swrOptions?: SwrOptions<UseCartResponse | null, CartInput> + swrOptions?: SwrOptions<BigcommerceCart | null, CartInput> ) { const useCart = () => { const response = useCommerceCart(defaultOpts, [], customFetcher, { diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index 535e77501..3827989f1 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -1,4 +1,4 @@ -import type { Cart as BigCommerceCart } from '../api/cart' +import type { Cart as BigcommerceCart } from '../api/cart' import update from './immutability' function normalizeProductOption(productOption: any) { @@ -68,7 +68,7 @@ export function normalizeProduct(productNode: any): Product { }) } -export function normalizeCart(data: BigCommerceCart): Cart { +export function normalizeCart(data: BigcommerceCart): Cart { const d: BaseCart = data && { id: data.id, customerId: String(data.customer_id), diff --git a/framework/bigcommerce/types.d.ts b/framework/bigcommerce/types.d.ts new file mode 100644 index 000000000..d41b22f18 --- /dev/null +++ b/framework/bigcommerce/types.d.ts @@ -0,0 +1 @@ +interface Cart extends BaseCart {} diff --git a/framework/commerce/utils/use-response.tsx b/framework/commerce/utils/use-response.tsx index f3a1ed071..de1b5088c 100644 --- a/framework/commerce/utils/use-response.tsx +++ b/framework/commerce/utils/use-response.tsx @@ -25,7 +25,7 @@ const useResponse: UseResponse = (response, { descriptors, normalizer }) => { ? { data: { get() { - return normalizer(response.data) + return response.data && normalizer(response.data) }, enumerable: true, }, diff --git a/tsconfig.json b/tsconfig.json index 0992a8cd6..67de1ee36 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,12 +26,6 @@ "@framework": ["framework/bigcommerce"] } }, - "include": [ - "framework/types.d.ts", - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - "**/*.js" - ], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], "exclude": ["node_modules", "components/wishlist"] } From 7edd5aa1825320acc0c0ba2a0f0b2b3df9bf3509 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 25 Jan 2021 16:58:19 -0500 Subject: [PATCH 051/221] Moved normalizer to happen after fetch --- framework/bigcommerce/cart/use-cart.tsx | 10 ++++++---- framework/commerce/cart/use-cart.tsx | 10 +++++----- framework/commerce/utils/types.ts | 4 ++-- framework/commerce/utils/use-data.tsx | 14 +++++++------- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index b3ea897c9..9cdd8f56c 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -10,17 +10,20 @@ const defaultOpts = { method: 'GET', } -export const fetcher: HookFetcher<BigcommerceCart | null, CartInput> = ( +export const fetcher: HookFetcher<Cart | null, CartInput> = async ( options, { cartId }, fetch ) => { - return cartId ? fetch({ ...defaultOpts, ...options }) : null + const data = cartId + ? await fetch<BigcommerceCart>({ ...defaultOpts, ...options }) + : null + return data && normalizeCart(data) } export function extendHook( customFetcher: typeof fetcher, - swrOptions?: SwrOptions<BigcommerceCart | null, CartInput> + swrOptions?: SwrOptions<Cart | null, CartInput> ) { const useCart = () => { const response = useCommerceCart(defaultOpts, [], customFetcher, { @@ -28,7 +31,6 @@ export function extendHook( ...swrOptions, }) const res = useResponse(response, { - normalizer: normalizeCart, descriptors: { isEmpty: { get() { diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index c0fd10a5e..223767b6f 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -3,18 +3,18 @@ import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types' import useData, { ResponseState, SwrOptions } from '../utils/use-data' import { useCommerce } from '..' -export type CartResponse<Result> = ResponseState<Result> & { isEmpty?: boolean } +export type CartResponse<Data> = ResponseState<Data> & { isEmpty?: boolean } export type CartInput = { cartId: Cart['id'] } -export default function useCart<Result>( +export default function useCart<Data>( options: HookFetcherOptions, input: HookInput, - fetcherFn: HookFetcher<Result, CartInput>, - swrOptions?: SwrOptions<Result, CartInput> -): CartResponse<Result> { + fetcherFn: HookFetcher<Data, CartInput>, + swrOptions?: SwrOptions<Data, CartInput> +): CartResponse<Data> { const { cartCookie } = useCommerce() const fetcher: typeof fetcherFn = (options, input, fetch) => { input.cartId = Cookies.get(cartCookie) diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 2cd838964..483b0c73c 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -9,11 +9,11 @@ export type FetcherOptions = { body?: any } -export type HookFetcher<Result, Input = null> = ( +export type HookFetcher<Data, Input = null, Result = any> = ( options: HookFetcherOptions | null, input: Input, fetch: <T = Result>(options: FetcherOptions) => Promise<T> -) => Result | Promise<Result> +) => Data | Promise<Data> export type HookFetcherOptions = { query?: string diff --git a/framework/commerce/utils/use-data.tsx b/framework/commerce/utils/use-data.tsx index a921eb529..38af46a44 100644 --- a/framework/commerce/utils/use-data.tsx +++ b/framework/commerce/utils/use-data.tsx @@ -4,22 +4,22 @@ import defineProperty from './define-property' import { CommerceError } from './errors' import { useCommerce } from '..' -export type SwrOptions<Result, Input = null> = ConfigInterface< - Result, +export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< + Data, CommerceError, - HookFetcher<Result, Input> + HookFetcher<Data, Input, Result> > export type ResponseState<Result> = responseInterface<Result, CommerceError> & { isLoading: boolean } -export type UseData = <Result = any, Input = null>( +export type UseData = <Data = any, Input = null, Result = any>( options: HookFetcherOptions | (() => HookFetcherOptions | null), input: HookInput, - fetcherFn: HookFetcher<Result, Input>, - swrOptions?: SwrOptions<Result, Input> -) => ResponseState<Result> + fetcherFn: HookFetcher<Data, Input, Result>, + swrOptions?: SwrOptions<Data, Input, Result> +) => ResponseState<Data> const useData: UseData = (options, input, fetcherFn, swrOptions) => { const { fetcherRef } = useCommerce() From 61fa423673d3a22f15e713809fc18eb263b365f0 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Tue, 26 Jan 2021 17:10:33 -0500 Subject: [PATCH 052/221] updated useCart types --- framework/commerce/cart/use-cart.tsx | 4 ++-- framework/commerce/types.d.ts | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index 223767b6f..ecc537539 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -6,10 +6,10 @@ import { useCommerce } from '..' export type CartResponse<Data> = ResponseState<Data> & { isEmpty?: boolean } export type CartInput = { - cartId: Cart['id'] + cartId?: BaseCart['id'] } -export default function useCart<Data>( +export default function useCart<Data extends BaseCart | null>( options: HookFetcherOptions, input: HookInput, fetcherFn: HookFetcher<Data, CartInput>, diff --git a/framework/commerce/types.d.ts b/framework/commerce/types.d.ts index e5b1ed7c1..eb178619d 100644 --- a/framework/commerce/types.d.ts +++ b/framework/commerce/types.d.ts @@ -127,7 +127,8 @@ interface BaseCart { discounts?: DiscountBase[] } -interface Cart extends Entity { +// TODO: Remove this type in favor of BaseCart +interface Cart2 extends Entity { id: string | undefined currency: { code: string } taxIncluded?: boolean From 7cf1ace9fbd03271213993f6e1fc5b7a61b34673 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Wed, 27 Jan 2021 15:41:04 -0500 Subject: [PATCH 053/221] Updated normalizer for useData --- .../cart/CartSidebarView/CartSidebarView.tsx | 4 +-- framework/bigcommerce/cart/use-cart.tsx | 4 +-- framework/bigcommerce/lib/normalize.ts | 36 +++++++++++++------ framework/commerce/types.d.ts | 2 +- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index 5cd409559..0e0f5e5e9 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -92,11 +92,11 @@ const CartSidebarView: FC = () => { My Cart </h2> <ul className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accents-3 border-t border-accents-3"> - {data?.items?.map((item) => ( + {data!.lineItems.map((item) => ( <CartItem key={item.id} item={item} - currencyCode={data?.currency.code!} + currencyCode={data!.currency.code} /> ))} </ul> diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index 9cdd8f56c..75d81a1fb 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -34,9 +34,7 @@ export function extendHook( descriptors: { isEmpty: { get() { - return Object.values(response.data?.line_items ?? {}).every( - (items) => !items.length - ) + return (response.data?.lineItems.length ?? 0) <= 0 }, enumerable: true, }, diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index 3827989f1..742f750d5 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -69,14 +69,30 @@ export function normalizeProduct(productNode: any): Product { } export function normalizeCart(data: BigcommerceCart): Cart { - const d: BaseCart = data && { + // const d: BaseCart = data && { + // id: data.id, + // customerId: String(data.customer_id), + // email: data.email, + // createdAt: data.created_time, + // currency: data.currency, + // taxesIncluded: data.tax_included, + // lineItems: data.line_items as any, + // lineItemsSubtotalPrice: data.base_amount, + // subtotalPrice: data.base_amount + data.discount_amount, + // totalPrice: data.cart_amount, + // discounts: data.discounts?.map((discount) => ({ + // value: discount.discounted_amount, + // })), + // } + + return { id: data.id, customerId: String(data.customer_id), email: data.email, createdAt: data.created_time, currency: data.currency, taxesIncluded: data.tax_included, - lineItems: data.line_items as any, + lineItems: data.line_items.physical_items.map(itemsToProducts) as any, lineItemsSubtotalPrice: data.base_amount, subtotalPrice: data.base_amount + data.discount_amount, totalPrice: data.cart_amount, @@ -85,14 +101,14 @@ export function normalizeCart(data: BigcommerceCart): Cart { })), } - return update(data as any, { - $auto: { - items: { $set: data?.line_items?.physical_items?.map(itemsToProducts) }, - subTotal: { $set: data?.base_amount }, - total: { $set: data?.cart_amount }, - }, - $unset: ['created_time', 'coupons', 'line_items', 'email'], - }) + // return update(data as any, { + // $auto: { + // items: { $set: data?.line_items?.physical_items?.map(itemsToProducts) }, + // subTotal: { $set: data?.base_amount }, + // total: { $set: data?.cart_amount }, + // }, + // $unset: ['created_time', 'coupons', 'line_items', 'email'], + // }) } function itemsToProducts(item: any): CartItem { diff --git a/framework/commerce/types.d.ts b/framework/commerce/types.d.ts index eb178619d..c31784984 100644 --- a/framework/commerce/types.d.ts +++ b/framework/commerce/types.d.ts @@ -114,7 +114,7 @@ interface BaseCart { currency: { code: string } // Specifies if taxes are included in the line items. taxesIncluded: boolean - lineItems: Pick<Product, 'id' | 'name' | 'prices'> & CartItem[] + lineItems: BaseLineItem[] // The sum of all the prices of all the items in the cart. // Duties, taxes, shipping and discounts excluded. lineItemsSubtotalPrice: number From 3e27b80658e135f03ed57e86f983310758ebd37e Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 28 Jan 2021 15:54:17 -0500 Subject: [PATCH 054/221] Added new normalizer for the cart to the UI --- components/cart/CartItem/CartItem.tsx | 12 +-- .../cart/CartSidebarView/CartSidebarView.tsx | 9 +- framework/bigcommerce/lib/normalize.ts | 82 +++++-------------- framework/bigcommerce/types.d.ts | 6 +- framework/commerce/types.d.ts | 20 +++-- 5 files changed, 49 insertions(+), 80 deletions(-) diff --git a/components/cart/CartItem/CartItem.tsx b/components/cart/CartItem/CartItem.tsx index 91a70af0b..71ab58966 100644 --- a/components/cart/CartItem/CartItem.tsx +++ b/components/cart/CartItem/CartItem.tsx @@ -14,14 +14,14 @@ const Item = ({ currencyCode, ...rest }: { - item: CartItem + item: LineItem currencyCode: string }) => { const { closeSidebarIfPresent } = useUI() const { price } = usePrice({ - amount: item.extended_sale_price, - baseAmount: item.extended_list_price, + amount: item.variant.price * item.quantity, + baseAmount: item.variant.listPrice * item.quantity, currencyCode, }) @@ -87,13 +87,13 @@ const Item = ({ className={s.productImage} width={150} height={150} - src={item.images[0].url} - alt={item.images[0].alt} + src={item.variant.image.url} + alt={item.variant.image.altText} unoptimized /> </div> <div className="flex-1 flex flex-col text-base"> - <Link href={`/product/${item.url.split('/')[3]}`}> + <Link href={`/product/${item.path}`}> <span className="font-bold mb-5 text-lg cursor-pointer" onClick={() => closeSidebarIfPresent()} diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index 0e0f5e5e9..c25bd7c95 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -15,15 +15,14 @@ const CartSidebarView: FC = () => { const { price: subTotal } = usePrice( data && { - amount: Number(data.subTotal), - currencyCode: data.currency?.code || 'USD', + amount: Number(data.subtotalPrice), + currencyCode: data.currency.code, } ) - const { price: total } = usePrice( data && { - amount: Number(data.total), - currencyCode: data.currency?.code || 'USD', + amount: Number(data.totalPrice), + currencyCode: data.currency.code, } ) const handleClose = () => closeSidebar() diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index 742f750d5..ec0f73a86 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -69,22 +69,6 @@ export function normalizeProduct(productNode: any): Product { } export function normalizeCart(data: BigcommerceCart): Cart { - // const d: BaseCart = data && { - // id: data.id, - // customerId: String(data.customer_id), - // email: data.email, - // createdAt: data.created_time, - // currency: data.currency, - // taxesIncluded: data.tax_included, - // lineItems: data.line_items as any, - // lineItemsSubtotalPrice: data.base_amount, - // subtotalPrice: data.base_amount + data.discount_amount, - // totalPrice: data.cart_amount, - // discounts: data.discounts?.map((discount) => ({ - // value: discount.discounted_amount, - // })), - // } - return { id: data.id, customerId: String(data.customer_id), @@ -92,7 +76,7 @@ export function normalizeCart(data: BigcommerceCart): Cart { createdAt: data.created_time, currency: data.currency, taxesIncluded: data.tax_included, - lineItems: data.line_items.physical_items.map(itemsToProducts) as any, + lineItems: data.line_items.physical_items.map(normalizeLineItem), lineItemsSubtotalPrice: data.base_amount, subtotalPrice: data.base_amount + data.discount_amount, totalPrice: data.cart_amount, @@ -100,52 +84,28 @@ export function normalizeCart(data: BigcommerceCart): Cart { value: discount.discounted_amount, })), } - - // return update(data as any, { - // $auto: { - // items: { $set: data?.line_items?.physical_items?.map(itemsToProducts) }, - // subTotal: { $set: data?.base_amount }, - // total: { $set: data?.cart_amount }, - // }, - // $unset: ['created_time', 'coupons', 'line_items', 'email'], - // }) } -function itemsToProducts(item: any): CartItem { - const { - id, - name, - quantity, - product_id, - variant_id, - image_url, - list_price, - sale_price, - extended_list_price, - extended_sale_price, - ...rest - } = item - - return update(item, { - $auto: { - prices: { - $auto: { - listPrice: { $set: list_price }, - salePrice: { $set: sale_price }, - extendedListPrice: { $set: extended_list_price }, - extendedSalePrice: { $set: extended_sale_price }, - }, +function normalizeLineItem(item: any): LineItem { + return { + id: item.id, + variantId: String(item.variant_id), + name: item.name, + quantity: item.quantity, + variant: { + id: String(item.variant_id), + sku: item.sku, + name: item.name, + image: { + url: item.image_url, }, - images: { - $set: [ - { - alt: name, - url: image_url, - }, - ], - }, - productId: { $set: product_id }, - variantId: { $set: variant_id }, + requiresShipping: item.is_require_shipping, + price: item.sale_price, + listPrice: item.list_price, }, - }) + path: item.url.split('/')[3], + discounts: item.discounts.map((discount: any) => ({ + value: discount.discounted_amount, + })), + } } diff --git a/framework/bigcommerce/types.d.ts b/framework/bigcommerce/types.d.ts index d41b22f18..d987381e7 100644 --- a/framework/bigcommerce/types.d.ts +++ b/framework/bigcommerce/types.d.ts @@ -1 +1,5 @@ -interface Cart extends BaseCart {} +interface Cart extends BaseCart { + lineItems: LineItem[] +} + +interface LineItem extends BaseLineItem {} diff --git a/framework/commerce/types.d.ts b/framework/commerce/types.d.ts index c31784984..831af5517 100644 --- a/framework/commerce/types.d.ts +++ b/framework/commerce/types.d.ts @@ -56,6 +56,8 @@ interface BaseLineItem { name: string quantity: number discounts: DiscountBase[] + // A human-friendly unique string automatically generated from the product’s name + path: string variant: BaseProductVariant } @@ -77,16 +79,20 @@ interface BaseProductVariant { sku: string // The product variant’s title, or the product's name. name: string - // Indicates whether this product variant is in stock. - isInStock: boolean - // Indicates if the product variant is available for sale. - availableForSale: boolean - // Whether a customer needs to provide a shipping address when placing - // an order for the product variant. - requiresShipping: boolean // Image associated with the product variant. Falls back to the product image // if no image is available. image: Image + // Whether a customer needs to provide a shipping address when placing + // an order for the product variant. + requiresShipping: boolean + // The product variant’s price after all discounts are applied. + price: number + // Product variant’s price, as quoted by the manufacturer/distributor. + listPrice: number + // Indicates whether this product variant is in stock. + isInStock?: boolean + // Indicates if the product variant is available for sale. + availableForSale?: boolean // The variant's weight. If a weight was not explicitly specified on the // variant this will be the product's weight. weight?: Measurement From 59a4535f0e61c7caaeb3afa7dc1811d9ab8f34d3 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 28 Jan 2021 18:34:44 -0500 Subject: [PATCH 055/221] More corrections for useCart --- components/common/UserNav/UserNav.tsx | 8 +++----- framework/bigcommerce/README.md | 6 ++---- framework/commerce/types.d.ts | 6 +++--- pages/cart.tsx | 15 +++++++++------ 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/components/common/UserNav/UserNav.tsx b/components/common/UserNav/UserNav.tsx index 7b912422d..f8e6373d9 100644 --- a/components/common/UserNav/UserNav.tsx +++ b/components/common/UserNav/UserNav.tsx @@ -13,15 +13,13 @@ interface Props { className?: string } -const countItem = (count: number, item: any) => count + item.quantity -const countItems = (count: number, items: any[]) => - items.reduce(countItem, count) +const countItem = (count: number, item: LineItem) => count + item.quantity -const UserNav: FC<Props> = ({ className, children, ...props }) => { +const UserNav: FC<Props> = ({ className, children }) => { const { data } = useCart() const { data: customer } = useCustomer() const { toggleSidebar, closeSidebarIfPresent, openModal } = useUI() - const itemsCount = Object.values(data?.line_items ?? {}).reduce(countItems, 0) + const itemsCount = data?.lineItems.reduce(countItem, 0) ?? 0 return ( <nav className={cn(s.root, className)}> diff --git a/framework/bigcommerce/README.md b/framework/bigcommerce/README.md index a847fb36d..86fbac91d 100644 --- a/framework/bigcommerce/README.md +++ b/framework/bigcommerce/README.md @@ -191,13 +191,11 @@ Returns the current cart data for use ... import useCart from '@bigcommerce/storefront-data-hooks/cart/use-cart' -const countItem = (count: number, item: any) => count + item.quantity -const countItems = (count: number, items: any[]) => - items.reduce(countItem, count) +const countItem = (count: number, item: LineItem) => count + item.quantity const CartNumber = () => { const { data } = useCart() - const itemsCount = Object.values(data?.line_items ?? {}).reduce(countItems, 0) + const itemsCount = data?.lineItems.reduce(countItem, 0) ?? 0 return itemsCount > 0 ? <span>{itemsCount}</span> : null } diff --git a/framework/commerce/types.d.ts b/framework/commerce/types.d.ts index 831af5517..9bb996470 100644 --- a/framework/commerce/types.d.ts +++ b/framework/commerce/types.d.ts @@ -79,9 +79,6 @@ interface BaseProductVariant { sku: string // The product variant’s title, or the product's name. name: string - // Image associated with the product variant. Falls back to the product image - // if no image is available. - image: Image // Whether a customer needs to provide a shipping address when placing // an order for the product variant. requiresShipping: boolean @@ -89,6 +86,9 @@ interface BaseProductVariant { price: number // Product variant’s price, as quoted by the manufacturer/distributor. listPrice: number + // Image associated with the product variant. Falls back to the product image + // if no image is available. + image?: Image // Indicates whether this product variant is in stock. isInStock?: boolean // Indicates if the product variant is available for sale. diff --git a/pages/cart.tsx b/pages/cart.tsx index b7f2027d3..74c313ed9 100644 --- a/pages/cart.tsx +++ b/pages/cart.tsx @@ -28,14 +28,14 @@ export default function Cart() { const { price: subTotal } = usePrice( data && { - amount: Number(data.subTotal), - currencyCode: data.currency.code || 'USD', + amount: Number(data.subtotalPrice), + currencyCode: data.currency.code, } ) const { price: total } = usePrice( data && { - amount: Number(data.total), - currencyCode: data.currency?.code || 'USD', + amount: Number(data.totalPrice), + currencyCode: data.currency.code, } ) @@ -78,7 +78,7 @@ export default function Cart() { <Text variant="pageHeading">My Cart</Text> <Text variant="sectionHeading">Review your Order</Text> <ul className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accents-2 border-b border-accents-2"> - {data?.items.map((item) => ( + {data!.lineItems.map((item) => ( <CartItem key={item.id} item={item} @@ -93,7 +93,10 @@ export default function Cart() { </Text> <div className="flex py-6 space-x-6"> {[1, 2, 3, 4, 5, 6].map((x) => ( - <div className="border border-accents-3 w-full h-24 bg-accents-2 bg-opacity-50 transform cursor-pointer hover:scale-110 duration-75" /> + <div + key={x} + className="border border-accents-3 w-full h-24 bg-accents-2 bg-opacity-50 transform cursor-pointer hover:scale-110 duration-75" + /> ))} </div> </div> From f541a545f95ee771204cf0130e329b539385ef35 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 28 Jan 2021 18:37:07 -0500 Subject: [PATCH 056/221] Updated cart update hooks --- framework/bigcommerce/cart/use-add-item.tsx | 15 +++++++++++---- framework/bigcommerce/cart/use-cart.tsx | 2 +- framework/bigcommerce/cart/use-remove-item.tsx | 10 ++++++---- framework/bigcommerce/cart/use-update-item.tsx | 15 +++++++++++---- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/framework/bigcommerce/cart/use-add-item.tsx b/framework/bigcommerce/cart/use-add-item.tsx index ab8e634a2..38ac69650 100644 --- a/framework/bigcommerce/cart/use-add-item.tsx +++ b/framework/bigcommerce/cart/use-add-item.tsx @@ -2,8 +2,13 @@ import { useCallback } from 'react' import type { HookFetcher } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' import useCartAddItem from '@commerce/cart/use-add-item' -import type { ItemBody, AddItemBody } from '../api/cart' -import useCart, { Cart } from './use-cart' +import { normalizeCart } from '../lib/normalize' +import type { + ItemBody, + AddItemBody, + Cart as BigcommerceCart, +} from '../api/cart' +import useCart from './use-cart' const defaultOpts = { url: '/api/bigcommerce/cart', @@ -12,7 +17,7 @@ const defaultOpts = { export type AddItemInput = ItemBody -export const fetcher: HookFetcher<Cart, AddItemBody> = ( +export const fetcher: HookFetcher<Cart, AddItemBody> = async ( options, { item }, fetch @@ -26,11 +31,13 @@ export const fetcher: HookFetcher<Cart, AddItemBody> = ( }) } - return fetch({ + const data = await fetch<BigcommerceCart>({ ...defaultOpts, ...options, body: { item }, }) + + return normalizeCart(data) } export function extendHook(customFetcher: typeof fetcher) { diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index 75d81a1fb..8e008c65a 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -1,8 +1,8 @@ -import { normalizeCart } from '../lib/normalize' import type { HookFetcher } from '@commerce/utils/types' import type { SwrOptions } from '@commerce/utils/use-data' import useResponse from '@commerce/utils/use-response' import useCommerceCart, { CartInput } from '@commerce/cart/use-cart' +import { normalizeCart } from '../lib/normalize' import type { Cart as BigcommerceCart } from '../api/cart' const defaultOpts = { diff --git a/framework/bigcommerce/cart/use-remove-item.tsx b/framework/bigcommerce/cart/use-remove-item.tsx index e38c267af..6f25c0c9e 100644 --- a/framework/bigcommerce/cart/use-remove-item.tsx +++ b/framework/bigcommerce/cart/use-remove-item.tsx @@ -1,8 +1,9 @@ import { useCallback } from 'react' import { HookFetcher } from '@commerce/utils/types' import useCartRemoveItem from '@commerce/cart/use-remove-item' -import type { RemoveItemBody } from '../api/cart' -import useCart, { Cart } from './use-cart' +import { normalizeCart } from '../lib/normalize' +import type { RemoveItemBody, Cart as BigcommerceCart } from '../api/cart' +import useCart from './use-cart' const defaultOpts = { url: '/api/bigcommerce/cart', @@ -13,16 +14,17 @@ export type RemoveItemInput = { id: string } -export const fetcher: HookFetcher<Cart | null, RemoveItemBody> = ( +export const fetcher: HookFetcher<Cart | null, RemoveItemBody> = async ( options, { itemId }, fetch ) => { - return fetch({ + const data = await fetch<BigcommerceCart>({ ...defaultOpts, ...options, body: { itemId }, }) + return normalizeCart(data) } export function extendHook(customFetcher: typeof fetcher) { diff --git a/framework/bigcommerce/cart/use-update-item.tsx b/framework/bigcommerce/cart/use-update-item.tsx index ca5703505..88a5b8c9d 100644 --- a/framework/bigcommerce/cart/use-update-item.tsx +++ b/framework/bigcommerce/cart/use-update-item.tsx @@ -3,9 +3,14 @@ import debounce from 'lodash.debounce' import type { HookFetcher } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' import useCartUpdateItem from '@commerce/cart/use-update-item' -import type { ItemBody, UpdateItemBody } from '../api/cart' +import { normalizeCart } from '../lib/normalize' +import type { + ItemBody, + UpdateItemBody, + Cart as BigcommerceCart, +} from '../api/cart' import { fetcher as removeFetcher } from './use-remove-item' -import useCart, { Cart } from './use-cart' +import useCart from './use-cart' const defaultOpts = { url: '/api/bigcommerce/cart', @@ -14,7 +19,7 @@ const defaultOpts = { export type UpdateItemInput = Partial<{ id: string } & ItemBody> -export const fetcher: HookFetcher<Cart | null, UpdateItemBody> = ( +export const fetcher: HookFetcher<Cart | null, UpdateItemBody> = async ( options, { itemId, item }, fetch @@ -30,11 +35,13 @@ export const fetcher: HookFetcher<Cart | null, UpdateItemBody> = ( }) } - return fetch({ + const data = await fetch<BigcommerceCart>({ ...defaultOpts, ...options, body: { itemId, item }, }) + + return normalizeCart(data) } function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) { From 2ffe1ab71bee55b92a5e1c7af23824de6100c256 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 28 Jan 2021 19:00:26 -0500 Subject: [PATCH 057/221] Removed import --- pages/cart.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/pages/cart.tsx b/pages/cart.tsx index 74c313ed9..61b9f632e 100644 --- a/pages/cart.tsx +++ b/pages/cart.tsx @@ -8,7 +8,6 @@ import { Button } from '@components/ui' import { Bag, Cross, Check } from '@components/icons' import { CartItem } from '@components/cart' import { Text } from '@components/ui' -import products from '@framework/api/catalog/products' export async function getStaticProps({ preview, From b8a0bb5a21029cc037a8af228d1406226ee311b6 Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Fri, 29 Jan 2021 11:22:41 -0300 Subject: [PATCH 058/221] Progress --- CHANGELOG.md | 5 +++-- components/product/ProductView/ProductView.tsx | 5 ++--- .../wishlist/WishlistButton/WishlistButton.tsx | 6 +++--- tsconfig.json | 12 ++++++------ 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d1d95638..b21b673f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ## Changelog -- Select Variants Working -- Click on cart item title, closes the sidebar +- Select Variants Fully Working +- Click on the Cart Item Title, closes the sidebar. + diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index d3d11ea16..448dfb10a 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -4,9 +4,8 @@ import { NextSeo } from 'next-seo' import { FC, useState } from 'react' import s from './ProductView.module.css' -import { useUI } from '@components/ui' import { Swatch, ProductSlider } from '@components/product' -import { Button, Container, Text } from '@components/ui' +import { Button, Container, Text, useUI} from '@components/ui' import usePrice from '@framework/product/use-price' import { useAddItem } from '@framework/cart' @@ -100,8 +99,8 @@ const ProductView: FC<Props> = ({ product }) => { ))} </ProductSlider> </div> - </div> + </div> <div className={s.sidebar}> <section> {product.options?.map((opt) => ( diff --git a/components/wishlist/WishlistButton/WishlistButton.tsx b/components/wishlist/WishlistButton/WishlistButton.tsx index dced18a89..19ae3ec1a 100644 --- a/components/wishlist/WishlistButton/WishlistButton.tsx +++ b/components/wishlist/WishlistButton/WishlistButton.tsx @@ -1,12 +1,12 @@ import React, { FC, useState } from 'react' import cn from 'classnames' -import { Heart } from '@components/icons' import { useUI } from '@components/ui' -import useCustomer from '@framework/customer/use-customer' +import { Heart } from '@components/icons' import useAddItem from '@framework/wishlist/use-add-item' +import useWishlist from '@framework/wishlist/use-wishlist' +import useCustomer from '@framework/customer/use-customer' import useRemoveItem from '@framework/wishlist/use-remove-item' -import useWishlist from '@framework/wishlist/use-add-item' type Props = { productId: Product['id'] diff --git a/tsconfig.json b/tsconfig.json index 67de1ee36..480622bb4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,16 +16,16 @@ "jsx": "preserve", "paths": { "@lib/*": ["lib/*"], - "@assets/*": ["assets/*"], - "@config/*": ["config/*"], - "@components/*": ["components/*"], "@utils/*": ["utils/*"], - "@commerce/*": ["framework/commerce/*"], + "@config/*": ["config/*"], + "@assets/*": ["assets/*"], + "@components/*": ["components/*"], "@commerce": ["framework/commerce"], - "@framework/*": ["framework/bigcommerce/*"], + "@commerce/*": ["framework/commerce/*"], "@framework": ["framework/bigcommerce"] + "@framework/*": ["framework/bigcommerce/*"], } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], - "exclude": ["node_modules", "components/wishlist"] + "exclude": ["node_modules"] } From 1de3e76513a2f07c475298973c9ce452f77db7bc Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 29 Jan 2021 11:30:17 -0500 Subject: [PATCH 059/221] Switch away from global types --- components/cart/CartItem/CartItem.tsx | 1 + framework/bigcommerce/api/cart/index.ts | 5 +- framework/bigcommerce/types.d.ts | 5 -- framework/bigcommerce/types.ts | 11 +++ framework/commerce/cart/use-cart.tsx | 5 +- framework/commerce/types.d.ts | 99 ---------------------- framework/commerce/types.ts | 108 ++++++++++++++++++++++++ 7 files changed, 124 insertions(+), 110 deletions(-) delete mode 100644 framework/bigcommerce/types.d.ts create mode 100644 framework/bigcommerce/types.ts create mode 100644 framework/commerce/types.ts diff --git a/components/cart/CartItem/CartItem.tsx b/components/cart/CartItem/CartItem.tsx index 71ab58966..1e9062e91 100644 --- a/components/cart/CartItem/CartItem.tsx +++ b/components/cart/CartItem/CartItem.tsx @@ -5,6 +5,7 @@ import Link from 'next/link' import s from './CartItem.module.css' import { Trash, Plus, Minus } from '@components/icons' import { useUI } from '@components/ui/context' +import type { LineItem } from '@framework/types' import usePrice from '@framework/product/use-price' import useUpdateItem from '@framework/cart/use-update-item' import useRemoveItem from '@framework/cart/use-remove-item' diff --git a/framework/bigcommerce/api/cart/index.ts b/framework/bigcommerce/api/cart/index.ts index 8988f3606..007e56ace 100644 --- a/framework/bigcommerce/api/cart/index.ts +++ b/framework/bigcommerce/api/cart/index.ts @@ -53,10 +53,7 @@ export type Cart = { export type CartHandlers = { getCart: BigcommerceHandler<Cart, { cartId?: string }> addItem: BigcommerceHandler<Cart, { cartId?: string } & Partial<AddItemBody>> - updateItem: BigcommerceHandler< - Cart, - { cartId?: string } & Partial<UpdateItemBody> - > + updateItem: BigcommerceHandler<Cart, UpdateItemBody> removeItem: BigcommerceHandler< Cart, { cartId?: string } & Partial<RemoveItemBody> diff --git a/framework/bigcommerce/types.d.ts b/framework/bigcommerce/types.d.ts deleted file mode 100644 index d987381e7..000000000 --- a/framework/bigcommerce/types.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -interface Cart extends BaseCart { - lineItems: LineItem[] -} - -interface LineItem extends BaseLineItem {} diff --git a/framework/bigcommerce/types.ts b/framework/bigcommerce/types.ts new file mode 100644 index 000000000..fe1ca6c36 --- /dev/null +++ b/framework/bigcommerce/types.ts @@ -0,0 +1,11 @@ +import * as Core from '@commerce/types' + +export interface Cart extends Core.Cart { + lineItems: LineItem[] +} + +export interface LineItem extends Core.LineItem {} + +export interface UpdateLineItemBody extends Core.UpdateLineItemBody {} + +export interface UpdateLineItem extends Core.UpdateItemBody {} diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index ecc537539..94ccb73fc 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -1,15 +1,16 @@ import Cookies from 'js-cookie' import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types' import useData, { ResponseState, SwrOptions } from '../utils/use-data' +import type { Cart } from '../types' import { useCommerce } from '..' export type CartResponse<Data> = ResponseState<Data> & { isEmpty?: boolean } export type CartInput = { - cartId?: BaseCart['id'] + cartId?: Cart['id'] } -export default function useCart<Data extends BaseCart | null>( +export default function useCart<Data extends Cart | null>( options: HookFetcherOptions, input: HookInput, fetcherFn: HookFetcher<Data, CartInput>, diff --git a/framework/commerce/types.d.ts b/framework/commerce/types.d.ts index 9bb996470..9e69ec25d 100644 --- a/framework/commerce/types.d.ts +++ b/framework/commerce/types.d.ts @@ -45,105 +45,6 @@ interface ProductPrice { extendedListPrice?: number } -interface DiscountBase { - // The value of the discount, can be an amount or percentage - value: number -} - -interface BaseLineItem { - id: string - variantId: string - name: string - quantity: number - discounts: DiscountBase[] - // A human-friendly unique string automatically generated from the product’s name - path: string - variant: BaseProductVariant -} - -interface Measurement { - value: number - unit: 'KILOGRAMS' | 'GRAMS' | 'POUNDS' | 'OUNCES' -} - -interface Image { - url: string - altText?: string - width?: number - height?: number -} - -interface BaseProductVariant { - id: string - // The SKU (stock keeping unit) associated with the product variant. - sku: string - // The product variant’s title, or the product's name. - name: string - // Whether a customer needs to provide a shipping address when placing - // an order for the product variant. - requiresShipping: boolean - // The product variant’s price after all discounts are applied. - price: number - // Product variant’s price, as quoted by the manufacturer/distributor. - listPrice: number - // Image associated with the product variant. Falls back to the product image - // if no image is available. - image?: Image - // Indicates whether this product variant is in stock. - isInStock?: boolean - // Indicates if the product variant is available for sale. - availableForSale?: boolean - // The variant's weight. If a weight was not explicitly specified on the - // variant this will be the product's weight. - weight?: Measurement - // The variant's height. If a height was not explicitly specified on the - // variant, this will be the product's height. - height?: Measurement - // The variant's width. If a width was not explicitly specified on the - // variant, this will be the product's width. - width?: Measurement - // The variant's depth. If a depth was not explicitly specified on the - // variant, this will be the product's depth. - depth?: Measurement -} - -// Shopping cart, a.k.a Checkout -interface BaseCart { - id: string - // ID of the customer to which the cart belongs. - customerId?: string - // The email assigned to this cart - email?: string - // The date and time when the cart was created. - createdAt: string - // The currency used for this cart - currency: { code: string } - // Specifies if taxes are included in the line items. - taxesIncluded: boolean - lineItems: BaseLineItem[] - // The sum of all the prices of all the items in the cart. - // Duties, taxes, shipping and discounts excluded. - lineItemsSubtotalPrice: number - // Price of the cart before duties, shipping and taxes. - subtotalPrice: number - // The sum of all the prices of all the items in the cart. - // Duties, taxes and discounts included. - totalPrice: number - // Discounts that have been applied on the cart. - discounts?: DiscountBase[] -} - -// TODO: Remove this type in favor of BaseCart -interface Cart2 extends Entity { - id: string | undefined - currency: { code: string } - taxIncluded?: boolean - items: Pick<Product, 'id' | 'name' | 'prices'> & CartItem[] - subTotal: number | string - total: number | string - customerId: Customer['id'] -} - interface CartItem extends Entity { quantity: number productId: Product['id'] diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts new file mode 100644 index 000000000..3bca844e0 --- /dev/null +++ b/framework/commerce/types.ts @@ -0,0 +1,108 @@ +export interface Discount { + // The value of the discount, can be an amount or percentage + value: number +} + +export interface LineItem { + id: string + variantId: string + name: string + quantity: number + discounts: Discount[] + // A human-friendly unique string automatically generated from the product’s name + path: string + variant: ProductVariant +} + +export interface Measurement { + value: number + unit: 'KILOGRAMS' | 'GRAMS' | 'POUNDS' | 'OUNCES' +} + +export interface Image { + url: string + altText?: string + width?: number + height?: number +} + +export interface ProductVariant { + id: string + // The SKU (stock keeping unit) associated with the product variant. + sku: string + // The product variant’s title, or the product's name. + name: string + // Whether a customer needs to provide a shipping address when placing + // an order for the product variant. + requiresShipping: boolean + // The product variant’s price after all discounts are applied. + price: number + // Product variant’s price, as quoted by the manufacturer/distributor. + listPrice: number + // Image associated with the product variant. Falls back to the product image + // if no image is available. + image?: Image + // Indicates whether this product variant is in stock. + isInStock?: boolean + // Indicates if the product variant is available for sale. + availableForSale?: boolean + // The variant's weight. If a weight was not explicitly specified on the + // variant this will be the product's weight. + weight?: Measurement + // The variant's height. If a height was not explicitly specified on the + // variant, this will be the product's height. + height?: Measurement + // The variant's width. If a width was not explicitly specified on the + // variant, this will be the product's width. + width?: Measurement + // The variant's depth. If a depth was not explicitly specified on the + // variant, this will be the product's depth. + depth?: Measurement +} + +// Shopping cart, a.k.a Checkout +export interface Cart { + id: string + // ID of the customer to which the cart belongs. + customerId?: string + // The email assigned to this cart + email?: string + // The date and time when the cart was created. + createdAt: string + // The currency used for this cart + currency: { code: string } + // Specifies if taxes are included in the line items. + taxesIncluded: boolean + lineItems: LineItem[] + // The sum of all the prices of all the items in the cart. + // Duties, taxes, shipping and discounts excluded. + lineItemsSubtotalPrice: number + // Price of the cart before duties, shipping and taxes. + subtotalPrice: number + // The sum of all the prices of all the items in the cart. + // Duties, taxes and discounts included. + totalPrice: number + // Discounts that have been applied on the cart. + discounts?: Discount[] +} + +// interface OptionSelections { +// option_id: number +// option_value: number | string +// } + +export interface LineItemBody { + productId: string + variantId: string + quantity?: number + // optionSelections?: OptionSelections +} + +export interface UpdateLineItemBody { + itemId: string + item: LineItemBody +} + +export interface UpdateItemBody extends Partial<UpdateLineItemBody> { + cartId?: string +} From 172b413521589fd7210943d7c7b26ab48efe870b Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 29 Jan 2021 16:45:50 -0500 Subject: [PATCH 060/221] Making multiple changes --- .../api/cart/handlers/update-item.ts | 3 ++ framework/bigcommerce/api/cart/index.ts | 28 +----------- framework/bigcommerce/api/utils/parse-item.ts | 15 +++++-- .../bigcommerce/cart/use-update-item.tsx | 25 +++++++---- framework/bigcommerce/types.ts | 45 ++++++++++++++++++- framework/commerce/types.ts | 22 ++++----- 6 files changed, 87 insertions(+), 51 deletions(-) diff --git a/framework/bigcommerce/api/cart/handlers/update-item.ts b/framework/bigcommerce/api/cart/handlers/update-item.ts index df9ccaee8..b0ccc710b 100644 --- a/framework/bigcommerce/api/cart/handlers/update-item.ts +++ b/framework/bigcommerce/api/cart/handlers/update-item.ts @@ -14,6 +14,9 @@ const updateItem: CartHandlers['updateItem'] = async ({ }) } + console.log('ITEM', item) + console.log('AFTER', parseCartItem(item)) + const { data } = await config.storeApiFetch( `/v3/carts/${cartId}/items/${itemId}`, { diff --git a/framework/bigcommerce/api/cart/index.ts b/framework/bigcommerce/api/cart/index.ts index 007e56ace..0f0365cc3 100644 --- a/framework/bigcommerce/api/cart/index.ts +++ b/framework/bigcommerce/api/cart/index.ts @@ -8,6 +8,7 @@ import getCart from './handlers/get-cart' import addItem from './handlers/add-item' import updateItem from './handlers/update-item' import removeItem from './handlers/remove-item' +import type { Cart, UpdateCartItemHandlerBody } from '../../types' type OptionSelections = { option_id: Number @@ -23,37 +24,12 @@ export type ItemBody = { export type AddItemBody = { item: ItemBody } -export type UpdateItemBody = { itemId: string; item: ItemBody } - export type RemoveItemBody = { itemId: string } -// TODO: this type should match: -// https://developer.bigcommerce.com/api-reference/cart-checkout/server-server-cart-api/cart/getacart#responses -export type Cart = { - id: string - parent_id?: string - customer_id: number - email: string - currency: { code: string } - tax_included: boolean - base_amount: number - discount_amount: number - cart_amount: number - line_items: { - custom_items: any[] - digital_items: any[] - gift_certificates: any[] - physical_items: any[] - } - created_time: string - discounts?: { id: number; discounted_amount: number }[] - // TODO: add missing fields -} - export type CartHandlers = { getCart: BigcommerceHandler<Cart, { cartId?: string }> addItem: BigcommerceHandler<Cart, { cartId?: string } & Partial<AddItemBody>> - updateItem: BigcommerceHandler<Cart, UpdateItemBody> + updateItem: BigcommerceHandler<Cart, UpdateCartItemHandlerBody> removeItem: BigcommerceHandler< Cart, { cartId?: string } & Partial<RemoveItemBody> diff --git a/framework/bigcommerce/api/utils/parse-item.ts b/framework/bigcommerce/api/utils/parse-item.ts index 8a50881b1..dcc716c23 100644 --- a/framework/bigcommerce/api/utils/parse-item.ts +++ b/framework/bigcommerce/api/utils/parse-item.ts @@ -1,14 +1,21 @@ import type { ItemBody as WishlistItemBody } from '../wishlist' -import type { ItemBody } from '../cart' +import type { CartItemBody, OptionSelections } from '../../types' + +type BCCartItemBody = { + product_id: number + variant_id: number + quantity?: number + option_selections?: OptionSelections +} export const parseWishlistItem = (item: WishlistItemBody) => ({ product_id: item.productId, variant_id: item.variantId, }) -export const parseCartItem = (item: ItemBody) => ({ +export const parseCartItem = (item: CartItemBody): BCCartItemBody => ({ quantity: item.quantity, - product_id: item.productId, - variant_id: item.variantId, + product_id: Number(item.productId), + variant_id: Number(item.variantId), option_selections: item.optionSelections, }) diff --git a/framework/bigcommerce/cart/use-update-item.tsx b/framework/bigcommerce/cart/use-update-item.tsx index 88a5b8c9d..b3a3e0566 100644 --- a/framework/bigcommerce/cart/use-update-item.tsx +++ b/framework/bigcommerce/cart/use-update-item.tsx @@ -5,10 +5,11 @@ import { CommerceError } from '@commerce/utils/errors' import useCartUpdateItem from '@commerce/cart/use-update-item' import { normalizeCart } from '../lib/normalize' import type { - ItemBody, - UpdateItemBody, - Cart as BigcommerceCart, -} from '../api/cart' + Cart, + BigcommerceCart, + UpdateCartItemBody, + UpdateCartItemInput, +} from '../types' import { fetcher as removeFetcher } from './use-remove-item' import useCart from './use-cart' @@ -17,9 +18,7 @@ const defaultOpts = { method: 'PUT', } -export type UpdateItemInput = Partial<{ id: string } & ItemBody> - -export const fetcher: HookFetcher<Cart | null, UpdateItemBody> = async ( +export const fetcher: HookFetcher<Cart | null, UpdateCartItemBody> = async ( options, { itemId, item }, fetch @@ -47,13 +46,21 @@ export const fetcher: HookFetcher<Cart | null, UpdateItemBody> = async ( function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) { const useUpdateItem = (item?: any) => { const { mutate } = useCart() - const fn = useCartUpdateItem<Cart | null, UpdateItemBody>( + const fn = useCartUpdateItem<Cart | null, UpdateCartItemBody>( defaultOpts, customFetcher ) return useCallback( - debounce(async (input: UpdateItemInput) => { + debounce(async (input: UpdateCartItemInput) => { + console.log('INPUT', input, { + itemId: input.id ?? item?.id, + item: { + productId: input.productId ?? item?.product_id, + variantId: input.productId ?? item?.variant_id, + quantity: input.quantity, + }, + }) const data = await fn({ itemId: input.id ?? item?.id, item: { diff --git a/framework/bigcommerce/types.ts b/framework/bigcommerce/types.ts index fe1ca6c36..d9b81acea 100644 --- a/framework/bigcommerce/types.ts +++ b/framework/bigcommerce/types.ts @@ -1,11 +1,52 @@ import * as Core from '@commerce/types' +// TODO: this type should match: +// https://developer.bigcommerce.com/api-reference/cart-checkout/server-server-cart-api/cart/getacart#responses +export type BigcommerceCart = { + id: string + parent_id?: string + customer_id: number + email: string + currency: { code: string } + tax_included: boolean + base_amount: number + discount_amount: number + cart_amount: number + line_items: { + custom_items: any[] + digital_items: any[] + gift_certificates: any[] + physical_items: any[] + } + created_time: string + discounts?: { id: number; discounted_amount: number }[] + // TODO: add missing fields +} + export interface Cart extends Core.Cart { lineItems: LineItem[] } export interface LineItem extends Core.LineItem {} -export interface UpdateLineItemBody extends Core.UpdateLineItemBody {} +/** + * Cart mutations + */ -export interface UpdateLineItem extends Core.UpdateItemBody {} +export type OptionSelections = { + option_id: number + option_value: number | string +} + +export interface CartItemBody extends Core.CartItemBody { + optionSelections?: OptionSelections +} + +export interface UpdateCartItemBody extends Core.UpdateCartItemBody { + item: CartItemBody +} + +export interface UpdateCartItemInput extends Core.UpdateCartItemInput {} + +export interface UpdateCartItemHandlerBody + extends Core.UpdateCartItemHandlerBody {} diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index 3bca844e0..8ffbf5421 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -86,23 +86,25 @@ export interface Cart { discounts?: Discount[] } -// interface OptionSelections { -// option_id: number -// option_value: number | string -// } - -export interface LineItemBody { +// Base cart item body used for cart mutations +export interface CartItemBody { productId: string variantId: string quantity?: number - // optionSelections?: OptionSelections } -export interface UpdateLineItemBody { +// Body by the update operation +export interface UpdateCartItemBody { itemId: string - item: LineItemBody + item: CartItemBody } -export interface UpdateItemBody extends Partial<UpdateLineItemBody> { +// Input expected by the `useUpdateItem` hook +export interface UpdateCartItemInput extends Partial<CartItemBody> { + id?: string +} + +// Body expected by the update operation handler +export interface UpdateCartItemHandlerBody extends Partial<UpdateCartItemBody> { cartId?: string } From e5f0809070b28126da1e36b1b7031f44d4d8b59a Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 29 Jan 2021 19:24:10 -0500 Subject: [PATCH 061/221] Improved types for operations --- .../bigcommerce/cart/use-update-item.tsx | 60 +++++++++++-------- framework/bigcommerce/lib/normalize.ts | 3 +- framework/bigcommerce/types.ts | 4 +- framework/commerce/types.ts | 7 ++- framework/commerce/utils/errors.ts | 8 +++ framework/commerce/utils/types.ts | 6 +- 6 files changed, 54 insertions(+), 34 deletions(-) diff --git a/framework/bigcommerce/cart/use-update-item.tsx b/framework/bigcommerce/cart/use-update-item.tsx index b3a3e0566..6592b090f 100644 --- a/framework/bigcommerce/cart/use-update-item.tsx +++ b/framework/bigcommerce/cart/use-update-item.tsx @@ -1,14 +1,15 @@ import { useCallback } from 'react' import debounce from 'lodash.debounce' import type { HookFetcher } from '@commerce/utils/types' -import { CommerceError } from '@commerce/utils/errors' +import { ValidationError } from '@commerce/utils/errors' import useCartUpdateItem from '@commerce/cart/use-update-item' import { normalizeCart } from '../lib/normalize' import type { - Cart, - BigcommerceCart, UpdateCartItemBody, UpdateCartItemInput, + Cart, + BigcommerceCart, + LineItem, } from '../types' import { fetcher as removeFetcher } from './use-remove-item' import useCart from './use-cart' @@ -29,12 +30,12 @@ export const fetcher: HookFetcher<Cart | null, UpdateCartItemBody> = async ( return removeFetcher(null, { itemId }, fetch) } } else if (item.quantity) { - throw new CommerceError({ + throw new ValidationError({ message: 'The item quantity has to be a valid integer', }) } - const data = await fetch<BigcommerceCart>({ + const data = await fetch<BigcommerceCart, UpdateCartItemBody>({ ...defaultOpts, ...options, body: { itemId, item }, @@ -44,7 +45,9 @@ export const fetcher: HookFetcher<Cart | null, UpdateCartItemBody> = async ( } function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) { - const useUpdateItem = (item?: any) => { + const useUpdateItem = <T extends LineItem | undefined = undefined>( + item?: T + ) => { const { mutate } = useCart() const fn = useCartUpdateItem<Cart | null, UpdateCartItemBody>( defaultOpts, @@ -52,26 +55,31 @@ function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) { ) return useCallback( - debounce(async (input: UpdateCartItemInput) => { - console.log('INPUT', input, { - itemId: input.id ?? item?.id, - item: { - productId: input.productId ?? item?.product_id, - variantId: input.productId ?? item?.variant_id, - quantity: input.quantity, - }, - }) - const data = await fn({ - itemId: input.id ?? item?.id, - item: { - productId: input.productId ?? item?.product_id, - variantId: input.productId ?? item?.variant_id, - quantity: input.quantity, - }, - }) - await mutate(data, false) - return data - }, cfg?.wait ?? 500), + debounce( + async ( + input: T extends LineItem + ? Partial<UpdateCartItemInput> + : UpdateCartItemInput + ) => { + const itemId = input.id ?? item?.id + const productId = input.productId ?? item?.productId + const variantId = input.productId ?? item?.variantId + + if (!itemId || !productId || !variantId) { + throw new ValidationError({ + message: 'Invalid input used for this operation', + }) + } + + const data = await fn({ + itemId, + item: { productId, variantId, quantity: input.quantity }, + }) + await mutate(data, false) + return data + }, + cfg?.wait ?? 500 + ), [fn, mutate] ) } diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index ec0f73a86..89aed2c38 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -1,4 +1,4 @@ -import type { Cart as BigcommerceCart } from '../api/cart' +import type { Cart, BigcommerceCart, LineItem } from '../types' import update from './immutability' function normalizeProductOption(productOption: any) { @@ -90,6 +90,7 @@ function normalizeLineItem(item: any): LineItem { return { id: item.id, variantId: String(item.variant_id), + productId: String(item.product_id), name: item.name, quantity: item.quantity, variant: { diff --git a/framework/bigcommerce/types.ts b/framework/bigcommerce/types.ts index d9b81acea..766f2dbe9 100644 --- a/framework/bigcommerce/types.ts +++ b/framework/bigcommerce/types.ts @@ -39,6 +39,7 @@ export type OptionSelections = { } export interface CartItemBody extends Core.CartItemBody { + productId: string // The product id is always required for BC optionSelections?: OptionSelections } @@ -46,7 +47,8 @@ export interface UpdateCartItemBody extends Core.UpdateCartItemBody { item: CartItemBody } -export interface UpdateCartItemInput extends Core.UpdateCartItemInput {} +export interface UpdateCartItemInput + extends Core.UpdateCartItemInput<CartItemBody> {} export interface UpdateCartItemHandlerBody extends Core.UpdateCartItemHandlerBody {} diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index 8ffbf5421..c662077a1 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -6,6 +6,7 @@ export interface Discount { export interface LineItem { id: string variantId: string + productId: string name: string quantity: number discounts: Discount[] @@ -88,8 +89,8 @@ export interface Cart { // Base cart item body used for cart mutations export interface CartItemBody { - productId: string variantId: string + productId?: string quantity?: number } @@ -100,8 +101,8 @@ export interface UpdateCartItemBody { } // Input expected by the `useUpdateItem` hook -export interface UpdateCartItemInput extends Partial<CartItemBody> { - id?: string +export type UpdateCartItemInput<T extends CartItemBody> = T & { + id: string } // Body expected by the update operation handler diff --git a/framework/commerce/utils/errors.ts b/framework/commerce/utils/errors.ts index 76f899ab7..f4ab9fb9a 100644 --- a/framework/commerce/utils/errors.ts +++ b/framework/commerce/utils/errors.ts @@ -26,6 +26,14 @@ export class CommerceError extends Error { } } +// Used for errors that come from a bad implementation of the hooks +export class ValidationError extends CommerceError { + constructor(options: ErrorProps) { + super(options) + this.code = 'validation_error' + } +} + export class FetcherError extends CommerceError { status: number diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 483b0c73c..010205f62 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -1,18 +1,18 @@ // Core fetcher added by CommerceProvider export type Fetcher<T> = (options: FetcherOptions) => T | Promise<T> -export type FetcherOptions = { +export type FetcherOptions<Body = any> = { url?: string query?: string method?: string variables?: any - body?: any + body?: Body } export type HookFetcher<Data, Input = null, Result = any> = ( options: HookFetcherOptions | null, input: Input, - fetch: <T = Result>(options: FetcherOptions) => Promise<T> + fetch: <T = Result, Body = any>(options: FetcherOptions<Body>) => Promise<T> ) => Data | Promise<Data> export type HookFetcherOptions = { From 023058dc0ca72cf5e47dd2dd7e1b7dd5f8cc63b4 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Tue, 2 Feb 2021 17:49:05 -0500 Subject: [PATCH 062/221] Moved and updated cart types --- .../bigcommerce/api/cart/handlers/get-cart.ts | 5 +- .../api/cart/handlers/update-item.ts | 3 -- framework/bigcommerce/api/cart/index.ts | 17 +++++-- framework/bigcommerce/cart/use-cart.tsx | 2 +- .../bigcommerce/cart/use-update-item.tsx | 48 +++++++++---------- framework/bigcommerce/types.ts | 10 ++-- framework/commerce/cart/use-cart.tsx | 1 + framework/commerce/cart/use-update-item.tsx | 6 +++ framework/commerce/types.ts | 17 +++---- 9 files changed, 59 insertions(+), 50 deletions(-) diff --git a/framework/bigcommerce/api/cart/handlers/get-cart.ts b/framework/bigcommerce/api/cart/handlers/get-cart.ts index 9fb42d730..031bbb63f 100644 --- a/framework/bigcommerce/api/cart/handlers/get-cart.ts +++ b/framework/bigcommerce/api/cart/handlers/get-cart.ts @@ -1,6 +1,7 @@ +import type { BigcommerceCart } from '../../../types' import { BigcommerceApiError } from '../../utils/errors' import getCartCookie from '../../utils/get-cart-cookie' -import type { Cart, CartHandlers } from '..' +import type { CartHandlers } from '../' // Return current cart info const getCart: CartHandlers['getCart'] = async ({ @@ -8,7 +9,7 @@ const getCart: CartHandlers['getCart'] = async ({ body: { cartId }, config, }) => { - let result: { data?: Cart } = {} + let result: { data?: BigcommerceCart } = {} if (cartId) { try { diff --git a/framework/bigcommerce/api/cart/handlers/update-item.ts b/framework/bigcommerce/api/cart/handlers/update-item.ts index b0ccc710b..df9ccaee8 100644 --- a/framework/bigcommerce/api/cart/handlers/update-item.ts +++ b/framework/bigcommerce/api/cart/handlers/update-item.ts @@ -14,9 +14,6 @@ const updateItem: CartHandlers['updateItem'] = async ({ }) } - console.log('ITEM', item) - console.log('AFTER', parseCartItem(item)) - const { data } = await config.storeApiFetch( `/v3/carts/${cartId}/items/${itemId}`, { diff --git a/framework/bigcommerce/api/cart/index.ts b/framework/bigcommerce/api/cart/index.ts index 0f0365cc3..7b5be31c3 100644 --- a/framework/bigcommerce/api/cart/index.ts +++ b/framework/bigcommerce/api/cart/index.ts @@ -8,7 +8,11 @@ import getCart from './handlers/get-cart' import addItem from './handlers/add-item' import updateItem from './handlers/update-item' import removeItem from './handlers/remove-item' -import type { Cart, UpdateCartItemHandlerBody } from '../../types' +import type { + BigcommerceCart, + GetCartHandlerBody, + UpdateCartItemHandlerBody, +} from '../../types' type OptionSelections = { option_id: Number @@ -27,11 +31,14 @@ export type AddItemBody = { item: ItemBody } export type RemoveItemBody = { itemId: string } export type CartHandlers = { - getCart: BigcommerceHandler<Cart, { cartId?: string }> - addItem: BigcommerceHandler<Cart, { cartId?: string } & Partial<AddItemBody>> - updateItem: BigcommerceHandler<Cart, UpdateCartItemHandlerBody> + getCart: BigcommerceHandler<BigcommerceCart, GetCartHandlerBody> + addItem: BigcommerceHandler< + BigcommerceCart, + { cartId?: string } & Partial<AddItemBody> + > + updateItem: BigcommerceHandler<BigcommerceCart, UpdateCartItemHandlerBody> removeItem: BigcommerceHandler< - Cart, + BigcommerceCart, { cartId?: string } & Partial<RemoveItemBody> > } diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index 8e008c65a..afa37ec98 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -3,7 +3,7 @@ import type { SwrOptions } from '@commerce/utils/use-data' import useResponse from '@commerce/utils/use-response' import useCommerceCart, { CartInput } from '@commerce/cart/use-cart' import { normalizeCart } from '../lib/normalize' -import type { Cart as BigcommerceCart } from '../api/cart' +import type { Cart, BigcommerceCart } from '../types' const defaultOpts = { url: '/api/bigcommerce/cart', diff --git a/framework/bigcommerce/cart/use-update-item.tsx b/framework/bigcommerce/cart/use-update-item.tsx index 6592b090f..d1870c818 100644 --- a/framework/bigcommerce/cart/use-update-item.tsx +++ b/framework/bigcommerce/cart/use-update-item.tsx @@ -2,11 +2,12 @@ import { useCallback } from 'react' import debounce from 'lodash.debounce' import type { HookFetcher } from '@commerce/utils/types' import { ValidationError } from '@commerce/utils/errors' -import useCartUpdateItem from '@commerce/cart/use-update-item' +import useCartUpdateItem, { + UpdateItemInput as UseUpdateItemInput, +} from '@commerce/cart/use-update-item' import { normalizeCart } from '../lib/normalize' import type { UpdateCartItemBody, - UpdateCartItemInput, Cart, BigcommerceCart, LineItem, @@ -19,6 +20,10 @@ const defaultOpts = { method: 'PUT', } +export type UpdateItemInput<T = any> = T extends LineItem + ? Partial<UseUpdateItemInput<LineItem>> + : UseUpdateItemInput<LineItem> + export const fetcher: HookFetcher<Cart | null, UpdateCartItemBody> = async ( options, { itemId, item }, @@ -55,31 +60,24 @@ function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) { ) return useCallback( - debounce( - async ( - input: T extends LineItem - ? Partial<UpdateCartItemInput> - : UpdateCartItemInput - ) => { - const itemId = input.id ?? item?.id - const productId = input.productId ?? item?.productId - const variantId = input.productId ?? item?.variantId + debounce(async (input: UpdateItemInput<T>) => { + const itemId = input.id ?? item?.id + const productId = input.productId ?? item?.productId + const variantId = input.productId ?? item?.variantId - if (!itemId || !productId || !variantId) { - throw new ValidationError({ - message: 'Invalid input used for this operation', - }) - } - - const data = await fn({ - itemId, - item: { productId, variantId, quantity: input.quantity }, + if (!itemId || !productId || !variantId) { + throw new ValidationError({ + message: 'Invalid input used for this operation', }) - await mutate(data, false) - return data - }, - cfg?.wait ?? 500 - ), + } + + const data = await fn({ + itemId, + item: { productId, variantId, quantity: input.quantity }, + }) + await mutate(data, false) + return data + }, cfg?.wait ?? 500), [fn, mutate] ) } diff --git a/framework/bigcommerce/types.ts b/framework/bigcommerce/types.ts index 766f2dbe9..26d093bfe 100644 --- a/framework/bigcommerce/types.ts +++ b/framework/bigcommerce/types.ts @@ -43,12 +43,10 @@ export interface CartItemBody extends Core.CartItemBody { optionSelections?: OptionSelections } -export interface UpdateCartItemBody extends Core.UpdateCartItemBody { - item: CartItemBody -} +export interface GetCartHandlerBody extends Core.GetCartHandlerBody {} -export interface UpdateCartItemInput - extends Core.UpdateCartItemInput<CartItemBody> {} +export interface UpdateCartItemBody + extends Core.UpdateCartItemBody<CartItemBody> {} export interface UpdateCartItemHandlerBody - extends Core.UpdateCartItemHandlerBody {} + extends Core.UpdateCartItemHandlerBody<CartItemBody> {} diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index 94ccb73fc..0a7ba49ee 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -6,6 +6,7 @@ import { useCommerce } from '..' export type CartResponse<Data> = ResponseState<Data> & { isEmpty?: boolean } +// Input expected by the `useCart` hook export type CartInput = { cartId?: Cart['id'] } diff --git a/framework/commerce/cart/use-update-item.tsx b/framework/commerce/cart/use-update-item.tsx index 1c6261054..e1adcb5fb 100644 --- a/framework/commerce/cart/use-update-item.tsx +++ b/framework/commerce/cart/use-update-item.tsx @@ -1,4 +1,10 @@ import useAction from '../utils/use-action' +import type { CartItemBody } from '../types' + +// Input expected by the action returned by the `useUpdateItem` hook +export type UpdateItemInput<T extends CartItemBody> = T & { + id: string +} const useUpdateItem = useAction diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index c662077a1..61bf0d5e7 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -94,18 +94,19 @@ export interface CartItemBody { quantity?: number } -// Body by the update operation -export interface UpdateCartItemBody { - itemId: string - item: CartItemBody +// Body used by the `getCart` operation handler +export interface GetCartHandlerBody { + cartId?: string } -// Input expected by the `useUpdateItem` hook -export type UpdateCartItemInput<T extends CartItemBody> = T & { - id: string +// Body used by the update operation +export interface UpdateCartItemBody<T extends CartItemBody> { + itemId: string + item: T } // Body expected by the update operation handler -export interface UpdateCartItemHandlerBody extends Partial<UpdateCartItemBody> { +export interface UpdateCartItemHandlerBody<T extends CartItemBody> + extends Partial<UpdateCartItemBody<T>> { cartId?: string } From 5cfa8241f66e6b952e3e6601e76a375c34a2257f Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Tue, 2 Feb 2021 20:16:47 -0500 Subject: [PATCH 063/221] Updated the useAddItem and useRemoveItem hooks --- framework/bigcommerce/api/cart/index.ts | 30 ++--------- framework/bigcommerce/cart/use-add-item.tsx | 19 ++++--- .../bigcommerce/cart/use-remove-item.tsx | 52 +++++++++++++------ framework/bigcommerce/types.ts | 10 ++++ framework/commerce/cart/use-add-item.tsx | 4 ++ framework/commerce/cart/use-remove-item.tsx | 5 ++ framework/commerce/types.ts | 29 ++++++++++- 7 files changed, 98 insertions(+), 51 deletions(-) diff --git a/framework/bigcommerce/api/cart/index.ts b/framework/bigcommerce/api/cart/index.ts index 7b5be31c3..4ee668895 100644 --- a/framework/bigcommerce/api/cart/index.ts +++ b/framework/bigcommerce/api/cart/index.ts @@ -11,42 +11,22 @@ import removeItem from './handlers/remove-item' import type { BigcommerceCart, GetCartHandlerBody, + AddCartItemHandlerBody, UpdateCartItemHandlerBody, + RemoveCartItemHandlerBody, } from '../../types' -type OptionSelections = { - option_id: Number - option_value: Number | String -} - -export type ItemBody = { - productId: number - variantId: number - quantity?: number - optionSelections?: OptionSelections -} - -export type AddItemBody = { item: ItemBody } - -export type RemoveItemBody = { itemId: string } - export type CartHandlers = { getCart: BigcommerceHandler<BigcommerceCart, GetCartHandlerBody> - addItem: BigcommerceHandler< - BigcommerceCart, - { cartId?: string } & Partial<AddItemBody> - > + addItem: BigcommerceHandler<BigcommerceCart, AddCartItemHandlerBody> updateItem: BigcommerceHandler<BigcommerceCart, UpdateCartItemHandlerBody> - removeItem: BigcommerceHandler< - BigcommerceCart, - { cartId?: string } & Partial<RemoveItemBody> - > + removeItem: BigcommerceHandler<BigcommerceCart, RemoveCartItemHandlerBody> } const METHODS = ['GET', 'POST', 'PUT', 'DELETE'] // TODO: a complete implementation should have schema validation for `req.body` -const cartApi: BigcommerceApiHandler<Cart, CartHandlers> = async ( +const cartApi: BigcommerceApiHandler<BigcommerceCart, CartHandlers> = async ( req, res, config, diff --git a/framework/bigcommerce/cart/use-add-item.tsx b/framework/bigcommerce/cart/use-add-item.tsx index 38ac69650..c66ee462a 100644 --- a/framework/bigcommerce/cart/use-add-item.tsx +++ b/framework/bigcommerce/cart/use-add-item.tsx @@ -1,13 +1,16 @@ import { useCallback } from 'react' import type { HookFetcher } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' -import useCartAddItem from '@commerce/cart/use-add-item' +import useCartAddItem, { + AddItemInput as UseAddItemInput, +} from '@commerce/cart/use-add-item' import { normalizeCart } from '../lib/normalize' import type { - ItemBody, - AddItemBody, - Cart as BigcommerceCart, -} from '../api/cart' + AddCartItemBody, + Cart, + BigcommerceCart, + CartItemBody, +} from '../types' import useCart from './use-cart' const defaultOpts = { @@ -15,9 +18,9 @@ const defaultOpts = { method: 'POST', } -export type AddItemInput = ItemBody +export type AddItemInput = UseAddItemInput<CartItemBody> -export const fetcher: HookFetcher<Cart, AddItemBody> = async ( +export const fetcher: HookFetcher<Cart, AddCartItemBody> = async ( options, { item }, fetch @@ -31,7 +34,7 @@ export const fetcher: HookFetcher<Cart, AddItemBody> = async ( }) } - const data = await fetch<BigcommerceCart>({ + const data = await fetch<BigcommerceCart, AddCartItemBody>({ ...defaultOpts, ...options, body: { item }, diff --git a/framework/bigcommerce/cart/use-remove-item.tsx b/framework/bigcommerce/cart/use-remove-item.tsx index 6f25c0c9e..c8cdaeb0d 100644 --- a/framework/bigcommerce/cart/use-remove-item.tsx +++ b/framework/bigcommerce/cart/use-remove-item.tsx @@ -1,8 +1,16 @@ import { useCallback } from 'react' import { HookFetcher } from '@commerce/utils/types' -import useCartRemoveItem from '@commerce/cart/use-remove-item' +import { ValidationError } from '@commerce/utils/errors' +import useCartRemoveItem, { + RemoveItemInput as UseRemoveItemInput, +} from '@commerce/cart/use-remove-item' import { normalizeCart } from '../lib/normalize' -import type { RemoveItemBody, Cart as BigcommerceCart } from '../api/cart' +import type { + RemoveCartItemBody, + Cart, + BigcommerceCart, + LineItem, +} from '../types' import useCart from './use-cart' const defaultOpts = { @@ -10,11 +18,15 @@ const defaultOpts = { method: 'DELETE', } -export type RemoveItemInput = { - id: string -} +export type RemoveItemFn<T = any> = T extends LineItem + ? (input?: RemoveItemInput<T>) => Promise<Cart | null> + : (input: RemoveItemInput<T>) => Promise<Cart | null> -export const fetcher: HookFetcher<Cart | null, RemoveItemBody> = async ( +export type RemoveItemInput<T = any> = T extends LineItem + ? Partial<UseRemoveItemInput> + : UseRemoveItemInput + +export const fetcher: HookFetcher<Cart | null, RemoveCartItemBody> = async ( options, { itemId }, fetch @@ -28,21 +40,29 @@ export const fetcher: HookFetcher<Cart | null, RemoveItemBody> = async ( } export function extendHook(customFetcher: typeof fetcher) { - const useRemoveItem = (item?: any) => { + const useRemoveItem = <T extends LineItem | undefined = undefined>( + item?: T + ) => { const { mutate } = useCart() - const fn = useCartRemoveItem<Cart | null, RemoveItemBody>( + const fn = useCartRemoveItem<Cart | null, RemoveCartItemBody>( defaultOpts, customFetcher ) + const removeItem: RemoveItemFn<LineItem> = async (input) => { + const itemId = input?.id ?? item?.id - return useCallback( - async function removeItem(input: RemoveItemInput) { - const data = await fn({ itemId: input.id ?? item?.id }) - await mutate(data, false) - return data - }, - [fn, mutate] - ) + if (!itemId) { + throw new ValidationError({ + message: 'Invalid input used for this operation', + }) + } + + const data = await fn({ itemId }) + await mutate(data, false) + return data + } + + return useCallback(removeItem as RemoveItemFn<T>, [fn, mutate]) } useRemoveItem.extend = extendHook diff --git a/framework/bigcommerce/types.ts b/framework/bigcommerce/types.ts index 26d093bfe..90afb425d 100644 --- a/framework/bigcommerce/types.ts +++ b/framework/bigcommerce/types.ts @@ -45,8 +45,18 @@ export interface CartItemBody extends Core.CartItemBody { export interface GetCartHandlerBody extends Core.GetCartHandlerBody {} +export interface AddCartItemBody extends Core.AddCartItemBody<CartItemBody> {} + +export interface AddCartItemHandlerBody + extends Core.AddCartItemHandlerBody<CartItemBody> {} + export interface UpdateCartItemBody extends Core.UpdateCartItemBody<CartItemBody> {} export interface UpdateCartItemHandlerBody extends Core.UpdateCartItemHandlerBody<CartItemBody> {} + +export interface RemoveCartItemBody extends Core.RemoveCartItemBody {} + +export interface RemoveCartItemHandlerBody + extends Core.RemoveCartItemHandlerBody {} diff --git a/framework/commerce/cart/use-add-item.tsx b/framework/commerce/cart/use-add-item.tsx index f6c069f2b..2f6422ab3 100644 --- a/framework/commerce/cart/use-add-item.tsx +++ b/framework/commerce/cart/use-add-item.tsx @@ -1,4 +1,8 @@ import useAction from '../utils/use-action' +import type { CartItemBody } from '../types' + +// Input expected by the action returned by the `useAddItem` hook +export type AddItemInput<T extends CartItemBody> = T const useAddItem = useAction diff --git a/framework/commerce/cart/use-remove-item.tsx b/framework/commerce/cart/use-remove-item.tsx index dfa60c363..8a63b1b73 100644 --- a/framework/commerce/cart/use-remove-item.tsx +++ b/framework/commerce/cart/use-remove-item.tsx @@ -1,5 +1,10 @@ import useAction from '../utils/use-action' +// Input expected by the action returned by the `useRemoveItem` hook +export interface RemoveItemInput { + id: string +} + const useRemoveItem = useAction export default useRemoveItem diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index 61bf0d5e7..cc04f52e1 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -87,6 +87,10 @@ export interface Cart { discounts?: Discount[] } +/** + * Cart mutations + */ + // Base cart item body used for cart mutations export interface CartItemBody { variantId: string @@ -99,14 +103,35 @@ export interface GetCartHandlerBody { cartId?: string } -// Body used by the update operation +// Body used by the add item to cart operation +export interface AddCartItemBody<T extends CartItemBody> { + item: T +} + +// Body expected by the add item to cart operation handler +export interface AddCartItemHandlerBody<T extends CartItemBody> + extends Partial<AddCartItemBody<T>> { + cartId?: string +} + +// Body used by the update cart item operation export interface UpdateCartItemBody<T extends CartItemBody> { itemId: string item: T } -// Body expected by the update operation handler +// Body expected by the update cart item operation handler export interface UpdateCartItemHandlerBody<T extends CartItemBody> extends Partial<UpdateCartItemBody<T>> { cartId?: string } + +// Body used by the remove cart item operation +export interface RemoveCartItemBody { + itemId: string +} + +// Body expected by the remove cart item operation handler +export interface RemoveCartItemHandlerBody extends Partial<RemoveCartItemBody> { + cartId?: string +} From 56ff15c91d66a5c7de9c433789281117850c1003 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Tue, 2 Feb 2021 20:18:31 -0500 Subject: [PATCH 064/221] Minor life improvement --- components/cart/CartItem/CartItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/cart/CartItem/CartItem.tsx b/components/cart/CartItem/CartItem.tsx index 1e9062e91..3f6a43103 100644 --- a/components/cart/CartItem/CartItem.tsx +++ b/components/cart/CartItem/CartItem.tsx @@ -63,7 +63,7 @@ const Item = ({ try { // If this action succeeds then there's no need to do `setRemoving(true)` // because the component will be removed from the view - await removeItem({ id: String(item.id) }) + await removeItem(item) } catch (error) { setRemoving(false) } From 1898f094bc9b87fb9f651c3ad56181e15f050705 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Wed, 3 Feb 2021 17:14:19 -0500 Subject: [PATCH 065/221] Minor change --- components/wishlist/WishlistCard/WishlistCard.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/wishlist/WishlistCard/WishlistCard.tsx b/components/wishlist/WishlistCard/WishlistCard.tsx index 82147f575..d1a9403b3 100644 --- a/components/wishlist/WishlistCard/WishlistCard.tsx +++ b/components/wishlist/WishlistCard/WishlistCard.tsx @@ -42,8 +42,8 @@ const WishlistCard: FC<Props> = ({ product }) => { setLoading(true) try { await addItem({ - productId: Number(product.id), - variantId: Number(product.variants[0].id), + productId: product.id, + variantId: product.variants[0].id, }) openSidebar() setLoading(false) From 14c3f961b3c229ed77327a28382acefd0afe798a Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 4 Feb 2021 13:23:44 +0200 Subject: [PATCH 066/221] Implement Shopify Provider --- .../cart/CartSidebarView/CartSidebarView.tsx | 2 +- .../HomeAllProductsGrid.tsx | 16 +- components/common/Navbar/Navbar.tsx | 3 + .../ProductCard/ProductCard.module.css | 2 +- framework/bigcommerce/README.md | 1 + framework/shopify/README.md | 260 + .../shopify/api/customers/handlers/login.ts | 38 + framework/shopify/api/customers/index.ts | 1 + framework/shopify/api/customers/login.ts | 1 + framework/shopify/api/customers/logout.ts | 1 + framework/shopify/api/customers/signup.ts | 1 + framework/shopify/api/index.ts | 54 + framework/shopify/api/wishlist/index.tsx | 2 + framework/shopify/auth/use-login.tsx | 53 + framework/shopify/auth/use-logout.tsx | 13 + framework/shopify/auth/use-signup.tsx | 13 + framework/shopify/cart/index.ts | 3 + framework/shopify/cart/use-add-item.tsx | 70 + framework/shopify/cart/use-cart.tsx | 67 + framework/shopify/cart/use-remove-item.tsx | 72 + framework/shopify/cart/use-update-item.tsx | 85 + .../shopify/cart/utils/checkout-create.ts | 20 + .../shopify/cart/utils/checkout-to-cart.ts | 57 + framework/shopify/cart/utils/index.ts | 2 + framework/shopify/common/get-all-pages.ts | 34 + framework/shopify/common/get-page.ts | 27 + framework/shopify/common/get-site-info.ts | 31 + framework/shopify/customer/use-customer.tsx | 32 + framework/shopify/index.tsx | 78 + .../shopify/product/get-all-collections.ts | 29 + .../shopify/product/get-all-product-paths.ts | 34 + framework/shopify/product/get-all-products.ts | 39 + framework/shopify/product/get-product.ts | 37 + framework/shopify/product/use-price.tsx | 2 + framework/shopify/product/use-search.tsx | 86 + framework/shopify/schema.d.ts | 4985 +++++++++ framework/shopify/schema.graphql | 9631 +++++++++++++++++ framework/shopify/utils/fetch-graphql-api.ts | 38 + framework/shopify/utils/fetch.ts | 3 + framework/shopify/utils/get-checkout-id.ts | 8 + .../shopify/utils/get-search-variables.ts | 15 + framework/shopify/utils/get-sort-variables.ts | 32 + .../utils/mutations/checkout-create.ts | 15 + .../utils/mutations/checkout-line-item-add.ts | 16 + .../mutations/checkout-line-item-remove.ts | 19 + .../mutations/checkout-line-item-update.ts | 16 + framework/shopify/utils/mutations/index.ts | 4 + .../queries/get-all-collections-query.ts | 14 + .../utils/queries/get-all-pages-query.ts | 17 + .../queries/get-all-products-paths-query.ts | 16 + .../utils/queries/get-all-products-query.ts | 47 + .../utils/queries/get-checkout-query.ts | 40 + .../utils/queries/get-product-query.ts | 59 + framework/shopify/utils/queries/index.ts | 6 + .../shopify/utils/to-commerce-products.ts | 96 + framework/shopify/wishlist/use-add-item.tsx | 13 + .../shopify/wishlist/use-remove-item.tsx | 17 + framework/shopify/wishlist/use-wishlist.tsx | 45 + next.config.js | 2 +- pages/search.tsx | 1 + tsconfig.json | 4 +- 61 files changed, 16405 insertions(+), 20 deletions(-) create mode 100644 framework/shopify/README.md create mode 100644 framework/shopify/api/customers/handlers/login.ts create mode 100644 framework/shopify/api/customers/index.ts create mode 100644 framework/shopify/api/customers/login.ts create mode 100644 framework/shopify/api/customers/logout.ts create mode 100644 framework/shopify/api/customers/signup.ts create mode 100644 framework/shopify/api/index.ts create mode 100644 framework/shopify/api/wishlist/index.tsx create mode 100644 framework/shopify/auth/use-login.tsx create mode 100644 framework/shopify/auth/use-logout.tsx create mode 100644 framework/shopify/auth/use-signup.tsx create mode 100644 framework/shopify/cart/index.ts create mode 100644 framework/shopify/cart/use-add-item.tsx create mode 100644 framework/shopify/cart/use-cart.tsx create mode 100644 framework/shopify/cart/use-remove-item.tsx create mode 100644 framework/shopify/cart/use-update-item.tsx create mode 100644 framework/shopify/cart/utils/checkout-create.ts create mode 100644 framework/shopify/cart/utils/checkout-to-cart.ts create mode 100644 framework/shopify/cart/utils/index.ts create mode 100644 framework/shopify/common/get-all-pages.ts create mode 100644 framework/shopify/common/get-page.ts create mode 100644 framework/shopify/common/get-site-info.ts create mode 100644 framework/shopify/customer/use-customer.tsx create mode 100644 framework/shopify/index.tsx create mode 100644 framework/shopify/product/get-all-collections.ts create mode 100644 framework/shopify/product/get-all-product-paths.ts create mode 100644 framework/shopify/product/get-all-products.ts create mode 100644 framework/shopify/product/get-product.ts create mode 100644 framework/shopify/product/use-price.tsx create mode 100644 framework/shopify/product/use-search.tsx create mode 100644 framework/shopify/schema.d.ts create mode 100644 framework/shopify/schema.graphql create mode 100644 framework/shopify/utils/fetch-graphql-api.ts create mode 100644 framework/shopify/utils/fetch.ts create mode 100644 framework/shopify/utils/get-checkout-id.ts create mode 100644 framework/shopify/utils/get-search-variables.ts create mode 100644 framework/shopify/utils/get-sort-variables.ts create mode 100644 framework/shopify/utils/mutations/checkout-create.ts create mode 100644 framework/shopify/utils/mutations/checkout-line-item-add.ts create mode 100644 framework/shopify/utils/mutations/checkout-line-item-remove.ts create mode 100644 framework/shopify/utils/mutations/checkout-line-item-update.ts create mode 100644 framework/shopify/utils/mutations/index.ts create mode 100644 framework/shopify/utils/queries/get-all-collections-query.ts create mode 100644 framework/shopify/utils/queries/get-all-pages-query.ts create mode 100644 framework/shopify/utils/queries/get-all-products-paths-query.ts create mode 100644 framework/shopify/utils/queries/get-all-products-query.ts create mode 100644 framework/shopify/utils/queries/get-checkout-query.ts create mode 100644 framework/shopify/utils/queries/get-product-query.ts create mode 100644 framework/shopify/utils/queries/index.ts create mode 100644 framework/shopify/utils/to-commerce-products.ts create mode 100644 framework/shopify/wishlist/use-add-item.tsx create mode 100644 framework/shopify/wishlist/use-remove-item.tsx create mode 100644 framework/shopify/wishlist/use-wishlist.tsx diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index c25bd7c95..394f89414 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -122,7 +122,7 @@ const CartSidebarView: FC = () => { <span>{total}</span> </div> </div> - <Button href="/checkout" Component="a" width="100%"> + <Button href={data.webUrl} Component="a" width="100%"> Proceed to Checkout </Button> </div> diff --git a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx index d55f51f7a..c7f07bf48 100644 --- a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx +++ b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx @@ -19,7 +19,7 @@ const Head: FC<Props> = ({ categories, brands, products = [] }) => { <ul className="mb-10"> <li className="py-1 text-base font-bold tracking-wide"> <Link href={getCategoryPath('')}> - <a>All Categories</a> + <a>All Collections</a> </Link> </li> {categories.map((cat: any) => ( @@ -30,20 +30,6 @@ const Head: FC<Props> = ({ categories, brands, products = [] }) => { </li> ))} </ul> - <ul className=""> - <li className="py-1 text-base font-bold tracking-wide"> - <Link href={getDesignerPath('')}> - <a>All Designers</a> - </Link> - </li> - {brands.flatMap(({ node }: any) => ( - <li key={node.path} className="py-1 text-accents-8 text-base"> - <Link href={getDesignerPath(node.path)}> - <a>{node.name}</a> - </Link> - </li> - ))} - </ul> </div> </div> <div className="flex-1"> diff --git a/components/common/Navbar/Navbar.tsx b/components/common/Navbar/Navbar.tsx index b2a372f66..fcf9f1e10 100644 --- a/components/common/Navbar/Navbar.tsx +++ b/components/common/Navbar/Navbar.tsx @@ -25,6 +25,9 @@ const Navbar: FC = () => ( <Link href="/search?q=accessories"> <a className={s.link}>Accessories</a> </Link> + <Link href="/search?q=shoes"> + <a className={s.link}>Shoes</a> + </Link> </nav> </div> diff --git a/components/product/ProductCard/ProductCard.module.css b/components/product/ProductCard/ProductCard.module.css index 1484cfaa4..082b22e56 100644 --- a/components/product/ProductCard/ProductCard.module.css +++ b/components/product/ProductCard/ProductCard.module.css @@ -132,5 +132,5 @@ } .productImage { - @apply transform transition-transform duration-500 object-cover scale-120; + @apply transform transition-transform duration-500 object-cover; } diff --git a/framework/bigcommerce/README.md b/framework/bigcommerce/README.md index 86fbac91d..9792bcdb0 100644 --- a/framework/bigcommerce/README.md +++ b/framework/bigcommerce/README.md @@ -329,6 +329,7 @@ const SearchPage = ({ searchString, category, brand, sortStr }) => { const { data } = useSearch({ search: searchString || '', categoryId: category?.entityId, + categorySlug: category?.slug, brandId: brand?.entityId, sort: sortStr || '', }) diff --git a/framework/shopify/README.md b/framework/shopify/README.md new file mode 100644 index 000000000..fc6a70ce3 --- /dev/null +++ b/framework/shopify/README.md @@ -0,0 +1,260 @@ +## Table of Contents + +- [Getting Started](#getting-started) + - [Modifications](#modifications) + - [Adding item to Cart](#adding-item-to-cart) + - [Proceed to Checkout](#proceed-to-checkout) +- [General Usage](#general-usage) + - [CommerceProvider](#commerceprovider) + - [useCommerce](#usecommerce) +- [Hooks](#hooks) + - [usePrice](#useprice) + - [useAddItem](#useadditem) + - [useRemoveItem](#useremoveitem) + - [useUpdateItem](#useupdateitem) +- [APIs](#apis) + - [getProduct](#getproduct) + - [getAllProducts](#getallproducts) + - [getAllCollections](#getallcollections) + - [getAllPages](#getallpages) + +# Shopify Storefront Data Hooks + +Collection of hooks and data fetching functions to integrate Shopify in a React application. Designed to work with [Next.js Commerce](https://demo.vercel.store/). + +## Getting Started + +1. Install dependencies: + +``` +yarn install shopify-buy +yarn install -D @types/shopify-buy +``` + +3. Environment variables need to be set: + +``` +SHOPIFY_STORE_DOMAIN= +SHOPIFY_STOREFRONT_ACCESS_TOKEN= +NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN= +NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN= +``` + +4. Point the framework to `shopify` by updating `tsconfig.json`: + +``` +"@framework/*": ["framework/shopify/*"], +"@framework": ["framework/shopify"] +``` + +### Modifications + +These modifications are temporarily until contributions are made to remove them. + +#### Adding item to Cart + +```js +// components/product/ProductView/ProductView.tsx +const ProductView: FC<Props> = ({ product }) => { + const addToCart = async () => { + setLoading(true) + try { + await addItem({ + productId: product.id, + variantId: variant ? variant.id : product.variants[0].id, + }) + openSidebar() + setLoading(false) + } catch (err) { + setLoading(false) + } + } +} +``` + +#### Proceed to Checkout + +```js +// components/cart/CartSidebarView/CartSidebarView.tsx +import { useCommerce } from '@framework' + +const CartSidebarView: FC = () => { + const { checkout } = useCommerce() + return ( + <Button href={checkout.webUrl} Component="a" width="100%"> + Proceed to Checkout + </Button> + ) +} +``` + +## General Usage + +### CommerceProvider + +Provider component that creates the commerce context for children. + +```js +import { CommerceProvider } from '@framework' + +const App = ({ children }) => { + return <CommerceProvider locale={locale}>{children}</CommerceProvider> +} + +export default App +``` + +### useCommerce + +Returns the configs that are defined in the nearest `CommerceProvider`. Also provides access to Shopify's `checkout` and `shop`. + +```js +import { useCommerce } from 'nextjs-commerce-shopify' + +const { checkout, shop } = useCommerce() +``` + +- `checkout`: The information required to checkout items and pay ([Documentation](https://shopify.dev/docs/storefront-api/reference/checkouts/checkout)). +- `shop`: Represents a collection of the general settings and information about the shop ([Documentation](https://shopify.dev/docs/storefront-api/reference/online-store/shop/index)). + +## Hooks + +### usePrice + +Display the product variant price according to currency and locale. + +```js +import usePrice from '@framework/product/use-price' + +const { price } = usePrice({ + amount, +}) +``` + +Takes in either `amount` or `variant`: + +- `amount`: A price value for a particular item if the amount is known. +- `variant`: A shopify product variant. Price will be extracted from the variant. + +### useAddItem + +```js +import { useAddItem } from '@framework/cart' + +const AddToCartButton = ({ variantId, quantity }) => { + const addItem = useAddItem() + + const addToCart = async () => { + await addItem({ + variantId, + }) + } + + return <button onClick={addToCart}>Add To Cart</button> +} +``` + +### useRemoveItem + +```js +import { useRemoveItem } from '@framework/cart' + +const RemoveButton = ({ item }) => { + const removeItem = useRemoveItem() + + const handleRemove = async () => { + await removeItem({ id: item.id }) + } + + return <button onClick={handleRemove}>Remove</button> +} +``` + +### useUpdateItem + +```js +import { useUpdateItem } from '@framework/cart' + +const CartItem = ({ item }) => { + const [quantity, setQuantity] = useState(item.quantity) + const updateItem = useUpdateItem(item) + + const updateQuantity = async (e) => { + const val = e.target.value + await updateItem({ quantity: val }) + } + + return ( + <input + type="number" + max={99} + min={0} + value={quantity} + onChange={updateQuantity} + /> + ) +} +``` + +## APIs + +Collections of APIs to fetch data from a Shopify store. + +The data is fetched using the [Shopify JavaScript Buy SDK](https://github.com/Shopify/js-buy-sdk#readme). Read the [Shopify Storefront API reference](https://shopify.dev/docs/storefront-api/reference) for more information. + +### getProduct + +Get a single product by its `handle`. + +```js +import getProduct from '@framework/product/get-product' +import { getConfig } from '@framework/api' + +const config = getConfig() + +const product = await getProduct({ + variables: { slug }, + config, +}) +``` + +### getAllProducts + +```js +import getAllProducts from '@framework/product/get-all-products' +import { getConfig } from '@framework/api' + +const config = getConfig() + +const { products } = await getAllProducts({ + variables: { first: 12 }, + config, +}) +``` + +### getAllCollections + +```js +import getAllCollections from '@framework/product/get-all-collections' +import { getConfig } from '@framework/api' + +const config = getConfig() + +const collections = await getAllCollections({ + config, +}) +``` + +### getAllPages + +```js +import getAllPages from '@framework/common/get-all-pages' +import { getConfig } from '@framework/api' + +const config = getConfig() + +const pages = await getAllPages({ + variables: { first: 12 }, + config, +}) +``` diff --git a/framework/shopify/api/customers/handlers/login.ts b/framework/shopify/api/customers/handlers/login.ts new file mode 100644 index 000000000..22231e06e --- /dev/null +++ b/framework/shopify/api/customers/handlers/login.ts @@ -0,0 +1,38 @@ +import { FetcherError } from '@commerce/utils/errors' +import type { LoginHandlers } from '../login' + +const loginHandler: LoginHandlers['login'] = async ({ + res, + body: { email, password }, + config, +}) => { + if (!(email && password)) { + return res.status(400).json({ + data: null, + errors: [{ message: 'Invalid request' }], + }) + } + + try { + } catch (error) { + // Check if the email and password didn't match an existing account + if (error instanceof FetcherError) { + return res.status(401).json({ + data: null, + errors: [ + { + message: + 'Cannot find an account that matches the provided credentials', + code: 'invalid_credentials', + }, + ], + }) + } + + throw error + } + + res.status(200).json({ data: null }) +} + +export default loginHandler diff --git a/framework/shopify/api/customers/index.ts b/framework/shopify/api/customers/index.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/customers/index.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/customers/login.ts b/framework/shopify/api/customers/login.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/customers/login.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/customers/logout.ts b/framework/shopify/api/customers/logout.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/customers/logout.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/customers/signup.ts b/framework/shopify/api/customers/signup.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/customers/signup.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts new file mode 100644 index 000000000..f62fb3437 --- /dev/null +++ b/framework/shopify/api/index.ts @@ -0,0 +1,54 @@ +import type { CommerceAPIConfig } from '@commerce/api' +import fetchGraphqlApi from '../utils/fetch-graphql-api' + +export interface ShopifyConfig extends CommerceAPIConfig {} + +// No I don't like this - will fix it later +const API_URL = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN +const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN + +if (!API_URL) { + throw new Error( + `The environment variable NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN is missing and it's required to access your store` + ) +} + +if (!API_TOKEN) { + throw new Error( + `The environment variable NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store` + ) +} + +export class Config { + private config: ShopifyConfig + + constructor(config: ShopifyConfig) { + this.config = config + } + + getConfig(userConfig: Partial<ShopifyConfig> = {}) { + return Object.entries(userConfig).reduce<ShopifyConfig>( + (cfg, [key, value]) => Object.assign(cfg, { [key]: value }), + { ...this.config } + ) + } + + setConfig(newConfig: Partial<ShopifyConfig>) { + Object.assign(this.config, newConfig) + } +} + +const config = new Config({ + commerceUrl: API_URL, + apiToken: API_TOKEN, + fetch: fetchGraphqlApi, + customerCookie: 'SHOP_TOKEN', +}) + +export function getConfig(userConfig?: Partial<ShopifyConfig>) { + return config.getConfig(userConfig) +} + +export function setConfig(newConfig: Partial<ShopifyConfig>) { + return config.setConfig(newConfig) +} diff --git a/framework/shopify/api/wishlist/index.tsx b/framework/shopify/api/wishlist/index.tsx new file mode 100644 index 000000000..a72856673 --- /dev/null +++ b/framework/shopify/api/wishlist/index.tsx @@ -0,0 +1,2 @@ +export type WishlistItem = { product: any; id: number } +export default function () {} diff --git a/framework/shopify/auth/use-login.tsx b/framework/shopify/auth/use-login.tsx new file mode 100644 index 000000000..384599fe9 --- /dev/null +++ b/framework/shopify/auth/use-login.tsx @@ -0,0 +1,53 @@ +import { useCallback } from 'react' +import type { HookFetcher } from '@commerce/utils/types' +import { CommerceError } from '@commerce/utils/errors' +import useCommerceLogin from '@commerce/use-login' +import type { LoginBody } from '../api/customers/login' +import useCustomer from '../customer/use-customer' + +const defaultOpts = { + query: '/api/bigcommerce/customers/login', +} + +export type LoginInput = LoginBody + +export const fetcher: HookFetcher<null, LoginBody> = ( + options, + { email, password }, + fetch +) => { + if (!(email && password)) { + throw new CommerceError({ + message: + 'A first name, last name, email and password are required to login', + }) + } + + return fetch({ + ...defaultOpts, + ...options, + body: { email, password }, + }) +} + +export function extendHook(customFetcher: typeof fetcher) { + const useLogin = () => { + const { revalidate } = useCustomer() + const fn = useCommerceLogin<null, LoginInput>(defaultOpts, customFetcher) + + return useCallback( + async function login(input: LoginInput) { + const data = await fn(input) + await revalidate() + return data + }, + [fn] + ) + } + + useLogin.extend = extendHook + + return useLogin +} + +export default extendHook(fetcher) diff --git a/framework/shopify/auth/use-logout.tsx b/framework/shopify/auth/use-logout.tsx new file mode 100644 index 000000000..75f067c3a --- /dev/null +++ b/framework/shopify/auth/use-logout.tsx @@ -0,0 +1,13 @@ +import { useCallback } from 'react' + +export function emptyHook() { + const useEmptyHook = async (options = {}) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/shopify/auth/use-signup.tsx b/framework/shopify/auth/use-signup.tsx new file mode 100644 index 000000000..75f067c3a --- /dev/null +++ b/framework/shopify/auth/use-signup.tsx @@ -0,0 +1,13 @@ +import { useCallback } from 'react' + +export function emptyHook() { + const useEmptyHook = async (options = {}) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/shopify/cart/index.ts b/framework/shopify/cart/index.ts new file mode 100644 index 000000000..3d288b1df --- /dev/null +++ b/framework/shopify/cart/index.ts @@ -0,0 +1,3 @@ +export { default as useCart } from './use-cart' +export { default as useAddItem } from './use-add-item' +export { default as useRemoveItem } from './use-remove-item' diff --git a/framework/shopify/cart/use-add-item.tsx b/framework/shopify/cart/use-add-item.tsx new file mode 100644 index 000000000..cbca0a572 --- /dev/null +++ b/framework/shopify/cart/use-add-item.tsx @@ -0,0 +1,70 @@ +import { useCallback } from 'react' +import { CommerceError } from '@commerce/utils/errors' +import useCart from './use-cart' +import useCartAddItem, { + AddItemInput as UseAddItemInput, +} from '@commerce/cart/use-add-item' +import type { HookFetcher } from '@commerce/utils/types' +import type { Cart } from '@commerce/types' +import checkoutLineItemAddMutation from '../utils/mutations/checkout-line-item-add' +import getCheckoutId from '@framework/utils/get-checkout-id' +import { checkoutToCart } from './utils' + +const defaultOpts = { + query: checkoutLineItemAddMutation, +} + +export type AddItemInput = UseAddItemInput<any> + +export const fetcher: HookFetcher<Cart, any> = async ( + options, + { checkoutId, item }, + fetch +) => { + if ( + item.quantity && + (!Number.isInteger(item.quantity) || item.quantity! < 1) + ) { + throw new CommerceError({ + message: 'The item quantity has to be a valid integer greater than 0', + }) + } + + const data = await fetch<any, any>({ + ...options, + variables: { + checkoutId, + lineItems: [item], + }, + }) + + return checkoutToCart(data?.checkoutLineItemsAdd) +} + +export function extendHook(customFetcher: typeof fetcher) { + const useAddItem = () => { + const { mutate, data: cart } = useCart() + const fn = useCartAddItem(defaultOpts, customFetcher) + + return useCallback( + async function addItem(input: AddItemInput) { + const data = await fn({ + item: { + variantId: input.variantId, + quantity: input.quantity ?? 1, + }, + checkoutId: getCheckoutId(cart?.id), + }) + await mutate(data, false) + return data + }, + [fn, mutate] + ) + } + + useAddItem.extend = extendHook + + return useAddItem +} + +export default extendHook(fetcher) diff --git a/framework/shopify/cart/use-cart.tsx b/framework/shopify/cart/use-cart.tsx new file mode 100644 index 000000000..24d4e9f94 --- /dev/null +++ b/framework/shopify/cart/use-cart.tsx @@ -0,0 +1,67 @@ +import type { HookFetcher } from '@commerce/utils/types' +import type { SwrOptions } from '@commerce/utils/use-data' + +import useResponse from '@commerce/utils/use-response' +import useCommerceCart, { CartInput } from '@commerce/cart/use-cart' +import getCheckoutQuery from '@framework/utils/queries/get-checkout-query' + +import { Cart } from '@commerce/types' +import { checkoutToCart, checkoutCreate } from './utils' + +const defaultOpts = { + query: getCheckoutQuery, +} + +export const fetcher: HookFetcher<Cart | null, CartInput> = async ( + options, + { cartId: checkoutId }, + fetch +) => { + let checkout + + if (checkoutId) { + const data = await fetch({ + ...defaultOpts, + ...options, + variables: { + checkoutId, + }, + }) + checkout = data?.node + } + + if (checkout?.completedAt || !checkoutId) { + checkout = await checkoutCreate(fetch) + } + + return checkoutToCart({ checkout }) +} + +export function extendHook( + customFetcher: typeof fetcher, + swrOptions?: SwrOptions<Cart | null, CartInput> +) { + const useCart = () => { + const response = useCommerceCart(defaultOpts, [], customFetcher, { + revalidateOnFocus: false, + ...swrOptions, + }) + const res = useResponse(response, { + descriptors: { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, + }, + }, + }) + return res + } + + useCart.extend = extendHook + + return useCart +} + +export default extendHook(fetcher) diff --git a/framework/shopify/cart/use-remove-item.tsx b/framework/shopify/cart/use-remove-item.tsx new file mode 100644 index 000000000..193adede6 --- /dev/null +++ b/framework/shopify/cart/use-remove-item.tsx @@ -0,0 +1,72 @@ +import { useCallback } from 'react' +import { HookFetcher } from '@commerce/utils/types' +import { ValidationError } from '@commerce/utils/errors' +import useCartRemoveItem, { + RemoveItemInput as UseRemoveItemInput, +} from '@commerce/cart/use-remove-item' + +import useCart from './use-cart' + +import type { Cart, LineItem, RemoveCartItemBody } from '@commerce/types' +import { checkoutLineItemRemoveMutation } from '@framework/utils/mutations' +import getCheckoutId from '@framework/utils/get-checkout-id' +import { checkoutToCart } from './utils' + +const defaultOpts = { + query: checkoutLineItemRemoveMutation, +} + +export type RemoveItemFn<T = any> = T extends LineItem + ? (input?: RemoveItemInput<T>) => Promise<Cart | null> + : (input: RemoveItemInput<T>) => Promise<Cart | null> + +export type RemoveItemInput<T = any> = T extends LineItem + ? Partial<UseRemoveItemInput> + : UseRemoveItemInput + +export const fetcher: HookFetcher<Cart | null, any> = async ( + options, + { itemId, checkoutId }, + fetch +) => { + const data = await fetch<any>({ + ...defaultOpts, + ...options, + variables: { lineItemIds: [itemId], checkoutId }, + }) + return checkoutToCart(data?.checkoutLineItemsRemove) +} + +export function extendHook(customFetcher: typeof fetcher) { + const useRemoveItem = <T extends LineItem | undefined = undefined>( + item?: T + ) => { + const { mutate, data: cart } = useCart() + const fn = useCartRemoveItem<Cart | null, any>(defaultOpts, customFetcher) + const removeItem: RemoveItemFn<LineItem> = async (input) => { + const itemId = input?.id ?? item?.id + + if (!itemId) { + throw new ValidationError({ + message: 'Invalid input used for this operation', + }) + } + + const data = await fn({ + checkoutId: getCheckoutId(cart?.id), + itemId, + }) + + await mutate(data, false) + return data + } + + return useCallback(removeItem as RemoveItemFn<T>, [fn, mutate]) + } + + useRemoveItem.extend = extendHook + + return useRemoveItem +} + +export default extendHook(fetcher) diff --git a/framework/shopify/cart/use-update-item.tsx b/framework/shopify/cart/use-update-item.tsx new file mode 100644 index 000000000..e29bf8b72 --- /dev/null +++ b/framework/shopify/cart/use-update-item.tsx @@ -0,0 +1,85 @@ +import { useCallback } from 'react' +import debounce from 'lodash.debounce' +import type { HookFetcher } from '@commerce/utils/types' +import { ValidationError } from '@commerce/utils/errors' +import useCartUpdateItem, { + UpdateItemInput as UseUpdateItemInput, +} from '@commerce/cart/use-update-item' + +import { fetcher as removeFetcher } from './use-remove-item' + +import useCart from './use-cart' + +import type { Cart, LineItem, UpdateCartItemBody } from '@commerce/types' +import { checkoutToCart } from './utils' +import checkoutLineItemUpdateMutation from '@framework/utils/mutations/checkout-line-item-update' +import getCheckoutId from '@framework/utils/get-checkout-id' + +const defaultOpts = { + query: checkoutLineItemUpdateMutation, +} + +export type UpdateItemInput<T = any> = T extends LineItem + ? Partial<UseUpdateItemInput<LineItem>> + : UseUpdateItemInput<LineItem> + +export const fetcher: HookFetcher<Cart | null, any> = async ( + options, + { item, checkoutId }, + fetch +) => { + if (Number.isInteger(item.quantity)) { + // Also allow the update hook to remove an item if the quantity is lower than 1 + if (item.quantity! < 1) { + return removeFetcher(null, { itemId: item.id, checkoutId }, fetch) + } + } else if (item.quantity) { + throw new ValidationError({ + message: 'The item quantity has to be a valid integer', + }) + } + const data = await fetch<any, any>({ + ...defaultOpts, + ...options, + variables: { checkoutId, lineItems: [item] }, + }) + + return checkoutToCart(data?.checkoutLineItemsUpdate) +} + +function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) { + const useUpdateItem = <T extends LineItem | undefined = undefined>( + item?: T + ) => { + const { mutate, data: cart } = useCart() + const fn = useCartUpdateItem<Cart | null, any>(defaultOpts, customFetcher) + + return useCallback( + debounce(async (input: UpdateItemInput<T>) => { + const itemId = input.id ?? item?.id + const variantId = input.productId ?? item?.variantId + + if (!itemId || !variantId) { + throw new ValidationError({ + message: 'Invalid input used for this operation', + }) + } + + const data = await fn({ + item: { id: itemId, variantId, quantity: input.quantity }, + checkoutId: getCheckoutId(cart?.id), + }) + + await mutate(data, false) + return data + }, cfg?.wait ?? 500), + [fn, mutate] + ) + } + + useUpdateItem.extend = extendHook + + return useUpdateItem +} + +export default extendHook(fetcher) diff --git a/framework/shopify/cart/utils/checkout-create.ts b/framework/shopify/cart/utils/checkout-create.ts new file mode 100644 index 000000000..50cbd1299 --- /dev/null +++ b/framework/shopify/cart/utils/checkout-create.ts @@ -0,0 +1,20 @@ +import { SHOPIFY_CHECKOUT_COOKIE } from '@framework' +import checkoutCreateMutation from '@framework/utils/mutations/checkout-create' +import Cookies from 'js-cookie' + +export const createCheckout = async (fetch: any) => { + const data = await fetch({ + query: checkoutCreateMutation, + }) + + const checkout = data?.checkoutCreate?.checkout + const checkoutId = checkout?.id + + if (checkoutId) { + Cookies.set(SHOPIFY_CHECKOUT_COOKIE, checkoutId) + } + + return checkout +} + +export default createCheckout diff --git a/framework/shopify/cart/utils/checkout-to-cart.ts b/framework/shopify/cart/utils/checkout-to-cart.ts new file mode 100644 index 000000000..c7f0e2783 --- /dev/null +++ b/framework/shopify/cart/utils/checkout-to-cart.ts @@ -0,0 +1,57 @@ +import { Cart } from '@commerce/types' +import { CommerceError, ValidationError } from '@commerce/utils/errors' +import { Checkout, CheckoutLineItemEdge, Maybe } from '@framework/schema' + +const checkoutToCart = (checkoutResponse?: any): Maybe<Cart> => { + if (!checkoutResponse) { + throw new CommerceError({ + message: 'Missing checkout details from response cart Response', + }) + } + + const { + checkout, + userErrors, + }: { checkout?: Checkout; userErrors?: any[] } = checkoutResponse + + if (userErrors && userErrors.length) { + throw new ValidationError({ + message: userErrors[0].message, + }) + } + + if (!checkout) { + throw new ValidationError({ + message: 'Missing checkout details from response cart Response', + }) + } + + return { + ...checkout, + currency: { code: checkout.currencyCode }, + lineItems: checkout.lineItems?.edges.map( + ({ + node: { id, title: name, quantity, variant }, + }: CheckoutLineItemEdge) => ({ + id, + checkoutUrl: checkout.webUrl, + variantId: variant?.id, + productId: id, + name, + quantity, + discounts: [], + path: '', + variant: { + id: variant?.id, + image: { + url: variant?.image?.src, + altText: variant?.title, + }, + price: variant?.price, + }, + }) + ), + } +} + +export default checkoutToCart diff --git a/framework/shopify/cart/utils/index.ts b/framework/shopify/cart/utils/index.ts new file mode 100644 index 000000000..20d04955d --- /dev/null +++ b/framework/shopify/cart/utils/index.ts @@ -0,0 +1,2 @@ +export { default as checkoutToCart } from './checkout-to-cart' +export { default as checkoutCreate } from './checkout-create' diff --git a/framework/shopify/common/get-all-pages.ts b/framework/shopify/common/get-all-pages.ts new file mode 100644 index 000000000..9546c3fb3 --- /dev/null +++ b/framework/shopify/common/get-all-pages.ts @@ -0,0 +1,34 @@ +import { getConfig, ShopifyConfig } from '../api' +import { Page, PageEdge } from '../schema' +import { getAllPagesQuery } from '../utils/queries' + +type Variables = { + first?: number +} + +type ReturnType = { + pages: Page[] +} + +const getAllPages = async (options?: { + variables?: Variables + config: ShopifyConfig + preview?: boolean +}): Promise<ReturnType> => { + let { config, variables = { first: 250 } } = options ?? {} + config = getConfig(config) + + const { data } = await config.fetch(getAllPagesQuery, { variables }) + + const pages = data.pages.edges.map(({ node }: PageEdge) => { + return { + ...node, + name: node.handle, + url: `${config!.locale}/${node.handle}`, + } + }) + + return { pages } +} + +export default getAllPages diff --git a/framework/shopify/common/get-page.ts b/framework/shopify/common/get-page.ts new file mode 100644 index 000000000..8c2fb7165 --- /dev/null +++ b/framework/shopify/common/get-page.ts @@ -0,0 +1,27 @@ +import { ShopifyConfig, getConfig } from '../api' +import type { Page } from '../types' + +export type { Page } + +export type GetPageResult<T extends { page?: any } = { page?: Page }> = T + +export type PageVariables = { + id: string +} + +async function getPage({ + url, + variables, + config, + preview, +}: { + url?: string + variables: PageVariables + config?: ShopifyConfig + preview?: boolean +}): Promise<GetPageResult> { + config = getConfig(config) + return {} +} + +export default getPage diff --git a/framework/shopify/common/get-site-info.ts b/framework/shopify/common/get-site-info.ts new file mode 100644 index 000000000..1a87e2d5d --- /dev/null +++ b/framework/shopify/common/get-site-info.ts @@ -0,0 +1,31 @@ +import { CollectionEdge } from '@framework/schema' +import { getConfig, ShopifyConfig } from '../api' +import getAllCollectionsQuery from '../utils/queries/get-all-collections-query' + +const getSiteInfo = async (options?: { + variables?: any + config: ShopifyConfig + preview?: boolean +}) => { + let { config, variables = { first: 250 } } = options ?? {} + + config = getConfig(config) + + const { data } = await config.fetch(getAllCollectionsQuery, { variables }) + const edges = data.collections?.edges ?? [] + + const categories = edges.map( + ({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({ + entityId, + name, + path: `/${handle}`, + }) + ) + + return { + categories, + brands: [], + } +} + +export default getSiteInfo diff --git a/framework/shopify/customer/use-customer.tsx b/framework/shopify/customer/use-customer.tsx new file mode 100644 index 000000000..a909443ff --- /dev/null +++ b/framework/shopify/customer/use-customer.tsx @@ -0,0 +1,32 @@ +import type { HookFetcher } from '@commerce/utils/types' +import type { SwrOptions } from '@commerce/utils/use-data' +import useCommerceCustomer from '@commerce/use-customer' + +const defaultOpts = {} + +export type Customer = { + entityId: number + firstName: string + lastName: string + email: string +} +export type CustomerData = {} + +export const fetcher: HookFetcher<Customer | null> = async () => { + return null +} + +export function extendHook( + customFetcher: typeof fetcher, + swrOptions?: SwrOptions<Customer | null> +) { + const useCustomer = () => { + return { data: { firstName: null, lastName: null, email: null } } + } + + useCustomer.extend = extendHook + + return useCustomer +} + +export default extendHook(fetcher) diff --git a/framework/shopify/index.tsx b/framework/shopify/index.tsx new file mode 100644 index 000000000..3d9db989f --- /dev/null +++ b/framework/shopify/index.tsx @@ -0,0 +1,78 @@ +import { ReactNode } from 'react' +import * as React from 'react' + +import { + CommerceConfig, + CommerceProvider as CoreCommerceProvider, + useCommerce as useCoreCommerce, +} from '@commerce' + +import { CommerceError, FetcherError } from '@commerce/utils/errors' + +export const SHOPIFY_CHECKOUT_COOKIE = 'shopify_checkoutId' + +async function getText(res: Response) { + try { + return (await res.text()) || res.statusText + } catch (error) { + return res.statusText + } +} + +async function getError(res: Response) { + if (res.headers.get('Content-Type')?.includes('application/json')) { + const data = await res.json() + + return new FetcherError({ errors: data.errors, status: res.status }) + } + return new FetcherError({ message: await getText(res), status: res.status }) +} + +export const shopifyConfig: CommerceConfig = { + locale: 'en-us', + cartCookie: SHOPIFY_CHECKOUT_COOKIE, + async fetcher({ method = 'POST', variables, query }) { + const res = await fetch( + `https://${process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN}/api/2021-01/graphql.json`, + { + method, + body: JSON.stringify({ query, variables }), + headers: { + 'X-Shopify-Storefront-Access-Token': + process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN, + 'Content-Type': 'application/json', + }, + } + ) + + if (res.ok) { + const { data, errors } = await res.json() + + if (errors && errors.length) { + throw new CommerceError({ + message: errors[0].message, + }) + } + return data + } + + throw await getError(res) + }, +} + +export type ShopifyConfig = Partial<CommerceConfig> + +export type ShopifyProps = { + children?: ReactNode + locale: string +} & ShopifyConfig + +export function CommerceProvider({ children, ...config }: ShopifyProps) { + return ( + <CoreCommerceProvider config={{ ...shopifyConfig, ...config }}> + {children} + </CoreCommerceProvider> + ) +} + +export const useCommerce = () => useCoreCommerce() diff --git a/framework/shopify/product/get-all-collections.ts b/framework/shopify/product/get-all-collections.ts new file mode 100644 index 000000000..bf3fee392 --- /dev/null +++ b/framework/shopify/product/get-all-collections.ts @@ -0,0 +1,29 @@ +import { CollectionEdge } from '@framework/schema' +import { getConfig, ShopifyConfig } from '../api' +import getAllCollectionsQuery from '../utils/queries/get-all-collections-query' + +const getAllCollections = async (options?: { + variables?: any + config: ShopifyConfig + preview?: boolean +}) => { + let { config, variables = { first: 250 } } = options ?? {} + config = getConfig(config) + + const { data } = await config.fetch(getAllCollectionsQuery, { variables }) + const edges = data.collections?.edges ?? [] + + const categories = edges.map( + ({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({ + entityId, + name, + path: `/${handle}`, + }) + ) + + return { + categories, + } +} + +export default getAllCollections diff --git a/framework/shopify/product/get-all-product-paths.ts b/framework/shopify/product/get-all-product-paths.ts new file mode 100644 index 000000000..3627321a8 --- /dev/null +++ b/framework/shopify/product/get-all-product-paths.ts @@ -0,0 +1,34 @@ +import { getConfig, ShopifyConfig } from '../api' +import { ProductEdge } from '../schema' +import getAllProductsPathsQuery from '../utils/queries/get-all-products-paths-query' + +type ReturnType = { + products: any[] +} + +const getAllProductPaths = async (options?: { + variables?: any + config?: ShopifyConfig + previe?: boolean +}): Promise<ReturnType> => { + let { config, variables = { first: 250 } } = options ?? {} + config = getConfig(config) + + const { data } = await config.fetch(getAllProductsPathsQuery, { + variables, + }) + + const edges = data.products?.edges + const productInfo = data.products?.productInfo + const hasNextPage = productInfo?.hasNextPage + + return { + products: edges.map(({ node: { handle } }: ProductEdge) => ({ + node: { + path: `/${handle}`, + }, + })), + } +} + +export default getAllProductPaths diff --git a/framework/shopify/product/get-all-products.ts b/framework/shopify/product/get-all-products.ts new file mode 100644 index 000000000..01d086d84 --- /dev/null +++ b/framework/shopify/product/get-all-products.ts @@ -0,0 +1,39 @@ +import { GraphQLFetcherResult } from '@commerce/api' +import toCommerceProducts from '../utils/to-commerce-products' +import { getConfig, ShopifyConfig } from '../api' +import { Product } from '../schema' +import { getAllProductsQuery } from '../utils/queries' + +export type ProductNode = Product + +type Variables = { + first?: number + field?: string +} + +type ReturnType = { + products: any[] +} + +const getAllProducts = async (options: { + variables?: Variables + config?: ShopifyConfig + preview?: boolean +}): Promise<ReturnType> => { + let { config, variables = { first: 250 } } = options ?? {} + config = getConfig(config) + + const { data }: GraphQLFetcherResult = await config.fetch( + getAllProductsQuery, + { variables } + ) + + const shopifyProducts = data.products?.edges + const products = toCommerceProducts(shopifyProducts) + + return { + products, + } +} + +export default getAllProducts diff --git a/framework/shopify/product/get-product.ts b/framework/shopify/product/get-product.ts new file mode 100644 index 000000000..0cfc8a44a --- /dev/null +++ b/framework/shopify/product/get-product.ts @@ -0,0 +1,37 @@ +import { GraphQLFetcherResult } from '@commerce/api' + +import { getConfig, ShopifyConfig } from '../api' +import { Product } from '../schema' +import { toCommerceProduct } from '../utils/to-commerce-products' +import getProductQuery from '../utils/queries/get-product-query' + +export type ProductNode = Product + +type Variables = { + slug: string +} + +type Options = { + variables: Variables + config: ShopifyConfig + preview?: boolean +} + +type ReturnType = { + product: any +} + +const getProduct = async (options: Options): Promise<ReturnType> => { + let { config, variables = { first: 250 } } = options ?? {} + config = getConfig(config) + + const { + data: { productByHandle: product }, + }: GraphQLFetcherResult = await config.fetch(getProductQuery, { variables }) + + return { + product: product ? toCommerceProduct(product) : null, + } +} + +export default getProduct diff --git a/framework/shopify/product/use-price.tsx b/framework/shopify/product/use-price.tsx new file mode 100644 index 000000000..a79940a76 --- /dev/null +++ b/framework/shopify/product/use-price.tsx @@ -0,0 +1,2 @@ +export * from '@commerce/use-price' +export { default } from '@commerce/use-price' diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx new file mode 100644 index 000000000..421323bf9 --- /dev/null +++ b/framework/shopify/product/use-search.tsx @@ -0,0 +1,86 @@ +import useCommerceSearch from '@commerce/products/use-search' + +import toCommerceProducts from '@framework/utils/to-commerce-products' +import getAllProductsQuery from '@framework/utils/queries/get-all-products-query' + +import type { Product } from 'framework/bigcommerce/schema' +import type { HookFetcher } from '@commerce/utils/types' +import type { SwrOptions } from '@commerce/utils/use-data' +import type { ProductEdge } from '@framework/schema' + +import { + searchByProductType, + searchByTag, +} from '@framework/utils/get-search-variables' + +import sortBy from '@framework/utils/get-sort-variables' + +export type CommerceProductEdge = { + node: Product +} + +export type SearchProductsInput = { + search?: string + categoryPath?: string + sort?: string +} + +export type SearchRequestProductsData = { + products?: ProductEdge[] +} + +export type SearchProductsData = { + products: Product[] + found: boolean +} + +export const fetcher: HookFetcher< + SearchRequestProductsData, + SearchProductsInput +> = (options, { search, categoryPath, sort }, fetch) => { + return fetch({ + query: options?.query, + method: options?.method, + variables: { + ...searchByProductType(search), + ...searchByTag(categoryPath), + ...sortBy(sort), + }, + }).then( + ({ products }): SearchProductsData => { + return { + products: toCommerceProducts(products.edges), + found: !!products.edges.length, + } + } + ) +} + +export function extendHook( + customFetcher: typeof fetcher, + swrOptions?: SwrOptions<SearchProductsData, SearchProductsInput> +) { + const useSearch = (input: SearchProductsInput = {}) => { + const response = useCommerceSearch( + { + query: getAllProductsQuery, + method: 'POST', + }, + [ + ['search', input.search], + ['categoryPath', input.categoryPath], + ['sort', input.sort], + ], + customFetcher, + { revalidateOnFocus: false, ...swrOptions } + ) + + return response + } + + useSearch.extend = extendHook + + return useSearch +} + +export default extendHook(fetcher) diff --git a/framework/shopify/schema.d.ts b/framework/shopify/schema.d.ts new file mode 100644 index 000000000..b1b23a3e5 --- /dev/null +++ b/framework/shopify/schema.d.ts @@ -0,0 +1,4985 @@ +export type Maybe<T> = T | null +export type Exact<T extends { [key: string]: unknown }> = { + [K in keyof T]: T[K] +} +export type MakeOptional<T, K extends keyof T> = Omit<T, K> & + { [SubKey in K]?: Maybe<T[SubKey]> } +export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & + { [SubKey in K]: Maybe<T[SubKey]> } +/** All built-in and custom scalars, mapped to their actual values */ +export type Scalars = { + ID: string + String: string + Boolean: boolean + Int: number + Float: number + /** An ISO-8601 encoded UTC date time string. Example value: `"2019-07-03T20:47:55Z"`. */ + DateTime: any + /** A signed decimal number, which supports arbitrary precision and is serialized as a string. Example value: `"29.99"`. */ + Decimal: any + /** A string containing HTML code. Example value: `"<p>Grey cotton knit sweater.</p>"`. */ + HTML: any + /** A monetary value string. Example value: `"100.57"`. */ + Money: any + /** + * An RFC 3986 and RFC 3987 compliant URI string. + * + * Example value: `"https://johns-apparel.myshopify.com"`. + * + */ + URL: any +} + +/** A version of the API. */ +export type ApiVersion = { + __typename?: 'ApiVersion' + /** The human-readable name of the version. */ + displayName: Scalars['String'] + /** The unique identifier of an ApiVersion. All supported API versions have a date-based (YYYY-MM) or `unstable` handle. */ + handle: Scalars['String'] + /** Whether the version is supported by Shopify. */ + supported: Scalars['Boolean'] +} + +/** Details about the gift card used on the checkout. */ +export type AppliedGiftCard = Node & { + __typename?: 'AppliedGiftCard' + /** + * The amount that was taken from the gift card by applying it. + * @deprecated Use `amountUsedV2` instead + */ + amountUsed: Scalars['Money'] + /** The amount that was taken from the gift card by applying it. */ + amountUsedV2: MoneyV2 + /** + * The amount left on the gift card. + * @deprecated Use `balanceV2` instead + */ + balance: Scalars['Money'] + /** The amount left on the gift card. */ + balanceV2: MoneyV2 + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The last characters of the gift card. */ + lastCharacters: Scalars['String'] + /** The amount that was applied to the checkout in its currency. */ + presentmentAmountUsed: MoneyV2 +} + +/** An article in an online store blog. */ +export type Article = Node & { + __typename?: 'Article' + /** + * The article's author. + * @deprecated Use `authorV2` instead + */ + author: ArticleAuthor + /** The article's author. */ + authorV2?: Maybe<ArticleAuthor> + /** The blog that the article belongs to. */ + blog: Blog + /** List of comments posted on the article. */ + comments: CommentConnection + /** Stripped content of the article, single line with HTML tags removed. */ + content: Scalars['String'] + /** The content of the article, complete with HTML formatting. */ + contentHtml: Scalars['HTML'] + /** Stripped excerpt of the article, single line with HTML tags removed. */ + excerpt?: Maybe<Scalars['String']> + /** The excerpt of the article, complete with HTML formatting. */ + excerptHtml?: Maybe<Scalars['HTML']> + /** A human-friendly unique string for the Article automatically generated from its title. */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The image associated with the article. */ + image?: Maybe<Image> + /** The date and time when the article was published. */ + publishedAt: Scalars['DateTime'] + /** The article’s SEO information. */ + seo?: Maybe<Seo> + /** A categorization that a article can be tagged with. */ + tags: Array<Scalars['String']> + /** The article’s name. */ + title: Scalars['String'] + /** The url pointing to the article accessible from the web. */ + url: Scalars['URL'] +} + +/** An article in an online store blog. */ +export type ArticleCommentsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** An article in an online store blog. */ +export type ArticleContentArgs = { + truncateAt?: Maybe<Scalars['Int']> +} + +/** An article in an online store blog. */ +export type ArticleExcerptArgs = { + truncateAt?: Maybe<Scalars['Int']> +} + +/** An article in an online store blog. */ +export type ArticleImageArgs = { + maxWidth?: Maybe<Scalars['Int']> + maxHeight?: Maybe<Scalars['Int']> + crop?: Maybe<CropRegion> + scale?: Maybe<Scalars['Int']> +} + +/** The author of an article. */ +export type ArticleAuthor = { + __typename?: 'ArticleAuthor' + /** The author's bio. */ + bio?: Maybe<Scalars['String']> + /** The author’s email. */ + email: Scalars['String'] + /** The author's first name. */ + firstName: Scalars['String'] + /** The author's last name. */ + lastName: Scalars['String'] + /** The author's full name. */ + name: Scalars['String'] +} + +/** An auto-generated type for paginating through multiple Articles. */ +export type ArticleConnection = { + __typename?: 'ArticleConnection' + /** A list of edges. */ + edges: Array<ArticleEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Article and a cursor during pagination. */ +export type ArticleEdge = { + __typename?: 'ArticleEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ArticleEdge. */ + node: Article +} + +/** The set of valid sort keys for the Article query. */ +export enum ArticleSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `blog_title` value. */ + BlogTitle = 'BLOG_TITLE', + /** Sort by the `author` value. */ + Author = 'AUTHOR', + /** Sort by the `updated_at` value. */ + UpdatedAt = 'UPDATED_AT', + /** Sort by the `published_at` value. */ + PublishedAt = 'PUBLISHED_AT', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** Represents a generic custom attribute. */ +export type Attribute = { + __typename?: 'Attribute' + /** Key or name of the attribute. */ + key: Scalars['String'] + /** Value of the attribute. */ + value?: Maybe<Scalars['String']> +} + +/** Specifies the input fields required for an attribute. */ +export type AttributeInput = { + /** Key or name of the attribute. */ + key: Scalars['String'] + /** Value of the attribute. */ + value: Scalars['String'] +} + +/** Automatic discount applications capture the intentions of a discount that was automatically applied. */ +export type AutomaticDiscountApplication = DiscountApplication & { + __typename?: 'AutomaticDiscountApplication' + /** The method by which the discount's value is allocated to its entitled items. */ + allocationMethod: DiscountApplicationAllocationMethod + /** Which lines of targetType that the discount is allocated over. */ + targetSelection: DiscountApplicationTargetSelection + /** The type of line that the discount is applicable towards. */ + targetType: DiscountApplicationTargetType + /** The title of the application. */ + title: Scalars['String'] + /** The value of the discount application. */ + value: PricingValue +} + +/** A collection of available shipping rates for a checkout. */ +export type AvailableShippingRates = { + __typename?: 'AvailableShippingRates' + /** + * Whether or not the shipping rates are ready. + * The `shippingRates` field is `null` when this value is `false`. + * This field should be polled until its value becomes `true`. + */ + ready: Scalars['Boolean'] + /** The fetched shipping rates. `null` until the `ready` field is `true`. */ + shippingRates?: Maybe<Array<ShippingRate>> +} + +/** An online store blog. */ +export type Blog = Node & { + __typename?: 'Blog' + /** Find an article by its handle. */ + articleByHandle?: Maybe<Article> + /** List of the blog's articles. */ + articles: ArticleConnection + /** The authors who have contributed to the blog. */ + authors: Array<ArticleAuthor> + /** A human-friendly unique string for the Blog automatically generated from its title. */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The blog's SEO information. */ + seo?: Maybe<Seo> + /** The blogs’s title. */ + title: Scalars['String'] + /** The url pointing to the blog accessible from the web. */ + url: Scalars['URL'] +} + +/** An online store blog. */ +export type BlogArticleByHandleArgs = { + handle: Scalars['String'] +} + +/** An online store blog. */ +export type BlogArticlesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ArticleSortKeys> + query?: Maybe<Scalars['String']> +} + +/** An auto-generated type for paginating through multiple Blogs. */ +export type BlogConnection = { + __typename?: 'BlogConnection' + /** A list of edges. */ + edges: Array<BlogEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Blog and a cursor during pagination. */ +export type BlogEdge = { + __typename?: 'BlogEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of BlogEdge. */ + node: Blog +} + +/** The set of valid sort keys for the Blog query. */ +export enum BlogSortKeys { + /** Sort by the `handle` value. */ + Handle = 'HANDLE', + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** Card brand, such as Visa or Mastercard, which can be used for payments. */ +export enum CardBrand { + /** Visa */ + Visa = 'VISA', + /** Mastercard */ + Mastercard = 'MASTERCARD', + /** Discover */ + Discover = 'DISCOVER', + /** American Express */ + AmericanExpress = 'AMERICAN_EXPRESS', + /** Diners Club */ + DinersClub = 'DINERS_CLUB', + /** JCB */ + Jcb = 'JCB', +} + +/** A container for all the information required to checkout items and pay. */ +export type Checkout = Node & { + __typename?: 'Checkout' + /** The gift cards used on the checkout. */ + appliedGiftCards: Array<AppliedGiftCard> + /** + * The available shipping rates for this Checkout. + * Should only be used when checkout `requiresShipping` is `true` and + * the shipping address is valid. + */ + availableShippingRates?: Maybe<AvailableShippingRates> + /** The date and time when the checkout was completed. */ + completedAt?: Maybe<Scalars['DateTime']> + /** The date and time when the checkout was created. */ + createdAt: Scalars['DateTime'] + /** The currency code for the Checkout. */ + currencyCode: CurrencyCode + /** A list of extra information that is added to the checkout. */ + customAttributes: Array<Attribute> + /** + * The customer associated with the checkout. + * @deprecated This field will always return null. If you have an authentication token for the customer, you can use the `customer` field on the query root to retrieve it. + */ + customer?: Maybe<Customer> + /** Discounts that have been applied on the checkout. */ + discountApplications: DiscountApplicationConnection + /** The email attached to this checkout. */ + email?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** A list of line item objects, each one containing information about an item in the checkout. */ + lineItems: CheckoutLineItemConnection + /** The sum of all the prices of all the items in the checkout. Duties, taxes, shipping and discounts excluded. */ + lineItemsSubtotalPrice: MoneyV2 + /** The note associated with the checkout. */ + note?: Maybe<Scalars['String']> + /** The resulting order from a paid checkout. */ + order?: Maybe<Order> + /** The Order Status Page for this Checkout, null when checkout is not completed. */ + orderStatusUrl?: Maybe<Scalars['URL']> + /** + * The amount left to be paid. This is equal to the cost of the line items, taxes and shipping minus discounts and gift cards. + * @deprecated Use `paymentDueV2` instead + */ + paymentDue: Scalars['Money'] + /** The amount left to be paid. This is equal to the cost of the line items, duties, taxes and shipping minus discounts and gift cards. */ + paymentDueV2: MoneyV2 + /** + * Whether or not the Checkout is ready and can be completed. Checkouts may + * have asynchronous operations that can take time to finish. If you want + * to complete a checkout or ensure all the fields are populated and up to + * date, polling is required until the value is true. + */ + ready: Scalars['Boolean'] + /** States whether or not the fulfillment requires shipping. */ + requiresShipping: Scalars['Boolean'] + /** The shipping address to where the line items will be shipped. */ + shippingAddress?: Maybe<MailingAddress> + /** The discounts that have been allocated onto the shipping line by discount applications. */ + shippingDiscountAllocations: Array<DiscountAllocation> + /** Once a shipping rate is selected by the customer it is transitioned to a `shipping_line` object. */ + shippingLine?: Maybe<ShippingRate> + /** + * Price of the checkout before shipping and taxes. + * @deprecated Use `subtotalPriceV2` instead + */ + subtotalPrice: Scalars['Money'] + /** Price of the checkout before duties, shipping and taxes. */ + subtotalPriceV2: MoneyV2 + /** Specifies if the Checkout is tax exempt. */ + taxExempt: Scalars['Boolean'] + /** Specifies if taxes are included in the line item and shipping line prices. */ + taxesIncluded: Scalars['Boolean'] + /** + * The sum of all the prices of all the items in the checkout, taxes and discounts included. + * @deprecated Use `totalPriceV2` instead + */ + totalPrice: Scalars['Money'] + /** The sum of all the prices of all the items in the checkout, duties, taxes and discounts included. */ + totalPriceV2: MoneyV2 + /** + * The sum of all the taxes applied to the line items and shipping lines in the checkout. + * @deprecated Use `totalTaxV2` instead + */ + totalTax: Scalars['Money'] + /** The sum of all the taxes applied to the line items and shipping lines in the checkout. */ + totalTaxV2: MoneyV2 + /** The date and time when the checkout was last updated. */ + updatedAt: Scalars['DateTime'] + /** The url pointing to the checkout accessible from the web. */ + webUrl: Scalars['URL'] +} + +/** A container for all the information required to checkout items and pay. */ +export type CheckoutDiscountApplicationsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** A container for all the information required to checkout items and pay. */ +export type CheckoutLineItemsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** Specifies the fields required to update a checkout's attributes. */ +export type CheckoutAttributesUpdateInput = { + /** The text of an optional note that a shop owner can attach to the checkout. */ + note?: Maybe<Scalars['String']> + /** A list of extra information that is added to the checkout. */ + customAttributes?: Maybe<Array<AttributeInput>> + /** + * Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + * The required attributes are city, province, and country. + * Full validation of the addresses is still done at complete time. + */ + allowPartialAddresses?: Maybe<Scalars['Boolean']> +} + +/** Return type for `checkoutAttributesUpdate` mutation. */ +export type CheckoutAttributesUpdatePayload = { + __typename?: 'CheckoutAttributesUpdatePayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Specifies the fields required to update a checkout's attributes. */ +export type CheckoutAttributesUpdateV2Input = { + /** The text of an optional note that a shop owner can attach to the checkout. */ + note?: Maybe<Scalars['String']> + /** A list of extra information that is added to the checkout. */ + customAttributes?: Maybe<Array<AttributeInput>> + /** + * Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + * The required attributes are city, province, and country. + * Full validation of the addresses is still done at complete time. + */ + allowPartialAddresses?: Maybe<Scalars['Boolean']> +} + +/** Return type for `checkoutAttributesUpdateV2` mutation. */ +export type CheckoutAttributesUpdateV2Payload = { + __typename?: 'CheckoutAttributesUpdateV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteFree` mutation. */ +export type CheckoutCompleteFreePayload = { + __typename?: 'CheckoutCompleteFreePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteWithCreditCard` mutation. */ +export type CheckoutCompleteWithCreditCardPayload = { + __typename?: 'CheckoutCompleteWithCreditCardPayload' + /** The checkout on which the payment was applied. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** A representation of the attempted payment. */ + payment?: Maybe<Payment> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteWithCreditCardV2` mutation. */ +export type CheckoutCompleteWithCreditCardV2Payload = { + __typename?: 'CheckoutCompleteWithCreditCardV2Payload' + /** The checkout on which the payment was applied. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** A representation of the attempted payment. */ + payment?: Maybe<Payment> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteWithTokenizedPayment` mutation. */ +export type CheckoutCompleteWithTokenizedPaymentPayload = { + __typename?: 'CheckoutCompleteWithTokenizedPaymentPayload' + /** The checkout on which the payment was applied. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** A representation of the attempted payment. */ + payment?: Maybe<Payment> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteWithTokenizedPaymentV2` mutation. */ +export type CheckoutCompleteWithTokenizedPaymentV2Payload = { + __typename?: 'CheckoutCompleteWithTokenizedPaymentV2Payload' + /** The checkout on which the payment was applied. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** A representation of the attempted payment. */ + payment?: Maybe<Payment> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteWithTokenizedPaymentV3` mutation. */ +export type CheckoutCompleteWithTokenizedPaymentV3Payload = { + __typename?: 'CheckoutCompleteWithTokenizedPaymentV3Payload' + /** The checkout on which the payment was applied. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** A representation of the attempted payment. */ + payment?: Maybe<Payment> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Specifies the fields required to create a checkout. */ +export type CheckoutCreateInput = { + /** The email with which the customer wants to checkout. */ + email?: Maybe<Scalars['String']> + /** A list of line item objects, each one containing information about an item in the checkout. */ + lineItems?: Maybe<Array<CheckoutLineItemInput>> + /** The shipping address to where the line items will be shipped. */ + shippingAddress?: Maybe<MailingAddressInput> + /** The text of an optional note that a shop owner can attach to the checkout. */ + note?: Maybe<Scalars['String']> + /** A list of extra information that is added to the checkout. */ + customAttributes?: Maybe<Array<AttributeInput>> + /** + * Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + * The required attributes are city, province, and country. + * Full validation of addresses is still done at complete time. + */ + allowPartialAddresses?: Maybe<Scalars['Boolean']> + /** + * The three-letter currency code of one of the shop's enabled presentment currencies. + * Including this field creates a checkout in the specified currency. By default, new + * checkouts are created in the shop's primary currency. + */ + presentmentCurrencyCode?: Maybe<CurrencyCode> +} + +/** Return type for `checkoutCreate` mutation. */ +export type CheckoutCreatePayload = { + __typename?: 'CheckoutCreatePayload' + /** The new checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCustomerAssociate` mutation. */ +export type CheckoutCustomerAssociatePayload = { + __typename?: 'CheckoutCustomerAssociatePayload' + /** The updated checkout object. */ + checkout: Checkout + /** The associated customer object. */ + customer?: Maybe<Customer> + /** List of errors that occurred executing the mutation. */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCustomerAssociateV2` mutation. */ +export type CheckoutCustomerAssociateV2Payload = { + __typename?: 'CheckoutCustomerAssociateV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** The associated customer object. */ + customer?: Maybe<Customer> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCustomerDisassociate` mutation. */ +export type CheckoutCustomerDisassociatePayload = { + __typename?: 'CheckoutCustomerDisassociatePayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCustomerDisassociateV2` mutation. */ +export type CheckoutCustomerDisassociateV2Payload = { + __typename?: 'CheckoutCustomerDisassociateV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutDiscountCodeApply` mutation. */ +export type CheckoutDiscountCodeApplyPayload = { + __typename?: 'CheckoutDiscountCodeApplyPayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutDiscountCodeApplyV2` mutation. */ +export type CheckoutDiscountCodeApplyV2Payload = { + __typename?: 'CheckoutDiscountCodeApplyV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutDiscountCodeRemove` mutation. */ +export type CheckoutDiscountCodeRemovePayload = { + __typename?: 'CheckoutDiscountCodeRemovePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutEmailUpdate` mutation. */ +export type CheckoutEmailUpdatePayload = { + __typename?: 'CheckoutEmailUpdatePayload' + /** The checkout object with the updated email. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutEmailUpdateV2` mutation. */ +export type CheckoutEmailUpdateV2Payload = { + __typename?: 'CheckoutEmailUpdateV2Payload' + /** The checkout object with the updated email. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Possible error codes that could be returned by CheckoutUserError. */ +export enum CheckoutErrorCode { + /** Input value is blank. */ + Blank = 'BLANK', + /** Input value is invalid. */ + Invalid = 'INVALID', + /** Input value is too long. */ + TooLong = 'TOO_LONG', + /** Input value is not present. */ + Present = 'PRESENT', + /** Input value should be less than maximum allowed value. */ + LessThan = 'LESS_THAN', + /** Input value should be greater than or equal to minimum allowed value. */ + GreaterThanOrEqualTo = 'GREATER_THAN_OR_EQUAL_TO', + /** Input value should be less or equal to maximum allowed value. */ + LessThanOrEqualTo = 'LESS_THAN_OR_EQUAL_TO', + /** Checkout is already completed. */ + AlreadyCompleted = 'ALREADY_COMPLETED', + /** Checkout is locked. */ + Locked = 'LOCKED', + /** Input value is not supported. */ + NotSupported = 'NOT_SUPPORTED', + /** Input email contains an invalid domain name. */ + BadDomain = 'BAD_DOMAIN', + /** Input Zip is invalid for country provided. */ + InvalidForCountry = 'INVALID_FOR_COUNTRY', + /** Input Zip is invalid for country and province provided. */ + InvalidForCountryAndProvince = 'INVALID_FOR_COUNTRY_AND_PROVINCE', + /** Invalid state in country. */ + InvalidStateInCountry = 'INVALID_STATE_IN_COUNTRY', + /** Invalid province in country. */ + InvalidProvinceInCountry = 'INVALID_PROVINCE_IN_COUNTRY', + /** Invalid region in country. */ + InvalidRegionInCountry = 'INVALID_REGION_IN_COUNTRY', + /** Shipping rate expired. */ + ShippingRateExpired = 'SHIPPING_RATE_EXPIRED', + /** Gift card cannot be applied to a checkout that contains a gift card. */ + GiftCardUnusable = 'GIFT_CARD_UNUSABLE', + /** Gift card is disabled. */ + GiftCardDisabled = 'GIFT_CARD_DISABLED', + /** Gift card code is invalid. */ + GiftCardCodeInvalid = 'GIFT_CARD_CODE_INVALID', + /** Gift card has already been applied. */ + GiftCardAlreadyApplied = 'GIFT_CARD_ALREADY_APPLIED', + /** Gift card currency does not match checkout currency. */ + GiftCardCurrencyMismatch = 'GIFT_CARD_CURRENCY_MISMATCH', + /** Gift card is expired. */ + GiftCardExpired = 'GIFT_CARD_EXPIRED', + /** Gift card has no funds left. */ + GiftCardDepleted = 'GIFT_CARD_DEPLETED', + /** Gift card was not found. */ + GiftCardNotFound = 'GIFT_CARD_NOT_FOUND', + /** Cart does not meet discount requirements notice. */ + CartDoesNotMeetDiscountRequirementsNotice = 'CART_DOES_NOT_MEET_DISCOUNT_REQUIREMENTS_NOTICE', + /** Discount expired. */ + DiscountExpired = 'DISCOUNT_EXPIRED', + /** Discount disabled. */ + DiscountDisabled = 'DISCOUNT_DISABLED', + /** Discount limit reached. */ + DiscountLimitReached = 'DISCOUNT_LIMIT_REACHED', + /** Discount not found. */ + DiscountNotFound = 'DISCOUNT_NOT_FOUND', + /** Customer already used once per customer discount notice. */ + CustomerAlreadyUsedOncePerCustomerDiscountNotice = 'CUSTOMER_ALREADY_USED_ONCE_PER_CUSTOMER_DISCOUNT_NOTICE', + /** Checkout is already completed. */ + Empty = 'EMPTY', + /** Not enough in stock. */ + NotEnoughInStock = 'NOT_ENOUGH_IN_STOCK', + /** Missing payment input. */ + MissingPaymentInput = 'MISSING_PAYMENT_INPUT', + /** The amount of the payment does not match the value to be paid. */ + TotalPriceMismatch = 'TOTAL_PRICE_MISMATCH', + /** Line item was not found in checkout. */ + LineItemNotFound = 'LINE_ITEM_NOT_FOUND', + /** Unable to apply discount. */ + UnableToApply = 'UNABLE_TO_APPLY', + /** Discount already applied. */ + DiscountAlreadyApplied = 'DISCOUNT_ALREADY_APPLIED', +} + +/** Return type for `checkoutGiftCardApply` mutation. */ +export type CheckoutGiftCardApplyPayload = { + __typename?: 'CheckoutGiftCardApplyPayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutGiftCardRemove` mutation. */ +export type CheckoutGiftCardRemovePayload = { + __typename?: 'CheckoutGiftCardRemovePayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutGiftCardRemoveV2` mutation. */ +export type CheckoutGiftCardRemoveV2Payload = { + __typename?: 'CheckoutGiftCardRemoveV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutGiftCardsAppend` mutation. */ +export type CheckoutGiftCardsAppendPayload = { + __typename?: 'CheckoutGiftCardsAppendPayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** A single line item in the checkout, grouped by variant and attributes. */ +export type CheckoutLineItem = Node & { + __typename?: 'CheckoutLineItem' + /** Extra information in the form of an array of Key-Value pairs about the line item. */ + customAttributes: Array<Attribute> + /** The discounts that have been allocated onto the checkout line item by discount applications. */ + discountAllocations: Array<DiscountAllocation> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The quantity of the line item. */ + quantity: Scalars['Int'] + /** Title of the line item. Defaults to the product's title. */ + title: Scalars['String'] + /** Unit price of the line item. */ + unitPrice?: Maybe<MoneyV2> + /** Product variant of the line item. */ + variant?: Maybe<ProductVariant> +} + +/** An auto-generated type for paginating through multiple CheckoutLineItems. */ +export type CheckoutLineItemConnection = { + __typename?: 'CheckoutLineItemConnection' + /** A list of edges. */ + edges: Array<CheckoutLineItemEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one CheckoutLineItem and a cursor during pagination. */ +export type CheckoutLineItemEdge = { + __typename?: 'CheckoutLineItemEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of CheckoutLineItemEdge. */ + node: CheckoutLineItem +} + +/** Specifies the input fields to create a line item on a checkout. */ +export type CheckoutLineItemInput = { + /** Extra information in the form of an array of Key-Value pairs about the line item. */ + customAttributes?: Maybe<Array<AttributeInput>> + /** The quantity of the line item. */ + quantity: Scalars['Int'] + /** The identifier of the product variant for the line item. */ + variantId: Scalars['ID'] +} + +/** Specifies the input fields to update a line item on the checkout. */ +export type CheckoutLineItemUpdateInput = { + /** The identifier of the line item. */ + id?: Maybe<Scalars['ID']> + /** The variant identifier of the line item. */ + variantId?: Maybe<Scalars['ID']> + /** The quantity of the line item. */ + quantity?: Maybe<Scalars['Int']> + /** Extra information in the form of an array of Key-Value pairs about the line item. */ + customAttributes?: Maybe<Array<AttributeInput>> +} + +/** Return type for `checkoutLineItemsAdd` mutation. */ +export type CheckoutLineItemsAddPayload = { + __typename?: 'CheckoutLineItemsAddPayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutLineItemsRemove` mutation. */ +export type CheckoutLineItemsRemovePayload = { + __typename?: 'CheckoutLineItemsRemovePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutLineItemsReplace` mutation. */ +export type CheckoutLineItemsReplacePayload = { + __typename?: 'CheckoutLineItemsReplacePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + userErrors: Array<CheckoutUserError> +} + +/** Return type for `checkoutLineItemsUpdate` mutation. */ +export type CheckoutLineItemsUpdatePayload = { + __typename?: 'CheckoutLineItemsUpdatePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutShippingAddressUpdate` mutation. */ +export type CheckoutShippingAddressUpdatePayload = { + __typename?: 'CheckoutShippingAddressUpdatePayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutShippingAddressUpdateV2` mutation. */ +export type CheckoutShippingAddressUpdateV2Payload = { + __typename?: 'CheckoutShippingAddressUpdateV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutShippingLineUpdate` mutation. */ +export type CheckoutShippingLineUpdatePayload = { + __typename?: 'CheckoutShippingLineUpdatePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Represents an error that happens during execution of a checkout mutation. */ +export type CheckoutUserError = DisplayableError & { + __typename?: 'CheckoutUserError' + /** Error code to uniquely identify the error. */ + code?: Maybe<CheckoutErrorCode> + /** Path to the input field which caused the error. */ + field?: Maybe<Array<Scalars['String']>> + /** The error message. */ + message: Scalars['String'] +} + +/** A collection represents a grouping of products that a shop owner can create to organize them or make their shops easier to browse. */ +export type Collection = Node & { + __typename?: 'Collection' + /** Stripped description of the collection, single line with HTML tags removed. */ + description: Scalars['String'] + /** The description of the collection, complete with HTML formatting. */ + descriptionHtml: Scalars['HTML'] + /** + * A human-friendly unique string for the collection automatically generated from its title. + * Limit of 255 characters. + */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** Image associated with the collection. */ + image?: Maybe<Image> + /** List of products in the collection. */ + products: ProductConnection + /** The collection’s name. Limit of 255 characters. */ + title: Scalars['String'] + /** The date and time when the collection was last modified. */ + updatedAt: Scalars['DateTime'] +} + +/** A collection represents a grouping of products that a shop owner can create to organize them or make their shops easier to browse. */ +export type CollectionDescriptionArgs = { + truncateAt?: Maybe<Scalars['Int']> +} + +/** A collection represents a grouping of products that a shop owner can create to organize them or make their shops easier to browse. */ +export type CollectionImageArgs = { + maxWidth?: Maybe<Scalars['Int']> + maxHeight?: Maybe<Scalars['Int']> + crop?: Maybe<CropRegion> + scale?: Maybe<Scalars['Int']> +} + +/** A collection represents a grouping of products that a shop owner can create to organize them or make their shops easier to browse. */ +export type CollectionProductsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductCollectionSortKeys> +} + +/** An auto-generated type for paginating through multiple Collections. */ +export type CollectionConnection = { + __typename?: 'CollectionConnection' + /** A list of edges. */ + edges: Array<CollectionEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Collection and a cursor during pagination. */ +export type CollectionEdge = { + __typename?: 'CollectionEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of CollectionEdge. */ + node: Collection +} + +/** The set of valid sort keys for the Collection query. */ +export enum CollectionSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `updated_at` value. */ + UpdatedAt = 'UPDATED_AT', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** A comment on an article. */ +export type Comment = Node & { + __typename?: 'Comment' + /** The comment’s author. */ + author: CommentAuthor + /** Stripped content of the comment, single line with HTML tags removed. */ + content: Scalars['String'] + /** The content of the comment, complete with HTML formatting. */ + contentHtml: Scalars['HTML'] + /** Globally unique identifier. */ + id: Scalars['ID'] +} + +/** A comment on an article. */ +export type CommentContentArgs = { + truncateAt?: Maybe<Scalars['Int']> +} + +/** The author of a comment. */ +export type CommentAuthor = { + __typename?: 'CommentAuthor' + /** The author's email. */ + email: Scalars['String'] + /** The author’s name. */ + name: Scalars['String'] +} + +/** An auto-generated type for paginating through multiple Comments. */ +export type CommentConnection = { + __typename?: 'CommentConnection' + /** A list of edges. */ + edges: Array<CommentEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Comment and a cursor during pagination. */ +export type CommentEdge = { + __typename?: 'CommentEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of CommentEdge. */ + node: Comment +} + +/** ISO 3166-1 alpha-2 country codes with some differences. */ +export enum CountryCode { + /** Afghanistan. */ + Af = 'AF', + /** Åland Islands. */ + Ax = 'AX', + /** Albania. */ + Al = 'AL', + /** Algeria. */ + Dz = 'DZ', + /** Andorra. */ + Ad = 'AD', + /** Angola. */ + Ao = 'AO', + /** Anguilla. */ + Ai = 'AI', + /** Antigua & Barbuda. */ + Ag = 'AG', + /** Argentina. */ + Ar = 'AR', + /** Armenia. */ + Am = 'AM', + /** Aruba. */ + Aw = 'AW', + /** Australia. */ + Au = 'AU', + /** Austria. */ + At = 'AT', + /** Azerbaijan. */ + Az = 'AZ', + /** Bahamas. */ + Bs = 'BS', + /** Bahrain. */ + Bh = 'BH', + /** Bangladesh. */ + Bd = 'BD', + /** Barbados. */ + Bb = 'BB', + /** Belarus. */ + By = 'BY', + /** Belgium. */ + Be = 'BE', + /** Belize. */ + Bz = 'BZ', + /** Benin. */ + Bj = 'BJ', + /** Bermuda. */ + Bm = 'BM', + /** Bhutan. */ + Bt = 'BT', + /** Bolivia. */ + Bo = 'BO', + /** Bosnia & Herzegovina. */ + Ba = 'BA', + /** Botswana. */ + Bw = 'BW', + /** Bouvet Island. */ + Bv = 'BV', + /** Brazil. */ + Br = 'BR', + /** British Indian Ocean Territory. */ + Io = 'IO', + /** Brunei. */ + Bn = 'BN', + /** Bulgaria. */ + Bg = 'BG', + /** Burkina Faso. */ + Bf = 'BF', + /** Burundi. */ + Bi = 'BI', + /** Cambodia. */ + Kh = 'KH', + /** Canada. */ + Ca = 'CA', + /** Cape Verde. */ + Cv = 'CV', + /** Caribbean Netherlands. */ + Bq = 'BQ', + /** Cayman Islands. */ + Ky = 'KY', + /** Central African Republic. */ + Cf = 'CF', + /** Chad. */ + Td = 'TD', + /** Chile. */ + Cl = 'CL', + /** China. */ + Cn = 'CN', + /** Christmas Island. */ + Cx = 'CX', + /** Cocos (Keeling) Islands. */ + Cc = 'CC', + /** Colombia. */ + Co = 'CO', + /** Comoros. */ + Km = 'KM', + /** Congo - Brazzaville. */ + Cg = 'CG', + /** Congo - Kinshasa. */ + Cd = 'CD', + /** Cook Islands. */ + Ck = 'CK', + /** Costa Rica. */ + Cr = 'CR', + /** Croatia. */ + Hr = 'HR', + /** Cuba. */ + Cu = 'CU', + /** Curaçao. */ + Cw = 'CW', + /** Cyprus. */ + Cy = 'CY', + /** Czechia. */ + Cz = 'CZ', + /** Côte d’Ivoire. */ + Ci = 'CI', + /** Denmark. */ + Dk = 'DK', + /** Djibouti. */ + Dj = 'DJ', + /** Dominica. */ + Dm = 'DM', + /** Dominican Republic. */ + Do = 'DO', + /** Ecuador. */ + Ec = 'EC', + /** Egypt. */ + Eg = 'EG', + /** El Salvador. */ + Sv = 'SV', + /** Equatorial Guinea. */ + Gq = 'GQ', + /** Eritrea. */ + Er = 'ER', + /** Estonia. */ + Ee = 'EE', + /** Eswatini. */ + Sz = 'SZ', + /** Ethiopia. */ + Et = 'ET', + /** Falkland Islands. */ + Fk = 'FK', + /** Faroe Islands. */ + Fo = 'FO', + /** Fiji. */ + Fj = 'FJ', + /** Finland. */ + Fi = 'FI', + /** France. */ + Fr = 'FR', + /** French Guiana. */ + Gf = 'GF', + /** French Polynesia. */ + Pf = 'PF', + /** French Southern Territories. */ + Tf = 'TF', + /** Gabon. */ + Ga = 'GA', + /** Gambia. */ + Gm = 'GM', + /** Georgia. */ + Ge = 'GE', + /** Germany. */ + De = 'DE', + /** Ghana. */ + Gh = 'GH', + /** Gibraltar. */ + Gi = 'GI', + /** Greece. */ + Gr = 'GR', + /** Greenland. */ + Gl = 'GL', + /** Grenada. */ + Gd = 'GD', + /** Guadeloupe. */ + Gp = 'GP', + /** Guatemala. */ + Gt = 'GT', + /** Guernsey. */ + Gg = 'GG', + /** Guinea. */ + Gn = 'GN', + /** Guinea-Bissau. */ + Gw = 'GW', + /** Guyana. */ + Gy = 'GY', + /** Haiti. */ + Ht = 'HT', + /** Heard & McDonald Islands. */ + Hm = 'HM', + /** Vatican City. */ + Va = 'VA', + /** Honduras. */ + Hn = 'HN', + /** Hong Kong SAR. */ + Hk = 'HK', + /** Hungary. */ + Hu = 'HU', + /** Iceland. */ + Is = 'IS', + /** India. */ + In = 'IN', + /** Indonesia. */ + Id = 'ID', + /** Iran. */ + Ir = 'IR', + /** Iraq. */ + Iq = 'IQ', + /** Ireland. */ + Ie = 'IE', + /** Isle of Man. */ + Im = 'IM', + /** Israel. */ + Il = 'IL', + /** Italy. */ + It = 'IT', + /** Jamaica. */ + Jm = 'JM', + /** Japan. */ + Jp = 'JP', + /** Jersey. */ + Je = 'JE', + /** Jordan. */ + Jo = 'JO', + /** Kazakhstan. */ + Kz = 'KZ', + /** Kenya. */ + Ke = 'KE', + /** Kiribati. */ + Ki = 'KI', + /** North Korea. */ + Kp = 'KP', + /** Kosovo. */ + Xk = 'XK', + /** Kuwait. */ + Kw = 'KW', + /** Kyrgyzstan. */ + Kg = 'KG', + /** Laos. */ + La = 'LA', + /** Latvia. */ + Lv = 'LV', + /** Lebanon. */ + Lb = 'LB', + /** Lesotho. */ + Ls = 'LS', + /** Liberia. */ + Lr = 'LR', + /** Libya. */ + Ly = 'LY', + /** Liechtenstein. */ + Li = 'LI', + /** Lithuania. */ + Lt = 'LT', + /** Luxembourg. */ + Lu = 'LU', + /** Macao SAR. */ + Mo = 'MO', + /** Madagascar. */ + Mg = 'MG', + /** Malawi. */ + Mw = 'MW', + /** Malaysia. */ + My = 'MY', + /** Maldives. */ + Mv = 'MV', + /** Mali. */ + Ml = 'ML', + /** Malta. */ + Mt = 'MT', + /** Martinique. */ + Mq = 'MQ', + /** Mauritania. */ + Mr = 'MR', + /** Mauritius. */ + Mu = 'MU', + /** Mayotte. */ + Yt = 'YT', + /** Mexico. */ + Mx = 'MX', + /** Moldova. */ + Md = 'MD', + /** Monaco. */ + Mc = 'MC', + /** Mongolia. */ + Mn = 'MN', + /** Montenegro. */ + Me = 'ME', + /** Montserrat. */ + Ms = 'MS', + /** Morocco. */ + Ma = 'MA', + /** Mozambique. */ + Mz = 'MZ', + /** Myanmar (Burma). */ + Mm = 'MM', + /** Namibia. */ + Na = 'NA', + /** Nauru. */ + Nr = 'NR', + /** Nepal. */ + Np = 'NP', + /** Netherlands. */ + Nl = 'NL', + /** Netherlands Antilles. */ + An = 'AN', + /** New Caledonia. */ + Nc = 'NC', + /** New Zealand. */ + Nz = 'NZ', + /** Nicaragua. */ + Ni = 'NI', + /** Niger. */ + Ne = 'NE', + /** Nigeria. */ + Ng = 'NG', + /** Niue. */ + Nu = 'NU', + /** Norfolk Island. */ + Nf = 'NF', + /** North Macedonia. */ + Mk = 'MK', + /** Norway. */ + No = 'NO', + /** Oman. */ + Om = 'OM', + /** Pakistan. */ + Pk = 'PK', + /** Palestinian Territories. */ + Ps = 'PS', + /** Panama. */ + Pa = 'PA', + /** Papua New Guinea. */ + Pg = 'PG', + /** Paraguay. */ + Py = 'PY', + /** Peru. */ + Pe = 'PE', + /** Philippines. */ + Ph = 'PH', + /** Pitcairn Islands. */ + Pn = 'PN', + /** Poland. */ + Pl = 'PL', + /** Portugal. */ + Pt = 'PT', + /** Qatar. */ + Qa = 'QA', + /** Cameroon. */ + Cm = 'CM', + /** Réunion. */ + Re = 'RE', + /** Romania. */ + Ro = 'RO', + /** Russia. */ + Ru = 'RU', + /** Rwanda. */ + Rw = 'RW', + /** St. Barthélemy. */ + Bl = 'BL', + /** St. Helena. */ + Sh = 'SH', + /** St. Kitts & Nevis. */ + Kn = 'KN', + /** St. Lucia. */ + Lc = 'LC', + /** St. Martin. */ + Mf = 'MF', + /** St. Pierre & Miquelon. */ + Pm = 'PM', + /** Samoa. */ + Ws = 'WS', + /** San Marino. */ + Sm = 'SM', + /** São Tomé & Príncipe. */ + St = 'ST', + /** Saudi Arabia. */ + Sa = 'SA', + /** Senegal. */ + Sn = 'SN', + /** Serbia. */ + Rs = 'RS', + /** Seychelles. */ + Sc = 'SC', + /** Sierra Leone. */ + Sl = 'SL', + /** Singapore. */ + Sg = 'SG', + /** Sint Maarten. */ + Sx = 'SX', + /** Slovakia. */ + Sk = 'SK', + /** Slovenia. */ + Si = 'SI', + /** Solomon Islands. */ + Sb = 'SB', + /** Somalia. */ + So = 'SO', + /** South Africa. */ + Za = 'ZA', + /** South Georgia & South Sandwich Islands. */ + Gs = 'GS', + /** South Korea. */ + Kr = 'KR', + /** South Sudan. */ + Ss = 'SS', + /** Spain. */ + Es = 'ES', + /** Sri Lanka. */ + Lk = 'LK', + /** St. Vincent & Grenadines. */ + Vc = 'VC', + /** Sudan. */ + Sd = 'SD', + /** Suriname. */ + Sr = 'SR', + /** Svalbard & Jan Mayen. */ + Sj = 'SJ', + /** Sweden. */ + Se = 'SE', + /** Switzerland. */ + Ch = 'CH', + /** Syria. */ + Sy = 'SY', + /** Taiwan. */ + Tw = 'TW', + /** Tajikistan. */ + Tj = 'TJ', + /** Tanzania. */ + Tz = 'TZ', + /** Thailand. */ + Th = 'TH', + /** Timor-Leste. */ + Tl = 'TL', + /** Togo. */ + Tg = 'TG', + /** Tokelau. */ + Tk = 'TK', + /** Tonga. */ + To = 'TO', + /** Trinidad & Tobago. */ + Tt = 'TT', + /** Tunisia. */ + Tn = 'TN', + /** Turkey. */ + Tr = 'TR', + /** Turkmenistan. */ + Tm = 'TM', + /** Turks & Caicos Islands. */ + Tc = 'TC', + /** Tuvalu. */ + Tv = 'TV', + /** Uganda. */ + Ug = 'UG', + /** Ukraine. */ + Ua = 'UA', + /** United Arab Emirates. */ + Ae = 'AE', + /** United Kingdom. */ + Gb = 'GB', + /** United States. */ + Us = 'US', + /** U.S. Outlying Islands. */ + Um = 'UM', + /** Uruguay. */ + Uy = 'UY', + /** Uzbekistan. */ + Uz = 'UZ', + /** Vanuatu. */ + Vu = 'VU', + /** Venezuela. */ + Ve = 'VE', + /** Vietnam. */ + Vn = 'VN', + /** British Virgin Islands. */ + Vg = 'VG', + /** Wallis & Futuna. */ + Wf = 'WF', + /** Western Sahara. */ + Eh = 'EH', + /** Yemen. */ + Ye = 'YE', + /** Zambia. */ + Zm = 'ZM', + /** Zimbabwe. */ + Zw = 'ZW', +} + +/** Credit card information used for a payment. */ +export type CreditCard = { + __typename?: 'CreditCard' + /** The brand of the credit card. */ + brand?: Maybe<Scalars['String']> + /** The expiry month of the credit card. */ + expiryMonth?: Maybe<Scalars['Int']> + /** The expiry year of the credit card. */ + expiryYear?: Maybe<Scalars['Int']> + /** The credit card's BIN number. */ + firstDigits?: Maybe<Scalars['String']> + /** The first name of the card holder. */ + firstName?: Maybe<Scalars['String']> + /** The last 4 digits of the credit card. */ + lastDigits?: Maybe<Scalars['String']> + /** The last name of the card holder. */ + lastName?: Maybe<Scalars['String']> + /** The masked credit card number with only the last 4 digits displayed. */ + maskedNumber?: Maybe<Scalars['String']> +} + +/** + * Specifies the fields required to complete a checkout with + * a Shopify vaulted credit card payment. + */ +export type CreditCardPaymentInput = { + /** The amount of the payment. */ + amount: Scalars['Money'] + /** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */ + idempotencyKey: Scalars['String'] + /** The billing address for the payment. */ + billingAddress: MailingAddressInput + /** The ID returned by Shopify's Card Vault. */ + vaultId: Scalars['String'] + /** Executes the payment in test mode if possible. Defaults to `false`. */ + test?: Maybe<Scalars['Boolean']> +} + +/** + * Specifies the fields required to complete a checkout with + * a Shopify vaulted credit card payment. + */ +export type CreditCardPaymentInputV2 = { + /** The amount and currency of the payment. */ + paymentAmount: MoneyInput + /** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */ + idempotencyKey: Scalars['String'] + /** The billing address for the payment. */ + billingAddress: MailingAddressInput + /** The ID returned by Shopify's Card Vault. */ + vaultId: Scalars['String'] + /** Executes the payment in test mode if possible. Defaults to `false`. */ + test?: Maybe<Scalars['Boolean']> +} + +/** The part of the image that should remain after cropping. */ +export enum CropRegion { + /** Keep the center of the image. */ + Center = 'CENTER', + /** Keep the top of the image. */ + Top = 'TOP', + /** Keep the bottom of the image. */ + Bottom = 'BOTTOM', + /** Keep the left of the image. */ + Left = 'LEFT', + /** Keep the right of the image. */ + Right = 'RIGHT', +} + +/** Currency codes. */ +export enum CurrencyCode { + /** United States Dollars (USD). */ + Usd = 'USD', + /** Euro (EUR). */ + Eur = 'EUR', + /** United Kingdom Pounds (GBP). */ + Gbp = 'GBP', + /** Canadian Dollars (CAD). */ + Cad = 'CAD', + /** Afghan Afghani (AFN). */ + Afn = 'AFN', + /** Albanian Lek (ALL). */ + All = 'ALL', + /** Algerian Dinar (DZD). */ + Dzd = 'DZD', + /** Angolan Kwanza (AOA). */ + Aoa = 'AOA', + /** Argentine Pesos (ARS). */ + Ars = 'ARS', + /** Armenian Dram (AMD). */ + Amd = 'AMD', + /** Aruban Florin (AWG). */ + Awg = 'AWG', + /** Australian Dollars (AUD). */ + Aud = 'AUD', + /** Barbadian Dollar (BBD). */ + Bbd = 'BBD', + /** Azerbaijani Manat (AZN). */ + Azn = 'AZN', + /** Bangladesh Taka (BDT). */ + Bdt = 'BDT', + /** Bahamian Dollar (BSD). */ + Bsd = 'BSD', + /** Bahraini Dinar (BHD). */ + Bhd = 'BHD', + /** Burundian Franc (BIF). */ + Bif = 'BIF', + /** Belarusian Ruble (BYN). */ + Byn = 'BYN', + /** Belarusian Ruble (BYR). */ + Byr = 'BYR', + /** Belize Dollar (BZD). */ + Bzd = 'BZD', + /** Bermudian Dollar (BMD). */ + Bmd = 'BMD', + /** Bhutanese Ngultrum (BTN). */ + Btn = 'BTN', + /** Bosnia and Herzegovina Convertible Mark (BAM). */ + Bam = 'BAM', + /** Brazilian Real (BRL). */ + Brl = 'BRL', + /** Bolivian Boliviano (BOB). */ + Bob = 'BOB', + /** Botswana Pula (BWP). */ + Bwp = 'BWP', + /** Brunei Dollar (BND). */ + Bnd = 'BND', + /** Bulgarian Lev (BGN). */ + Bgn = 'BGN', + /** Burmese Kyat (MMK). */ + Mmk = 'MMK', + /** Cambodian Riel. */ + Khr = 'KHR', + /** Cape Verdean escudo (CVE). */ + Cve = 'CVE', + /** Cayman Dollars (KYD). */ + Kyd = 'KYD', + /** Central African CFA Franc (XAF). */ + Xaf = 'XAF', + /** Chilean Peso (CLP). */ + Clp = 'CLP', + /** Chinese Yuan Renminbi (CNY). */ + Cny = 'CNY', + /** Colombian Peso (COP). */ + Cop = 'COP', + /** Comorian Franc (KMF). */ + Kmf = 'KMF', + /** Congolese franc (CDF). */ + Cdf = 'CDF', + /** Costa Rican Colones (CRC). */ + Crc = 'CRC', + /** Croatian Kuna (HRK). */ + Hrk = 'HRK', + /** Czech Koruny (CZK). */ + Czk = 'CZK', + /** Danish Kroner (DKK). */ + Dkk = 'DKK', + /** Djiboutian Franc (DJF). */ + Djf = 'DJF', + /** Dominican Peso (DOP). */ + Dop = 'DOP', + /** East Caribbean Dollar (XCD). */ + Xcd = 'XCD', + /** Egyptian Pound (EGP). */ + Egp = 'EGP', + /** Eritrean Nakfa (ERN). */ + Ern = 'ERN', + /** Ethiopian Birr (ETB). */ + Etb = 'ETB', + /** Falkland Islands Pounds (FKP). */ + Fkp = 'FKP', + /** CFP Franc (XPF). */ + Xpf = 'XPF', + /** Fijian Dollars (FJD). */ + Fjd = 'FJD', + /** Gibraltar Pounds (GIP). */ + Gip = 'GIP', + /** Gambian Dalasi (GMD). */ + Gmd = 'GMD', + /** Ghanaian Cedi (GHS). */ + Ghs = 'GHS', + /** Guatemalan Quetzal (GTQ). */ + Gtq = 'GTQ', + /** Guyanese Dollar (GYD). */ + Gyd = 'GYD', + /** Georgian Lari (GEL). */ + Gel = 'GEL', + /** Guinean Franc (GNF). */ + Gnf = 'GNF', + /** Haitian Gourde (HTG). */ + Htg = 'HTG', + /** Honduran Lempira (HNL). */ + Hnl = 'HNL', + /** Hong Kong Dollars (HKD). */ + Hkd = 'HKD', + /** Hungarian Forint (HUF). */ + Huf = 'HUF', + /** Icelandic Kronur (ISK). */ + Isk = 'ISK', + /** Indian Rupees (INR). */ + Inr = 'INR', + /** Indonesian Rupiah (IDR). */ + Idr = 'IDR', + /** Israeli New Shekel (NIS). */ + Ils = 'ILS', + /** Iranian Rial (IRR). */ + Irr = 'IRR', + /** Iraqi Dinar (IQD). */ + Iqd = 'IQD', + /** Jamaican Dollars (JMD). */ + Jmd = 'JMD', + /** Japanese Yen (JPY). */ + Jpy = 'JPY', + /** Jersey Pound. */ + Jep = 'JEP', + /** Jordanian Dinar (JOD). */ + Jod = 'JOD', + /** Kazakhstani Tenge (KZT). */ + Kzt = 'KZT', + /** Kenyan Shilling (KES). */ + Kes = 'KES', + /** Kiribati Dollar (KID). */ + Kid = 'KID', + /** Kuwaiti Dinar (KWD). */ + Kwd = 'KWD', + /** Kyrgyzstani Som (KGS). */ + Kgs = 'KGS', + /** Laotian Kip (LAK). */ + Lak = 'LAK', + /** Latvian Lati (LVL). */ + Lvl = 'LVL', + /** Lebanese Pounds (LBP). */ + Lbp = 'LBP', + /** Lesotho Loti (LSL). */ + Lsl = 'LSL', + /** Liberian Dollar (LRD). */ + Lrd = 'LRD', + /** Libyan Dinar (LYD). */ + Lyd = 'LYD', + /** Lithuanian Litai (LTL). */ + Ltl = 'LTL', + /** Malagasy Ariary (MGA). */ + Mga = 'MGA', + /** Macedonia Denar (MKD). */ + Mkd = 'MKD', + /** Macanese Pataca (MOP). */ + Mop = 'MOP', + /** Malawian Kwacha (MWK). */ + Mwk = 'MWK', + /** Maldivian Rufiyaa (MVR). */ + Mvr = 'MVR', + /** Mauritanian Ouguiya (MRU). */ + Mru = 'MRU', + /** Mexican Pesos (MXN). */ + Mxn = 'MXN', + /** Malaysian Ringgits (MYR). */ + Myr = 'MYR', + /** Mauritian Rupee (MUR). */ + Mur = 'MUR', + /** Moldovan Leu (MDL). */ + Mdl = 'MDL', + /** Moroccan Dirham. */ + Mad = 'MAD', + /** Mongolian Tugrik. */ + Mnt = 'MNT', + /** Mozambican Metical. */ + Mzn = 'MZN', + /** Namibian Dollar. */ + Nad = 'NAD', + /** Nepalese Rupee (NPR). */ + Npr = 'NPR', + /** Netherlands Antillean Guilder. */ + Ang = 'ANG', + /** New Zealand Dollars (NZD). */ + Nzd = 'NZD', + /** Nicaraguan Córdoba (NIO). */ + Nio = 'NIO', + /** Nigerian Naira (NGN). */ + Ngn = 'NGN', + /** Norwegian Kroner (NOK). */ + Nok = 'NOK', + /** Omani Rial (OMR). */ + Omr = 'OMR', + /** Panamian Balboa (PAB). */ + Pab = 'PAB', + /** Pakistani Rupee (PKR). */ + Pkr = 'PKR', + /** Papua New Guinean Kina (PGK). */ + Pgk = 'PGK', + /** Paraguayan Guarani (PYG). */ + Pyg = 'PYG', + /** Peruvian Nuevo Sol (PEN). */ + Pen = 'PEN', + /** Philippine Peso (PHP). */ + Php = 'PHP', + /** Polish Zlotych (PLN). */ + Pln = 'PLN', + /** Qatari Rial (QAR). */ + Qar = 'QAR', + /** Romanian Lei (RON). */ + Ron = 'RON', + /** Russian Rubles (RUB). */ + Rub = 'RUB', + /** Rwandan Franc (RWF). */ + Rwf = 'RWF', + /** Samoan Tala (WST). */ + Wst = 'WST', + /** Saint Helena Pounds (SHP). */ + Shp = 'SHP', + /** Saudi Riyal (SAR). */ + Sar = 'SAR', + /** Sao Tome And Principe Dobra (STD). */ + Std = 'STD', + /** Serbian dinar (RSD). */ + Rsd = 'RSD', + /** Seychellois Rupee (SCR). */ + Scr = 'SCR', + /** Sierra Leonean Leone (SLL). */ + Sll = 'SLL', + /** Singapore Dollars (SGD). */ + Sgd = 'SGD', + /** Sudanese Pound (SDG). */ + Sdg = 'SDG', + /** Somali Shilling (SOS). */ + Sos = 'SOS', + /** Syrian Pound (SYP). */ + Syp = 'SYP', + /** South African Rand (ZAR). */ + Zar = 'ZAR', + /** South Korean Won (KRW). */ + Krw = 'KRW', + /** South Sudanese Pound (SSP). */ + Ssp = 'SSP', + /** Solomon Islands Dollar (SBD). */ + Sbd = 'SBD', + /** Sri Lankan Rupees (LKR). */ + Lkr = 'LKR', + /** Surinamese Dollar (SRD). */ + Srd = 'SRD', + /** Swazi Lilangeni (SZL). */ + Szl = 'SZL', + /** Swedish Kronor (SEK). */ + Sek = 'SEK', + /** Swiss Francs (CHF). */ + Chf = 'CHF', + /** Taiwan Dollars (TWD). */ + Twd = 'TWD', + /** Thai baht (THB). */ + Thb = 'THB', + /** Tajikistani Somoni (TJS). */ + Tjs = 'TJS', + /** Tanzanian Shilling (TZS). */ + Tzs = 'TZS', + /** Tongan Pa'anga (TOP). */ + Top = 'TOP', + /** Trinidad and Tobago Dollars (TTD). */ + Ttd = 'TTD', + /** Tunisian Dinar (TND). */ + Tnd = 'TND', + /** Turkish Lira (TRY). */ + Try = 'TRY', + /** Turkmenistani Manat (TMT). */ + Tmt = 'TMT', + /** Ugandan Shilling (UGX). */ + Ugx = 'UGX', + /** Ukrainian Hryvnia (UAH). */ + Uah = 'UAH', + /** United Arab Emirates Dirham (AED). */ + Aed = 'AED', + /** Uruguayan Pesos (UYU). */ + Uyu = 'UYU', + /** Uzbekistan som (UZS). */ + Uzs = 'UZS', + /** Vanuatu Vatu (VUV). */ + Vuv = 'VUV', + /** Venezuelan Bolivares (VEF). */ + Vef = 'VEF', + /** Venezuelan Bolivares (VES). */ + Ves = 'VES', + /** Vietnamese đồng (VND). */ + Vnd = 'VND', + /** West African CFA franc (XOF). */ + Xof = 'XOF', + /** Yemeni Rial (YER). */ + Yer = 'YER', + /** Zambian Kwacha (ZMW). */ + Zmw = 'ZMW', +} + +/** A customer represents a customer account with the shop. Customer accounts store contact information for the customer, saving logged-in customers the trouble of having to provide it at every checkout. */ +export type Customer = { + __typename?: 'Customer' + /** Indicates whether the customer has consented to be sent marketing material via email. */ + acceptsMarketing: Scalars['Boolean'] + /** A list of addresses for the customer. */ + addresses: MailingAddressConnection + /** The date and time when the customer was created. */ + createdAt: Scalars['DateTime'] + /** The customer’s default address. */ + defaultAddress?: Maybe<MailingAddress> + /** The customer’s name, email or phone number. */ + displayName: Scalars['String'] + /** The customer’s email address. */ + email?: Maybe<Scalars['String']> + /** The customer’s first name. */ + firstName?: Maybe<Scalars['String']> + /** A unique identifier for the customer. */ + id: Scalars['ID'] + /** The customer's most recently updated, incomplete checkout. */ + lastIncompleteCheckout?: Maybe<Checkout> + /** The customer’s last name. */ + lastName?: Maybe<Scalars['String']> + /** The orders associated with the customer. */ + orders: OrderConnection + /** The customer’s phone number. */ + phone?: Maybe<Scalars['String']> + /** + * A comma separated list of tags that have been added to the customer. + * Additional access scope required: unauthenticated_read_customer_tags. + */ + tags: Array<Scalars['String']> + /** The date and time when the customer information was updated. */ + updatedAt: Scalars['DateTime'] +} + +/** A customer represents a customer account with the shop. Customer accounts store contact information for the customer, saving logged-in customers the trouble of having to provide it at every checkout. */ +export type CustomerAddressesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** A customer represents a customer account with the shop. Customer accounts store contact information for the customer, saving logged-in customers the trouble of having to provide it at every checkout. */ +export type CustomerOrdersArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<OrderSortKeys> + query?: Maybe<Scalars['String']> +} + +/** A CustomerAccessToken represents the unique token required to make modifications to the customer object. */ +export type CustomerAccessToken = { + __typename?: 'CustomerAccessToken' + /** The customer’s access token. */ + accessToken: Scalars['String'] + /** The date and time when the customer access token expires. */ + expiresAt: Scalars['DateTime'] +} + +/** Specifies the input fields required to create a customer access token. */ +export type CustomerAccessTokenCreateInput = { + /** The email associated to the customer. */ + email: Scalars['String'] + /** The login password to be used by the customer. */ + password: Scalars['String'] +} + +/** Return type for `customerAccessTokenCreate` mutation. */ +export type CustomerAccessTokenCreatePayload = { + __typename?: 'CustomerAccessTokenCreatePayload' + /** The newly created customer access token object. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerAccessTokenCreateWithMultipass` mutation. */ +export type CustomerAccessTokenCreateWithMultipassPayload = { + __typename?: 'CustomerAccessTokenCreateWithMultipassPayload' + /** An access token object associated with the customer. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> +} + +/** Return type for `customerAccessTokenDelete` mutation. */ +export type CustomerAccessTokenDeletePayload = { + __typename?: 'CustomerAccessTokenDeletePayload' + /** The destroyed access token. */ + deletedAccessToken?: Maybe<Scalars['String']> + /** ID of the destroyed customer access token. */ + deletedCustomerAccessTokenId?: Maybe<Scalars['String']> + /** List of errors that occurred executing the mutation. */ + userErrors: Array<UserError> +} + +/** Return type for `customerAccessTokenRenew` mutation. */ +export type CustomerAccessTokenRenewPayload = { + __typename?: 'CustomerAccessTokenRenewPayload' + /** The renewed customer access token object. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + userErrors: Array<UserError> +} + +/** Return type for `customerActivateByUrl` mutation. */ +export type CustomerActivateByUrlPayload = { + __typename?: 'CustomerActivateByUrlPayload' + /** The customer that was activated. */ + customer?: Maybe<Customer> + /** A new customer access token for the customer. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> +} + +/** Specifies the input fields required to activate a customer. */ +export type CustomerActivateInput = { + /** The activation token required to activate the customer. */ + activationToken: Scalars['String'] + /** New password that will be set during activation. */ + password: Scalars['String'] +} + +/** Return type for `customerActivate` mutation. */ +export type CustomerActivatePayload = { + __typename?: 'CustomerActivatePayload' + /** The customer object. */ + customer?: Maybe<Customer> + /** A newly created customer access token object for the customer. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerAddressCreate` mutation. */ +export type CustomerAddressCreatePayload = { + __typename?: 'CustomerAddressCreatePayload' + /** The new customer address object. */ + customerAddress?: Maybe<MailingAddress> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerAddressDelete` mutation. */ +export type CustomerAddressDeletePayload = { + __typename?: 'CustomerAddressDeletePayload' + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** ID of the deleted customer address. */ + deletedCustomerAddressId?: Maybe<Scalars['String']> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerAddressUpdate` mutation. */ +export type CustomerAddressUpdatePayload = { + __typename?: 'CustomerAddressUpdatePayload' + /** The customer’s updated mailing address. */ + customerAddress?: Maybe<MailingAddress> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Specifies the fields required to create a new customer. */ +export type CustomerCreateInput = { + /** The customer’s first name. */ + firstName?: Maybe<Scalars['String']> + /** The customer’s last name. */ + lastName?: Maybe<Scalars['String']> + /** The customer’s email. */ + email: Scalars['String'] + /** + * A unique phone number for the customer. + * + * Formatted using E.164 standard. For example, _+16135551111_. + */ + phone?: Maybe<Scalars['String']> + /** The login password used by the customer. */ + password: Scalars['String'] + /** Indicates whether the customer has consented to be sent marketing material via email. */ + acceptsMarketing?: Maybe<Scalars['Boolean']> +} + +/** Return type for `customerCreate` mutation. */ +export type CustomerCreatePayload = { + __typename?: 'CustomerCreatePayload' + /** The created customer object. */ + customer?: Maybe<Customer> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerDefaultAddressUpdate` mutation. */ +export type CustomerDefaultAddressUpdatePayload = { + __typename?: 'CustomerDefaultAddressUpdatePayload' + /** The updated customer object. */ + customer?: Maybe<Customer> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Possible error codes that could be returned by CustomerUserError. */ +export enum CustomerErrorCode { + /** Input value is blank. */ + Blank = 'BLANK', + /** Input value is invalid. */ + Invalid = 'INVALID', + /** Input value is already taken. */ + Taken = 'TAKEN', + /** Input value is too long. */ + TooLong = 'TOO_LONG', + /** Input value is too short. */ + TooShort = 'TOO_SHORT', + /** Unidentified customer. */ + UnidentifiedCustomer = 'UNIDENTIFIED_CUSTOMER', + /** Customer is disabled. */ + CustomerDisabled = 'CUSTOMER_DISABLED', + /** Input password starts or ends with whitespace. */ + PasswordStartsOrEndsWithWhitespace = 'PASSWORD_STARTS_OR_ENDS_WITH_WHITESPACE', + /** Input contains HTML tags. */ + ContainsHtmlTags = 'CONTAINS_HTML_TAGS', + /** Input contains URL. */ + ContainsUrl = 'CONTAINS_URL', + /** Invalid activation token. */ + TokenInvalid = 'TOKEN_INVALID', + /** Customer already enabled. */ + AlreadyEnabled = 'ALREADY_ENABLED', + /** Address does not exist. */ + NotFound = 'NOT_FOUND', + /** Input email contains an invalid domain name. */ + BadDomain = 'BAD_DOMAIN', + /** Multipass token is not valid. */ + InvalidMultipassRequest = 'INVALID_MULTIPASS_REQUEST', +} + +/** Return type for `customerRecover` mutation. */ +export type CustomerRecoverPayload = { + __typename?: 'CustomerRecoverPayload' + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerResetByUrl` mutation. */ +export type CustomerResetByUrlPayload = { + __typename?: 'CustomerResetByUrlPayload' + /** The customer object which was reset. */ + customer?: Maybe<Customer> + /** A newly created customer access token object for the customer. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Specifies the fields required to reset a customer’s password. */ +export type CustomerResetInput = { + /** The reset token required to reset the customer’s password. */ + resetToken: Scalars['String'] + /** New password that will be set as part of the reset password process. */ + password: Scalars['String'] +} + +/** Return type for `customerReset` mutation. */ +export type CustomerResetPayload = { + __typename?: 'CustomerResetPayload' + /** The customer object which was reset. */ + customer?: Maybe<Customer> + /** A newly created customer access token object for the customer. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Specifies the fields required to update the Customer information. */ +export type CustomerUpdateInput = { + /** The customer’s first name. */ + firstName?: Maybe<Scalars['String']> + /** The customer’s last name. */ + lastName?: Maybe<Scalars['String']> + /** The customer’s email. */ + email?: Maybe<Scalars['String']> + /** + * A unique phone number for the customer. + * + * Formatted using E.164 standard. For example, _+16135551111_. To remove the phone number, specify `null`. + */ + phone?: Maybe<Scalars['String']> + /** The login password used by the customer. */ + password?: Maybe<Scalars['String']> + /** Indicates whether the customer has consented to be sent marketing material via email. */ + acceptsMarketing?: Maybe<Scalars['Boolean']> +} + +/** Return type for `customerUpdate` mutation. */ +export type CustomerUpdatePayload = { + __typename?: 'CustomerUpdatePayload' + /** The updated customer object. */ + customer?: Maybe<Customer> + /** + * The newly created customer access token. If the customer's password is updated, all previous access tokens + * (including the one used to perform this mutation) become invalid, and a new token is generated. + */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Represents an error that happens during execution of a customer mutation. */ +export type CustomerUserError = DisplayableError & { + __typename?: 'CustomerUserError' + /** Error code to uniquely identify the error. */ + code?: Maybe<CustomerErrorCode> + /** Path to the input field which caused the error. */ + field?: Maybe<Array<Scalars['String']>> + /** The error message. */ + message: Scalars['String'] +} + +/** Digital wallet, such as Apple Pay, which can be used for accelerated checkouts. */ +export enum DigitalWallet { + /** Apple Pay. */ + ApplePay = 'APPLE_PAY', + /** Android Pay. */ + AndroidPay = 'ANDROID_PAY', + /** Google Pay. */ + GooglePay = 'GOOGLE_PAY', + /** Shopify Pay. */ + ShopifyPay = 'SHOPIFY_PAY', +} + +/** An amount discounting the line that has been allocated by a discount. */ +export type DiscountAllocation = { + __typename?: 'DiscountAllocation' + /** Amount of discount allocated. */ + allocatedAmount: MoneyV2 + /** The discount this allocated amount originated from. */ + discountApplication: DiscountApplication +} + +/** + * Discount applications capture the intentions of a discount source at + * the time of application. + */ +export type DiscountApplication = { + /** The method by which the discount's value is allocated to its entitled items. */ + allocationMethod: DiscountApplicationAllocationMethod + /** Which lines of targetType that the discount is allocated over. */ + targetSelection: DiscountApplicationTargetSelection + /** The type of line that the discount is applicable towards. */ + targetType: DiscountApplicationTargetType + /** The value of the discount application. */ + value: PricingValue +} + +/** The method by which the discount's value is allocated onto its entitled lines. */ +export enum DiscountApplicationAllocationMethod { + /** The value is spread across all entitled lines. */ + Across = 'ACROSS', + /** The value is applied onto every entitled line. */ + Each = 'EACH', + /** The value is specifically applied onto a particular line. */ + One = 'ONE', +} + +/** An auto-generated type for paginating through multiple DiscountApplications. */ +export type DiscountApplicationConnection = { + __typename?: 'DiscountApplicationConnection' + /** A list of edges. */ + edges: Array<DiscountApplicationEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one DiscountApplication and a cursor during pagination. */ +export type DiscountApplicationEdge = { + __typename?: 'DiscountApplicationEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of DiscountApplicationEdge. */ + node: DiscountApplication +} + +/** + * Which lines on the order that the discount is allocated over, of the type + * defined by the Discount Application's target_type. + */ +export enum DiscountApplicationTargetSelection { + /** The discount is allocated onto all the lines. */ + All = 'ALL', + /** The discount is allocated onto only the lines it is entitled for. */ + Entitled = 'ENTITLED', + /** The discount is allocated onto explicitly chosen lines. */ + Explicit = 'EXPLICIT', +} + +/** The type of line (i.e. line item or shipping line) on an order that the discount is applicable towards. */ +export enum DiscountApplicationTargetType { + /** The discount applies onto line items. */ + LineItem = 'LINE_ITEM', + /** The discount applies onto shipping lines. */ + ShippingLine = 'SHIPPING_LINE', +} + +/** + * Discount code applications capture the intentions of a discount code at + * the time that it is applied. + */ +export type DiscountCodeApplication = DiscountApplication & { + __typename?: 'DiscountCodeApplication' + /** The method by which the discount's value is allocated to its entitled items. */ + allocationMethod: DiscountApplicationAllocationMethod + /** Specifies whether the discount code was applied successfully. */ + applicable: Scalars['Boolean'] + /** The string identifying the discount code that was used at the time of application. */ + code: Scalars['String'] + /** Which lines of targetType that the discount is allocated over. */ + targetSelection: DiscountApplicationTargetSelection + /** The type of line that the discount is applicable towards. */ + targetType: DiscountApplicationTargetType + /** The value of the discount application. */ + value: PricingValue +} + +/** Represents an error in the input of a mutation. */ +export type DisplayableError = { + /** Path to the input field which caused the error. */ + field?: Maybe<Array<Scalars['String']>> + /** The error message. */ + message: Scalars['String'] +} + +/** Represents a web address. */ +export type Domain = { + __typename?: 'Domain' + /** The host name of the domain (eg: `example.com`). */ + host: Scalars['String'] + /** Whether SSL is enabled or not. */ + sslEnabled: Scalars['Boolean'] + /** The URL of the domain (eg: `https://example.com`). */ + url: Scalars['URL'] +} + +/** Represents a video hosted outside of Shopify. */ +export type ExternalVideo = Node & + Media & { + __typename?: 'ExternalVideo' + /** A word or phrase to share the nature or contents of a media. */ + alt?: Maybe<Scalars['String']> + /** The URL. */ + embeddedUrl: Scalars['URL'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The media content type. */ + mediaContentType: MediaContentType + /** The preview image for the media. */ + previewImage?: Maybe<Image> + } + +/** Represents a single fulfillment in an order. */ +export type Fulfillment = { + __typename?: 'Fulfillment' + /** List of the fulfillment's line items. */ + fulfillmentLineItems: FulfillmentLineItemConnection + /** The name of the tracking company. */ + trackingCompany?: Maybe<Scalars['String']> + /** + * Tracking information associated with the fulfillment, + * such as the tracking number and tracking URL. + */ + trackingInfo: Array<FulfillmentTrackingInfo> +} + +/** Represents a single fulfillment in an order. */ +export type FulfillmentFulfillmentLineItemsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** Represents a single fulfillment in an order. */ +export type FulfillmentTrackingInfoArgs = { + first?: Maybe<Scalars['Int']> +} + +/** Represents a single line item in a fulfillment. There is at most one fulfillment line item for each order line item. */ +export type FulfillmentLineItem = { + __typename?: 'FulfillmentLineItem' + /** The associated order's line item. */ + lineItem: OrderLineItem + /** The amount fulfilled in this fulfillment. */ + quantity: Scalars['Int'] +} + +/** An auto-generated type for paginating through multiple FulfillmentLineItems. */ +export type FulfillmentLineItemConnection = { + __typename?: 'FulfillmentLineItemConnection' + /** A list of edges. */ + edges: Array<FulfillmentLineItemEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one FulfillmentLineItem and a cursor during pagination. */ +export type FulfillmentLineItemEdge = { + __typename?: 'FulfillmentLineItemEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of FulfillmentLineItemEdge. */ + node: FulfillmentLineItem +} + +/** Tracking information associated with the fulfillment. */ +export type FulfillmentTrackingInfo = { + __typename?: 'FulfillmentTrackingInfo' + /** The tracking number of the fulfillment. */ + number?: Maybe<Scalars['String']> + /** The URL to track the fulfillment. */ + url?: Maybe<Scalars['URL']> +} + +/** Represents information about the metafields associated to the specified resource. */ +export type HasMetafields = { + /** The metafield associated with the resource. */ + metafield?: Maybe<Metafield> + /** A paginated list of metafields associated with the resource. */ + metafields: MetafieldConnection +} + +/** Represents information about the metafields associated to the specified resource. */ +export type HasMetafieldsMetafieldArgs = { + namespace: Scalars['String'] + key: Scalars['String'] +} + +/** Represents information about the metafields associated to the specified resource. */ +export type HasMetafieldsMetafieldsArgs = { + namespace?: Maybe<Scalars['String']> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** Represents an image resource. */ +export type Image = { + __typename?: 'Image' + /** A word or phrase to share the nature or contents of an image. */ + altText?: Maybe<Scalars['String']> + /** The original height of the image in pixels. Returns `null` if the image is not hosted by Shopify. */ + height?: Maybe<Scalars['Int']> + /** A unique identifier for the image. */ + id?: Maybe<Scalars['ID']> + /** + * The location of the original image as a URL. + * + * If there are any existing transformations in the original source URL, they will remain and not be stripped. + */ + originalSrc: Scalars['URL'] + /** + * The location of the image as a URL. + * @deprecated Previously an image had a single `src` field. This could either return the original image + * location or a URL that contained transformations such as sizing or scale. + * + * These transformations were specified by arguments on the parent field. + * + * Now an image has two distinct URL fields: `originalSrc` and `transformedSrc`. + * + * * `originalSrc` - the original unmodified image URL + * * `transformedSrc` - the image URL with the specified transformations included + * + * To migrate to the new fields, image transformations should be moved from the parent field to `transformedSrc`. + * + * Before: + * ```graphql + * { + * shop { + * productImages(maxWidth: 200, scale: 2) { + * edges { + * node { + * src + * } + * } + * } + * } + * } + * ``` + * + * After: + * ```graphql + * { + * shop { + * productImages { + * edges { + * node { + * transformedSrc(maxWidth: 200, scale: 2) + * } + * } + * } + * } + * } + * ``` + * + */ + src: Scalars['URL'] + /** + * The location of the transformed image as a URL. + * + * All transformation arguments are considered "best-effort". If they can be applied to an image, they will be. + * Otherwise any transformations which an image type does not support will be ignored. + */ + transformedSrc: Scalars['URL'] + /** The original width of the image in pixels. Returns `null` if the image is not hosted by Shopify. */ + width?: Maybe<Scalars['Int']> +} + +/** Represents an image resource. */ +export type ImageTransformedSrcArgs = { + maxWidth?: Maybe<Scalars['Int']> + maxHeight?: Maybe<Scalars['Int']> + crop?: Maybe<CropRegion> + scale?: Maybe<Scalars['Int']> + preferredContentType?: Maybe<ImageContentType> +} + +/** An auto-generated type for paginating through multiple Images. */ +export type ImageConnection = { + __typename?: 'ImageConnection' + /** A list of edges. */ + edges: Array<ImageEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** List of supported image content types. */ +export enum ImageContentType { + /** A PNG image. */ + Png = 'PNG', + /** A JPG image. */ + Jpg = 'JPG', + /** A WEBP image. */ + Webp = 'WEBP', +} + +/** An auto-generated type which holds one Image and a cursor during pagination. */ +export type ImageEdge = { + __typename?: 'ImageEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ImageEdge. */ + node: Image +} + +/** Represents a mailing address for customers and shipping. */ +export type MailingAddress = Node & { + __typename?: 'MailingAddress' + /** The first line of the address. Typically the street address or PO Box number. */ + address1?: Maybe<Scalars['String']> + /** The second line of the address. Typically the number of the apartment, suite, or unit. */ + address2?: Maybe<Scalars['String']> + /** The name of the city, district, village, or town. */ + city?: Maybe<Scalars['String']> + /** The name of the customer's company or organization. */ + company?: Maybe<Scalars['String']> + /** The name of the country. */ + country?: Maybe<Scalars['String']> + /** + * The two-letter code for the country of the address. + * + * For example, US. + * @deprecated Use `countryCodeV2` instead + */ + countryCode?: Maybe<Scalars['String']> + /** + * The two-letter code for the country of the address. + * + * For example, US. + */ + countryCodeV2?: Maybe<CountryCode> + /** The first name of the customer. */ + firstName?: Maybe<Scalars['String']> + /** A formatted version of the address, customized by the provided arguments. */ + formatted: Array<Scalars['String']> + /** A comma-separated list of the values for city, province, and country. */ + formattedArea?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The last name of the customer. */ + lastName?: Maybe<Scalars['String']> + /** The latitude coordinate of the customer address. */ + latitude?: Maybe<Scalars['Float']> + /** The longitude coordinate of the customer address. */ + longitude?: Maybe<Scalars['Float']> + /** The full name of the customer, based on firstName and lastName. */ + name?: Maybe<Scalars['String']> + /** + * A unique phone number for the customer. + * + * Formatted using E.164 standard. For example, _+16135551111_. + */ + phone?: Maybe<Scalars['String']> + /** The region of the address, such as the province, state, or district. */ + province?: Maybe<Scalars['String']> + /** + * The two-letter code for the region. + * + * For example, ON. + */ + provinceCode?: Maybe<Scalars['String']> + /** The zip or postal code of the address. */ + zip?: Maybe<Scalars['String']> +} + +/** Represents a mailing address for customers and shipping. */ +export type MailingAddressFormattedArgs = { + withName?: Maybe<Scalars['Boolean']> + withCompany?: Maybe<Scalars['Boolean']> +} + +/** An auto-generated type for paginating through multiple MailingAddresses. */ +export type MailingAddressConnection = { + __typename?: 'MailingAddressConnection' + /** A list of edges. */ + edges: Array<MailingAddressEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one MailingAddress and a cursor during pagination. */ +export type MailingAddressEdge = { + __typename?: 'MailingAddressEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of MailingAddressEdge. */ + node: MailingAddress +} + +/** Specifies the fields accepted to create or update a mailing address. */ +export type MailingAddressInput = { + /** The first line of the address. Typically the street address or PO Box number. */ + address1?: Maybe<Scalars['String']> + /** The second line of the address. Typically the number of the apartment, suite, or unit. */ + address2?: Maybe<Scalars['String']> + /** The name of the city, district, village, or town. */ + city?: Maybe<Scalars['String']> + /** The name of the customer's company or organization. */ + company?: Maybe<Scalars['String']> + /** The name of the country. */ + country?: Maybe<Scalars['String']> + /** The first name of the customer. */ + firstName?: Maybe<Scalars['String']> + /** The last name of the customer. */ + lastName?: Maybe<Scalars['String']> + /** + * A unique phone number for the customer. + * + * Formatted using E.164 standard. For example, _+16135551111_. + */ + phone?: Maybe<Scalars['String']> + /** The region of the address, such as the province, state, or district. */ + province?: Maybe<Scalars['String']> + /** The zip or postal code of the address. */ + zip?: Maybe<Scalars['String']> +} + +/** Manual discount applications capture the intentions of a discount that was manually created. */ +export type ManualDiscountApplication = DiscountApplication & { + __typename?: 'ManualDiscountApplication' + /** The method by which the discount's value is allocated to its entitled items. */ + allocationMethod: DiscountApplicationAllocationMethod + /** The description of the application. */ + description?: Maybe<Scalars['String']> + /** Which lines of targetType that the discount is allocated over. */ + targetSelection: DiscountApplicationTargetSelection + /** The type of line that the discount is applicable towards. */ + targetType: DiscountApplicationTargetType + /** The title of the application. */ + title: Scalars['String'] + /** The value of the discount application. */ + value: PricingValue +} + +/** Represents a media interface. */ +export type Media = { + /** A word or phrase to share the nature or contents of a media. */ + alt?: Maybe<Scalars['String']> + /** The media content type. */ + mediaContentType: MediaContentType + /** The preview image for the media. */ + previewImage?: Maybe<Image> +} + +/** An auto-generated type for paginating through multiple Media. */ +export type MediaConnection = { + __typename?: 'MediaConnection' + /** A list of edges. */ + edges: Array<MediaEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** The possible content types for a media object. */ +export enum MediaContentType { + /** An externally hosted video. */ + ExternalVideo = 'EXTERNAL_VIDEO', + /** A Shopify hosted image. */ + Image = 'IMAGE', + /** A 3d model. */ + Model_3D = 'MODEL_3D', + /** A Shopify hosted video. */ + Video = 'VIDEO', +} + +/** An auto-generated type which holds one Media and a cursor during pagination. */ +export type MediaEdge = { + __typename?: 'MediaEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of MediaEdge. */ + node: Media +} + +/** Represents a Shopify hosted image. */ +export type MediaImage = Node & + Media & { + __typename?: 'MediaImage' + /** A word or phrase to share the nature or contents of a media. */ + alt?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The image for the media. */ + image?: Maybe<Image> + /** The media content type. */ + mediaContentType: MediaContentType + /** The preview image for the media. */ + previewImage?: Maybe<Image> + } + +/** + * Metafields represent custom metadata attached to a resource. Metafields can be sorted into namespaces and are + * comprised of keys, values, and value types. + */ +export type Metafield = Node & { + __typename?: 'Metafield' + /** The date and time when the storefront metafield was created. */ + createdAt: Scalars['DateTime'] + /** The description of a metafield. */ + description?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The key name for a metafield. */ + key: Scalars['String'] + /** The namespace for a metafield. */ + namespace: Scalars['String'] + /** The parent object that the metafield belongs to. */ + parentResource: MetafieldParentResource + /** The date and time when the storefront metafield was updated. */ + updatedAt: Scalars['DateTime'] + /** The value of a metafield. */ + value: Scalars['String'] + /** Represents the metafield value type. */ + valueType: MetafieldValueType +} + +/** An auto-generated type for paginating through multiple Metafields. */ +export type MetafieldConnection = { + __typename?: 'MetafieldConnection' + /** A list of edges. */ + edges: Array<MetafieldEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Metafield and a cursor during pagination. */ +export type MetafieldEdge = { + __typename?: 'MetafieldEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of MetafieldEdge. */ + node: Metafield +} + +/** A resource that the metafield belongs to. */ +export type MetafieldParentResource = Product | ProductVariant + +/** Metafield value types. */ +export enum MetafieldValueType { + /** A string metafield. */ + String = 'STRING', + /** An integer metafield. */ + Integer = 'INTEGER', + /** A json string metafield. */ + JsonString = 'JSON_STRING', +} + +/** Represents a Shopify hosted 3D model. */ +export type Model3d = Node & + Media & { + __typename?: 'Model3d' + /** A word or phrase to share the nature or contents of a media. */ + alt?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The media content type. */ + mediaContentType: MediaContentType + /** The preview image for the media. */ + previewImage?: Maybe<Image> + /** The sources for a 3d model. */ + sources: Array<Model3dSource> + } + +/** Represents a source for a Shopify hosted 3d model. */ +export type Model3dSource = { + __typename?: 'Model3dSource' + /** The filesize of the 3d model. */ + filesize: Scalars['Int'] + /** The format of the 3d model. */ + format: Scalars['String'] + /** The MIME type of the 3d model. */ + mimeType: Scalars['String'] + /** The URL of the 3d model. */ + url: Scalars['String'] +} + +/** Specifies the fields for a monetary value with currency. */ +export type MoneyInput = { + /** Decimal money amount. */ + amount: Scalars['Decimal'] + /** Currency of the money. */ + currencyCode: CurrencyCode +} + +/** + * A monetary value with currency. + * + * To format currencies, combine this type's amount and currencyCode fields with your client's locale. + * + * For example, in JavaScript you could use Intl.NumberFormat: + * + * ```js + * new Intl.NumberFormat(locale, { + * style: 'currency', + * currency: currencyCode + * }).format(amount); + * ``` + * + * Other formatting libraries include: + * + * * iOS - [NumberFormatter](https://developer.apple.com/documentation/foundation/numberformatter) + * * Android - [NumberFormat](https://developer.android.com/reference/java/text/NumberFormat.html) + * * PHP - [NumberFormatter](http://php.net/manual/en/class.numberformatter.php) + * + * For a more general solution, the [Unicode CLDR number formatting database] is available with many implementations + * (such as [TwitterCldr](https://github.com/twitter/twitter-cldr-rb)). + */ +export type MoneyV2 = { + __typename?: 'MoneyV2' + /** Decimal money amount. */ + amount: Scalars['Decimal'] + /** Currency of the money. */ + currencyCode: CurrencyCode +} + +/** An auto-generated type for paginating through multiple MoneyV2s. */ +export type MoneyV2Connection = { + __typename?: 'MoneyV2Connection' + /** A list of edges. */ + edges: Array<MoneyV2Edge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one MoneyV2 and a cursor during pagination. */ +export type MoneyV2Edge = { + __typename?: 'MoneyV2Edge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of MoneyV2Edge. */ + node: MoneyV2 +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type Mutation = { + __typename?: 'Mutation' + /** + * Updates the attributes of a checkout. + * @deprecated Use `checkoutAttributesUpdateV2` instead + */ + checkoutAttributesUpdate?: Maybe<CheckoutAttributesUpdatePayload> + /** Updates the attributes of a checkout. */ + checkoutAttributesUpdateV2?: Maybe<CheckoutAttributesUpdateV2Payload> + /** Completes a checkout without providing payment information. You can use this mutation for free items or items whose purchase price is covered by a gift card. */ + checkoutCompleteFree?: Maybe<CheckoutCompleteFreePayload> + /** + * Completes a checkout using a credit card token from Shopify's Vault. + * @deprecated Use `checkoutCompleteWithCreditCardV2` instead + */ + checkoutCompleteWithCreditCard?: Maybe<CheckoutCompleteWithCreditCardPayload> + /** Completes a checkout using a credit card token from Shopify's card vault. Before you can complete checkouts using CheckoutCompleteWithCreditCardV2, you need to [_request payment processing_](https://help.shopify.com/api/guides/sales-channel-sdk/getting-started#request-payment-processing). */ + checkoutCompleteWithCreditCardV2?: Maybe<CheckoutCompleteWithCreditCardV2Payload> + /** + * Completes a checkout with a tokenized payment. + * @deprecated Use `checkoutCompleteWithTokenizedPaymentV2` instead + */ + checkoutCompleteWithTokenizedPayment?: Maybe<CheckoutCompleteWithTokenizedPaymentPayload> + /** + * Completes a checkout with a tokenized payment. + * @deprecated Use `checkoutCompleteWithTokenizedPaymentV3` instead + */ + checkoutCompleteWithTokenizedPaymentV2?: Maybe<CheckoutCompleteWithTokenizedPaymentV2Payload> + /** Completes a checkout with a tokenized payment. */ + checkoutCompleteWithTokenizedPaymentV3?: Maybe<CheckoutCompleteWithTokenizedPaymentV3Payload> + /** Creates a new checkout. */ + checkoutCreate?: Maybe<CheckoutCreatePayload> + /** + * Associates a customer to the checkout. + * @deprecated Use `checkoutCustomerAssociateV2` instead + */ + checkoutCustomerAssociate?: Maybe<CheckoutCustomerAssociatePayload> + /** Associates a customer to the checkout. */ + checkoutCustomerAssociateV2?: Maybe<CheckoutCustomerAssociateV2Payload> + /** + * Disassociates the current checkout customer from the checkout. + * @deprecated Use `checkoutCustomerDisassociateV2` instead + */ + checkoutCustomerDisassociate?: Maybe<CheckoutCustomerDisassociatePayload> + /** Disassociates the current checkout customer from the checkout. */ + checkoutCustomerDisassociateV2?: Maybe<CheckoutCustomerDisassociateV2Payload> + /** + * Applies a discount to an existing checkout using a discount code. + * @deprecated Use `checkoutDiscountCodeApplyV2` instead + */ + checkoutDiscountCodeApply?: Maybe<CheckoutDiscountCodeApplyPayload> + /** Applies a discount to an existing checkout using a discount code. */ + checkoutDiscountCodeApplyV2?: Maybe<CheckoutDiscountCodeApplyV2Payload> + /** Removes the applied discount from an existing checkout. */ + checkoutDiscountCodeRemove?: Maybe<CheckoutDiscountCodeRemovePayload> + /** + * Updates the email on an existing checkout. + * @deprecated Use `checkoutEmailUpdateV2` instead + */ + checkoutEmailUpdate?: Maybe<CheckoutEmailUpdatePayload> + /** Updates the email on an existing checkout. */ + checkoutEmailUpdateV2?: Maybe<CheckoutEmailUpdateV2Payload> + /** + * Applies a gift card to an existing checkout using a gift card code. This will replace all currently applied gift cards. + * @deprecated Use `checkoutGiftCardsAppend` instead + */ + checkoutGiftCardApply?: Maybe<CheckoutGiftCardApplyPayload> + /** + * Removes an applied gift card from the checkout. + * @deprecated Use `checkoutGiftCardRemoveV2` instead + */ + checkoutGiftCardRemove?: Maybe<CheckoutGiftCardRemovePayload> + /** Removes an applied gift card from the checkout. */ + checkoutGiftCardRemoveV2?: Maybe<CheckoutGiftCardRemoveV2Payload> + /** Appends gift cards to an existing checkout. */ + checkoutGiftCardsAppend?: Maybe<CheckoutGiftCardsAppendPayload> + /** Adds a list of line items to a checkout. */ + checkoutLineItemsAdd?: Maybe<CheckoutLineItemsAddPayload> + /** Removes line items from an existing checkout. */ + checkoutLineItemsRemove?: Maybe<CheckoutLineItemsRemovePayload> + /** Sets a list of line items to a checkout. */ + checkoutLineItemsReplace?: Maybe<CheckoutLineItemsReplacePayload> + /** Updates line items on a checkout. */ + checkoutLineItemsUpdate?: Maybe<CheckoutLineItemsUpdatePayload> + /** + * Updates the shipping address of an existing checkout. + * @deprecated Use `checkoutShippingAddressUpdateV2` instead + */ + checkoutShippingAddressUpdate?: Maybe<CheckoutShippingAddressUpdatePayload> + /** Updates the shipping address of an existing checkout. */ + checkoutShippingAddressUpdateV2?: Maybe<CheckoutShippingAddressUpdateV2Payload> + /** Updates the shipping lines on an existing checkout. */ + checkoutShippingLineUpdate?: Maybe<CheckoutShippingLineUpdatePayload> + /** + * Creates a customer access token. + * The customer access token is required to modify the customer object in any way. + */ + customerAccessTokenCreate?: Maybe<CustomerAccessTokenCreatePayload> + /** + * Creates a customer access token using a multipass token instead of email and password. + * A customer record is created if customer does not exist. If a customer record already + * exists but the record is disabled, then it's enabled. + */ + customerAccessTokenCreateWithMultipass?: Maybe<CustomerAccessTokenCreateWithMultipassPayload> + /** Permanently destroys a customer access token. */ + customerAccessTokenDelete?: Maybe<CustomerAccessTokenDeletePayload> + /** + * Renews a customer access token. + * + * Access token renewal must happen *before* a token expires. + * If a token has already expired, a new one should be created instead via `customerAccessTokenCreate`. + */ + customerAccessTokenRenew?: Maybe<CustomerAccessTokenRenewPayload> + /** Activates a customer. */ + customerActivate?: Maybe<CustomerActivatePayload> + /** Activates a customer with the activation url received from `customerCreate`. */ + customerActivateByUrl?: Maybe<CustomerActivateByUrlPayload> + /** Creates a new address for a customer. */ + customerAddressCreate?: Maybe<CustomerAddressCreatePayload> + /** Permanently deletes the address of an existing customer. */ + customerAddressDelete?: Maybe<CustomerAddressDeletePayload> + /** Updates the address of an existing customer. */ + customerAddressUpdate?: Maybe<CustomerAddressUpdatePayload> + /** Creates a new customer. */ + customerCreate?: Maybe<CustomerCreatePayload> + /** Updates the default address of an existing customer. */ + customerDefaultAddressUpdate?: Maybe<CustomerDefaultAddressUpdatePayload> + /** Sends a reset password email to the customer, as the first step in the reset password process. */ + customerRecover?: Maybe<CustomerRecoverPayload> + /** Resets a customer’s password with a token received from `CustomerRecover`. */ + customerReset?: Maybe<CustomerResetPayload> + /** Resets a customer’s password with the reset password url received from `CustomerRecover`. */ + customerResetByUrl?: Maybe<CustomerResetByUrlPayload> + /** Updates an existing customer. */ + customerUpdate?: Maybe<CustomerUpdatePayload> +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutAttributesUpdateArgs = { + checkoutId: Scalars['ID'] + input: CheckoutAttributesUpdateInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutAttributesUpdateV2Args = { + checkoutId: Scalars['ID'] + input: CheckoutAttributesUpdateV2Input +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteFreeArgs = { + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteWithCreditCardArgs = { + checkoutId: Scalars['ID'] + payment: CreditCardPaymentInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteWithCreditCardV2Args = { + checkoutId: Scalars['ID'] + payment: CreditCardPaymentInputV2 +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteWithTokenizedPaymentArgs = { + checkoutId: Scalars['ID'] + payment: TokenizedPaymentInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteWithTokenizedPaymentV2Args = { + checkoutId: Scalars['ID'] + payment: TokenizedPaymentInputV2 +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteWithTokenizedPaymentV3Args = { + checkoutId: Scalars['ID'] + payment: TokenizedPaymentInputV3 +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCreateArgs = { + input: CheckoutCreateInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCustomerAssociateArgs = { + checkoutId: Scalars['ID'] + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCustomerAssociateV2Args = { + checkoutId: Scalars['ID'] + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCustomerDisassociateArgs = { + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCustomerDisassociateV2Args = { + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutDiscountCodeApplyArgs = { + discountCode: Scalars['String'] + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutDiscountCodeApplyV2Args = { + discountCode: Scalars['String'] + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutDiscountCodeRemoveArgs = { + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutEmailUpdateArgs = { + checkoutId: Scalars['ID'] + email: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutEmailUpdateV2Args = { + checkoutId: Scalars['ID'] + email: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutGiftCardApplyArgs = { + giftCardCode: Scalars['String'] + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutGiftCardRemoveArgs = { + appliedGiftCardId: Scalars['ID'] + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutGiftCardRemoveV2Args = { + appliedGiftCardId: Scalars['ID'] + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutGiftCardsAppendArgs = { + giftCardCodes: Array<Scalars['String']> + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutLineItemsAddArgs = { + lineItems: Array<CheckoutLineItemInput> + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutLineItemsRemoveArgs = { + checkoutId: Scalars['ID'] + lineItemIds: Array<Scalars['ID']> +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutLineItemsReplaceArgs = { + lineItems: Array<CheckoutLineItemInput> + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutLineItemsUpdateArgs = { + checkoutId: Scalars['ID'] + lineItems: Array<CheckoutLineItemUpdateInput> +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutShippingAddressUpdateArgs = { + shippingAddress: MailingAddressInput + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutShippingAddressUpdateV2Args = { + shippingAddress: MailingAddressInput + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutShippingLineUpdateArgs = { + checkoutId: Scalars['ID'] + shippingRateHandle: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAccessTokenCreateArgs = { + input: CustomerAccessTokenCreateInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAccessTokenCreateWithMultipassArgs = { + multipassToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAccessTokenDeleteArgs = { + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAccessTokenRenewArgs = { + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerActivateArgs = { + id: Scalars['ID'] + input: CustomerActivateInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerActivateByUrlArgs = { + activationUrl: Scalars['URL'] + password: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAddressCreateArgs = { + customerAccessToken: Scalars['String'] + address: MailingAddressInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAddressDeleteArgs = { + id: Scalars['ID'] + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAddressUpdateArgs = { + customerAccessToken: Scalars['String'] + id: Scalars['ID'] + address: MailingAddressInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerCreateArgs = { + input: CustomerCreateInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerDefaultAddressUpdateArgs = { + customerAccessToken: Scalars['String'] + addressId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerRecoverArgs = { + email: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerResetArgs = { + id: Scalars['ID'] + input: CustomerResetInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerResetByUrlArgs = { + resetUrl: Scalars['URL'] + password: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerUpdateArgs = { + customerAccessToken: Scalars['String'] + customer: CustomerUpdateInput +} + +/** An object with an ID to support global identification. */ +export type Node = { + /** Globally unique identifier. */ + id: Scalars['ID'] +} + +/** An order is a customer’s completed request to purchase one or more products from a shop. An order is created when a customer completes the checkout process, during which time they provides an email address, billing address and payment information. */ +export type Order = Node & { + __typename?: 'Order' + /** The reason for the order's cancellation. Returns `null` if the order wasn't canceled. */ + cancelReason?: Maybe<OrderCancelReason> + /** The date and time when the order was canceled. Returns null if the order wasn't canceled. */ + canceledAt?: Maybe<Scalars['DateTime']> + /** The code of the currency used for the payment. */ + currencyCode: CurrencyCode + /** The subtotal of line items and their discounts, excluding line items that have been removed. Does not contain order-level discounts, duties, shipping costs, or shipping discounts. Taxes are not included unless the order is a taxes-included order. */ + currentSubtotalPrice: MoneyV2 + /** The total amount of the order, including duties, taxes and discounts, minus amounts for line items that have been removed. */ + currentTotalPrice: MoneyV2 + /** The total of all taxes applied to the order, excluding taxes for returned line items. */ + currentTotalTax: MoneyV2 + /** The locale code in which this specific order happened. */ + customerLocale?: Maybe<Scalars['String']> + /** The unique URL that the customer can use to access the order. */ + customerUrl?: Maybe<Scalars['URL']> + /** Discounts that have been applied on the order. */ + discountApplications: DiscountApplicationConnection + /** Whether the order has had any edits applied or not. */ + edited: Scalars['Boolean'] + /** The customer's email address. */ + email?: Maybe<Scalars['String']> + /** The financial status of the order. */ + financialStatus?: Maybe<OrderFinancialStatus> + /** The fulfillment status for the order. */ + fulfillmentStatus: OrderFulfillmentStatus + /** Globally unique identifier. */ + id: Scalars['ID'] + /** List of the order’s line items. */ + lineItems: OrderLineItemConnection + /** + * Unique identifier for the order that appears on the order. + * For example, _#1000_ or _Store1001. + */ + name: Scalars['String'] + /** A unique numeric identifier for the order for use by shop owner and customer. */ + orderNumber: Scalars['Int'] + /** The total price of the order before any applied edits. */ + originalTotalPrice: MoneyV2 + /** The customer's phone number for receiving SMS notifications. */ + phone?: Maybe<Scalars['String']> + /** + * The date and time when the order was imported. + * This value can be set to dates in the past when importing from other systems. + * If no value is provided, it will be auto-generated based on current date and time. + */ + processedAt: Scalars['DateTime'] + /** The address to where the order will be shipped. */ + shippingAddress?: Maybe<MailingAddress> + /** The discounts that have been allocated onto the shipping line by discount applications. */ + shippingDiscountAllocations: Array<DiscountAllocation> + /** The unique URL for the order's status page. */ + statusUrl: Scalars['URL'] + /** + * Price of the order before shipping and taxes. + * @deprecated Use `subtotalPriceV2` instead + */ + subtotalPrice?: Maybe<Scalars['Money']> + /** Price of the order before duties, shipping and taxes. */ + subtotalPriceV2?: Maybe<MoneyV2> + /** List of the order’s successful fulfillments. */ + successfulFulfillments?: Maybe<Array<Fulfillment>> + /** + * The sum of all the prices of all the items in the order, taxes and discounts included (must be positive). + * @deprecated Use `totalPriceV2` instead + */ + totalPrice: Scalars['Money'] + /** The sum of all the prices of all the items in the order, duties, taxes and discounts included (must be positive). */ + totalPriceV2: MoneyV2 + /** + * The total amount that has been refunded. + * @deprecated Use `totalRefundedV2` instead + */ + totalRefunded: Scalars['Money'] + /** The total amount that has been refunded. */ + totalRefundedV2: MoneyV2 + /** + * The total cost of shipping. + * @deprecated Use `totalShippingPriceV2` instead + */ + totalShippingPrice: Scalars['Money'] + /** The total cost of shipping. */ + totalShippingPriceV2: MoneyV2 + /** + * The total cost of taxes. + * @deprecated Use `totalTaxV2` instead + */ + totalTax?: Maybe<Scalars['Money']> + /** The total cost of taxes. */ + totalTaxV2?: Maybe<MoneyV2> +} + +/** An order is a customer’s completed request to purchase one or more products from a shop. An order is created when a customer completes the checkout process, during which time they provides an email address, billing address and payment information. */ +export type OrderDiscountApplicationsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** An order is a customer’s completed request to purchase one or more products from a shop. An order is created when a customer completes the checkout process, during which time they provides an email address, billing address and payment information. */ +export type OrderLineItemsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** An order is a customer’s completed request to purchase one or more products from a shop. An order is created when a customer completes the checkout process, during which time they provides an email address, billing address and payment information. */ +export type OrderSuccessfulFulfillmentsArgs = { + first?: Maybe<Scalars['Int']> +} + +/** Represents the reason for the order's cancellation. */ +export enum OrderCancelReason { + /** The customer wanted to cancel the order. */ + Customer = 'CUSTOMER', + /** The order was fraudulent. */ + Fraud = 'FRAUD', + /** There was insufficient inventory. */ + Inventory = 'INVENTORY', + /** Payment was declined. */ + Declined = 'DECLINED', + /** The order was canceled for an unlisted reason. */ + Other = 'OTHER', +} + +/** An auto-generated type for paginating through multiple Orders. */ +export type OrderConnection = { + __typename?: 'OrderConnection' + /** A list of edges. */ + edges: Array<OrderEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Order and a cursor during pagination. */ +export type OrderEdge = { + __typename?: 'OrderEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of OrderEdge. */ + node: Order +} + +/** Represents the order's current financial status. */ +export enum OrderFinancialStatus { + /** Displayed as **Pending**. */ + Pending = 'PENDING', + /** Displayed as **Authorized**. */ + Authorized = 'AUTHORIZED', + /** Displayed as **Partially paid**. */ + PartiallyPaid = 'PARTIALLY_PAID', + /** Displayed as **Partially refunded**. */ + PartiallyRefunded = 'PARTIALLY_REFUNDED', + /** Displayed as **Voided**. */ + Voided = 'VOIDED', + /** Displayed as **Paid**. */ + Paid = 'PAID', + /** Displayed as **Refunded**. */ + Refunded = 'REFUNDED', +} + +/** Represents the order's current fulfillment status. */ +export enum OrderFulfillmentStatus { + /** Displayed as **Unfulfilled**. */ + Unfulfilled = 'UNFULFILLED', + /** Displayed as **Partially fulfilled**. */ + PartiallyFulfilled = 'PARTIALLY_FULFILLED', + /** Displayed as **Fulfilled**. */ + Fulfilled = 'FULFILLED', + /** Displayed as **Restocked**. */ + Restocked = 'RESTOCKED', + /** Displayed as **Pending fulfillment**. */ + PendingFulfillment = 'PENDING_FULFILLMENT', + /** Displayed as **Open**. */ + Open = 'OPEN', + /** Displayed as **In progress**. */ + InProgress = 'IN_PROGRESS', + /** Displayed as **Scheduled**. */ + Scheduled = 'SCHEDULED', +} + +/** Represents a single line in an order. There is one line item for each distinct product variant. */ +export type OrderLineItem = { + __typename?: 'OrderLineItem' + /** The number of entries associated to the line item minus the items that have been removed. */ + currentQuantity: Scalars['Int'] + /** List of custom attributes associated to the line item. */ + customAttributes: Array<Attribute> + /** The discounts that have been allocated onto the order line item by discount applications. */ + discountAllocations: Array<DiscountAllocation> + /** The total price of the line item, including discounts, and displayed in the presentment currency. */ + discountedTotalPrice: MoneyV2 + /** The total price of the line item, not including any discounts. The total price is calculated using the original unit price multiplied by the quantity, and it is displayed in the presentment currency. */ + originalTotalPrice: MoneyV2 + /** The number of products variants associated to the line item. */ + quantity: Scalars['Int'] + /** The title of the product combined with title of the variant. */ + title: Scalars['String'] + /** The product variant object associated to the line item. */ + variant?: Maybe<ProductVariant> +} + +/** An auto-generated type for paginating through multiple OrderLineItems. */ +export type OrderLineItemConnection = { + __typename?: 'OrderLineItemConnection' + /** A list of edges. */ + edges: Array<OrderLineItemEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one OrderLineItem and a cursor during pagination. */ +export type OrderLineItemEdge = { + __typename?: 'OrderLineItemEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of OrderLineItemEdge. */ + node: OrderLineItem +} + +/** The set of valid sort keys for the Order query. */ +export enum OrderSortKeys { + /** Sort by the `processed_at` value. */ + ProcessedAt = 'PROCESSED_AT', + /** Sort by the `total_price` value. */ + TotalPrice = 'TOTAL_PRICE', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** Shopify merchants can create pages to hold static HTML content. Each Page object represents a custom page on the online store. */ +export type Page = Node & { + __typename?: 'Page' + /** The description of the page, complete with HTML formatting. */ + body: Scalars['HTML'] + /** Summary of the page body. */ + bodySummary: Scalars['String'] + /** The timestamp of the page creation. */ + createdAt: Scalars['DateTime'] + /** A human-friendly unique string for the page automatically generated from its title. */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The page's SEO information. */ + seo?: Maybe<Seo> + /** The title of the page. */ + title: Scalars['String'] + /** The timestamp of the latest page update. */ + updatedAt: Scalars['DateTime'] + /** The url pointing to the page accessible from the web. */ + url: Scalars['URL'] +} + +/** An auto-generated type for paginating through multiple Pages. */ +export type PageConnection = { + __typename?: 'PageConnection' + /** A list of edges. */ + edges: Array<PageEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Page and a cursor during pagination. */ +export type PageEdge = { + __typename?: 'PageEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of PageEdge. */ + node: Page +} + +/** Information about pagination in a connection. */ +export type PageInfo = { + __typename?: 'PageInfo' + /** Indicates if there are more pages to fetch. */ + hasNextPage: Scalars['Boolean'] + /** Indicates if there are any pages prior to the current page. */ + hasPreviousPage: Scalars['Boolean'] +} + +/** The set of valid sort keys for the Page query. */ +export enum PageSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `updated_at` value. */ + UpdatedAt = 'UPDATED_AT', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** A payment applied to a checkout. */ +export type Payment = Node & { + __typename?: 'Payment' + /** + * The amount of the payment. + * @deprecated Use `amountV2` instead + */ + amount: Scalars['Money'] + /** The amount of the payment. */ + amountV2: MoneyV2 + /** The billing address for the payment. */ + billingAddress?: Maybe<MailingAddress> + /** The checkout to which the payment belongs. */ + checkout: Checkout + /** The credit card used for the payment in the case of direct payments. */ + creditCard?: Maybe<CreditCard> + /** A message describing a processing error during asynchronous processing. */ + errorMessage?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** A client-side generated token to identify a payment and perform idempotent operations. */ + idempotencyKey?: Maybe<Scalars['String']> + /** The URL where the customer needs to be redirected so they can complete the 3D Secure payment flow. */ + nextActionUrl?: Maybe<Scalars['URL']> + /** Whether or not the payment is still processing asynchronously. */ + ready: Scalars['Boolean'] + /** A flag to indicate if the payment is to be done in test mode for gateways that support it. */ + test: Scalars['Boolean'] + /** The actual transaction recorded by Shopify after having processed the payment with the gateway. */ + transaction?: Maybe<Transaction> +} + +/** Settings related to payments. */ +export type PaymentSettings = { + __typename?: 'PaymentSettings' + /** List of the card brands which the shop accepts. */ + acceptedCardBrands: Array<CardBrand> + /** The url pointing to the endpoint to vault credit cards. */ + cardVaultUrl: Scalars['URL'] + /** The country where the shop is located. */ + countryCode: CountryCode + /** The three-letter code for the shop's primary currency. */ + currencyCode: CurrencyCode + /** A list of enabled currencies (ISO 4217 format) that the shop accepts. Merchants can enable currencies from their Shopify Payments settings in the Shopify admin. */ + enabledPresentmentCurrencies: Array<CurrencyCode> + /** The shop’s Shopify Payments account id. */ + shopifyPaymentsAccountId?: Maybe<Scalars['String']> + /** List of the digital wallets which the shop supports. */ + supportedDigitalWallets: Array<DigitalWallet> +} + +/** The valid values for the types of payment token. */ +export enum PaymentTokenType { + /** Apple Pay token type. */ + ApplePay = 'APPLE_PAY', + /** Vault payment token type. */ + Vault = 'VAULT', + /** Shopify Pay token type. */ + ShopifyPay = 'SHOPIFY_PAY', + /** Google Pay token type. */ + GooglePay = 'GOOGLE_PAY', +} + +/** The value of the percentage pricing object. */ +export type PricingPercentageValue = { + __typename?: 'PricingPercentageValue' + /** The percentage value of the object. */ + percentage: Scalars['Float'] +} + +/** The price value (fixed or percentage) for a discount application. */ +export type PricingValue = MoneyV2 | PricingPercentageValue + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type Product = Node & + HasMetafields & { + __typename?: 'Product' + /** Indicates if at least one product variant is available for sale. */ + availableForSale: Scalars['Boolean'] + /** List of collections a product belongs to. */ + collections: CollectionConnection + /** The compare at price of the product across all variants. */ + compareAtPriceRange: ProductPriceRange + /** The date and time when the product was created. */ + createdAt: Scalars['DateTime'] + /** Stripped description of the product, single line with HTML tags removed. */ + description: Scalars['String'] + /** The description of the product, complete with HTML formatting. */ + descriptionHtml: Scalars['HTML'] + /** + * A human-friendly unique string for the Product automatically generated from its title. + * They are used by the Liquid templating language to refer to objects. + */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** List of images associated with the product. */ + images: ImageConnection + /** The media associated with the product. */ + media: MediaConnection + /** The metafield associated with the resource. */ + metafield?: Maybe<Metafield> + /** A paginated list of metafields associated with the resource. */ + metafields: MetafieldConnection + /** + * The online store URL for the product. + * A value of `null` indicates that the product is not published to the Online Store sales channel. + */ + onlineStoreUrl?: Maybe<Scalars['URL']> + /** List of product options. */ + options: Array<ProductOption> + /** List of price ranges in the presentment currencies for this shop. */ + presentmentPriceRanges: ProductPriceRangeConnection + /** The price range. */ + priceRange: ProductPriceRange + /** A categorization that a product can be tagged with, commonly used for filtering and searching. */ + productType: Scalars['String'] + /** The date and time when the product was published to the channel. */ + publishedAt: Scalars['DateTime'] + /** The product's SEO information. */ + seo: Seo + /** + * A comma separated list of tags that have been added to the product. + * Additional access scope required for private apps: unauthenticated_read_product_tags. + */ + tags: Array<Scalars['String']> + /** The product’s title. */ + title: Scalars['String'] + /** The total quantity of inventory in stock for this Product. */ + totalInventory?: Maybe<Scalars['Int']> + /** + * The date and time when the product was last modified. + * A product's `updatedAt` value can change for different reasons. For example, if an order + * is placed for a product that has inventory tracking set up, then the inventory adjustment + * is counted as an update. + */ + updatedAt: Scalars['DateTime'] + /** + * Find a product’s variant based on its selected options. + * This is useful for converting a user’s selection of product options into a single matching variant. + * If there is not a variant for the selected options, `null` will be returned. + */ + variantBySelectedOptions?: Maybe<ProductVariant> + /** List of the product’s variants. */ + variants: ProductVariantConnection + /** The product’s vendor name. */ + vendor: Scalars['String'] + } + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductCollectionsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductDescriptionArgs = { + truncateAt?: Maybe<Scalars['Int']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductImagesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductImageSortKeys> + maxWidth?: Maybe<Scalars['Int']> + maxHeight?: Maybe<Scalars['Int']> + crop?: Maybe<CropRegion> + scale?: Maybe<Scalars['Int']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductMediaArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductMediaSortKeys> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductMetafieldArgs = { + namespace: Scalars['String'] + key: Scalars['String'] +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductMetafieldsArgs = { + namespace?: Maybe<Scalars['String']> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductOptionsArgs = { + first?: Maybe<Scalars['Int']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductPresentmentPriceRangesArgs = { + presentmentCurrencies?: Maybe<Array<CurrencyCode>> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductVariantBySelectedOptionsArgs = { + selectedOptions: Array<SelectedOptionInput> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductVariantsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductVariantSortKeys> +} + +/** The set of valid sort keys for the ProductCollection query. */ +export enum ProductCollectionSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `price` value. */ + Price = 'PRICE', + /** Sort by the `best-selling` value. */ + BestSelling = 'BEST_SELLING', + /** Sort by the `created` value. */ + Created = 'CREATED', + /** Sort by the `id` value. */ + Id = 'ID', + /** Sort by the `manual` value. */ + Manual = 'MANUAL', + /** Sort by the `collection-default` value. */ + CollectionDefault = 'COLLECTION_DEFAULT', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** An auto-generated type for paginating through multiple Products. */ +export type ProductConnection = { + __typename?: 'ProductConnection' + /** A list of edges. */ + edges: Array<ProductEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Product and a cursor during pagination. */ +export type ProductEdge = { + __typename?: 'ProductEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ProductEdge. */ + node: Product +} + +/** The set of valid sort keys for the ProductImage query. */ +export enum ProductImageSortKeys { + /** Sort by the `created_at` value. */ + CreatedAt = 'CREATED_AT', + /** Sort by the `position` value. */ + Position = 'POSITION', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** The set of valid sort keys for the ProductMedia query. */ +export enum ProductMediaSortKeys { + /** Sort by the `position` value. */ + Position = 'POSITION', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** + * Product property names like "Size", "Color", and "Material" that the customers can select. + * Variants are selected based on permutations of these options. + * 255 characters limit each. + */ +export type ProductOption = Node & { + __typename?: 'ProductOption' + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The product option’s name. */ + name: Scalars['String'] + /** The corresponding value to the product option name. */ + values: Array<Scalars['String']> +} + +/** The price range of the product. */ +export type ProductPriceRange = { + __typename?: 'ProductPriceRange' + /** The highest variant's price. */ + maxVariantPrice: MoneyV2 + /** The lowest variant's price. */ + minVariantPrice: MoneyV2 +} + +/** An auto-generated type for paginating through multiple ProductPriceRanges. */ +export type ProductPriceRangeConnection = { + __typename?: 'ProductPriceRangeConnection' + /** A list of edges. */ + edges: Array<ProductPriceRangeEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one ProductPriceRange and a cursor during pagination. */ +export type ProductPriceRangeEdge = { + __typename?: 'ProductPriceRangeEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ProductPriceRangeEdge. */ + node: ProductPriceRange +} + +/** The set of valid sort keys for the Product query. */ +export enum ProductSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `product_type` value. */ + ProductType = 'PRODUCT_TYPE', + /** Sort by the `vendor` value. */ + Vendor = 'VENDOR', + /** Sort by the `updated_at` value. */ + UpdatedAt = 'UPDATED_AT', + /** Sort by the `created_at` value. */ + CreatedAt = 'CREATED_AT', + /** Sort by the `best_selling` value. */ + BestSelling = 'BEST_SELLING', + /** Sort by the `price` value. */ + Price = 'PRICE', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariant = Node & + HasMetafields & { + __typename?: 'ProductVariant' + /** + * Indicates if the product variant is in stock. + * @deprecated Use `availableForSale` instead + */ + available?: Maybe<Scalars['Boolean']> + /** Indicates if the product variant is available for sale. */ + availableForSale: Scalars['Boolean'] + /** + * The compare at price of the variant. This can be used to mark a variant as on sale, when `compareAtPrice` is higher than `price`. + * @deprecated Use `compareAtPriceV2` instead + */ + compareAtPrice?: Maybe<Scalars['Money']> + /** The compare at price of the variant. This can be used to mark a variant as on sale, when `compareAtPriceV2` is higher than `priceV2`. */ + compareAtPriceV2?: Maybe<MoneyV2> + /** Whether a product is out of stock but still available for purchase (used for backorders). */ + currentlyNotInStock: Scalars['Boolean'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** Image associated with the product variant. This field falls back to the product image if no image is available. */ + image?: Maybe<Image> + /** The metafield associated with the resource. */ + metafield?: Maybe<Metafield> + /** A paginated list of metafields associated with the resource. */ + metafields: MetafieldConnection + /** List of prices and compare-at prices in the presentment currencies for this shop. */ + presentmentPrices: ProductVariantPricePairConnection + /** List of unit prices in the presentment currencies for this shop. */ + presentmentUnitPrices: MoneyV2Connection + /** + * The product variant’s price. + * @deprecated Use `priceV2` instead + */ + price: Scalars['Money'] + /** The product variant’s price. */ + priceV2: MoneyV2 + /** The product object that the product variant belongs to. */ + product: Product + /** The total sellable quantity of the variant for online sales channels. */ + quantityAvailable?: Maybe<Scalars['Int']> + /** Whether a customer needs to provide a shipping address when placing an order for the product variant. */ + requiresShipping: Scalars['Boolean'] + /** List of product options applied to the variant. */ + selectedOptions: Array<SelectedOption> + /** The SKU (stock keeping unit) associated with the variant. */ + sku?: Maybe<Scalars['String']> + /** The product variant’s title. */ + title: Scalars['String'] + /** The unit price value for the variant based on the variant's measurement. */ + unitPrice?: Maybe<MoneyV2> + /** The unit price measurement for the variant. */ + unitPriceMeasurement?: Maybe<UnitPriceMeasurement> + /** The weight of the product variant in the unit system specified with `weight_unit`. */ + weight?: Maybe<Scalars['Float']> + /** Unit of measurement for weight. */ + weightUnit: WeightUnit + } + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariantImageArgs = { + maxWidth?: Maybe<Scalars['Int']> + maxHeight?: Maybe<Scalars['Int']> + crop?: Maybe<CropRegion> + scale?: Maybe<Scalars['Int']> +} + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariantMetafieldArgs = { + namespace: Scalars['String'] + key: Scalars['String'] +} + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariantMetafieldsArgs = { + namespace?: Maybe<Scalars['String']> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariantPresentmentPricesArgs = { + presentmentCurrencies?: Maybe<Array<CurrencyCode>> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariantPresentmentUnitPricesArgs = { + presentmentCurrencies?: Maybe<Array<CurrencyCode>> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** An auto-generated type for paginating through multiple ProductVariants. */ +export type ProductVariantConnection = { + __typename?: 'ProductVariantConnection' + /** A list of edges. */ + edges: Array<ProductVariantEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one ProductVariant and a cursor during pagination. */ +export type ProductVariantEdge = { + __typename?: 'ProductVariantEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ProductVariantEdge. */ + node: ProductVariant +} + +/** The compare-at price and price of a variant sharing a currency. */ +export type ProductVariantPricePair = { + __typename?: 'ProductVariantPricePair' + /** The compare-at price of the variant with associated currency. */ + compareAtPrice?: Maybe<MoneyV2> + /** The price of the variant with associated currency. */ + price: MoneyV2 +} + +/** An auto-generated type for paginating through multiple ProductVariantPricePairs. */ +export type ProductVariantPricePairConnection = { + __typename?: 'ProductVariantPricePairConnection' + /** A list of edges. */ + edges: Array<ProductVariantPricePairEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one ProductVariantPricePair and a cursor during pagination. */ +export type ProductVariantPricePairEdge = { + __typename?: 'ProductVariantPricePairEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ProductVariantPricePairEdge. */ + node: ProductVariantPricePair +} + +/** The set of valid sort keys for the ProductVariant query. */ +export enum ProductVariantSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `sku` value. */ + Sku = 'SKU', + /** Sort by the `position` value. */ + Position = 'POSITION', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRoot = { + __typename?: 'QueryRoot' + /** List of the shop's articles. */ + articles: ArticleConnection + /** Find a blog by its handle. */ + blogByHandle?: Maybe<Blog> + /** List of the shop's blogs. */ + blogs: BlogConnection + /** Find a collection by its handle. */ + collectionByHandle?: Maybe<Collection> + /** List of the shop’s collections. */ + collections: CollectionConnection + /** Find a customer by its access token. */ + customer?: Maybe<Customer> + node?: Maybe<Node> + nodes: Array<Maybe<Node>> + /** Find a page by its handle. */ + pageByHandle?: Maybe<Page> + /** List of the shop's pages. */ + pages: PageConnection + /** Find a product by its handle. */ + productByHandle?: Maybe<Product> + /** + * Find recommended products related to a given `product_id`. + * To learn more about how recommendations are generated, see + * [*Showing product recommendations on product pages*](https://help.shopify.com/themes/development/recommended-products). + */ + productRecommendations?: Maybe<Array<Product>> + /** + * Tags added to products. + * Additional access scope required: unauthenticated_read_product_tags. + */ + productTags: StringConnection + /** List of product types for the shop's products that are published to your app. */ + productTypes: StringConnection + /** List of the shop’s products. */ + products: ProductConnection + /** The list of public Storefront API versions, including supported, release candidate and unstable versions. */ + publicApiVersions: Array<ApiVersion> + /** The shop associated with the storefront access token. */ + shop: Shop +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootArticlesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ArticleSortKeys> + query?: Maybe<Scalars['String']> +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootBlogByHandleArgs = { + handle: Scalars['String'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootBlogsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<BlogSortKeys> + query?: Maybe<Scalars['String']> +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootCollectionByHandleArgs = { + handle: Scalars['String'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootCollectionsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<CollectionSortKeys> + query?: Maybe<Scalars['String']> +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootCustomerArgs = { + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootNodeArgs = { + id: Scalars['ID'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootNodesArgs = { + ids: Array<Scalars['ID']> +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootPageByHandleArgs = { + handle: Scalars['String'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootPagesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<PageSortKeys> + query?: Maybe<Scalars['String']> +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootProductByHandleArgs = { + handle: Scalars['String'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootProductRecommendationsArgs = { + productId: Scalars['ID'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootProductTagsArgs = { + first: Scalars['Int'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootProductTypesArgs = { + first: Scalars['Int'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootProductsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductSortKeys> + query?: Maybe<Scalars['String']> +} + +/** SEO information. */ +export type Seo = { + __typename?: 'SEO' + /** The meta description. */ + description?: Maybe<Scalars['String']> + /** The SEO title. */ + title?: Maybe<Scalars['String']> +} + +/** + * Script discount applications capture the intentions of a discount that + * was created by a Shopify Script. + */ +export type ScriptDiscountApplication = DiscountApplication & { + __typename?: 'ScriptDiscountApplication' + /** The method by which the discount's value is allocated to its entitled items. */ + allocationMethod: DiscountApplicationAllocationMethod + /** + * The description of the application as defined by the Script. + * @deprecated Use `title` instead + */ + description: Scalars['String'] + /** Which lines of targetType that the discount is allocated over. */ + targetSelection: DiscountApplicationTargetSelection + /** The type of line that the discount is applicable towards. */ + targetType: DiscountApplicationTargetType + /** The title of the application as defined by the Script. */ + title: Scalars['String'] + /** The value of the discount application. */ + value: PricingValue +} + +/** + * Properties used by customers to select a product variant. + * Products can have multiple options, like different sizes or colors. + */ +export type SelectedOption = { + __typename?: 'SelectedOption' + /** The product option’s name. */ + name: Scalars['String'] + /** The product option’s value. */ + value: Scalars['String'] +} + +/** Specifies the input fields required for a selected option. */ +export type SelectedOptionInput = { + /** The product option’s name. */ + name: Scalars['String'] + /** The product option’s value. */ + value: Scalars['String'] +} + +/** A shipping rate to be applied to a checkout. */ +export type ShippingRate = { + __typename?: 'ShippingRate' + /** Human-readable unique identifier for this shipping rate. */ + handle: Scalars['String'] + /** + * Price of this shipping rate. + * @deprecated Use `priceV2` instead + */ + price: Scalars['Money'] + /** Price of this shipping rate. */ + priceV2: MoneyV2 + /** Title of this shipping rate. */ + title: Scalars['String'] +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type Shop = { + __typename?: 'Shop' + /** + * List of the shop' articles. + * @deprecated Use `QueryRoot.articles` instead. + */ + articles: ArticleConnection + /** + * List of the shop' blogs. + * @deprecated Use `QueryRoot.blogs` instead. + */ + blogs: BlogConnection + /** + * Find a collection by its handle. + * @deprecated Use `QueryRoot.collectionByHandle` instead. + */ + collectionByHandle?: Maybe<Collection> + /** + * List of the shop’s collections. + * @deprecated Use `QueryRoot.collections` instead. + */ + collections: CollectionConnection + /** + * The three-letter code for the currency that the shop accepts. + * @deprecated Use `paymentSettings` instead + */ + currencyCode: CurrencyCode + /** A description of the shop. */ + description?: Maybe<Scalars['String']> + /** A string representing the way currency is formatted when the currency isn’t specified. */ + moneyFormat: Scalars['String'] + /** The shop’s name. */ + name: Scalars['String'] + /** Settings related to payments. */ + paymentSettings: PaymentSettings + /** The shop’s primary domain. */ + primaryDomain: Domain + /** The shop’s privacy policy. */ + privacyPolicy?: Maybe<ShopPolicy> + /** + * Find a product by its handle. + * @deprecated Use `QueryRoot.productByHandle` instead. + */ + productByHandle?: Maybe<Product> + /** + * A list of tags that have been added to products. + * Additional access scope required: unauthenticated_read_product_tags. + * @deprecated Use `QueryRoot.productTags` instead. + */ + productTags: StringConnection + /** + * List of the shop’s product types. + * @deprecated Use `QueryRoot.productTypes` instead. + */ + productTypes: StringConnection + /** + * List of the shop’s products. + * @deprecated Use `QueryRoot.products` instead. + */ + products: ProductConnection + /** The shop’s refund policy. */ + refundPolicy?: Maybe<ShopPolicy> + /** The shop’s shipping policy. */ + shippingPolicy?: Maybe<ShopPolicy> + /** Countries that the shop ships to. */ + shipsToCountries: Array<CountryCode> + /** + * The shop’s Shopify Payments account id. + * @deprecated Use `paymentSettings` instead + */ + shopifyPaymentsAccountId?: Maybe<Scalars['String']> + /** The shop’s terms of service. */ + termsOfService?: Maybe<ShopPolicy> +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopArticlesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ArticleSortKeys> + query?: Maybe<Scalars['String']> +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopBlogsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<BlogSortKeys> + query?: Maybe<Scalars['String']> +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopCollectionByHandleArgs = { + handle: Scalars['String'] +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopCollectionsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<CollectionSortKeys> + query?: Maybe<Scalars['String']> +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopProductByHandleArgs = { + handle: Scalars['String'] +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopProductTagsArgs = { + first: Scalars['Int'] +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopProductTypesArgs = { + first: Scalars['Int'] +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopProductsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductSortKeys> + query?: Maybe<Scalars['String']> +} + +/** Policy that a merchant has configured for their store, such as their refund or privacy policy. */ +export type ShopPolicy = Node & { + __typename?: 'ShopPolicy' + /** Policy text, maximum size of 64kb. */ + body: Scalars['String'] + /** Policy’s handle. */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** Policy’s title. */ + title: Scalars['String'] + /** Public URL to the policy. */ + url: Scalars['URL'] +} + +/** An auto-generated type for paginating through multiple Strings. */ +export type StringConnection = { + __typename?: 'StringConnection' + /** A list of edges. */ + edges: Array<StringEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one String and a cursor during pagination. */ +export type StringEdge = { + __typename?: 'StringEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of StringEdge. */ + node: Scalars['String'] +} + +/** + * Specifies the fields required to complete a checkout with + * a tokenized payment. + */ +export type TokenizedPaymentInput = { + /** The amount of the payment. */ + amount: Scalars['Money'] + /** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */ + idempotencyKey: Scalars['String'] + /** The billing address for the payment. */ + billingAddress: MailingAddressInput + /** The type of payment token. */ + type: Scalars['String'] + /** A simple string or JSON containing the required payment data for the tokenized payment. */ + paymentData: Scalars['String'] + /** Executes the payment in test mode if possible. Defaults to `false`. */ + test?: Maybe<Scalars['Boolean']> + /** Public Hash Key used for AndroidPay payments only. */ + identifier?: Maybe<Scalars['String']> +} + +/** + * Specifies the fields required to complete a checkout with + * a tokenized payment. + */ +export type TokenizedPaymentInputV2 = { + /** The amount and currency of the payment. */ + paymentAmount: MoneyInput + /** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */ + idempotencyKey: Scalars['String'] + /** The billing address for the payment. */ + billingAddress: MailingAddressInput + /** A simple string or JSON containing the required payment data for the tokenized payment. */ + paymentData: Scalars['String'] + /** Whether to execute the payment in test mode, if possible. Test mode is not supported in production stores. Defaults to `false`. */ + test?: Maybe<Scalars['Boolean']> + /** Public Hash Key used for AndroidPay payments only. */ + identifier?: Maybe<Scalars['String']> + /** The type of payment token. */ + type: Scalars['String'] +} + +/** + * Specifies the fields required to complete a checkout with + * a tokenized payment. + */ +export type TokenizedPaymentInputV3 = { + /** The amount and currency of the payment. */ + paymentAmount: MoneyInput + /** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */ + idempotencyKey: Scalars['String'] + /** The billing address for the payment. */ + billingAddress: MailingAddressInput + /** A simple string or JSON containing the required payment data for the tokenized payment. */ + paymentData: Scalars['String'] + /** Whether to execute the payment in test mode, if possible. Test mode is not supported in production stores. Defaults to `false`. */ + test?: Maybe<Scalars['Boolean']> + /** Public Hash Key used for AndroidPay payments only. */ + identifier?: Maybe<Scalars['String']> + /** The type of payment token. */ + type: PaymentTokenType +} + +/** An object representing exchange of money for a product or service. */ +export type Transaction = { + __typename?: 'Transaction' + /** + * The amount of money that the transaction was for. + * @deprecated Use `amountV2` instead + */ + amount: Scalars['Money'] + /** The amount of money that the transaction was for. */ + amountV2: MoneyV2 + /** The kind of the transaction. */ + kind: TransactionKind + /** + * The status of the transaction. + * @deprecated Use `statusV2` instead + */ + status: TransactionStatus + /** The status of the transaction. */ + statusV2?: Maybe<TransactionStatus> + /** Whether the transaction was done in test mode or not. */ + test: Scalars['Boolean'] +} + +export enum TransactionKind { + Sale = 'SALE', + Capture = 'CAPTURE', + Authorization = 'AUTHORIZATION', + EmvAuthorization = 'EMV_AUTHORIZATION', + Change = 'CHANGE', +} + +export enum TransactionStatus { + Pending = 'PENDING', + Success = 'SUCCESS', + Failure = 'FAILURE', + Error = 'ERROR', +} + +/** The measurement used to calculate a unit price for a product variant (e.g. $9.99 / 100ml). */ +export type UnitPriceMeasurement = { + __typename?: 'UnitPriceMeasurement' + /** The type of unit of measurement for the unit price measurement. */ + measuredType?: Maybe<UnitPriceMeasurementMeasuredType> + /** The quantity unit for the unit price measurement. */ + quantityUnit?: Maybe<UnitPriceMeasurementMeasuredUnit> + /** The quantity value for the unit price measurement. */ + quantityValue: Scalars['Float'] + /** The reference unit for the unit price measurement. */ + referenceUnit?: Maybe<UnitPriceMeasurementMeasuredUnit> + /** The reference value for the unit price measurement. */ + referenceValue: Scalars['Int'] +} + +/** The accepted types of unit of measurement. */ +export enum UnitPriceMeasurementMeasuredType { + /** Unit of measurements representing volumes. */ + Volume = 'VOLUME', + /** Unit of measurements representing weights. */ + Weight = 'WEIGHT', + /** Unit of measurements representing lengths. */ + Length = 'LENGTH', + /** Unit of measurements representing areas. */ + Area = 'AREA', +} + +/** The valid units of measurement for a unit price measurement. */ +export enum UnitPriceMeasurementMeasuredUnit { + /** 1000 milliliters equals 1 liter. */ + Ml = 'ML', + /** 100 centiliters equals 1 liter. */ + Cl = 'CL', + /** Metric system unit of volume. */ + L = 'L', + /** 1 cubic meter equals 1000 liters. */ + M3 = 'M3', + /** 1000 milligrams equals 1 gram. */ + Mg = 'MG', + /** Metric system unit of weight. */ + G = 'G', + /** 1 kilogram equals 1000 grams. */ + Kg = 'KG', + /** 1000 millimeters equals 1 meter. */ + Mm = 'MM', + /** 100 centimeters equals 1 meter. */ + Cm = 'CM', + /** Metric system unit of length. */ + M = 'M', + /** Metric system unit of area. */ + M2 = 'M2', +} + +/** Represents an error in the input of a mutation. */ +export type UserError = DisplayableError & { + __typename?: 'UserError' + /** Path to the input field which caused the error. */ + field?: Maybe<Array<Scalars['String']>> + /** The error message. */ + message: Scalars['String'] +} + +/** Represents a Shopify hosted video. */ +export type Video = Node & + Media & { + __typename?: 'Video' + /** A word or phrase to share the nature or contents of a media. */ + alt?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The media content type. */ + mediaContentType: MediaContentType + /** The preview image for the media. */ + previewImage?: Maybe<Image> + /** The sources for a video. */ + sources: Array<VideoSource> + } + +/** Represents a source for a Shopify hosted video. */ +export type VideoSource = { + __typename?: 'VideoSource' + /** The format of the video source. */ + format: Scalars['String'] + /** The height of the video. */ + height: Scalars['Int'] + /** The video MIME type. */ + mimeType: Scalars['String'] + /** The URL of the video. */ + url: Scalars['String'] + /** The width of the video. */ + width: Scalars['Int'] +} + +/** Units of measurement for weight. */ +export enum WeightUnit { + /** 1 kilogram equals 1000 grams. */ + Kilograms = 'KILOGRAMS', + /** Metric system unit of mass. */ + Grams = 'GRAMS', + /** 1 pound equals 16 ounces. */ + Pounds = 'POUNDS', + /** Imperial system unit of mass. */ + Ounces = 'OUNCES', +} + +export type Unnamed_1_QueryVariables = Exact<{ + first: Scalars['Int'] +}> + +export type Unnamed_1_Query = { __typename?: 'QueryRoot' } & { + pages: { __typename?: 'PageConnection' } & { + edges: Array< + { __typename?: 'PageEdge' } & { + node: { __typename?: 'Page' } & Pick< + Page, + 'id' | 'title' | 'handle' | 'body' | 'bodySummary' | 'url' + > + } + > + } +} diff --git a/framework/shopify/schema.graphql b/framework/shopify/schema.graphql new file mode 100644 index 000000000..822e6007e --- /dev/null +++ b/framework/shopify/schema.graphql @@ -0,0 +1,9631 @@ +schema { + query: QueryRoot + mutation: Mutation +} + +""" +Marks an element of a GraphQL schema as having restricted access. +""" +directive @accessRestricted( + """ + Explains the reason around this restriction + """ + reason: String = null +) on FIELD_DEFINITION | OBJECT + +""" +A version of the API. +""" +type ApiVersion { + """ + The human-readable name of the version. + """ + displayName: String! + + """ + The unique identifier of an ApiVersion. All supported API versions have a date-based (YYYY-MM) or `unstable` handle. + """ + handle: String! + + """ + Whether the version is supported by Shopify. + """ + supported: Boolean! +} + +""" +Details about the gift card used on the checkout. +""" +type AppliedGiftCard implements Node { + """ + The amount that was taken from the gift card by applying it. + """ + amountUsed: Money! @deprecated(reason: "Use `amountUsedV2` instead") + + """ + The amount that was taken from the gift card by applying it. + """ + amountUsedV2: MoneyV2! + + """ + The amount left on the gift card. + """ + balance: Money! @deprecated(reason: "Use `balanceV2` instead") + + """ + The amount left on the gift card. + """ + balanceV2: MoneyV2! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The last characters of the gift card. + """ + lastCharacters: String! + + """ + The amount that was applied to the checkout in its currency. + """ + presentmentAmountUsed: MoneyV2! +} + +""" +An article in an online store blog. +""" +type Article implements Node { + """ + The article's author. + """ + author: ArticleAuthor! @deprecated(reason: "Use `authorV2` instead") + + """ + The article's author. + """ + authorV2: ArticleAuthor + + """ + The blog that the article belongs to. + """ + blog: Blog! + + """ + List of comments posted on the article. + """ + comments( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): CommentConnection! + + """ + Stripped content of the article, single line with HTML tags removed. + """ + content( + """ + Truncates string after the given length. + """ + truncateAt: Int + ): String! + + """ + The content of the article, complete with HTML formatting. + """ + contentHtml: HTML! + + """ + Stripped excerpt of the article, single line with HTML tags removed. + """ + excerpt( + """ + Truncates string after the given length. + """ + truncateAt: Int + ): String + + """ + The excerpt of the article, complete with HTML formatting. + """ + excerptHtml: HTML + + """ + A human-friendly unique string for the Article automatically generated from its title. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The image associated with the article. + """ + image( + """ + Image width in pixels between 1 and 2048. This argument is deprecated: Use `maxWidth` on `Image.transformedSrc` instead. + """ + maxWidth: Int + + """ + Image height in pixels between 1 and 2048. This argument is deprecated: Use `maxHeight` on `Image.transformedSrc` instead. + """ + maxHeight: Int + + """ + Crops the image according to the specified region. This argument is deprecated: Use `crop` on `Image.transformedSrc` instead. + """ + crop: CropRegion + + """ + Image size multiplier for high-resolution retina displays. Must be between 1 and 3. This argument is deprecated: Use `scale` on `Image.transformedSrc` instead. + """ + scale: Int = 1 + ): Image + + """ + The date and time when the article was published. + """ + publishedAt: DateTime! + + """ + The article’s SEO information. + """ + seo: SEO + + """ + A categorization that a article can be tagged with. + """ + tags: [String!]! + + """ + The article’s name. + """ + title: String! + + """ + The url pointing to the article accessible from the web. + """ + url: URL! +} + +""" +The author of an article. +""" +type ArticleAuthor { + """ + The author's bio. + """ + bio: String + + """ + The author’s email. + """ + email: String! + + """ + The author's first name. + """ + firstName: String! + + """ + The author's last name. + """ + lastName: String! + + """ + The author's full name. + """ + name: String! +} + +""" +An auto-generated type for paginating through multiple Articles. +""" +type ArticleConnection { + """ + A list of edges. + """ + edges: [ArticleEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Article and a cursor during pagination. +""" +type ArticleEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ArticleEdge. + """ + node: Article! +} + +""" +The set of valid sort keys for the Article query. +""" +enum ArticleSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `blog_title` value. + """ + BLOG_TITLE + + """ + Sort by the `author` value. + """ + AUTHOR + + """ + Sort by the `updated_at` value. + """ + UPDATED_AT + + """ + Sort by the `published_at` value. + """ + PUBLISHED_AT + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +Represents a generic custom attribute. +""" +type Attribute { + """ + Key or name of the attribute. + """ + key: String! + + """ + Value of the attribute. + """ + value: String +} + +""" +Specifies the input fields required for an attribute. +""" +input AttributeInput { + """ + Key or name of the attribute. + """ + key: String! + + """ + Value of the attribute. + """ + value: String! +} + +""" +Automatic discount applications capture the intentions of a discount that was automatically applied. +""" +type AutomaticDiscountApplication implements DiscountApplication { + """ + The method by which the discount's value is allocated to its entitled items. + """ + allocationMethod: DiscountApplicationAllocationMethod! + + """ + Which lines of targetType that the discount is allocated over. + """ + targetSelection: DiscountApplicationTargetSelection! + + """ + The type of line that the discount is applicable towards. + """ + targetType: DiscountApplicationTargetType! + + """ + The title of the application. + """ + title: String! + + """ + The value of the discount application. + """ + value: PricingValue! +} + +""" +A collection of available shipping rates for a checkout. +""" +type AvailableShippingRates { + """ + Whether or not the shipping rates are ready. + The `shippingRates` field is `null` when this value is `false`. + This field should be polled until its value becomes `true`. + """ + ready: Boolean! + + """ + The fetched shipping rates. `null` until the `ready` field is `true`. + """ + shippingRates: [ShippingRate!] +} + +""" +An online store blog. +""" +type Blog implements Node { + """ + Find an article by its handle. + """ + articleByHandle( + """ + The handle of the article. + """ + handle: String! + ): Article + + """ + List of the blog's articles. + """ + articles( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ArticleSortKeys = ID + + """ + Supported filter parameters: + - `author` + - `blog_title` + - `created_at` + - `tag` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): ArticleConnection! + + """ + The authors who have contributed to the blog. + """ + authors: [ArticleAuthor!]! + + """ + A human-friendly unique string for the Blog automatically generated from its title. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The blog's SEO information. + """ + seo: SEO + + """ + The blogs’s title. + """ + title: String! + + """ + The url pointing to the blog accessible from the web. + """ + url: URL! +} + +""" +An auto-generated type for paginating through multiple Blogs. +""" +type BlogConnection { + """ + A list of edges. + """ + edges: [BlogEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Blog and a cursor during pagination. +""" +type BlogEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of BlogEdge. + """ + node: Blog! +} + +""" +The set of valid sort keys for the Blog query. +""" +enum BlogSortKeys { + """ + Sort by the `handle` value. + """ + HANDLE + + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +Card brand, such as Visa or Mastercard, which can be used for payments. +""" +enum CardBrand { + """ + Visa + """ + VISA + + """ + Mastercard + """ + MASTERCARD + + """ + Discover + """ + DISCOVER + + """ + American Express + """ + AMERICAN_EXPRESS + + """ + Diners Club + """ + DINERS_CLUB + + """ + JCB + """ + JCB +} + +""" +A container for all the information required to checkout items and pay. +""" +type Checkout implements Node { + """ + The gift cards used on the checkout. + """ + appliedGiftCards: [AppliedGiftCard!]! + + """ + The available shipping rates for this Checkout. + Should only be used when checkout `requiresShipping` is `true` and + the shipping address is valid. + """ + availableShippingRates: AvailableShippingRates + + """ + The date and time when the checkout was completed. + """ + completedAt: DateTime + + """ + The date and time when the checkout was created. + """ + createdAt: DateTime! + + """ + The currency code for the Checkout. + """ + currencyCode: CurrencyCode! + + """ + A list of extra information that is added to the checkout. + """ + customAttributes: [Attribute!]! + + """ + The customer associated with the checkout. + """ + customer: Customer + @deprecated( + reason: "This field will always return null. If you have an authentication token for the customer, you can use the `customer` field on the query root to retrieve it." + ) + + """ + Discounts that have been applied on the checkout. + """ + discountApplications( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): DiscountApplicationConnection! + + """ + The email attached to this checkout. + """ + email: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + A list of line item objects, each one containing information about an item in the checkout. + """ + lineItems( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): CheckoutLineItemConnection! + + """ + The sum of all the prices of all the items in the checkout. Duties, taxes, shipping and discounts excluded. + """ + lineItemsSubtotalPrice: MoneyV2! + + """ + The note associated with the checkout. + """ + note: String + + """ + The resulting order from a paid checkout. + """ + order: Order + + """ + The Order Status Page for this Checkout, null when checkout is not completed. + """ + orderStatusUrl: URL + + """ + The amount left to be paid. This is equal to the cost of the line items, taxes and shipping minus discounts and gift cards. + """ + paymentDue: Money! @deprecated(reason: "Use `paymentDueV2` instead") + + """ + The amount left to be paid. This is equal to the cost of the line items, duties, taxes and shipping minus discounts and gift cards. + """ + paymentDueV2: MoneyV2! + + """ + Whether or not the Checkout is ready and can be completed. Checkouts may + have asynchronous operations that can take time to finish. If you want + to complete a checkout or ensure all the fields are populated and up to + date, polling is required until the value is true. + """ + ready: Boolean! + + """ + States whether or not the fulfillment requires shipping. + """ + requiresShipping: Boolean! + + """ + The shipping address to where the line items will be shipped. + """ + shippingAddress: MailingAddress + + """ + The discounts that have been allocated onto the shipping line by discount applications. + """ + shippingDiscountAllocations: [DiscountAllocation!]! + + """ + Once a shipping rate is selected by the customer it is transitioned to a `shipping_line` object. + """ + shippingLine: ShippingRate + + """ + Price of the checkout before shipping and taxes. + """ + subtotalPrice: Money! @deprecated(reason: "Use `subtotalPriceV2` instead") + + """ + Price of the checkout before duties, shipping and taxes. + """ + subtotalPriceV2: MoneyV2! + + """ + Specifies if the Checkout is tax exempt. + """ + taxExempt: Boolean! + + """ + Specifies if taxes are included in the line item and shipping line prices. + """ + taxesIncluded: Boolean! + + """ + The sum of all the prices of all the items in the checkout, taxes and discounts included. + """ + totalPrice: Money! @deprecated(reason: "Use `totalPriceV2` instead") + + """ + The sum of all the prices of all the items in the checkout, duties, taxes and discounts included. + """ + totalPriceV2: MoneyV2! + + """ + The sum of all the taxes applied to the line items and shipping lines in the checkout. + """ + totalTax: Money! @deprecated(reason: "Use `totalTaxV2` instead") + + """ + The sum of all the taxes applied to the line items and shipping lines in the checkout. + """ + totalTaxV2: MoneyV2! + + """ + The date and time when the checkout was last updated. + """ + updatedAt: DateTime! + + """ + The url pointing to the checkout accessible from the web. + """ + webUrl: URL! +} + +""" +Specifies the fields required to update a checkout's attributes. +""" +input CheckoutAttributesUpdateInput { + """ + The text of an optional note that a shop owner can attach to the checkout. + """ + note: String + + """ + A list of extra information that is added to the checkout. + """ + customAttributes: [AttributeInput!] + + """ + Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + The required attributes are city, province, and country. + Full validation of the addresses is still done at complete time. + """ + allowPartialAddresses: Boolean +} + +""" +Return type for `checkoutAttributesUpdate` mutation. +""" +type CheckoutAttributesUpdatePayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Specifies the fields required to update a checkout's attributes. +""" +input CheckoutAttributesUpdateV2Input { + """ + The text of an optional note that a shop owner can attach to the checkout. + """ + note: String + + """ + A list of extra information that is added to the checkout. + """ + customAttributes: [AttributeInput!] + + """ + Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + The required attributes are city, province, and country. + Full validation of the addresses is still done at complete time. + """ + allowPartialAddresses: Boolean +} + +""" +Return type for `checkoutAttributesUpdateV2` mutation. +""" +type CheckoutAttributesUpdateV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteFree` mutation. +""" +type CheckoutCompleteFreePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteWithCreditCard` mutation. +""" +type CheckoutCompleteWithCreditCardPayload { + """ + The checkout on which the payment was applied. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + A representation of the attempted payment. + """ + payment: Payment + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteWithCreditCardV2` mutation. +""" +type CheckoutCompleteWithCreditCardV2Payload { + """ + The checkout on which the payment was applied. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + A representation of the attempted payment. + """ + payment: Payment + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteWithTokenizedPayment` mutation. +""" +type CheckoutCompleteWithTokenizedPaymentPayload { + """ + The checkout on which the payment was applied. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + A representation of the attempted payment. + """ + payment: Payment + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteWithTokenizedPaymentV2` mutation. +""" +type CheckoutCompleteWithTokenizedPaymentV2Payload { + """ + The checkout on which the payment was applied. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + A representation of the attempted payment. + """ + payment: Payment + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteWithTokenizedPaymentV3` mutation. +""" +type CheckoutCompleteWithTokenizedPaymentV3Payload { + """ + The checkout on which the payment was applied. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + A representation of the attempted payment. + """ + payment: Payment + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Specifies the fields required to create a checkout. +""" +input CheckoutCreateInput { + """ + The email with which the customer wants to checkout. + """ + email: String + + """ + A list of line item objects, each one containing information about an item in the checkout. + """ + lineItems: [CheckoutLineItemInput!] + + """ + The shipping address to where the line items will be shipped. + """ + shippingAddress: MailingAddressInput + + """ + The text of an optional note that a shop owner can attach to the checkout. + """ + note: String + + """ + A list of extra information that is added to the checkout. + """ + customAttributes: [AttributeInput!] + + """ + Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + The required attributes are city, province, and country. + Full validation of addresses is still done at complete time. + """ + allowPartialAddresses: Boolean + + """ + The three-letter currency code of one of the shop's enabled presentment currencies. + Including this field creates a checkout in the specified currency. By default, new + checkouts are created in the shop's primary currency. + """ + presentmentCurrencyCode: CurrencyCode +} + +""" +Return type for `checkoutCreate` mutation. +""" +type CheckoutCreatePayload { + """ + The new checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCustomerAssociate` mutation. +""" +type CheckoutCustomerAssociatePayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + The associated customer object. + """ + customer: Customer + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! +} + +""" +Return type for `checkoutCustomerAssociateV2` mutation. +""" +type CheckoutCustomerAssociateV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + The associated customer object. + """ + customer: Customer + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCustomerDisassociate` mutation. +""" +type CheckoutCustomerDisassociatePayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCustomerDisassociateV2` mutation. +""" +type CheckoutCustomerDisassociateV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutDiscountCodeApply` mutation. +""" +type CheckoutDiscountCodeApplyPayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutDiscountCodeApplyV2` mutation. +""" +type CheckoutDiscountCodeApplyV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutDiscountCodeRemove` mutation. +""" +type CheckoutDiscountCodeRemovePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutEmailUpdate` mutation. +""" +type CheckoutEmailUpdatePayload { + """ + The checkout object with the updated email. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutEmailUpdateV2` mutation. +""" +type CheckoutEmailUpdateV2Payload { + """ + The checkout object with the updated email. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Possible error codes that could be returned by CheckoutUserError. +""" +enum CheckoutErrorCode { + """ + Input value is blank. + """ + BLANK + + """ + Input value is invalid. + """ + INVALID + + """ + Input value is too long. + """ + TOO_LONG + + """ + Input value is not present. + """ + PRESENT + + """ + Input value should be less than maximum allowed value. + """ + LESS_THAN + + """ + Input value should be greater than or equal to minimum allowed value. + """ + GREATER_THAN_OR_EQUAL_TO + + """ + Input value should be less or equal to maximum allowed value. + """ + LESS_THAN_OR_EQUAL_TO + + """ + Checkout is already completed. + """ + ALREADY_COMPLETED + + """ + Checkout is locked. + """ + LOCKED + + """ + Input value is not supported. + """ + NOT_SUPPORTED + + """ + Input email contains an invalid domain name. + """ + BAD_DOMAIN + + """ + Input Zip is invalid for country provided. + """ + INVALID_FOR_COUNTRY + + """ + Input Zip is invalid for country and province provided. + """ + INVALID_FOR_COUNTRY_AND_PROVINCE + + """ + Invalid state in country. + """ + INVALID_STATE_IN_COUNTRY + + """ + Invalid province in country. + """ + INVALID_PROVINCE_IN_COUNTRY + + """ + Invalid region in country. + """ + INVALID_REGION_IN_COUNTRY + + """ + Shipping rate expired. + """ + SHIPPING_RATE_EXPIRED + + """ + Gift card cannot be applied to a checkout that contains a gift card. + """ + GIFT_CARD_UNUSABLE + + """ + Gift card is disabled. + """ + GIFT_CARD_DISABLED + + """ + Gift card code is invalid. + """ + GIFT_CARD_CODE_INVALID + + """ + Gift card has already been applied. + """ + GIFT_CARD_ALREADY_APPLIED + + """ + Gift card currency does not match checkout currency. + """ + GIFT_CARD_CURRENCY_MISMATCH + + """ + Gift card is expired. + """ + GIFT_CARD_EXPIRED + + """ + Gift card has no funds left. + """ + GIFT_CARD_DEPLETED + + """ + Gift card was not found. + """ + GIFT_CARD_NOT_FOUND + + """ + Cart does not meet discount requirements notice. + """ + CART_DOES_NOT_MEET_DISCOUNT_REQUIREMENTS_NOTICE + + """ + Discount expired. + """ + DISCOUNT_EXPIRED + + """ + Discount disabled. + """ + DISCOUNT_DISABLED + + """ + Discount limit reached. + """ + DISCOUNT_LIMIT_REACHED + + """ + Discount not found. + """ + DISCOUNT_NOT_FOUND + + """ + Customer already used once per customer discount notice. + """ + CUSTOMER_ALREADY_USED_ONCE_PER_CUSTOMER_DISCOUNT_NOTICE + + """ + Checkout is already completed. + """ + EMPTY + + """ + Not enough in stock. + """ + NOT_ENOUGH_IN_STOCK + + """ + Missing payment input. + """ + MISSING_PAYMENT_INPUT + + """ + The amount of the payment does not match the value to be paid. + """ + TOTAL_PRICE_MISMATCH + + """ + Line item was not found in checkout. + """ + LINE_ITEM_NOT_FOUND + + """ + Unable to apply discount. + """ + UNABLE_TO_APPLY + + """ + Discount already applied. + """ + DISCOUNT_ALREADY_APPLIED +} + +""" +Return type for `checkoutGiftCardApply` mutation. +""" +type CheckoutGiftCardApplyPayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutGiftCardRemove` mutation. +""" +type CheckoutGiftCardRemovePayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutGiftCardRemoveV2` mutation. +""" +type CheckoutGiftCardRemoveV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutGiftCardsAppend` mutation. +""" +type CheckoutGiftCardsAppendPayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +A single line item in the checkout, grouped by variant and attributes. +""" +type CheckoutLineItem implements Node { + """ + Extra information in the form of an array of Key-Value pairs about the line item. + """ + customAttributes: [Attribute!]! + + """ + The discounts that have been allocated onto the checkout line item by discount applications. + """ + discountAllocations: [DiscountAllocation!]! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The quantity of the line item. + """ + quantity: Int! + + """ + Title of the line item. Defaults to the product's title. + """ + title: String! + + """ + Unit price of the line item. + """ + unitPrice: MoneyV2 + + """ + Product variant of the line item. + """ + variant: ProductVariant +} + +""" +An auto-generated type for paginating through multiple CheckoutLineItems. +""" +type CheckoutLineItemConnection { + """ + A list of edges. + """ + edges: [CheckoutLineItemEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one CheckoutLineItem and a cursor during pagination. +""" +type CheckoutLineItemEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of CheckoutLineItemEdge. + """ + node: CheckoutLineItem! +} + +""" +Specifies the input fields to create a line item on a checkout. +""" +input CheckoutLineItemInput { + """ + Extra information in the form of an array of Key-Value pairs about the line item. + """ + customAttributes: [AttributeInput!] + + """ + The quantity of the line item. + """ + quantity: Int! + + """ + The identifier of the product variant for the line item. + """ + variantId: ID! +} + +""" +Specifies the input fields to update a line item on the checkout. +""" +input CheckoutLineItemUpdateInput { + """ + The identifier of the line item. + """ + id: ID + + """ + The variant identifier of the line item. + """ + variantId: ID + + """ + The quantity of the line item. + """ + quantity: Int + + """ + Extra information in the form of an array of Key-Value pairs about the line item. + """ + customAttributes: [AttributeInput!] +} + +""" +Return type for `checkoutLineItemsAdd` mutation. +""" +type CheckoutLineItemsAddPayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutLineItemsRemove` mutation. +""" +type CheckoutLineItemsRemovePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutLineItemsReplace` mutation. +""" +type CheckoutLineItemsReplacePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [CheckoutUserError!]! +} + +""" +Return type for `checkoutLineItemsUpdate` mutation. +""" +type CheckoutLineItemsUpdatePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutShippingAddressUpdate` mutation. +""" +type CheckoutShippingAddressUpdatePayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutShippingAddressUpdateV2` mutation. +""" +type CheckoutShippingAddressUpdateV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutShippingLineUpdate` mutation. +""" +type CheckoutShippingLineUpdatePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Represents an error that happens during execution of a checkout mutation. +""" +type CheckoutUserError implements DisplayableError { + """ + Error code to uniquely identify the error. + """ + code: CheckoutErrorCode + + """ + Path to the input field which caused the error. + """ + field: [String!] + + """ + The error message. + """ + message: String! +} + +""" +A collection represents a grouping of products that a shop owner can create to organize them or make their shops easier to browse. +""" +type Collection implements Node { + """ + Stripped description of the collection, single line with HTML tags removed. + """ + description( + """ + Truncates string after the given length. + """ + truncateAt: Int + ): String! + + """ + The description of the collection, complete with HTML formatting. + """ + descriptionHtml: HTML! + + """ + A human-friendly unique string for the collection automatically generated from its title. + Limit of 255 characters. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + Image associated with the collection. + """ + image( + """ + Image width in pixels between 1 and 2048. This argument is deprecated: Use `maxWidth` on `Image.transformedSrc` instead. + """ + maxWidth: Int + + """ + Image height in pixels between 1 and 2048. This argument is deprecated: Use `maxHeight` on `Image.transformedSrc` instead. + """ + maxHeight: Int + + """ + Crops the image according to the specified region. This argument is deprecated: Use `crop` on `Image.transformedSrc` instead. + """ + crop: CropRegion + + """ + Image size multiplier for high-resolution retina displays. Must be between 1 and 3. This argument is deprecated: Use `scale` on `Image.transformedSrc` instead. + """ + scale: Int = 1 + ): Image + + """ + List of products in the collection. + """ + products( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductCollectionSortKeys = COLLECTION_DEFAULT + ): ProductConnection! + + """ + The collection’s name. Limit of 255 characters. + """ + title: String! + + """ + The date and time when the collection was last modified. + """ + updatedAt: DateTime! +} + +""" +An auto-generated type for paginating through multiple Collections. +""" +type CollectionConnection { + """ + A list of edges. + """ + edges: [CollectionEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Collection and a cursor during pagination. +""" +type CollectionEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of CollectionEdge. + """ + node: Collection! +} + +""" +The set of valid sort keys for the Collection query. +""" +enum CollectionSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `updated_at` value. + """ + UPDATED_AT + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +A comment on an article. +""" +type Comment implements Node { + """ + The comment’s author. + """ + author: CommentAuthor! + + """ + Stripped content of the comment, single line with HTML tags removed. + """ + content( + """ + Truncates string after the given length. + """ + truncateAt: Int + ): String! + + """ + The content of the comment, complete with HTML formatting. + """ + contentHtml: HTML! + + """ + Globally unique identifier. + """ + id: ID! +} + +""" +The author of a comment. +""" +type CommentAuthor { + """ + The author's email. + """ + email: String! + + """ + The author’s name. + """ + name: String! +} + +""" +An auto-generated type for paginating through multiple Comments. +""" +type CommentConnection { + """ + A list of edges. + """ + edges: [CommentEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Comment and a cursor during pagination. +""" +type CommentEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of CommentEdge. + """ + node: Comment! +} + +""" +ISO 3166-1 alpha-2 country codes with some differences. +""" +enum CountryCode { + """ + Afghanistan. + """ + AF + + """ + Åland Islands. + """ + AX + + """ + Albania. + """ + AL + + """ + Algeria. + """ + DZ + + """ + Andorra. + """ + AD + + """ + Angola. + """ + AO + + """ + Anguilla. + """ + AI + + """ + Antigua & Barbuda. + """ + AG + + """ + Argentina. + """ + AR + + """ + Armenia. + """ + AM + + """ + Aruba. + """ + AW + + """ + Australia. + """ + AU + + """ + Austria. + """ + AT + + """ + Azerbaijan. + """ + AZ + + """ + Bahamas. + """ + BS + + """ + Bahrain. + """ + BH + + """ + Bangladesh. + """ + BD + + """ + Barbados. + """ + BB + + """ + Belarus. + """ + BY + + """ + Belgium. + """ + BE + + """ + Belize. + """ + BZ + + """ + Benin. + """ + BJ + + """ + Bermuda. + """ + BM + + """ + Bhutan. + """ + BT + + """ + Bolivia. + """ + BO + + """ + Bosnia & Herzegovina. + """ + BA + + """ + Botswana. + """ + BW + + """ + Bouvet Island. + """ + BV + + """ + Brazil. + """ + BR + + """ + British Indian Ocean Territory. + """ + IO + + """ + Brunei. + """ + BN + + """ + Bulgaria. + """ + BG + + """ + Burkina Faso. + """ + BF + + """ + Burundi. + """ + BI + + """ + Cambodia. + """ + KH + + """ + Canada. + """ + CA + + """ + Cape Verde. + """ + CV + + """ + Caribbean Netherlands. + """ + BQ + + """ + Cayman Islands. + """ + KY + + """ + Central African Republic. + """ + CF + + """ + Chad. + """ + TD + + """ + Chile. + """ + CL + + """ + China. + """ + CN + + """ + Christmas Island. + """ + CX + + """ + Cocos (Keeling) Islands. + """ + CC + + """ + Colombia. + """ + CO + + """ + Comoros. + """ + KM + + """ + Congo - Brazzaville. + """ + CG + + """ + Congo - Kinshasa. + """ + CD + + """ + Cook Islands. + """ + CK + + """ + Costa Rica. + """ + CR + + """ + Croatia. + """ + HR + + """ + Cuba. + """ + CU + + """ + Curaçao. + """ + CW + + """ + Cyprus. + """ + CY + + """ + Czechia. + """ + CZ + + """ + Côte d’Ivoire. + """ + CI + + """ + Denmark. + """ + DK + + """ + Djibouti. + """ + DJ + + """ + Dominica. + """ + DM + + """ + Dominican Republic. + """ + DO + + """ + Ecuador. + """ + EC + + """ + Egypt. + """ + EG + + """ + El Salvador. + """ + SV + + """ + Equatorial Guinea. + """ + GQ + + """ + Eritrea. + """ + ER + + """ + Estonia. + """ + EE + + """ + Eswatini. + """ + SZ + + """ + Ethiopia. + """ + ET + + """ + Falkland Islands. + """ + FK + + """ + Faroe Islands. + """ + FO + + """ + Fiji. + """ + FJ + + """ + Finland. + """ + FI + + """ + France. + """ + FR + + """ + French Guiana. + """ + GF + + """ + French Polynesia. + """ + PF + + """ + French Southern Territories. + """ + TF + + """ + Gabon. + """ + GA + + """ + Gambia. + """ + GM + + """ + Georgia. + """ + GE + + """ + Germany. + """ + DE + + """ + Ghana. + """ + GH + + """ + Gibraltar. + """ + GI + + """ + Greece. + """ + GR + + """ + Greenland. + """ + GL + + """ + Grenada. + """ + GD + + """ + Guadeloupe. + """ + GP + + """ + Guatemala. + """ + GT + + """ + Guernsey. + """ + GG + + """ + Guinea. + """ + GN + + """ + Guinea-Bissau. + """ + GW + + """ + Guyana. + """ + GY + + """ + Haiti. + """ + HT + + """ + Heard & McDonald Islands. + """ + HM + + """ + Vatican City. + """ + VA + + """ + Honduras. + """ + HN + + """ + Hong Kong SAR. + """ + HK + + """ + Hungary. + """ + HU + + """ + Iceland. + """ + IS + + """ + India. + """ + IN + + """ + Indonesia. + """ + ID + + """ + Iran. + """ + IR + + """ + Iraq. + """ + IQ + + """ + Ireland. + """ + IE + + """ + Isle of Man. + """ + IM + + """ + Israel. + """ + IL + + """ + Italy. + """ + IT + + """ + Jamaica. + """ + JM + + """ + Japan. + """ + JP + + """ + Jersey. + """ + JE + + """ + Jordan. + """ + JO + + """ + Kazakhstan. + """ + KZ + + """ + Kenya. + """ + KE + + """ + Kiribati. + """ + KI + + """ + North Korea. + """ + KP + + """ + Kosovo. + """ + XK + + """ + Kuwait. + """ + KW + + """ + Kyrgyzstan. + """ + KG + + """ + Laos. + """ + LA + + """ + Latvia. + """ + LV + + """ + Lebanon. + """ + LB + + """ + Lesotho. + """ + LS + + """ + Liberia. + """ + LR + + """ + Libya. + """ + LY + + """ + Liechtenstein. + """ + LI + + """ + Lithuania. + """ + LT + + """ + Luxembourg. + """ + LU + + """ + Macao SAR. + """ + MO + + """ + Madagascar. + """ + MG + + """ + Malawi. + """ + MW + + """ + Malaysia. + """ + MY + + """ + Maldives. + """ + MV + + """ + Mali. + """ + ML + + """ + Malta. + """ + MT + + """ + Martinique. + """ + MQ + + """ + Mauritania. + """ + MR + + """ + Mauritius. + """ + MU + + """ + Mayotte. + """ + YT + + """ + Mexico. + """ + MX + + """ + Moldova. + """ + MD + + """ + Monaco. + """ + MC + + """ + Mongolia. + """ + MN + + """ + Montenegro. + """ + ME + + """ + Montserrat. + """ + MS + + """ + Morocco. + """ + MA + + """ + Mozambique. + """ + MZ + + """ + Myanmar (Burma). + """ + MM + + """ + Namibia. + """ + NA + + """ + Nauru. + """ + NR + + """ + Nepal. + """ + NP + + """ + Netherlands. + """ + NL + + """ + Netherlands Antilles. + """ + AN + + """ + New Caledonia. + """ + NC + + """ + New Zealand. + """ + NZ + + """ + Nicaragua. + """ + NI + + """ + Niger. + """ + NE + + """ + Nigeria. + """ + NG + + """ + Niue. + """ + NU + + """ + Norfolk Island. + """ + NF + + """ + North Macedonia. + """ + MK + + """ + Norway. + """ + NO + + """ + Oman. + """ + OM + + """ + Pakistan. + """ + PK + + """ + Palestinian Territories. + """ + PS + + """ + Panama. + """ + PA + + """ + Papua New Guinea. + """ + PG + + """ + Paraguay. + """ + PY + + """ + Peru. + """ + PE + + """ + Philippines. + """ + PH + + """ + Pitcairn Islands. + """ + PN + + """ + Poland. + """ + PL + + """ + Portugal. + """ + PT + + """ + Qatar. + """ + QA + + """ + Cameroon. + """ + CM + + """ + Réunion. + """ + RE + + """ + Romania. + """ + RO + + """ + Russia. + """ + RU + + """ + Rwanda. + """ + RW + + """ + St. Barthélemy. + """ + BL + + """ + St. Helena. + """ + SH + + """ + St. Kitts & Nevis. + """ + KN + + """ + St. Lucia. + """ + LC + + """ + St. Martin. + """ + MF + + """ + St. Pierre & Miquelon. + """ + PM + + """ + Samoa. + """ + WS + + """ + San Marino. + """ + SM + + """ + São Tomé & Príncipe. + """ + ST + + """ + Saudi Arabia. + """ + SA + + """ + Senegal. + """ + SN + + """ + Serbia. + """ + RS + + """ + Seychelles. + """ + SC + + """ + Sierra Leone. + """ + SL + + """ + Singapore. + """ + SG + + """ + Sint Maarten. + """ + SX + + """ + Slovakia. + """ + SK + + """ + Slovenia. + """ + SI + + """ + Solomon Islands. + """ + SB + + """ + Somalia. + """ + SO + + """ + South Africa. + """ + ZA + + """ + South Georgia & South Sandwich Islands. + """ + GS + + """ + South Korea. + """ + KR + + """ + South Sudan. + """ + SS + + """ + Spain. + """ + ES + + """ + Sri Lanka. + """ + LK + + """ + St. Vincent & Grenadines. + """ + VC + + """ + Sudan. + """ + SD + + """ + Suriname. + """ + SR + + """ + Svalbard & Jan Mayen. + """ + SJ + + """ + Sweden. + """ + SE + + """ + Switzerland. + """ + CH + + """ + Syria. + """ + SY + + """ + Taiwan. + """ + TW + + """ + Tajikistan. + """ + TJ + + """ + Tanzania. + """ + TZ + + """ + Thailand. + """ + TH + + """ + Timor-Leste. + """ + TL + + """ + Togo. + """ + TG + + """ + Tokelau. + """ + TK + + """ + Tonga. + """ + TO + + """ + Trinidad & Tobago. + """ + TT + + """ + Tunisia. + """ + TN + + """ + Turkey. + """ + TR + + """ + Turkmenistan. + """ + TM + + """ + Turks & Caicos Islands. + """ + TC + + """ + Tuvalu. + """ + TV + + """ + Uganda. + """ + UG + + """ + Ukraine. + """ + UA + + """ + United Arab Emirates. + """ + AE + + """ + United Kingdom. + """ + GB + + """ + United States. + """ + US + + """ + U.S. Outlying Islands. + """ + UM + + """ + Uruguay. + """ + UY + + """ + Uzbekistan. + """ + UZ + + """ + Vanuatu. + """ + VU + + """ + Venezuela. + """ + VE + + """ + Vietnam. + """ + VN + + """ + British Virgin Islands. + """ + VG + + """ + Wallis & Futuna. + """ + WF + + """ + Western Sahara. + """ + EH + + """ + Yemen. + """ + YE + + """ + Zambia. + """ + ZM + + """ + Zimbabwe. + """ + ZW +} + +""" +Credit card information used for a payment. +""" +type CreditCard { + """ + The brand of the credit card. + """ + brand: String + + """ + The expiry month of the credit card. + """ + expiryMonth: Int + + """ + The expiry year of the credit card. + """ + expiryYear: Int + + """ + The credit card's BIN number. + """ + firstDigits: String + + """ + The first name of the card holder. + """ + firstName: String + + """ + The last 4 digits of the credit card. + """ + lastDigits: String + + """ + The last name of the card holder. + """ + lastName: String + + """ + The masked credit card number with only the last 4 digits displayed. + """ + maskedNumber: String +} + +""" +Specifies the fields required to complete a checkout with +a Shopify vaulted credit card payment. +""" +input CreditCardPaymentInput { + """ + The amount of the payment. + """ + amount: Money! + + """ + A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. + """ + idempotencyKey: String! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddressInput! + + """ + The ID returned by Shopify's Card Vault. + """ + vaultId: String! + + """ + Executes the payment in test mode if possible. Defaults to `false`. + """ + test: Boolean +} + +""" +Specifies the fields required to complete a checkout with +a Shopify vaulted credit card payment. +""" +input CreditCardPaymentInputV2 { + """ + The amount and currency of the payment. + """ + paymentAmount: MoneyInput! + + """ + A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. + """ + idempotencyKey: String! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddressInput! + + """ + The ID returned by Shopify's Card Vault. + """ + vaultId: String! + + """ + Executes the payment in test mode if possible. Defaults to `false`. + """ + test: Boolean +} + +""" +The part of the image that should remain after cropping. +""" +enum CropRegion { + """ + Keep the center of the image. + """ + CENTER + + """ + Keep the top of the image. + """ + TOP + + """ + Keep the bottom of the image. + """ + BOTTOM + + """ + Keep the left of the image. + """ + LEFT + + """ + Keep the right of the image. + """ + RIGHT +} + +""" +Currency codes. +""" +enum CurrencyCode { + """ + United States Dollars (USD). + """ + USD + + """ + Euro (EUR). + """ + EUR + + """ + United Kingdom Pounds (GBP). + """ + GBP + + """ + Canadian Dollars (CAD). + """ + CAD + + """ + Afghan Afghani (AFN). + """ + AFN + + """ + Albanian Lek (ALL). + """ + ALL + + """ + Algerian Dinar (DZD). + """ + DZD + + """ + Angolan Kwanza (AOA). + """ + AOA + + """ + Argentine Pesos (ARS). + """ + ARS + + """ + Armenian Dram (AMD). + """ + AMD + + """ + Aruban Florin (AWG). + """ + AWG + + """ + Australian Dollars (AUD). + """ + AUD + + """ + Barbadian Dollar (BBD). + """ + BBD + + """ + Azerbaijani Manat (AZN). + """ + AZN + + """ + Bangladesh Taka (BDT). + """ + BDT + + """ + Bahamian Dollar (BSD). + """ + BSD + + """ + Bahraini Dinar (BHD). + """ + BHD + + """ + Burundian Franc (BIF). + """ + BIF + + """ + Belarusian Ruble (BYN). + """ + BYN + + """ + Belarusian Ruble (BYR). + """ + BYR + + """ + Belize Dollar (BZD). + """ + BZD + + """ + Bermudian Dollar (BMD). + """ + BMD + + """ + Bhutanese Ngultrum (BTN). + """ + BTN + + """ + Bosnia and Herzegovina Convertible Mark (BAM). + """ + BAM + + """ + Brazilian Real (BRL). + """ + BRL + + """ + Bolivian Boliviano (BOB). + """ + BOB + + """ + Botswana Pula (BWP). + """ + BWP + + """ + Brunei Dollar (BND). + """ + BND + + """ + Bulgarian Lev (BGN). + """ + BGN + + """ + Burmese Kyat (MMK). + """ + MMK + + """ + Cambodian Riel. + """ + KHR + + """ + Cape Verdean escudo (CVE). + """ + CVE + + """ + Cayman Dollars (KYD). + """ + KYD + + """ + Central African CFA Franc (XAF). + """ + XAF + + """ + Chilean Peso (CLP). + """ + CLP + + """ + Chinese Yuan Renminbi (CNY). + """ + CNY + + """ + Colombian Peso (COP). + """ + COP + + """ + Comorian Franc (KMF). + """ + KMF + + """ + Congolese franc (CDF). + """ + CDF + + """ + Costa Rican Colones (CRC). + """ + CRC + + """ + Croatian Kuna (HRK). + """ + HRK + + """ + Czech Koruny (CZK). + """ + CZK + + """ + Danish Kroner (DKK). + """ + DKK + + """ + Djiboutian Franc (DJF). + """ + DJF + + """ + Dominican Peso (DOP). + """ + DOP + + """ + East Caribbean Dollar (XCD). + """ + XCD + + """ + Egyptian Pound (EGP). + """ + EGP + + """ + Eritrean Nakfa (ERN). + """ + ERN + + """ + Ethiopian Birr (ETB). + """ + ETB + + """ + Falkland Islands Pounds (FKP). + """ + FKP + + """ + CFP Franc (XPF). + """ + XPF + + """ + Fijian Dollars (FJD). + """ + FJD + + """ + Gibraltar Pounds (GIP). + """ + GIP + + """ + Gambian Dalasi (GMD). + """ + GMD + + """ + Ghanaian Cedi (GHS). + """ + GHS + + """ + Guatemalan Quetzal (GTQ). + """ + GTQ + + """ + Guyanese Dollar (GYD). + """ + GYD + + """ + Georgian Lari (GEL). + """ + GEL + + """ + Guinean Franc (GNF). + """ + GNF + + """ + Haitian Gourde (HTG). + """ + HTG + + """ + Honduran Lempira (HNL). + """ + HNL + + """ + Hong Kong Dollars (HKD). + """ + HKD + + """ + Hungarian Forint (HUF). + """ + HUF + + """ + Icelandic Kronur (ISK). + """ + ISK + + """ + Indian Rupees (INR). + """ + INR + + """ + Indonesian Rupiah (IDR). + """ + IDR + + """ + Israeli New Shekel (NIS). + """ + ILS + + """ + Iranian Rial (IRR). + """ + IRR + + """ + Iraqi Dinar (IQD). + """ + IQD + + """ + Jamaican Dollars (JMD). + """ + JMD + + """ + Japanese Yen (JPY). + """ + JPY + + """ + Jersey Pound. + """ + JEP + + """ + Jordanian Dinar (JOD). + """ + JOD + + """ + Kazakhstani Tenge (KZT). + """ + KZT + + """ + Kenyan Shilling (KES). + """ + KES + + """ + Kiribati Dollar (KID). + """ + KID + + """ + Kuwaiti Dinar (KWD). + """ + KWD + + """ + Kyrgyzstani Som (KGS). + """ + KGS + + """ + Laotian Kip (LAK). + """ + LAK + + """ + Latvian Lati (LVL). + """ + LVL + + """ + Lebanese Pounds (LBP). + """ + LBP + + """ + Lesotho Loti (LSL). + """ + LSL + + """ + Liberian Dollar (LRD). + """ + LRD + + """ + Libyan Dinar (LYD). + """ + LYD + + """ + Lithuanian Litai (LTL). + """ + LTL + + """ + Malagasy Ariary (MGA). + """ + MGA + + """ + Macedonia Denar (MKD). + """ + MKD + + """ + Macanese Pataca (MOP). + """ + MOP + + """ + Malawian Kwacha (MWK). + """ + MWK + + """ + Maldivian Rufiyaa (MVR). + """ + MVR + + """ + Mauritanian Ouguiya (MRU). + """ + MRU + + """ + Mexican Pesos (MXN). + """ + MXN + + """ + Malaysian Ringgits (MYR). + """ + MYR + + """ + Mauritian Rupee (MUR). + """ + MUR + + """ + Moldovan Leu (MDL). + """ + MDL + + """ + Moroccan Dirham. + """ + MAD + + """ + Mongolian Tugrik. + """ + MNT + + """ + Mozambican Metical. + """ + MZN + + """ + Namibian Dollar. + """ + NAD + + """ + Nepalese Rupee (NPR). + """ + NPR + + """ + Netherlands Antillean Guilder. + """ + ANG + + """ + New Zealand Dollars (NZD). + """ + NZD + + """ + Nicaraguan Córdoba (NIO). + """ + NIO + + """ + Nigerian Naira (NGN). + """ + NGN + + """ + Norwegian Kroner (NOK). + """ + NOK + + """ + Omani Rial (OMR). + """ + OMR + + """ + Panamian Balboa (PAB). + """ + PAB + + """ + Pakistani Rupee (PKR). + """ + PKR + + """ + Papua New Guinean Kina (PGK). + """ + PGK + + """ + Paraguayan Guarani (PYG). + """ + PYG + + """ + Peruvian Nuevo Sol (PEN). + """ + PEN + + """ + Philippine Peso (PHP). + """ + PHP + + """ + Polish Zlotych (PLN). + """ + PLN + + """ + Qatari Rial (QAR). + """ + QAR + + """ + Romanian Lei (RON). + """ + RON + + """ + Russian Rubles (RUB). + """ + RUB + + """ + Rwandan Franc (RWF). + """ + RWF + + """ + Samoan Tala (WST). + """ + WST + + """ + Saint Helena Pounds (SHP). + """ + SHP + + """ + Saudi Riyal (SAR). + """ + SAR + + """ + Sao Tome And Principe Dobra (STD). + """ + STD + + """ + Serbian dinar (RSD). + """ + RSD + + """ + Seychellois Rupee (SCR). + """ + SCR + + """ + Sierra Leonean Leone (SLL). + """ + SLL + + """ + Singapore Dollars (SGD). + """ + SGD + + """ + Sudanese Pound (SDG). + """ + SDG + + """ + Somali Shilling (SOS). + """ + SOS + + """ + Syrian Pound (SYP). + """ + SYP + + """ + South African Rand (ZAR). + """ + ZAR + + """ + South Korean Won (KRW). + """ + KRW + + """ + South Sudanese Pound (SSP). + """ + SSP + + """ + Solomon Islands Dollar (SBD). + """ + SBD + + """ + Sri Lankan Rupees (LKR). + """ + LKR + + """ + Surinamese Dollar (SRD). + """ + SRD + + """ + Swazi Lilangeni (SZL). + """ + SZL + + """ + Swedish Kronor (SEK). + """ + SEK + + """ + Swiss Francs (CHF). + """ + CHF + + """ + Taiwan Dollars (TWD). + """ + TWD + + """ + Thai baht (THB). + """ + THB + + """ + Tajikistani Somoni (TJS). + """ + TJS + + """ + Tanzanian Shilling (TZS). + """ + TZS + + """ + Tongan Pa'anga (TOP). + """ + TOP + + """ + Trinidad and Tobago Dollars (TTD). + """ + TTD + + """ + Tunisian Dinar (TND). + """ + TND + + """ + Turkish Lira (TRY). + """ + TRY + + """ + Turkmenistani Manat (TMT). + """ + TMT + + """ + Ugandan Shilling (UGX). + """ + UGX + + """ + Ukrainian Hryvnia (UAH). + """ + UAH + + """ + United Arab Emirates Dirham (AED). + """ + AED + + """ + Uruguayan Pesos (UYU). + """ + UYU + + """ + Uzbekistan som (UZS). + """ + UZS + + """ + Vanuatu Vatu (VUV). + """ + VUV + + """ + Venezuelan Bolivares (VEF). + """ + VEF + + """ + Venezuelan Bolivares (VES). + """ + VES + + """ + Vietnamese đồng (VND). + """ + VND + + """ + West African CFA franc (XOF). + """ + XOF + + """ + Yemeni Rial (YER). + """ + YER + + """ + Zambian Kwacha (ZMW). + """ + ZMW +} + +""" +A customer represents a customer account with the shop. Customer accounts store contact information for the customer, saving logged-in customers the trouble of having to provide it at every checkout. +""" +type Customer { + """ + Indicates whether the customer has consented to be sent marketing material via email. + """ + acceptsMarketing: Boolean! + + """ + A list of addresses for the customer. + """ + addresses( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): MailingAddressConnection! + + """ + The date and time when the customer was created. + """ + createdAt: DateTime! + + """ + The customer’s default address. + """ + defaultAddress: MailingAddress + + """ + The customer’s name, email or phone number. + """ + displayName: String! + + """ + The customer’s email address. + """ + email: String + + """ + The customer’s first name. + """ + firstName: String + + """ + A unique identifier for the customer. + """ + id: ID! + + """ + The customer's most recently updated, incomplete checkout. + """ + lastIncompleteCheckout: Checkout + + """ + The customer’s last name. + """ + lastName: String + + """ + The orders associated with the customer. + """ + orders( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: OrderSortKeys = ID + + """ + Supported filter parameters: + - `processed_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): OrderConnection! + + """ + The customer’s phone number. + """ + phone: String + + """ + A comma separated list of tags that have been added to the customer. + Additional access scope required: unauthenticated_read_customer_tags. + """ + tags: [String!]! + + """ + The date and time when the customer information was updated. + """ + updatedAt: DateTime! +} + +""" +A CustomerAccessToken represents the unique token required to make modifications to the customer object. +""" +type CustomerAccessToken { + """ + The customer’s access token. + """ + accessToken: String! + + """ + The date and time when the customer access token expires. + """ + expiresAt: DateTime! +} + +""" +Specifies the input fields required to create a customer access token. +""" +input CustomerAccessTokenCreateInput { + """ + The email associated to the customer. + """ + email: String! + + """ + The login password to be used by the customer. + """ + password: String! +} + +""" +Return type for `customerAccessTokenCreate` mutation. +""" +type CustomerAccessTokenCreatePayload { + """ + The newly created customer access token object. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerAccessTokenCreateWithMultipass` mutation. +""" +type CustomerAccessTokenCreateWithMultipassPayload { + """ + An access token object associated with the customer. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! +} + +""" +Return type for `customerAccessTokenDelete` mutation. +""" +type CustomerAccessTokenDeletePayload { + """ + The destroyed access token. + """ + deletedAccessToken: String + + """ + ID of the destroyed customer access token. + """ + deletedCustomerAccessTokenId: String + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! +} + +""" +Return type for `customerAccessTokenRenew` mutation. +""" +type CustomerAccessTokenRenewPayload { + """ + The renewed customer access token object. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! +} + +""" +Return type for `customerActivateByUrl` mutation. +""" +type CustomerActivateByUrlPayload { + """ + The customer that was activated. + """ + customer: Customer + + """ + A new customer access token for the customer. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! +} + +""" +Specifies the input fields required to activate a customer. +""" +input CustomerActivateInput { + """ + The activation token required to activate the customer. + """ + activationToken: String! + + """ + New password that will be set during activation. + """ + password: String! +} + +""" +Return type for `customerActivate` mutation. +""" +type CustomerActivatePayload { + """ + The customer object. + """ + customer: Customer + + """ + A newly created customer access token object for the customer. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerAddressCreate` mutation. +""" +type CustomerAddressCreatePayload { + """ + The new customer address object. + """ + customerAddress: MailingAddress + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerAddressDelete` mutation. +""" +type CustomerAddressDeletePayload { + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + ID of the deleted customer address. + """ + deletedCustomerAddressId: String + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerAddressUpdate` mutation. +""" +type CustomerAddressUpdatePayload { + """ + The customer’s updated mailing address. + """ + customerAddress: MailingAddress + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Specifies the fields required to create a new customer. +""" +input CustomerCreateInput { + """ + The customer’s first name. + """ + firstName: String + + """ + The customer’s last name. + """ + lastName: String + + """ + The customer’s email. + """ + email: String! + + """ + A unique phone number for the customer. + + Formatted using E.164 standard. For example, _+16135551111_. + """ + phone: String + + """ + The login password used by the customer. + """ + password: String! + + """ + Indicates whether the customer has consented to be sent marketing material via email. + """ + acceptsMarketing: Boolean +} + +""" +Return type for `customerCreate` mutation. +""" +type CustomerCreatePayload { + """ + The created customer object. + """ + customer: Customer + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerDefaultAddressUpdate` mutation. +""" +type CustomerDefaultAddressUpdatePayload { + """ + The updated customer object. + """ + customer: Customer + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Possible error codes that could be returned by CustomerUserError. +""" +enum CustomerErrorCode { + """ + Input value is blank. + """ + BLANK + + """ + Input value is invalid. + """ + INVALID + + """ + Input value is already taken. + """ + TAKEN + + """ + Input value is too long. + """ + TOO_LONG + + """ + Input value is too short. + """ + TOO_SHORT + + """ + Unidentified customer. + """ + UNIDENTIFIED_CUSTOMER + + """ + Customer is disabled. + """ + CUSTOMER_DISABLED + + """ + Input password starts or ends with whitespace. + """ + PASSWORD_STARTS_OR_ENDS_WITH_WHITESPACE + + """ + Input contains HTML tags. + """ + CONTAINS_HTML_TAGS + + """ + Input contains URL. + """ + CONTAINS_URL + + """ + Invalid activation token. + """ + TOKEN_INVALID + + """ + Customer already enabled. + """ + ALREADY_ENABLED + + """ + Address does not exist. + """ + NOT_FOUND + + """ + Input email contains an invalid domain name. + """ + BAD_DOMAIN + + """ + Multipass token is not valid. + """ + INVALID_MULTIPASS_REQUEST +} + +""" +Return type for `customerRecover` mutation. +""" +type CustomerRecoverPayload { + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerResetByUrl` mutation. +""" +type CustomerResetByUrlPayload { + """ + The customer object which was reset. + """ + customer: Customer + + """ + A newly created customer access token object for the customer. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Specifies the fields required to reset a customer’s password. +""" +input CustomerResetInput { + """ + The reset token required to reset the customer’s password. + """ + resetToken: String! + + """ + New password that will be set as part of the reset password process. + """ + password: String! +} + +""" +Return type for `customerReset` mutation. +""" +type CustomerResetPayload { + """ + The customer object which was reset. + """ + customer: Customer + + """ + A newly created customer access token object for the customer. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Specifies the fields required to update the Customer information. +""" +input CustomerUpdateInput { + """ + The customer’s first name. + """ + firstName: String + + """ + The customer’s last name. + """ + lastName: String + + """ + The customer’s email. + """ + email: String + + """ + A unique phone number for the customer. + + Formatted using E.164 standard. For example, _+16135551111_. To remove the phone number, specify `null`. + """ + phone: String + + """ + The login password used by the customer. + """ + password: String + + """ + Indicates whether the customer has consented to be sent marketing material via email. + """ + acceptsMarketing: Boolean +} + +""" +Return type for `customerUpdate` mutation. +""" +type CustomerUpdatePayload { + """ + The updated customer object. + """ + customer: Customer + + """ + The newly created customer access token. If the customer's password is updated, all previous access tokens + (including the one used to perform this mutation) become invalid, and a new token is generated. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Represents an error that happens during execution of a customer mutation. +""" +type CustomerUserError implements DisplayableError { + """ + Error code to uniquely identify the error. + """ + code: CustomerErrorCode + + """ + Path to the input field which caused the error. + """ + field: [String!] + + """ + The error message. + """ + message: String! +} + +""" +An ISO-8601 encoded UTC date time string. Example value: `"2019-07-03T20:47:55Z"`. +""" +scalar DateTime + +""" +A signed decimal number, which supports arbitrary precision and is serialized as a string. Example value: `"29.99"`. +""" +scalar Decimal + +""" +Digital wallet, such as Apple Pay, which can be used for accelerated checkouts. +""" +enum DigitalWallet { + """ + Apple Pay. + """ + APPLE_PAY + + """ + Android Pay. + """ + ANDROID_PAY + + """ + Google Pay. + """ + GOOGLE_PAY + + """ + Shopify Pay. + """ + SHOPIFY_PAY +} + +""" +An amount discounting the line that has been allocated by a discount. +""" +type DiscountAllocation { + """ + Amount of discount allocated. + """ + allocatedAmount: MoneyV2! + + """ + The discount this allocated amount originated from. + """ + discountApplication: DiscountApplication! +} + +""" +Discount applications capture the intentions of a discount source at +the time of application. +""" +interface DiscountApplication { + """ + The method by which the discount's value is allocated to its entitled items. + """ + allocationMethod: DiscountApplicationAllocationMethod! + + """ + Which lines of targetType that the discount is allocated over. + """ + targetSelection: DiscountApplicationTargetSelection! + + """ + The type of line that the discount is applicable towards. + """ + targetType: DiscountApplicationTargetType! + + """ + The value of the discount application. + """ + value: PricingValue! +} + +""" +The method by which the discount's value is allocated onto its entitled lines. +""" +enum DiscountApplicationAllocationMethod { + """ + The value is spread across all entitled lines. + """ + ACROSS + + """ + The value is applied onto every entitled line. + """ + EACH + + """ + The value is specifically applied onto a particular line. + """ + ONE +} + +""" +An auto-generated type for paginating through multiple DiscountApplications. +""" +type DiscountApplicationConnection { + """ + A list of edges. + """ + edges: [DiscountApplicationEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one DiscountApplication and a cursor during pagination. +""" +type DiscountApplicationEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of DiscountApplicationEdge. + """ + node: DiscountApplication! +} + +""" +Which lines on the order that the discount is allocated over, of the type +defined by the Discount Application's target_type. +""" +enum DiscountApplicationTargetSelection { + """ + The discount is allocated onto all the lines. + """ + ALL + + """ + The discount is allocated onto only the lines it is entitled for. + """ + ENTITLED + + """ + The discount is allocated onto explicitly chosen lines. + """ + EXPLICIT +} + +""" +The type of line (i.e. line item or shipping line) on an order that the discount is applicable towards. +""" +enum DiscountApplicationTargetType { + """ + The discount applies onto line items. + """ + LINE_ITEM + + """ + The discount applies onto shipping lines. + """ + SHIPPING_LINE +} + +""" +Discount code applications capture the intentions of a discount code at +the time that it is applied. +""" +type DiscountCodeApplication implements DiscountApplication { + """ + The method by which the discount's value is allocated to its entitled items. + """ + allocationMethod: DiscountApplicationAllocationMethod! + + """ + Specifies whether the discount code was applied successfully. + """ + applicable: Boolean! + + """ + The string identifying the discount code that was used at the time of application. + """ + code: String! + + """ + Which lines of targetType that the discount is allocated over. + """ + targetSelection: DiscountApplicationTargetSelection! + + """ + The type of line that the discount is applicable towards. + """ + targetType: DiscountApplicationTargetType! + + """ + The value of the discount application. + """ + value: PricingValue! +} + +""" +Represents an error in the input of a mutation. +""" +interface DisplayableError { + """ + Path to the input field which caused the error. + """ + field: [String!] + + """ + The error message. + """ + message: String! +} + +""" +Represents a web address. +""" +type Domain { + """ + The host name of the domain (eg: `example.com`). + """ + host: String! + + """ + Whether SSL is enabled or not. + """ + sslEnabled: Boolean! + + """ + The URL of the domain (eg: `https://example.com`). + """ + url: URL! +} + +""" +Represents a video hosted outside of Shopify. +""" +type ExternalVideo implements Node & Media { + """ + A word or phrase to share the nature or contents of a media. + """ + alt: String + + """ + The URL. + """ + embeddedUrl: URL! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The media content type. + """ + mediaContentType: MediaContentType! + + """ + The preview image for the media. + """ + previewImage: Image +} + +""" +Represents a single fulfillment in an order. +""" +type Fulfillment { + """ + List of the fulfillment's line items. + """ + fulfillmentLineItems( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): FulfillmentLineItemConnection! + + """ + The name of the tracking company. + """ + trackingCompany: String + + """ + Tracking information associated with the fulfillment, + such as the tracking number and tracking URL. + """ + trackingInfo( + """ + Truncate the array result to this size. + """ + first: Int + ): [FulfillmentTrackingInfo!]! +} + +""" +Represents a single line item in a fulfillment. There is at most one fulfillment line item for each order line item. +""" +type FulfillmentLineItem { + """ + The associated order's line item. + """ + lineItem: OrderLineItem! + + """ + The amount fulfilled in this fulfillment. + """ + quantity: Int! +} + +""" +An auto-generated type for paginating through multiple FulfillmentLineItems. +""" +type FulfillmentLineItemConnection { + """ + A list of edges. + """ + edges: [FulfillmentLineItemEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one FulfillmentLineItem and a cursor during pagination. +""" +type FulfillmentLineItemEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of FulfillmentLineItemEdge. + """ + node: FulfillmentLineItem! +} + +""" +Tracking information associated with the fulfillment. +""" +type FulfillmentTrackingInfo { + """ + The tracking number of the fulfillment. + """ + number: String + + """ + The URL to track the fulfillment. + """ + url: URL +} + +""" +A string containing HTML code. Example value: `"<p>Grey cotton knit sweater.</p>"`. +""" +scalar HTML + +""" +Represents information about the metafields associated to the specified resource. +""" +interface HasMetafields { + """ + The metafield associated with the resource. + """ + metafield( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String! + + """ + Identifier for the metafield (maximum of 30 characters). + """ + key: String! + ): Metafield + + """ + A paginated list of metafields associated with the resource. + """ + metafields( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): MetafieldConnection! +} + +""" +Represents an image resource. +""" +type Image { + """ + A word or phrase to share the nature or contents of an image. + """ + altText: String + + """ + The original height of the image in pixels. Returns `null` if the image is not hosted by Shopify. + """ + height: Int + + """ + A unique identifier for the image. + """ + id: ID + + """ + The location of the original image as a URL. + + If there are any existing transformations in the original source URL, they will remain and not be stripped. + """ + originalSrc: URL! + + """ + The location of the image as a URL. + """ + src: URL! + @deprecated( + reason: "Previously an image had a single `src` field. This could either return the original image\nlocation or a URL that contained transformations such as sizing or scale.\n\nThese transformations were specified by arguments on the parent field.\n\nNow an image has two distinct URL fields: `originalSrc` and `transformedSrc`.\n\n* `originalSrc` - the original unmodified image URL\n* `transformedSrc` - the image URL with the specified transformations included\n\nTo migrate to the new fields, image transformations should be moved from the parent field to `transformedSrc`.\n\nBefore:\n```graphql\n{\n shop {\n productImages(maxWidth: 200, scale: 2) {\n edges {\n node {\n src\n }\n }\n }\n }\n}\n```\n\nAfter:\n```graphql\n{\n shop {\n productImages {\n edges {\n node {\n transformedSrc(maxWidth: 200, scale: 2)\n }\n }\n }\n }\n}\n```\n" + ) + + """ + The location of the transformed image as a URL. + + All transformation arguments are considered "best-effort". If they can be applied to an image, they will be. + Otherwise any transformations which an image type does not support will be ignored. + """ + transformedSrc( + """ + Image width in pixels between 1 and 5760. + """ + maxWidth: Int + + """ + Image height in pixels between 1 and 5760. + """ + maxHeight: Int + + """ + Crops the image according to the specified region. + """ + crop: CropRegion + + """ + Image size multiplier for high-resolution retina displays. Must be between 1 and 3. + """ + scale: Int = 1 + + """ + Best effort conversion of image into content type (SVG -> PNG, Anything -> JGP, Anything -> WEBP are supported). + """ + preferredContentType: ImageContentType + ): URL! + + """ + The original width of the image in pixels. Returns `null` if the image is not hosted by Shopify. + """ + width: Int +} + +""" +An auto-generated type for paginating through multiple Images. +""" +type ImageConnection { + """ + A list of edges. + """ + edges: [ImageEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +List of supported image content types. +""" +enum ImageContentType { + """ + A PNG image. + """ + PNG + + """ + A JPG image. + """ + JPG + + """ + A WEBP image. + """ + WEBP +} + +""" +An auto-generated type which holds one Image and a cursor during pagination. +""" +type ImageEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ImageEdge. + """ + node: Image! +} + +""" +Represents a mailing address for customers and shipping. +""" +type MailingAddress implements Node { + """ + The first line of the address. Typically the street address or PO Box number. + """ + address1: String + + """ + The second line of the address. Typically the number of the apartment, suite, or unit. + """ + address2: String + + """ + The name of the city, district, village, or town. + """ + city: String + + """ + The name of the customer's company or organization. + """ + company: String + + """ + The name of the country. + """ + country: String + + """ + The two-letter code for the country of the address. + + For example, US. + """ + countryCode: String @deprecated(reason: "Use `countryCodeV2` instead") + + """ + The two-letter code for the country of the address. + + For example, US. + """ + countryCodeV2: CountryCode + + """ + The first name of the customer. + """ + firstName: String + + """ + A formatted version of the address, customized by the provided arguments. + """ + formatted( + """ + Whether to include the customer's name in the formatted address. + """ + withName: Boolean = false + + """ + Whether to include the customer's company in the formatted address. + """ + withCompany: Boolean = true + ): [String!]! + + """ + A comma-separated list of the values for city, province, and country. + """ + formattedArea: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + The last name of the customer. + """ + lastName: String + + """ + The latitude coordinate of the customer address. + """ + latitude: Float + + """ + The longitude coordinate of the customer address. + """ + longitude: Float + + """ + The full name of the customer, based on firstName and lastName. + """ + name: String + + """ + A unique phone number for the customer. + + Formatted using E.164 standard. For example, _+16135551111_. + """ + phone: String + + """ + The region of the address, such as the province, state, or district. + """ + province: String + + """ + The two-letter code for the region. + + For example, ON. + """ + provinceCode: String + + """ + The zip or postal code of the address. + """ + zip: String +} + +""" +An auto-generated type for paginating through multiple MailingAddresses. +""" +type MailingAddressConnection { + """ + A list of edges. + """ + edges: [MailingAddressEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one MailingAddress and a cursor during pagination. +""" +type MailingAddressEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of MailingAddressEdge. + """ + node: MailingAddress! +} + +""" +Specifies the fields accepted to create or update a mailing address. +""" +input MailingAddressInput { + """ + The first line of the address. Typically the street address or PO Box number. + """ + address1: String + + """ + The second line of the address. Typically the number of the apartment, suite, or unit. + """ + address2: String + + """ + The name of the city, district, village, or town. + """ + city: String + + """ + The name of the customer's company or organization. + """ + company: String + + """ + The name of the country. + """ + country: String + + """ + The first name of the customer. + """ + firstName: String + + """ + The last name of the customer. + """ + lastName: String + + """ + A unique phone number for the customer. + + Formatted using E.164 standard. For example, _+16135551111_. + """ + phone: String + + """ + The region of the address, such as the province, state, or district. + """ + province: String + + """ + The zip or postal code of the address. + """ + zip: String +} + +""" +Manual discount applications capture the intentions of a discount that was manually created. +""" +type ManualDiscountApplication implements DiscountApplication { + """ + The method by which the discount's value is allocated to its entitled items. + """ + allocationMethod: DiscountApplicationAllocationMethod! + + """ + The description of the application. + """ + description: String + + """ + Which lines of targetType that the discount is allocated over. + """ + targetSelection: DiscountApplicationTargetSelection! + + """ + The type of line that the discount is applicable towards. + """ + targetType: DiscountApplicationTargetType! + + """ + The title of the application. + """ + title: String! + + """ + The value of the discount application. + """ + value: PricingValue! +} + +""" +Represents a media interface. +""" +interface Media { + """ + A word or phrase to share the nature or contents of a media. + """ + alt: String + + """ + The media content type. + """ + mediaContentType: MediaContentType! + + """ + The preview image for the media. + """ + previewImage: Image +} + +""" +An auto-generated type for paginating through multiple Media. +""" +type MediaConnection { + """ + A list of edges. + """ + edges: [MediaEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +The possible content types for a media object. +""" +enum MediaContentType { + """ + An externally hosted video. + """ + EXTERNAL_VIDEO + + """ + A Shopify hosted image. + """ + IMAGE + + """ + A 3d model. + """ + MODEL_3D + + """ + A Shopify hosted video. + """ + VIDEO +} + +""" +An auto-generated type which holds one Media and a cursor during pagination. +""" +type MediaEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of MediaEdge. + """ + node: Media! +} + +""" +Represents a Shopify hosted image. +""" +type MediaImage implements Node & Media { + """ + A word or phrase to share the nature or contents of a media. + """ + alt: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + The image for the media. + """ + image: Image + + """ + The media content type. + """ + mediaContentType: MediaContentType! + + """ + The preview image for the media. + """ + previewImage: Image +} + +""" +Metafields represent custom metadata attached to a resource. Metafields can be sorted into namespaces and are +comprised of keys, values, and value types. +""" +type Metafield implements Node { + """ + The date and time when the storefront metafield was created. + """ + createdAt: DateTime! + + """ + The description of a metafield. + """ + description: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + The key name for a metafield. + """ + key: String! + + """ + The namespace for a metafield. + """ + namespace: String! + + """ + The parent object that the metafield belongs to. + """ + parentResource: MetafieldParentResource! + + """ + The date and time when the storefront metafield was updated. + """ + updatedAt: DateTime! + + """ + The value of a metafield. + """ + value: String! + + """ + Represents the metafield value type. + """ + valueType: MetafieldValueType! +} + +""" +An auto-generated type for paginating through multiple Metafields. +""" +type MetafieldConnection { + """ + A list of edges. + """ + edges: [MetafieldEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Metafield and a cursor during pagination. +""" +type MetafieldEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of MetafieldEdge. + """ + node: Metafield! +} + +""" +A resource that the metafield belongs to. +""" +union MetafieldParentResource = Product | ProductVariant + +""" +Metafield value types. +""" +enum MetafieldValueType { + """ + A string metafield. + """ + STRING + + """ + An integer metafield. + """ + INTEGER + + """ + A json string metafield. + """ + JSON_STRING +} + +""" +Represents a Shopify hosted 3D model. +""" +type Model3d implements Node & Media { + """ + A word or phrase to share the nature or contents of a media. + """ + alt: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + The media content type. + """ + mediaContentType: MediaContentType! + + """ + The preview image for the media. + """ + previewImage: Image + + """ + The sources for a 3d model. + """ + sources: [Model3dSource!]! +} + +""" +Represents a source for a Shopify hosted 3d model. +""" +type Model3dSource { + """ + The filesize of the 3d model. + """ + filesize: Int! + + """ + The format of the 3d model. + """ + format: String! + + """ + The MIME type of the 3d model. + """ + mimeType: String! + + """ + The URL of the 3d model. + """ + url: String! +} + +""" +A monetary value string. Example value: `"100.57"`. +""" +scalar Money + +""" +Specifies the fields for a monetary value with currency. +""" +input MoneyInput { + """ + Decimal money amount. + """ + amount: Decimal! + + """ + Currency of the money. + """ + currencyCode: CurrencyCode! +} + +""" +A monetary value with currency. + +To format currencies, combine this type's amount and currencyCode fields with your client's locale. + +For example, in JavaScript you could use Intl.NumberFormat: + +```js +new Intl.NumberFormat(locale, { + style: 'currency', + currency: currencyCode +}).format(amount); +``` + +Other formatting libraries include: + +* iOS - [NumberFormatter](https://developer.apple.com/documentation/foundation/numberformatter) +* Android - [NumberFormat](https://developer.android.com/reference/java/text/NumberFormat.html) +* PHP - [NumberFormatter](http://php.net/manual/en/class.numberformatter.php) + +For a more general solution, the [Unicode CLDR number formatting database] is available with many implementations +(such as [TwitterCldr](https://github.com/twitter/twitter-cldr-rb)). +""" +type MoneyV2 { + """ + Decimal money amount. + """ + amount: Decimal! + + """ + Currency of the money. + """ + currencyCode: CurrencyCode! +} + +""" +An auto-generated type for paginating through multiple MoneyV2s. +""" +type MoneyV2Connection { + """ + A list of edges. + """ + edges: [MoneyV2Edge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one MoneyV2 and a cursor during pagination. +""" +type MoneyV2Edge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of MoneyV2Edge. + """ + node: MoneyV2! +} + +""" +The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. +""" +type Mutation { + """ + Updates the attributes of a checkout. + """ + checkoutAttributesUpdate( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The fields used to update a checkout's attributes. + """ + input: CheckoutAttributesUpdateInput! + ): CheckoutAttributesUpdatePayload + @deprecated(reason: "Use `checkoutAttributesUpdateV2` instead") + + """ + Updates the attributes of a checkout. + """ + checkoutAttributesUpdateV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The checkout attributes to update. + """ + input: CheckoutAttributesUpdateV2Input! + ): CheckoutAttributesUpdateV2Payload + + """ + Completes a checkout without providing payment information. You can use this mutation for free items or items whose purchase price is covered by a gift card. + """ + checkoutCompleteFree( + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutCompleteFreePayload + + """ + Completes a checkout using a credit card token from Shopify's Vault. + """ + checkoutCompleteWithCreditCard( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The credit card info to apply as a payment. + """ + payment: CreditCardPaymentInput! + ): CheckoutCompleteWithCreditCardPayload + @deprecated(reason: "Use `checkoutCompleteWithCreditCardV2` instead") + + """ + Completes a checkout using a credit card token from Shopify's card vault. Before you can complete checkouts using CheckoutCompleteWithCreditCardV2, you need to [_request payment processing_](https://help.shopify.com/api/guides/sales-channel-sdk/getting-started#request-payment-processing). + """ + checkoutCompleteWithCreditCardV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The credit card info to apply as a payment. + """ + payment: CreditCardPaymentInputV2! + ): CheckoutCompleteWithCreditCardV2Payload + + """ + Completes a checkout with a tokenized payment. + """ + checkoutCompleteWithTokenizedPayment( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The info to apply as a tokenized payment. + """ + payment: TokenizedPaymentInput! + ): CheckoutCompleteWithTokenizedPaymentPayload + @deprecated(reason: "Use `checkoutCompleteWithTokenizedPaymentV2` instead") + + """ + Completes a checkout with a tokenized payment. + """ + checkoutCompleteWithTokenizedPaymentV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The info to apply as a tokenized payment. + """ + payment: TokenizedPaymentInputV2! + ): CheckoutCompleteWithTokenizedPaymentV2Payload + @deprecated(reason: "Use `checkoutCompleteWithTokenizedPaymentV3` instead") + + """ + Completes a checkout with a tokenized payment. + """ + checkoutCompleteWithTokenizedPaymentV3( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The info to apply as a tokenized payment. + """ + payment: TokenizedPaymentInputV3! + ): CheckoutCompleteWithTokenizedPaymentV3Payload + + """ + Creates a new checkout. + """ + checkoutCreate( + """ + The fields used to create a checkout. + """ + input: CheckoutCreateInput! + ): CheckoutCreatePayload + + """ + Associates a customer to the checkout. + """ + checkoutCustomerAssociate( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The customer access token of the customer to associate. + """ + customerAccessToken: String! + ): CheckoutCustomerAssociatePayload + @deprecated(reason: "Use `checkoutCustomerAssociateV2` instead") + + """ + Associates a customer to the checkout. + """ + checkoutCustomerAssociateV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The customer access token of the customer to associate. + """ + customerAccessToken: String! + ): CheckoutCustomerAssociateV2Payload + + """ + Disassociates the current checkout customer from the checkout. + """ + checkoutCustomerDisassociate( + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutCustomerDisassociatePayload + @deprecated(reason: "Use `checkoutCustomerDisassociateV2` instead") + + """ + Disassociates the current checkout customer from the checkout. + """ + checkoutCustomerDisassociateV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutCustomerDisassociateV2Payload + + """ + Applies a discount to an existing checkout using a discount code. + """ + checkoutDiscountCodeApply( + """ + The discount code to apply to the checkout. + """ + discountCode: String! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutDiscountCodeApplyPayload + @deprecated(reason: "Use `checkoutDiscountCodeApplyV2` instead") + + """ + Applies a discount to an existing checkout using a discount code. + """ + checkoutDiscountCodeApplyV2( + """ + The discount code to apply to the checkout. + """ + discountCode: String! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutDiscountCodeApplyV2Payload + + """ + Removes the applied discount from an existing checkout. + """ + checkoutDiscountCodeRemove( + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutDiscountCodeRemovePayload + + """ + Updates the email on an existing checkout. + """ + checkoutEmailUpdate( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The email to update the checkout with. + """ + email: String! + ): CheckoutEmailUpdatePayload + @deprecated(reason: "Use `checkoutEmailUpdateV2` instead") + + """ + Updates the email on an existing checkout. + """ + checkoutEmailUpdateV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The email to update the checkout with. + """ + email: String! + ): CheckoutEmailUpdateV2Payload + + """ + Applies a gift card to an existing checkout using a gift card code. This will replace all currently applied gift cards. + """ + checkoutGiftCardApply( + """ + The code of the gift card to apply on the checkout. + """ + giftCardCode: String! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutGiftCardApplyPayload + @deprecated(reason: "Use `checkoutGiftCardsAppend` instead") + + """ + Removes an applied gift card from the checkout. + """ + checkoutGiftCardRemove( + """ + The ID of the Applied Gift Card to remove from the Checkout. + """ + appliedGiftCardId: ID! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutGiftCardRemovePayload + @deprecated(reason: "Use `checkoutGiftCardRemoveV2` instead") + + """ + Removes an applied gift card from the checkout. + """ + checkoutGiftCardRemoveV2( + """ + The ID of the Applied Gift Card to remove from the Checkout. + """ + appliedGiftCardId: ID! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutGiftCardRemoveV2Payload + + """ + Appends gift cards to an existing checkout. + """ + checkoutGiftCardsAppend( + """ + A list of gift card codes to append to the checkout. + """ + giftCardCodes: [String!]! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutGiftCardsAppendPayload + + """ + Adds a list of line items to a checkout. + """ + checkoutLineItemsAdd( + """ + A list of line item objects to add to the checkout. + """ + lineItems: [CheckoutLineItemInput!]! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutLineItemsAddPayload + + """ + Removes line items from an existing checkout. + """ + checkoutLineItemsRemove( + """ + The checkout on which to remove line items. + """ + checkoutId: ID! + + """ + Line item ids to remove. + """ + lineItemIds: [ID!]! + ): CheckoutLineItemsRemovePayload + + """ + Sets a list of line items to a checkout. + """ + checkoutLineItemsReplace( + """ + A list of line item objects to set on the checkout. + """ + lineItems: [CheckoutLineItemInput!]! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutLineItemsReplacePayload + + """ + Updates line items on a checkout. + """ + checkoutLineItemsUpdate( + """ + The checkout on which to update line items. + """ + checkoutId: ID! + + """ + Line items to update. + """ + lineItems: [CheckoutLineItemUpdateInput!]! + ): CheckoutLineItemsUpdatePayload + + """ + Updates the shipping address of an existing checkout. + """ + checkoutShippingAddressUpdate( + """ + The shipping address to where the line items will be shipped. + """ + shippingAddress: MailingAddressInput! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutShippingAddressUpdatePayload + @deprecated(reason: "Use `checkoutShippingAddressUpdateV2` instead") + + """ + Updates the shipping address of an existing checkout. + """ + checkoutShippingAddressUpdateV2( + """ + The shipping address to where the line items will be shipped. + """ + shippingAddress: MailingAddressInput! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutShippingAddressUpdateV2Payload + + """ + Updates the shipping lines on an existing checkout. + """ + checkoutShippingLineUpdate( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + A unique identifier to a Checkout’s shipping provider, price, and title combination, enabling the customer to select the availableShippingRates. + """ + shippingRateHandle: String! + ): CheckoutShippingLineUpdatePayload + + """ + Creates a customer access token. + The customer access token is required to modify the customer object in any way. + """ + customerAccessTokenCreate( + """ + The fields used to create a customer access token. + """ + input: CustomerAccessTokenCreateInput! + ): CustomerAccessTokenCreatePayload + + """ + Creates a customer access token using a multipass token instead of email and password. + A customer record is created if customer does not exist. If a customer record already + exists but the record is disabled, then it's enabled. + """ + customerAccessTokenCreateWithMultipass( + """ + A valid multipass token to be authenticated. + """ + multipassToken: String! + ): CustomerAccessTokenCreateWithMultipassPayload + + """ + Permanently destroys a customer access token. + """ + customerAccessTokenDelete( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + ): CustomerAccessTokenDeletePayload + + """ + Renews a customer access token. + + Access token renewal must happen *before* a token expires. + If a token has already expired, a new one should be created instead via `customerAccessTokenCreate`. + """ + customerAccessTokenRenew( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + ): CustomerAccessTokenRenewPayload + + """ + Activates a customer. + """ + customerActivate( + """ + Specifies the customer to activate. + """ + id: ID! + + """ + The fields used to activate a customer. + """ + input: CustomerActivateInput! + ): CustomerActivatePayload + + """ + Activates a customer with the activation url received from `customerCreate`. + """ + customerActivateByUrl( + """ + The customer activation URL. + """ + activationUrl: URL! + + """ + A new password set during activation. + """ + password: String! + ): CustomerActivateByUrlPayload + + """ + Creates a new address for a customer. + """ + customerAddressCreate( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + + """ + The customer mailing address to create. + """ + address: MailingAddressInput! + ): CustomerAddressCreatePayload + + """ + Permanently deletes the address of an existing customer. + """ + customerAddressDelete( + """ + Specifies the address to delete. + """ + id: ID! + + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + ): CustomerAddressDeletePayload + + """ + Updates the address of an existing customer. + """ + customerAddressUpdate( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + + """ + Specifies the customer address to update. + """ + id: ID! + + """ + The customer’s mailing address. + """ + address: MailingAddressInput! + ): CustomerAddressUpdatePayload + + """ + Creates a new customer. + """ + customerCreate( + """ + The fields used to create a new customer. + """ + input: CustomerCreateInput! + ): CustomerCreatePayload + + """ + Updates the default address of an existing customer. + """ + customerDefaultAddressUpdate( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + + """ + ID of the address to set as the new default for the customer. + """ + addressId: ID! + ): CustomerDefaultAddressUpdatePayload + + """ + Sends a reset password email to the customer, as the first step in the reset password process. + """ + customerRecover( + """ + The email address of the customer to recover. + """ + email: String! + ): CustomerRecoverPayload + + """ + Resets a customer’s password with a token received from `CustomerRecover`. + """ + customerReset( + """ + Specifies the customer to reset. + """ + id: ID! + + """ + The fields used to reset a customer’s password. + """ + input: CustomerResetInput! + ): CustomerResetPayload + + """ + Resets a customer’s password with the reset password url received from `CustomerRecover`. + """ + customerResetByUrl( + """ + The customer's reset password url. + """ + resetUrl: URL! + + """ + New password that will be set as part of the reset password process. + """ + password: String! + ): CustomerResetByUrlPayload + + """ + Updates an existing customer. + """ + customerUpdate( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + + """ + The customer object input. + """ + customer: CustomerUpdateInput! + ): CustomerUpdatePayload +} + +""" +An object with an ID to support global identification. +""" +interface Node { + """ + Globally unique identifier. + """ + id: ID! +} + +""" +An order is a customer’s completed request to purchase one or more products from a shop. An order is created when a customer completes the checkout process, during which time they provides an email address, billing address and payment information. +""" +type Order implements Node { + """ + The reason for the order's cancellation. Returns `null` if the order wasn't canceled. + """ + cancelReason: OrderCancelReason + + """ + The date and time when the order was canceled. Returns null if the order wasn't canceled. + """ + canceledAt: DateTime + + """ + The code of the currency used for the payment. + """ + currencyCode: CurrencyCode! + + """ + The subtotal of line items and their discounts, excluding line items that have been removed. Does not contain order-level discounts, duties, shipping costs, or shipping discounts. Taxes are not included unless the order is a taxes-included order. + """ + currentSubtotalPrice: MoneyV2! + + """ + The total amount of the order, including duties, taxes and discounts, minus amounts for line items that have been removed. + """ + currentTotalPrice: MoneyV2! + + """ + The total of all taxes applied to the order, excluding taxes for returned line items. + """ + currentTotalTax: MoneyV2! + + """ + The locale code in which this specific order happened. + """ + customerLocale: String + + """ + The unique URL that the customer can use to access the order. + """ + customerUrl: URL + + """ + Discounts that have been applied on the order. + """ + discountApplications( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): DiscountApplicationConnection! + + """ + Whether the order has had any edits applied or not. + """ + edited: Boolean! + + """ + The customer's email address. + """ + email: String + + """ + The financial status of the order. + """ + financialStatus: OrderFinancialStatus + + """ + The fulfillment status for the order. + """ + fulfillmentStatus: OrderFulfillmentStatus! + + """ + Globally unique identifier. + """ + id: ID! + + """ + List of the order’s line items. + """ + lineItems( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): OrderLineItemConnection! + + """ + Unique identifier for the order that appears on the order. + For example, _#1000_ or _Store1001. + """ + name: String! + + """ + A unique numeric identifier for the order for use by shop owner and customer. + """ + orderNumber: Int! + + """ + The total price of the order before any applied edits. + """ + originalTotalPrice: MoneyV2! + + """ + The customer's phone number for receiving SMS notifications. + """ + phone: String + + """ + The date and time when the order was imported. + This value can be set to dates in the past when importing from other systems. + If no value is provided, it will be auto-generated based on current date and time. + """ + processedAt: DateTime! + + """ + The address to where the order will be shipped. + """ + shippingAddress: MailingAddress + + """ + The discounts that have been allocated onto the shipping line by discount applications. + """ + shippingDiscountAllocations: [DiscountAllocation!]! + + """ + The unique URL for the order's status page. + """ + statusUrl: URL! + + """ + Price of the order before shipping and taxes. + """ + subtotalPrice: Money @deprecated(reason: "Use `subtotalPriceV2` instead") + + """ + Price of the order before duties, shipping and taxes. + """ + subtotalPriceV2: MoneyV2 + + """ + List of the order’s successful fulfillments. + """ + successfulFulfillments( + """ + Truncate the array result to this size. + """ + first: Int + ): [Fulfillment!] + + """ + The sum of all the prices of all the items in the order, taxes and discounts included (must be positive). + """ + totalPrice: Money! @deprecated(reason: "Use `totalPriceV2` instead") + + """ + The sum of all the prices of all the items in the order, duties, taxes and discounts included (must be positive). + """ + totalPriceV2: MoneyV2! + + """ + The total amount that has been refunded. + """ + totalRefunded: Money! @deprecated(reason: "Use `totalRefundedV2` instead") + + """ + The total amount that has been refunded. + """ + totalRefundedV2: MoneyV2! + + """ + The total cost of shipping. + """ + totalShippingPrice: Money! + @deprecated(reason: "Use `totalShippingPriceV2` instead") + + """ + The total cost of shipping. + """ + totalShippingPriceV2: MoneyV2! + + """ + The total cost of taxes. + """ + totalTax: Money @deprecated(reason: "Use `totalTaxV2` instead") + + """ + The total cost of taxes. + """ + totalTaxV2: MoneyV2 +} + +""" +Represents the reason for the order's cancellation. +""" +enum OrderCancelReason { + """ + The customer wanted to cancel the order. + """ + CUSTOMER + + """ + The order was fraudulent. + """ + FRAUD + + """ + There was insufficient inventory. + """ + INVENTORY + + """ + Payment was declined. + """ + DECLINED + + """ + The order was canceled for an unlisted reason. + """ + OTHER +} + +""" +An auto-generated type for paginating through multiple Orders. +""" +type OrderConnection { + """ + A list of edges. + """ + edges: [OrderEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Order and a cursor during pagination. +""" +type OrderEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of OrderEdge. + """ + node: Order! +} + +""" +Represents the order's current financial status. +""" +enum OrderFinancialStatus { + """ + Displayed as **Pending**. + """ + PENDING + + """ + Displayed as **Authorized**. + """ + AUTHORIZED + + """ + Displayed as **Partially paid**. + """ + PARTIALLY_PAID + + """ + Displayed as **Partially refunded**. + """ + PARTIALLY_REFUNDED + + """ + Displayed as **Voided**. + """ + VOIDED + + """ + Displayed as **Paid**. + """ + PAID + + """ + Displayed as **Refunded**. + """ + REFUNDED +} + +""" +Represents the order's current fulfillment status. +""" +enum OrderFulfillmentStatus { + """ + Displayed as **Unfulfilled**. + """ + UNFULFILLED + + """ + Displayed as **Partially fulfilled**. + """ + PARTIALLY_FULFILLED + + """ + Displayed as **Fulfilled**. + """ + FULFILLED + + """ + Displayed as **Restocked**. + """ + RESTOCKED + + """ + Displayed as **Pending fulfillment**. + """ + PENDING_FULFILLMENT + + """ + Displayed as **Open**. + """ + OPEN + + """ + Displayed as **In progress**. + """ + IN_PROGRESS + + """ + Displayed as **Scheduled**. + """ + SCHEDULED +} + +""" +Represents a single line in an order. There is one line item for each distinct product variant. +""" +type OrderLineItem { + """ + The number of entries associated to the line item minus the items that have been removed. + """ + currentQuantity: Int! + + """ + List of custom attributes associated to the line item. + """ + customAttributes: [Attribute!]! + + """ + The discounts that have been allocated onto the order line item by discount applications. + """ + discountAllocations: [DiscountAllocation!]! + + """ + The total price of the line item, including discounts, and displayed in the presentment currency. + """ + discountedTotalPrice: MoneyV2! + + """ + The total price of the line item, not including any discounts. The total price is calculated using the original unit price multiplied by the quantity, and it is displayed in the presentment currency. + """ + originalTotalPrice: MoneyV2! + + """ + The number of products variants associated to the line item. + """ + quantity: Int! + + """ + The title of the product combined with title of the variant. + """ + title: String! + + """ + The product variant object associated to the line item. + """ + variant: ProductVariant +} + +""" +An auto-generated type for paginating through multiple OrderLineItems. +""" +type OrderLineItemConnection { + """ + A list of edges. + """ + edges: [OrderLineItemEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one OrderLineItem and a cursor during pagination. +""" +type OrderLineItemEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of OrderLineItemEdge. + """ + node: OrderLineItem! +} + +""" +The set of valid sort keys for the Order query. +""" +enum OrderSortKeys { + """ + Sort by the `processed_at` value. + """ + PROCESSED_AT + + """ + Sort by the `total_price` value. + """ + TOTAL_PRICE + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +Shopify merchants can create pages to hold static HTML content. Each Page object represents a custom page on the online store. +""" +type Page implements Node { + """ + The description of the page, complete with HTML formatting. + """ + body: HTML! + + """ + Summary of the page body. + """ + bodySummary: String! + + """ + The timestamp of the page creation. + """ + createdAt: DateTime! + + """ + A human-friendly unique string for the page automatically generated from its title. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The page's SEO information. + """ + seo: SEO + + """ + The title of the page. + """ + title: String! + + """ + The timestamp of the latest page update. + """ + updatedAt: DateTime! + + """ + The url pointing to the page accessible from the web. + """ + url: URL! +} + +""" +An auto-generated type for paginating through multiple Pages. +""" +type PageConnection { + """ + A list of edges. + """ + edges: [PageEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Page and a cursor during pagination. +""" +type PageEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of PageEdge. + """ + node: Page! +} + +""" +Information about pagination in a connection. +""" +type PageInfo { + """ + Indicates if there are more pages to fetch. + """ + hasNextPage: Boolean! + + """ + Indicates if there are any pages prior to the current page. + """ + hasPreviousPage: Boolean! +} + +""" +The set of valid sort keys for the Page query. +""" +enum PageSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `updated_at` value. + """ + UPDATED_AT + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +A payment applied to a checkout. +""" +type Payment implements Node { + """ + The amount of the payment. + """ + amount: Money! @deprecated(reason: "Use `amountV2` instead") + + """ + The amount of the payment. + """ + amountV2: MoneyV2! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddress + + """ + The checkout to which the payment belongs. + """ + checkout: Checkout! + + """ + The credit card used for the payment in the case of direct payments. + """ + creditCard: CreditCard + + """ + A message describing a processing error during asynchronous processing. + """ + errorMessage: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + A client-side generated token to identify a payment and perform idempotent operations. + """ + idempotencyKey: String + + """ + The URL where the customer needs to be redirected so they can complete the 3D Secure payment flow. + """ + nextActionUrl: URL + + """ + Whether or not the payment is still processing asynchronously. + """ + ready: Boolean! + + """ + A flag to indicate if the payment is to be done in test mode for gateways that support it. + """ + test: Boolean! + + """ + The actual transaction recorded by Shopify after having processed the payment with the gateway. + """ + transaction: Transaction +} + +""" +Settings related to payments. +""" +type PaymentSettings { + """ + List of the card brands which the shop accepts. + """ + acceptedCardBrands: [CardBrand!]! + + """ + The url pointing to the endpoint to vault credit cards. + """ + cardVaultUrl: URL! + + """ + The country where the shop is located. + """ + countryCode: CountryCode! + + """ + The three-letter code for the shop's primary currency. + """ + currencyCode: CurrencyCode! + + """ + A list of enabled currencies (ISO 4217 format) that the shop accepts. Merchants can enable currencies from their Shopify Payments settings in the Shopify admin. + """ + enabledPresentmentCurrencies: [CurrencyCode!]! + + """ + The shop’s Shopify Payments account id. + """ + shopifyPaymentsAccountId: String + + """ + List of the digital wallets which the shop supports. + """ + supportedDigitalWallets: [DigitalWallet!]! +} + +""" +The valid values for the types of payment token. +""" +enum PaymentTokenType { + """ + Apple Pay token type. + """ + APPLE_PAY + + """ + Vault payment token type. + """ + VAULT + + """ + Shopify Pay token type. + """ + SHOPIFY_PAY + + """ + Google Pay token type. + """ + GOOGLE_PAY +} + +""" +The value of the percentage pricing object. +""" +type PricingPercentageValue { + """ + The percentage value of the object. + """ + percentage: Float! +} + +""" +The price value (fixed or percentage) for a discount application. +""" +union PricingValue = MoneyV2 | PricingPercentageValue + +""" +A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. +For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). +""" +type Product implements Node & HasMetafields { + """ + Indicates if at least one product variant is available for sale. + """ + availableForSale: Boolean! + + """ + List of collections a product belongs to. + """ + collections( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): CollectionConnection! + + """ + The compare at price of the product across all variants. + """ + compareAtPriceRange: ProductPriceRange! + + """ + The date and time when the product was created. + """ + createdAt: DateTime! + + """ + Stripped description of the product, single line with HTML tags removed. + """ + description( + """ + Truncates string after the given length. + """ + truncateAt: Int + ): String! + + """ + The description of the product, complete with HTML formatting. + """ + descriptionHtml: HTML! + + """ + A human-friendly unique string for the Product automatically generated from its title. + They are used by the Liquid templating language to refer to objects. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + List of images associated with the product. + """ + images( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductImageSortKeys = POSITION + + """ + Image width in pixels between 1 and 2048. This argument is deprecated: Use `maxWidth` on `Image.transformedSrc` instead. + """ + maxWidth: Int + + """ + Image height in pixels between 1 and 2048. This argument is deprecated: Use `maxHeight` on `Image.transformedSrc` instead. + """ + maxHeight: Int + + """ + Crops the image according to the specified region. This argument is deprecated: Use `crop` on `Image.transformedSrc` instead. + """ + crop: CropRegion + + """ + Image size multiplier for high-resolution retina displays. Must be between 1 and 3. This argument is deprecated: Use `scale` on `Image.transformedSrc` instead. + """ + scale: Int = 1 + ): ImageConnection! + + """ + The media associated with the product. + """ + media( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductMediaSortKeys = POSITION + ): MediaConnection! + + """ + The metafield associated with the resource. + """ + metafield( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String! + + """ + Identifier for the metafield (maximum of 30 characters). + """ + key: String! + ): Metafield + + """ + A paginated list of metafields associated with the resource. + """ + metafields( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): MetafieldConnection! + + """ + The online store URL for the product. + A value of `null` indicates that the product is not published to the Online Store sales channel. + """ + onlineStoreUrl: URL + + """ + List of product options. + """ + options( + """ + Truncate the array result to this size. + """ + first: Int + ): [ProductOption!]! + + """ + List of price ranges in the presentment currencies for this shop. + """ + presentmentPriceRanges( + """ + Specifies the presentment currencies to return a price range in. + """ + presentmentCurrencies: [CurrencyCode!] + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): ProductPriceRangeConnection! + + """ + The price range. + """ + priceRange: ProductPriceRange! + + """ + A categorization that a product can be tagged with, commonly used for filtering and searching. + """ + productType: String! + + """ + The date and time when the product was published to the channel. + """ + publishedAt: DateTime! + + """ + The product's SEO information. + """ + seo: SEO! + + """ + A comma separated list of tags that have been added to the product. + Additional access scope required for private apps: unauthenticated_read_product_tags. + """ + tags: [String!]! + + """ + The product’s title. + """ + title: String! + + """ + The total quantity of inventory in stock for this Product. + """ + totalInventory: Int + + """ + The date and time when the product was last modified. + A product's `updatedAt` value can change for different reasons. For example, if an order + is placed for a product that has inventory tracking set up, then the inventory adjustment + is counted as an update. + """ + updatedAt: DateTime! + + """ + Find a product’s variant based on its selected options. + This is useful for converting a user’s selection of product options into a single matching variant. + If there is not a variant for the selected options, `null` will be returned. + """ + variantBySelectedOptions( + """ + The input fields used for a selected option. + """ + selectedOptions: [SelectedOptionInput!]! + ): ProductVariant + + """ + List of the product’s variants. + """ + variants( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductVariantSortKeys = POSITION + ): ProductVariantConnection! + + """ + The product’s vendor name. + """ + vendor: String! +} + +""" +The set of valid sort keys for the ProductCollection query. +""" +enum ProductCollectionSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `price` value. + """ + PRICE + + """ + Sort by the `best-selling` value. + """ + BEST_SELLING + + """ + Sort by the `created` value. + """ + CREATED + + """ + Sort by the `id` value. + """ + ID + + """ + Sort by the `manual` value. + """ + MANUAL + + """ + Sort by the `collection-default` value. + """ + COLLECTION_DEFAULT + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +An auto-generated type for paginating through multiple Products. +""" +type ProductConnection { + """ + A list of edges. + """ + edges: [ProductEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Product and a cursor during pagination. +""" +type ProductEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ProductEdge. + """ + node: Product! +} + +""" +The set of valid sort keys for the ProductImage query. +""" +enum ProductImageSortKeys { + """ + Sort by the `created_at` value. + """ + CREATED_AT + + """ + Sort by the `position` value. + """ + POSITION + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +The set of valid sort keys for the ProductMedia query. +""" +enum ProductMediaSortKeys { + """ + Sort by the `position` value. + """ + POSITION + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +Product property names like "Size", "Color", and "Material" that the customers can select. +Variants are selected based on permutations of these options. +255 characters limit each. +""" +type ProductOption implements Node { + """ + Globally unique identifier. + """ + id: ID! + + """ + The product option’s name. + """ + name: String! + + """ + The corresponding value to the product option name. + """ + values: [String!]! +} + +""" +The price range of the product. +""" +type ProductPriceRange { + """ + The highest variant's price. + """ + maxVariantPrice: MoneyV2! + + """ + The lowest variant's price. + """ + minVariantPrice: MoneyV2! +} + +""" +An auto-generated type for paginating through multiple ProductPriceRanges. +""" +type ProductPriceRangeConnection { + """ + A list of edges. + """ + edges: [ProductPriceRangeEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one ProductPriceRange and a cursor during pagination. +""" +type ProductPriceRangeEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ProductPriceRangeEdge. + """ + node: ProductPriceRange! +} + +""" +The set of valid sort keys for the Product query. +""" +enum ProductSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `product_type` value. + """ + PRODUCT_TYPE + + """ + Sort by the `vendor` value. + """ + VENDOR + + """ + Sort by the `updated_at` value. + """ + UPDATED_AT + + """ + Sort by the `created_at` value. + """ + CREATED_AT + + """ + Sort by the `best_selling` value. + """ + BEST_SELLING + + """ + Sort by the `price` value. + """ + PRICE + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +A product variant represents a different version of a product, such as differing sizes or differing colors. +""" +type ProductVariant implements Node & HasMetafields { + """ + Indicates if the product variant is in stock. + """ + available: Boolean @deprecated(reason: "Use `availableForSale` instead") + + """ + Indicates if the product variant is available for sale. + """ + availableForSale: Boolean! + + """ + The compare at price of the variant. This can be used to mark a variant as on sale, when `compareAtPrice` is higher than `price`. + """ + compareAtPrice: Money @deprecated(reason: "Use `compareAtPriceV2` instead") + + """ + The compare at price of the variant. This can be used to mark a variant as on sale, when `compareAtPriceV2` is higher than `priceV2`. + """ + compareAtPriceV2: MoneyV2 + + """ + Whether a product is out of stock but still available for purchase (used for backorders). + """ + currentlyNotInStock: Boolean! + + """ + Globally unique identifier. + """ + id: ID! + + """ + Image associated with the product variant. This field falls back to the product image if no image is available. + """ + image( + """ + Image width in pixels between 1 and 2048. This argument is deprecated: Use `maxWidth` on `Image.transformedSrc` instead. + """ + maxWidth: Int + + """ + Image height in pixels between 1 and 2048. This argument is deprecated: Use `maxHeight` on `Image.transformedSrc` instead. + """ + maxHeight: Int + + """ + Crops the image according to the specified region. This argument is deprecated: Use `crop` on `Image.transformedSrc` instead. + """ + crop: CropRegion + + """ + Image size multiplier for high-resolution retina displays. Must be between 1 and 3. This argument is deprecated: Use `scale` on `Image.transformedSrc` instead. + """ + scale: Int = 1 + ): Image + + """ + The metafield associated with the resource. + """ + metafield( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String! + + """ + Identifier for the metafield (maximum of 30 characters). + """ + key: String! + ): Metafield + + """ + A paginated list of metafields associated with the resource. + """ + metafields( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): MetafieldConnection! + + """ + List of prices and compare-at prices in the presentment currencies for this shop. + """ + presentmentPrices( + """ + The presentment currencies prices should return in. + """ + presentmentCurrencies: [CurrencyCode!] + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): ProductVariantPricePairConnection! + + """ + List of unit prices in the presentment currencies for this shop. + """ + presentmentUnitPrices( + """ + Specify the currencies in which to return presentment unit prices. + """ + presentmentCurrencies: [CurrencyCode!] + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): MoneyV2Connection! + + """ + The product variant’s price. + """ + price: Money! @deprecated(reason: "Use `priceV2` instead") + + """ + The product variant’s price. + """ + priceV2: MoneyV2! + + """ + The product object that the product variant belongs to. + """ + product: Product! + + """ + The total sellable quantity of the variant for online sales channels. + """ + quantityAvailable: Int + + """ + Whether a customer needs to provide a shipping address when placing an order for the product variant. + """ + requiresShipping: Boolean! + + """ + List of product options applied to the variant. + """ + selectedOptions: [SelectedOption!]! + + """ + The SKU (stock keeping unit) associated with the variant. + """ + sku: String + + """ + The product variant’s title. + """ + title: String! + + """ + The unit price value for the variant based on the variant's measurement. + """ + unitPrice: MoneyV2 + + """ + The unit price measurement for the variant. + """ + unitPriceMeasurement: UnitPriceMeasurement + + """ + The weight of the product variant in the unit system specified with `weight_unit`. + """ + weight: Float + + """ + Unit of measurement for weight. + """ + weightUnit: WeightUnit! +} + +""" +An auto-generated type for paginating through multiple ProductVariants. +""" +type ProductVariantConnection { + """ + A list of edges. + """ + edges: [ProductVariantEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one ProductVariant and a cursor during pagination. +""" +type ProductVariantEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ProductVariantEdge. + """ + node: ProductVariant! +} + +""" +The compare-at price and price of a variant sharing a currency. +""" +type ProductVariantPricePair { + """ + The compare-at price of the variant with associated currency. + """ + compareAtPrice: MoneyV2 + + """ + The price of the variant with associated currency. + """ + price: MoneyV2! +} + +""" +An auto-generated type for paginating through multiple ProductVariantPricePairs. +""" +type ProductVariantPricePairConnection { + """ + A list of edges. + """ + edges: [ProductVariantPricePairEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one ProductVariantPricePair and a cursor during pagination. +""" +type ProductVariantPricePairEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ProductVariantPricePairEdge. + """ + node: ProductVariantPricePair! +} + +""" +The set of valid sort keys for the ProductVariant query. +""" +enum ProductVariantSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `sku` value. + """ + SKU + + """ + Sort by the `position` value. + """ + POSITION + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. +""" +type QueryRoot { + """ + List of the shop's articles. + """ + articles( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ArticleSortKeys = ID + + """ + Supported filter parameters: + - `author` + - `blog_title` + - `created_at` + - `tag` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): ArticleConnection! + + """ + Find a blog by its handle. + """ + blogByHandle( + """ + The handle of the blog. + """ + handle: String! + ): Blog + + """ + List of the shop's blogs. + """ + blogs( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: BlogSortKeys = ID + + """ + Supported filter parameters: + - `created_at` + - `handle` + - `title` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): BlogConnection! + + """ + Find a collection by its handle. + """ + collectionByHandle( + """ + The handle of the collection. + """ + handle: String! + ): Collection + + """ + List of the shop’s collections. + """ + collections( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: CollectionSortKeys = ID + + """ + Supported filter parameters: + - `collection_type` + - `title` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): CollectionConnection! + + """ + Find a customer by its access token. + """ + customer( + """ + The customer access token. + """ + customerAccessToken: String! + ): Customer + node( + """ + The ID of the Node to return. + """ + id: ID! + ): Node + nodes( + """ + The IDs of the Nodes to return. + """ + ids: [ID!]! + ): [Node]! + + """ + Find a page by its handle. + """ + pageByHandle( + """ + The handle of the page. + """ + handle: String! + ): Page + + """ + List of the shop's pages. + """ + pages( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: PageSortKeys = ID + + """ + Supported filter parameters: + - `created_at` + - `handle` + - `title` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): PageConnection! + + """ + Find a product by its handle. + """ + productByHandle( + """ + The handle of the product. + """ + handle: String! + ): Product + + """ + Find recommended products related to a given `product_id`. + To learn more about how recommendations are generated, see + [*Showing product recommendations on product pages*](https://help.shopify.com/themes/development/recommended-products). + """ + productRecommendations( + """ + The id of the product. + """ + productId: ID! + ): [Product!] + + """ + Tags added to products. + Additional access scope required: unauthenticated_read_product_tags. + """ + productTags( + """ + Returns up to the first `n` elements from the list. + """ + first: Int! + ): StringConnection! + + """ + List of product types for the shop's products that are published to your app. + """ + productTypes( + """ + Returns up to the first `n` elements from the list. + """ + first: Int! + ): StringConnection! + + """ + List of the shop’s products. + """ + products( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductSortKeys = ID + + """ + Supported filter parameters: + - `available_for_sale` + - `created_at` + - `product_type` + - `tag` + - `title` + - `updated_at` + - `variants.price` + - `vendor` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): ProductConnection! + + """ + The list of public Storefront API versions, including supported, release candidate and unstable versions. + """ + publicApiVersions: [ApiVersion!]! + + """ + The shop associated with the storefront access token. + """ + shop: Shop! +} + +""" +SEO information. +""" +type SEO { + """ + The meta description. + """ + description: String + + """ + The SEO title. + """ + title: String +} + +""" +Script discount applications capture the intentions of a discount that +was created by a Shopify Script. +""" +type ScriptDiscountApplication implements DiscountApplication { + """ + The method by which the discount's value is allocated to its entitled items. + """ + allocationMethod: DiscountApplicationAllocationMethod! + + """ + The description of the application as defined by the Script. + """ + description: String! @deprecated(reason: "Use `title` instead") + + """ + Which lines of targetType that the discount is allocated over. + """ + targetSelection: DiscountApplicationTargetSelection! + + """ + The type of line that the discount is applicable towards. + """ + targetType: DiscountApplicationTargetType! + + """ + The title of the application as defined by the Script. + """ + title: String! + + """ + The value of the discount application. + """ + value: PricingValue! +} + +""" +Properties used by customers to select a product variant. +Products can have multiple options, like different sizes or colors. +""" +type SelectedOption { + """ + The product option’s name. + """ + name: String! + + """ + The product option’s value. + """ + value: String! +} + +""" +Specifies the input fields required for a selected option. +""" +input SelectedOptionInput { + """ + The product option’s name. + """ + name: String! + + """ + The product option’s value. + """ + value: String! +} + +""" +A shipping rate to be applied to a checkout. +""" +type ShippingRate { + """ + Human-readable unique identifier for this shipping rate. + """ + handle: String! + + """ + Price of this shipping rate. + """ + price: Money! @deprecated(reason: "Use `priceV2` instead") + + """ + Price of this shipping rate. + """ + priceV2: MoneyV2! + + """ + Title of this shipping rate. + """ + title: String! +} + +""" +Shop represents a collection of the general settings and information about the shop. +""" +type Shop { + """ + List of the shop' articles. + """ + articles( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ArticleSortKeys = ID + + """ + Supported filter parameters: + - `author` + - `blog_title` + - `created_at` + - `tag` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): ArticleConnection! @deprecated(reason: "Use `QueryRoot.articles` instead.") + + """ + List of the shop' blogs. + """ + blogs( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: BlogSortKeys = ID + + """ + Supported filter parameters: + - `created_at` + - `handle` + - `title` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): BlogConnection! @deprecated(reason: "Use `QueryRoot.blogs` instead.") + + """ + Find a collection by its handle. + """ + collectionByHandle( + """ + The handle of the collection. + """ + handle: String! + ): Collection + @deprecated(reason: "Use `QueryRoot.collectionByHandle` instead.") + + """ + List of the shop’s collections. + """ + collections( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: CollectionSortKeys = ID + + """ + Supported filter parameters: + - `collection_type` + - `title` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): CollectionConnection! + @deprecated(reason: "Use `QueryRoot.collections` instead.") + + """ + The three-letter code for the currency that the shop accepts. + """ + currencyCode: CurrencyCode! + @deprecated(reason: "Use `paymentSettings` instead") + + """ + A description of the shop. + """ + description: String + + """ + A string representing the way currency is formatted when the currency isn’t specified. + """ + moneyFormat: String! + + """ + The shop’s name. + """ + name: String! + + """ + Settings related to payments. + """ + paymentSettings: PaymentSettings! + + """ + The shop’s primary domain. + """ + primaryDomain: Domain! + + """ + The shop’s privacy policy. + """ + privacyPolicy: ShopPolicy + + """ + Find a product by its handle. + """ + productByHandle( + """ + The handle of the product. + """ + handle: String! + ): Product @deprecated(reason: "Use `QueryRoot.productByHandle` instead.") + + """ + A list of tags that have been added to products. + Additional access scope required: unauthenticated_read_product_tags. + """ + productTags( + """ + Returns up to the first `n` elements from the list. + """ + first: Int! + ): StringConnection! + @deprecated(reason: "Use `QueryRoot.productTags` instead.") + + """ + List of the shop’s product types. + """ + productTypes( + """ + Returns up to the first `n` elements from the list. + """ + first: Int! + ): StringConnection! + @deprecated(reason: "Use `QueryRoot.productTypes` instead.") + + """ + List of the shop’s products. + """ + products( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductSortKeys = ID + + """ + Supported filter parameters: + - `available_for_sale` + - `created_at` + - `product_type` + - `tag` + - `title` + - `updated_at` + - `variants.price` + - `vendor` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): ProductConnection! @deprecated(reason: "Use `QueryRoot.products` instead.") + + """ + The shop’s refund policy. + """ + refundPolicy: ShopPolicy + + """ + The shop’s shipping policy. + """ + shippingPolicy: ShopPolicy + + """ + Countries that the shop ships to. + """ + shipsToCountries: [CountryCode!]! + + """ + The shop’s Shopify Payments account id. + """ + shopifyPaymentsAccountId: String + @deprecated(reason: "Use `paymentSettings` instead") + + """ + The shop’s terms of service. + """ + termsOfService: ShopPolicy +} + +""" +Policy that a merchant has configured for their store, such as their refund or privacy policy. +""" +type ShopPolicy implements Node { + """ + Policy text, maximum size of 64kb. + """ + body: String! + + """ + Policy’s handle. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + Policy’s title. + """ + title: String! + + """ + Public URL to the policy. + """ + url: URL! +} + +""" +An auto-generated type for paginating through multiple Strings. +""" +type StringConnection { + """ + A list of edges. + """ + edges: [StringEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one String and a cursor during pagination. +""" +type StringEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of StringEdge. + """ + node: String! +} + +""" +Specifies the fields required to complete a checkout with +a tokenized payment. +""" +input TokenizedPaymentInput { + """ + The amount of the payment. + """ + amount: Money! + + """ + A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. + """ + idempotencyKey: String! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddressInput! + + """ + The type of payment token. + """ + type: String! + + """ + A simple string or JSON containing the required payment data for the tokenized payment. + """ + paymentData: String! + + """ + Executes the payment in test mode if possible. Defaults to `false`. + """ + test: Boolean + + """ + Public Hash Key used for AndroidPay payments only. + """ + identifier: String +} + +""" +Specifies the fields required to complete a checkout with +a tokenized payment. +""" +input TokenizedPaymentInputV2 { + """ + The amount and currency of the payment. + """ + paymentAmount: MoneyInput! + + """ + A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. + """ + idempotencyKey: String! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddressInput! + + """ + A simple string or JSON containing the required payment data for the tokenized payment. + """ + paymentData: String! + + """ + Whether to execute the payment in test mode, if possible. Test mode is not supported in production stores. Defaults to `false`. + """ + test: Boolean + + """ + Public Hash Key used for AndroidPay payments only. + """ + identifier: String + + """ + The type of payment token. + """ + type: String! +} + +""" +Specifies the fields required to complete a checkout with +a tokenized payment. +""" +input TokenizedPaymentInputV3 { + """ + The amount and currency of the payment. + """ + paymentAmount: MoneyInput! + + """ + A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. + """ + idempotencyKey: String! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddressInput! + + """ + A simple string or JSON containing the required payment data for the tokenized payment. + """ + paymentData: String! + + """ + Whether to execute the payment in test mode, if possible. Test mode is not supported in production stores. Defaults to `false`. + """ + test: Boolean + + """ + Public Hash Key used for AndroidPay payments only. + """ + identifier: String + + """ + The type of payment token. + """ + type: PaymentTokenType! +} + +""" +An object representing exchange of money for a product or service. +""" +type Transaction { + """ + The amount of money that the transaction was for. + """ + amount: Money! @deprecated(reason: "Use `amountV2` instead") + + """ + The amount of money that the transaction was for. + """ + amountV2: MoneyV2! + + """ + The kind of the transaction. + """ + kind: TransactionKind! + + """ + The status of the transaction. + """ + status: TransactionStatus! @deprecated(reason: "Use `statusV2` instead") + + """ + The status of the transaction. + """ + statusV2: TransactionStatus + + """ + Whether the transaction was done in test mode or not. + """ + test: Boolean! +} + +enum TransactionKind { + SALE + CAPTURE + AUTHORIZATION + EMV_AUTHORIZATION + CHANGE +} + +enum TransactionStatus { + PENDING + SUCCESS + FAILURE + ERROR +} + +""" +An RFC 3986 and RFC 3987 compliant URI string. + +Example value: `"https://johns-apparel.myshopify.com"`. +""" +scalar URL + +""" +The measurement used to calculate a unit price for a product variant (e.g. $9.99 / 100ml). +""" +type UnitPriceMeasurement { + """ + The type of unit of measurement for the unit price measurement. + """ + measuredType: UnitPriceMeasurementMeasuredType + + """ + The quantity unit for the unit price measurement. + """ + quantityUnit: UnitPriceMeasurementMeasuredUnit + + """ + The quantity value for the unit price measurement. + """ + quantityValue: Float! + + """ + The reference unit for the unit price measurement. + """ + referenceUnit: UnitPriceMeasurementMeasuredUnit + + """ + The reference value for the unit price measurement. + """ + referenceValue: Int! +} + +""" +The accepted types of unit of measurement. +""" +enum UnitPriceMeasurementMeasuredType { + """ + Unit of measurements representing volumes. + """ + VOLUME + + """ + Unit of measurements representing weights. + """ + WEIGHT + + """ + Unit of measurements representing lengths. + """ + LENGTH + + """ + Unit of measurements representing areas. + """ + AREA +} + +""" +The valid units of measurement for a unit price measurement. +""" +enum UnitPriceMeasurementMeasuredUnit { + """ + 1000 milliliters equals 1 liter. + """ + ML + + """ + 100 centiliters equals 1 liter. + """ + CL + + """ + Metric system unit of volume. + """ + L + + """ + 1 cubic meter equals 1000 liters. + """ + M3 + + """ + 1000 milligrams equals 1 gram. + """ + MG + + """ + Metric system unit of weight. + """ + G + + """ + 1 kilogram equals 1000 grams. + """ + KG + + """ + 1000 millimeters equals 1 meter. + """ + MM + + """ + 100 centimeters equals 1 meter. + """ + CM + + """ + Metric system unit of length. + """ + M + + """ + Metric system unit of area. + """ + M2 +} + +""" +Represents an error in the input of a mutation. +""" +type UserError implements DisplayableError { + """ + Path to the input field which caused the error. + """ + field: [String!] + + """ + The error message. + """ + message: String! +} + +""" +Represents a Shopify hosted video. +""" +type Video implements Node & Media { + """ + A word or phrase to share the nature or contents of a media. + """ + alt: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + The media content type. + """ + mediaContentType: MediaContentType! + + """ + The preview image for the media. + """ + previewImage: Image + + """ + The sources for a video. + """ + sources: [VideoSource!]! +} + +""" +Represents a source for a Shopify hosted video. +""" +type VideoSource { + """ + The format of the video source. + """ + format: String! + + """ + The height of the video. + """ + height: Int! + + """ + The video MIME type. + """ + mimeType: String! + + """ + The URL of the video. + """ + url: String! + + """ + The width of the video. + """ + width: Int! +} + +""" +Units of measurement for weight. +""" +enum WeightUnit { + """ + 1 kilogram equals 1000 grams. + """ + KILOGRAMS + + """ + Metric system unit of mass. + """ + GRAMS + + """ + 1 pound equals 16 ounces. + """ + POUNDS + + """ + Imperial system unit of mass. + """ + OUNCES +} diff --git a/framework/shopify/utils/fetch-graphql-api.ts b/framework/shopify/utils/fetch-graphql-api.ts new file mode 100644 index 000000000..a690314ed --- /dev/null +++ b/framework/shopify/utils/fetch-graphql-api.ts @@ -0,0 +1,38 @@ +import type { GraphQLFetcher } from '@commerce/api' +import { FetcherError } from '@commerce/utils/errors' +import { getConfig } from '../api/index' +import fetch from './fetch' + +const fetchGraphqlApi: GraphQLFetcher = async ( + query: string, + { variables } = {}, + fetchOptions +) => { + const { commerceUrl, apiToken } = getConfig() + + const res = await fetch(`https://${commerceUrl}/api/2021-01/graphql.json`, { + ...fetchOptions, + method: 'POST', + headers: { + 'X-Shopify-Storefront-Access-Token': apiToken, + ...fetchOptions?.headers, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query, + variables, + }), + }) + + const json = await res.json() + + if (json.errors) { + throw new FetcherError({ + errors: json.errors ?? [{ message: 'Failed to fetch Shopify API' }], + status: res.status, + }) + } + + return { data: json.data, res } +} +export default fetchGraphqlApi diff --git a/framework/shopify/utils/fetch.ts b/framework/shopify/utils/fetch.ts new file mode 100644 index 000000000..9d9fff3ed --- /dev/null +++ b/framework/shopify/utils/fetch.ts @@ -0,0 +1,3 @@ +import zeitFetch from '@vercel/fetch' + +export default zeitFetch() diff --git a/framework/shopify/utils/get-checkout-id.ts b/framework/shopify/utils/get-checkout-id.ts new file mode 100644 index 000000000..5ec9bb1ea --- /dev/null +++ b/framework/shopify/utils/get-checkout-id.ts @@ -0,0 +1,8 @@ +import Cookies from 'js-cookie' +import { SHOPIFY_CHECKOUT_COOKIE } from '..' + +const getCheckoutId = (id?: string) => { + return id ?? Cookies.get(SHOPIFY_CHECKOUT_COOKIE) +} + +export default getCheckoutId diff --git a/framework/shopify/utils/get-search-variables.ts b/framework/shopify/utils/get-search-variables.ts new file mode 100644 index 000000000..0b37f015f --- /dev/null +++ b/framework/shopify/utils/get-search-variables.ts @@ -0,0 +1,15 @@ +export const searchByProductType = (search?: string) => { + return search + ? { + query: `product_type:${search}`, + } + : {} +} + +export const searchByTag = (categoryPath?: string) => { + return categoryPath + ? { + query: `tag:${categoryPath}`, + } + : {} +} diff --git a/framework/shopify/utils/get-sort-variables.ts b/framework/shopify/utils/get-sort-variables.ts new file mode 100644 index 000000000..47650c0d7 --- /dev/null +++ b/framework/shopify/utils/get-sort-variables.ts @@ -0,0 +1,32 @@ +const getSortVariables = (sort?: string) => { + let output = {} + switch (sort) { + case 'price-asc': + output = { + sortKey: 'PRICE', + reverse: false, + } + break + case 'price-desc': + output = { + sortKey: 'PRICE', + reverse: true, + } + break + case 'trending-desc': + output = { + sortKey: 'BEST_SELLING', + reverse: false, + } + break + case 'latest-desc': + output = { + sortKey: 'CREATED_AT', + reverse: true, + } + break + } + return output +} + +export default getSortVariables diff --git a/framework/shopify/utils/mutations/checkout-create.ts b/framework/shopify/utils/mutations/checkout-create.ts new file mode 100644 index 000000000..6dfe438b3 --- /dev/null +++ b/framework/shopify/utils/mutations/checkout-create.ts @@ -0,0 +1,15 @@ +const checkoutCreateMutation = /* GraphQL */ ` + mutation { + checkoutCreate(input: {}) { + userErrors { + message + field + } + checkout { + id + webUrl + } + } + } +` +export default checkoutCreateMutation diff --git a/framework/shopify/utils/mutations/checkout-line-item-add.ts b/framework/shopify/utils/mutations/checkout-line-item-add.ts new file mode 100644 index 000000000..67b9cf250 --- /dev/null +++ b/framework/shopify/utils/mutations/checkout-line-item-add.ts @@ -0,0 +1,16 @@ +import { checkoutDetailsFragment } from '../queries/get-checkout-query' + +const checkoutLineItemAddMutation = /* GraphQL */ ` + mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemInput!]!) { + checkoutLineItemsAdd(checkoutId: $checkoutId, lineItems: $lineItems) { + userErrors { + message + field + } + checkout { + ${checkoutDetailsFragment} + } + } + } +` +export default checkoutLineItemAddMutation diff --git a/framework/shopify/utils/mutations/checkout-line-item-remove.ts b/framework/shopify/utils/mutations/checkout-line-item-remove.ts new file mode 100644 index 000000000..d967a5168 --- /dev/null +++ b/framework/shopify/utils/mutations/checkout-line-item-remove.ts @@ -0,0 +1,19 @@ +import { checkoutDetailsFragment } from '../queries/get-checkout-query' + +const checkoutLineItemRemoveMutation = /* GraphQL */ ` + mutation($checkoutId: ID!, $lineItemIds: [ID!]!) { + checkoutLineItemsRemove( + checkoutId: $checkoutId + lineItemIds: $lineItemIds + ) { + userErrors { + message + field + } + checkout { + ${checkoutDetailsFragment} + } + } + } +` +export default checkoutLineItemRemoveMutation diff --git a/framework/shopify/utils/mutations/checkout-line-item-update.ts b/framework/shopify/utils/mutations/checkout-line-item-update.ts new file mode 100644 index 000000000..8edf17587 --- /dev/null +++ b/framework/shopify/utils/mutations/checkout-line-item-update.ts @@ -0,0 +1,16 @@ +import { checkoutDetailsFragment } from '../queries/get-checkout-query' + +const checkoutLineItemUpdateMutation = /* GraphQL */ ` + mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemUpdateInput!]!) { + checkoutLineItemsUpdate(checkoutId: $checkoutId, lineItems: $lineItems) { + userErrors { + message + field + } + checkout { + ${checkoutDetailsFragment} + } + } + } +` +export default checkoutLineItemUpdateMutation diff --git a/framework/shopify/utils/mutations/index.ts b/framework/shopify/utils/mutations/index.ts new file mode 100644 index 000000000..9a7a7c9c1 --- /dev/null +++ b/framework/shopify/utils/mutations/index.ts @@ -0,0 +1,4 @@ +export { default as checkoutCreateMutation } from './checkout-create' +export { default as checkoutLineItemAddMutation } from './checkout-line-item-add' +export { default as checkoutLineItemUpdateMutation } from './checkout-create' +export { default as checkoutLineItemRemoveMutation } from './checkout-line-item-remove' diff --git a/framework/shopify/utils/queries/get-all-collections-query.ts b/framework/shopify/utils/queries/get-all-collections-query.ts new file mode 100644 index 000000000..2abf374d6 --- /dev/null +++ b/framework/shopify/utils/queries/get-all-collections-query.ts @@ -0,0 +1,14 @@ +const getSiteCollectionsQuery = /* GraphQL */ ` + query getSiteCollections($first: Int!) { + collections(first: $first) { + edges { + node { + id + title + handle + } + } + } + } +` +export default getSiteCollectionsQuery diff --git a/framework/shopify/utils/queries/get-all-pages-query.ts b/framework/shopify/utils/queries/get-all-pages-query.ts new file mode 100644 index 000000000..7d1d71459 --- /dev/null +++ b/framework/shopify/utils/queries/get-all-pages-query.ts @@ -0,0 +1,17 @@ +export const getAllPagesQuery = /* GraphQL */ ` + query($first: Int!) { + pages(first: $first) { + edges { + node { + id + title + handle + body + bodySummary + url + } + } + } + } +` +export default getAllPagesQuery diff --git a/framework/shopify/utils/queries/get-all-products-paths-query.ts b/framework/shopify/utils/queries/get-all-products-paths-query.ts new file mode 100644 index 000000000..74e9f490d --- /dev/null +++ b/framework/shopify/utils/queries/get-all-products-paths-query.ts @@ -0,0 +1,16 @@ +const getAllProductsPathsQuery = /* GraphQL */ ` + query getAllProductPaths($first: Int!) { + products(first: $first) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + handle + } + } + } + } +` +export default getAllProductsPathsQuery diff --git a/framework/shopify/utils/queries/get-all-products-query.ts b/framework/shopify/utils/queries/get-all-products-query.ts new file mode 100644 index 000000000..a833c35c7 --- /dev/null +++ b/framework/shopify/utils/queries/get-all-products-query.ts @@ -0,0 +1,47 @@ +const getAllProductsQuery = /* GraphQL */ ` + query getAllProducts( + $first: Int = 250 + $query: String = "" + $sortKey: ProductSortKeys = RELEVANCE + $reverse: Boolean = false + ) { + products( + first: $first + sortKey: $sortKey + reverse: $reverse + query: $query + ) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + id + title + vendor + handle + description + priceRange { + minVariantPrice { + amount + currencyCode + } + } + images(first: 1) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + src + } + } + } + } + } + } + } +` +export default getAllProductsQuery diff --git a/framework/shopify/utils/queries/get-checkout-query.ts b/framework/shopify/utils/queries/get-checkout-query.ts new file mode 100644 index 000000000..e4b06ab55 --- /dev/null +++ b/framework/shopify/utils/queries/get-checkout-query.ts @@ -0,0 +1,40 @@ +export const checkoutDetailsFragment = /* GraphQL */ ` + id + webUrl + subtotalPrice + totalTax + totalPrice + currencyCode + lineItems(first: 250) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + id + title + variant { + id + title + image { + src + } + price + } + quantity + } + } + } +` + +const getCheckoutQuery = /* GraphQL */ ` + query($checkoutId: ID!) { + node(id: $checkoutId) { + ... on Checkout { + ${checkoutDetailsFragment} + } + } + } +` +export default getCheckoutQuery diff --git a/framework/shopify/utils/queries/get-product-query.ts b/framework/shopify/utils/queries/get-product-query.ts new file mode 100644 index 000000000..3ed5d0e64 --- /dev/null +++ b/framework/shopify/utils/queries/get-product-query.ts @@ -0,0 +1,59 @@ +const getProductQuery = /* GraphQL */ ` + query getProductBySlug($slug: String!) { + productByHandle(handle: $slug) { + id + handle + title + productType + vendor + description + descriptionHtml + options { + id + name + values + } + priceRange { + maxVariantPrice { + amount + currencyCode + } + minVariantPrice { + amount + currencyCode + } + } + variants(first: 250) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + id + title + selectedOptions { + name + value + } + price + compareAtPrice + } + } + } + images(first: 250) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + src + } + } + } + } + } +` + +export default getProductQuery diff --git a/framework/shopify/utils/queries/index.ts b/framework/shopify/utils/queries/index.ts new file mode 100644 index 000000000..a1282f1e1 --- /dev/null +++ b/framework/shopify/utils/queries/index.ts @@ -0,0 +1,6 @@ +export { default as getSiteCollectionsQuery } from './get-all-collections-query' +export { default as getProductQuery } from './get-all-products-paths-query' +export { default as getAllProductsQuery } from './get-all-products-query' +export { default as getAllProductsPathtsQuery } from './get-all-products-paths-query' +export { default as getCheckoutQuery } from './get-checkout-query' +export { default as getAllPagesQuery } from './get-all-pages-query' diff --git a/framework/shopify/utils/to-commerce-products.ts b/framework/shopify/utils/to-commerce-products.ts new file mode 100644 index 000000000..cafb657d2 --- /dev/null +++ b/framework/shopify/utils/to-commerce-products.ts @@ -0,0 +1,96 @@ +import { + Product as ShopifyProduct, + ImageEdge, + SelectedOption, + ProductEdge, + ProductVariantEdge, + MoneyV2, + ProductOption, +} from '../schema' + +const money = ({ amount, currencyCode }: MoneyV2) => { + return { + value: +amount, + currencyCode, + } +} + +const tranformProductOption = ({ + id, + name: displayName, + values, +}: ProductOption) => ({ + __typename: 'MultipleChoiceOption', + displayName, + values: values.map((value) => ({ + label: value, + })), +}) + +const transformImages = (images: ImageEdge[]) => + images.map(({ node: { src: url } }) => ({ + url, + })) + +export const toCommerceProduct = (product: ShopifyProduct) => { + const { + id, + title: name, + vendor, + images: { edges: images }, + variants: { edges: variants }, + description, + handle: slug, + priceRange, + options, + } = product + + return { + id, + name, + slug, + vendor, + description, + path: `/${slug}`, + price: money(priceRange.minVariantPrice), + images: transformImages(images), + variants: variants.map( + ({ node: { id, selectedOptions } }: ProductVariantEdge) => { + return { + id, + options: selectedOptions.map(({ name, value }: SelectedOption) => + tranformProductOption({ + id, + name, + values: [value], + } as ProductOption) + ), + } + } + ), + options: options.map((option: ProductOption) => + tranformProductOption(option) + ), + } +} + +export default function toCommerceProducts(products: ProductEdge[]) { + return products.map( + ({ + node: { + id, + title: name, + images: { edges: images }, + handle: slug, + priceRange, + }, + }: ProductEdge) => ({ + id, + name, + images: transformImages(images), + price: money(priceRange.minVariantPrice), + slug, + path: `/${slug}`, + }) + ) +} diff --git a/framework/shopify/wishlist/use-add-item.tsx b/framework/shopify/wishlist/use-add-item.tsx new file mode 100644 index 000000000..75f067c3a --- /dev/null +++ b/framework/shopify/wishlist/use-add-item.tsx @@ -0,0 +1,13 @@ +import { useCallback } from 'react' + +export function emptyHook() { + const useEmptyHook = async (options = {}) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/shopify/wishlist/use-remove-item.tsx b/framework/shopify/wishlist/use-remove-item.tsx new file mode 100644 index 000000000..a2d3a8a05 --- /dev/null +++ b/framework/shopify/wishlist/use-remove-item.tsx @@ -0,0 +1,17 @@ +import { useCallback } from 'react' + +type Options = { + includeProducts?: boolean +} + +export function emptyHook(options?: Options) { + const useEmptyHook = async ({ id }: { id: string | number }) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/shopify/wishlist/use-wishlist.tsx b/framework/shopify/wishlist/use-wishlist.tsx new file mode 100644 index 000000000..f8db4216f --- /dev/null +++ b/framework/shopify/wishlist/use-wishlist.tsx @@ -0,0 +1,45 @@ +import { HookFetcher } from '@commerce/utils/types' +import { SwrOptions } from '@commerce/utils/use-data' +import useCommerceWishlist from '@commerce/wishlist/use-wishlist' +import { Product } from '../schema' +import useCustomer from '../customer/use-customer' + +const defaultOpts = {} + +export type Wishlist = { + items: [ + { + product_id: number + variant_id: number + id: number + product: Product + } + ] +} + +export interface UseWishlistOptions { + includeProducts?: boolean +} + +export interface UseWishlistInput extends UseWishlistOptions { + customerId?: number +} + +export const fetcher: HookFetcher<Wishlist | null, UseWishlistInput> = () => { + return null +} + +export function extendHook( + customFetcher: typeof fetcher, + swrOptions?: SwrOptions<Wishlist | null, UseWishlistInput> +) { + const useWishlist = ({ includeProducts }: UseWishlistOptions = {}) => { + return { data: null } + } + + useWishlist.extend = extendHook + + return useWishlist +} + +export default extendHook(fetcher) diff --git a/next.config.js b/next.config.js index e732ef78a..3c9e37210 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,6 @@ module.exports = { images: { - domains: ['cdn11.bigcommerce.com'], + domains: ['cdn11.bigcommerce.com', 'cdn.shopify.com'], }, i18n: { locales: ['en-US', 'es'], diff --git a/pages/search.tsx b/pages/search.tsx index 97bee34d4..64fea5600 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -71,6 +71,7 @@ export default function Search({ const { data } = useSearch({ search: typeof q === 'string' ? q : '', categoryId: activeCategory?.entityId, + categoryPath: activeCategory?.path, brandId: activeBrand?.entityId, sort: typeof sort === 'string' ? sort : '', }) diff --git a/tsconfig.json b/tsconfig.json index 67de1ee36..4b4389c44 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,8 +22,8 @@ "@utils/*": ["utils/*"], "@commerce/*": ["framework/commerce/*"], "@commerce": ["framework/commerce"], - "@framework/*": ["framework/bigcommerce/*"], - "@framework": ["framework/bigcommerce"] + "@framework/*": ["framework/shopify/*"], + "@framework": ["framework/shopify"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], From f63e2c67a0e69aabd3f06d9b2ff06406f0949d08 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 4 Feb 2021 13:27:58 +0200 Subject: [PATCH 067/221] Update README.md --- framework/shopify/README.md | 225 +----------------------------------- 1 file changed, 2 insertions(+), 223 deletions(-) diff --git a/framework/shopify/README.md b/framework/shopify/README.md index fc6a70ce3..066a02e52 100644 --- a/framework/shopify/README.md +++ b/framework/shopify/README.md @@ -24,237 +24,16 @@ Collection of hooks and data fetching functions to integrate Shopify in a React ## Getting Started -1. Install dependencies: +1. Environment variables need to be set: ``` -yarn install shopify-buy -yarn install -D @types/shopify-buy -``` - -3. Environment variables need to be set: - -``` -SHOPIFY_STORE_DOMAIN= -SHOPIFY_STOREFRONT_ACCESS_TOKEN= NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN= NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN= ``` -4. Point the framework to `shopify` by updating `tsconfig.json`: +2. Point the framework to `shopify` by updating `tsconfig.json`: ``` "@framework/*": ["framework/shopify/*"], "@framework": ["framework/shopify"] ``` - -### Modifications - -These modifications are temporarily until contributions are made to remove them. - -#### Adding item to Cart - -```js -// components/product/ProductView/ProductView.tsx -const ProductView: FC<Props> = ({ product }) => { - const addToCart = async () => { - setLoading(true) - try { - await addItem({ - productId: product.id, - variantId: variant ? variant.id : product.variants[0].id, - }) - openSidebar() - setLoading(false) - } catch (err) { - setLoading(false) - } - } -} -``` - -#### Proceed to Checkout - -```js -// components/cart/CartSidebarView/CartSidebarView.tsx -import { useCommerce } from '@framework' - -const CartSidebarView: FC = () => { - const { checkout } = useCommerce() - return ( - <Button href={checkout.webUrl} Component="a" width="100%"> - Proceed to Checkout - </Button> - ) -} -``` - -## General Usage - -### CommerceProvider - -Provider component that creates the commerce context for children. - -```js -import { CommerceProvider } from '@framework' - -const App = ({ children }) => { - return <CommerceProvider locale={locale}>{children}</CommerceProvider> -} - -export default App -``` - -### useCommerce - -Returns the configs that are defined in the nearest `CommerceProvider`. Also provides access to Shopify's `checkout` and `shop`. - -```js -import { useCommerce } from 'nextjs-commerce-shopify' - -const { checkout, shop } = useCommerce() -``` - -- `checkout`: The information required to checkout items and pay ([Documentation](https://shopify.dev/docs/storefront-api/reference/checkouts/checkout)). -- `shop`: Represents a collection of the general settings and information about the shop ([Documentation](https://shopify.dev/docs/storefront-api/reference/online-store/shop/index)). - -## Hooks - -### usePrice - -Display the product variant price according to currency and locale. - -```js -import usePrice from '@framework/product/use-price' - -const { price } = usePrice({ - amount, -}) -``` - -Takes in either `amount` or `variant`: - -- `amount`: A price value for a particular item if the amount is known. -- `variant`: A shopify product variant. Price will be extracted from the variant. - -### useAddItem - -```js -import { useAddItem } from '@framework/cart' - -const AddToCartButton = ({ variantId, quantity }) => { - const addItem = useAddItem() - - const addToCart = async () => { - await addItem({ - variantId, - }) - } - - return <button onClick={addToCart}>Add To Cart</button> -} -``` - -### useRemoveItem - -```js -import { useRemoveItem } from '@framework/cart' - -const RemoveButton = ({ item }) => { - const removeItem = useRemoveItem() - - const handleRemove = async () => { - await removeItem({ id: item.id }) - } - - return <button onClick={handleRemove}>Remove</button> -} -``` - -### useUpdateItem - -```js -import { useUpdateItem } from '@framework/cart' - -const CartItem = ({ item }) => { - const [quantity, setQuantity] = useState(item.quantity) - const updateItem = useUpdateItem(item) - - const updateQuantity = async (e) => { - const val = e.target.value - await updateItem({ quantity: val }) - } - - return ( - <input - type="number" - max={99} - min={0} - value={quantity} - onChange={updateQuantity} - /> - ) -} -``` - -## APIs - -Collections of APIs to fetch data from a Shopify store. - -The data is fetched using the [Shopify JavaScript Buy SDK](https://github.com/Shopify/js-buy-sdk#readme). Read the [Shopify Storefront API reference](https://shopify.dev/docs/storefront-api/reference) for more information. - -### getProduct - -Get a single product by its `handle`. - -```js -import getProduct from '@framework/product/get-product' -import { getConfig } from '@framework/api' - -const config = getConfig() - -const product = await getProduct({ - variables: { slug }, - config, -}) -``` - -### getAllProducts - -```js -import getAllProducts from '@framework/product/get-all-products' -import { getConfig } from '@framework/api' - -const config = getConfig() - -const { products } = await getAllProducts({ - variables: { first: 12 }, - config, -}) -``` - -### getAllCollections - -```js -import getAllCollections from '@framework/product/get-all-collections' -import { getConfig } from '@framework/api' - -const config = getConfig() - -const collections = await getAllCollections({ - config, -}) -``` - -### getAllPages - -```js -import getAllPages from '@framework/common/get-all-pages' -import { getConfig } from '@framework/api' - -const config = getConfig() - -const pages = await getAllPages({ - variables: { first: 12 }, - config, -}) -``` From 3c985278c68502dbdad2ed688023681b2566282c Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 4 Feb 2021 13:28:41 +0200 Subject: [PATCH 068/221] Update README.md --- framework/shopify/README.md | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/framework/shopify/README.md b/framework/shopify/README.md index 066a02e52..1533ceccd 100644 --- a/framework/shopify/README.md +++ b/framework/shopify/README.md @@ -1,27 +1,3 @@ -## Table of Contents - -- [Getting Started](#getting-started) - - [Modifications](#modifications) - - [Adding item to Cart](#adding-item-to-cart) - - [Proceed to Checkout](#proceed-to-checkout) -- [General Usage](#general-usage) - - [CommerceProvider](#commerceprovider) - - [useCommerce](#usecommerce) -- [Hooks](#hooks) - - [usePrice](#useprice) - - [useAddItem](#useadditem) - - [useRemoveItem](#useremoveitem) - - [useUpdateItem](#useupdateitem) -- [APIs](#apis) - - [getProduct](#getproduct) - - [getAllProducts](#getallproducts) - - [getAllCollections](#getallcollections) - - [getAllPages](#getallpages) - -# Shopify Storefront Data Hooks - -Collection of hooks and data fetching functions to integrate Shopify in a React application. Designed to work with [Next.js Commerce](https://demo.vercel.store/). - ## Getting Started 1. Environment variables need to be set: From 2dffcb7bfb88baa28d00f382449017cf92bb3466 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 4 Feb 2021 15:34:22 +0200 Subject: [PATCH 069/221] normalizations & missing files --- framework/shopify/api/cart/index.ts | 1 + framework/shopify/api/catalog/index.ts | 1 + framework/shopify/api/catalog/products.ts | 1 + framework/shopify/api/checkout/index.ts | 103 +++++++++++++++ framework/shopify/api/customer.ts | 1 + .../shopify/api/customers/handlers/login.ts | 38 ------ framework/shopify/cart/use-add-item.tsx | 37 +++--- framework/shopify/customer/index.ts | 1 + framework/shopify/lib/normalize.ts | 124 ++++++++++++++++++ framework/shopify/product/get-all-products.ts | 9 +- framework/shopify/product/get-product.ts | 12 +- framework/shopify/product/use-search.tsx | 13 +- framework/shopify/types.ts | 46 +++++++ .../utils/queries/get-all-products-query.ts | 5 +- .../utils/queries/get-checkout-query.ts | 1 + .../utils/queries/get-product-query.ts | 5 +- .../shopify/utils/to-commerce-products.ts | 96 -------------- next.config.js | 3 + 18 files changed, 323 insertions(+), 174 deletions(-) create mode 100644 framework/shopify/api/cart/index.ts create mode 100644 framework/shopify/api/catalog/index.ts create mode 100644 framework/shopify/api/catalog/products.ts create mode 100644 framework/shopify/api/checkout/index.ts create mode 100644 framework/shopify/api/customer.ts delete mode 100644 framework/shopify/api/customers/handlers/login.ts create mode 100644 framework/shopify/customer/index.ts create mode 100644 framework/shopify/lib/normalize.ts create mode 100644 framework/shopify/types.ts delete mode 100644 framework/shopify/utils/to-commerce-products.ts diff --git a/framework/shopify/api/cart/index.ts b/framework/shopify/api/cart/index.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/cart/index.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/catalog/index.ts b/framework/shopify/api/catalog/index.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/catalog/index.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/catalog/products.ts b/framework/shopify/api/catalog/products.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/catalog/products.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/checkout/index.ts b/framework/shopify/api/checkout/index.ts new file mode 100644 index 000000000..26b8299a9 --- /dev/null +++ b/framework/shopify/api/checkout/index.ts @@ -0,0 +1,103 @@ +import { CommerceAPIFetchOptions } from '@commerce/api' +import { + CheckoutCreateInput, + CheckoutCreatePayload, + Maybe, +} from '@framework/schema' +import { getProductQuery } from 'framework/bigcommerce/product/get-product' +import Cookies from 'js-cookie' +import { getConfig, ShopifyConfig } from '..' + +const createCheckoutMutation = ` +mutation($input) { + checkoutCreate(input: $input) { + checkout { + id + webUrl + lineItems(first: 100) { + edges { + node { + title + quantity + } + } + } + } + } +} +` + +const getCheckoutQuery = ` +query { + shop { + name + currencyCode + checkout { + id + webUrl + lineItems(first: 100) { + edges { + node { + title + quantity + } + } + } + } + } +} +` + +const createCheckout = async (fetcher: any, input: CheckoutCreateInput) => { + return await fetcher(createCheckoutMutation, { + variables: { + input, + }, + }) +} + +const getCheckout = async (req: any, res: any, config: any): Promise<any> => { + console.log(config) + + return + config = getConfig(config) + + const { data: shop } = await config.fetch(getProductQuery) + + const checkout = shop?.checkout + const completedAt = checkout.completedAt + + const checkoutId = Cookies.get('nextjs-commerce-shopify-token') + + const checkoutCreateInput = { + presentmentCurrencyCode: shop?.currencyCode, + } + + // we could have a cart id stored in session storage + // user could be refreshing or navigating back and forthlet checkoutResource + let checkoutCreatePayload: Maybe<CheckoutCreatePayload> + + if (checkoutId) { + checkoutCreatePayload = await createCheckout( + config.fetch, + checkoutCreateInput + ) + const existingCheckout = checkoutCreatePayload?.checkout + const completedAt = existingCheckout?.completedAt + if (completedAt) { + checkoutCreatePayload = await createCheckout( + config.fetch, + checkoutCreateInput + ) + } + } else { + checkoutCreatePayload = await createCheckout( + config.fetch, + checkoutCreateInput + ) + } + + console.log(checkout) +} + +export default getCheckout diff --git a/framework/shopify/api/customer.ts b/framework/shopify/api/customer.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/customer.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/customers/handlers/login.ts b/framework/shopify/api/customers/handlers/login.ts deleted file mode 100644 index 22231e06e..000000000 --- a/framework/shopify/api/customers/handlers/login.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { FetcherError } from '@commerce/utils/errors' -import type { LoginHandlers } from '../login' - -const loginHandler: LoginHandlers['login'] = async ({ - res, - body: { email, password }, - config, -}) => { - if (!(email && password)) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) - } - - try { - } catch (error) { - // Check if the email and password didn't match an existing account - if (error instanceof FetcherError) { - return res.status(401).json({ - data: null, - errors: [ - { - message: - 'Cannot find an account that matches the provided credentials', - code: 'invalid_credentials', - }, - ], - }) - } - - throw error - } - - res.status(200).json({ data: null }) -} - -export default loginHandler diff --git a/framework/shopify/cart/use-add-item.tsx b/framework/shopify/cart/use-add-item.tsx index cbca0a572..b0e9444bf 100644 --- a/framework/shopify/cart/use-add-item.tsx +++ b/framework/shopify/cart/use-add-item.tsx @@ -9,32 +9,25 @@ import type { Cart } from '@commerce/types' import checkoutLineItemAddMutation from '../utils/mutations/checkout-line-item-add' import getCheckoutId from '@framework/utils/get-checkout-id' import { checkoutToCart } from './utils' +import { AddCartItemBody, CartItemBody } from '@framework/types' +import { AddItemBody } from '../types' +import { MutationCheckoutLineItemsAddArgs } from '@framework/schema' const defaultOpts = { query: checkoutLineItemAddMutation, } -export type AddItemInput = UseAddItemInput<any> +export type AddItemInput = UseAddItemInput<CartItemBody> -export const fetcher: HookFetcher<Cart, any> = async ( - options, - { checkoutId, item }, - fetch -) => { - if ( - item.quantity && - (!Number.isInteger(item.quantity) || item.quantity! < 1) - ) { - throw new CommerceError({ - message: 'The item quantity has to be a valid integer greater than 0', - }) - } - - const data = await fetch<any, any>({ +export const fetcher: HookFetcher< + Cart, + MutationCheckoutLineItemsAddArgs +> = async (options, { checkoutId, lineItems }, fetch) => { + const data = await fetch<any, AddCartItemBody>({ ...options, variables: { checkoutId, - lineItems: [item], + lineItems, }, }) @@ -49,10 +42,12 @@ export function extendHook(customFetcher: typeof fetcher) { return useCallback( async function addItem(input: AddItemInput) { const data = await fn({ - item: { - variantId: input.variantId, - quantity: input.quantity ?? 1, - }, + lineItems: [ + { + variantId: input.variantId, + quantity: input.quantity ?? 1, + }, + ], checkoutId: getCheckoutId(cart?.id), }) await mutate(data, false) diff --git a/framework/shopify/customer/index.ts b/framework/shopify/customer/index.ts new file mode 100644 index 000000000..6c903ecc5 --- /dev/null +++ b/framework/shopify/customer/index.ts @@ -0,0 +1 @@ +export { default as useCustomer } from './use-customer' diff --git a/framework/shopify/lib/normalize.ts b/framework/shopify/lib/normalize.ts new file mode 100644 index 000000000..027694f20 --- /dev/null +++ b/framework/shopify/lib/normalize.ts @@ -0,0 +1,124 @@ +import { + Product as ShopifyProduct, + Checkout, + CheckoutLineItemEdge, + SelectedOption, + ImageConnection, + ProductVariantConnection, + ProductOption, + MoneyV2, +} from '@framework/schema' + +import type { Cart, LineItem } from '../types' + +const money = ({ amount, currencyCode }: MoneyV2) => { + return { + value: +amount, + currencyCode, + } +} + +const normalizeProductOption = ({ + name: displayName, + values, + ...rest +}: ProductOption) => ({ + __typename: 'MultipleChoiceOption', + displayName, + values: values.map((value) => ({ + label: value, + })), + ...rest, +}) + +const normalizeProductImages = ({ edges }: ImageConnection) => + edges?.map(({ node: { originalSrc: url, ...rest } }) => ({ + url, + ...rest, + })) + +const normalizeProductVariants = ({ edges }: ProductVariantConnection) => + edges?.map(({ node: { id, selectedOptions } }) => ({ + id, + options: selectedOptions.map(({ name, value }: SelectedOption) => + normalizeProductOption({ + id, + name, + values: [value], + }) + ), + })) + +export function normalizeProduct(productNode: ShopifyProduct): any { + const { + id, + title: name, + vendor, + images, + variants, + description, + handle, + priceRange, + options, + ...rest + } = productNode + + return { + id: { $set: String(id) }, + name, + vendor, + description, + path: `/${handle}`, + slug: handle?.replace(/^\/+|\/+$/g, ''), + price: money(priceRange?.minVariantPrice), + images: normalizeProductImages(images), + variants: variants ? normalizeProductVariants(variants) : null, + options: options ? options.map((o) => normalizeProductOption) : [], + ...rest, + } +} + +export function normalizeCart(data: Checkout): Cart { + return { + id: data.id, + customerId: '', + email: '', + createdAt: data.createdAt, + currency: { + code: data.currencyCode, + }, + taxesIncluded: data.taxesIncluded, + lineItems: data.lineItems?.edges.map(normalizeLineItem), + lineItemsSubtotalPrice: data.subtotalPrice, + subtotalPrice: data.subtotalPrice, + totalPrice: data.totalPrice, + discounts: data.discountApplications?.edges.map(({ value }: any) => ({ + value, + })), + } +} + +function normalizeLineItem({ node: item }: CheckoutLineItemEdge): LineItem { + return { + id: item.id, + variantId: String(item.variant?.id), + productId: String(item.variant?.id), + name: item.title, + quantity: item.quantity, + variant: { + id: String(item.variant?.id), + sku: item.variant?.sku ?? '', + name: item.title, + image: { + url: item.variant?.image?.originalSrc, + }, + requiresShipping: item.variant?.requiresShipping ?? false, + price: item.variant?.price, + listPrice: item.variant?.compareAtPrice, + }, + path: '', + discounts: item.discountAllocations.map(({ value }: any) => ({ + value, + })), + } +} diff --git a/framework/shopify/product/get-all-products.ts b/framework/shopify/product/get-all-products.ts index 01d086d84..a7cc3043c 100644 --- a/framework/shopify/product/get-all-products.ts +++ b/framework/shopify/product/get-all-products.ts @@ -1,8 +1,8 @@ import { GraphQLFetcherResult } from '@commerce/api' -import toCommerceProducts from '../utils/to-commerce-products' import { getConfig, ShopifyConfig } from '../api' -import { Product } from '../schema' +import { Product, ProductEdge } from '../schema' import { getAllProductsQuery } from '../utils/queries' +import { normalizeProduct } from '@framework/lib/normalize' export type ProductNode = Product @@ -28,8 +28,9 @@ const getAllProducts = async (options: { { variables } ) - const shopifyProducts = data.products?.edges - const products = toCommerceProducts(shopifyProducts) + const products = data?.products?.edges?.map(({ node: p }: ProductEdge) => + normalizeProduct(p) + ) return { products, diff --git a/framework/shopify/product/get-product.ts b/framework/shopify/product/get-product.ts index 0cfc8a44a..ba4856520 100644 --- a/framework/shopify/product/get-product.ts +++ b/framework/shopify/product/get-product.ts @@ -2,8 +2,8 @@ import { GraphQLFetcherResult } from '@commerce/api' import { getConfig, ShopifyConfig } from '../api' import { Product } from '../schema' -import { toCommerceProduct } from '../utils/to-commerce-products' import getProductQuery from '../utils/queries/get-product-query' +import { normalizeProduct } from '@framework/lib/normalize' export type ProductNode = Product @@ -25,12 +25,14 @@ const getProduct = async (options: Options): Promise<ReturnType> => { let { config, variables = { first: 250 } } = options ?? {} config = getConfig(config) - const { - data: { productByHandle: product }, - }: GraphQLFetcherResult = await config.fetch(getProductQuery, { variables }) + const { data }: GraphQLFetcherResult = await config.fetch(getProductQuery, { + variables, + }) + + const product = data?.productByHandle?.product return { - product: product ? toCommerceProduct(product) : null, + product: product ? normalizeProduct(product) : null, } } diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx index 421323bf9..ee59c9448 100644 --- a/framework/shopify/product/use-search.tsx +++ b/framework/shopify/product/use-search.tsx @@ -1,6 +1,4 @@ import useCommerceSearch from '@commerce/products/use-search' - -import toCommerceProducts from '@framework/utils/to-commerce-products' import getAllProductsQuery from '@framework/utils/queries/get-all-products-query' import type { Product } from 'framework/bigcommerce/schema' @@ -14,10 +12,7 @@ import { } from '@framework/utils/get-search-variables' import sortBy from '@framework/utils/get-sort-variables' - -export type CommerceProductEdge = { - node: Product -} +import { normalizeProduct } from '@framework/lib/normalize' export type SearchProductsInput = { search?: string @@ -49,8 +44,10 @@ export const fetcher: HookFetcher< }).then( ({ products }): SearchProductsData => { return { - products: toCommerceProducts(products.edges), - found: !!products.edges.length, + products: products?.edges?.map(({ node: p }: ProductEdge) => + normalizeProduct(p) + ), + found: !!products?.edges?.length, } } ) diff --git a/framework/shopify/types.ts b/framework/shopify/types.ts new file mode 100644 index 000000000..5d285d211 --- /dev/null +++ b/framework/shopify/types.ts @@ -0,0 +1,46 @@ +import * as Core from '@commerce/types' +import { CheckoutLineItem } from './schema' + +export type ShopifyCheckout = { + id: string + webUrl: string + lineItems: CheckoutLineItem[] +} + +export interface Cart extends Core.Cart { + lineItems: LineItem[] +} + +export interface LineItem extends Core.LineItem {} + +/** + * Cart mutations + */ + +export type OptionSelections = { + option_id: number + option_value: number | string +} + +export interface CartItemBody extends Core.CartItemBody { + productId: string // The product id is always required for BC + optionSelections?: OptionSelections +} + +export interface GetCartHandlerBody extends Core.GetCartHandlerBody {} + +export interface AddCartItemBody extends Core.AddCartItemBody<CartItemBody> {} + +export interface AddCartItemHandlerBody + extends Core.AddCartItemHandlerBody<CartItemBody> {} + +export interface UpdateCartItemBody + extends Core.UpdateCartItemBody<CartItemBody> {} + +export interface UpdateCartItemHandlerBody + extends Core.UpdateCartItemHandlerBody<CartItemBody> {} + +export interface RemoveCartItemBody extends Core.RemoveCartItemBody {} + +export interface RemoveCartItemHandlerBody + extends Core.RemoveCartItemHandlerBody {} diff --git a/framework/shopify/utils/queries/get-all-products-query.ts b/framework/shopify/utils/queries/get-all-products-query.ts index a833c35c7..de84d60bf 100644 --- a/framework/shopify/utils/queries/get-all-products-query.ts +++ b/framework/shopify/utils/queries/get-all-products-query.ts @@ -35,7 +35,10 @@ const getAllProductsQuery = /* GraphQL */ ` } edges { node { - src + originalSrc + altText + width + height } } } diff --git a/framework/shopify/utils/queries/get-checkout-query.ts b/framework/shopify/utils/queries/get-checkout-query.ts index e4b06ab55..3b57eb83e 100644 --- a/framework/shopify/utils/queries/get-checkout-query.ts +++ b/framework/shopify/utils/queries/get-checkout-query.ts @@ -16,6 +16,7 @@ export const checkoutDetailsFragment = /* GraphQL */ ` title variant { id + sku title image { src diff --git a/framework/shopify/utils/queries/get-product-query.ts b/framework/shopify/utils/queries/get-product-query.ts index 3ed5d0e64..d61824ed5 100644 --- a/framework/shopify/utils/queries/get-product-query.ts +++ b/framework/shopify/utils/queries/get-product-query.ts @@ -48,7 +48,10 @@ const getProductQuery = /* GraphQL */ ` } edges { node { - src + originalSrc + altText + width + height } } } diff --git a/framework/shopify/utils/to-commerce-products.ts b/framework/shopify/utils/to-commerce-products.ts deleted file mode 100644 index cafb657d2..000000000 --- a/framework/shopify/utils/to-commerce-products.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { - Product as ShopifyProduct, - ImageEdge, - SelectedOption, - ProductEdge, - ProductVariantEdge, - MoneyV2, - ProductOption, -} from '../schema' - -const money = ({ amount, currencyCode }: MoneyV2) => { - return { - value: +amount, - currencyCode, - } -} - -const tranformProductOption = ({ - id, - name: displayName, - values, -}: ProductOption) => ({ - __typename: 'MultipleChoiceOption', - displayName, - values: values.map((value) => ({ - label: value, - })), -}) - -const transformImages = (images: ImageEdge[]) => - images.map(({ node: { src: url } }) => ({ - url, - })) - -export const toCommerceProduct = (product: ShopifyProduct) => { - const { - id, - title: name, - vendor, - images: { edges: images }, - variants: { edges: variants }, - description, - handle: slug, - priceRange, - options, - } = product - - return { - id, - name, - slug, - vendor, - description, - path: `/${slug}`, - price: money(priceRange.minVariantPrice), - images: transformImages(images), - variants: variants.map( - ({ node: { id, selectedOptions } }: ProductVariantEdge) => { - return { - id, - options: selectedOptions.map(({ name, value }: SelectedOption) => - tranformProductOption({ - id, - name, - values: [value], - } as ProductOption) - ), - } - } - ), - options: options.map((option: ProductOption) => - tranformProductOption(option) - ), - } -} - -export default function toCommerceProducts(products: ProductEdge[]) { - return products.map( - ({ - node: { - id, - title: name, - images: { edges: images }, - handle: slug, - priceRange, - }, - }: ProductEdge) => ({ - id, - name, - images: transformImages(images), - price: money(priceRange.minVariantPrice), - slug, - path: `/${slug}`, - }) - ) -} diff --git a/next.config.js b/next.config.js index 3c9e37210..abdfe251c 100644 --- a/next.config.js +++ b/next.config.js @@ -34,4 +34,7 @@ module.exports = { }, ] }, + typescript: { + ignoreBuildErrors: true, + }, } From 67ed55d11401eeb60cf302b20e55df24b0277f58 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 4 Feb 2021 15:35:53 +0200 Subject: [PATCH 070/221] Update index.ts --- framework/shopify/api/checkout/index.ts | 100 +----------------------- 1 file changed, 1 insertion(+), 99 deletions(-) diff --git a/framework/shopify/api/checkout/index.ts b/framework/shopify/api/checkout/index.ts index 26b8299a9..0b35324d8 100644 --- a/framework/shopify/api/checkout/index.ts +++ b/framework/shopify/api/checkout/index.ts @@ -1,103 +1,5 @@ -import { CommerceAPIFetchOptions } from '@commerce/api' -import { - CheckoutCreateInput, - CheckoutCreatePayload, - Maybe, -} from '@framework/schema' -import { getProductQuery } from 'framework/bigcommerce/product/get-product' -import Cookies from 'js-cookie' -import { getConfig, ShopifyConfig } from '..' - -const createCheckoutMutation = ` -mutation($input) { - checkoutCreate(input: $input) { - checkout { - id - webUrl - lineItems(first: 100) { - edges { - node { - title - quantity - } - } - } - } - } -} -` - -const getCheckoutQuery = ` -query { - shop { - name - currencyCode - checkout { - id - webUrl - lineItems(first: 100) { - edges { - node { - title - quantity - } - } - } - } - } -} -` - -const createCheckout = async (fetcher: any, input: CheckoutCreateInput) => { - return await fetcher(createCheckoutMutation, { - variables: { - input, - }, - }) -} - const getCheckout = async (req: any, res: any, config: any): Promise<any> => { - console.log(config) - - return - config = getConfig(config) - - const { data: shop } = await config.fetch(getProductQuery) - - const checkout = shop?.checkout - const completedAt = checkout.completedAt - - const checkoutId = Cookies.get('nextjs-commerce-shopify-token') - - const checkoutCreateInput = { - presentmentCurrencyCode: shop?.currencyCode, - } - - // we could have a cart id stored in session storage - // user could be refreshing or navigating back and forthlet checkoutResource - let checkoutCreatePayload: Maybe<CheckoutCreatePayload> - - if (checkoutId) { - checkoutCreatePayload = await createCheckout( - config.fetch, - checkoutCreateInput - ) - const existingCheckout = checkoutCreatePayload?.checkout - const completedAt = existingCheckout?.completedAt - if (completedAt) { - checkoutCreatePayload = await createCheckout( - config.fetch, - checkoutCreateInput - ) - } - } else { - checkoutCreatePayload = await createCheckout( - config.fetch, - checkoutCreateInput - ) - } - - console.log(checkout) + res.end() } export default getCheckout From 32184ecdbdb51682583dd9d3dd7bf7373f7f2f54 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 4 Feb 2021 16:03:52 +0200 Subject: [PATCH 071/221] fixes --- framework/shopify/auth/use-login.tsx | 54 +++---------------- framework/shopify/cart/use-add-item.tsx | 1 - .../shopify/cart/utils/checkout-to-cart.ts | 40 +++----------- framework/shopify/lib/normalize.ts | 32 +++++------ framework/shopify/product/get-product.ts | 16 +++--- framework/shopify/utils/get-checkout-id.ts | 4 +- 6 files changed, 42 insertions(+), 105 deletions(-) diff --git a/framework/shopify/auth/use-login.tsx b/framework/shopify/auth/use-login.tsx index 384599fe9..75f067c3a 100644 --- a/framework/shopify/auth/use-login.tsx +++ b/framework/shopify/auth/use-login.tsx @@ -1,53 +1,13 @@ import { useCallback } from 'react' -import type { HookFetcher } from '@commerce/utils/types' -import { CommerceError } from '@commerce/utils/errors' -import useCommerceLogin from '@commerce/use-login' -import type { LoginBody } from '../api/customers/login' -import useCustomer from '../customer/use-customer' -const defaultOpts = { - query: '/api/bigcommerce/customers/login', -} - -export type LoginInput = LoginBody - -export const fetcher: HookFetcher<null, LoginBody> = ( - options, - { email, password }, - fetch -) => { - if (!(email && password)) { - throw new CommerceError({ - message: - 'A first name, last name, email and password are required to login', - }) +export function emptyHook() { + const useEmptyHook = async (options = {}) => { + return useCallback(async function () { + return Promise.resolve() + }, []) } - return fetch({ - ...defaultOpts, - ...options, - body: { email, password }, - }) + return useEmptyHook } -export function extendHook(customFetcher: typeof fetcher) { - const useLogin = () => { - const { revalidate } = useCustomer() - const fn = useCommerceLogin<null, LoginInput>(defaultOpts, customFetcher) - - return useCallback( - async function login(input: LoginInput) { - const data = await fn(input) - await revalidate() - return data - }, - [fn] - ) - } - - useLogin.extend = extendHook - - return useLogin -} - -export default extendHook(fetcher) +export default emptyHook diff --git a/framework/shopify/cart/use-add-item.tsx b/framework/shopify/cart/use-add-item.tsx index b0e9444bf..313bffde7 100644 --- a/framework/shopify/cart/use-add-item.tsx +++ b/framework/shopify/cart/use-add-item.tsx @@ -10,7 +10,6 @@ import checkoutLineItemAddMutation from '../utils/mutations/checkout-line-item-a import getCheckoutId from '@framework/utils/get-checkout-id' import { checkoutToCart } from './utils' import { AddCartItemBody, CartItemBody } from '@framework/types' -import { AddItemBody } from '../types' import { MutationCheckoutLineItemsAddArgs } from '@framework/schema' const defaultOpts = { diff --git a/framework/shopify/cart/utils/checkout-to-cart.ts b/framework/shopify/cart/utils/checkout-to-cart.ts index c7f0e2783..b38738e2f 100644 --- a/framework/shopify/cart/utils/checkout-to-cart.ts +++ b/framework/shopify/cart/utils/checkout-to-cart.ts @@ -1,18 +1,19 @@ import { Cart } from '@commerce/types' import { CommerceError, ValidationError } from '@commerce/utils/errors' -import { Checkout, CheckoutLineItemEdge, Maybe } from '@framework/schema' +import { normalizeCart } from '@framework/lib/normalize' +import { Checkout, Maybe, UserError } from '@framework/schema' -const checkoutToCart = (checkoutResponse?: any): Maybe<Cart> => { +const checkoutToCart = (checkoutResponse?: { + checkout: Checkout + userErrors?: UserError[] +}): Maybe<Cart> => { if (!checkoutResponse) { throw new CommerceError({ message: 'Missing checkout details from response cart Response', }) } - const { - checkout, - userErrors, - }: { checkout?: Checkout; userErrors?: any[] } = checkoutResponse + const { checkout, userErrors } = checkoutResponse if (userErrors && userErrors.length) { throw new ValidationError({ @@ -26,32 +27,7 @@ const checkoutToCart = (checkoutResponse?: any): Maybe<Cart> => { }) } - return { - ...checkout, - currency: { code: checkout.currencyCode }, - lineItems: checkout.lineItems?.edges.map( - ({ - node: { id, title: name, quantity, variant }, - }: CheckoutLineItemEdge) => ({ - id, - checkoutUrl: checkout.webUrl, - variantId: variant?.id, - productId: id, - name, - quantity, - discounts: [], - path: '', - variant: { - id: variant?.id, - image: { - url: variant?.image?.src, - altText: variant?.title, - }, - price: variant?.price, - }, - }) - ), - } + return normalizeCart(checkout) } export default checkoutToCart diff --git a/framework/shopify/lib/normalize.ts b/framework/shopify/lib/normalize.ts index 027694f20..e2670cb3b 100644 --- a/framework/shopify/lib/normalize.ts +++ b/framework/shopify/lib/normalize.ts @@ -64,7 +64,7 @@ export function normalizeProduct(productNode: ShopifyProduct): any { } = productNode return { - id: { $set: String(id) }, + id, name, vendor, description, @@ -73,7 +73,7 @@ export function normalizeProduct(productNode: ShopifyProduct): any { price: money(priceRange?.minVariantPrice), images: normalizeProductImages(images), variants: variants ? normalizeProductVariants(variants) : null, - options: options ? options.map((o) => normalizeProductOption) : [], + options: options ? options.map((o) => normalizeProductOption(o)) : [], ...rest, } } @@ -98,23 +98,25 @@ export function normalizeCart(data: Checkout): Cart { } } -function normalizeLineItem({ node: item }: CheckoutLineItemEdge): LineItem { +function normalizeLineItem({ + node: { id, title, variant, quantity, ...item }, +}: CheckoutLineItemEdge): LineItem { return { - id: item.id, - variantId: String(item.variant?.id), - productId: String(item.variant?.id), - name: item.title, - quantity: item.quantity, + id, + variantId: String(variant?.id), + productId: String(variant?.id), + name: title, + quantity: quantity, variant: { - id: String(item.variant?.id), - sku: item.variant?.sku ?? '', - name: item.title, + id: String(variant?.id), + sku: variant?.sku ?? '', + name: title, image: { - url: item.variant?.image?.originalSrc, + url: variant?.image?.originalSrc, }, - requiresShipping: item.variant?.requiresShipping ?? false, - price: item.variant?.price, - listPrice: item.variant?.compareAtPrice, + requiresShipping: variant?.requiresShipping ?? false, + price: variant?.price, + listPrice: variant?.compareAtPrice, }, path: '', discounts: item.discountAllocations.map(({ value }: any) => ({ diff --git a/framework/shopify/product/get-product.ts b/framework/shopify/product/get-product.ts index ba4856520..84e74c611 100644 --- a/framework/shopify/product/get-product.ts +++ b/framework/shopify/product/get-product.ts @@ -11,25 +11,23 @@ type Variables = { slug: string } -type Options = { - variables: Variables - config: ShopifyConfig - preview?: boolean -} - type ReturnType = { product: any } -const getProduct = async (options: Options): Promise<ReturnType> => { - let { config, variables = { first: 250 } } = options ?? {} +const getProduct = async (options: { + variables: Variables + config: ShopifyConfig + preview?: boolean +}): Promise<ReturnType> => { + let { config, variables } = options ?? {} config = getConfig(config) const { data }: GraphQLFetcherResult = await config.fetch(getProductQuery, { variables, }) - const product = data?.productByHandle?.product + const product = data?.productByHandle return { product: product ? normalizeProduct(product) : null, diff --git a/framework/shopify/utils/get-checkout-id.ts b/framework/shopify/utils/get-checkout-id.ts index 5ec9bb1ea..4f5b4f7b3 100644 --- a/framework/shopify/utils/get-checkout-id.ts +++ b/framework/shopify/utils/get-checkout-id.ts @@ -2,7 +2,9 @@ import Cookies from 'js-cookie' import { SHOPIFY_CHECKOUT_COOKIE } from '..' const getCheckoutId = (id?: string) => { - return id ?? Cookies.get(SHOPIFY_CHECKOUT_COOKIE) + const checkoutID = id ?? Cookies.get(SHOPIFY_CHECKOUT_COOKIE) + console.log(checkoutID) + return checkoutID } export default getCheckoutId From 9783537ca23fc0cf07150277253af66c4a4f615b Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 4 Feb 2021 16:06:37 +0200 Subject: [PATCH 072/221] Update normalize.ts --- framework/shopify/lib/normalize.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/framework/shopify/lib/normalize.ts b/framework/shopify/lib/normalize.ts index e2670cb3b..537e36f69 100644 --- a/framework/shopify/lib/normalize.ts +++ b/framework/shopify/lib/normalize.ts @@ -92,9 +92,7 @@ export function normalizeCart(data: Checkout): Cart { lineItemsSubtotalPrice: data.subtotalPrice, subtotalPrice: data.subtotalPrice, totalPrice: data.totalPrice, - discounts: data.discountApplications?.edges.map(({ value }: any) => ({ - value, - })), + discounts: [], } } @@ -119,8 +117,6 @@ function normalizeLineItem({ listPrice: variant?.compareAtPrice, }, path: '', - discounts: item.discountAllocations.map(({ value }: any) => ({ - value, - })), + discounts: [], } } From 2acc21164bb7ed3d055503abdee8cee5476e2853 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 4 Feb 2021 16:18:01 +0200 Subject: [PATCH 073/221] fix: cart error on first load --- framework/shopify/utils/mutations/checkout-create.ts | 5 +++-- framework/shopify/utils/queries/get-checkout-query.ts | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/framework/shopify/utils/mutations/checkout-create.ts b/framework/shopify/utils/mutations/checkout-create.ts index 6dfe438b3..912e1cbd2 100644 --- a/framework/shopify/utils/mutations/checkout-create.ts +++ b/framework/shopify/utils/mutations/checkout-create.ts @@ -1,3 +1,5 @@ +import { checkoutDetailsFragment } from '../queries/get-checkout-query' + const checkoutCreateMutation = /* GraphQL */ ` mutation { checkoutCreate(input: {}) { @@ -6,8 +8,7 @@ const checkoutCreateMutation = /* GraphQL */ ` field } checkout { - id - webUrl + ${checkoutDetailsFragment} } } } diff --git a/framework/shopify/utils/queries/get-checkout-query.ts b/framework/shopify/utils/queries/get-checkout-query.ts index 3b57eb83e..5d0b60545 100644 --- a/framework/shopify/utils/queries/get-checkout-query.ts +++ b/framework/shopify/utils/queries/get-checkout-query.ts @@ -19,7 +19,10 @@ export const checkoutDetailsFragment = /* GraphQL */ ` sku title image { - src + originalSrc + altText + width + height } price } From b0d8ae565d081b88e437dd8f862539509f3dcb84 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 4 Feb 2021 17:18:33 +0200 Subject: [PATCH 074/221] shopify checkout redirect & api handler --- .../cart/CartSidebarView/CartSidebarView.tsx | 2 +- framework/bigcommerce/README.md | 1 - framework/shopify/api/checkout/index.ts | 24 +++++++- .../shopify/api/utils/create-api-handler.ts | 58 +++++++++++++++++++ .../shopify/api/utils/is-allowed-method.ts | 28 +++++++++ .../shopify/cart/utils/checkout-create.ts | 12 ++-- framework/shopify/const.ts | 2 + framework/shopify/index.tsx | 11 ++-- framework/shopify/lib/normalize.ts | 2 +- .../utils/queries/get-checkout-query.ts | 3 + pages/api/bigcommerce/customers/index.ts | 2 +- 11 files changed, 128 insertions(+), 17 deletions(-) create mode 100644 framework/shopify/api/utils/create-api-handler.ts create mode 100644 framework/shopify/api/utils/is-allowed-method.ts create mode 100644 framework/shopify/const.ts diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index 394f89414..c25bd7c95 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -122,7 +122,7 @@ const CartSidebarView: FC = () => { <span>{total}</span> </div> </div> - <Button href={data.webUrl} Component="a" width="100%"> + <Button href="/checkout" Component="a" width="100%"> Proceed to Checkout </Button> </div> diff --git a/framework/bigcommerce/README.md b/framework/bigcommerce/README.md index 9792bcdb0..86fbac91d 100644 --- a/framework/bigcommerce/README.md +++ b/framework/bigcommerce/README.md @@ -329,7 +329,6 @@ const SearchPage = ({ searchString, category, brand, sortStr }) => { const { data } = useSearch({ search: searchString || '', categoryId: category?.entityId, - categorySlug: category?.slug, brandId: brand?.entityId, sort: sortStr || '', }) diff --git a/framework/shopify/api/checkout/index.ts b/framework/shopify/api/checkout/index.ts index 0b35324d8..79b38747c 100644 --- a/framework/shopify/api/checkout/index.ts +++ b/framework/shopify/api/checkout/index.ts @@ -1,5 +1,23 @@ -const getCheckout = async (req: any, res: any, config: any): Promise<any> => { - res.end() +import isAllowedMethod from '../utils/is-allowed-method' +import createApiHandler, { + ShopifyApiHandler, +} from '../utils/create-api-handler' + +import { SHOPIFY_CHECKOUT_URL_COOKIE } from '@framework/const' + +const METHODS = ['GET'] + +const checkoutApi: ShopifyApiHandler<any> = async (req, res) => { + if (!isAllowedMethod(req, res, METHODS)) return + + const { cookies } = req + const checkoutUrl = cookies[SHOPIFY_CHECKOUT_URL_COOKIE] + + if (checkoutUrl) { + res.redirect(checkoutUrl) + } else { + res.redirect('/cart') + } } -export default getCheckout +export default createApiHandler(checkoutApi, {}, {}) diff --git a/framework/shopify/api/utils/create-api-handler.ts b/framework/shopify/api/utils/create-api-handler.ts new file mode 100644 index 000000000..8820aeabc --- /dev/null +++ b/framework/shopify/api/utils/create-api-handler.ts @@ -0,0 +1,58 @@ +import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next' +import { ShopifyConfig, getConfig } from '..' + +export type ShopifyApiHandler< + T = any, + H extends ShopifyHandlers = {}, + Options extends {} = {} +> = ( + req: NextApiRequest, + res: NextApiResponse<ShopifyApiResponse<T>>, + config: ShopifyConfig, + handlers: H, + // Custom configs that may be used by a particular handler + options: Options +) => void | Promise<void> + +export type ShopifyHandler<T = any, Body = null> = (options: { + req: NextApiRequest + res: NextApiResponse<ShopifyApiResponse<T>> + config: ShopifyConfig + body: Body +}) => void | Promise<void> + +export type ShopifyHandlers<T = any> = { + [k: string]: ShopifyHandler<T, any> +} + +export type ShopifyApiResponse<T> = { + data: T | null + errors?: { message: string; code?: string }[] +} + +export default function createApiHandler< + T = any, + H extends ShopifyHandlers = {}, + Options extends {} = {} +>( + handler: ShopifyApiHandler<T, H, Options>, + handlers: H, + defaultOptions: Options +) { + return function getApiHandler({ + config, + operations, + options, + }: { + config?: ShopifyConfig + operations?: Partial<H> + options?: Options extends {} ? Partial<Options> : never + } = {}): NextApiHandler { + const ops = { ...operations, ...handlers } + const opts = { ...defaultOptions, ...options } + + return function apiHandler(req, res) { + return handler(req, res, getConfig(config), ops, opts) + } + } +} diff --git a/framework/shopify/api/utils/is-allowed-method.ts b/framework/shopify/api/utils/is-allowed-method.ts new file mode 100644 index 000000000..78bbba568 --- /dev/null +++ b/framework/shopify/api/utils/is-allowed-method.ts @@ -0,0 +1,28 @@ +import type { NextApiRequest, NextApiResponse } from 'next' + +export default function isAllowedMethod( + req: NextApiRequest, + res: NextApiResponse, + allowedMethods: string[] +) { + const methods = allowedMethods.includes('OPTIONS') + ? allowedMethods + : [...allowedMethods, 'OPTIONS'] + + if (!req.method || !methods.includes(req.method)) { + res.status(405) + res.setHeader('Allow', methods.join(', ')) + res.end() + return false + } + + if (req.method === 'OPTIONS') { + res.status(200) + res.setHeader('Allow', methods.join(', ')) + res.setHeader('Content-Length', '0') + res.end() + return false + } + + return true +} diff --git a/framework/shopify/cart/utils/checkout-create.ts b/framework/shopify/cart/utils/checkout-create.ts index 50cbd1299..24d080551 100644 --- a/framework/shopify/cart/utils/checkout-create.ts +++ b/framework/shopify/cart/utils/checkout-create.ts @@ -1,8 +1,11 @@ -import { SHOPIFY_CHECKOUT_COOKIE } from '@framework' +import { + SHOPIFY_CHECKOUT_ID_COOKIE, + SHOPIFY_CHECKOUT_URL_COOKIE, +} from '@framework/const' import checkoutCreateMutation from '@framework/utils/mutations/checkout-create' import Cookies from 'js-cookie' -export const createCheckout = async (fetch: any) => { +export const checkoutCreate = async (fetch: any) => { const data = await fetch({ query: checkoutCreateMutation, }) @@ -11,10 +14,11 @@ export const createCheckout = async (fetch: any) => { const checkoutId = checkout?.id if (checkoutId) { - Cookies.set(SHOPIFY_CHECKOUT_COOKIE, checkoutId) + Cookies.set(SHOPIFY_CHECKOUT_ID_COOKIE, checkoutId) + Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout?.webUrl) } return checkout } -export default createCheckout +export default checkoutCreate diff --git a/framework/shopify/const.ts b/framework/shopify/const.ts new file mode 100644 index 000000000..0c957632c --- /dev/null +++ b/framework/shopify/const.ts @@ -0,0 +1,2 @@ +export const SHOPIFY_CHECKOUT_ID_COOKIE = 'shopify_checkoutId' +export const SHOPIFY_CHECKOUT_URL_COOKIE = 'shopify_checkoutUrl' diff --git a/framework/shopify/index.tsx b/framework/shopify/index.tsx index 3d9db989f..f6b8a8af6 100644 --- a/framework/shopify/index.tsx +++ b/framework/shopify/index.tsx @@ -8,8 +8,7 @@ import { } from '@commerce' import { CommerceError, FetcherError } from '@commerce/utils/errors' - -export const SHOPIFY_CHECKOUT_COOKIE = 'shopify_checkoutId' +import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const' async function getText(res: Response) { try { @@ -28,9 +27,11 @@ async function getError(res: Response) { return new FetcherError({ message: await getText(res), status: res.status }) } -export const shopifyConfig: CommerceConfig = { +export type ShopifyConfig = Partial<CommerceConfig> + +export const shopifyConfig: ShopifyConfig = { locale: 'en-us', - cartCookie: SHOPIFY_CHECKOUT_COOKIE, + cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, async fetcher({ method = 'POST', variables, query }) { const res = await fetch( `https://${process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN}/api/2021-01/graphql.json`, @@ -60,8 +61,6 @@ export const shopifyConfig: CommerceConfig = { }, } -export type ShopifyConfig = Partial<CommerceConfig> - export type ShopifyProps = { children?: ReactNode locale: string diff --git a/framework/shopify/lib/normalize.ts b/framework/shopify/lib/normalize.ts index 537e36f69..ff2de330c 100644 --- a/framework/shopify/lib/normalize.ts +++ b/framework/shopify/lib/normalize.ts @@ -97,7 +97,7 @@ export function normalizeCart(data: Checkout): Cart { } function normalizeLineItem({ - node: { id, title, variant, quantity, ...item }, + node: { id, title, variant, quantity }, }: CheckoutLineItemEdge): LineItem { return { id, diff --git a/framework/shopify/utils/queries/get-checkout-query.ts b/framework/shopify/utils/queries/get-checkout-query.ts index 5d0b60545..b582ca3bb 100644 --- a/framework/shopify/utils/queries/get-checkout-query.ts +++ b/framework/shopify/utils/queries/get-checkout-query.ts @@ -5,6 +5,9 @@ export const checkoutDetailsFragment = /* GraphQL */ ` totalTax totalPrice currencyCode + completedAt + createdAt + taxesIncluded lineItems(first: 250) { pageInfo { hasNextPage diff --git a/pages/api/bigcommerce/customers/index.ts b/pages/api/bigcommerce/customers/index.ts index 7b55d3aa8..911a4521a 100644 --- a/pages/api/bigcommerce/customers/index.ts +++ b/pages/api/bigcommerce/customers/index.ts @@ -1,3 +1,3 @@ -import customersApi from '@framework/api/customers' +import customersApi from '@framework/api/customer' export default customersApi() From 029a8372a23989d4f639eac4b56a70a2aebad02a Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 4 Feb 2021 17:22:01 +0200 Subject: [PATCH 075/221] Update get-checkout-id.ts --- framework/shopify/utils/get-checkout-id.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/shopify/utils/get-checkout-id.ts b/framework/shopify/utils/get-checkout-id.ts index 4f5b4f7b3..5d35513a5 100644 --- a/framework/shopify/utils/get-checkout-id.ts +++ b/framework/shopify/utils/get-checkout-id.ts @@ -1,8 +1,8 @@ import Cookies from 'js-cookie' -import { SHOPIFY_CHECKOUT_COOKIE } from '..' +import { SHOPIFY_CHECKOUT_ID_COOKIE } from '../const' const getCheckoutId = (id?: string) => { - const checkoutID = id ?? Cookies.get(SHOPIFY_CHECKOUT_COOKIE) + const checkoutID = id ?? Cookies.get(SHOPIFY_CHECKOUT_ID_COOKIE) console.log(checkoutID) return checkoutID } From 3f30344619730ce20eac92c0c8b0d66b3d18955a Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Thu, 4 Feb 2021 12:29:20 -0300 Subject: [PATCH 076/221] userAvatar --- components/common/Avatar/Avatar.tsx | 14 +++++--------- components/common/UserNav/UserNav.tsx | 2 +- components/ui/context.tsx | 16 ++++++++++++++++ lib/hooks/useUserAvatar.ts | 26 ++++++++++++++++++++++++++ tsconfig.json | 4 ++-- 5 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 lib/hooks/useUserAvatar.ts diff --git a/components/common/Avatar/Avatar.tsx b/components/common/Avatar/Avatar.tsx index 351a117ec..ab91d7089 100644 --- a/components/common/Avatar/Avatar.tsx +++ b/components/common/Avatar/Avatar.tsx @@ -1,5 +1,5 @@ -import { FC, useState, useMemo, useRef, useEffect } from 'react' -import { getRandomPairOfColors } from '@lib/colors' +import { FC, useRef, useEffect } from 'react' +import { useUserAvatar } from '@lib/hooks/useUserAvatar' interface Props { className?: string @@ -7,18 +7,14 @@ interface Props { } const Avatar: FC<Props> = ({}) => { - const [bg] = useState(useMemo(() => getRandomPairOfColors, [])) let ref = useRef() as React.MutableRefObject<HTMLInputElement> - - useEffect(() => { - if (ref && ref.current) { - ref.current.style.backgroundImage = `linear-gradient(140deg, ${bg[0]}, ${bg[1]} 100%)` - } - }, [bg]) + let { userAvatar } = useUserAvatar() + console.log(userAvatar) return ( <div ref={ref} + style={{ backgroundImage: userAvatar }} className="inline-block h-8 w-8 rounded-full border-2 border-primary hover:border-secondary focus:border-secondary transition linear-out duration-150" > {/* Add an image - We're generating a gradient as placeholder <img></img> */} diff --git a/components/common/UserNav/UserNav.tsx b/components/common/UserNav/UserNav.tsx index f8e6373d9..e33796927 100644 --- a/components/common/UserNav/UserNav.tsx +++ b/components/common/UserNav/UserNav.tsx @@ -3,11 +3,11 @@ import Link from 'next/link' import cn from 'classnames' import useCart from '@framework/cart/use-cart' import useCustomer from '@framework/customer/use-customer' +import { Avatar } from '@components/common' import { Heart, Bag } from '@components/icons' import { useUI } from '@components/ui/context' import DropdownMenu from './DropdownMenu' import s from './UserNav.module.css' -import { Avatar } from '@components/common' interface Props { className?: string diff --git a/components/ui/context.tsx b/components/ui/context.tsx index 206573858..13992a736 100644 --- a/components/ui/context.tsx +++ b/components/ui/context.tsx @@ -8,6 +8,7 @@ export interface State { displayToast: boolean modalView: string toastText: string + userAvatar: string } const initialState = { @@ -17,6 +18,7 @@ const initialState = { modalView: 'LOGIN_VIEW', displayToast: false, toastText: '', + userAvatar: '', } type Action = @@ -52,6 +54,10 @@ type Action = type: 'SET_MODAL_VIEW' view: MODAL_VIEWS } + | { + type: 'SET_USER_AVATAR' + value: string + } type MODAL_VIEWS = 'SIGNUP_VIEW' | 'LOGIN_VIEW' | 'FORGOT_VIEW' type ToastText = string @@ -123,6 +129,12 @@ function uiReducer(state: State, action: Action) { toastText: action.text, } } + case 'SET_USER_AVATAR': { + return { + ...state, + userAvatar: action.value, + } + } } } @@ -147,6 +159,9 @@ export const UIProvider: FC = (props) => { const openToast = () => dispatch({ type: 'OPEN_TOAST' }) const closeToast = () => dispatch({ type: 'CLOSE_TOAST' }) + const setUserAvatar = (value: string) => + dispatch({ type: 'SET_USER_AVATAR', value }) + const setModalView = (view: MODAL_VIEWS) => dispatch({ type: 'SET_MODAL_VIEW', view }) @@ -164,6 +179,7 @@ export const UIProvider: FC = (props) => { setModalView, openToast, closeToast, + setUserAvatar, }), [state] ) diff --git a/lib/hooks/useUserAvatar.ts b/lib/hooks/useUserAvatar.ts new file mode 100644 index 000000000..bc9020931 --- /dev/null +++ b/lib/hooks/useUserAvatar.ts @@ -0,0 +1,26 @@ +import { useEffect } from 'react' +import { getRandomPairOfColors } from '@lib/colors' +import { useUI } from '@components/ui/context' + +export const useUserAvatar = (name = 'userAvatar') => { + const { userAvatar, setUserAvatar } = useUI() + + useEffect(() => { + if (!userAvatar && localStorage.getItem(name)) { + // get bg value locally. + setUserAvatar(localStorage.getItem(name)) + } + if (!localStorage.getItem(name)) { + // local not set, set. + const bg = getRandomPairOfColors() + const value = `linear-gradient(140deg, ${bg[0]}, ${bg[1]} 100%)` + localStorage.setItem(name, value) + setUserAvatar(value) + } + }, []) + + return { + userAvatar, + setUserAvatar, + } +} diff --git a/tsconfig.json b/tsconfig.json index 480622bb4..f8161ccf2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,8 +22,8 @@ "@components/*": ["components/*"], "@commerce": ["framework/commerce"], "@commerce/*": ["framework/commerce/*"], - "@framework": ["framework/bigcommerce"] - "@framework/*": ["framework/bigcommerce/*"], + "@framework": ["framework/bigcommerce"], + "@framework/*": ["framework/bigcommerce/*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], From 553a1fd9d3e20929e576503be9c138671854f8ee Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 4 Feb 2021 17:44:17 +0200 Subject: [PATCH 077/221] Fix: color option --- .../product/ProductCard/ProductCard.tsx | 4 ++-- framework/shopify/lib/normalize.ts | 19 +++++++++++-------- framework/shopify/product/get-product.ts | 2 ++ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index 464f097e8..8d4402271 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -25,8 +25,8 @@ const ProductCard: FC<Props> = ({ <a className={cn(s.root, { [s.simple]: variant === 'simple' }, className)}> {variant === 'slim' ? ( <div className="relative overflow-hidden box-border"> - <div className="absolute inset-0 flex items-center justify-end mr-8 z-20"> - <span className="bg-black text-white inline-block p-3 font-bold text-xl break-words"> + <div className="absolute inset-0 flex items-start justify-end m-1 z-20"> + <span className="text-black inline-block p-3 font-bold text-xl break-words"> {product.name} </span> </div> diff --git a/framework/shopify/lib/normalize.ts b/framework/shopify/lib/normalize.ts index ff2de330c..c84a9da1c 100644 --- a/framework/shopify/lib/normalize.ts +++ b/framework/shopify/lib/normalize.ts @@ -22,14 +22,17 @@ const normalizeProductOption = ({ name: displayName, values, ...rest -}: ProductOption) => ({ - __typename: 'MultipleChoiceOption', - displayName, - values: values.map((value) => ({ - label: value, - })), - ...rest, -}) +}: ProductOption) => { + return { + __typename: 'MultipleChoiceOption', + displayName, + values: values.map((value) => ({ + label: value, + hexColors: displayName === 'Color' ? [value] : null, + })), + ...rest, + } +} const normalizeProductImages = ({ edges }: ImageConnection) => edges?.map(({ node: { originalSrc: url, ...rest } }) => ({ diff --git a/framework/shopify/product/get-product.ts b/framework/shopify/product/get-product.ts index 84e74c611..e5a5469c0 100644 --- a/framework/shopify/product/get-product.ts +++ b/framework/shopify/product/get-product.ts @@ -29,6 +29,8 @@ const getProduct = async (options: { const product = data?.productByHandle + console.info(normalizeProduct(product)) + return { product: product ? normalizeProduct(product) : null, } From d35adf9c21a5f5cff896a368b6f9e3ef081edc73 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 4 Feb 2021 17:50:32 +0200 Subject: [PATCH 078/221] Update normalize.ts --- framework/shopify/lib/normalize.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/shopify/lib/normalize.ts b/framework/shopify/lib/normalize.ts index c84a9da1c..eeb637a48 100644 --- a/framework/shopify/lib/normalize.ts +++ b/framework/shopify/lib/normalize.ts @@ -106,12 +106,12 @@ function normalizeLineItem({ id, variantId: String(variant?.id), productId: String(variant?.id), - name: title, + name: `${title} - ${variant?.title}`, quantity: quantity, variant: { id: String(variant?.id), sku: variant?.sku ?? '', - name: title, + name: variant?.title, image: { url: variant?.image?.originalSrc, }, From 1a43a0ab57c38ff50099b4b864463674d497bebf Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 4 Feb 2021 18:09:20 +0200 Subject: [PATCH 079/221] changes --- framework/shopify/product/get-product.ts | 2 -- framework/shopify/utils/get-checkout-id.ts | 4 +--- next.config.js | 3 --- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/framework/shopify/product/get-product.ts b/framework/shopify/product/get-product.ts index e5a5469c0..84e74c611 100644 --- a/framework/shopify/product/get-product.ts +++ b/framework/shopify/product/get-product.ts @@ -29,8 +29,6 @@ const getProduct = async (options: { const product = data?.productByHandle - console.info(normalizeProduct(product)) - return { product: product ? normalizeProduct(product) : null, } diff --git a/framework/shopify/utils/get-checkout-id.ts b/framework/shopify/utils/get-checkout-id.ts index 5d35513a5..11e3802d9 100644 --- a/framework/shopify/utils/get-checkout-id.ts +++ b/framework/shopify/utils/get-checkout-id.ts @@ -2,9 +2,7 @@ import Cookies from 'js-cookie' import { SHOPIFY_CHECKOUT_ID_COOKIE } from '../const' const getCheckoutId = (id?: string) => { - const checkoutID = id ?? Cookies.get(SHOPIFY_CHECKOUT_ID_COOKIE) - console.log(checkoutID) - return checkoutID + return id ?? Cookies.get(SHOPIFY_CHECKOUT_ID_COOKIE) } export default getCheckoutId diff --git a/next.config.js b/next.config.js index abdfe251c..3c9e37210 100644 --- a/next.config.js +++ b/next.config.js @@ -34,7 +34,4 @@ module.exports = { }, ] }, - typescript: { - ignoreBuildErrors: true, - }, } From 2a29218285b6cba72544de739f09251915e9fe96 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 4 Feb 2021 18:10:11 +0200 Subject: [PATCH 080/221] Update next.config.js --- next.config.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/next.config.js b/next.config.js index 3c9e37210..abdfe251c 100644 --- a/next.config.js +++ b/next.config.js @@ -34,4 +34,7 @@ module.exports = { }, ] }, + typescript: { + ignoreBuildErrors: true, + }, } From 57ebfe42f56470eceebb9771bc9fdaceb2d21814 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Fri, 5 Feb 2021 00:02:39 +0200 Subject: [PATCH 081/221] start customer auth & signup --- .../api/utils/fetch-graphql-api.ts | 1 - framework/bigcommerce/auth/use-signup.tsx | 19 +++--- framework/shopify/api/index.ts | 6 +- framework/shopify/auth/use-login.tsx | 60 ++++++++++++++++--- framework/shopify/cart/use-cart.tsx | 1 + framework/shopify/config.ts | 44 ++++++++++++++ framework/shopify/customer/use-customer.tsx | 24 ++++---- framework/shopify/index.tsx | 55 +---------------- framework/shopify/utils/fetch-graphql-api.ts | 21 +++++-- .../mutations/customer-acces-token-create.ts | 16 +++++ .../utils/mutations/customer-create.ts | 15 +++++ framework/shopify/utils/mutations/index.ts | 1 + 12 files changed, 173 insertions(+), 90 deletions(-) create mode 100644 framework/shopify/config.ts create mode 100644 framework/shopify/utils/mutations/customer-acces-token-create.ts create mode 100644 framework/shopify/utils/mutations/customer-create.ts diff --git a/framework/bigcommerce/api/utils/fetch-graphql-api.ts b/framework/bigcommerce/api/utils/fetch-graphql-api.ts index a449b81e0..aa211dd88 100644 --- a/framework/bigcommerce/api/utils/fetch-graphql-api.ts +++ b/framework/bigcommerce/api/utils/fetch-graphql-api.ts @@ -8,7 +8,6 @@ const fetchGraphqlApi: GraphQLFetcher = async ( { variables, preview } = {}, fetchOptions ) => { - // log.warn(query) const config = getConfig() const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), { ...fetchOptions, diff --git a/framework/bigcommerce/auth/use-signup.tsx b/framework/bigcommerce/auth/use-signup.tsx index c68ce7b7a..0fb54d332 100644 --- a/framework/bigcommerce/auth/use-signup.tsx +++ b/framework/bigcommerce/auth/use-signup.tsx @@ -2,17 +2,15 @@ import { useCallback } from 'react' import type { HookFetcher } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' import useCommerceSignup from '@commerce/use-signup' -import type { SignupBody } from '../api/customers/signup' import useCustomer from '../customer/use-customer' +import customerCreateMutation from '@framework/utils/mutations/customer-create' +import { CustomerCreateInput } from '@framework/schema' const defaultOpts = { - url: '/api/bigcommerce/customers/signup', - method: 'POST', + query: customerCreateMutation, } -export type SignupInput = SignupBody - -export const fetcher: HookFetcher<null, SignupBody> = ( +export const fetcher: HookFetcher<null, CustomerCreateInput> = ( options, { firstName, lastName, email, password }, fetch @@ -27,17 +25,20 @@ export const fetcher: HookFetcher<null, SignupBody> = ( return fetch({ ...defaultOpts, ...options, - body: { firstName, lastName, email, password }, + variables: { firstName, lastName, email, password }, }) } export function extendHook(customFetcher: typeof fetcher) { const useSignup = () => { const { revalidate } = useCustomer() - const fn = useCommerceSignup<null, SignupInput>(defaultOpts, customFetcher) + const fn = useCommerceSignup<null, CustomerCreateInput>( + defaultOpts, + customFetcher + ) return useCallback( - async function signup(input: SignupInput) { + async function signup(input: CustomerCreateInput) { const data = await fn(input) await revalidate() return data diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts index f62fb3437..7b04ed602 100644 --- a/framework/shopify/api/index.ts +++ b/framework/shopify/api/index.ts @@ -1,9 +1,9 @@ import type { CommerceAPIConfig } from '@commerce/api' +import { SHOPIFY_CHECKOUT_ID_COOKIE } from '@framework/const' import fetchGraphqlApi from '../utils/fetch-graphql-api' export interface ShopifyConfig extends CommerceAPIConfig {} -// No I don't like this - will fix it later const API_URL = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN @@ -38,9 +38,13 @@ export class Config { } } +const ONE_DAY = 60 * 60 * 24 + const config = new Config({ commerceUrl: API_URL, apiToken: API_TOKEN, + cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, + cartCookieMaxAge: ONE_DAY * 30, fetch: fetchGraphqlApi, customerCookie: 'SHOP_TOKEN', }) diff --git a/framework/shopify/auth/use-login.tsx b/framework/shopify/auth/use-login.tsx index 75f067c3a..ee447962e 100644 --- a/framework/shopify/auth/use-login.tsx +++ b/framework/shopify/auth/use-login.tsx @@ -1,13 +1,55 @@ import { useCallback } from 'react' +import type { HookFetcher } from '@commerce/utils/types' +import { CommerceError } from '@commerce/utils/errors' +import useCommerceLogin from '@commerce/use-login' +import useCustomer from '../customer/use-customer' +import createCustomerAccessTokenMutation from '../utils/mutations/customer-acces-token-create' +import { CustomerAccessTokenCreateInput } from '@framework/schema' -export function emptyHook() { - const useEmptyHook = async (options = {}) => { - return useCallback(async function () { - return Promise.resolve() - }, []) - } - - return useEmptyHook +const defaultOpts = { + query: createCustomerAccessTokenMutation, } -export default emptyHook +export const fetcher: HookFetcher<null, CustomerAccessTokenCreateInput> = ( + options, + { email, password }, + fetch +) => { + if (!(email && password)) { + throw new CommerceError({ + message: + 'A first name, last name, email and password are required to login', + }) + } + + return fetch({ + ...defaultOpts, + ...options, + body: { email, password }, + }) +} + +export function extendHook(customFetcher: typeof fetcher) { + const useLogin = () => { + const { revalidate } = useCustomer() + const fn = useCommerceLogin<null, CustomerAccessTokenCreateInput>( + defaultOpts, + customFetcher + ) + + return useCallback( + async function login(input: CustomerAccessTokenCreateInput) { + const data = await fn(input) + await revalidate() + return data + }, + [fn] + ) + } + + useLogin.extend = extendHook + + return useLogin +} + +export default extendHook(fetcher) diff --git a/framework/shopify/cart/use-cart.tsx b/framework/shopify/cart/use-cart.tsx index 24d4e9f94..a4b92786f 100644 --- a/framework/shopify/cart/use-cart.tsx +++ b/framework/shopify/cart/use-cart.tsx @@ -7,6 +7,7 @@ import getCheckoutQuery from '@framework/utils/queries/get-checkout-query' import { Cart } from '@commerce/types' import { checkoutToCart, checkoutCreate } from './utils' +import { getConfig } from '@framework/api' const defaultOpts = { query: getCheckoutQuery, diff --git a/framework/shopify/config.ts b/framework/shopify/config.ts new file mode 100644 index 000000000..6b1743fbf --- /dev/null +++ b/framework/shopify/config.ts @@ -0,0 +1,44 @@ +import { CommerceError, FetcherError } from '@commerce/utils/errors' +import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const' +import type { CommerceConfig } from '@commerce' + +export type ShopifyConfig = { + locale: string + cartCookie: string + storeDomain: string | undefined +} & CommerceConfig + +export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN +export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json` +export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN + +const shopifyConfig: ShopifyConfig = { + locale: 'en-us', + cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, + storeDomain: STORE_DOMAIN, + async fetcher({ method = 'POST', variables, query }) { + const res = await fetch(API_URL, { + method, + body: JSON.stringify({ query, variables }), + headers: { + 'X-Shopify-Storefront-Access-Token': API_TOKEN!, + 'Content-Type': 'application/json', + }, + }) + + const json = await res.json() + + if (json.errors) { + throw new FetcherError({ + errors: json.errors ?? [ + { message: 'Failed to fetch Shopify Storefront API' }, + ], + status: res.status, + }) + } + + return { data: json.data, res } + }, +} + +export default shopifyConfig diff --git a/framework/shopify/customer/use-customer.tsx b/framework/shopify/customer/use-customer.tsx index a909443ff..d46879b91 100644 --- a/framework/shopify/customer/use-customer.tsx +++ b/framework/shopify/customer/use-customer.tsx @@ -2,18 +2,17 @@ import type { HookFetcher } from '@commerce/utils/types' import type { SwrOptions } from '@commerce/utils/use-data' import useCommerceCustomer from '@commerce/use-customer' -const defaultOpts = {} - -export type Customer = { - entityId: number - firstName: string - lastName: string - email: string +const defaultOpts = { + query: '/api/bigcommerce/customers', } -export type CustomerData = {} -export const fetcher: HookFetcher<Customer | null> = async () => { - return null +export const fetcher: HookFetcher<Customer | null> = async ( + options, + _, + fetch +) => { + const data = await fetch<CustomerData | null>({ ...defaultOpts, ...options }) + return data?.customer ?? null } export function extendHook( @@ -21,7 +20,10 @@ export function extendHook( swrOptions?: SwrOptions<Customer | null> ) { const useCustomer = () => { - return { data: { firstName: null, lastName: null, email: null } } + return useCommerceCustomer(defaultOpts, [], customFetcher, { + revalidateOnFocus: false, + ...swrOptions, + }) } useCustomer.extend = extendHook diff --git a/framework/shopify/index.tsx b/framework/shopify/index.tsx index f6b8a8af6..ad54ec6ab 100644 --- a/framework/shopify/index.tsx +++ b/framework/shopify/index.tsx @@ -2,64 +2,11 @@ import { ReactNode } from 'react' import * as React from 'react' import { - CommerceConfig, CommerceProvider as CoreCommerceProvider, useCommerce as useCoreCommerce, } from '@commerce' -import { CommerceError, FetcherError } from '@commerce/utils/errors' -import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const' - -async function getText(res: Response) { - try { - return (await res.text()) || res.statusText - } catch (error) { - return res.statusText - } -} - -async function getError(res: Response) { - if (res.headers.get('Content-Type')?.includes('application/json')) { - const data = await res.json() - - return new FetcherError({ errors: data.errors, status: res.status }) - } - return new FetcherError({ message: await getText(res), status: res.status }) -} - -export type ShopifyConfig = Partial<CommerceConfig> - -export const shopifyConfig: ShopifyConfig = { - locale: 'en-us', - cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, - async fetcher({ method = 'POST', variables, query }) { - const res = await fetch( - `https://${process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN}/api/2021-01/graphql.json`, - { - method, - body: JSON.stringify({ query, variables }), - headers: { - 'X-Shopify-Storefront-Access-Token': - process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN, - 'Content-Type': 'application/json', - }, - } - ) - - if (res.ok) { - const { data, errors } = await res.json() - - if (errors && errors.length) { - throw new CommerceError({ - message: errors[0].message, - }) - } - return data - } - - throw await getError(res) - }, -} +import shopifyConfig, { ShopifyConfig } from './config' export type ShopifyProps = { children?: ReactNode diff --git a/framework/shopify/utils/fetch-graphql-api.ts b/framework/shopify/utils/fetch-graphql-api.ts index a690314ed..b552a7f18 100644 --- a/framework/shopify/utils/fetch-graphql-api.ts +++ b/framework/shopify/utils/fetch-graphql-api.ts @@ -1,20 +1,31 @@ import type { GraphQLFetcher } from '@commerce/api' import { FetcherError } from '@commerce/utils/errors' -import { getConfig } from '../api/index' import fetch from './fetch' +import { STORE_DOMAIN, API_URL, API_TOKEN } from '../config' + +if (!STORE_DOMAIN) { + throw new Error( + `The environment variable NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN is missing and it's required to access your store` + ) +} + +if (!API_TOKEN) { + throw new Error( + `The environment variable NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store` + ) +} + const fetchGraphqlApi: GraphQLFetcher = async ( query: string, { variables } = {}, fetchOptions ) => { - const { commerceUrl, apiToken } = getConfig() - - const res = await fetch(`https://${commerceUrl}/api/2021-01/graphql.json`, { + const res = await fetch(API_URL, { ...fetchOptions, method: 'POST', headers: { - 'X-Shopify-Storefront-Access-Token': apiToken, + 'X-Shopify-Storefront-Access-Token': API_TOKEN!, ...fetchOptions?.headers, 'Content-Type': 'application/json', }, diff --git a/framework/shopify/utils/mutations/customer-acces-token-create.ts b/framework/shopify/utils/mutations/customer-acces-token-create.ts new file mode 100644 index 000000000..7a45c3f49 --- /dev/null +++ b/framework/shopify/utils/mutations/customer-acces-token-create.ts @@ -0,0 +1,16 @@ +const customerAccessTokenCreateMutation = /* GraphQL */ ` + mutation customerAccessTokenCreate($input: CustomerAccessTokenCreateInput!) { + customerAccessTokenCreate(input: $input) { + customerAccessToken { + accessToken + expiresAt + } + customerUserErrors { + code + field + message + } + } + } +` +export default customerAccessTokenCreateMutation diff --git a/framework/shopify/utils/mutations/customer-create.ts b/framework/shopify/utils/mutations/customer-create.ts new file mode 100644 index 000000000..05c728a25 --- /dev/null +++ b/framework/shopify/utils/mutations/customer-create.ts @@ -0,0 +1,15 @@ +const customerCreateMutation = /* GraphQL */ ` + mutation customerCreate($input: CustomerCreateInput!) { + customerCreate(input: $input) { + customerUserErrors { + code + field + message + } + customer { + id + } + } + } +` +export default customerCreateMutation diff --git a/framework/shopify/utils/mutations/index.ts b/framework/shopify/utils/mutations/index.ts index 9a7a7c9c1..14f82a476 100644 --- a/framework/shopify/utils/mutations/index.ts +++ b/framework/shopify/utils/mutations/index.ts @@ -1,3 +1,4 @@ +export { default as createCustomerMutation } from './customer-create' export { default as checkoutCreateMutation } from './checkout-create' export { default as checkoutLineItemAddMutation } from './checkout-line-item-add' export { default as checkoutLineItemUpdateMutation } from './checkout-create' From 612392aabacbf792bb07fd1f1eda342a988aacb2 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Fri, 5 Feb 2021 00:14:10 +0200 Subject: [PATCH 082/221] Update config.ts --- framework/shopify/config.ts | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/framework/shopify/config.ts b/framework/shopify/config.ts index 6b1743fbf..735090185 100644 --- a/framework/shopify/config.ts +++ b/framework/shopify/config.ts @@ -12,6 +12,22 @@ export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json` export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN +async function getText(res: Response) { + try { + return (await res.text()) || res.statusText + } catch (error) { + return res.statusText + } +} + +async function getError(res: Response) { + if (res.headers.get('Content-Type')?.includes('application/json')) { + const data = await res.json() + return new FetcherError({ errors: data.errors, status: res.status }) + } + return new FetcherError({ message: await getText(res), status: res.status }) +} + const shopifyConfig: ShopifyConfig = { locale: 'en-us', cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, @@ -26,18 +42,18 @@ const shopifyConfig: ShopifyConfig = { }, }) - const json = await res.json() + if (res.ok) { + const { data, errors } = await res.json() - if (json.errors) { - throw new FetcherError({ - errors: json.errors ?? [ - { message: 'Failed to fetch Shopify Storefront API' }, - ], - status: res.status, - }) + if (errors && errors.length) { + throw new CommerceError({ + message: errors[0].message, + }) + } + return data } - return { data: json.data, res } + throw await getError(res) }, } From dde09c5105b0fd36933ba2fac3c4b0fcb761eb33 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Fri, 5 Feb 2021 10:31:04 +0200 Subject: [PATCH 083/221] Login, Sign Up, Log Out, and checkout & customer association --- framework/shopify/api/checkout/index.ts | 23 ++++++- framework/shopify/api/index.ts | 7 ++- framework/shopify/auth/use-login.tsx | 44 +++++++++++-- framework/shopify/auth/use-logout.tsx | 50 ++++++++++++--- framework/shopify/auth/use-signup.tsx | 62 ++++++++++++++++--- framework/shopify/config.ts | 2 +- framework/shopify/const.ts | 1 + framework/shopify/customer/get-customer-id.ts | 24 +++++++ framework/shopify/customer/use-customer.tsx | 24 ++++--- framework/shopify/lib/normalize.ts | 2 +- .../shopify/product/get-all-collections.ts | 2 +- .../shopify/product/get-all-product-paths.ts | 6 +- framework/shopify/product/use-search.tsx | 1 - framework/shopify/utils/customer-token.ts | 12 ++++ .../associate-customer-with-checkout.ts | 18 ++++++ ...ate.ts => customer-access-token-create.ts} | 0 .../mutations/customer-access-token-delete.ts | 14 +++++ framework/shopify/utils/mutations/index.ts | 2 + .../utils/queries/get-customer-id-query.ts | 8 +++ .../utils/queries/get-customer-query.ts | 16 +++++ framework/shopify/utils/queries/index.ts | 1 + 21 files changed, 276 insertions(+), 43 deletions(-) create mode 100644 framework/shopify/customer/get-customer-id.ts create mode 100644 framework/shopify/utils/customer-token.ts create mode 100644 framework/shopify/utils/mutations/associate-customer-with-checkout.ts rename framework/shopify/utils/mutations/{customer-acces-token-create.ts => customer-access-token-create.ts} (100%) create mode 100644 framework/shopify/utils/mutations/customer-access-token-delete.ts create mode 100644 framework/shopify/utils/queries/get-customer-id-query.ts create mode 100644 framework/shopify/utils/queries/get-customer-query.ts diff --git a/framework/shopify/api/checkout/index.ts b/framework/shopify/api/checkout/index.ts index 79b38747c..638dc20ee 100644 --- a/framework/shopify/api/checkout/index.ts +++ b/framework/shopify/api/checkout/index.ts @@ -3,16 +3,35 @@ import createApiHandler, { ShopifyApiHandler, } from '../utils/create-api-handler' -import { SHOPIFY_CHECKOUT_URL_COOKIE } from '@framework/const' +import { + SHOPIFY_CHECKOUT_ID_COOKIE, + SHOPIFY_CHECKOUT_URL_COOKIE, + SHOPIFY_CUSTOMER_TOKEN_COOKIE, +} from '@framework/const' +import { getConfig } from '..' +import associateCustomerWithCheckoutMutation from '@framework/utils/mutations/associate-customer-with-checkout' const METHODS = ['GET'] -const checkoutApi: ShopifyApiHandler<any> = async (req, res) => { +const checkoutApi: ShopifyApiHandler<any> = async (req, res, config) => { if (!isAllowedMethod(req, res, METHODS)) return + config = getConfig() + const { cookies } = req const checkoutUrl = cookies[SHOPIFY_CHECKOUT_URL_COOKIE] + try { + await config.fetch(associateCustomerWithCheckoutMutation, { + variables: { + checkoutId: cookies[SHOPIFY_CHECKOUT_ID_COOKIE], + customerAccessToken: cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE], + }, + }) + } catch (error) { + console.error(error) + } + if (checkoutUrl) { res.redirect(checkoutUrl) } else { diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts index 7b04ed602..273c43c8b 100644 --- a/framework/shopify/api/index.ts +++ b/framework/shopify/api/index.ts @@ -1,5 +1,8 @@ import type { CommerceAPIConfig } from '@commerce/api' -import { SHOPIFY_CHECKOUT_ID_COOKIE } from '@framework/const' +import { + SHOPIFY_CHECKOUT_ID_COOKIE, + SHOPIFY_CUSTOMER_TOKEN_COOKIE, +} from '@framework/const' import fetchGraphqlApi from '../utils/fetch-graphql-api' export interface ShopifyConfig extends CommerceAPIConfig {} @@ -46,7 +49,7 @@ const config = new Config({ cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, cartCookieMaxAge: ONE_DAY * 30, fetch: fetchGraphqlApi, - customerCookie: 'SHOP_TOKEN', + customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE, }) export function getConfig(userConfig?: Partial<ShopifyConfig>) { diff --git a/framework/shopify/auth/use-login.tsx b/framework/shopify/auth/use-login.tsx index ee447962e..516d0b14a 100644 --- a/framework/shopify/auth/use-login.tsx +++ b/framework/shopify/auth/use-login.tsx @@ -1,21 +1,37 @@ import { useCallback } from 'react' import type { HookFetcher } from '@commerce/utils/types' -import { CommerceError } from '@commerce/utils/errors' +import { CommerceError, ValidationError } from '@commerce/utils/errors' import useCommerceLogin from '@commerce/use-login' import useCustomer from '../customer/use-customer' -import createCustomerAccessTokenMutation from '../utils/mutations/customer-acces-token-create' +import createCustomerAccessTokenMutation from '../utils/mutations/customer-access-token-create' import { CustomerAccessTokenCreateInput } from '@framework/schema' +import { setCustomerToken } from '@framework/utils/customer-token' const defaultOpts = { query: createCustomerAccessTokenMutation, } +const getErrorMessage = ({ + code, + message, +}: { + code: string + message: string +}) => { + switch (code) { + case 'UNIDENTIFIED_CUSTOMER': + message = 'Cannot find an account that matches the provided credentials' + break + } + return message +} + export const fetcher: HookFetcher<null, CustomerAccessTokenCreateInput> = ( options, - { email, password }, + input, fetch ) => { - if (!(email && password)) { + if (!(input.email && input.password)) { throw new CommerceError({ message: 'A first name, last name, email and password are required to login', @@ -25,7 +41,25 @@ export const fetcher: HookFetcher<null, CustomerAccessTokenCreateInput> = ( return fetch({ ...defaultOpts, ...options, - body: { email, password }, + variables: { input }, + }).then((data) => { + const response = data?.customerAccessTokenCreate + const errors = response?.customerUserErrors + + if (errors && errors.length) { + throw new ValidationError({ + message: getErrorMessage(errors[0]), + }) + } + + const customerAccessToken = response?.customerAccessToken + const accessToken = customerAccessToken?.accessToken + + if (accessToken) { + setCustomerToken(accessToken) + } + + return customerAccessToken }) } diff --git a/framework/shopify/auth/use-logout.tsx b/framework/shopify/auth/use-logout.tsx index 75f067c3a..f8bd55579 100644 --- a/framework/shopify/auth/use-logout.tsx +++ b/framework/shopify/auth/use-logout.tsx @@ -1,13 +1,45 @@ import { useCallback } from 'react' +import type { HookFetcher } from '@commerce/utils/types' +import useCommerceLogout from '@commerce/use-logout' +import useCustomer from '../customer/use-customer' +import customerAccessTokenDeleteMutation from '@framework/utils/mutations/customer-access-token-delete' +import { + getCustomerToken, + setCustomerToken, +} from '@framework/utils/customer-token' -export function emptyHook() { - const useEmptyHook = async (options = {}) => { - return useCallback(async function () { - return Promise.resolve() - }, []) - } - - return useEmptyHook +const defaultOpts = { + query: customerAccessTokenDeleteMutation, } -export default emptyHook +export const fetcher: HookFetcher<null> = (options, _, fetch) => { + return fetch({ + ...defaultOpts, + ...options, + variables: { + customerAccessToken: getCustomerToken(), + }, + }).then((d) => setCustomerToken(null)) +} + +export function extendHook(customFetcher: typeof fetcher) { + const useLogout = () => { + const { mutate } = useCustomer() + const fn = useCommerceLogout<null>(defaultOpts, customFetcher) + + return useCallback( + async function login() { + const data = await fn(null) + await mutate(null, false) + return data + }, + [fn] + ) + } + + useLogout.extend = extendHook + + return useLogout +} + +export default extendHook(fetcher) diff --git a/framework/shopify/auth/use-signup.tsx b/framework/shopify/auth/use-signup.tsx index 75f067c3a..7184554bb 100644 --- a/framework/shopify/auth/use-signup.tsx +++ b/framework/shopify/auth/use-signup.tsx @@ -1,13 +1,57 @@ import { useCallback } from 'react' +import type { HookFetcher } from '@commerce/utils/types' +import { CommerceError } from '@commerce/utils/errors' +import useCommerceSignup from '@commerce/use-signup' +import useCustomer from '../customer/use-customer' +import customerCreateMutation from '@framework/utils/mutations/customer-create' +import { CustomerCreateInput } from '@framework/schema' -export function emptyHook() { - const useEmptyHook = async (options = {}) => { - return useCallback(async function () { - return Promise.resolve() - }, []) - } - - return useEmptyHook +const defaultOpts = { + query: customerCreateMutation, } -export default emptyHook +export const fetcher: HookFetcher<null, CustomerCreateInput> = ( + options, + input, + fetch +) => { + if (!(input.firstName && input.lastName && input.email && input.password)) { + throw new CommerceError({ + message: + 'A first name, last name, email and password are required to signup', + }) + } + + return fetch({ + ...defaultOpts, + ...options, + variables: { input }, + }).then((data) => { + return data + }) +} + +export function extendHook(customFetcher: typeof fetcher) { + const useSignup = () => { + const { revalidate } = useCustomer() + const fn = useCommerceSignup<null, CustomerCreateInput>( + defaultOpts, + customFetcher + ) + + return useCallback( + async function signup(input: CustomerCreateInput) { + const data = await fn(input) + await revalidate() + return data + }, + [fn] + ) + } + + useSignup.extend = extendHook + + return useSignup +} + +export default extendHook(fetcher) diff --git a/framework/shopify/config.ts b/framework/shopify/config.ts index 735090185..66f2b4bc8 100644 --- a/framework/shopify/config.ts +++ b/framework/shopify/config.ts @@ -32,7 +32,7 @@ const shopifyConfig: ShopifyConfig = { locale: 'en-us', cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, storeDomain: STORE_DOMAIN, - async fetcher({ method = 'POST', variables, query }) { + async fetcher({ method = 'POST', query, variables }) { const res = await fetch(API_URL, { method, body: JSON.stringify({ query, variables }), diff --git a/framework/shopify/const.ts b/framework/shopify/const.ts index 0c957632c..aa8291c02 100644 --- a/framework/shopify/const.ts +++ b/framework/shopify/const.ts @@ -1,2 +1,3 @@ export const SHOPIFY_CHECKOUT_ID_COOKIE = 'shopify_checkoutId' export const SHOPIFY_CHECKOUT_URL_COOKIE = 'shopify_checkoutUrl' +export const SHOPIFY_CUSTOMER_TOKEN_COOKIE = 'shopify_customerToken' diff --git a/framework/shopify/customer/get-customer-id.ts b/framework/shopify/customer/get-customer-id.ts new file mode 100644 index 000000000..39a9e2572 --- /dev/null +++ b/framework/shopify/customer/get-customer-id.ts @@ -0,0 +1,24 @@ +import { getConfig, ShopifyConfig } from '@framework/api' +import getCustomerIdQuery from '@framework/utils/queries/get-customer-id-query' +import Cookies from 'js-cookie' + +async function getCustomerId({ + customerToken: customerAccesToken, + config, +}: { + customerToken: string + config?: ShopifyConfig +}): Promise<number | undefined> { + config = getConfig(config) + + const { data } = await config.fetch(getCustomerIdQuery, { + variables: { + customerAccesToken: + customerAccesToken || Cookies.get(config.customerCookie), + }, + }) + + return data?.customer?.id +} + +export default getCustomerId diff --git a/framework/shopify/customer/use-customer.tsx b/framework/shopify/customer/use-customer.tsx index d46879b91..6f956d2c2 100644 --- a/framework/shopify/customer/use-customer.tsx +++ b/framework/shopify/customer/use-customer.tsx @@ -1,23 +1,29 @@ import type { HookFetcher } from '@commerce/utils/types' import type { SwrOptions } from '@commerce/utils/use-data' import useCommerceCustomer from '@commerce/use-customer' +import getCustomerQuery from '@framework/utils/queries/get-customer-query' +import { getCustomerToken } from '@framework/utils/customer-token' const defaultOpts = { - query: '/api/bigcommerce/customers', + query: getCustomerQuery, } -export const fetcher: HookFetcher<Customer | null> = async ( - options, - _, - fetch -) => { - const data = await fetch<CustomerData | null>({ ...defaultOpts, ...options }) - return data?.customer ?? null +export const fetcher: HookFetcher<any | null> = async (options, _, fetch) => { + const customerAccessToken = getCustomerToken() + if (customerAccessToken) { + const data = await fetch<any | null>({ + ...defaultOpts, + ...options, + variables: { customerAccessToken }, + }) + return data?.customer ?? null + } + return null } export function extendHook( customFetcher: typeof fetcher, - swrOptions?: SwrOptions<Customer | null> + swrOptions?: SwrOptions<any | null> ) { const useCustomer = () => { return useCommerceCustomer(defaultOpts, [], customFetcher, { diff --git a/framework/shopify/lib/normalize.ts b/framework/shopify/lib/normalize.ts index eeb637a48..c39331cd9 100644 --- a/framework/shopify/lib/normalize.ts +++ b/framework/shopify/lib/normalize.ts @@ -111,7 +111,7 @@ function normalizeLineItem({ variant: { id: String(variant?.id), sku: variant?.sku ?? '', - name: variant?.title, + name: variant?.title!, image: { url: variant?.image?.originalSrc, }, diff --git a/framework/shopify/product/get-all-collections.ts b/framework/shopify/product/get-all-collections.ts index bf3fee392..b63adf159 100644 --- a/framework/shopify/product/get-all-collections.ts +++ b/framework/shopify/product/get-all-collections.ts @@ -11,7 +11,7 @@ const getAllCollections = async (options?: { config = getConfig(config) const { data } = await config.fetch(getAllCollectionsQuery, { variables }) - const edges = data.collections?.edges ?? [] + const edges = data?.collections?.edges ?? [] const categories = edges.map( ({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({ diff --git a/framework/shopify/product/get-all-product-paths.ts b/framework/shopify/product/get-all-product-paths.ts index 3627321a8..e632219f7 100644 --- a/framework/shopify/product/get-all-product-paths.ts +++ b/framework/shopify/product/get-all-product-paths.ts @@ -18,12 +18,12 @@ const getAllProductPaths = async (options?: { variables, }) - const edges = data.products?.edges - const productInfo = data.products?.productInfo + const edges = data?.products?.edges + const productInfo = data?.products?.productInfo const hasNextPage = productInfo?.hasNextPage return { - products: edges.map(({ node: { handle } }: ProductEdge) => ({ + products: edges?.map(({ node: { handle } }: ProductEdge) => ({ node: { path: `/${handle}`, }, diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx index ee59c9448..51c390bba 100644 --- a/framework/shopify/product/use-search.tsx +++ b/framework/shopify/product/use-search.tsx @@ -61,7 +61,6 @@ export function extendHook( const response = useCommerceSearch( { query: getAllProductsQuery, - method: 'POST', }, [ ['search', input.search], diff --git a/framework/shopify/utils/customer-token.ts b/framework/shopify/utils/customer-token.ts new file mode 100644 index 000000000..c78a07c44 --- /dev/null +++ b/framework/shopify/utils/customer-token.ts @@ -0,0 +1,12 @@ +import Cookies from 'js-cookie' +import { SHOPIFY_CUSTOMER_TOKEN_COOKIE } from '@framework/const' + +export const getCustomerToken = () => Cookies.get(SHOPIFY_CUSTOMER_TOKEN_COOKIE) + +export const setCustomerToken = (token: string | null, options?: any) => { + if (!token) { + Cookies.remove(SHOPIFY_CUSTOMER_TOKEN_COOKIE) + } else { + Cookies.set(SHOPIFY_CUSTOMER_TOKEN_COOKIE, token, options) + } +} diff --git a/framework/shopify/utils/mutations/associate-customer-with-checkout.ts b/framework/shopify/utils/mutations/associate-customer-with-checkout.ts new file mode 100644 index 000000000..6b1350e05 --- /dev/null +++ b/framework/shopify/utils/mutations/associate-customer-with-checkout.ts @@ -0,0 +1,18 @@ +const associateCustomerWithCheckoutMutation = /* GraphQl */ ` +mutation associateCustomerWithCheckout($checkoutId: ID!, $customerAccessToken: String!) { + checkoutCustomerAssociateV2(checkoutId: $checkoutId, customerAccessToken: $customerAccessToken) { + checkout { + id + } + checkoutUserErrors { + code + field + message + } + customer { + id + } + } + } +` +export default associateCustomerWithCheckoutMutation diff --git a/framework/shopify/utils/mutations/customer-acces-token-create.ts b/framework/shopify/utils/mutations/customer-access-token-create.ts similarity index 100% rename from framework/shopify/utils/mutations/customer-acces-token-create.ts rename to framework/shopify/utils/mutations/customer-access-token-create.ts diff --git a/framework/shopify/utils/mutations/customer-access-token-delete.ts b/framework/shopify/utils/mutations/customer-access-token-delete.ts new file mode 100644 index 000000000..c46eff1e5 --- /dev/null +++ b/framework/shopify/utils/mutations/customer-access-token-delete.ts @@ -0,0 +1,14 @@ +const customerAccessTokenDeleteMutation = /* GraphQL */ ` + mutation customerAccessTokenDelete($customerAccessToken: String!) { + customerAccessTokenDelete(customerAccessToken: $customerAccessToken) { + deletedAccessToken + deletedCustomerAccessTokenId + userErrors { + field + message + } + } + } +` + +export default customerAccessTokenDeleteMutation diff --git a/framework/shopify/utils/mutations/index.ts b/framework/shopify/utils/mutations/index.ts index 14f82a476..fe1beb82d 100644 --- a/framework/shopify/utils/mutations/index.ts +++ b/framework/shopify/utils/mutations/index.ts @@ -3,3 +3,5 @@ export { default as checkoutCreateMutation } from './checkout-create' export { default as checkoutLineItemAddMutation } from './checkout-line-item-add' export { default as checkoutLineItemUpdateMutation } from './checkout-create' export { default as checkoutLineItemRemoveMutation } from './checkout-line-item-remove' +export { default as customerAccessTokenCreateMutation } from './customer-access-token-create' +export { default as customerAccessTokenDeleteMutation } from './customer-access-token-delete' diff --git a/framework/shopify/utils/queries/get-customer-id-query.ts b/framework/shopify/utils/queries/get-customer-id-query.ts new file mode 100644 index 000000000..076ceb10b --- /dev/null +++ b/framework/shopify/utils/queries/get-customer-id-query.ts @@ -0,0 +1,8 @@ +export const getCustomerQuery = /* GraphQL */ ` + query getCustomerId($customerAccessToken: String!) { + customer(customerAccessToken: $customerAccessToken) { + id + } + } +` +export default getCustomerQuery diff --git a/framework/shopify/utils/queries/get-customer-query.ts b/framework/shopify/utils/queries/get-customer-query.ts new file mode 100644 index 000000000..87e37e68d --- /dev/null +++ b/framework/shopify/utils/queries/get-customer-query.ts @@ -0,0 +1,16 @@ +export const getCustomerQuery = /* GraphQL */ ` + query getCustomer($customerAccessToken: String!) { + customer(customerAccessToken: $customerAccessToken) { + id + firstName + lastName + displayName + email + phone + tags + acceptsMarketing + createdAt + } + } +` +export default getCustomerQuery diff --git a/framework/shopify/utils/queries/index.ts b/framework/shopify/utils/queries/index.ts index a1282f1e1..e0506cb5a 100644 --- a/framework/shopify/utils/queries/index.ts +++ b/framework/shopify/utils/queries/index.ts @@ -4,3 +4,4 @@ export { default as getAllProductsQuery } from './get-all-products-query' export { default as getAllProductsPathtsQuery } from './get-all-products-paths-query' export { default as getCheckoutQuery } from './get-checkout-query' export { default as getAllPagesQuery } from './get-all-pages-query' +export { default as getCustomerQuery } from './get-checkout-query' From 41b14e55c3eeb01935c05179a1d8d811fef4a7e6 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Fri, 5 Feb 2021 11:06:03 +0200 Subject: [PATCH 084/221] Automatic login after sign-up --- framework/shopify/auth/use-login.tsx | 37 ++------------------ framework/shopify/auth/use-signup.tsx | 22 ++++++++++-- framework/shopify/utils/handle-login.ts | 39 ++++++++++++++++++++++ framework/shopify/utils/mutations/index.ts | 1 + 4 files changed, 61 insertions(+), 38 deletions(-) create mode 100644 framework/shopify/utils/handle-login.ts diff --git a/framework/shopify/auth/use-login.tsx b/framework/shopify/auth/use-login.tsx index 516d0b14a..4811bbe8d 100644 --- a/framework/shopify/auth/use-login.tsx +++ b/framework/shopify/auth/use-login.tsx @@ -5,27 +5,12 @@ import useCommerceLogin from '@commerce/use-login' import useCustomer from '../customer/use-customer' import createCustomerAccessTokenMutation from '../utils/mutations/customer-access-token-create' import { CustomerAccessTokenCreateInput } from '@framework/schema' -import { setCustomerToken } from '@framework/utils/customer-token' +import handleLogin from '@framework/utils/handle-login' const defaultOpts = { query: createCustomerAccessTokenMutation, } -const getErrorMessage = ({ - code, - message, -}: { - code: string - message: string -}) => { - switch (code) { - case 'UNIDENTIFIED_CUSTOMER': - message = 'Cannot find an account that matches the provided credentials' - break - } - return message -} - export const fetcher: HookFetcher<null, CustomerAccessTokenCreateInput> = ( options, input, @@ -42,25 +27,7 @@ export const fetcher: HookFetcher<null, CustomerAccessTokenCreateInput> = ( ...defaultOpts, ...options, variables: { input }, - }).then((data) => { - const response = data?.customerAccessTokenCreate - const errors = response?.customerUserErrors - - if (errors && errors.length) { - throw new ValidationError({ - message: getErrorMessage(errors[0]), - }) - } - - const customerAccessToken = response?.customerAccessToken - const accessToken = customerAccessToken?.accessToken - - if (accessToken) { - setCustomerToken(accessToken) - } - - return customerAccessToken - }) + }).then(handleLogin) } export function extendHook(customFetcher: typeof fetcher) { diff --git a/framework/shopify/auth/use-signup.tsx b/framework/shopify/auth/use-signup.tsx index 7184554bb..c1b40e60c 100644 --- a/framework/shopify/auth/use-signup.tsx +++ b/framework/shopify/auth/use-signup.tsx @@ -3,9 +3,14 @@ import type { HookFetcher } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' import useCommerceSignup from '@commerce/use-signup' import useCustomer from '../customer/use-customer' -import customerCreateMutation from '@framework/utils/mutations/customer-create' import { CustomerCreateInput } from '@framework/schema' +import { + customerCreateMutation, + customerAccessTokenCreateMutation, +} from '@framework/utils/mutations' +import handleLogin from '@framework/utils/handle-login' + const defaultOpts = { query: customerCreateMutation, } @@ -21,12 +26,23 @@ export const fetcher: HookFetcher<null, CustomerCreateInput> = ( 'A first name, last name, email and password are required to signup', }) } - return fetch({ ...defaultOpts, ...options, variables: { input }, - }).then((data) => { + }).then(async (data) => { + try { + const loginData = await fetch({ + query: customerAccessTokenCreateMutation, + variables: { + input: { + email: input.email, + password: input.password, + }, + }, + }) + handleLogin(loginData) + } catch (error) {} return data }) } diff --git a/framework/shopify/utils/handle-login.ts b/framework/shopify/utils/handle-login.ts new file mode 100644 index 000000000..81a016240 --- /dev/null +++ b/framework/shopify/utils/handle-login.ts @@ -0,0 +1,39 @@ +import { ValidationError } from '@commerce/utils/errors' +import { setCustomerToken } from './customer-token' + +const getErrorMessage = ({ + code, + message, +}: { + code: string + message: string +}) => { + switch (code) { + case 'UNIDENTIFIED_CUSTOMER': + message = 'Cannot find an account that matches the provided credentials' + break + } + return message +} + +const handleLogin = (data: any) => { + const response = data?.customerAccessTokenCreate + const errors = response?.customerUserErrors + + if (errors && errors.length) { + throw new ValidationError({ + message: errors[0], + }) + } + + const customerAccessToken = response?.customerAccessToken + const accessToken = customerAccessToken?.accessToken + + if (accessToken) { + setCustomerToken(accessToken) + } + + return customerAccessToken +} + +export default handleLogin diff --git a/framework/shopify/utils/mutations/index.ts b/framework/shopify/utils/mutations/index.ts index fe1beb82d..5ccf5b1dd 100644 --- a/framework/shopify/utils/mutations/index.ts +++ b/framework/shopify/utils/mutations/index.ts @@ -3,5 +3,6 @@ export { default as checkoutCreateMutation } from './checkout-create' export { default as checkoutLineItemAddMutation } from './checkout-line-item-add' export { default as checkoutLineItemUpdateMutation } from './checkout-create' export { default as checkoutLineItemRemoveMutation } from './checkout-line-item-remove' +export { default as customerCreateMutation } from './customer-create' export { default as customerAccessTokenCreateMutation } from './customer-access-token-create' export { default as customerAccessTokenDeleteMutation } from './customer-access-token-delete' From 1384a884403ad39acf9fa64154e862caba884ff6 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Fri, 5 Feb 2021 12:48:22 +0200 Subject: [PATCH 085/221] Update handle-login.ts --- framework/shopify/utils/handle-login.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/shopify/utils/handle-login.ts b/framework/shopify/utils/handle-login.ts index 81a016240..70725b75b 100644 --- a/framework/shopify/utils/handle-login.ts +++ b/framework/shopify/utils/handle-login.ts @@ -22,7 +22,7 @@ const handleLogin = (data: any) => { if (errors && errors.length) { throw new ValidationError({ - message: errors[0], + message: getErrorMessage(errors[0]), }) } From 2c9b8b100dff224fe7401cd8aa951d9c04c12d89 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 5 Feb 2021 17:44:10 -0500 Subject: [PATCH 086/221] MOving stuff around and adding temporal new files --- framework/bigcommerce/index.tsx | 63 +++++++++++++++++++- framework/commerce/cart/use-cart-2.tsx | 28 +++++++++ framework/commerce/cart/use-fake.tsx | 42 ++++++++++++++ framework/commerce/index.tsx | 53 ++++++++++++++--- framework/commerce/utils/types.ts | 7 +++ framework/commerce/utils/use-data-2.ts | 79 ++++++++++++++++++++++++++ 6 files changed, 261 insertions(+), 11 deletions(-) create mode 100644 framework/commerce/cart/use-cart-2.tsx create mode 100644 framework/commerce/cart/use-fake.tsx create mode 100644 framework/commerce/utils/use-data-2.ts diff --git a/framework/bigcommerce/index.tsx b/framework/bigcommerce/index.tsx index a4c9fffa5..268b0bd02 100644 --- a/framework/bigcommerce/index.tsx +++ b/framework/bigcommerce/index.tsx @@ -1,11 +1,16 @@ import { ReactNode } from 'react' import * as React from 'react' +import { Fetcher } from '@commerce/utils/types' import { CommerceConfig, CommerceProvider as CoreCommerceProvider, useCommerce as useCoreCommerce, + HookHandler, } from '@commerce' import { FetcherError } from '@commerce/utils/errors' +import type { CartInput } from '@commerce/cart/use-cart' +import { normalizeCart } from './lib/normalize' +import { Cart } from './types' async function getText(res: Response) { try { @@ -23,6 +28,57 @@ async function getError(res: Response) { return new FetcherError({ message: await getText(res), status: res.status }) } +const fetcher: Fetcher<any> = async ({ + url, + method = 'GET', + variables, + body: bodyObj, +}) => { + const hasBody = Boolean(variables || bodyObj) + const body = hasBody + ? JSON.stringify(variables ? { variables } : bodyObj) + : undefined + const headers = hasBody ? { 'Content-Type': 'application/json' } : undefined + const res = await fetch(url!, { method, body, headers }) + + if (res.ok) { + const { data } = await res.json() + return data + } + + throw await getError(res) +} + +const useCart: HookHandler<Cart, CartInput> = { + fetchOptions: { + url: '/api/bigcommerce/cart', + method: 'GET', + }, + fetcher(context) { + return undefined as any + }, + onResponse(response) { + return Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, + }, + }) + }, +} + +export const bigcommerceProvider = { + locale: 'en-us', + cartCookie: 'bc_cartId', + fetcher, + cartNormalizer: normalizeCart, + cart: { useCart }, +} + +export type BigcommerceProvider = typeof bigcommerceProvider + export const bigcommerceConfig: CommerceConfig = { locale: 'en-us', cartCookie: 'bc_cartId', @@ -52,10 +108,13 @@ export type BigcommerceProps = { export function CommerceProvider({ children, ...config }: BigcommerceProps) { return ( - <CoreCommerceProvider config={{ ...bigcommerceConfig, ...config }}> + <CoreCommerceProvider + provider={bigcommerceProvider} + config={{ ...bigcommerceConfig, ...config }} + > {children} </CoreCommerceProvider> ) } -export const useCommerce = () => useCoreCommerce() +export const useCommerce = () => useCoreCommerce<BigcommerceProvider>() diff --git a/framework/commerce/cart/use-cart-2.tsx b/framework/commerce/cart/use-cart-2.tsx new file mode 100644 index 000000000..cfeb85c87 --- /dev/null +++ b/framework/commerce/cart/use-cart-2.tsx @@ -0,0 +1,28 @@ +import Cookies from 'js-cookie' +import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types' +import useData, { ResponseState, SwrOptions } from '../utils/use-data' +import type { Cart } from '../types' +import { useCommerce } from '..' + +export type CartResponse<Data> = ResponseState<Data> & { isEmpty?: boolean } + +// Input expected by the `useCart` hook +export type CartInput = { + cartId?: Cart['id'] +} + +export default function useCart<Data extends Cart | null>( + options: HookFetcherOptions, + input: HookInput, + fetcherFn: HookFetcher<Data, CartInput>, + swrOptions?: SwrOptions<Data, CartInput> +): CartResponse<Data> { + const { providerRef, cartCookie } = useCommerce() + const fetcher: typeof fetcherFn = (options, input, fetch) => { + input.cartId = Cookies.get(cartCookie) + return fetcherFn(options, input, fetch) + } + const response = useData(options, input, fetcher, swrOptions) + + return response +} diff --git a/framework/commerce/cart/use-fake.tsx b/framework/commerce/cart/use-fake.tsx new file mode 100644 index 000000000..324e1c4d9 --- /dev/null +++ b/framework/commerce/cart/use-fake.tsx @@ -0,0 +1,42 @@ +import { useMemo } from 'react' +import Cookies from 'js-cookie' +import type { Cart } from '../types' +import type { HookFetcherFn } from '../utils/types' +import useData from '../utils/use-data-2' +import { Provider, useCommerce } from '..' + +// Input expected by the `useCart` hook +export type CartInput = { + cartId?: Cart['id'] +} + +const fetcher: HookFetcherFn<Cart | null, CartInput> = async ({ + options, + input: { cartId }, + fetch, + normalize, +}) => { + const data = cartId ? await fetch({ ...options }) : null + return data && normalize ? normalize(data) : data +} + +export default function useFake<P extends Provider>() { + const { providerRef, cartCookie } = useCommerce<P>() + + const provider = providerRef.current + const opts = provider.cart?.useCart + const options = opts?.fetchOptions ?? {} + const fetcherFn = opts?.fetcher ?? fetcher + const wrapper: typeof fetcher = (context) => { + context.input.cartId = Cookies.get(cartCookie) + return fetcherFn(context) + } + + const response = useData(options, [], wrapper, opts?.swrOptions) + const memoizedResponse = useMemo( + () => (opts?.onResponse ? opts.onResponse(response) : response), + [response] + ) + + return memoizedResponse +} diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index 506502ea2..91a1be2e0 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -7,36 +7,71 @@ import { useRef, } from 'react' import * as React from 'react' -import { Fetcher } from './utils/types' +import { Fetcher, HookFetcherFn, HookFetcherOptions } from './utils/types' +import { Cart } from './types' +import type { ResponseState, SwrOptions } from './utils/use-data' +import type { CartInput } from './cart/use-cart' -const Commerce = createContext<CommerceContextValue | {}>({}) +const Commerce = createContext<CommerceContextValue<any> | {}>({}) -export type CommerceProps = { +export type Provider = CommerceConfig & { + cart?: { + useCart?: HookHandler<Cart, CartInput> + } + cartNormalizer(data: any): Cart +} + +export type HookHandler<Data, Input, Result = any, Body = any> = { + swrOptions?: SwrOptions<Data | null, Input, Result> + onResponse?(response: ResponseState<Data | null>): ResponseState<Data | null> + onMutation?: any + fetchOptions?: HookFetcherOptions +} & ( + | // TODO: Maybe the normalizer is not required if it's used by the API route directly? + { + fetcher: HookFetcherFn<Data | null, Input, Result, Body> + normalizer?(data: Result): Data + } + | { + fetcher?: never + normalizer(data: Result): Data + } +) + +export type CommerceProps<P extends Provider> = { children?: ReactNode + provider: P config: CommerceConfig } export type CommerceConfig = { fetcher: Fetcher<any> } & Omit< - CommerceContextValue, - 'fetcherRef' + CommerceContextValue<any>, + 'providerRef' | 'fetcherRef' > -export type CommerceContextValue = { +export type CommerceContextValue<P extends Provider> = { + providerRef: MutableRefObject<P> fetcherRef: MutableRefObject<Fetcher<any>> locale: string cartCookie: string } -export function CommerceProvider({ children, config }: CommerceProps) { +export function CommerceProvider<P extends Provider>({ + provider, + children, + config, +}: CommerceProps<P>) { if (!config) { throw new Error('CommerceProvider requires a valid config object') } + const providerRef = useRef(provider) const fetcherRef = useRef(config.fetcher) // Because the config is an object, if the parent re-renders this provider // will re-render every consumer unless we memoize the config const cfg = useMemo( () => ({ + providerRef, fetcherRef, locale: config.locale, cartCookie: config.cartCookie, @@ -47,6 +82,6 @@ export function CommerceProvider({ children, config }: CommerceProps) { return <Commerce.Provider value={cfg}>{children}</Commerce.Provider> } -export function useCommerce<T extends CommerceContextValue>() { - return useContext(Commerce) as T +export function useCommerce<P extends Provider>() { + return useContext(Commerce) as CommerceContextValue<P> } diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 010205f62..3428b5194 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -15,6 +15,13 @@ export type HookFetcher<Data, Input = null, Result = any> = ( fetch: <T = Result, Body = any>(options: FetcherOptions<Body>) => Promise<T> ) => Data | Promise<Data> +export type HookFetcherFn<Data, Input, Result = any, Body = any> = (context: { + options: HookFetcherOptions | null + input: Input + fetch: <T = Result, B = Body>(options: FetcherOptions<B>) => Promise<T> + normalize?(data: Result): Data +}) => Data | Promise<Data> + export type HookFetcherOptions = { query?: string url?: string diff --git a/framework/commerce/utils/use-data-2.ts b/framework/commerce/utils/use-data-2.ts new file mode 100644 index 000000000..69eb223b0 --- /dev/null +++ b/framework/commerce/utils/use-data-2.ts @@ -0,0 +1,79 @@ +import useSWR, { ConfigInterface, responseInterface } from 'swr' +import type { + HookInput, + HookFetcher, + HookFetcherOptions, + HookFetcherFn, +} from './types' +import defineProperty from './define-property' +import { CommerceError } from './errors' +import { useCommerce } from '..' + +export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< + Data, + CommerceError, + HookFetcher<Data, Input, Result> +> + +export type ResponseState<Result> = responseInterface<Result, CommerceError> & { + isLoading: boolean +} + +export type UseData = <Data = any, Input = null, Result = any>( + options: HookFetcherOptions | (() => HookFetcherOptions | null), + input: HookInput, + fetcherFn: HookFetcherFn<Data, Input, Result>, + swrOptions?: SwrOptions<Data, Input, Result> +) => ResponseState<Data> + +const useData: UseData = (options, input, fetcherFn, swrOptions) => { + const { fetcherRef } = useCommerce() + const fetcher = async ( + url?: string, + query?: string, + method?: string, + ...args: any[] + ) => { + try { + return await fetcherFn({ + options: { url, query, method }, + // Transform the input array into an object + input: args.reduce((obj, val, i) => { + obj[input[i][0]!] = val + return obj + }, {}), + fetch: fetcherRef.current, + }) + } catch (error) { + // SWR will not log errors, but any error that's not an instance + // of CommerceError is not welcomed by this hook + if (!(error instanceof CommerceError)) { + console.error(error) + } + throw error + } + } + const response = useSWR( + () => { + const opts = typeof options === 'function' ? options() : options + return opts + ? [opts.url, opts.query, opts.method, ...input.map((e) => e[1])] + : null + }, + fetcher, + swrOptions + ) + + if (!('isLoading' in response)) { + defineProperty(response, 'isLoading', { + get() { + return response.data === undefined + }, + enumerable: true, + }) + } + + return response +} + +export default useData From 0dad4ddedbf0bff2d0b5800ca469fda0073889ea Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Mon, 8 Feb 2021 09:15:20 +0200 Subject: [PATCH 087/221] changes --- framework/shopify/api/checkout/index.ts | 2 +- framework/shopify/api/index.ts | 29 +++----- .../{ => api}/utils/fetch-graphql-api.ts | 17 ++--- framework/shopify/{ => api}/utils/fetch.ts | 1 - framework/shopify/cart/use-add-item.tsx | 3 +- framework/shopify/cart/use-cart.tsx | 2 +- .../shopify/cart/utils/checkout-create.ts | 2 +- .../shopify/cart/utils/checkout-to-cart.ts | 11 +--- framework/shopify/config.ts | 66 +++++++------------ framework/shopify/const.ts | 3 - framework/shopify/index.tsx | 3 +- framework/shopify/utils/customer-token.ts | 2 +- framework/shopify/utils/get-checkout-id.ts | 2 +- .../shopify/utils/handle-fetch-response.ts | 27 ++++++++ 14 files changed, 75 insertions(+), 95 deletions(-) rename framework/shopify/{ => api}/utils/fetch-graphql-api.ts (69%) rename framework/shopify/{ => api}/utils/fetch.ts (98%) delete mode 100644 framework/shopify/const.ts create mode 100644 framework/shopify/utils/handle-fetch-response.ts diff --git a/framework/shopify/api/checkout/index.ts b/framework/shopify/api/checkout/index.ts index 638dc20ee..344be62d6 100644 --- a/framework/shopify/api/checkout/index.ts +++ b/framework/shopify/api/checkout/index.ts @@ -7,7 +7,7 @@ import { SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CHECKOUT_URL_COOKIE, SHOPIFY_CUSTOMER_TOKEN_COOKIE, -} from '@framework/const' +} from '@framework/config' import { getConfig } from '..' import associateCustomerWithCheckoutMutation from '@framework/utils/mutations/associate-customer-with-checkout' diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts index 273c43c8b..d246829ff 100644 --- a/framework/shopify/api/index.ts +++ b/framework/shopify/api/index.ts @@ -1,27 +1,16 @@ import type { CommerceAPIConfig } from '@commerce/api' + import { + API_URL, + API_TOKEN, SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CUSTOMER_TOKEN_COOKIE, -} from '@framework/const' -import fetchGraphqlApi from '../utils/fetch-graphql-api' +} from '@framework/config' + +import fetchGraphqlApi from './utils/fetch-graphql-api' export interface ShopifyConfig extends CommerceAPIConfig {} -const API_URL = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN -const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN - -if (!API_URL) { - throw new Error( - `The environment variable NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN is missing and it's required to access your store` - ) -} - -if (!API_TOKEN) { - throw new Error( - `The environment variable NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store` - ) -} - export class Config { private config: ShopifyConfig @@ -41,13 +30,11 @@ export class Config { } } -const ONE_DAY = 60 * 60 * 24 - const config = new Config({ commerceUrl: API_URL, - apiToken: API_TOKEN, + apiToken: API_TOKEN!, cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, - cartCookieMaxAge: ONE_DAY * 30, + cartCookieMaxAge: 60 * 60 * 24 * 30, fetch: fetchGraphqlApi, customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE, }) diff --git a/framework/shopify/utils/fetch-graphql-api.ts b/framework/shopify/api/utils/fetch-graphql-api.ts similarity index 69% rename from framework/shopify/utils/fetch-graphql-api.ts rename to framework/shopify/api/utils/fetch-graphql-api.ts index b552a7f18..8ff4b27b7 100644 --- a/framework/shopify/utils/fetch-graphql-api.ts +++ b/framework/shopify/api/utils/fetch-graphql-api.ts @@ -1,10 +1,10 @@ import type { GraphQLFetcher } from '@commerce/api' -import { FetcherError } from '@commerce/utils/errors' import fetch from './fetch' -import { STORE_DOMAIN, API_URL, API_TOKEN } from '../config' +import { API_URL, API_TOKEN } from '../../config' +import { getError } from '@framework/utils/handle-fetch-response' -if (!STORE_DOMAIN) { +if (!API_URL) { throw new Error( `The environment variable NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN is missing and it's required to access your store` ) @@ -35,15 +35,12 @@ const fetchGraphqlApi: GraphQLFetcher = async ( }), }) - const json = await res.json() + const { data, errors, status } = await res.json() - if (json.errors) { - throw new FetcherError({ - errors: json.errors ?? [{ message: 'Failed to fetch Shopify API' }], - status: res.status, - }) + if (errors) { + throw getError(errors, status) } - return { data: json.data, res } + return { data: data, res } } export default fetchGraphqlApi diff --git a/framework/shopify/utils/fetch.ts b/framework/shopify/api/utils/fetch.ts similarity index 98% rename from framework/shopify/utils/fetch.ts rename to framework/shopify/api/utils/fetch.ts index 9d9fff3ed..0b8367102 100644 --- a/framework/shopify/utils/fetch.ts +++ b/framework/shopify/api/utils/fetch.ts @@ -1,3 +1,2 @@ import zeitFetch from '@vercel/fetch' - export default zeitFetch() diff --git a/framework/shopify/cart/use-add-item.tsx b/framework/shopify/cart/use-add-item.tsx index 313bffde7..b5b34adc3 100644 --- a/framework/shopify/cart/use-add-item.tsx +++ b/framework/shopify/cart/use-add-item.tsx @@ -1,5 +1,4 @@ import { useCallback } from 'react' -import { CommerceError } from '@commerce/utils/errors' import useCart from './use-cart' import useCartAddItem, { AddItemInput as UseAddItemInput, @@ -47,7 +46,7 @@ export function extendHook(customFetcher: typeof fetcher) { quantity: input.quantity ?? 1, }, ], - checkoutId: getCheckoutId(cart?.id), + checkoutId: getCheckoutId(cart?.id)!, }) await mutate(data, false) return data diff --git a/framework/shopify/cart/use-cart.tsx b/framework/shopify/cart/use-cart.tsx index a4b92786f..a04c30def 100644 --- a/framework/shopify/cart/use-cart.tsx +++ b/framework/shopify/cart/use-cart.tsx @@ -44,7 +44,7 @@ export function extendHook( ) { const useCart = () => { const response = useCommerceCart(defaultOpts, [], customFetcher, { - revalidateOnFocus: false, + revalidateOnFocus: true, ...swrOptions, }) const res = useResponse(response, { diff --git a/framework/shopify/cart/utils/checkout-create.ts b/framework/shopify/cart/utils/checkout-create.ts index 24d080551..17377e7f5 100644 --- a/framework/shopify/cart/utils/checkout-create.ts +++ b/framework/shopify/cart/utils/checkout-create.ts @@ -1,7 +1,7 @@ import { SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CHECKOUT_URL_COOKIE, -} from '@framework/const' +} from '@framework/config' import checkoutCreateMutation from '@framework/utils/mutations/checkout-create' import Cookies from 'js-cookie' diff --git a/framework/shopify/cart/utils/checkout-to-cart.ts b/framework/shopify/cart/utils/checkout-to-cart.ts index b38738e2f..4c64d23e6 100644 --- a/framework/shopify/cart/utils/checkout-to-cart.ts +++ b/framework/shopify/cart/utils/checkout-to-cart.ts @@ -7,13 +7,8 @@ const checkoutToCart = (checkoutResponse?: { checkout: Checkout userErrors?: UserError[] }): Maybe<Cart> => { - if (!checkoutResponse) { - throw new CommerceError({ - message: 'Missing checkout details from response cart Response', - }) - } - - const { checkout, userErrors } = checkoutResponse + const checkout = checkoutResponse?.checkout + const userErrors = checkoutResponse?.userErrors if (userErrors && userErrors.length) { throw new ValidationError({ @@ -22,7 +17,7 @@ const checkoutToCart = (checkoutResponse?: { } if (!checkout) { - throw new ValidationError({ + throw new CommerceError({ message: 'Missing checkout details from response cart Response', }) } diff --git a/framework/shopify/config.ts b/framework/shopify/config.ts index 66f2b4bc8..2444f6033 100644 --- a/framework/shopify/config.ts +++ b/framework/shopify/config.ts @@ -1,6 +1,17 @@ -import { CommerceError, FetcherError } from '@commerce/utils/errors' -import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const' import type { CommerceConfig } from '@commerce' +import handleFetchResponse from './utils/handle-fetch-response' + +export const SHOPIFY_CHECKOUT_ID_COOKIE = 'shopify_checkoutId' + +export const SHOPIFY_CHECKOUT_URL_COOKIE = 'shopify_checkoutUrl' + +export const SHOPIFY_CUSTOMER_TOKEN_COOKIE = 'shopify_customerToken' + +export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN + +export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json` + +export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN export type ShopifyConfig = { locale: string @@ -8,52 +19,21 @@ export type ShopifyConfig = { storeDomain: string | undefined } & CommerceConfig -export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN -export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json` -export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN - -async function getText(res: Response) { - try { - return (await res.text()) || res.statusText - } catch (error) { - return res.statusText - } -} - -async function getError(res: Response) { - if (res.headers.get('Content-Type')?.includes('application/json')) { - const data = await res.json() - return new FetcherError({ errors: data.errors, status: res.status }) - } - return new FetcherError({ message: await getText(res), status: res.status }) -} - const shopifyConfig: ShopifyConfig = { locale: 'en-us', cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, storeDomain: STORE_DOMAIN, async fetcher({ method = 'POST', query, variables }) { - const res = await fetch(API_URL, { - method, - body: JSON.stringify({ query, variables }), - headers: { - 'X-Shopify-Storefront-Access-Token': API_TOKEN!, - 'Content-Type': 'application/json', - }, - }) - - if (res.ok) { - const { data, errors } = await res.json() - - if (errors && errors.length) { - throw new CommerceError({ - message: errors[0].message, - }) - } - return data - } - - throw await getError(res) + return handleFetchResponse( + await fetch(API_URL, { + method, + body: JSON.stringify({ query, variables }), + headers: { + 'X-Shopify-Storefront-Access-Token': API_TOKEN!, + 'Content-Type': 'application/json', + }, + }) + ) }, } diff --git a/framework/shopify/const.ts b/framework/shopify/const.ts deleted file mode 100644 index aa8291c02..000000000 --- a/framework/shopify/const.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const SHOPIFY_CHECKOUT_ID_COOKIE = 'shopify_checkoutId' -export const SHOPIFY_CHECKOUT_URL_COOKIE = 'shopify_checkoutUrl' -export const SHOPIFY_CUSTOMER_TOKEN_COOKIE = 'shopify_customerToken' diff --git a/framework/shopify/index.tsx b/framework/shopify/index.tsx index ad54ec6ab..0d6f9fca9 100644 --- a/framework/shopify/index.tsx +++ b/framework/shopify/index.tsx @@ -1,5 +1,5 @@ -import { ReactNode } from 'react' import * as React from 'react' +import { ReactNode } from 'react' import { CommerceProvider as CoreCommerceProvider, @@ -7,7 +7,6 @@ import { } from '@commerce' import shopifyConfig, { ShopifyConfig } from './config' - export type ShopifyProps = { children?: ReactNode locale: string diff --git a/framework/shopify/utils/customer-token.ts b/framework/shopify/utils/customer-token.ts index c78a07c44..119f465b5 100644 --- a/framework/shopify/utils/customer-token.ts +++ b/framework/shopify/utils/customer-token.ts @@ -1,5 +1,5 @@ import Cookies from 'js-cookie' -import { SHOPIFY_CUSTOMER_TOKEN_COOKIE } from '@framework/const' +import { SHOPIFY_CUSTOMER_TOKEN_COOKIE } from '@framework/config' export const getCustomerToken = () => Cookies.get(SHOPIFY_CUSTOMER_TOKEN_COOKIE) diff --git a/framework/shopify/utils/get-checkout-id.ts b/framework/shopify/utils/get-checkout-id.ts index 11e3802d9..623cf9577 100644 --- a/framework/shopify/utils/get-checkout-id.ts +++ b/framework/shopify/utils/get-checkout-id.ts @@ -1,5 +1,5 @@ import Cookies from 'js-cookie' -import { SHOPIFY_CHECKOUT_ID_COOKIE } from '../const' +import { SHOPIFY_CHECKOUT_ID_COOKIE } from '../config' const getCheckoutId = (id?: string) => { return id ?? Cookies.get(SHOPIFY_CHECKOUT_ID_COOKIE) diff --git a/framework/shopify/utils/handle-fetch-response.ts b/framework/shopify/utils/handle-fetch-response.ts new file mode 100644 index 000000000..8d7427d91 --- /dev/null +++ b/framework/shopify/utils/handle-fetch-response.ts @@ -0,0 +1,27 @@ +import { FetcherError } from '@commerce/utils/errors' + +export function getError(errors: any[], status: number) { + errors = errors ?? [{ message: 'Failed to fetch Shopify API' }] + return new FetcherError({ errors, status }) +} + +export async function getAsyncError(res: Response) { + const data = await res.json() + return getError(data.errors, res.status) +} + +const handleFetchResponse = async (res: Response) => { + if (res.ok) { + const { data, errors } = await res.json() + + if (errors && errors.length) { + throw getError(errors, res.status) + } + + return data + } + + throw await getAsyncError(res) +} + +export default handleFetchResponse From aab2e7f7cc97040daa05bdd4124125d0f6b5c7c0 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 8 Feb 2021 10:52:35 -0500 Subject: [PATCH 088/221] Replace use-cart with the new hook --- components/common/UserNav/UserNav.tsx | 7 +++- framework/bigcommerce/cart/use-cart.tsx | 54 ++----------------------- framework/bigcommerce/index.tsx | 10 +++-- framework/commerce/cart/use-cart-2.tsx | 28 ------------- framework/commerce/cart/use-cart.tsx | 54 ++++++++++++++++--------- framework/commerce/cart/use-fake.tsx | 9 +++-- framework/commerce/index.tsx | 6 ++- framework/commerce/utils/use-data-2.ts | 7 ++-- 8 files changed, 66 insertions(+), 109 deletions(-) delete mode 100644 framework/commerce/cart/use-cart-2.tsx diff --git a/components/common/UserNav/UserNav.tsx b/components/common/UserNav/UserNav.tsx index f8e6373d9..7048cc468 100644 --- a/components/common/UserNav/UserNav.tsx +++ b/components/common/UserNav/UserNav.tsx @@ -1,7 +1,10 @@ import { FC } from 'react' import Link from 'next/link' import cn from 'classnames' +import type { BigcommerceProvider } from '@framework' +import { LineItem } from '@framework/types' import useCart from '@framework/cart/use-cart' +import useFake from '@commerce/cart/use-fake' import useCustomer from '@framework/customer/use-customer' import { Heart, Bag } from '@components/icons' import { useUI } from '@components/ui/context' @@ -15,12 +18,14 @@ interface Props { const countItem = (count: number, item: LineItem) => count + item.quantity -const UserNav: FC<Props> = ({ className, children }) => { +const UserNav: FC<Props> = ({ className }) => { const { data } = useCart() const { data: customer } = useCustomer() const { toggleSidebar, closeSidebarIfPresent, openModal } = useUI() const itemsCount = data?.lineItems.reduce(countItem, 0) ?? 0 + const x = useFake<BigcommerceProvider>() + return ( <nav className={cn(s.root, className)}> <div className={s.mainContainer}> diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index afa37ec98..8b984ab9f 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -1,52 +1,4 @@ -import type { HookFetcher } from '@commerce/utils/types' -import type { SwrOptions } from '@commerce/utils/use-data' -import useResponse from '@commerce/utils/use-response' -import useCommerceCart, { CartInput } from '@commerce/cart/use-cart' -import { normalizeCart } from '../lib/normalize' -import type { Cart, BigcommerceCart } from '../types' +import useCommerceCart, { UseCart } from '@commerce/cart/use-cart' +import { BigcommerceProvider } from '..' -const defaultOpts = { - url: '/api/bigcommerce/cart', - method: 'GET', -} - -export const fetcher: HookFetcher<Cart | null, CartInput> = async ( - options, - { cartId }, - fetch -) => { - const data = cartId - ? await fetch<BigcommerceCart>({ ...defaultOpts, ...options }) - : null - return data && normalizeCart(data) -} - -export function extendHook( - customFetcher: typeof fetcher, - swrOptions?: SwrOptions<Cart | null, CartInput> -) { - const useCart = () => { - const response = useCommerceCart(defaultOpts, [], customFetcher, { - revalidateOnFocus: false, - ...swrOptions, - }) - const res = useResponse(response, { - descriptors: { - isEmpty: { - get() { - return (response.data?.lineItems.length ?? 0) <= 0 - }, - enumerable: true, - }, - }, - }) - - return res - } - - useCart.extend = extendHook - - return useCart -} - -export default extendHook(fetcher) +export default useCommerceCart as UseCart<BigcommerceProvider> diff --git a/framework/bigcommerce/index.tsx b/framework/bigcommerce/index.tsx index 268b0bd02..e1584c9e0 100644 --- a/framework/bigcommerce/index.tsx +++ b/framework/bigcommerce/index.tsx @@ -49,14 +49,18 @@ const fetcher: Fetcher<any> = async ({ throw await getError(res) } -const useCart: HookHandler<Cart, CartInput> = { +const useCart: HookHandler<Cart, CartInput, any, any, { isEmpty?: boolean }> = { fetchOptions: { url: '/api/bigcommerce/cart', method: 'GET', }, - fetcher(context) { - return undefined as any + swrOptions: { + revalidateOnFocus: false, }, + // fetcher(context) { + // return undefined as any + // }, + normalizer: normalizeCart, onResponse(response) { return Object.create(response, { isEmpty: { diff --git a/framework/commerce/cart/use-cart-2.tsx b/framework/commerce/cart/use-cart-2.tsx deleted file mode 100644 index cfeb85c87..000000000 --- a/framework/commerce/cart/use-cart-2.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import Cookies from 'js-cookie' -import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types' -import useData, { ResponseState, SwrOptions } from '../utils/use-data' -import type { Cart } from '../types' -import { useCommerce } from '..' - -export type CartResponse<Data> = ResponseState<Data> & { isEmpty?: boolean } - -// Input expected by the `useCart` hook -export type CartInput = { - cartId?: Cart['id'] -} - -export default function useCart<Data extends Cart | null>( - options: HookFetcherOptions, - input: HookInput, - fetcherFn: HookFetcher<Data, CartInput>, - swrOptions?: SwrOptions<Data, CartInput> -): CartResponse<Data> { - const { providerRef, cartCookie } = useCommerce() - const fetcher: typeof fetcherFn = (options, input, fetch) => { - input.cartId = Cookies.get(cartCookie) - return fetcherFn(options, input, fetch) - } - const response = useData(options, input, fetcher, swrOptions) - - return response -} diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index 0a7ba49ee..77271b593 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -1,28 +1,46 @@ +import { useMemo } from 'react' import Cookies from 'js-cookie' -import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types' -import useData, { ResponseState, SwrOptions } from '../utils/use-data' import type { Cart } from '../types' -import { useCommerce } from '..' - -export type CartResponse<Data> = ResponseState<Data> & { isEmpty?: boolean } +import type { HookFetcherFn } from '../utils/types' +import useData from '../utils/use-data-2' +import { Provider, useCommerce } from '..' // Input expected by the `useCart` hook export type CartInput = { cartId?: Cart['id'] } -export default function useCart<Data extends Cart | null>( - options: HookFetcherOptions, - input: HookInput, - fetcherFn: HookFetcher<Data, CartInput>, - swrOptions?: SwrOptions<Data, CartInput> -): CartResponse<Data> { - const { cartCookie } = useCommerce() - const fetcher: typeof fetcherFn = (options, input, fetch) => { - input.cartId = Cookies.get(cartCookie) - return fetcherFn(options, input, fetch) - } - const response = useData(options, input, fetcher, swrOptions) +export type CartResponse<P extends Provider> = ReturnType< + NonNullable<NonNullable<NonNullable<P['cart']>['useCart']>['onResponse']> +> - return response +export type UseCart<P extends Provider> = () => CartResponse<P> + +export const fetcher: HookFetcherFn<Cart | null, CartInput> = async ({ + options, + input: { cartId }, + fetch, + normalize, +}) => { + const data = cartId ? await fetch({ ...options }) : null + return data && normalize ? normalize(data) : data +} + +export default function useCart<P extends Provider>() { + const { providerRef, cartCookie } = useCommerce<P>() + + const provider = providerRef.current + const opts = provider.cart?.useCart + const fetcherFn = opts?.fetcher ?? fetcher + const wrapper: typeof fetcher = (context) => { + context.input.cartId = Cookies.get(cartCookie) + return fetcherFn(context) + } + const response = useData(opts!, [], wrapper, opts?.swrOptions) + const memoizedResponse = useMemo( + () => (opts?.onResponse ? opts.onResponse(response) : response), + [response] + ) + + return memoizedResponse as CartResponse<P> } diff --git a/framework/commerce/cart/use-fake.tsx b/framework/commerce/cart/use-fake.tsx index 324e1c4d9..2f0083d10 100644 --- a/framework/commerce/cart/use-fake.tsx +++ b/framework/commerce/cart/use-fake.tsx @@ -10,7 +10,11 @@ export type CartInput = { cartId?: Cart['id'] } -const fetcher: HookFetcherFn<Cart | null, CartInput> = async ({ +export type CartResponse<P extends Provider> = ReturnType< + NonNullable<NonNullable<NonNullable<P['cart']>['useCart']>['onResponse']> +> + +export const fetcher: HookFetcherFn<Cart | null, CartInput> = async ({ options, input: { cartId }, fetch, @@ -31,12 +35,11 @@ export default function useFake<P extends Provider>() { context.input.cartId = Cookies.get(cartCookie) return fetcherFn(context) } - const response = useData(options, [], wrapper, opts?.swrOptions) const memoizedResponse = useMemo( () => (opts?.onResponse ? opts.onResponse(response) : response), [response] ) - return memoizedResponse + return memoizedResponse as CartResponse<P> } diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index 91a1be2e0..e52aa539d 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -21,9 +21,11 @@ export type Provider = CommerceConfig & { cartNormalizer(data: any): Cart } -export type HookHandler<Data, Input, Result = any, Body = any> = { +export type HookHandler<Data, Input, Result = any, Body = any, State = {}> = { swrOptions?: SwrOptions<Data | null, Input, Result> - onResponse?(response: ResponseState<Data | null>): ResponseState<Data | null> + onResponse?( + response: ResponseState<Data | null> + ): ResponseState<Data | null> & State onMutation?: any fetchOptions?: HookFetcherOptions } & ( diff --git a/framework/commerce/utils/use-data-2.ts b/framework/commerce/utils/use-data-2.ts index 69eb223b0..ed3218840 100644 --- a/framework/commerce/utils/use-data-2.ts +++ b/framework/commerce/utils/use-data-2.ts @@ -7,7 +7,7 @@ import type { } from './types' import defineProperty from './define-property' import { CommerceError } from './errors' -import { useCommerce } from '..' +import { HookHandler, useCommerce } from '..' export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< Data, @@ -20,7 +20,7 @@ export type ResponseState<Result> = responseInterface<Result, CommerceError> & { } export type UseData = <Data = any, Input = null, Result = any>( - options: HookFetcherOptions | (() => HookFetcherOptions | null), + options: HookHandler<Data, Input, Result>, input: HookInput, fetcherFn: HookFetcherFn<Data, Input, Result>, swrOptions?: SwrOptions<Data, Input, Result> @@ -43,6 +43,7 @@ const useData: UseData = (options, input, fetcherFn, swrOptions) => { return obj }, {}), fetch: fetcherRef.current, + normalize: options.normalizer, }) } catch (error) { // SWR will not log errors, but any error that's not an instance @@ -55,7 +56,7 @@ const useData: UseData = (options, input, fetcherFn, swrOptions) => { } const response = useSWR( () => { - const opts = typeof options === 'function' ? options() : options + const opts = options.fetchOptions return opts ? [opts.url, opts.query, opts.method, ...input.map((e) => e[1])] : null From 25b14007da296c491c23ceab5c438a2da5ae7966 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 8 Feb 2021 11:10:35 -0500 Subject: [PATCH 089/221] Removed old hook --- components/common/UserNav/UserNav.tsx | 6 +--- framework/commerce/cart/use-fake.tsx | 45 -------------------------- framework/commerce/utils/use-data-2.ts | 7 +--- 3 files changed, 2 insertions(+), 56 deletions(-) delete mode 100644 framework/commerce/cart/use-fake.tsx diff --git a/components/common/UserNav/UserNav.tsx b/components/common/UserNav/UserNav.tsx index 7048cc468..c615c18b1 100644 --- a/components/common/UserNav/UserNav.tsx +++ b/components/common/UserNav/UserNav.tsx @@ -1,10 +1,8 @@ import { FC } from 'react' import Link from 'next/link' import cn from 'classnames' -import type { BigcommerceProvider } from '@framework' -import { LineItem } from '@framework/types' +import type { LineItem } from '@framework/types' import useCart from '@framework/cart/use-cart' -import useFake from '@commerce/cart/use-fake' import useCustomer from '@framework/customer/use-customer' import { Heart, Bag } from '@components/icons' import { useUI } from '@components/ui/context' @@ -24,8 +22,6 @@ const UserNav: FC<Props> = ({ className }) => { const { toggleSidebar, closeSidebarIfPresent, openModal } = useUI() const itemsCount = data?.lineItems.reduce(countItem, 0) ?? 0 - const x = useFake<BigcommerceProvider>() - return ( <nav className={cn(s.root, className)}> <div className={s.mainContainer}> diff --git a/framework/commerce/cart/use-fake.tsx b/framework/commerce/cart/use-fake.tsx deleted file mode 100644 index 2f0083d10..000000000 --- a/framework/commerce/cart/use-fake.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { useMemo } from 'react' -import Cookies from 'js-cookie' -import type { Cart } from '../types' -import type { HookFetcherFn } from '../utils/types' -import useData from '../utils/use-data-2' -import { Provider, useCommerce } from '..' - -// Input expected by the `useCart` hook -export type CartInput = { - cartId?: Cart['id'] -} - -export type CartResponse<P extends Provider> = ReturnType< - NonNullable<NonNullable<NonNullable<P['cart']>['useCart']>['onResponse']> -> - -export const fetcher: HookFetcherFn<Cart | null, CartInput> = async ({ - options, - input: { cartId }, - fetch, - normalize, -}) => { - const data = cartId ? await fetch({ ...options }) : null - return data && normalize ? normalize(data) : data -} - -export default function useFake<P extends Provider>() { - const { providerRef, cartCookie } = useCommerce<P>() - - const provider = providerRef.current - const opts = provider.cart?.useCart - const options = opts?.fetchOptions ?? {} - const fetcherFn = opts?.fetcher ?? fetcher - const wrapper: typeof fetcher = (context) => { - context.input.cartId = Cookies.get(cartCookie) - return fetcherFn(context) - } - const response = useData(options, [], wrapper, opts?.swrOptions) - const memoizedResponse = useMemo( - () => (opts?.onResponse ? opts.onResponse(response) : response), - [response] - ) - - return memoizedResponse as CartResponse<P> -} diff --git a/framework/commerce/utils/use-data-2.ts b/framework/commerce/utils/use-data-2.ts index ed3218840..76fce8960 100644 --- a/framework/commerce/utils/use-data-2.ts +++ b/framework/commerce/utils/use-data-2.ts @@ -1,10 +1,5 @@ import useSWR, { ConfigInterface, responseInterface } from 'swr' -import type { - HookInput, - HookFetcher, - HookFetcherOptions, - HookFetcherFn, -} from './types' +import type { HookInput, HookFetcher, HookFetcherFn } from './types' import defineProperty from './define-property' import { CommerceError } from './errors' import { HookHandler, useCommerce } from '..' From 17b336017c77727770e0d36c8b2f5b4ba2d85a94 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 8 Feb 2021 11:16:14 -0500 Subject: [PATCH 090/221] Improved HookHandler type --- framework/bigcommerce/cart/use-cart.tsx | 2 +- framework/bigcommerce/index.tsx | 8 +++++++- framework/commerce/index.tsx | 22 ++++++---------------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index 8b984ab9f..ba005ec59 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -1,4 +1,4 @@ import useCommerceCart, { UseCart } from '@commerce/cart/use-cart' -import { BigcommerceProvider } from '..' +import type { BigcommerceProvider } from '..' export default useCommerceCart as UseCart<BigcommerceProvider> diff --git a/framework/bigcommerce/index.tsx b/framework/bigcommerce/index.tsx index e1584c9e0..a4271226e 100644 --- a/framework/bigcommerce/index.tsx +++ b/framework/bigcommerce/index.tsx @@ -49,7 +49,13 @@ const fetcher: Fetcher<any> = async ({ throw await getError(res) } -const useCart: HookHandler<Cart, CartInput, any, any, { isEmpty?: boolean }> = { +const useCart: HookHandler< + Cart | null, + CartInput, + any, + any, + { isEmpty?: boolean } +> = { fetchOptions: { url: '/api/bigcommerce/cart', method: 'GET', diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index e52aa539d..bc660b1df 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -16,29 +16,19 @@ const Commerce = createContext<CommerceContextValue<any> | {}>({}) export type Provider = CommerceConfig & { cart?: { - useCart?: HookHandler<Cart, CartInput> + useCart?: HookHandler<Cart | null, CartInput> } cartNormalizer(data: any): Cart } export type HookHandler<Data, Input, Result = any, Body = any, State = {}> = { - swrOptions?: SwrOptions<Data | null, Input, Result> - onResponse?( - response: ResponseState<Data | null> - ): ResponseState<Data | null> & State + swrOptions?: SwrOptions<Data, Input, Result> + onResponse?(response: ResponseState<Data>): ResponseState<Data> & State onMutation?: any fetchOptions?: HookFetcherOptions -} & ( - | // TODO: Maybe the normalizer is not required if it's used by the API route directly? - { - fetcher: HookFetcherFn<Data | null, Input, Result, Body> - normalizer?(data: Result): Data - } - | { - fetcher?: never - normalizer(data: Result): Data - } -) + fetcher?: HookFetcherFn<Data, Input, Result, Body> + normalizer?(data: Result): Data +} export type CommerceProps<P extends Provider> = { children?: ReactNode From 74c55cc50e61d2cf0ed72d4f71f8f6ffcee01499 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 8 Feb 2021 17:16:45 -0500 Subject: [PATCH 091/221] Moved types --- framework/bigcommerce/index.tsx | 10 +++----- framework/commerce/cart/use-cart.tsx | 16 ++++++++---- framework/commerce/index.tsx | 17 +++---------- framework/commerce/utils/types.ts | 34 ++++++++++++++++++++++++-- framework/commerce/utils/use-data-2.ts | 23 ++++++++++++----- 5 files changed, 67 insertions(+), 33 deletions(-) diff --git a/framework/bigcommerce/index.tsx b/framework/bigcommerce/index.tsx index a4271226e..4f6c9316b 100644 --- a/framework/bigcommerce/index.tsx +++ b/framework/bigcommerce/index.tsx @@ -5,10 +5,10 @@ import { CommerceConfig, CommerceProvider as CoreCommerceProvider, useCommerce as useCoreCommerce, - HookHandler, } from '@commerce' import { FetcherError } from '@commerce/utils/errors' -import type { CartInput } from '@commerce/cart/use-cart' +import type { HookHandler } from '@commerce/utils/types' +import type { FetchCartInput } from '@commerce/cart/use-cart' import { normalizeCart } from './lib/normalize' import { Cart } from './types' @@ -51,7 +51,8 @@ const fetcher: Fetcher<any> = async ({ const useCart: HookHandler< Cart | null, - CartInput, + [], + FetchCartInput, any, any, { isEmpty?: boolean } @@ -63,9 +64,6 @@ const useCart: HookHandler< swrOptions: { revalidateOnFocus: false, }, - // fetcher(context) { - // return undefined as any - // }, normalizer: normalizeCart, onResponse(response) { return Object.create(response, { diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index 77271b593..7c6730497 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -6,7 +6,7 @@ import useData from '../utils/use-data-2' import { Provider, useCommerce } from '..' // Input expected by the `useCart` hook -export type CartInput = { +export type FetchCartInput = { cartId?: Cart['id'] } @@ -14,9 +14,15 @@ export type CartResponse<P extends Provider> = ReturnType< NonNullable<NonNullable<NonNullable<P['cart']>['useCart']>['onResponse']> > -export type UseCart<P extends Provider> = () => CartResponse<P> +export type UseCart<P extends Provider> = ( + ...input: UseCartInput<P> +) => CartResponse<P> -export const fetcher: HookFetcherFn<Cart | null, CartInput> = async ({ +export type UseCartInput<P extends Provider> = NonNullable< + NonNullable<NonNullable<NonNullable<P['cart']>['useCart']>>['input'] +> + +export const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ options, input: { cartId }, fetch, @@ -26,7 +32,7 @@ export const fetcher: HookFetcherFn<Cart | null, CartInput> = async ({ return data && normalize ? normalize(data) : data } -export default function useCart<P extends Provider>() { +export default function useCart<P extends Provider>(...input: UseCartInput<P>) { const { providerRef, cartCookie } = useCommerce<P>() const provider = providerRef.current @@ -36,7 +42,7 @@ export default function useCart<P extends Provider>() { context.input.cartId = Cookies.get(cartCookie) return fetcherFn(context) } - const response = useData(opts!, [], wrapper, opts?.swrOptions) + const response = useData(opts!, input, wrapper, opts?.swrOptions) const memoizedResponse = useMemo( () => (opts?.onResponse ? opts.onResponse(response) : response), [response] diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index bc660b1df..f329c8054 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -7,27 +7,16 @@ import { useRef, } from 'react' import * as React from 'react' -import { Fetcher, HookFetcherFn, HookFetcherOptions } from './utils/types' +import { Fetcher, HookHandler } from './utils/types' import { Cart } from './types' -import type { ResponseState, SwrOptions } from './utils/use-data' -import type { CartInput } from './cart/use-cart' +import type { FetchCartInput } from './cart/use-cart' const Commerce = createContext<CommerceContextValue<any> | {}>({}) export type Provider = CommerceConfig & { cart?: { - useCart?: HookHandler<Cart | null, CartInput> + useCart?: HookHandler<Cart | null, [...any], FetchCartInput> } - cartNormalizer(data: any): Cart -} - -export type HookHandler<Data, Input, Result = any, Body = any, State = {}> = { - swrOptions?: SwrOptions<Data, Input, Result> - onResponse?(response: ResponseState<Data>): ResponseState<Data> & State - onMutation?: any - fetchOptions?: HookFetcherOptions - fetcher?: HookFetcherFn<Data, Input, Result, Body> - normalizer?(data: Result): Data } export type CommerceProps<P extends Provider> = { diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 3428b5194..bef1f2d8b 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -1,3 +1,7 @@ +import type { ResponseState, SwrOptions } from './use-data' + +export type Override<T, K> = Omit<T, keyof K> & K + // Core fetcher added by CommerceProvider export type Fetcher<T> = (options: FetcherOptions) => T | Promise<T> @@ -15,7 +19,12 @@ export type HookFetcher<Data, Input = null, Result = any> = ( fetch: <T = Result, Body = any>(options: FetcherOptions<Body>) => Promise<T> ) => Data | Promise<Data> -export type HookFetcherFn<Data, Input, Result = any, Body = any> = (context: { +export type HookFetcherFn< + Data, + Input = unknown, + Result = any, + Body = any +> = (context: { options: HookFetcherOptions | null input: Input fetch: <T = Result, B = Body>(options: FetcherOptions<B>) => Promise<T> @@ -30,4 +39,25 @@ export type HookFetcherOptions = { export type HookInput = [string, string | number | boolean | undefined][] -export type Override<T, K> = Omit<T, keyof K> & K +export type HookHandler< + // Data obj returned by the hook and fetch operation + Data, + // Input expected by the hook + Input = [...any], + // Input expected before doing a fetch operation + FetchInput = unknown, + // Data returned by the API after a fetch operation + Result = any, + // Body expected by the API endpoint + Body = any, + // Custom state added to the response object of SWR + State = {} +> = { + input?: Input + swrOptions?: SwrOptions<Data, FetchInput, Result> + onResponse?(response: ResponseState<Data>): ResponseState<Data> & State + onMutation?: any + fetchOptions?: HookFetcherOptions + fetcher?: HookFetcherFn<Data, FetchInput, Result, Body> + normalizer?(data: Result): Data +} diff --git a/framework/commerce/utils/use-data-2.ts b/framework/commerce/utils/use-data-2.ts index 76fce8960..840251409 100644 --- a/framework/commerce/utils/use-data-2.ts +++ b/framework/commerce/utils/use-data-2.ts @@ -1,8 +1,13 @@ import useSWR, { ConfigInterface, responseInterface } from 'swr' -import type { HookInput, HookFetcher, HookFetcherFn } from './types' +import type { + HookHandler, + HookInput, + HookFetcher, + HookFetcherFn, +} from './types' import defineProperty from './define-property' import { CommerceError } from './errors' -import { HookHandler, useCommerce } from '..' +import { useCommerce } from '..' export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< Data, @@ -14,11 +19,17 @@ export type ResponseState<Result> = responseInterface<Result, CommerceError> & { isLoading: boolean } -export type UseData = <Data = any, Input = null, Result = any>( - options: HookHandler<Data, Input, Result>, +export type UseData = < + Data = any, + Input = [...any], + FetchInput = unknown, + Result = any, + Body = any +>( + options: HookHandler<Data, Input, FetchInput, Result, Body>, input: HookInput, - fetcherFn: HookFetcherFn<Data, Input, Result>, - swrOptions?: SwrOptions<Data, Input, Result> + fetcherFn: HookFetcherFn<Data, FetchInput, Result, Body>, + swrOptions?: SwrOptions<Data, FetchInput, Result> ) => ResponseState<Data> const useData: UseData = (options, input, fetcherFn, swrOptions) => { From 8bf42c3b5007164047488c964a831c73e7322409 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 8 Feb 2021 17:55:23 -0500 Subject: [PATCH 092/221] Simplified useData types --- framework/commerce/cart/use-cart.tsx | 9 ++++++--- framework/commerce/utils/types.ts | 4 ++++ framework/commerce/utils/use-data-2.ts | 20 +++++++++++--------- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index 7c6730497..b19e609da 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -5,7 +5,6 @@ import type { HookFetcherFn } from '../utils/types' import useData from '../utils/use-data-2' import { Provider, useCommerce } from '..' -// Input expected by the `useCart` hook export type FetchCartInput = { cartId?: Cart['id'] } @@ -33,7 +32,7 @@ export const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ } export default function useCart<P extends Provider>(...input: UseCartInput<P>) { - const { providerRef, cartCookie } = useCommerce<P>() + const { providerRef, fetcherRef, cartCookie } = useCommerce<P>() const provider = providerRef.current const opts = provider.cart?.useCart @@ -42,7 +41,11 @@ export default function useCart<P extends Provider>(...input: UseCartInput<P>) { context.input.cartId = Cookies.get(cartCookie) return fetcherFn(context) } - const response = useData(opts!, input, wrapper, opts?.swrOptions) + const response = useData( + { ...opts, fetcher: wrapper }, + input, + provider.fetcher ?? fetcherRef.current + ) const memoizedResponse = useMemo( () => (opts?.onResponse ? opts.onResponse(response) : response), [response] diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index bef1f2d8b..5ff0360a1 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -2,6 +2,10 @@ import type { ResponseState, SwrOptions } from './use-data' export type Override<T, K> = Omit<T, keyof K> & K +// Returns the properties in T with the properties in type K changed from optional to required +export type PickRequired<T, K extends keyof T> = Omit<T, K> & + Required<Pick<T, K>> + // Core fetcher added by CommerceProvider export type Fetcher<T> = (options: FetcherOptions) => T | Promise<T> diff --git a/framework/commerce/utils/use-data-2.ts b/framework/commerce/utils/use-data-2.ts index 840251409..b8dc00b3d 100644 --- a/framework/commerce/utils/use-data-2.ts +++ b/framework/commerce/utils/use-data-2.ts @@ -3,7 +3,8 @@ import type { HookHandler, HookInput, HookFetcher, - HookFetcherFn, + PickRequired, + Fetcher, } from './types' import defineProperty from './define-property' import { CommerceError } from './errors' @@ -26,14 +27,15 @@ export type UseData = < Result = any, Body = any >( - options: HookHandler<Data, Input, FetchInput, Result, Body>, + options: PickRequired< + HookHandler<Data, Input, FetchInput, Result, Body>, + 'fetcher' + >, input: HookInput, - fetcherFn: HookFetcherFn<Data, FetchInput, Result, Body>, - swrOptions?: SwrOptions<Data, FetchInput, Result> + fetcherFn: Fetcher<any> ) => ResponseState<Data> -const useData: UseData = (options, input, fetcherFn, swrOptions) => { - const { fetcherRef } = useCommerce() +const useData: UseData = (options, input, fetcherFn) => { const fetcher = async ( url?: string, query?: string, @@ -41,14 +43,14 @@ const useData: UseData = (options, input, fetcherFn, swrOptions) => { ...args: any[] ) => { try { - return await fetcherFn({ + return await options.fetcher({ options: { url, query, method }, // Transform the input array into an object input: args.reduce((obj, val, i) => { obj[input[i][0]!] = val return obj }, {}), - fetch: fetcherRef.current, + fetch: fetcherFn, normalize: options.normalizer, }) } catch (error) { @@ -68,7 +70,7 @@ const useData: UseData = (options, input, fetcherFn, swrOptions) => { : null }, fetcher, - swrOptions + options.swrOptions ) if (!('isLoading' in response)) { From 82c5cd4abab6fc060ede2efa36ff398d2d1016d1 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 8 Feb 2021 18:00:48 -0500 Subject: [PATCH 093/221] Updated Fetcher type --- framework/bigcommerce/index.tsx | 2 +- framework/commerce/index.tsx | 4 ++-- framework/commerce/utils/types.ts | 4 +++- framework/commerce/utils/use-data-2.ts | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/framework/bigcommerce/index.tsx b/framework/bigcommerce/index.tsx index 4f6c9316b..261aa4d28 100644 --- a/framework/bigcommerce/index.tsx +++ b/framework/bigcommerce/index.tsx @@ -28,7 +28,7 @@ async function getError(res: Response) { return new FetcherError({ message: await getText(res), status: res.status }) } -const fetcher: Fetcher<any> = async ({ +const fetcher: Fetcher = async ({ url, method = 'GET', variables, diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index f329c8054..e2dc60a9a 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -25,14 +25,14 @@ export type CommerceProps<P extends Provider> = { config: CommerceConfig } -export type CommerceConfig = { fetcher: Fetcher<any> } & Omit< +export type CommerceConfig = { fetcher: Fetcher } & Omit< CommerceContextValue<any>, 'providerRef' | 'fetcherRef' > export type CommerceContextValue<P extends Provider> = { providerRef: MutableRefObject<P> - fetcherRef: MutableRefObject<Fetcher<any>> + fetcherRef: MutableRefObject<Fetcher> locale: string cartCookie: string } diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 5ff0360a1..3c81bac61 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -7,7 +7,9 @@ export type PickRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>> // Core fetcher added by CommerceProvider -export type Fetcher<T> = (options: FetcherOptions) => T | Promise<T> +export type Fetcher<T = any, B = any> = ( + options: FetcherOptions<B> +) => T | Promise<T> export type FetcherOptions<Body = any> = { url?: string diff --git a/framework/commerce/utils/use-data-2.ts b/framework/commerce/utils/use-data-2.ts index b8dc00b3d..1fddf56fb 100644 --- a/framework/commerce/utils/use-data-2.ts +++ b/framework/commerce/utils/use-data-2.ts @@ -32,7 +32,7 @@ export type UseData = < 'fetcher' >, input: HookInput, - fetcherFn: Fetcher<any> + fetcherFn: Fetcher ) => ResponseState<Data> const useData: UseData = (options, input, fetcherFn) => { From 7f3174bcd05f024c7eb9a840d7365d9238de526e Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 8 Feb 2021 18:13:29 -0500 Subject: [PATCH 094/221] Moved SwrOptions type --- framework/commerce/utils/types.ts | 10 +++++++++- framework/commerce/utils/use-data-2.ts | 17 ++--------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 3c81bac61..f6dbe866f 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -1,4 +1,6 @@ -import type { ResponseState, SwrOptions } from './use-data' +import type { ConfigInterface } from 'swr' +import type { CommerceError } from './errors' +import type { ResponseState } from './use-data' export type Override<T, K> = Omit<T, keyof K> & K @@ -67,3 +69,9 @@ export type HookHandler< fetcher?: HookFetcherFn<Data, FetchInput, Result, Body> normalizer?(data: Result): Data } + +export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< + Data, + CommerceError, + HookFetcher<Data, Input, Result> +> diff --git a/framework/commerce/utils/use-data-2.ts b/framework/commerce/utils/use-data-2.ts index 1fddf56fb..a5d237aaa 100644 --- a/framework/commerce/utils/use-data-2.ts +++ b/framework/commerce/utils/use-data-2.ts @@ -1,20 +1,7 @@ -import useSWR, { ConfigInterface, responseInterface } from 'swr' -import type { - HookHandler, - HookInput, - HookFetcher, - PickRequired, - Fetcher, -} from './types' +import useSWR, { responseInterface } from 'swr' +import type { HookHandler, HookInput, PickRequired, Fetcher } from './types' import defineProperty from './define-property' import { CommerceError } from './errors' -import { useCommerce } from '..' - -export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< - Data, - CommerceError, - HookFetcher<Data, Input, Result> -> export type ResponseState<Result> = responseInterface<Result, CommerceError> & { isLoading: boolean From 2a5cbadd3a8801ee995874f77d29ee519917d231 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 8 Feb 2021 19:50:45 -0500 Subject: [PATCH 095/221] Removed duplicated fetcher --- framework/bigcommerce/index.tsx | 15 --------------- framework/commerce/index.tsx | 6 ++++-- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/framework/bigcommerce/index.tsx b/framework/bigcommerce/index.tsx index 261aa4d28..2ff73366c 100644 --- a/framework/bigcommerce/index.tsx +++ b/framework/bigcommerce/index.tsx @@ -90,21 +90,6 @@ export type BigcommerceProvider = typeof bigcommerceProvider export const bigcommerceConfig: CommerceConfig = { locale: 'en-us', cartCookie: 'bc_cartId', - async fetcher({ url, method = 'GET', variables, body: bodyObj }) { - const hasBody = Boolean(variables || bodyObj) - const body = hasBody - ? JSON.stringify(variables ? { variables } : bodyObj) - : undefined - const headers = hasBody ? { 'Content-Type': 'application/json' } : undefined - const res = await fetch(url!, { method, body, headers }) - - if (res.ok) { - const { data } = await res.json() - return data - } - - throw await getError(res) - }, } export type BigcommerceConfig = Partial<CommerceConfig> diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index e2dc60a9a..cb2fafcd7 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -14,6 +14,7 @@ import type { FetchCartInput } from './cart/use-cart' const Commerce = createContext<CommerceContextValue<any> | {}>({}) export type Provider = CommerceConfig & { + fetcher: Fetcher cart?: { useCart?: HookHandler<Cart | null, [...any], FetchCartInput> } @@ -25,7 +26,7 @@ export type CommerceProps<P extends Provider> = { config: CommerceConfig } -export type CommerceConfig = { fetcher: Fetcher } & Omit< +export type CommerceConfig = Omit< CommerceContextValue<any>, 'providerRef' | 'fetcherRef' > @@ -47,7 +48,8 @@ export function CommerceProvider<P extends Provider>({ } const providerRef = useRef(provider) - const fetcherRef = useRef(config.fetcher) + // TODO: Remove the fetcherRef + const fetcherRef = useRef(provider.fetcher) // Because the config is an object, if the parent re-renders this provider // will re-render every consumer unless we memoize the config const cfg = useMemo( From 479b30ba5f5f1931a328a21e71ab09613e75be37 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 8 Feb 2021 21:57:39 -0500 Subject: [PATCH 096/221] Moved provider to its own file --- framework/bigcommerce/index.tsx | 84 +---------------------- framework/bigcommerce/provider.ts | 108 ++++++++++++++++++++++++++++++ framework/commerce/index.tsx | 3 + 3 files changed, 114 insertions(+), 81 deletions(-) create mode 100644 framework/bigcommerce/provider.ts diff --git a/framework/bigcommerce/index.tsx b/framework/bigcommerce/index.tsx index 2ff73366c..83fbdbcbc 100644 --- a/framework/bigcommerce/index.tsx +++ b/framework/bigcommerce/index.tsx @@ -1,91 +1,13 @@ import { ReactNode } from 'react' -import * as React from 'react' -import { Fetcher } from '@commerce/utils/types' import { CommerceConfig, CommerceProvider as CoreCommerceProvider, useCommerce as useCoreCommerce, } from '@commerce' -import { FetcherError } from '@commerce/utils/errors' -import type { HookHandler } from '@commerce/utils/types' -import type { FetchCartInput } from '@commerce/cart/use-cart' -import { normalizeCart } from './lib/normalize' -import { Cart } from './types' +import { bigcommerceProvider, BigcommerceProvider } from './provider' -async function getText(res: Response) { - try { - return (await res.text()) || res.statusText - } catch (error) { - return res.statusText - } -} - -async function getError(res: Response) { - if (res.headers.get('Content-Type')?.includes('application/json')) { - const data = await res.json() - return new FetcherError({ errors: data.errors, status: res.status }) - } - return new FetcherError({ message: await getText(res), status: res.status }) -} - -const fetcher: Fetcher = async ({ - url, - method = 'GET', - variables, - body: bodyObj, -}) => { - const hasBody = Boolean(variables || bodyObj) - const body = hasBody - ? JSON.stringify(variables ? { variables } : bodyObj) - : undefined - const headers = hasBody ? { 'Content-Type': 'application/json' } : undefined - const res = await fetch(url!, { method, body, headers }) - - if (res.ok) { - const { data } = await res.json() - return data - } - - throw await getError(res) -} - -const useCart: HookHandler< - Cart | null, - [], - FetchCartInput, - any, - any, - { isEmpty?: boolean } -> = { - fetchOptions: { - url: '/api/bigcommerce/cart', - method: 'GET', - }, - swrOptions: { - revalidateOnFocus: false, - }, - normalizer: normalizeCart, - onResponse(response) { - return Object.create(response, { - isEmpty: { - get() { - return (response.data?.lineItems.length ?? 0) <= 0 - }, - enumerable: true, - }, - }) - }, -} - -export const bigcommerceProvider = { - locale: 'en-us', - cartCookie: 'bc_cartId', - fetcher, - cartNormalizer: normalizeCart, - cart: { useCart }, -} - -export type BigcommerceProvider = typeof bigcommerceProvider +export { bigcommerceProvider } +export type { BigcommerceProvider } export const bigcommerceConfig: CommerceConfig = { locale: 'en-us', diff --git a/framework/bigcommerce/provider.ts b/framework/bigcommerce/provider.ts new file mode 100644 index 000000000..b6385546c --- /dev/null +++ b/framework/bigcommerce/provider.ts @@ -0,0 +1,108 @@ +import { FetcherError } from '@commerce/utils/errors' +import type { Fetcher, HookHandler } from '@commerce/utils/types' +import type { FetchCartInput } from '@commerce/cart/use-cart' +import { normalizeCart } from './lib/normalize' +import type { Cart } from './types' + +async function getText(res: Response) { + try { + return (await res.text()) || res.statusText + } catch (error) { + return res.statusText + } +} + +async function getError(res: Response) { + if (res.headers.get('Content-Type')?.includes('application/json')) { + const data = await res.json() + return new FetcherError({ errors: data.errors, status: res.status }) + } + return new FetcherError({ message: await getText(res), status: res.status }) +} + +const fetcher: Fetcher = async ({ + url, + method = 'GET', + variables, + body: bodyObj, +}) => { + const hasBody = Boolean(variables || bodyObj) + const body = hasBody + ? JSON.stringify(variables ? { variables } : bodyObj) + : undefined + const headers = hasBody ? { 'Content-Type': 'application/json' } : undefined + const res = await fetch(url!, { method, body, headers }) + + if (res.ok) { + const { data } = await res.json() + return data + } + + throw await getError(res) +} + +const useCart: HookHandler< + Cart | null, + [], + FetchCartInput, + any, + any, + { isEmpty?: boolean } +> = { + fetchOptions: { + url: '/api/bigcommerce/cart', + method: 'GET', + }, + swrOptions: { + revalidateOnFocus: false, + }, + normalizer: normalizeCart, + onResponse(response) { + return Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, + }, + }) + }, +} + +const useWishlist: HookHandler< + Cart | null, + [], + FetchCartInput, + any, + any, + { isEmpty?: boolean } +> = { + fetchOptions: { + url: '/api/bigcommerce/wishlist', + method: 'GET', + }, + swrOptions: { + revalidateOnFocus: false, + }, + onResponse(response) { + return Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, + }, + }) + }, +} + +export const bigcommerceProvider = { + locale: 'en-us', + cartCookie: 'bc_cartId', + fetcher, + cartNormalizer: normalizeCart, + cart: { useCart }, + wishlist: { useWishlist }, +} + +export type BigcommerceProvider = typeof bigcommerceProvider diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index cb2fafcd7..82e86947d 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -18,6 +18,9 @@ export type Provider = CommerceConfig & { cart?: { useCart?: HookHandler<Cart | null, [...any], FetchCartInput> } + wishlist?: { + useWishlist?: HookHandler<Cart | null, [...any], FetchCartInput> + } } export type CommerceProps<P extends Provider> = { From 016be86d9a464e9180d5cf1ab84e1bb7589f8666 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 8 Feb 2021 22:07:26 -0500 Subject: [PATCH 097/221] Added proper type for fetch input --- framework/commerce/utils/types.ts | 8 ++++++-- framework/commerce/utils/use-data-2.ts | 10 ++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index f6dbe866f..d84ec07f0 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -45,7 +45,11 @@ export type HookFetcherOptions = { method?: string } -export type HookInput = [string, string | number | boolean | undefined][] +export type HookInputValue = string | number | boolean | undefined + +export type HookInput = [string, HookInputValue][] + +export type HookFetchInput = { [k: string]: HookInputValue } export type HookHandler< // Data obj returned by the hook and fetch operation @@ -53,7 +57,7 @@ export type HookHandler< // Input expected by the hook Input = [...any], // Input expected before doing a fetch operation - FetchInput = unknown, + FetchInput extends HookFetchInput = never, // Data returned by the API after a fetch operation Result = any, // Body expected by the API endpoint diff --git a/framework/commerce/utils/use-data-2.ts b/framework/commerce/utils/use-data-2.ts index a5d237aaa..5536bb02c 100644 --- a/framework/commerce/utils/use-data-2.ts +++ b/framework/commerce/utils/use-data-2.ts @@ -1,5 +1,11 @@ import useSWR, { responseInterface } from 'swr' -import type { HookHandler, HookInput, PickRequired, Fetcher } from './types' +import type { + HookHandler, + HookInput, + HookFetchInput, + PickRequired, + Fetcher, +} from './types' import defineProperty from './define-property' import { CommerceError } from './errors' @@ -10,7 +16,7 @@ export type ResponseState<Result> = responseInterface<Result, CommerceError> & { export type UseData = < Data = any, Input = [...any], - FetchInput = unknown, + FetchInput extends HookFetchInput = never, Result = any, Body = any >( From c9a43f1bce0572d0eff41f3af893be8bdb00bedd Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Tue, 9 Feb 2021 09:13:04 +0200 Subject: [PATCH 098/221] Revert "Merge branch 'agnostic' of https://github.com/vercel/commerce into agnostic" This reverts commit 23c8ed7c2d48d30e74ad94216f9910650fadf30c, reversing changes made to bf50965a39ef0b1b956461ebe62070809fbe1d63. --- components/common/UserNav/UserNav.tsx | 3 +- .../wishlist/WishlistCard/WishlistCard.tsx | 4 +- framework/bigcommerce/cart/use-cart.tsx | 54 ++++++++- framework/bigcommerce/index.tsx | 42 +++++-- framework/bigcommerce/provider.ts | 108 ------------------ framework/commerce/cart/use-cart.tsx | 65 +++-------- framework/commerce/index.tsx | 45 ++------ framework/commerce/utils/types.ts | 61 +--------- framework/commerce/utils/use-data-2.ts | 81 ------------- 9 files changed, 122 insertions(+), 341 deletions(-) delete mode 100644 framework/bigcommerce/provider.ts delete mode 100644 framework/commerce/utils/use-data-2.ts diff --git a/components/common/UserNav/UserNav.tsx b/components/common/UserNav/UserNav.tsx index c615c18b1..f8e6373d9 100644 --- a/components/common/UserNav/UserNav.tsx +++ b/components/common/UserNav/UserNav.tsx @@ -1,7 +1,6 @@ import { FC } from 'react' import Link from 'next/link' import cn from 'classnames' -import type { LineItem } from '@framework/types' import useCart from '@framework/cart/use-cart' import useCustomer from '@framework/customer/use-customer' import { Heart, Bag } from '@components/icons' @@ -16,7 +15,7 @@ interface Props { const countItem = (count: number, item: LineItem) => count + item.quantity -const UserNav: FC<Props> = ({ className }) => { +const UserNav: FC<Props> = ({ className, children }) => { const { data } = useCart() const { data: customer } = useCustomer() const { toggleSidebar, closeSidebarIfPresent, openModal } = useUI() diff --git a/components/wishlist/WishlistCard/WishlistCard.tsx b/components/wishlist/WishlistCard/WishlistCard.tsx index d1a9403b3..82147f575 100644 --- a/components/wishlist/WishlistCard/WishlistCard.tsx +++ b/components/wishlist/WishlistCard/WishlistCard.tsx @@ -42,8 +42,8 @@ const WishlistCard: FC<Props> = ({ product }) => { setLoading(true) try { await addItem({ - productId: product.id, - variantId: product.variants[0].id, + productId: Number(product.id), + variantId: Number(product.variants[0].id), }) openSidebar() setLoading(false) diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index ba005ec59..afa37ec98 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -1,4 +1,52 @@ -import useCommerceCart, { UseCart } from '@commerce/cart/use-cart' -import type { BigcommerceProvider } from '..' +import type { HookFetcher } from '@commerce/utils/types' +import type { SwrOptions } from '@commerce/utils/use-data' +import useResponse from '@commerce/utils/use-response' +import useCommerceCart, { CartInput } from '@commerce/cart/use-cart' +import { normalizeCart } from '../lib/normalize' +import type { Cart, BigcommerceCart } from '../types' -export default useCommerceCart as UseCart<BigcommerceProvider> +const defaultOpts = { + url: '/api/bigcommerce/cart', + method: 'GET', +} + +export const fetcher: HookFetcher<Cart | null, CartInput> = async ( + options, + { cartId }, + fetch +) => { + const data = cartId + ? await fetch<BigcommerceCart>({ ...defaultOpts, ...options }) + : null + return data && normalizeCart(data) +} + +export function extendHook( + customFetcher: typeof fetcher, + swrOptions?: SwrOptions<Cart | null, CartInput> +) { + const useCart = () => { + const response = useCommerceCart(defaultOpts, [], customFetcher, { + revalidateOnFocus: false, + ...swrOptions, + }) + const res = useResponse(response, { + descriptors: { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, + }, + }, + }) + + return res + } + + useCart.extend = extendHook + + return useCart +} + +export default extendHook(fetcher) diff --git a/framework/bigcommerce/index.tsx b/framework/bigcommerce/index.tsx index 83fbdbcbc..a4c9fffa5 100644 --- a/framework/bigcommerce/index.tsx +++ b/framework/bigcommerce/index.tsx @@ -1,17 +1,46 @@ import { ReactNode } from 'react' +import * as React from 'react' import { CommerceConfig, CommerceProvider as CoreCommerceProvider, useCommerce as useCoreCommerce, } from '@commerce' -import { bigcommerceProvider, BigcommerceProvider } from './provider' +import { FetcherError } from '@commerce/utils/errors' -export { bigcommerceProvider } -export type { BigcommerceProvider } +async function getText(res: Response) { + try { + return (await res.text()) || res.statusText + } catch (error) { + return res.statusText + } +} + +async function getError(res: Response) { + if (res.headers.get('Content-Type')?.includes('application/json')) { + const data = await res.json() + return new FetcherError({ errors: data.errors, status: res.status }) + } + return new FetcherError({ message: await getText(res), status: res.status }) +} export const bigcommerceConfig: CommerceConfig = { locale: 'en-us', cartCookie: 'bc_cartId', + async fetcher({ url, method = 'GET', variables, body: bodyObj }) { + const hasBody = Boolean(variables || bodyObj) + const body = hasBody + ? JSON.stringify(variables ? { variables } : bodyObj) + : undefined + const headers = hasBody ? { 'Content-Type': 'application/json' } : undefined + const res = await fetch(url!, { method, body, headers }) + + if (res.ok) { + const { data } = await res.json() + return data + } + + throw await getError(res) + }, } export type BigcommerceConfig = Partial<CommerceConfig> @@ -23,13 +52,10 @@ export type BigcommerceProps = { export function CommerceProvider({ children, ...config }: BigcommerceProps) { return ( - <CoreCommerceProvider - provider={bigcommerceProvider} - config={{ ...bigcommerceConfig, ...config }} - > + <CoreCommerceProvider config={{ ...bigcommerceConfig, ...config }}> {children} </CoreCommerceProvider> ) } -export const useCommerce = () => useCoreCommerce<BigcommerceProvider>() +export const useCommerce = () => useCoreCommerce() diff --git a/framework/bigcommerce/provider.ts b/framework/bigcommerce/provider.ts deleted file mode 100644 index b6385546c..000000000 --- a/framework/bigcommerce/provider.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { FetcherError } from '@commerce/utils/errors' -import type { Fetcher, HookHandler } from '@commerce/utils/types' -import type { FetchCartInput } from '@commerce/cart/use-cart' -import { normalizeCart } from './lib/normalize' -import type { Cart } from './types' - -async function getText(res: Response) { - try { - return (await res.text()) || res.statusText - } catch (error) { - return res.statusText - } -} - -async function getError(res: Response) { - if (res.headers.get('Content-Type')?.includes('application/json')) { - const data = await res.json() - return new FetcherError({ errors: data.errors, status: res.status }) - } - return new FetcherError({ message: await getText(res), status: res.status }) -} - -const fetcher: Fetcher = async ({ - url, - method = 'GET', - variables, - body: bodyObj, -}) => { - const hasBody = Boolean(variables || bodyObj) - const body = hasBody - ? JSON.stringify(variables ? { variables } : bodyObj) - : undefined - const headers = hasBody ? { 'Content-Type': 'application/json' } : undefined - const res = await fetch(url!, { method, body, headers }) - - if (res.ok) { - const { data } = await res.json() - return data - } - - throw await getError(res) -} - -const useCart: HookHandler< - Cart | null, - [], - FetchCartInput, - any, - any, - { isEmpty?: boolean } -> = { - fetchOptions: { - url: '/api/bigcommerce/cart', - method: 'GET', - }, - swrOptions: { - revalidateOnFocus: false, - }, - normalizer: normalizeCart, - onResponse(response) { - return Object.create(response, { - isEmpty: { - get() { - return (response.data?.lineItems.length ?? 0) <= 0 - }, - enumerable: true, - }, - }) - }, -} - -const useWishlist: HookHandler< - Cart | null, - [], - FetchCartInput, - any, - any, - { isEmpty?: boolean } -> = { - fetchOptions: { - url: '/api/bigcommerce/wishlist', - method: 'GET', - }, - swrOptions: { - revalidateOnFocus: false, - }, - onResponse(response) { - return Object.create(response, { - isEmpty: { - get() { - return (response.data?.lineItems.length ?? 0) <= 0 - }, - enumerable: true, - }, - }) - }, -} - -export const bigcommerceProvider = { - locale: 'en-us', - cartCookie: 'bc_cartId', - fetcher, - cartNormalizer: normalizeCart, - cart: { useCart }, - wishlist: { useWishlist }, -} - -export type BigcommerceProvider = typeof bigcommerceProvider diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index b19e609da..0a7ba49ee 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -1,55 +1,28 @@ -import { useMemo } from 'react' import Cookies from 'js-cookie' +import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types' +import useData, { ResponseState, SwrOptions } from '../utils/use-data' import type { Cart } from '../types' -import type { HookFetcherFn } from '../utils/types' -import useData from '../utils/use-data-2' -import { Provider, useCommerce } from '..' +import { useCommerce } from '..' -export type FetchCartInput = { +export type CartResponse<Data> = ResponseState<Data> & { isEmpty?: boolean } + +// Input expected by the `useCart` hook +export type CartInput = { cartId?: Cart['id'] } -export type CartResponse<P extends Provider> = ReturnType< - NonNullable<NonNullable<NonNullable<P['cart']>['useCart']>['onResponse']> -> - -export type UseCart<P extends Provider> = ( - ...input: UseCartInput<P> -) => CartResponse<P> - -export type UseCartInput<P extends Provider> = NonNullable< - NonNullable<NonNullable<NonNullable<P['cart']>['useCart']>>['input'] -> - -export const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ - options, - input: { cartId }, - fetch, - normalize, -}) => { - const data = cartId ? await fetch({ ...options }) : null - return data && normalize ? normalize(data) : data -} - -export default function useCart<P extends Provider>(...input: UseCartInput<P>) { - const { providerRef, fetcherRef, cartCookie } = useCommerce<P>() - - const provider = providerRef.current - const opts = provider.cart?.useCart - const fetcherFn = opts?.fetcher ?? fetcher - const wrapper: typeof fetcher = (context) => { - context.input.cartId = Cookies.get(cartCookie) - return fetcherFn(context) +export default function useCart<Data extends Cart | null>( + options: HookFetcherOptions, + input: HookInput, + fetcherFn: HookFetcher<Data, CartInput>, + swrOptions?: SwrOptions<Data, CartInput> +): CartResponse<Data> { + const { cartCookie } = useCommerce() + const fetcher: typeof fetcherFn = (options, input, fetch) => { + input.cartId = Cookies.get(cartCookie) + return fetcherFn(options, input, fetch) } - const response = useData( - { ...opts, fetcher: wrapper }, - input, - provider.fetcher ?? fetcherRef.current - ) - const memoizedResponse = useMemo( - () => (opts?.onResponse ? opts.onResponse(response) : response), - [response] - ) + const response = useData(options, input, fetcher, swrOptions) - return memoizedResponse as CartResponse<P> + return response } diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index 82e86947d..506502ea2 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -7,57 +7,36 @@ import { useRef, } from 'react' import * as React from 'react' -import { Fetcher, HookHandler } from './utils/types' -import { Cart } from './types' -import type { FetchCartInput } from './cart/use-cart' +import { Fetcher } from './utils/types' -const Commerce = createContext<CommerceContextValue<any> | {}>({}) +const Commerce = createContext<CommerceContextValue | {}>({}) -export type Provider = CommerceConfig & { - fetcher: Fetcher - cart?: { - useCart?: HookHandler<Cart | null, [...any], FetchCartInput> - } - wishlist?: { - useWishlist?: HookHandler<Cart | null, [...any], FetchCartInput> - } -} - -export type CommerceProps<P extends Provider> = { +export type CommerceProps = { children?: ReactNode - provider: P config: CommerceConfig } -export type CommerceConfig = Omit< - CommerceContextValue<any>, - 'providerRef' | 'fetcherRef' +export type CommerceConfig = { fetcher: Fetcher<any> } & Omit< + CommerceContextValue, + 'fetcherRef' > -export type CommerceContextValue<P extends Provider> = { - providerRef: MutableRefObject<P> - fetcherRef: MutableRefObject<Fetcher> +export type CommerceContextValue = { + fetcherRef: MutableRefObject<Fetcher<any>> locale: string cartCookie: string } -export function CommerceProvider<P extends Provider>({ - provider, - children, - config, -}: CommerceProps<P>) { +export function CommerceProvider({ children, config }: CommerceProps) { if (!config) { throw new Error('CommerceProvider requires a valid config object') } - const providerRef = useRef(provider) - // TODO: Remove the fetcherRef - const fetcherRef = useRef(provider.fetcher) + const fetcherRef = useRef(config.fetcher) // Because the config is an object, if the parent re-renders this provider // will re-render every consumer unless we memoize the config const cfg = useMemo( () => ({ - providerRef, fetcherRef, locale: config.locale, cartCookie: config.cartCookie, @@ -68,6 +47,6 @@ export function CommerceProvider<P extends Provider>({ return <Commerce.Provider value={cfg}>{children}</Commerce.Provider> } -export function useCommerce<P extends Provider>() { - return useContext(Commerce) as CommerceContextValue<P> +export function useCommerce<T extends CommerceContextValue>() { + return useContext(Commerce) as T } diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index d84ec07f0..010205f62 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -1,17 +1,5 @@ -import type { ConfigInterface } from 'swr' -import type { CommerceError } from './errors' -import type { ResponseState } from './use-data' - -export type Override<T, K> = Omit<T, keyof K> & K - -// Returns the properties in T with the properties in type K changed from optional to required -export type PickRequired<T, K extends keyof T> = Omit<T, K> & - Required<Pick<T, K>> - // Core fetcher added by CommerceProvider -export type Fetcher<T = any, B = any> = ( - options: FetcherOptions<B> -) => T | Promise<T> +export type Fetcher<T> = (options: FetcherOptions) => T | Promise<T> export type FetcherOptions<Body = any> = { url?: string @@ -27,55 +15,12 @@ export type HookFetcher<Data, Input = null, Result = any> = ( fetch: <T = Result, Body = any>(options: FetcherOptions<Body>) => Promise<T> ) => Data | Promise<Data> -export type HookFetcherFn< - Data, - Input = unknown, - Result = any, - Body = any -> = (context: { - options: HookFetcherOptions | null - input: Input - fetch: <T = Result, B = Body>(options: FetcherOptions<B>) => Promise<T> - normalize?(data: Result): Data -}) => Data | Promise<Data> - export type HookFetcherOptions = { query?: string url?: string method?: string } -export type HookInputValue = string | number | boolean | undefined +export type HookInput = [string, string | number | boolean | undefined][] -export type HookInput = [string, HookInputValue][] - -export type HookFetchInput = { [k: string]: HookInputValue } - -export type HookHandler< - // Data obj returned by the hook and fetch operation - Data, - // Input expected by the hook - Input = [...any], - // Input expected before doing a fetch operation - FetchInput extends HookFetchInput = never, - // Data returned by the API after a fetch operation - Result = any, - // Body expected by the API endpoint - Body = any, - // Custom state added to the response object of SWR - State = {} -> = { - input?: Input - swrOptions?: SwrOptions<Data, FetchInput, Result> - onResponse?(response: ResponseState<Data>): ResponseState<Data> & State - onMutation?: any - fetchOptions?: HookFetcherOptions - fetcher?: HookFetcherFn<Data, FetchInput, Result, Body> - normalizer?(data: Result): Data -} - -export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< - Data, - CommerceError, - HookFetcher<Data, Input, Result> -> +export type Override<T, K> = Omit<T, keyof K> & K diff --git a/framework/commerce/utils/use-data-2.ts b/framework/commerce/utils/use-data-2.ts deleted file mode 100644 index 5536bb02c..000000000 --- a/framework/commerce/utils/use-data-2.ts +++ /dev/null @@ -1,81 +0,0 @@ -import useSWR, { responseInterface } from 'swr' -import type { - HookHandler, - HookInput, - HookFetchInput, - PickRequired, - Fetcher, -} from './types' -import defineProperty from './define-property' -import { CommerceError } from './errors' - -export type ResponseState<Result> = responseInterface<Result, CommerceError> & { - isLoading: boolean -} - -export type UseData = < - Data = any, - Input = [...any], - FetchInput extends HookFetchInput = never, - Result = any, - Body = any ->( - options: PickRequired< - HookHandler<Data, Input, FetchInput, Result, Body>, - 'fetcher' - >, - input: HookInput, - fetcherFn: Fetcher -) => ResponseState<Data> - -const useData: UseData = (options, input, fetcherFn) => { - const fetcher = async ( - url?: string, - query?: string, - method?: string, - ...args: any[] - ) => { - try { - return await options.fetcher({ - options: { url, query, method }, - // Transform the input array into an object - input: args.reduce((obj, val, i) => { - obj[input[i][0]!] = val - return obj - }, {}), - fetch: fetcherFn, - normalize: options.normalizer, - }) - } catch (error) { - // SWR will not log errors, but any error that's not an instance - // of CommerceError is not welcomed by this hook - if (!(error instanceof CommerceError)) { - console.error(error) - } - throw error - } - } - const response = useSWR( - () => { - const opts = options.fetchOptions - return opts - ? [opts.url, opts.query, opts.method, ...input.map((e) => e[1])] - : null - }, - fetcher, - options.swrOptions - ) - - if (!('isLoading' in response)) { - defineProperty(response, 'isLoading', { - get() { - return response.data === undefined - }, - enumerable: true, - }) - } - - return response -} - -export default useData From 65af7d55d129085617f6ae90c478d83ca282a81b Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Tue, 9 Feb 2021 09:14:42 +0200 Subject: [PATCH 099/221] change readme --- framework/shopify/README.md | 8 ++++++++ package.json | 11 ++++++++--- yarn.lock | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/framework/shopify/README.md b/framework/shopify/README.md index 1533ceccd..372e9d68f 100644 --- a/framework/shopify/README.md +++ b/framework/shopify/README.md @@ -1,3 +1,11 @@ +## Table of Contents + +- [Getting Started](#getting-started) + +# Shopify Storefront Data Hooks + +Collection of hooks and data fetching functions to integrate Shopify in a React application. Designed to work with [Next.js Commerce](https://commerce-theta-ashy.vercel.app). + ## Getting Started 1. Environment variables need to be set: diff --git a/package.json b/package.json index ebba98b84..ff492a35e 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ }, "dependencies": { "@reach/portal": "^0.11.2", + "@tailwindcss/ui": "^0.6.2", "@vercel/fetch": "^6.1.0", "body-scroll-lock": "^3.1.5", "bowser": "^2.11.0", @@ -39,13 +40,13 @@ "next-themes": "^0.0.4", "normalizr": "^3.6.1", "postcss-nesting": "^7.0.1", - "react": "^17.0.1", - "react-dom": "^17.0.1", + "react": "^16.14.0", + "react-dom": "^16.14.0", "react-merge-refs": "^1.1.0", "react-ticker": "^1.2.2", "swr": "^0.4.0", "tabbable": "^5.1.5", - "tailwindcss": "^2.0.2" + "tailwindcss": "^1.9" }, "devDependencies": { "@graphql-codegen/cli": "^1.20.0", @@ -55,6 +56,8 @@ "@manifoldco/swagger-to-ts": "^2.1.0", "@next/bundle-analyzer": "^10.0.1", "@types/body-scroll-lock": "^2.6.1", + "@types/bunyan": "^1.8.6", + "@types/bunyan-prettystream": "^0.1.31", "@types/classnames": "^2.2.10", "@types/cookie": "^0.4.0", "@types/js-cookie": "^2.2.6", @@ -63,6 +66,8 @@ "@types/lodash.throttle": "^4.1.6", "@types/node": "^14.14.16", "@types/react": "^17.0.0", + "bunyan": "^1.8.14", + "bunyan-prettystream": "^0.1.3", "graphql": "^15.4.0", "husky": "^4.3.8", "lint-staged": "^10.5.3", diff --git a/yarn.lock b/yarn.lock index 493da6d40..b46f7acce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7997,4 +7997,4 @@ yn@3.1.1: yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== \ No newline at end of file + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 954760865b58f2883e193118e4b28d52866646e6 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Tue, 9 Feb 2021 09:15:38 +0200 Subject: [PATCH 100/221] Revert "Merge branch 'master' of https://github.com/vercel/commerce into agnostic" This reverts commit bf50965a39ef0b1b956461ebe62070809fbe1d63, reversing changes made to 0dad4ddedbf0bff2d0b5800ca469fda0073889ea. --- assets/base.css | 8 ++++-- assets/components.css | 2 +- components/common/Avatar/Avatar.tsx | 13 +++++++--- .../common/Searchbar/Searchbar.module.css | 2 +- components/common/UserNav/UserNav.module.css | 6 +---- .../ProductSlider/ProductSlider.module.css | 4 +-- components/ui/Button/Button.module.css | 2 +- components/ui/Input/Input.module.css | 2 +- components/ui/context.tsx | 14 ---------- lib/hooks/useUserAvatar.ts | 26 ------------------- lib/logger.ts | 18 +++++++++++++ pages/search.tsx | 6 ++--- tailwind.config.js | 3 ++- 13 files changed, 45 insertions(+), 61 deletions(-) delete mode 100644 lib/hooks/useUserAvatar.ts create mode 100644 lib/logger.ts diff --git a/assets/base.css b/assets/base.css index dfdaf1475..f854065ba 100644 --- a/assets/base.css +++ b/assets/base.css @@ -3,6 +3,7 @@ --primary-2: #f1f3f5; --secondary: #000000; --secondary-2: #111; + --selection: var(--cyan); --text-base: #000000; @@ -12,14 +13,18 @@ --hover: rgba(0, 0, 0, 0.075); --hover-1: rgba(0, 0, 0, 0.15); --hover-2: rgba(0, 0, 0, 0.25); + --cyan: #22b8cf; --green: #37b679; --red: #da3c3c; --pink: #e64980; --purple: #f81ce5; + --blue: #0070f3; - --violet: #5f3dc4; + --violet-light: #7048e8; + --violet: #5f3dc4; + --accents-0: #f8f9fa; --accents-1: #f1f3f5; --accents-2: #e9ecef; @@ -127,4 +132,3 @@ a { opacity: 1; } } - diff --git a/assets/components.css b/assets/components.css index 8c4c5a357..ebebcc238 100644 --- a/assets/components.css +++ b/assets/components.css @@ -1,3 +1,3 @@ .fit { min-height: calc(100vh - 88px); -} \ No newline at end of file +} diff --git a/components/common/Avatar/Avatar.tsx b/components/common/Avatar/Avatar.tsx index f78aa1d01..351a117ec 100644 --- a/components/common/Avatar/Avatar.tsx +++ b/components/common/Avatar/Avatar.tsx @@ -1,5 +1,5 @@ -import { FC, useRef, useEffect } from 'react' -import { useUserAvatar } from '@lib/hooks/useUserAvatar' +import { FC, useState, useMemo, useRef, useEffect } from 'react' +import { getRandomPairOfColors } from '@lib/colors' interface Props { className?: string @@ -7,13 +7,18 @@ interface Props { } const Avatar: FC<Props> = ({}) => { + const [bg] = useState(useMemo(() => getRandomPairOfColors, [])) let ref = useRef() as React.MutableRefObject<HTMLInputElement> - let { userAvatar } = useUserAvatar() + + useEffect(() => { + if (ref && ref.current) { + ref.current.style.backgroundImage = `linear-gradient(140deg, ${bg[0]}, ${bg[1]} 100%)` + } + }, [bg]) return ( <div ref={ref} - style={{ backgroundImage: userAvatar }} className="inline-block h-8 w-8 rounded-full border-2 border-primary hover:border-secondary focus:border-secondary transition linear-out duration-150" > {/* Add an image - We're generating a gradient as placeholder <img></img> */} diff --git a/components/common/Searchbar/Searchbar.module.css b/components/common/Searchbar/Searchbar.module.css index 071a14ef0..500483195 100644 --- a/components/common/Searchbar/Searchbar.module.css +++ b/components/common/Searchbar/Searchbar.module.css @@ -7,7 +7,7 @@ } .input:focus { - @apply outline-none shadow-outline-normal; + @apply outline-none shadow-outline-2; } .iconContainer { diff --git a/components/common/UserNav/UserNav.module.css b/components/common/UserNav/UserNav.module.css index cd1a6ce1f..a319e3dac 100644 --- a/components/common/UserNav/UserNav.module.css +++ b/components/common/UserNav/UserNav.module.css @@ -24,11 +24,7 @@ } .bagCount { - @apply border border-accents-1 bg-secondary text-secondary absolute rounded-full right-3 top-3 flex items-center justify-center font-bold text-xs; - padding-left: 2.5px; - padding-right: 2.5px; - min-width: 1.25rem; - min-height: 1.25rem; + @apply border border-accents-1 bg-secondary text-secondary h-4 w-4 absolute rounded-full right-3 top-3 flex items-center justify-center font-bold text-xs; } .avatarButton { diff --git a/components/product/ProductSlider/ProductSlider.module.css b/components/product/ProductSlider/ProductSlider.module.css index 259d15801..5ad48cc3d 100644 --- a/components/product/ProductSlider/ProductSlider.module.css +++ b/components/product/ProductSlider/ProductSlider.module.css @@ -15,7 +15,7 @@ .leftControl:hover, .rightControl:hover { - @apply outline-none shadow-outline-normal; + @apply outline-none shadow-outline-blue; } .leftControl { @@ -70,7 +70,7 @@ } .positionIndicator:focus .dot { - @apply shadow-outline-normal; + @apply shadow-outline-blue; } .positionIndicatorActive .dot { diff --git a/components/ui/Button/Button.module.css b/components/ui/Button/Button.module.css index 5b563f496..df2be8802 100644 --- a/components/ui/Button/Button.module.css +++ b/components/ui/Button/Button.module.css @@ -7,7 +7,7 @@ } .root:focus { - @apply shadow-outline-normal outline-none; + @apply shadow-outline outline-none; } .root[data-active] { diff --git a/components/ui/Input/Input.module.css b/components/ui/Input/Input.module.css index 9daee1418..9ace85277 100644 --- a/components/ui/Input/Input.module.css +++ b/components/ui/Input/Input.module.css @@ -3,5 +3,5 @@ } .root:focus { - @apply outline-none shadow-outline-normal; + @apply outline-none shadow-outline-gray; } diff --git a/components/ui/context.tsx b/components/ui/context.tsx index 013589941..206573858 100644 --- a/components/ui/context.tsx +++ b/components/ui/context.tsx @@ -52,10 +52,6 @@ type Action = type: 'SET_MODAL_VIEW' view: MODAL_VIEWS } - | { - type: 'SET_USER_AVATAR' - value: string - } type MODAL_VIEWS = 'SIGNUP_VIEW' | 'LOGIN_VIEW' | 'FORGOT_VIEW' type ToastText = string @@ -127,12 +123,6 @@ function uiReducer(state: State, action: Action) { toastText: action.text, } } - case 'SET_USER_AVATAR': { - return { - ...state, - userAvatar: action.value, - } - } } } @@ -157,9 +147,6 @@ export const UIProvider: FC = (props) => { const openToast = () => dispatch({ type: 'OPEN_TOAST' }) const closeToast = () => dispatch({ type: 'CLOSE_TOAST' }) - const setUserAvatar = (value: string) => - dispatch({ type: 'SET_USER_AVATAR', value }) - const setModalView = (view: MODAL_VIEWS) => dispatch({ type: 'SET_MODAL_VIEW', view }) @@ -177,7 +164,6 @@ export const UIProvider: FC = (props) => { setModalView, openToast, closeToast, - setUserAvatar, }), [state] ) diff --git a/lib/hooks/useUserAvatar.ts b/lib/hooks/useUserAvatar.ts deleted file mode 100644 index 840daae6d..000000000 --- a/lib/hooks/useUserAvatar.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { useEffect } from 'react' -import { useUI } from '@components/ui/context' -import { getRandomPairOfColors } from '@lib/colors' - -export const useUserAvatar = (name = 'userAvatar') => { - const { userAvatar, setUserAvatar } = useUI() - - useEffect(() => { - if (!userAvatar && localStorage.getItem(name)) { - // Get bg from localStorage and push it to the context. - setUserAvatar(localStorage.getItem(name)) - } - if (!localStorage.getItem(name)) { - // bg not set locally, generating one, setting localStorage and context to persist. - const bg = getRandomPairOfColors() - const value = `linear-gradient(140deg, ${bg[0]}, ${bg[1]} 100%)` - localStorage.setItem(name, value) - setUserAvatar(value) - } - }, []) - - return { - userAvatar, - setUserAvatar, - } -} diff --git a/lib/logger.ts b/lib/logger.ts new file mode 100644 index 000000000..eeda2c325 --- /dev/null +++ b/lib/logger.ts @@ -0,0 +1,18 @@ +import bunyan from 'bunyan' +import PrettyStream from 'bunyan-prettystream' + +const prettyStdOut = new PrettyStream() + +const log = bunyan.createLogger({ + name: 'Next.js - Commerce', + level: 'debug', + streams: [ + { + level: 'debug', + type: 'raw', + stream: prettyStdOut, + }, + ], +}) + +export default log diff --git a/pages/search.tsx b/pages/search.tsx index 6357232f0..64fea5600 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -96,7 +96,7 @@ export default function Search({ <button type="button" onClick={(e) => handleClick(e, 'categories')} - className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-normal active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150" + className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150" id="options-menu" aria-haspopup="true" aria-expanded="true" @@ -195,7 +195,7 @@ export default function Search({ <button type="button" onClick={(e) => handleClick(e, 'brands')} - className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-900 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-normal active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150" + className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-900 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150" id="options-menu" aria-haspopup="true" aria-expanded="true" @@ -372,7 +372,7 @@ export default function Search({ <button type="button" onClick={(e) => handleClick(e, 'sort')} - className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-normal active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150" + className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150" id="options-menu" aria-haspopup="true" aria-expanded="true" diff --git a/tailwind.config.js b/tailwind.config.js index 1ee8cad1d..4793c44a1 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -51,7 +51,7 @@ module.exports = { secondary: 'var(--text-secondary)', }, boxShadow: { - 'outline-normal': '0 0 0 2px var(--accents-2)', + 'outline-2': '0 0 0 2px var(--accents-2)', magical: 'rgba(0, 0, 0, 0.02) 0px 30px 30px, rgba(0, 0, 0, 0.03) 0px 0px 8px, rgba(0, 0, 0, 0.05) 0px 1px 0px', }, @@ -63,4 +63,5 @@ module.exports = { }, }, }, + plugins: [require('@tailwindcss/ui')], } From a934cb51fdac955a48f2fc7961600986854fa479 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Tue, 9 Feb 2021 14:40:38 +0200 Subject: [PATCH 101/221] Revert "Revert "Merge branch 'agnostic' of https://github.com/vercel/commerce into agnostic"" This reverts commit c9a43f1bce0572d0eff41f3af893be8bdb00bedd. --- components/common/UserNav/UserNav.tsx | 3 +- .../wishlist/WishlistCard/WishlistCard.tsx | 4 +- framework/bigcommerce/cart/use-cart.tsx | 54 +-------- framework/bigcommerce/index.tsx | 42 ++----- framework/bigcommerce/provider.ts | 108 ++++++++++++++++++ framework/commerce/cart/use-cart.tsx | 67 +++++++---- framework/commerce/index.tsx | 45 ++++++-- framework/commerce/utils/types.ts | 61 +++++++++- framework/commerce/utils/use-data-2.ts | 81 +++++++++++++ 9 files changed, 342 insertions(+), 123 deletions(-) create mode 100644 framework/bigcommerce/provider.ts create mode 100644 framework/commerce/utils/use-data-2.ts diff --git a/components/common/UserNav/UserNav.tsx b/components/common/UserNav/UserNav.tsx index f8e6373d9..c615c18b1 100644 --- a/components/common/UserNav/UserNav.tsx +++ b/components/common/UserNav/UserNav.tsx @@ -1,6 +1,7 @@ import { FC } from 'react' import Link from 'next/link' import cn from 'classnames' +import type { LineItem } from '@framework/types' import useCart from '@framework/cart/use-cart' import useCustomer from '@framework/customer/use-customer' import { Heart, Bag } from '@components/icons' @@ -15,7 +16,7 @@ interface Props { const countItem = (count: number, item: LineItem) => count + item.quantity -const UserNav: FC<Props> = ({ className, children }) => { +const UserNav: FC<Props> = ({ className }) => { const { data } = useCart() const { data: customer } = useCustomer() const { toggleSidebar, closeSidebarIfPresent, openModal } = useUI() diff --git a/components/wishlist/WishlistCard/WishlistCard.tsx b/components/wishlist/WishlistCard/WishlistCard.tsx index 82147f575..d1a9403b3 100644 --- a/components/wishlist/WishlistCard/WishlistCard.tsx +++ b/components/wishlist/WishlistCard/WishlistCard.tsx @@ -42,8 +42,8 @@ const WishlistCard: FC<Props> = ({ product }) => { setLoading(true) try { await addItem({ - productId: Number(product.id), - variantId: Number(product.variants[0].id), + productId: product.id, + variantId: product.variants[0].id, }) openSidebar() setLoading(false) diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index afa37ec98..ba005ec59 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -1,52 +1,4 @@ -import type { HookFetcher } from '@commerce/utils/types' -import type { SwrOptions } from '@commerce/utils/use-data' -import useResponse from '@commerce/utils/use-response' -import useCommerceCart, { CartInput } from '@commerce/cart/use-cart' -import { normalizeCart } from '../lib/normalize' -import type { Cart, BigcommerceCart } from '../types' +import useCommerceCart, { UseCart } from '@commerce/cart/use-cart' +import type { BigcommerceProvider } from '..' -const defaultOpts = { - url: '/api/bigcommerce/cart', - method: 'GET', -} - -export const fetcher: HookFetcher<Cart | null, CartInput> = async ( - options, - { cartId }, - fetch -) => { - const data = cartId - ? await fetch<BigcommerceCart>({ ...defaultOpts, ...options }) - : null - return data && normalizeCart(data) -} - -export function extendHook( - customFetcher: typeof fetcher, - swrOptions?: SwrOptions<Cart | null, CartInput> -) { - const useCart = () => { - const response = useCommerceCart(defaultOpts, [], customFetcher, { - revalidateOnFocus: false, - ...swrOptions, - }) - const res = useResponse(response, { - descriptors: { - isEmpty: { - get() { - return (response.data?.lineItems.length ?? 0) <= 0 - }, - enumerable: true, - }, - }, - }) - - return res - } - - useCart.extend = extendHook - - return useCart -} - -export default extendHook(fetcher) +export default useCommerceCart as UseCart<BigcommerceProvider> diff --git a/framework/bigcommerce/index.tsx b/framework/bigcommerce/index.tsx index a4c9fffa5..83fbdbcbc 100644 --- a/framework/bigcommerce/index.tsx +++ b/framework/bigcommerce/index.tsx @@ -1,46 +1,17 @@ import { ReactNode } from 'react' -import * as React from 'react' import { CommerceConfig, CommerceProvider as CoreCommerceProvider, useCommerce as useCoreCommerce, } from '@commerce' -import { FetcherError } from '@commerce/utils/errors' +import { bigcommerceProvider, BigcommerceProvider } from './provider' -async function getText(res: Response) { - try { - return (await res.text()) || res.statusText - } catch (error) { - return res.statusText - } -} - -async function getError(res: Response) { - if (res.headers.get('Content-Type')?.includes('application/json')) { - const data = await res.json() - return new FetcherError({ errors: data.errors, status: res.status }) - } - return new FetcherError({ message: await getText(res), status: res.status }) -} +export { bigcommerceProvider } +export type { BigcommerceProvider } export const bigcommerceConfig: CommerceConfig = { locale: 'en-us', cartCookie: 'bc_cartId', - async fetcher({ url, method = 'GET', variables, body: bodyObj }) { - const hasBody = Boolean(variables || bodyObj) - const body = hasBody - ? JSON.stringify(variables ? { variables } : bodyObj) - : undefined - const headers = hasBody ? { 'Content-Type': 'application/json' } : undefined - const res = await fetch(url!, { method, body, headers }) - - if (res.ok) { - const { data } = await res.json() - return data - } - - throw await getError(res) - }, } export type BigcommerceConfig = Partial<CommerceConfig> @@ -52,10 +23,13 @@ export type BigcommerceProps = { export function CommerceProvider({ children, ...config }: BigcommerceProps) { return ( - <CoreCommerceProvider config={{ ...bigcommerceConfig, ...config }}> + <CoreCommerceProvider + provider={bigcommerceProvider} + config={{ ...bigcommerceConfig, ...config }} + > {children} </CoreCommerceProvider> ) } -export const useCommerce = () => useCoreCommerce() +export const useCommerce = () => useCoreCommerce<BigcommerceProvider>() diff --git a/framework/bigcommerce/provider.ts b/framework/bigcommerce/provider.ts new file mode 100644 index 000000000..b6385546c --- /dev/null +++ b/framework/bigcommerce/provider.ts @@ -0,0 +1,108 @@ +import { FetcherError } from '@commerce/utils/errors' +import type { Fetcher, HookHandler } from '@commerce/utils/types' +import type { FetchCartInput } from '@commerce/cart/use-cart' +import { normalizeCart } from './lib/normalize' +import type { Cart } from './types' + +async function getText(res: Response) { + try { + return (await res.text()) || res.statusText + } catch (error) { + return res.statusText + } +} + +async function getError(res: Response) { + if (res.headers.get('Content-Type')?.includes('application/json')) { + const data = await res.json() + return new FetcherError({ errors: data.errors, status: res.status }) + } + return new FetcherError({ message: await getText(res), status: res.status }) +} + +const fetcher: Fetcher = async ({ + url, + method = 'GET', + variables, + body: bodyObj, +}) => { + const hasBody = Boolean(variables || bodyObj) + const body = hasBody + ? JSON.stringify(variables ? { variables } : bodyObj) + : undefined + const headers = hasBody ? { 'Content-Type': 'application/json' } : undefined + const res = await fetch(url!, { method, body, headers }) + + if (res.ok) { + const { data } = await res.json() + return data + } + + throw await getError(res) +} + +const useCart: HookHandler< + Cart | null, + [], + FetchCartInput, + any, + any, + { isEmpty?: boolean } +> = { + fetchOptions: { + url: '/api/bigcommerce/cart', + method: 'GET', + }, + swrOptions: { + revalidateOnFocus: false, + }, + normalizer: normalizeCart, + onResponse(response) { + return Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, + }, + }) + }, +} + +const useWishlist: HookHandler< + Cart | null, + [], + FetchCartInput, + any, + any, + { isEmpty?: boolean } +> = { + fetchOptions: { + url: '/api/bigcommerce/wishlist', + method: 'GET', + }, + swrOptions: { + revalidateOnFocus: false, + }, + onResponse(response) { + return Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, + }, + }) + }, +} + +export const bigcommerceProvider = { + locale: 'en-us', + cartCookie: 'bc_cartId', + fetcher, + cartNormalizer: normalizeCart, + cart: { useCart }, + wishlist: { useWishlist }, +} + +export type BigcommerceProvider = typeof bigcommerceProvider diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index 0a7ba49ee..b19e609da 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -1,28 +1,55 @@ +import { useMemo } from 'react' import Cookies from 'js-cookie' -import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types' -import useData, { ResponseState, SwrOptions } from '../utils/use-data' import type { Cart } from '../types' -import { useCommerce } from '..' +import type { HookFetcherFn } from '../utils/types' +import useData from '../utils/use-data-2' +import { Provider, useCommerce } from '..' -export type CartResponse<Data> = ResponseState<Data> & { isEmpty?: boolean } - -// Input expected by the `useCart` hook -export type CartInput = { +export type FetchCartInput = { cartId?: Cart['id'] } -export default function useCart<Data extends Cart | null>( - options: HookFetcherOptions, - input: HookInput, - fetcherFn: HookFetcher<Data, CartInput>, - swrOptions?: SwrOptions<Data, CartInput> -): CartResponse<Data> { - const { cartCookie } = useCommerce() - const fetcher: typeof fetcherFn = (options, input, fetch) => { - input.cartId = Cookies.get(cartCookie) - return fetcherFn(options, input, fetch) - } - const response = useData(options, input, fetcher, swrOptions) +export type CartResponse<P extends Provider> = ReturnType< + NonNullable<NonNullable<NonNullable<P['cart']>['useCart']>['onResponse']> +> - return response +export type UseCart<P extends Provider> = ( + ...input: UseCartInput<P> +) => CartResponse<P> + +export type UseCartInput<P extends Provider> = NonNullable< + NonNullable<NonNullable<NonNullable<P['cart']>['useCart']>>['input'] +> + +export const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ + options, + input: { cartId }, + fetch, + normalize, +}) => { + const data = cartId ? await fetch({ ...options }) : null + return data && normalize ? normalize(data) : data +} + +export default function useCart<P extends Provider>(...input: UseCartInput<P>) { + const { providerRef, fetcherRef, cartCookie } = useCommerce<P>() + + const provider = providerRef.current + const opts = provider.cart?.useCart + const fetcherFn = opts?.fetcher ?? fetcher + const wrapper: typeof fetcher = (context) => { + context.input.cartId = Cookies.get(cartCookie) + return fetcherFn(context) + } + const response = useData( + { ...opts, fetcher: wrapper }, + input, + provider.fetcher ?? fetcherRef.current + ) + const memoizedResponse = useMemo( + () => (opts?.onResponse ? opts.onResponse(response) : response), + [response] + ) + + return memoizedResponse as CartResponse<P> } diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index 506502ea2..82e86947d 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -7,36 +7,57 @@ import { useRef, } from 'react' import * as React from 'react' -import { Fetcher } from './utils/types' +import { Fetcher, HookHandler } from './utils/types' +import { Cart } from './types' +import type { FetchCartInput } from './cart/use-cart' -const Commerce = createContext<CommerceContextValue | {}>({}) +const Commerce = createContext<CommerceContextValue<any> | {}>({}) -export type CommerceProps = { +export type Provider = CommerceConfig & { + fetcher: Fetcher + cart?: { + useCart?: HookHandler<Cart | null, [...any], FetchCartInput> + } + wishlist?: { + useWishlist?: HookHandler<Cart | null, [...any], FetchCartInput> + } +} + +export type CommerceProps<P extends Provider> = { children?: ReactNode + provider: P config: CommerceConfig } -export type CommerceConfig = { fetcher: Fetcher<any> } & Omit< - CommerceContextValue, - 'fetcherRef' +export type CommerceConfig = Omit< + CommerceContextValue<any>, + 'providerRef' | 'fetcherRef' > -export type CommerceContextValue = { - fetcherRef: MutableRefObject<Fetcher<any>> +export type CommerceContextValue<P extends Provider> = { + providerRef: MutableRefObject<P> + fetcherRef: MutableRefObject<Fetcher> locale: string cartCookie: string } -export function CommerceProvider({ children, config }: CommerceProps) { +export function CommerceProvider<P extends Provider>({ + provider, + children, + config, +}: CommerceProps<P>) { if (!config) { throw new Error('CommerceProvider requires a valid config object') } - const fetcherRef = useRef(config.fetcher) + const providerRef = useRef(provider) + // TODO: Remove the fetcherRef + const fetcherRef = useRef(provider.fetcher) // Because the config is an object, if the parent re-renders this provider // will re-render every consumer unless we memoize the config const cfg = useMemo( () => ({ + providerRef, fetcherRef, locale: config.locale, cartCookie: config.cartCookie, @@ -47,6 +68,6 @@ export function CommerceProvider({ children, config }: CommerceProps) { return <Commerce.Provider value={cfg}>{children}</Commerce.Provider> } -export function useCommerce<T extends CommerceContextValue>() { - return useContext(Commerce) as T +export function useCommerce<P extends Provider>() { + return useContext(Commerce) as CommerceContextValue<P> } diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 010205f62..d84ec07f0 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -1,5 +1,17 @@ +import type { ConfigInterface } from 'swr' +import type { CommerceError } from './errors' +import type { ResponseState } from './use-data' + +export type Override<T, K> = Omit<T, keyof K> & K + +// Returns the properties in T with the properties in type K changed from optional to required +export type PickRequired<T, K extends keyof T> = Omit<T, K> & + Required<Pick<T, K>> + // Core fetcher added by CommerceProvider -export type Fetcher<T> = (options: FetcherOptions) => T | Promise<T> +export type Fetcher<T = any, B = any> = ( + options: FetcherOptions<B> +) => T | Promise<T> export type FetcherOptions<Body = any> = { url?: string @@ -15,12 +27,55 @@ export type HookFetcher<Data, Input = null, Result = any> = ( fetch: <T = Result, Body = any>(options: FetcherOptions<Body>) => Promise<T> ) => Data | Promise<Data> +export type HookFetcherFn< + Data, + Input = unknown, + Result = any, + Body = any +> = (context: { + options: HookFetcherOptions | null + input: Input + fetch: <T = Result, B = Body>(options: FetcherOptions<B>) => Promise<T> + normalize?(data: Result): Data +}) => Data | Promise<Data> + export type HookFetcherOptions = { query?: string url?: string method?: string } -export type HookInput = [string, string | number | boolean | undefined][] +export type HookInputValue = string | number | boolean | undefined -export type Override<T, K> = Omit<T, keyof K> & K +export type HookInput = [string, HookInputValue][] + +export type HookFetchInput = { [k: string]: HookInputValue } + +export type HookHandler< + // Data obj returned by the hook and fetch operation + Data, + // Input expected by the hook + Input = [...any], + // Input expected before doing a fetch operation + FetchInput extends HookFetchInput = never, + // Data returned by the API after a fetch operation + Result = any, + // Body expected by the API endpoint + Body = any, + // Custom state added to the response object of SWR + State = {} +> = { + input?: Input + swrOptions?: SwrOptions<Data, FetchInput, Result> + onResponse?(response: ResponseState<Data>): ResponseState<Data> & State + onMutation?: any + fetchOptions?: HookFetcherOptions + fetcher?: HookFetcherFn<Data, FetchInput, Result, Body> + normalizer?(data: Result): Data +} + +export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< + Data, + CommerceError, + HookFetcher<Data, Input, Result> +> diff --git a/framework/commerce/utils/use-data-2.ts b/framework/commerce/utils/use-data-2.ts new file mode 100644 index 000000000..5536bb02c --- /dev/null +++ b/framework/commerce/utils/use-data-2.ts @@ -0,0 +1,81 @@ +import useSWR, { responseInterface } from 'swr' +import type { + HookHandler, + HookInput, + HookFetchInput, + PickRequired, + Fetcher, +} from './types' +import defineProperty from './define-property' +import { CommerceError } from './errors' + +export type ResponseState<Result> = responseInterface<Result, CommerceError> & { + isLoading: boolean +} + +export type UseData = < + Data = any, + Input = [...any], + FetchInput extends HookFetchInput = never, + Result = any, + Body = any +>( + options: PickRequired< + HookHandler<Data, Input, FetchInput, Result, Body>, + 'fetcher' + >, + input: HookInput, + fetcherFn: Fetcher +) => ResponseState<Data> + +const useData: UseData = (options, input, fetcherFn) => { + const fetcher = async ( + url?: string, + query?: string, + method?: string, + ...args: any[] + ) => { + try { + return await options.fetcher({ + options: { url, query, method }, + // Transform the input array into an object + input: args.reduce((obj, val, i) => { + obj[input[i][0]!] = val + return obj + }, {}), + fetch: fetcherFn, + normalize: options.normalizer, + }) + } catch (error) { + // SWR will not log errors, but any error that's not an instance + // of CommerceError is not welcomed by this hook + if (!(error instanceof CommerceError)) { + console.error(error) + } + throw error + } + } + const response = useSWR( + () => { + const opts = options.fetchOptions + return opts + ? [opts.url, opts.query, opts.method, ...input.map((e) => e[1])] + : null + }, + fetcher, + options.swrOptions + ) + + if (!('isLoading' in response)) { + defineProperty(response, 'isLoading', { + get() { + return response.data === undefined + }, + enumerable: true, + }) + } + + return response +} + +export default useData From 93fe44258cb090689acb7b11cc0945fe8fd66f2c Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Tue, 9 Feb 2021 15:02:22 +0200 Subject: [PATCH 102/221] align with upstream changes --- framework/shopify/api/checkout/index.ts | 2 +- framework/shopify/api/index.ts | 14 +++- .../shopify/api/utils/fetch-graphql-api.ts | 14 +--- framework/shopify/cart/use-cart.tsx | 70 +------------------ .../shopify/cart/utils/checkout-create.ts | 2 +- framework/shopify/config.ts | 40 ----------- framework/shopify/const.ts | 11 +++ framework/shopify/index.tsx | 20 +++++- framework/shopify/provider.ts | 63 +++++++++++++++++ framework/shopify/utils/customer-token.ts | 2 +- framework/shopify/utils/get-checkout-id.ts | 2 +- 11 files changed, 113 insertions(+), 127 deletions(-) delete mode 100644 framework/shopify/config.ts create mode 100644 framework/shopify/const.ts create mode 100644 framework/shopify/provider.ts diff --git a/framework/shopify/api/checkout/index.ts b/framework/shopify/api/checkout/index.ts index 344be62d6..1f1a5c491 100644 --- a/framework/shopify/api/checkout/index.ts +++ b/framework/shopify/api/checkout/index.ts @@ -7,7 +7,7 @@ import { SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CHECKOUT_URL_COOKIE, SHOPIFY_CUSTOMER_TOKEN_COOKIE, -} from '@framework/config' +} from '@framework/provider' import { getConfig } from '..' import associateCustomerWithCheckoutMutation from '@framework/utils/mutations/associate-customer-with-checkout' diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts index d246829ff..2a3c0c4ec 100644 --- a/framework/shopify/api/index.ts +++ b/framework/shopify/api/index.ts @@ -5,7 +5,19 @@ import { API_TOKEN, SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CUSTOMER_TOKEN_COOKIE, -} from '@framework/config' +} from '@framework/const' + +if (!API_URL) { + throw new Error( + `The environment variable NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN is missing and it's required to access your store` + ) +} + +if (!API_TOKEN) { + throw new Error( + `The environment variable NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store` + ) +} import fetchGraphqlApi from './utils/fetch-graphql-api' diff --git a/framework/shopify/api/utils/fetch-graphql-api.ts b/framework/shopify/api/utils/fetch-graphql-api.ts index 8ff4b27b7..a78eeed74 100644 --- a/framework/shopify/api/utils/fetch-graphql-api.ts +++ b/framework/shopify/api/utils/fetch-graphql-api.ts @@ -1,21 +1,9 @@ import type { GraphQLFetcher } from '@commerce/api' import fetch from './fetch' -import { API_URL, API_TOKEN } from '../../config' +import { API_URL, API_TOKEN } from '../../const' import { getError } from '@framework/utils/handle-fetch-response' -if (!API_URL) { - throw new Error( - `The environment variable NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN is missing and it's required to access your store` - ) -} - -if (!API_TOKEN) { - throw new Error( - `The environment variable NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store` - ) -} - const fetchGraphqlApi: GraphQLFetcher = async ( query: string, { variables } = {}, diff --git a/framework/shopify/cart/use-cart.tsx b/framework/shopify/cart/use-cart.tsx index a04c30def..e5ab8cafb 100644 --- a/framework/shopify/cart/use-cart.tsx +++ b/framework/shopify/cart/use-cart.tsx @@ -1,68 +1,4 @@ -import type { HookFetcher } from '@commerce/utils/types' -import type { SwrOptions } from '@commerce/utils/use-data' +import useCommerceCart, { UseCart } from '@commerce/cart/use-cart' +import type { ShopifyProvider } from '..' -import useResponse from '@commerce/utils/use-response' -import useCommerceCart, { CartInput } from '@commerce/cart/use-cart' -import getCheckoutQuery from '@framework/utils/queries/get-checkout-query' - -import { Cart } from '@commerce/types' -import { checkoutToCart, checkoutCreate } from './utils' -import { getConfig } from '@framework/api' - -const defaultOpts = { - query: getCheckoutQuery, -} - -export const fetcher: HookFetcher<Cart | null, CartInput> = async ( - options, - { cartId: checkoutId }, - fetch -) => { - let checkout - - if (checkoutId) { - const data = await fetch({ - ...defaultOpts, - ...options, - variables: { - checkoutId, - }, - }) - checkout = data?.node - } - - if (checkout?.completedAt || !checkoutId) { - checkout = await checkoutCreate(fetch) - } - - return checkoutToCart({ checkout }) -} - -export function extendHook( - customFetcher: typeof fetcher, - swrOptions?: SwrOptions<Cart | null, CartInput> -) { - const useCart = () => { - const response = useCommerceCart(defaultOpts, [], customFetcher, { - revalidateOnFocus: true, - ...swrOptions, - }) - const res = useResponse(response, { - descriptors: { - isEmpty: { - get() { - return (response.data?.lineItems.length ?? 0) <= 0 - }, - enumerable: true, - }, - }, - }) - return res - } - - useCart.extend = extendHook - - return useCart -} - -export default extendHook(fetcher) +export default useCommerceCart as UseCart<ShopifyProvider> diff --git a/framework/shopify/cart/utils/checkout-create.ts b/framework/shopify/cart/utils/checkout-create.ts index 17377e7f5..cb2038a10 100644 --- a/framework/shopify/cart/utils/checkout-create.ts +++ b/framework/shopify/cart/utils/checkout-create.ts @@ -1,7 +1,7 @@ import { SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CHECKOUT_URL_COOKIE, -} from '@framework/config' +} from '@framework/provider' import checkoutCreateMutation from '@framework/utils/mutations/checkout-create' import Cookies from 'js-cookie' diff --git a/framework/shopify/config.ts b/framework/shopify/config.ts deleted file mode 100644 index 2444f6033..000000000 --- a/framework/shopify/config.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { CommerceConfig } from '@commerce' -import handleFetchResponse from './utils/handle-fetch-response' - -export const SHOPIFY_CHECKOUT_ID_COOKIE = 'shopify_checkoutId' - -export const SHOPIFY_CHECKOUT_URL_COOKIE = 'shopify_checkoutUrl' - -export const SHOPIFY_CUSTOMER_TOKEN_COOKIE = 'shopify_customerToken' - -export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN - -export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json` - -export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN - -export type ShopifyConfig = { - locale: string - cartCookie: string - storeDomain: string | undefined -} & CommerceConfig - -const shopifyConfig: ShopifyConfig = { - locale: 'en-us', - cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, - storeDomain: STORE_DOMAIN, - async fetcher({ method = 'POST', query, variables }) { - return handleFetchResponse( - await fetch(API_URL, { - method, - body: JSON.stringify({ query, variables }), - headers: { - 'X-Shopify-Storefront-Access-Token': API_TOKEN!, - 'Content-Type': 'application/json', - }, - }) - ) - }, -} - -export default shopifyConfig diff --git a/framework/shopify/const.ts b/framework/shopify/const.ts new file mode 100644 index 000000000..a6e9e8d90 --- /dev/null +++ b/framework/shopify/const.ts @@ -0,0 +1,11 @@ +export const SHOPIFY_CHECKOUT_ID_COOKIE = 'shopify_checkoutId' + +export const SHOPIFY_CHECKOUT_URL_COOKIE = 'shopify_checkoutUrl' + +export const SHOPIFY_CUSTOMER_TOKEN_COOKIE = 'shopify_customerToken' + +export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN + +export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json` + +export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN diff --git a/framework/shopify/index.tsx b/framework/shopify/index.tsx index 0d6f9fca9..5b25d6b21 100644 --- a/framework/shopify/index.tsx +++ b/framework/shopify/index.tsx @@ -2,11 +2,24 @@ import * as React from 'react' import { ReactNode } from 'react' import { + CommerceConfig, CommerceProvider as CoreCommerceProvider, useCommerce as useCoreCommerce, } from '@commerce' -import shopifyConfig, { ShopifyConfig } from './config' +import { shopifyProvider, ShopifyProvider } from './provider' +import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const' + +export { shopifyProvider } +export type { ShopifyProvider } + +export const shopifyConfig: CommerceConfig = { + locale: 'en-us', + cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, +} + +export type ShopifyConfig = Partial<CommerceConfig> + export type ShopifyProps = { children?: ReactNode locale: string @@ -14,7 +27,10 @@ export type ShopifyProps = { export function CommerceProvider({ children, ...config }: ShopifyProps) { return ( - <CoreCommerceProvider config={{ ...shopifyConfig, ...config }}> + <CoreCommerceProvider + provider={shopifyProvider} + config={{ ...shopifyConfig, ...config }} + > {children} </CoreCommerceProvider> ) diff --git a/framework/shopify/provider.ts b/framework/shopify/provider.ts new file mode 100644 index 000000000..bd8872b1f --- /dev/null +++ b/framework/shopify/provider.ts @@ -0,0 +1,63 @@ +import { Fetcher, HookHandler } from '@commerce/utils/types' +import { + API_TOKEN, + API_URL, + SHOPIFY_CHECKOUT_ID_COOKIE, + STORE_DOMAIN, +} from './const' +import { normalizeCart } from './lib/normalize' +import { Cart } from './types' + +import handleFetchResponse from './utils/handle-fetch-response' + +const useCart: HookHandler< + Cart | null, + [], + any, + any, + any, + { isEmpty?: boolean } +> = { + fetchOptions: { + url: '/api/bigcommerce/cart', + method: 'GET', + }, + swrOptions: { + revalidateOnFocus: false, + }, + normalizer: normalizeCart, + onResponse(response) { + return Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, + }, + }) + }, +} + +const fetcher: Fetcher = async ({ method = 'GET', variables, query }) => { + return handleFetchResponse( + await fetch(API_URL, { + method, + body: JSON.stringify({ query, variables }), + headers: { + 'X-Shopify-Storefront-Access-Token': API_TOKEN!, + 'Content-Type': 'application/json', + }, + }) + ) +} + +export const shopifyProvider = { + locale: 'en-us', + cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, + storeDomain: STORE_DOMAIN, + fetcher, + cartNormalizer: normalizeCart, + cart: { useCart }, +} + +export type ShopifyProvider = typeof shopifyProvider diff --git a/framework/shopify/utils/customer-token.ts b/framework/shopify/utils/customer-token.ts index 119f465b5..beae54765 100644 --- a/framework/shopify/utils/customer-token.ts +++ b/framework/shopify/utils/customer-token.ts @@ -1,5 +1,5 @@ import Cookies from 'js-cookie' -import { SHOPIFY_CUSTOMER_TOKEN_COOKIE } from '@framework/config' +import { SHOPIFY_CUSTOMER_TOKEN_COOKIE } from '../const' export const getCustomerToken = () => Cookies.get(SHOPIFY_CUSTOMER_TOKEN_COOKIE) diff --git a/framework/shopify/utils/get-checkout-id.ts b/framework/shopify/utils/get-checkout-id.ts index 623cf9577..11e3802d9 100644 --- a/framework/shopify/utils/get-checkout-id.ts +++ b/framework/shopify/utils/get-checkout-id.ts @@ -1,5 +1,5 @@ import Cookies from 'js-cookie' -import { SHOPIFY_CHECKOUT_ID_COOKIE } from '../config' +import { SHOPIFY_CHECKOUT_ID_COOKIE } from '../const' const getCheckoutId = (id?: string) => { return id ?? Cookies.get(SHOPIFY_CHECKOUT_ID_COOKIE) From 5aecb0f303d1e452dc317bcb0e8e68b42e6e4473 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Tue, 9 Feb 2021 13:23:48 -0500 Subject: [PATCH 103/221] Updated how the hook input is handled --- framework/bigcommerce/index.tsx | 2 +- framework/bigcommerce/provider.ts | 4 ++-- framework/commerce/cart/use-cart.tsx | 22 ++++++++++++-------- framework/commerce/index.tsx | 4 ++-- framework/commerce/utils/types.ts | 18 +++++++++++----- framework/commerce/utils/use-data-2.ts | 11 +++++----- framework/commerce/utils/use-data.tsx | 4 ++-- framework/commerce/wishlist/use-wishlist.tsx | 8 +++++-- 8 files changed, 45 insertions(+), 28 deletions(-) diff --git a/framework/bigcommerce/index.tsx b/framework/bigcommerce/index.tsx index 83fbdbcbc..b35785ed2 100644 --- a/framework/bigcommerce/index.tsx +++ b/framework/bigcommerce/index.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react' +import type { ReactNode } from 'react' import { CommerceConfig, CommerceProvider as CoreCommerceProvider, diff --git a/framework/bigcommerce/provider.ts b/framework/bigcommerce/provider.ts index b6385546c..e4ab5c757 100644 --- a/framework/bigcommerce/provider.ts +++ b/framework/bigcommerce/provider.ts @@ -43,7 +43,7 @@ const fetcher: Fetcher = async ({ const useCart: HookHandler< Cart | null, - [], + {}, FetchCartInput, any, any, @@ -71,7 +71,7 @@ const useCart: HookHandler< const useWishlist: HookHandler< Cart | null, - [], + {}, FetchCartInput, any, any, diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index b19e609da..8f40fd055 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -1,8 +1,8 @@ import { useMemo } from 'react' import Cookies from 'js-cookie' -import type { Cart } from '../types' import type { HookFetcherFn } from '../utils/types' import useData from '../utils/use-data-2' +import type { Cart } from '../types' import { Provider, useCommerce } from '..' export type FetchCartInput = { @@ -13,13 +13,17 @@ export type CartResponse<P extends Provider> = ReturnType< NonNullable<NonNullable<NonNullable<P['cart']>['useCart']>['onResponse']> > -export type UseCart<P extends Provider> = ( - ...input: UseCartInput<P> -) => CartResponse<P> +export type UseCartInput<P extends Provider> = Parameters< + NonNullable< + NonNullable<NonNullable<NonNullable<P['cart']>['useCart']>>['input'] + > +>[0] -export type UseCartInput<P extends Provider> = NonNullable< - NonNullable<NonNullable<NonNullable<P['cart']>['useCart']>>['input'] -> +export type UseCart<P extends Provider> = Partial< + UseCartInput<P> +> extends UseCartInput<P> + ? (input?: UseCartInput<P>) => CartResponse<P> + : (input: UseCartInput<P>) => CartResponse<P> export const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ options, @@ -31,7 +35,7 @@ export const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ return data && normalize ? normalize(data) : data } -export default function useCart<P extends Provider>(...input: UseCartInput<P>) { +export default function useCart<P extends Provider>(input?: UseCartInput<P>) { const { providerRef, fetcherRef, cartCookie } = useCommerce<P>() const provider = providerRef.current @@ -43,7 +47,7 @@ export default function useCart<P extends Provider>(...input: UseCartInput<P>) { } const response = useData( { ...opts, fetcher: wrapper }, - input, + opts?.input ? opts.input(input ?? {}) : [], provider.fetcher ?? fetcherRef.current ) const memoizedResponse = useMemo( diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index 82e86947d..fa7e3e7e8 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -16,10 +16,10 @@ const Commerce = createContext<CommerceContextValue<any> | {}>({}) export type Provider = CommerceConfig & { fetcher: Fetcher cart?: { - useCart?: HookHandler<Cart | null, [...any], FetchCartInput> + useCart?: HookHandler<Cart | null, {}, FetchCartInput> } wishlist?: { - useWishlist?: HookHandler<Cart | null, [...any], FetchCartInput> + useWishlist?: HookHandler<Cart | null, {}, FetchCartInput> } } diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index d84ec07f0..6b4f1ddbe 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -4,11 +4,15 @@ import type { ResponseState } from './use-data' export type Override<T, K> = Omit<T, keyof K> & K -// Returns the properties in T with the properties in type K changed from optional to required +/** + * Returns the properties in T with the properties in type K changed from optional to required + */ export type PickRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>> -// Core fetcher added by CommerceProvider +/** + * Core fetcher added by CommerceProvider + */ export type Fetcher<T = any, B = any> = ( options: FetcherOptions<B> ) => T | Promise<T> @@ -47,15 +51,17 @@ export type HookFetcherOptions = { export type HookInputValue = string | number | boolean | undefined -export type HookInput = [string, HookInputValue][] +export type HookSwrInput = [string, HookInputValue][] export type HookFetchInput = { [k: string]: HookInputValue } +export type HookInput = {} + export type HookHandler< // Data obj returned by the hook and fetch operation Data, // Input expected by the hook - Input = [...any], + Input extends { [k: string]: unknown } = {}, // Input expected before doing a fetch operation FetchInput extends HookFetchInput = never, // Data returned by the API after a fetch operation @@ -65,7 +71,9 @@ export type HookHandler< // Custom state added to the response object of SWR State = {} > = { - input?: Input + input?( + input: Input & { swrOptions?: SwrOptions<Data, FetchInput, Result> } + ): HookFetchInput | HookSwrInput swrOptions?: SwrOptions<Data, FetchInput, Result> onResponse?(response: ResponseState<Data>): ResponseState<Data> & State onMutation?: any diff --git a/framework/commerce/utils/use-data-2.ts b/framework/commerce/utils/use-data-2.ts index 5536bb02c..d9a9e9a39 100644 --- a/framework/commerce/utils/use-data-2.ts +++ b/framework/commerce/utils/use-data-2.ts @@ -1,7 +1,7 @@ import useSWR, { responseInterface } from 'swr' import type { HookHandler, - HookInput, + HookSwrInput, HookFetchInput, PickRequired, Fetcher, @@ -15,7 +15,7 @@ export type ResponseState<Result> = responseInterface<Result, CommerceError> & { export type UseData = < Data = any, - Input = [...any], + Input extends { [k: string]: unknown } = {}, FetchInput extends HookFetchInput = never, Result = any, Body = any @@ -24,11 +24,12 @@ export type UseData = < HookHandler<Data, Input, FetchInput, Result, Body>, 'fetcher' >, - input: HookInput, + input: HookFetchInput | HookSwrInput, fetcherFn: Fetcher ) => ResponseState<Data> const useData: UseData = (options, input, fetcherFn) => { + const hookInput = Array.isArray(input) ? input : Object.entries(input) const fetcher = async ( url?: string, query?: string, @@ -40,7 +41,7 @@ const useData: UseData = (options, input, fetcherFn) => { options: { url, query, method }, // Transform the input array into an object input: args.reduce((obj, val, i) => { - obj[input[i][0]!] = val + obj[hookInput[i][0]!] = val return obj }, {}), fetch: fetcherFn, @@ -59,7 +60,7 @@ const useData: UseData = (options, input, fetcherFn) => { () => { const opts = options.fetchOptions return opts - ? [opts.url, opts.query, opts.method, ...input.map((e) => e[1])] + ? [opts.url, opts.query, opts.method, ...hookInput.map((e) => e[1])] : null }, fetcher, diff --git a/framework/commerce/utils/use-data.tsx b/framework/commerce/utils/use-data.tsx index 38af46a44..58a1a0a47 100644 --- a/framework/commerce/utils/use-data.tsx +++ b/framework/commerce/utils/use-data.tsx @@ -1,5 +1,5 @@ import useSWR, { ConfigInterface, responseInterface } from 'swr' -import type { HookInput, HookFetcher, HookFetcherOptions } from './types' +import type { HookSwrInput, HookFetcher, HookFetcherOptions } from './types' import defineProperty from './define-property' import { CommerceError } from './errors' import { useCommerce } from '..' @@ -16,7 +16,7 @@ export type ResponseState<Result> = responseInterface<Result, CommerceError> & { export type UseData = <Data = any, Input = null, Result = any>( options: HookFetcherOptions | (() => HookFetcherOptions | null), - input: HookInput, + input: HookSwrInput, fetcherFn: HookFetcher<Data, Input, Result>, swrOptions?: SwrOptions<Data, Input, Result> ) => ResponseState<Data> diff --git a/framework/commerce/wishlist/use-wishlist.tsx b/framework/commerce/wishlist/use-wishlist.tsx index df8bebe5c..5272766c6 100644 --- a/framework/commerce/wishlist/use-wishlist.tsx +++ b/framework/commerce/wishlist/use-wishlist.tsx @@ -1,4 +1,8 @@ -import type { HookInput, HookFetcher, HookFetcherOptions } from '../utils/types' +import type { + HookSwrInput, + HookFetcher, + HookFetcherOptions, +} from '../utils/types' import useData, { ResponseState, SwrOptions } from '../utils/use-data' export type WishlistResponse<Result> = ResponseState<Result> & { @@ -7,7 +11,7 @@ export type WishlistResponse<Result> = ResponseState<Result> & { export default function useWishlist<Result, Input = null>( options: HookFetcherOptions, - input: HookInput, + input: HookSwrInput, fetcherFn: HookFetcher<Result, Input>, swrOptions?: SwrOptions<Result, Input> ): WishlistResponse<Result> { From 0eeb290eb0ad702595329fb529ef66f4f6e25f14 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Wed, 10 Feb 2021 10:51:46 -0500 Subject: [PATCH 104/221] Add more options to the hook handler --- .../bigcommerce/{provider.ts => provider.tsx} | 17 ++++ framework/commerce/cart/use-cart.tsx | 85 +++++++++++++------ framework/commerce/utils/types.ts | 24 +++++- 3 files changed, 100 insertions(+), 26 deletions(-) rename framework/bigcommerce/{provider.ts => provider.tsx} (86%) diff --git a/framework/bigcommerce/provider.ts b/framework/bigcommerce/provider.tsx similarity index 86% rename from framework/bigcommerce/provider.ts rename to framework/bigcommerce/provider.tsx index e4ab5c757..0d794edd9 100644 --- a/framework/bigcommerce/provider.ts +++ b/framework/bigcommerce/provider.tsx @@ -1,3 +1,4 @@ +import { useMemo } from 'react' import { FetcherError } from '@commerce/utils/errors' import type { Fetcher, HookHandler } from '@commerce/utils/types' import type { FetchCartInput } from '@commerce/cart/use-cart' @@ -57,6 +58,22 @@ const useCart: HookHandler< revalidateOnFocus: false, }, normalizer: normalizeCart, + useHook({ input, useData }) { + const response = useData({ input }) + + return useMemo( + () => + Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, + }, + }), + [response] + ) + }, onResponse(response) { return Object.create(response, { isEmpty: { diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index 8f40fd055..4ca20df69 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -1,23 +1,24 @@ import { useMemo } from 'react' import Cookies from 'js-cookie' -import type { HookFetcherFn } from '../utils/types' -import useData from '../utils/use-data-2' import type { Cart } from '../types' +import type { Prop, HookFetcherFn, UseHookInput } from '../utils/types' +import useData from '../utils/use-data-2' import { Provider, useCommerce } from '..' export type FetchCartInput = { cartId?: Cart['id'] } -export type CartResponse<P extends Provider> = ReturnType< - NonNullable<NonNullable<NonNullable<P['cart']>['useCart']>['onResponse']> +export type UseCartHandler<P extends Provider> = Prop< + Prop<P, 'cart'>, + 'useCart' > -export type UseCartInput<P extends Provider> = Parameters< - NonNullable< - NonNullable<NonNullable<NonNullable<P['cart']>['useCart']>>['input'] - > ->[0] +export type CartResponse<P extends Provider> = ReturnType< + Prop<UseCartHandler<P>, 'onResponse'> +> + +export type UseCartInput<P extends Provider> = UseHookInput<UseCartHandler<P>> export type UseCart<P extends Provider> = Partial< UseCartInput<P> @@ -35,25 +36,59 @@ export const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ return data && normalize ? normalize(data) : data } -export default function useCart<P extends Provider>(input?: UseCartInput<P>) { +type X = UseCartInput<Provider> + +export default function useCart<P extends Provider>( + input: UseCartInput<P> = {} +) { const { providerRef, fetcherRef, cartCookie } = useCommerce<P>() const provider = providerRef.current const opts = provider.cart?.useCart - const fetcherFn = opts?.fetcher ?? fetcher - const wrapper: typeof fetcher = (context) => { - context.input.cartId = Cookies.get(cartCookie) - return fetcherFn(context) - } - const response = useData( - { ...opts, fetcher: wrapper }, - opts?.input ? opts.input(input ?? {}) : [], - provider.fetcher ?? fetcherRef.current - ) - const memoizedResponse = useMemo( - () => (opts?.onResponse ? opts.onResponse(response) : response), - [response] - ) - return memoizedResponse as CartResponse<P> + const { swrOptions, ...hookInput } = input + + return opts?.useHook!({ + input: hookInput, + swrOptions, + useData(ctx) { + const fetcherFn = opts?.fetcher ?? fetcher + const wrapper: typeof fetcher = (context) => { + context.input.cartId = Cookies.get(cartCookie) + return fetcherFn(context) + } + const response = useData( + { + ...opts, + fetcher: wrapper, + swrOptions: { + ...opts.swrOptions, + ...(ctx?.swrOptions ?? swrOptions), + }, + }, + ctx?.input ?? [], + provider.fetcher ?? fetcherRef.current + ) + return response + }, + }) + + // console.log(i) + + // const fetcherFn = opts?.fetcher ?? fetcher + // const wrapper: typeof fetcher = (context) => { + // context.input.cartId = Cookies.get(cartCookie) + // return fetcherFn(context) + // } + // const response = useData( + // { ...opts, fetcher: wrapper }, + // opts?.input ? opts.input(input ?? {}) : [], + // provider.fetcher ?? fetcherRef.current + // ) + // const memoizedResponse = useMemo( + // () => (opts?.onResponse ? opts.onResponse(response) : response), + // [response] + // ) + + // return memoizedResponse as CartResponse<P> } diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 6b4f1ddbe..d4d767ae1 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -74,9 +74,16 @@ export type HookHandler< input?( input: Input & { swrOptions?: SwrOptions<Data, FetchInput, Result> } ): HookFetchInput | HookSwrInput + useHook?(context: { + input: Input + swrOptions?: SwrOptions<Data, FetchInput, Result> + useData(context?: { + input?: HookFetchInput | HookSwrInput + swrOptions?: SwrOptions<Data, FetchInput, Result> + }): ResponseState<Data> + }): ResponseState<Data> & State swrOptions?: SwrOptions<Data, FetchInput, Result> onResponse?(response: ResponseState<Data>): ResponseState<Data> & State - onMutation?: any fetchOptions?: HookFetcherOptions fetcher?: HookFetcherFn<Data, FetchInput, Result, Body> normalizer?(data: Result): Data @@ -87,3 +94,18 @@ export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< CommerceError, HookFetcher<Data, Input, Result> > + +/** + * Returns the property K from type T excluding nullables + */ +export type Prop<T, K extends keyof T> = NonNullable<T[K]> + +export type UseHookParameters<H extends HookHandler<any>> = Parameters< + Prop<H, 'useHook'> +> + +export type UseHookInput< + H extends HookHandler<any> +> = UseHookParameters<H>[0]['input'] & { + swrOptions?: UseHookParameters<H>[0]['swrOptions'] +} From 271ed016311587fa0e62dad4a19ca7fa7e387453 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Wed, 10 Feb 2021 15:10:48 -0500 Subject: [PATCH 105/221] Final touches to the hook handler type --- framework/bigcommerce/provider.tsx | 30 ++---------- framework/commerce/cart/use-cart.tsx | 67 +++++++++----------------- framework/commerce/utils/types.ts | 16 +++--- framework/commerce/utils/use-data-2.ts | 8 +-- 4 files changed, 36 insertions(+), 85 deletions(-) diff --git a/framework/bigcommerce/provider.tsx b/framework/bigcommerce/provider.tsx index 0d794edd9..364e87539 100644 --- a/framework/bigcommerce/provider.tsx +++ b/framework/bigcommerce/provider.tsx @@ -54,12 +54,11 @@ const useCart: HookHandler< url: '/api/bigcommerce/cart', method: 'GET', }, - swrOptions: { - revalidateOnFocus: false, - }, normalizer: normalizeCart, useHook({ input, useData }) { - const response = useData({ input }) + const response = useData({ + swrOptions: { revalidateOnFocus: false, ...input.swrOptions }, + }) return useMemo( () => @@ -74,16 +73,6 @@ const useCart: HookHandler< [response] ) }, - onResponse(response) { - return Object.create(response, { - isEmpty: { - get() { - return (response.data?.lineItems.length ?? 0) <= 0 - }, - enumerable: true, - }, - }) - }, } const useWishlist: HookHandler< @@ -98,19 +87,6 @@ const useWishlist: HookHandler< url: '/api/bigcommerce/wishlist', method: 'GET', }, - swrOptions: { - revalidateOnFocus: false, - }, - onResponse(response) { - return Object.create(response, { - isEmpty: { - get() { - return (response.data?.lineItems.length ?? 0) <= 0 - }, - enumerable: true, - }, - }) - }, } export const bigcommerceProvider = { diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index 4ca20df69..9915c7478 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -1,7 +1,11 @@ -import { useMemo } from 'react' import Cookies from 'js-cookie' import type { Cart } from '../types' -import type { Prop, HookFetcherFn, UseHookInput } from '../utils/types' +import type { + Prop, + HookFetcherFn, + UseHookInput, + UseHookResponse, +} from '../utils/types' import useData from '../utils/use-data-2' import { Provider, useCommerce } from '..' @@ -14,12 +18,12 @@ export type UseCartHandler<P extends Provider> = Prop< 'useCart' > -export type CartResponse<P extends Provider> = ReturnType< - Prop<UseCartHandler<P>, 'onResponse'> -> - export type UseCartInput<P extends Provider> = UseHookInput<UseCartHandler<P>> +export type CartResponse<P extends Provider> = UseHookResponse< + UseCartHandler<P> +> + export type UseCart<P extends Provider> = Partial< UseCartInput<P> > extends UseCartInput<P> @@ -36,8 +40,6 @@ export const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ return data && normalize ? normalize(data) : data } -type X = UseCartInput<Provider> - export default function useCart<P extends Provider>( input: UseCartInput<P> = {} ) { @@ -46,49 +48,24 @@ export default function useCart<P extends Provider>( const provider = providerRef.current const opts = provider.cart?.useCart - const { swrOptions, ...hookInput } = input + const fetcherFn = opts?.fetcher ?? fetcher + const useHook = opts?.useHook ?? ((ctx) => ctx.useData()) - return opts?.useHook!({ - input: hookInput, - swrOptions, + const wrapper: typeof fetcher = (context) => { + context.input.cartId = Cookies.get(cartCookie) + return fetcherFn(context) + } + + return useHook({ + input, useData(ctx) { - const fetcherFn = opts?.fetcher ?? fetcher - const wrapper: typeof fetcher = (context) => { - context.input.cartId = Cookies.get(cartCookie) - return fetcherFn(context) - } const response = useData( - { - ...opts, - fetcher: wrapper, - swrOptions: { - ...opts.swrOptions, - ...(ctx?.swrOptions ?? swrOptions), - }, - }, + { ...opts, fetcher: wrapper }, ctx?.input ?? [], - provider.fetcher ?? fetcherRef.current + provider.fetcher ?? fetcherRef.current, + ctx?.swrOptions ?? input.swrOptions ) return response }, }) - - // console.log(i) - - // const fetcherFn = opts?.fetcher ?? fetcher - // const wrapper: typeof fetcher = (context) => { - // context.input.cartId = Cookies.get(cartCookie) - // return fetcherFn(context) - // } - // const response = useData( - // { ...opts, fetcher: wrapper }, - // opts?.input ? opts.input(input ?? {}) : [], - // provider.fetcher ?? fetcherRef.current - // ) - // const memoizedResponse = useMemo( - // () => (opts?.onResponse ? opts.onResponse(response) : response), - // [response] - // ) - - // return memoizedResponse as CartResponse<P> } diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index d4d767ae1..7a4b73f93 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -71,19 +71,13 @@ export type HookHandler< // Custom state added to the response object of SWR State = {} > = { - input?( - input: Input & { swrOptions?: SwrOptions<Data, FetchInput, Result> } - ): HookFetchInput | HookSwrInput useHook?(context: { - input: Input - swrOptions?: SwrOptions<Data, FetchInput, Result> + input: Input & { swrOptions?: SwrOptions<Data, FetchInput, Result> } useData(context?: { input?: HookFetchInput | HookSwrInput swrOptions?: SwrOptions<Data, FetchInput, Result> }): ResponseState<Data> }): ResponseState<Data> & State - swrOptions?: SwrOptions<Data, FetchInput, Result> - onResponse?(response: ResponseState<Data>): ResponseState<Data> & State fetchOptions?: HookFetcherOptions fetcher?: HookFetcherFn<Data, FetchInput, Result, Body> normalizer?(data: Result): Data @@ -104,8 +98,10 @@ export type UseHookParameters<H extends HookHandler<any>> = Parameters< Prop<H, 'useHook'> > +export type UseHookResponse<H extends HookHandler<any>> = ReturnType< + Prop<H, 'useHook'> +> + export type UseHookInput< H extends HookHandler<any> -> = UseHookParameters<H>[0]['input'] & { - swrOptions?: UseHookParameters<H>[0]['swrOptions'] -} +> = UseHookParameters<H>[0]['input'] diff --git a/framework/commerce/utils/use-data-2.ts b/framework/commerce/utils/use-data-2.ts index d9a9e9a39..e79daf632 100644 --- a/framework/commerce/utils/use-data-2.ts +++ b/framework/commerce/utils/use-data-2.ts @@ -5,6 +5,7 @@ import type { HookFetchInput, PickRequired, Fetcher, + SwrOptions, } from './types' import defineProperty from './define-property' import { CommerceError } from './errors' @@ -25,10 +26,11 @@ export type UseData = < 'fetcher' >, input: HookFetchInput | HookSwrInput, - fetcherFn: Fetcher + fetcherFn: Fetcher, + swrOptions?: SwrOptions<Data, FetchInput, Result> ) => ResponseState<Data> -const useData: UseData = (options, input, fetcherFn) => { +const useData: UseData = (options, input, fetcherFn, swrOptions) => { const hookInput = Array.isArray(input) ? input : Object.entries(input) const fetcher = async ( url?: string, @@ -64,7 +66,7 @@ const useData: UseData = (options, input, fetcherFn) => { : null }, fetcher, - options.swrOptions + swrOptions ) if (!('isLoading' in response)) { From 8966f0b583844094f36e50a523c054787ec9dbd9 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 11 Feb 2021 02:43:09 -0500 Subject: [PATCH 106/221] Moved useWishlist to use new handler --- framework/bigcommerce/cart/use-cart.tsx | 4 +- framework/bigcommerce/provider.tsx | 47 ++++++++++- .../bigcommerce/wishlist/use-wishlist.tsx | 80 +------------------ framework/commerce/cart/use-cart.tsx | 2 +- framework/commerce/index.tsx | 6 +- framework/commerce/types.ts | 5 ++ framework/commerce/utils/types.ts | 17 ++-- framework/commerce/utils/use-data-2.ts | 4 +- framework/commerce/wishlist/use-wishlist.tsx | 70 ++++++++++++---- 9 files changed, 124 insertions(+), 111 deletions(-) diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index ba005ec59..4f8a5cbcd 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -1,4 +1,4 @@ -import useCommerceCart, { UseCart } from '@commerce/cart/use-cart' +import useCart, { UseCart } from '@commerce/cart/use-cart' import type { BigcommerceProvider } from '..' -export default useCommerceCart as UseCart<BigcommerceProvider> +export default useCart as UseCart<BigcommerceProvider> diff --git a/framework/bigcommerce/provider.tsx b/framework/bigcommerce/provider.tsx index 364e87539..60106f7f8 100644 --- a/framework/bigcommerce/provider.tsx +++ b/framework/bigcommerce/provider.tsx @@ -3,6 +3,8 @@ import { FetcherError } from '@commerce/utils/errors' import type { Fetcher, HookHandler } from '@commerce/utils/types' import type { FetchCartInput } from '@commerce/cart/use-cart' import { normalizeCart } from './lib/normalize' +import type { Wishlist } from './api/wishlist' +import useCustomer from './customer/use-customer' import type { Cart } from './types' async function getText(res: Response) { @@ -76,9 +78,9 @@ const useCart: HookHandler< } const useWishlist: HookHandler< - Cart | null, - {}, - FetchCartInput, + Wishlist | null, + { includeProducts?: boolean }, + { customerId?: number; includeProducts: boolean }, any, any, { isEmpty?: boolean } @@ -87,6 +89,45 @@ const useWishlist: HookHandler< url: '/api/bigcommerce/wishlist', method: 'GET', }, + fetcher({ input: { customerId, includeProducts }, options, fetch }) { + if (!customerId) return null + + // Use a dummy base as we only care about the relative path + const url = new URL(options.url!, 'http://a') + + if (includeProducts) url.searchParams.set('products', '1') + + return fetch({ + url: url.pathname + url.search, + method: options.method, + }) + }, + useHook({ input, useData }) { + const { data: customer } = useCustomer() + const response = useData({ + input: [ + ['customerId', customer?.id], + ['includeProducts', input.includeProducts], + ], + swrOptions: { + revalidateOnFocus: false, + ...input.swrOptions, + }, + }) + + return useMemo( + () => + Object.create(response, { + isEmpty: { + get() { + return (response.data?.items?.length || 0) <= 0 + }, + enumerable: true, + }, + }), + [response] + ) + }, } export const bigcommerceProvider = { diff --git a/framework/bigcommerce/wishlist/use-wishlist.tsx b/framework/bigcommerce/wishlist/use-wishlist.tsx index 730bf19dc..dfa3d9dbc 100644 --- a/framework/bigcommerce/wishlist/use-wishlist.tsx +++ b/framework/bigcommerce/wishlist/use-wishlist.tsx @@ -1,78 +1,4 @@ -import { HookFetcher } from '@commerce/utils/types' -import { SwrOptions } from '@commerce/utils/use-data' -import useResponse from '@commerce/utils/use-response' -import useCommerceWishlist from '@commerce/wishlist/use-wishlist' -import type { Wishlist } from '../api/wishlist' -import useCustomer from '../customer/use-customer' +import useWishlist, { UseWishlist } from '@commerce/wishlist/use-wishlist' +import type { BigcommerceProvider } from '..' -const defaultOpts = { - url: '/api/bigcommerce/wishlist', - method: 'GET', -} - -export type { Wishlist } - -export interface UseWishlistOptions { - includeProducts?: boolean -} - -export interface UseWishlistInput extends UseWishlistOptions { - customerId?: number -} - -export const fetcher: HookFetcher<Wishlist | null, UseWishlistInput> = ( - options, - { customerId, includeProducts }, - fetch -) => { - if (!customerId) return null - - // Use a dummy base as we only care about the relative path - const url = new URL(options?.url ?? defaultOpts.url, 'http://a') - - if (includeProducts) url.searchParams.set('products', '1') - - return fetch({ - url: url.pathname + url.search, - method: options?.method ?? defaultOpts.method, - }) -} - -export function extendHook( - customFetcher: typeof fetcher, - swrOptions?: SwrOptions<Wishlist | null, UseWishlistInput> -) { - const useWishlist = ({ includeProducts }: UseWishlistOptions = {}) => { - const { data: customer } = useCustomer() - const response = useCommerceWishlist( - defaultOpts, - [ - ['customerId', customer?.id], - ['includeProducts', includeProducts], - ], - customFetcher, - { - revalidateOnFocus: false, - ...swrOptions, - } - ) - const res = useResponse(response, { - descriptors: { - isEmpty: { - get() { - return (response.data?.items?.length || 0) <= 0 - }, - set: (x) => x, - }, - }, - }) - - return res - } - - useWishlist.extend = extendHook - - return useWishlist -} - -export default extendHook(fetcher) +export default useWishlist as UseWishlist<BigcommerceProvider> diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index 9915c7478..6b1a3c789 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -60,7 +60,7 @@ export default function useCart<P extends Provider>( input, useData(ctx) { const response = useData( - { ...opts, fetcher: wrapper }, + { ...opts!, fetcher: wrapper }, ctx?.input ?? [], provider.fetcher ?? fetcherRef.current, ctx?.swrOptions ?? input.swrOptions diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index fa7e3e7e8..cb4136e3b 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -8,18 +8,18 @@ import { } from 'react' import * as React from 'react' import { Fetcher, HookHandler } from './utils/types' -import { Cart } from './types' import type { FetchCartInput } from './cart/use-cart' +import type { Cart, Wishlist } from './types' const Commerce = createContext<CommerceContextValue<any> | {}>({}) export type Provider = CommerceConfig & { fetcher: Fetcher cart?: { - useCart?: HookHandler<Cart | null, {}, FetchCartInput> + useCart?: HookHandler<Cart | null, any, FetchCartInput> } wishlist?: { - useWishlist?: HookHandler<Cart | null, {}, FetchCartInput> + useWishlist?: HookHandler<Wishlist | null, any, any> } } diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index cc04f52e1..743a93e4e 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -1,3 +1,5 @@ +import type { Wishlist as BCWishlist } from '@framework/api/wishlist' + export interface Discount { // The value of the discount, can be an amount or percentage value: number @@ -87,6 +89,9 @@ export interface Cart { discounts?: Discount[] } +// TODO: Properly define this type +export interface Wishlist extends BCWishlist {} + /** * Cart mutations */ diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 7a4b73f93..dbde3e7ec 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -33,21 +33,20 @@ export type HookFetcher<Data, Input = null, Result = any> = ( export type HookFetcherFn< Data, - Input = unknown, + Input = never, Result = any, Body = any > = (context: { - options: HookFetcherOptions | null + options: HookFetcherOptions input: Input fetch: <T = Result, B = Body>(options: FetcherOptions<B>) => Promise<T> normalize?(data: Result): Data }) => Data | Promise<Data> -export type HookFetcherOptions = { - query?: string - url?: string - method?: string -} +export type HookFetcherOptions = { method?: string } & ( + | { query: string; url?: string } + | { query?: string; url: string } +) export type HookInputValue = string | number | boolean | undefined @@ -63,7 +62,7 @@ export type HookHandler< // Input expected by the hook Input extends { [k: string]: unknown } = {}, // Input expected before doing a fetch operation - FetchInput extends HookFetchInput = never, + FetchInput extends HookFetchInput = {}, // Data returned by the API after a fetch operation Result = any, // Body expected by the API endpoint @@ -78,7 +77,7 @@ export type HookHandler< swrOptions?: SwrOptions<Data, FetchInput, Result> }): ResponseState<Data> }): ResponseState<Data> & State - fetchOptions?: HookFetcherOptions + fetchOptions: HookFetcherOptions fetcher?: HookFetcherFn<Data, FetchInput, Result, Body> normalizer?(data: Result): Data } diff --git a/framework/commerce/utils/use-data-2.ts b/framework/commerce/utils/use-data-2.ts index e79daf632..cc4d2cc5b 100644 --- a/framework/commerce/utils/use-data-2.ts +++ b/framework/commerce/utils/use-data-2.ts @@ -17,7 +17,7 @@ export type ResponseState<Result> = responseInterface<Result, CommerceError> & { export type UseData = < Data = any, Input extends { [k: string]: unknown } = {}, - FetchInput extends HookFetchInput = never, + FetchInput extends HookFetchInput = {}, Result = any, Body = any >( @@ -33,7 +33,7 @@ export type UseData = < const useData: UseData = (options, input, fetcherFn, swrOptions) => { const hookInput = Array.isArray(input) ? input : Object.entries(input) const fetcher = async ( - url?: string, + url: string, query?: string, method?: string, ...args: any[] diff --git a/framework/commerce/wishlist/use-wishlist.tsx b/framework/commerce/wishlist/use-wishlist.tsx index 5272766c6..c2e0d2dc1 100644 --- a/framework/commerce/wishlist/use-wishlist.tsx +++ b/framework/commerce/wishlist/use-wishlist.tsx @@ -1,20 +1,62 @@ +import type { Wishlist } from '../types' import type { - HookSwrInput, - HookFetcher, - HookFetcherOptions, + Prop, + HookFetcherFn, + UseHookInput, + UseHookResponse, } from '../utils/types' -import useData, { ResponseState, SwrOptions } from '../utils/use-data' +import useData from '../utils/use-data-2' +import { Provider, useCommerce } from '..' -export type WishlistResponse<Result> = ResponseState<Result> & { - isEmpty?: boolean +export type UseWishlistHandler<P extends Provider> = Prop< + Prop<P, 'wishlist'>, + 'useWishlist' +> + +export type UseWishlistInput<P extends Provider> = UseHookInput< + UseWishlistHandler<P> +> + +export type WishlistResponse<P extends Provider> = UseHookResponse< + UseWishlistHandler<P> +> + +export type UseWishlist<P extends Provider> = Partial< + WishlistResponse<P> +> extends WishlistResponse<P> + ? (input?: WishlistResponse<P>) => WishlistResponse<P> + : (input: WishlistResponse<P>) => WishlistResponse<P> + +export const fetcher: HookFetcherFn<Wishlist | null> = async ({ + options, + fetch, + normalize, +}) => { + const data = await fetch({ ...options }) + return data && normalize ? normalize(data) : data } -export default function useWishlist<Result, Input = null>( - options: HookFetcherOptions, - input: HookSwrInput, - fetcherFn: HookFetcher<Result, Input>, - swrOptions?: SwrOptions<Result, Input> -): WishlistResponse<Result> { - const response = useData(options, input, fetcherFn, swrOptions) - return response +export default function useWishlist<P extends Provider>( + input: UseWishlistInput<P> = {} +) { + const { providerRef, fetcherRef } = useCommerce<P>() + + const provider = providerRef.current + const opts = provider.wishlist?.useWishlist + + const fetcherFn = opts?.fetcher ?? fetcher + const useHook = opts?.useHook ?? ((ctx) => ctx.useData()) + + return useHook({ + input, + useData(ctx) { + const response = useData( + { ...opts!, fetcher: fetcherFn }, + ctx?.input ?? [], + provider.fetcher ?? fetcherRef.current, + ctx?.swrOptions ?? input.swrOptions + ) + return response + }, + }) } From d8de84bed139512b9cb18ed619d291fdc1a3667a Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 11 Feb 2021 13:26:09 -0500 Subject: [PATCH 107/221] Move useCustomer to the new hook --- .../bigcommerce/customer/use-customer.tsx | 40 +----------- framework/bigcommerce/provider.tsx | 24 +++++++ framework/commerce/customer/use-customer.tsx | 62 +++++++++++++++++++ framework/commerce/index.tsx | 6 +- framework/commerce/types.ts | 4 ++ framework/commerce/use-customer.tsx | 5 -- framework/commerce/utils/types.ts | 2 +- framework/commerce/wishlist/use-wishlist.tsx | 8 +-- 8 files changed, 102 insertions(+), 49 deletions(-) create mode 100644 framework/commerce/customer/use-customer.tsx delete mode 100644 framework/commerce/use-customer.tsx diff --git a/framework/bigcommerce/customer/use-customer.tsx b/framework/bigcommerce/customer/use-customer.tsx index f44f16c1f..95adb6fb3 100644 --- a/framework/bigcommerce/customer/use-customer.tsx +++ b/framework/bigcommerce/customer/use-customer.tsx @@ -1,38 +1,4 @@ -import type { HookFetcher } from '@commerce/utils/types' -import type { SwrOptions } from '@commerce/utils/use-data' -import useCommerceCustomer from '@commerce/use-customer' -import type { Customer, CustomerData } from '../api/customers' +import useCustomer, { UseCustomer } from '@commerce/customer/use-customer' +import type { BigcommerceProvider } from '..' -const defaultOpts = { - url: '/api/bigcommerce/customers', - method: 'GET', -} - -export type { Customer } - -export const fetcher: HookFetcher<Customer | null> = async ( - options, - _, - fetch -) => { - const data = await fetch<CustomerData | null>({ ...defaultOpts, ...options }) - return data?.customer ?? null -} - -export function extendHook( - customFetcher: typeof fetcher, - swrOptions?: SwrOptions<Customer | null> -) { - const useCustomer = () => { - return useCommerceCustomer(defaultOpts, [], customFetcher, { - revalidateOnFocus: false, - ...swrOptions, - }) - } - - useCustomer.extend = extendHook - - return useCustomer -} - -export default extendHook(fetcher) +export default useCustomer as UseCustomer<BigcommerceProvider> diff --git a/framework/bigcommerce/provider.tsx b/framework/bigcommerce/provider.tsx index 60106f7f8..d3ae4d171 100644 --- a/framework/bigcommerce/provider.tsx +++ b/framework/bigcommerce/provider.tsx @@ -4,6 +4,7 @@ import type { Fetcher, HookHandler } from '@commerce/utils/types' import type { FetchCartInput } from '@commerce/cart/use-cart' import { normalizeCart } from './lib/normalize' import type { Wishlist } from './api/wishlist' +import type { Customer, CustomerData } from './api/customers' import useCustomer from './customer/use-customer' import type { Cart } from './types' @@ -130,6 +131,28 @@ const useWishlist: HookHandler< }, } +const useCustomerHandler: HookHandler< + Customer | null, + {}, + {}, + CustomerData | null, + any +> = { + fetchOptions: { + url: '/api/bigcommerce/customers', + method: 'GET', + }, + normalizer: (data) => data.customer, + useHook({ input, useData }) { + return useData({ + swrOptions: { + revalidateOnFocus: false, + ...input.swrOptions, + }, + }) + }, +} + export const bigcommerceProvider = { locale: 'en-us', cartCookie: 'bc_cartId', @@ -137,6 +160,7 @@ export const bigcommerceProvider = { cartNormalizer: normalizeCart, cart: { useCart }, wishlist: { useWishlist }, + customer: { useCustomer: useCustomerHandler }, } export type BigcommerceProvider = typeof bigcommerceProvider diff --git a/framework/commerce/customer/use-customer.tsx b/framework/commerce/customer/use-customer.tsx new file mode 100644 index 000000000..f1675a544 --- /dev/null +++ b/framework/commerce/customer/use-customer.tsx @@ -0,0 +1,62 @@ +import type { Customer } from '../types' +import type { + Prop, + HookFetcherFn, + UseHookInput, + UseHookResponse, +} from '../utils/types' +import useData from '../utils/use-data-2' +import { Provider, useCommerce } from '..' + +export type UseCustomerHandler<P extends Provider> = Prop< + Prop<P, 'customer'>, + 'useCustomer' +> + +export type UseCustomerInput<P extends Provider> = UseHookInput< + UseCustomerHandler<P> +> + +export type CustomerResponse<P extends Provider> = UseHookResponse< + UseCustomerHandler<P> +> + +export type UseCustomer<P extends Provider> = Partial< + UseCustomerInput<P> +> extends UseCustomerInput<P> + ? (input?: UseCustomerInput<P>) => CustomerResponse<P> + : (input: UseCustomerInput<P>) => CustomerResponse<P> + +export const fetcher: HookFetcherFn<Customer | null> = async ({ + options, + fetch, + normalize, +}) => { + const data = await fetch({ ...options }) + return data && normalize ? normalize(data) : data +} + +export default function useCustomer<P extends Provider>( + input: UseCustomerInput<P> = {} +) { + const { providerRef, fetcherRef } = useCommerce<P>() + + const provider = providerRef.current + const opts = provider.customer?.useCustomer + + const fetcherFn = opts?.fetcher ?? fetcher + const useHook = opts?.useHook ?? ((ctx) => ctx.useData()) + + return useHook({ + input, + useData(ctx) { + const response = useData( + { ...opts!, fetcher: fetcherFn }, + ctx?.input ?? [], + provider.fetcher ?? fetcherRef.current, + ctx?.swrOptions ?? input.swrOptions + ) + return response + }, + }) +} diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index cb4136e3b..ccfc07e66 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -6,10 +6,9 @@ import { useMemo, useRef, } from 'react' -import * as React from 'react' import { Fetcher, HookHandler } from './utils/types' import type { FetchCartInput } from './cart/use-cart' -import type { Cart, Wishlist } from './types' +import type { Cart, Wishlist, Customer } from './types' const Commerce = createContext<CommerceContextValue<any> | {}>({}) @@ -21,6 +20,9 @@ export type Provider = CommerceConfig & { wishlist?: { useWishlist?: HookHandler<Wishlist | null, any, any> } + customer: { + useCustomer?: HookHandler<Customer | null, any, any> + } } export type CommerceProps<P extends Provider> = { diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index 743a93e4e..31aaa6fd7 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -1,4 +1,5 @@ import type { Wishlist as BCWishlist } from '@framework/api/wishlist' +import type { Customer as BCCustomer } from '@framework/api/customers' export interface Discount { // The value of the discount, can be an amount or percentage @@ -92,6 +93,9 @@ export interface Cart { // TODO: Properly define this type export interface Wishlist extends BCWishlist {} +// TODO: Properly define this type +export interface Customer extends BCCustomer {} + /** * Cart mutations */ diff --git a/framework/commerce/use-customer.tsx b/framework/commerce/use-customer.tsx deleted file mode 100644 index 8e2ff3ec2..000000000 --- a/framework/commerce/use-customer.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import useData from './utils/use-data' - -const useCustomer = useData - -export default useCustomer diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index dbde3e7ec..47da81a7f 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -79,7 +79,7 @@ export type HookHandler< }): ResponseState<Data> & State fetchOptions: HookFetcherOptions fetcher?: HookFetcherFn<Data, FetchInput, Result, Body> - normalizer?(data: Result): Data + normalizer?(data: NonNullable<Result>): Data } export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< diff --git a/framework/commerce/wishlist/use-wishlist.tsx b/framework/commerce/wishlist/use-wishlist.tsx index c2e0d2dc1..64bb5a1c1 100644 --- a/framework/commerce/wishlist/use-wishlist.tsx +++ b/framework/commerce/wishlist/use-wishlist.tsx @@ -22,10 +22,10 @@ export type WishlistResponse<P extends Provider> = UseHookResponse< > export type UseWishlist<P extends Provider> = Partial< - WishlistResponse<P> -> extends WishlistResponse<P> - ? (input?: WishlistResponse<P>) => WishlistResponse<P> - : (input: WishlistResponse<P>) => WishlistResponse<P> + UseWishlistInput<P> +> extends UseWishlistInput<P> + ? (input?: UseWishlistInput<P>) => WishlistResponse<P> + : (input: UseWishlistInput<P>) => WishlistResponse<P> export const fetcher: HookFetcherFn<Wishlist | null> = async ({ options, From 883fbcbcb941972b044227f03f3a88ce4b48521e Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 11 Feb 2021 13:35:17 -0500 Subject: [PATCH 108/221] Added a default fetcher --- framework/commerce/customer/use-customer.tsx | 10 ++-------- framework/commerce/utils/default-fetcher.ts | 12 ++++++++++++ framework/commerce/wishlist/use-wishlist.tsx | 10 ++-------- 3 files changed, 16 insertions(+), 16 deletions(-) create mode 100644 framework/commerce/utils/default-fetcher.ts diff --git a/framework/commerce/customer/use-customer.tsx b/framework/commerce/customer/use-customer.tsx index f1675a544..4fb9b430b 100644 --- a/framework/commerce/customer/use-customer.tsx +++ b/framework/commerce/customer/use-customer.tsx @@ -5,6 +5,7 @@ import type { UseHookInput, UseHookResponse, } from '../utils/types' +import defaultFetcher from '../utils/default-fetcher' import useData from '../utils/use-data-2' import { Provider, useCommerce } from '..' @@ -27,14 +28,7 @@ export type UseCustomer<P extends Provider> = Partial< ? (input?: UseCustomerInput<P>) => CustomerResponse<P> : (input: UseCustomerInput<P>) => CustomerResponse<P> -export const fetcher: HookFetcherFn<Customer | null> = async ({ - options, - fetch, - normalize, -}) => { - const data = await fetch({ ...options }) - return data && normalize ? normalize(data) : data -} +export const fetcher = defaultFetcher as HookFetcherFn<Customer | null> export default function useCustomer<P extends Provider>( input: UseCustomerInput<P> = {} diff --git a/framework/commerce/utils/default-fetcher.ts b/framework/commerce/utils/default-fetcher.ts new file mode 100644 index 000000000..25211a689 --- /dev/null +++ b/framework/commerce/utils/default-fetcher.ts @@ -0,0 +1,12 @@ +import { HookFetcherFn } from './types' + +const defaultFetcher: HookFetcherFn<any> = async ({ + options, + fetch, + normalize, +}) => { + const data = await fetch({ ...options }) + return data && normalize ? normalize(data) : data +} + +export default defaultFetcher diff --git a/framework/commerce/wishlist/use-wishlist.tsx b/framework/commerce/wishlist/use-wishlist.tsx index 64bb5a1c1..314f0a1c2 100644 --- a/framework/commerce/wishlist/use-wishlist.tsx +++ b/framework/commerce/wishlist/use-wishlist.tsx @@ -5,6 +5,7 @@ import type { UseHookInput, UseHookResponse, } from '../utils/types' +import defaultFetcher from '../utils/default-fetcher' import useData from '../utils/use-data-2' import { Provider, useCommerce } from '..' @@ -27,14 +28,7 @@ export type UseWishlist<P extends Provider> = Partial< ? (input?: UseWishlistInput<P>) => WishlistResponse<P> : (input: UseWishlistInput<P>) => WishlistResponse<P> -export const fetcher: HookFetcherFn<Wishlist | null> = async ({ - options, - fetch, - normalize, -}) => { - const data = await fetch({ ...options }) - return data && normalize ? normalize(data) : data -} +export const fetcher = defaultFetcher as HookFetcherFn<Wishlist | null> export default function useWishlist<P extends Provider>( input: UseWishlistInput<P> = {} From 8d801ce5d7af1b3e2554b6ea8eae4eca7d0896c3 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Fri, 12 Feb 2021 09:56:03 +0200 Subject: [PATCH 109/221] query all products for vendors & paths, improve search --- framework/shopify/api/checkout/index.ts | 3 +- .../shopify/api/utils/fetch-all-products.ts | 41 +++++++++ .../shopify/cart/utils/checkout-create.ts | 3 +- framework/shopify/common/get-all-pages.ts | 12 ++- framework/shopify/common/get-page.ts | 48 +++++++---- framework/shopify/common/get-site-info.ts | 30 +++---- .../shopify/product/get-all-product-paths.ts | 13 ++- framework/shopify/product/use-search.tsx | 33 ++++---- framework/shopify/provider.ts | 6 +- framework/shopify/utils/get-categories.ts | 29 +++++++ .../shopify/utils/get-search-variables.ts | 41 ++++++--- framework/shopify/utils/get-vendors.ts | 36 ++++++++ .../queries/get-all-product-vendors-query.ts | 17 ++++ .../queries/get-all-products-paths-query.ts | 5 +- .../utils/queries/get-all-products-query.ts | 84 ++++++++++--------- .../queries/get-collection-products-query.ts | 17 ++++ .../shopify/utils/queries/get-page-query.ts | 17 ++++ framework/shopify/utils/queries/index.ts | 3 + pages/index.tsx | 2 +- pages/search.tsx | 1 - 20 files changed, 315 insertions(+), 126 deletions(-) create mode 100644 framework/shopify/api/utils/fetch-all-products.ts create mode 100644 framework/shopify/utils/get-categories.ts create mode 100644 framework/shopify/utils/get-vendors.ts create mode 100644 framework/shopify/utils/queries/get-all-product-vendors-query.ts create mode 100644 framework/shopify/utils/queries/get-collection-products-query.ts create mode 100644 framework/shopify/utils/queries/get-page-query.ts diff --git a/framework/shopify/api/checkout/index.ts b/framework/shopify/api/checkout/index.ts index 1f1a5c491..0d0e752f0 100644 --- a/framework/shopify/api/checkout/index.ts +++ b/framework/shopify/api/checkout/index.ts @@ -7,7 +7,8 @@ import { SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CHECKOUT_URL_COOKIE, SHOPIFY_CUSTOMER_TOKEN_COOKIE, -} from '@framework/provider' +} from '@framework/const' + import { getConfig } from '..' import associateCustomerWithCheckoutMutation from '@framework/utils/mutations/associate-customer-with-checkout' diff --git a/framework/shopify/api/utils/fetch-all-products.ts b/framework/shopify/api/utils/fetch-all-products.ts new file mode 100644 index 000000000..0f6660cd5 --- /dev/null +++ b/framework/shopify/api/utils/fetch-all-products.ts @@ -0,0 +1,41 @@ +import { ProductEdge } from '@framework/schema' +import { ShopifyConfig } from '..' + +const fetchAllProducts = async ({ + config, + query, + variables, + acc = [], + cursor, +}: { + config: ShopifyConfig + query: string + acc?: ProductEdge[] + variables?: any + cursor?: string +}): Promise<ProductEdge[]> => { + const { data } = await config.fetch(query, { + variables: { ...variables, cursor }, + }) + + const edges: ProductEdge[] = data?.products?.edges ?? [] + const hasNextPage = data?.products?.pageInfo?.hasNextPage + acc = acc.concat(edges) + + if (hasNextPage) { + const cursor = edges.pop()?.cursor + if (cursor) { + return fetchAllProducts({ + config, + query, + variables, + acc, + cursor, + }) + } + } + + return acc +} + +export default fetchAllProducts diff --git a/framework/shopify/cart/utils/checkout-create.ts b/framework/shopify/cart/utils/checkout-create.ts index cb2038a10..9975befe8 100644 --- a/framework/shopify/cart/utils/checkout-create.ts +++ b/framework/shopify/cart/utils/checkout-create.ts @@ -1,7 +1,8 @@ import { SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CHECKOUT_URL_COOKIE, -} from '@framework/provider' +} from '@framework/const' + import checkoutCreateMutation from '@framework/utils/mutations/checkout-create' import Cookies from 'js-cookie' diff --git a/framework/shopify/common/get-all-pages.ts b/framework/shopify/common/get-all-pages.ts index 9546c3fb3..6cec2ef73 100644 --- a/framework/shopify/common/get-all-pages.ts +++ b/framework/shopify/common/get-all-pages.ts @@ -19,14 +19,12 @@ const getAllPages = async (options?: { config = getConfig(config) const { data } = await config.fetch(getAllPagesQuery, { variables }) + const edges = data?.pages?.edges - const pages = data.pages.edges.map(({ node }: PageEdge) => { - return { - ...node, - name: node.handle, - url: `${config!.locale}/${node.handle}`, - } - }) + const pages = edges?.map(({ node }: PageEdge) => ({ + ...node, + url: node.handle, + })) return { pages } } diff --git a/framework/shopify/common/get-page.ts b/framework/shopify/common/get-page.ts index 8c2fb7165..803272918 100644 --- a/framework/shopify/common/get-page.ts +++ b/framework/shopify/common/get-page.ts @@ -1,27 +1,39 @@ -import { ShopifyConfig, getConfig } from '../api' -import type { Page } from '../types' +import { GraphQLFetcherResult } from '@commerce/api' -export type { Page } +import { getConfig, ShopifyConfig } from '../api' +import getPageQuery from '@framework/utils/queries/get-page-query' +import { Page, PageEdge } from '@framework/schema' -export type GetPageResult<T extends { page?: any } = { page?: Page }> = T - -export type PageVariables = { - id: string +type Variables = { + slug: string } -async function getPage({ - url, - variables, - config, - preview, -}: { - url?: string - variables: PageVariables - config?: ShopifyConfig +type ReturnType = { + page: any +} + +const getPage = async (options: { + variables: Variables + config: ShopifyConfig preview?: boolean -}): Promise<GetPageResult> { +}): Promise<ReturnType> => { + let { config, variables } = options ?? {} config = getConfig(config) - return {} + + const { data }: GraphQLFetcherResult = await config.fetch(getPageQuery, { + variables, + }) + + const page: Page = data?.pageByHandle + + return { + page: page + ? { + ...page, + url: page?.handle, + } + : null, + } } export default getPage diff --git a/framework/shopify/common/get-site-info.ts b/framework/shopify/common/get-site-info.ts index 1a87e2d5d..f6cdaad85 100644 --- a/framework/shopify/common/get-site-info.ts +++ b/framework/shopify/common/get-site-info.ts @@ -1,30 +1,30 @@ -import { CollectionEdge } from '@framework/schema' +import getCategories, { Category } from '@framework/utils/get-categories' +import getVendors, { Brands } from '@framework/utils/get-vendors' + import { getConfig, ShopifyConfig } from '../api' -import getAllCollectionsQuery from '../utils/queries/get-all-collections-query' + +export type GetSiteInfoResult< + T extends { categories: any[]; brands: any[] } = { + categories: Category[] + brands: Brands + } +> = T const getSiteInfo = async (options?: { variables?: any config: ShopifyConfig preview?: boolean -}) => { - let { config, variables = { first: 250 } } = options ?? {} +}): Promise<GetSiteInfoResult> => { + let { config } = options ?? {} config = getConfig(config) - const { data } = await config.fetch(getAllCollectionsQuery, { variables }) - const edges = data.collections?.edges ?? [] - - const categories = edges.map( - ({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({ - entityId, - name, - path: `/${handle}`, - }) - ) + const categories = await getCategories(config) + const brands = await getVendors(config) return { categories, - brands: [], + brands, } } diff --git a/framework/shopify/product/get-all-product-paths.ts b/framework/shopify/product/get-all-product-paths.ts index e632219f7..7eff4e657 100644 --- a/framework/shopify/product/get-all-product-paths.ts +++ b/framework/shopify/product/get-all-product-paths.ts @@ -1,4 +1,5 @@ import { getConfig, ShopifyConfig } from '../api' +import fetchAllProducts from '../api/utils/fetch-all-products' import { ProductEdge } from '../schema' import getAllProductsPathsQuery from '../utils/queries/get-all-products-paths-query' @@ -9,21 +10,19 @@ type ReturnType = { const getAllProductPaths = async (options?: { variables?: any config?: ShopifyConfig - previe?: boolean + preview?: boolean }): Promise<ReturnType> => { let { config, variables = { first: 250 } } = options ?? {} config = getConfig(config) - const { data } = await config.fetch(getAllProductsPathsQuery, { + const products = await fetchAllProducts({ + config, + query: getAllProductsPathsQuery, variables, }) - const edges = data?.products?.edges - const productInfo = data?.products?.productInfo - const hasNextPage = productInfo?.hasNextPage - return { - products: edges?.map(({ node: { handle } }: ProductEdge) => ({ + products: products?.map(({ node: { handle } }: ProductEdge) => ({ node: { path: `/${handle}`, }, diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx index 51c390bba..6574db172 100644 --- a/framework/shopify/product/use-search.tsx +++ b/framework/shopify/product/use-search.tsx @@ -1,22 +1,22 @@ import useCommerceSearch from '@commerce/products/use-search' -import getAllProductsQuery from '@framework/utils/queries/get-all-products-query' +import { + getAllProductsQuery, + getCollectionProductsQuery, +} from '@framework/utils/queries' import type { Product } from 'framework/bigcommerce/schema' import type { HookFetcher } from '@commerce/utils/types' import type { SwrOptions } from '@commerce/utils/use-data' import type { ProductEdge } from '@framework/schema' -import { - searchByProductType, - searchByTag, -} from '@framework/utils/get-search-variables' +import getSearchVariables from '@framework/utils/get-search-variables' -import sortBy from '@framework/utils/get-sort-variables' import { normalizeProduct } from '@framework/lib/normalize' export type SearchProductsInput = { search?: string - categoryPath?: string + categoryId?: string + brandId?: string sort?: string } @@ -32,22 +32,20 @@ export type SearchProductsData = { export const fetcher: HookFetcher< SearchRequestProductsData, SearchProductsInput -> = (options, { search, categoryPath, sort }, fetch) => { +> = (options, input, fetch) => { return fetch({ query: options?.query, method: options?.method, variables: { - ...searchByProductType(search), - ...searchByTag(categoryPath), - ...sortBy(sort), + ...getSearchVariables(input), }, }).then( - ({ products }): SearchProductsData => { + (resp): SearchProductsData => { + const edges = resp.products?.edges + return { - products: products?.edges?.map(({ node: p }: ProductEdge) => - normalizeProduct(p) - ), - found: !!products?.edges?.length, + products: edges?.map(({ node: p }: ProductEdge) => normalizeProduct(p)), + found: !!edges?.length, } } ) @@ -64,7 +62,8 @@ export function extendHook( }, [ ['search', input.search], - ['categoryPath', input.categoryPath], + ['categoryId', input.categoryId], + ['brandId', input.brandId], ['sort', input.sort], ], customFetcher, diff --git a/framework/shopify/provider.ts b/framework/shopify/provider.ts index bd8872b1f..7c3a7a14e 100644 --- a/framework/shopify/provider.ts +++ b/framework/shopify/provider.ts @@ -9,6 +9,7 @@ import { normalizeCart } from './lib/normalize' import { Cart } from './types' import handleFetchResponse from './utils/handle-fetch-response' +import { getCheckoutQuery } from './utils/queries' const useCart: HookHandler< Cart | null, @@ -19,8 +20,7 @@ const useCart: HookHandler< { isEmpty?: boolean } > = { fetchOptions: { - url: '/api/bigcommerce/cart', - method: 'GET', + query: getCheckoutQuery, }, swrOptions: { revalidateOnFocus: false, @@ -38,7 +38,7 @@ const useCart: HookHandler< }, } -const fetcher: Fetcher = async ({ method = 'GET', variables, query }) => { +const fetcher: Fetcher = async ({ method = 'POST', variables, query }) => { return handleFetchResponse( await fetch(API_URL, { method, diff --git a/framework/shopify/utils/get-categories.ts b/framework/shopify/utils/get-categories.ts new file mode 100644 index 000000000..3319d827d --- /dev/null +++ b/framework/shopify/utils/get-categories.ts @@ -0,0 +1,29 @@ +import { ShopifyConfig } from '@framework/api' +import { CollectionEdge } from '@framework/schema' +import getSiteCollectionsQuery from './queries/get-all-collections-query' + +export type Category = { + endityId: string + name: string + path: string +} + +const getCategories = async (config: ShopifyConfig): Promise<Category[]> => { + const { data } = await config.fetch(getSiteCollectionsQuery, { + variables: { + first: 250, + }, + }) + + return ( + data?.collections?.edges?.map( + ({ node: { title: name, handle } }: CollectionEdge) => ({ + entityId: handle, + name, + path: `/${handle}`, + }) + ) ?? [] + ) +} + +export default getCategories diff --git a/framework/shopify/utils/get-search-variables.ts b/framework/shopify/utils/get-search-variables.ts index 0b37f015f..9bc91eca3 100644 --- a/framework/shopify/utils/get-search-variables.ts +++ b/framework/shopify/utils/get-search-variables.ts @@ -1,15 +1,30 @@ -export const searchByProductType = (search?: string) => { - return search - ? { - query: `product_type:${search}`, - } - : {} +import { SearchProductsInput } from '@framework/product/use-search' +import getSortVariables from './get-sort-variables' + +export const getSearchVariables = ({ + categoryId, + brandId, + search, + sort, +}: SearchProductsInput) => { + let query = '' + + if (search) { + query += `product_type:${search} OR title:${search} OR tag:${search}` + } + + if (categoryId) { + query += `tag:${categoryId}` + } + + if (brandId) { + query += `${categoryId ? ' AND ' : ''}vendor:${brandId}` + } + + return { + query, + ...getSortVariables(sort), + } } -export const searchByTag = (categoryPath?: string) => { - return categoryPath - ? { - query: `tag:${categoryPath}`, - } - : {} -} +export default getSearchVariables diff --git a/framework/shopify/utils/get-vendors.ts b/framework/shopify/utils/get-vendors.ts new file mode 100644 index 000000000..d3ebce194 --- /dev/null +++ b/framework/shopify/utils/get-vendors.ts @@ -0,0 +1,36 @@ +import { ShopifyConfig } from '@framework/api' +import fetchAllProducts from '@framework/api/utils/fetch-all-products' +import getAllProductVendors from './queries/get-all-product-vendors-query' + +export type BrandNode = { + name: string + path: string +} + +export type BrandEdge = { + node: BrandNode +} + +export type Brands = BrandEdge[] + +const getVendors = async (config: ShopifyConfig): Promise<BrandEdge[]> => { + const vendors = await fetchAllProducts({ + config, + query: getAllProductVendors, + variables: { + first: 250, + }, + }) + + let vendorsStrings = vendors.map(({ node: { vendor } }) => vendor) + + return [...new Set(vendorsStrings)].map((v) => ({ + node: { + entityId: v, + name: v, + path: `brands/${v}`, + }, + })) +} + +export default getVendors diff --git a/framework/shopify/utils/queries/get-all-product-vendors-query.ts b/framework/shopify/utils/queries/get-all-product-vendors-query.ts new file mode 100644 index 000000000..be08b8ec6 --- /dev/null +++ b/framework/shopify/utils/queries/get-all-product-vendors-query.ts @@ -0,0 +1,17 @@ +const getAllProductVendors = /* GraphQL */ ` + query getAllProductVendors($first: Int = 250, $cursor: String) { + products(first: $first, after: $cursor) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + vendor + } + cursor + } + } + } +` +export default getAllProductVendors diff --git a/framework/shopify/utils/queries/get-all-products-paths-query.ts b/framework/shopify/utils/queries/get-all-products-paths-query.ts index 74e9f490d..b8fe23b5b 100644 --- a/framework/shopify/utils/queries/get-all-products-paths-query.ts +++ b/framework/shopify/utils/queries/get-all-products-paths-query.ts @@ -1,6 +1,6 @@ const getAllProductsPathsQuery = /* GraphQL */ ` - query getAllProductPaths($first: Int!) { - products(first: $first) { + query getAllProductPaths($first: Int!, $cursor: String) { + products(first: $first, after: $cursor) { pageInfo { hasNextPage hasPreviousPage @@ -9,6 +9,7 @@ const getAllProductsPathsQuery = /* GraphQL */ ` node { handle } + cursor } } } diff --git a/framework/shopify/utils/queries/get-all-products-query.ts b/framework/shopify/utils/queries/get-all-products-query.ts index de84d60bf..4a6c20b6e 100644 --- a/framework/shopify/utils/queries/get-all-products-query.ts +++ b/framework/shopify/utils/queries/get-all-products-query.ts @@ -1,3 +1,46 @@ +export const productsFragment = ` +products( + first: $first + sortKey: $sortKey + reverse: $reverse + query: $query +) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + id + title + vendor + handle + description + priceRange { + minVariantPrice { + amount + currencyCode + } + } + images(first: 1) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + originalSrc + altText + width + height + } + } + } + } + } +} +` + const getAllProductsQuery = /* GraphQL */ ` query getAllProducts( $first: Int = 250 @@ -5,46 +48,7 @@ const getAllProductsQuery = /* GraphQL */ ` $sortKey: ProductSortKeys = RELEVANCE $reverse: Boolean = false ) { - products( - first: $first - sortKey: $sortKey - reverse: $reverse - query: $query - ) { - pageInfo { - hasNextPage - hasPreviousPage - } - edges { - node { - id - title - vendor - handle - description - priceRange { - minVariantPrice { - amount - currencyCode - } - } - images(first: 1) { - pageInfo { - hasNextPage - hasPreviousPage - } - edges { - node { - originalSrc - altText - width - height - } - } - } - } - } - } + ${productsFragment} } ` export default getAllProductsQuery diff --git a/framework/shopify/utils/queries/get-collection-products-query.ts b/framework/shopify/utils/queries/get-collection-products-query.ts new file mode 100644 index 000000000..dd504b575 --- /dev/null +++ b/framework/shopify/utils/queries/get-collection-products-query.ts @@ -0,0 +1,17 @@ +import { productsFragment } from './get-all-products-query' + +const getCollectionProductsQuery = /* GraphQL */ ` + query getProductsFromCollection( + $categoryHandle: String! + $first: Int = 250 + $query: String = "" + $sortKey: ProductSortKeys = RELEVANCE + $reverse: Boolean = false + ) { + collectionByHandle(handle: $categoryHandle) + { + ${productsFragment} + } + } +` +export default getCollectionProductsQuery diff --git a/framework/shopify/utils/queries/get-page-query.ts b/framework/shopify/utils/queries/get-page-query.ts new file mode 100644 index 000000000..91f80f9e7 --- /dev/null +++ b/framework/shopify/utils/queries/get-page-query.ts @@ -0,0 +1,17 @@ +export const getPageQuery = /* GraphQL */ ` + query($first: Int!) { + pages(first: $first) { + edges { + node { + id + title + handle + body + bodySummary + url + } + } + } + } +` +export default getPageQuery diff --git a/framework/shopify/utils/queries/index.ts b/framework/shopify/utils/queries/index.ts index e0506cb5a..f41cb2797 100644 --- a/framework/shopify/utils/queries/index.ts +++ b/framework/shopify/utils/queries/index.ts @@ -2,6 +2,9 @@ export { default as getSiteCollectionsQuery } from './get-all-collections-query' export { default as getProductQuery } from './get-all-products-paths-query' export { default as getAllProductsQuery } from './get-all-products-query' export { default as getAllProductsPathtsQuery } from './get-all-products-paths-query' +export { default as getAllProductVendors } from './get-all-product-vendors-query' +export { default as getCollectionProductsQuery } from './get-collection-products-query' export { default as getCheckoutQuery } from './get-checkout-query' export { default as getAllPagesQuery } from './get-all-pages-query' +export { default as getPageQuery } from './get-page-query' export { default as getCustomerQuery } from './get-checkout-query' diff --git a/pages/index.tsx b/pages/index.tsx index 811472674..4ddf561aa 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -31,7 +31,7 @@ export async function getStaticProps({ brands, pages, }, - revalidate: 14400, + revalidate: 1440, } } diff --git a/pages/search.tsx b/pages/search.tsx index 64fea5600..97bee34d4 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -71,7 +71,6 @@ export default function Search({ const { data } = useSearch({ search: typeof q === 'string' ? q : '', categoryId: activeCategory?.entityId, - categoryPath: activeCategory?.path, brandId: activeBrand?.entityId, sort: typeof sort === 'string' ? sort : '', }) From 469ed2cdfb0381be3ceb29359d4e72b5f0e03632 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Fri, 12 Feb 2021 10:00:34 +0200 Subject: [PATCH 110/221] Update use-search.tsx --- framework/shopify/product/use-search.tsx | 33 +++++++++--------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx index 6574db172..ba833e517 100644 --- a/framework/shopify/product/use-search.tsx +++ b/framework/shopify/product/use-search.tsx @@ -1,13 +1,10 @@ import useCommerceSearch from '@commerce/products/use-search' -import { - getAllProductsQuery, - getCollectionProductsQuery, -} from '@framework/utils/queries' +import { getAllProductsQuery } from '@framework/utils/queries' import type { Product } from 'framework/bigcommerce/schema' import type { HookFetcher } from '@commerce/utils/types' import type { SwrOptions } from '@commerce/utils/use-data' -import type { ProductEdge } from '@framework/schema' +import type { ProductConnection, ProductEdge } from '@framework/schema' import getSearchVariables from '@framework/utils/get-search-variables' @@ -30,25 +27,19 @@ export type SearchProductsData = { } export const fetcher: HookFetcher< - SearchRequestProductsData, + SearchProductsData, SearchProductsInput -> = (options, input, fetch) => { - return fetch({ +> = async (options, input, fetch) => { + const resp = await fetch({ query: options?.query, method: options?.method, - variables: { - ...getSearchVariables(input), - }, - }).then( - (resp): SearchProductsData => { - const edges = resp.products?.edges - - return { - products: edges?.map(({ node: p }: ProductEdge) => normalizeProduct(p)), - found: !!edges?.length, - } - } - ) + variables: getSearchVariables(input), + }) + const edges = resp.products?.edges + return { + products: edges?.map(({ node: p }: ProductEdge) => normalizeProduct(p)), + found: !!edges?.length, + } } export function extendHook( From 9ec9e23d6a538095007de3608299a8de4d6b4592 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Fri, 12 Feb 2021 10:29:32 +0200 Subject: [PATCH 111/221] fix cart after upstream changes --- framework/shopify/provider.ts | 63 ---------------------- framework/shopify/provider.tsx | 97 ++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 63 deletions(-) delete mode 100644 framework/shopify/provider.ts create mode 100644 framework/shopify/provider.tsx diff --git a/framework/shopify/provider.ts b/framework/shopify/provider.ts deleted file mode 100644 index 7c3a7a14e..000000000 --- a/framework/shopify/provider.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Fetcher, HookHandler } from '@commerce/utils/types' -import { - API_TOKEN, - API_URL, - SHOPIFY_CHECKOUT_ID_COOKIE, - STORE_DOMAIN, -} from './const' -import { normalizeCart } from './lib/normalize' -import { Cart } from './types' - -import handleFetchResponse from './utils/handle-fetch-response' -import { getCheckoutQuery } from './utils/queries' - -const useCart: HookHandler< - Cart | null, - [], - any, - any, - any, - { isEmpty?: boolean } -> = { - fetchOptions: { - query: getCheckoutQuery, - }, - swrOptions: { - revalidateOnFocus: false, - }, - normalizer: normalizeCart, - onResponse(response) { - return Object.create(response, { - isEmpty: { - get() { - return (response.data?.lineItems.length ?? 0) <= 0 - }, - enumerable: true, - }, - }) - }, -} - -const fetcher: Fetcher = async ({ method = 'POST', variables, query }) => { - return handleFetchResponse( - await fetch(API_URL, { - method, - body: JSON.stringify({ query, variables }), - headers: { - 'X-Shopify-Storefront-Access-Token': API_TOKEN!, - 'Content-Type': 'application/json', - }, - }) - ) -} - -export const shopifyProvider = { - locale: 'en-us', - cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, - storeDomain: STORE_DOMAIN, - fetcher, - cartNormalizer: normalizeCart, - cart: { useCart }, -} - -export type ShopifyProvider = typeof shopifyProvider diff --git a/framework/shopify/provider.tsx b/framework/shopify/provider.tsx new file mode 100644 index 000000000..e8770ac97 --- /dev/null +++ b/framework/shopify/provider.tsx @@ -0,0 +1,97 @@ +import { useMemo } from 'react' +import { Fetcher, HookFetcherFn, HookHandler } from '@commerce/utils/types' + +import { + API_TOKEN, + API_URL, + SHOPIFY_CHECKOUT_ID_COOKIE, + STORE_DOMAIN, +} from './const' + +import { Cart } from './types' +import { normalizeCart } from './lib/normalize' +import handleFetchResponse from './utils/handle-fetch-response' +import getCheckoutQuery from './utils/queries/get-checkout-query' +import { FetchCartInput, UseCartInput } from '@commerce/cart/use-cart' +import { checkoutCreate, checkoutToCart } from './cart/utils' + +const fetcher: Fetcher = async ({ method = 'POST', variables, query }) => { + return handleFetchResponse( + await fetch(API_URL, { + method, + body: JSON.stringify({ query, variables }), + headers: { + 'X-Shopify-Storefront-Access-Token': API_TOKEN!, + 'Content-Type': 'application/json', + }, + }) + ) +} + +export const cartFetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ + options, + input: { cartId }, + fetch, +}) => { + let checkout + + if (cartId) { + const data = await fetch({ + ...options, + variables: { + cartId, + }, + }) + checkout = data?.node + } + + if (checkout?.completedAt || !cartId) { + checkout = await checkoutCreate(fetch) + } + + return checkoutToCart({ checkout }) +} + +const useCart: HookHandler< + Cart | null, + {}, + any, + any, + any, + { isEmpty?: boolean } +> = { + fetchOptions: { + query: getCheckoutQuery, + }, + fetcher: cartFetcher, + normalizer: normalizeCart, + useHook({ input, useData }) { + const response = useData({ + swrOptions: { revalidateOnFocus: false, ...input.swrOptions }, + }) + + return useMemo( + () => + Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, + }, + }), + [response] + ) + }, +} + +export const shopifyProvider = { + locale: 'en-us', + cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, + storeDomain: STORE_DOMAIN, + fetcher, + cartNormalizer: normalizeCart, + cart: { useCart }, +} + +export type ShopifyProvider = typeof shopifyProvider From 300d04c1acadcc4e162c7d9810b6e2730f64c2c4 Mon Sep 17 00:00:00 2001 From: Peter Mekhaeil <4616064+petermekhaeil@users.noreply.github.com> Date: Fri, 12 Feb 2021 22:14:16 +0800 Subject: [PATCH 112/221] Shopify Provider (#186) * Start of Shopify provider * add missing comment to documentation * add missing env vars to documentation * update reference to types file --- .env.template | 4 +- framework/shopify/README.md | 260 ++++++++++++++++++ framework/shopify/api/cart/index.ts | 1 + framework/shopify/api/catalog/index.ts | 1 + framework/shopify/api/catalog/products.ts | 1 + framework/shopify/api/checkout/index.ts | 1 + framework/shopify/api/customers/index.ts | 1 + framework/shopify/api/customers/login.ts | 1 + framework/shopify/api/customers/logout.ts | 1 + framework/shopify/api/customers/signup.ts | 1 + framework/shopify/api/index.ts | 60 ++++ .../api/operations/get-all-collections.ts | 21 ++ framework/shopify/api/operations/get-page.ts | 27 ++ .../shopify/api/utils/fetch-graphql-api.ts | 51 ++++ framework/shopify/api/wishlist/index.tsx | 2 + framework/shopify/auth/use-login.tsx | 13 + framework/shopify/auth/use-logout.tsx | 13 + framework/shopify/auth/use-signup.tsx | 13 + framework/shopify/cart/index.ts | 5 + framework/shopify/cart/use-add-item.tsx | 30 ++ framework/shopify/cart/use-cart.tsx | 42 +++ framework/shopify/cart/use-remove-item.tsx | 17 ++ framework/shopify/cart/use-update-item.tsx | 24 ++ framework/shopify/common/get-all-pages.ts | 55 ++++ framework/shopify/common/get-site-info.ts | 30 ++ framework/shopify/customer/use-customer.tsx | 32 +++ framework/shopify/index.tsx | 109 ++++++++ .../shopify/product/get-all-product-paths.ts | 31 +++ framework/shopify/product/get-all-products.ts | 40 +++ framework/shopify/product/get-product.ts | 37 +++ framework/shopify/product/use-price.tsx | 2 + framework/shopify/product/use-search.tsx | 41 +++ framework/shopify/types.ts | 130 +++++++++ framework/shopify/utils/storage.ts | 13 + .../shopify/utils/to-commerce-products.ts | 60 ++++ framework/shopify/wishlist/use-add-item.tsx | 13 + .../shopify/wishlist/use-remove-item.tsx | 17 ++ framework/shopify/wishlist/use-wishlist.tsx | 45 +++ next.config.js | 2 +- 39 files changed, 1245 insertions(+), 2 deletions(-) create mode 100644 framework/shopify/README.md create mode 100644 framework/shopify/api/cart/index.ts create mode 100644 framework/shopify/api/catalog/index.ts create mode 100644 framework/shopify/api/catalog/products.ts create mode 100644 framework/shopify/api/checkout/index.ts create mode 100644 framework/shopify/api/customers/index.ts create mode 100644 framework/shopify/api/customers/login.ts create mode 100644 framework/shopify/api/customers/logout.ts create mode 100644 framework/shopify/api/customers/signup.ts create mode 100644 framework/shopify/api/index.ts create mode 100644 framework/shopify/api/operations/get-all-collections.ts create mode 100644 framework/shopify/api/operations/get-page.ts create mode 100644 framework/shopify/api/utils/fetch-graphql-api.ts create mode 100644 framework/shopify/api/wishlist/index.tsx create mode 100644 framework/shopify/auth/use-login.tsx create mode 100644 framework/shopify/auth/use-logout.tsx create mode 100644 framework/shopify/auth/use-signup.tsx create mode 100644 framework/shopify/cart/index.ts create mode 100644 framework/shopify/cart/use-add-item.tsx create mode 100644 framework/shopify/cart/use-cart.tsx create mode 100644 framework/shopify/cart/use-remove-item.tsx create mode 100644 framework/shopify/cart/use-update-item.tsx create mode 100644 framework/shopify/common/get-all-pages.ts create mode 100644 framework/shopify/common/get-site-info.ts create mode 100644 framework/shopify/customer/use-customer.tsx create mode 100644 framework/shopify/index.tsx create mode 100644 framework/shopify/product/get-all-product-paths.ts create mode 100644 framework/shopify/product/get-all-products.ts create mode 100644 framework/shopify/product/get-product.ts create mode 100644 framework/shopify/product/use-price.tsx create mode 100644 framework/shopify/product/use-search.tsx create mode 100644 framework/shopify/types.ts create mode 100644 framework/shopify/utils/storage.ts create mode 100644 framework/shopify/utils/to-commerce-products.ts create mode 100644 framework/shopify/wishlist/use-add-item.tsx create mode 100644 framework/shopify/wishlist/use-remove-item.tsx create mode 100644 framework/shopify/wishlist/use-wishlist.tsx diff --git a/.env.template b/.env.template index 73a8a6e3b..7d8400baf 100644 --- a/.env.template +++ b/.env.template @@ -2,4 +2,6 @@ BIGCOMMERCE_STOREFRONT_API_URL= BIGCOMMERCE_STOREFRONT_API_TOKEN= BIGCOMMERCE_STORE_API_URL= BIGCOMMERCE_STORE_API_TOKEN= -BIGCOMMERCE_STORE_API_CLIENT_ID= \ No newline at end of file +BIGCOMMERCE_STORE_API_CLIENT_ID= +SHOPIFY_STORE_DOMAIN= +SHOPIFY_STOREFRONT_ACCESS_TOKEN= \ No newline at end of file diff --git a/framework/shopify/README.md b/framework/shopify/README.md new file mode 100644 index 000000000..fc6a70ce3 --- /dev/null +++ b/framework/shopify/README.md @@ -0,0 +1,260 @@ +## Table of Contents + +- [Getting Started](#getting-started) + - [Modifications](#modifications) + - [Adding item to Cart](#adding-item-to-cart) + - [Proceed to Checkout](#proceed-to-checkout) +- [General Usage](#general-usage) + - [CommerceProvider](#commerceprovider) + - [useCommerce](#usecommerce) +- [Hooks](#hooks) + - [usePrice](#useprice) + - [useAddItem](#useadditem) + - [useRemoveItem](#useremoveitem) + - [useUpdateItem](#useupdateitem) +- [APIs](#apis) + - [getProduct](#getproduct) + - [getAllProducts](#getallproducts) + - [getAllCollections](#getallcollections) + - [getAllPages](#getallpages) + +# Shopify Storefront Data Hooks + +Collection of hooks and data fetching functions to integrate Shopify in a React application. Designed to work with [Next.js Commerce](https://demo.vercel.store/). + +## Getting Started + +1. Install dependencies: + +``` +yarn install shopify-buy +yarn install -D @types/shopify-buy +``` + +3. Environment variables need to be set: + +``` +SHOPIFY_STORE_DOMAIN= +SHOPIFY_STOREFRONT_ACCESS_TOKEN= +NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN= +NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN= +``` + +4. Point the framework to `shopify` by updating `tsconfig.json`: + +``` +"@framework/*": ["framework/shopify/*"], +"@framework": ["framework/shopify"] +``` + +### Modifications + +These modifications are temporarily until contributions are made to remove them. + +#### Adding item to Cart + +```js +// components/product/ProductView/ProductView.tsx +const ProductView: FC<Props> = ({ product }) => { + const addToCart = async () => { + setLoading(true) + try { + await addItem({ + productId: product.id, + variantId: variant ? variant.id : product.variants[0].id, + }) + openSidebar() + setLoading(false) + } catch (err) { + setLoading(false) + } + } +} +``` + +#### Proceed to Checkout + +```js +// components/cart/CartSidebarView/CartSidebarView.tsx +import { useCommerce } from '@framework' + +const CartSidebarView: FC = () => { + const { checkout } = useCommerce() + return ( + <Button href={checkout.webUrl} Component="a" width="100%"> + Proceed to Checkout + </Button> + ) +} +``` + +## General Usage + +### CommerceProvider + +Provider component that creates the commerce context for children. + +```js +import { CommerceProvider } from '@framework' + +const App = ({ children }) => { + return <CommerceProvider locale={locale}>{children}</CommerceProvider> +} + +export default App +``` + +### useCommerce + +Returns the configs that are defined in the nearest `CommerceProvider`. Also provides access to Shopify's `checkout` and `shop`. + +```js +import { useCommerce } from 'nextjs-commerce-shopify' + +const { checkout, shop } = useCommerce() +``` + +- `checkout`: The information required to checkout items and pay ([Documentation](https://shopify.dev/docs/storefront-api/reference/checkouts/checkout)). +- `shop`: Represents a collection of the general settings and information about the shop ([Documentation](https://shopify.dev/docs/storefront-api/reference/online-store/shop/index)). + +## Hooks + +### usePrice + +Display the product variant price according to currency and locale. + +```js +import usePrice from '@framework/product/use-price' + +const { price } = usePrice({ + amount, +}) +``` + +Takes in either `amount` or `variant`: + +- `amount`: A price value for a particular item if the amount is known. +- `variant`: A shopify product variant. Price will be extracted from the variant. + +### useAddItem + +```js +import { useAddItem } from '@framework/cart' + +const AddToCartButton = ({ variantId, quantity }) => { + const addItem = useAddItem() + + const addToCart = async () => { + await addItem({ + variantId, + }) + } + + return <button onClick={addToCart}>Add To Cart</button> +} +``` + +### useRemoveItem + +```js +import { useRemoveItem } from '@framework/cart' + +const RemoveButton = ({ item }) => { + const removeItem = useRemoveItem() + + const handleRemove = async () => { + await removeItem({ id: item.id }) + } + + return <button onClick={handleRemove}>Remove</button> +} +``` + +### useUpdateItem + +```js +import { useUpdateItem } from '@framework/cart' + +const CartItem = ({ item }) => { + const [quantity, setQuantity] = useState(item.quantity) + const updateItem = useUpdateItem(item) + + const updateQuantity = async (e) => { + const val = e.target.value + await updateItem({ quantity: val }) + } + + return ( + <input + type="number" + max={99} + min={0} + value={quantity} + onChange={updateQuantity} + /> + ) +} +``` + +## APIs + +Collections of APIs to fetch data from a Shopify store. + +The data is fetched using the [Shopify JavaScript Buy SDK](https://github.com/Shopify/js-buy-sdk#readme). Read the [Shopify Storefront API reference](https://shopify.dev/docs/storefront-api/reference) for more information. + +### getProduct + +Get a single product by its `handle`. + +```js +import getProduct from '@framework/product/get-product' +import { getConfig } from '@framework/api' + +const config = getConfig() + +const product = await getProduct({ + variables: { slug }, + config, +}) +``` + +### getAllProducts + +```js +import getAllProducts from '@framework/product/get-all-products' +import { getConfig } from '@framework/api' + +const config = getConfig() + +const { products } = await getAllProducts({ + variables: { first: 12 }, + config, +}) +``` + +### getAllCollections + +```js +import getAllCollections from '@framework/product/get-all-collections' +import { getConfig } from '@framework/api' + +const config = getConfig() + +const collections = await getAllCollections({ + config, +}) +``` + +### getAllPages + +```js +import getAllPages from '@framework/common/get-all-pages' +import { getConfig } from '@framework/api' + +const config = getConfig() + +const pages = await getAllPages({ + variables: { first: 12 }, + config, +}) +``` diff --git a/framework/shopify/api/cart/index.ts b/framework/shopify/api/cart/index.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/cart/index.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/catalog/index.ts b/framework/shopify/api/catalog/index.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/catalog/index.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/catalog/products.ts b/framework/shopify/api/catalog/products.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/catalog/products.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/checkout/index.ts b/framework/shopify/api/checkout/index.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/checkout/index.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/customers/index.ts b/framework/shopify/api/customers/index.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/customers/index.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/customers/login.ts b/framework/shopify/api/customers/login.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/customers/login.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/customers/logout.ts b/framework/shopify/api/customers/logout.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/customers/logout.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/customers/signup.ts b/framework/shopify/api/customers/signup.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/customers/signup.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts new file mode 100644 index 000000000..dcb0fc2ba --- /dev/null +++ b/framework/shopify/api/index.ts @@ -0,0 +1,60 @@ +import type { CommerceAPIConfig } from '@commerce/api' +import fetchGraphqlApi from './utils/fetch-graphql-api' + +export interface ShopifyConfig extends CommerceAPIConfig {} + +// No I don't like this - will fix it later +const API_URL = + process.env.SHOPIFY_STORE_DOMAIN || + process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN +const API_TOKEN = + process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN || + process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN + +if (!API_URL) { + throw new Error( + `The environment variable SHOPIFY_STORE_DOMAIN is missing and it's required to access your store` + ) +} + +if (!API_TOKEN) { + throw new Error( + `The environment variable SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store` + ) +} + +export class Config { + private config: ShopifyConfig + + constructor(config: ShopifyConfig) { + this.config = config + } + + getConfig(userConfig: Partial<ShopifyConfig> = {}) { + return Object.entries(userConfig).reduce<ShopifyConfig>( + (cfg, [key, value]) => Object.assign(cfg, { [key]: value }), + { ...this.config } + ) + } + + setConfig(newConfig: Partial<ShopifyConfig>) { + Object.assign(this.config, newConfig) + } +} + +const config = new Config({ + commerceUrl: API_URL, + apiToken: API_TOKEN, + // TODO + // @ts-ignore + fetch: fetchGraphqlApi, + customerCookie: 'SHOP_TOKEN', +}) + +export function getConfig(userConfig?: Partial<ShopifyConfig>) { + return config.getConfig(userConfig) +} + +export function setConfig(newConfig: Partial<ShopifyConfig>) { + return config.setConfig(newConfig) +} diff --git a/framework/shopify/api/operations/get-all-collections.ts b/framework/shopify/api/operations/get-all-collections.ts new file mode 100644 index 000000000..9cf216a91 --- /dev/null +++ b/framework/shopify/api/operations/get-all-collections.ts @@ -0,0 +1,21 @@ +import Client from 'shopify-buy' +import { ShopifyConfig } from '../index' + +type Options = { + config: ShopifyConfig +} + +const getAllCollections = async (options: Options) => { + const { config } = options + + const client = Client.buildClient({ + storefrontAccessToken: config.apiToken, + domain: config.commerceUrl, + }) + + const res = await client.collection.fetchAllWithProducts() + + return JSON.parse(JSON.stringify(res)) +} + +export default getAllCollections diff --git a/framework/shopify/api/operations/get-page.ts b/framework/shopify/api/operations/get-page.ts new file mode 100644 index 000000000..11651e335 --- /dev/null +++ b/framework/shopify/api/operations/get-page.ts @@ -0,0 +1,27 @@ +import { ShopifyConfig, getConfig } from '..' +import type { Page } from '../../types' + +export type { Page } + +export type GetPageResult<T extends { page?: any } = { page?: Page }> = T + +export type PageVariables = { + id: string +} + +async function getPage({ + url, + variables, + config, + preview, +}: { + url?: string + variables: PageVariables + config?: ShopifyConfig + preview?: boolean +}): Promise<GetPageResult> { + config = getConfig(config) + return {} +} + +export default getPage diff --git a/framework/shopify/api/utils/fetch-graphql-api.ts b/framework/shopify/api/utils/fetch-graphql-api.ts new file mode 100644 index 000000000..946242c93 --- /dev/null +++ b/framework/shopify/api/utils/fetch-graphql-api.ts @@ -0,0 +1,51 @@ +import { CommerceAPIFetchOptions } from '@commerce/api' +import { FetcherError } from '@commerce/utils/errors' +import { getConfig } from '../index' + +export interface GraphQLFetcherResult<Data = any> { + data: Data + res: Response +} +export type GraphQLFetcher< + Data extends GraphQLFetcherResult = GraphQLFetcherResult, + Variables = any +> = ( + query: string, + queryData?: CommerceAPIFetchOptions<Variables>, + fetchOptions?: RequestInit +) => Promise<Data> + +const fetchGraphqlApi: GraphQLFetcher = async ( + query: string, + { variables } = {}, + fetchOptions +) => { + const config = getConfig() + const url = `https://${config.commerceUrl}/api/2020-10/graphql.json` + + const res = await fetch(url, { + ...fetchOptions, + method: 'POST', + headers: { + 'X-Shopify-Storefront-Access-Token': config.apiToken, + ...fetchOptions?.headers, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query, + variables, + }), + }) + + const json = await res.json() + if (json.errors) { + throw new FetcherError({ + errors: json.errors ?? [{ message: 'Failed to fetch Shopify API' }], + status: res.status, + }) + } + + return { data: json.data, res } +} + +export default fetchGraphqlApi diff --git a/framework/shopify/api/wishlist/index.tsx b/framework/shopify/api/wishlist/index.tsx new file mode 100644 index 000000000..a72856673 --- /dev/null +++ b/framework/shopify/api/wishlist/index.tsx @@ -0,0 +1,2 @@ +export type WishlistItem = { product: any; id: number } +export default function () {} diff --git a/framework/shopify/auth/use-login.tsx b/framework/shopify/auth/use-login.tsx new file mode 100644 index 000000000..75f067c3a --- /dev/null +++ b/framework/shopify/auth/use-login.tsx @@ -0,0 +1,13 @@ +import { useCallback } from 'react' + +export function emptyHook() { + const useEmptyHook = async (options = {}) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/shopify/auth/use-logout.tsx b/framework/shopify/auth/use-logout.tsx new file mode 100644 index 000000000..75f067c3a --- /dev/null +++ b/framework/shopify/auth/use-logout.tsx @@ -0,0 +1,13 @@ +import { useCallback } from 'react' + +export function emptyHook() { + const useEmptyHook = async (options = {}) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/shopify/auth/use-signup.tsx b/framework/shopify/auth/use-signup.tsx new file mode 100644 index 000000000..75f067c3a --- /dev/null +++ b/framework/shopify/auth/use-signup.tsx @@ -0,0 +1,13 @@ +import { useCallback } from 'react' + +export function emptyHook() { + const useEmptyHook = async (options = {}) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/shopify/cart/index.ts b/framework/shopify/cart/index.ts new file mode 100644 index 000000000..e1c6ef823 --- /dev/null +++ b/framework/shopify/cart/index.ts @@ -0,0 +1,5 @@ +export { default as useCart } from './use-cart' +export { default as useAddItem } from './use-add-item' +export { default as useRemoveItem } from './use-remove-item' +// export { default as useWishlistActions } from './use-cart-actions' +// export { default as useUpdateItem } from './use-cart-actions' diff --git a/framework/shopify/cart/use-add-item.tsx b/framework/shopify/cart/use-add-item.tsx new file mode 100644 index 000000000..276d66e30 --- /dev/null +++ b/framework/shopify/cart/use-add-item.tsx @@ -0,0 +1,30 @@ +import { useCallback } from 'react' +import { LineItemToAdd } from 'shopify-buy' +import { useCommerce } from '../index' + +type Options = { + productId: number + variantId: string | number +} + +const useAddItem = () => { + const { checkout, client, updateCheckout } = useCommerce() + + return useCallback( + async function addItem(options: Options) { + const lineItems: LineItemToAdd[] = [ + { + variantId: `${options.variantId}`, + quantity: 1, + }, + ] + + const cart = await client?.checkout.addLineItems(checkout.id, lineItems) + updateCheckout(cart) + return cart + }, + [checkout, client] + ) +} + +export default useAddItem diff --git a/framework/shopify/cart/use-cart.tsx b/framework/shopify/cart/use-cart.tsx new file mode 100644 index 000000000..f067b520d --- /dev/null +++ b/framework/shopify/cart/use-cart.tsx @@ -0,0 +1,42 @@ +import { useCommerce } from '../index' + +export function emptyHook() { + const { checkout } = useCommerce() + const { lineItems, totalPriceV2 } = checkout || {} + + return { + data: { + subTotal: totalPriceV2?.amount || 0, + total: totalPriceV2?.amount || 0, + currency: { + code: '', + }, + line_items: + lineItems?.map((item) => { + return [ + { + id: item.id, + name: item.title, + quantity: item.quantity, + }, + ] + }) || [], + items: + lineItems?.map((item) => { + return { + id: item.id, + name: item.title, + images: [{ url: '/jacket.png' }], + url: '/', + quantity: item.quantity, + productId: item.id, + variantId: item.id, + } + }) || [], + }, + isEmpty: false, + isLoading: false, + } +} + +export default emptyHook diff --git a/framework/shopify/cart/use-remove-item.tsx b/framework/shopify/cart/use-remove-item.tsx new file mode 100644 index 000000000..c0ce93bd5 --- /dev/null +++ b/framework/shopify/cart/use-remove-item.tsx @@ -0,0 +1,17 @@ +import { useCallback } from 'react' +import { useCommerce } from '../index' + +const useRemoveItem = () => { + const { checkout, client, updateCheckout } = useCommerce() + + return useCallback( + async function removeItem({ id }: { id: string }) { + const cart = await client?.checkout.removeLineItems(checkout.id, [id]) + updateCheckout(cart) + return cart + }, + [checkout, client] + ) +} + +export default useRemoveItem diff --git a/framework/shopify/cart/use-update-item.tsx b/framework/shopify/cart/use-update-item.tsx new file mode 100644 index 000000000..05118a65b --- /dev/null +++ b/framework/shopify/cart/use-update-item.tsx @@ -0,0 +1,24 @@ +import { useCallback } from 'react' +import { useCommerce } from '../index' + +const useUpdateItem = (item: CartItem) => { + const { checkout, client, updateCheckout } = useCommerce() + + return useCallback( + async function updateItem({ quantity }: { quantity: number }) { + const lineItemsToUpdate = [{ id: item.id, quantity }] + + const cart = await client?.checkout.updateLineItems( + checkout.id, + lineItemsToUpdate + ) + + updateCheckout(cart) + + return cart + }, + [checkout, client] + ) +} + +export default useUpdateItem diff --git a/framework/shopify/common/get-all-pages.ts b/framework/shopify/common/get-all-pages.ts new file mode 100644 index 000000000..02db3fdc3 --- /dev/null +++ b/framework/shopify/common/get-all-pages.ts @@ -0,0 +1,55 @@ +import { getConfig, ShopifyConfig } from '../api' +import { Page as PageType, PageEdge } from '../types' + +export type Page = PageType + +export const getAllPagesQuery = /* GraphQL */ ` + query($first: Int!) { + pages(first: $first) { + edges { + node { + id + title + handle + body + bodySummary + url + } + } + } + } +` + +type Variables = { + first?: number +} + +type Options = { + variables?: Variables + config: ShopifyConfig + preview?: boolean +} + +type ReturnType = { + pages: Page[] +} + +const getAllPages = async (options?: Options): Promise<ReturnType> => { + let { config, variables = { first: 250 } } = options || {} + + config = getConfig(config) + + const { data } = await config.fetch(getAllPagesQuery, { variables }) + + const pages = data.pages.edges.map(({ node }: PageEdge) => { + return { + ...node, + name: node.handle, + url: `${config!.locale}/${node.handle}`, + } + }) + + return { pages } +} + +export default getAllPages diff --git a/framework/shopify/common/get-site-info.ts b/framework/shopify/common/get-site-info.ts new file mode 100644 index 000000000..c08ae2b92 --- /dev/null +++ b/framework/shopify/common/get-site-info.ts @@ -0,0 +1,30 @@ +import { ShopifyConfig } from '../index' + +type Options = { + config: ShopifyConfig + preview?: boolean +} + +const getSiteInfo = async (options: Options) => { + // TODO + return { + categories: [ + { + path: '', + name: '', + entityId: 0, + }, + ], + brands: [ + { + node: { + path: '', + name: '', + entityId: 0, + }, + }, + ], + } +} + +export default getSiteInfo diff --git a/framework/shopify/customer/use-customer.tsx b/framework/shopify/customer/use-customer.tsx new file mode 100644 index 000000000..a909443ff --- /dev/null +++ b/framework/shopify/customer/use-customer.tsx @@ -0,0 +1,32 @@ +import type { HookFetcher } from '@commerce/utils/types' +import type { SwrOptions } from '@commerce/utils/use-data' +import useCommerceCustomer from '@commerce/use-customer' + +const defaultOpts = {} + +export type Customer = { + entityId: number + firstName: string + lastName: string + email: string +} +export type CustomerData = {} + +export const fetcher: HookFetcher<Customer | null> = async () => { + return null +} + +export function extendHook( + customFetcher: typeof fetcher, + swrOptions?: SwrOptions<Customer | null> +) { + const useCustomer = () => { + return { data: { firstName: null, lastName: null, email: null } } + } + + useCustomer.extend = extendHook + + return useCustomer +} + +export default extendHook(fetcher) diff --git a/framework/shopify/index.tsx b/framework/shopify/index.tsx new file mode 100644 index 000000000..5fd08e0d9 --- /dev/null +++ b/framework/shopify/index.tsx @@ -0,0 +1,109 @@ +import React, { + ReactNode, + createContext, + useContext, + useMemo, + useState, + useEffect, +} from 'react' +import Client from 'shopify-buy' +import { Shop, Cart, Client as ClientType } from './types' +import { + getCheckoutIdFromStorage, + setCheckoutIdInStorage, +} from './utils/storage' +import { getConfig } from '@framework/api' + +const Commerce = createContext<CommerceContextValue | {}>({}) + +type CommerceProps = { + children?: ReactNode + locale: string +} + +type CommerceContextValue = { + client: ClientType + shop: Shop + checkout: Cart + updateCheckout: (cart: Cart | undefined) => void + currencyCode: string + locale: string + sessionToken: string +} + +export function CommerceProvider({ + children, + locale = 'en-US', +}: CommerceProps) { + const sessionToken = 'nextjs-commerce-shopify-token' + + const config = getConfig() + + const client = Client.buildClient({ + storefrontAccessToken: config.apiToken, + domain: config.commerceUrl, + language: locale, + }) as ClientType + + const [shop, setShop] = useState<Shop>() + const [checkout, setCheckout] = useState<Cart>() + + const fetchShopify = async () => { + const shopInfo: Shop = await client.shop.fetchInfo() + let checkoutResource: Cart + + const checkoutOptions = { + presentmentCurrencyCode: + /*config.currencyCode ||*/ shopInfo?.currencyCode, + } + + let checkoutId = getCheckoutIdFromStorage(sessionToken) + + // we could have a cart id stored in session storage + // user could be refreshing or navigating back and forth + if (checkoutId) { + checkoutResource = await client.checkout.fetch(checkoutId) + + // could be expired order - we will create a new order + if (checkoutResource.completedAt) { + checkoutResource = await client.checkout.create(checkoutOptions) + } + } else { + checkoutResource = await client.checkout.create(checkoutOptions) + } + + setCheckoutIdInStorage(sessionToken, checkoutResource.id) + + setShop(shopInfo) + setCheckout(checkoutResource) + } + + useEffect(() => { + fetchShopify() + }, []) + + const updateCheckout = (newCheckout: Cart) => { + setCheckout(newCheckout) + } + + // Because the config is an object, if the parent re-renders this provider + // will re-render every consumer unless we memoize the config + const cfg = useMemo( + () => ({ + client, + checkout, + shop, + updateCheckout: updateCheckout, + currencyCode: /*config.currencyCode ||*/ checkout?.currencyCode, + locale, + sessionToken, + }), + [client] + ) + + return <Commerce.Provider value={cfg}>{children}</Commerce.Provider> +} + +export function useCommerce<T extends CommerceContextValue>() { + return useContext(Commerce) as T +} diff --git a/framework/shopify/product/get-all-product-paths.ts b/framework/shopify/product/get-all-product-paths.ts new file mode 100644 index 000000000..3d4f0ef7a --- /dev/null +++ b/framework/shopify/product/get-all-product-paths.ts @@ -0,0 +1,31 @@ +import Client from 'shopify-buy' +import { getConfig } from '../api' +import { Product } from '../types' +import toCommerceProducts from '../utils/to-commerce-products' + +type ReturnType = { + products: any[] +} + +const getAllProductPaths = async (): Promise<ReturnType> => { + const config = getConfig() + + const client = Client.buildClient({ + storefrontAccessToken: config.apiToken, + domain: config.commerceUrl, + }) + + const res = (await client.product.fetchAll()) as Product[] + + const products = toCommerceProducts(res) + + return { + products: products.map((product) => { + return { + node: { ...product }, + } + }), + } +} + +export default getAllProductPaths diff --git a/framework/shopify/product/get-all-products.ts b/framework/shopify/product/get-all-products.ts new file mode 100644 index 000000000..6e4881e99 --- /dev/null +++ b/framework/shopify/product/get-all-products.ts @@ -0,0 +1,40 @@ +import Client from 'shopify-buy' +import { ShopifyConfig } from '../api' +import { Product } from '../types' +import toCommerceProducts from '../utils/to-commerce-products' + +export type ProductNode = Product + +type Variables = { + first?: number + field?: string +} + +type Options = { + variables: Variables + config: ShopifyConfig + preview?: boolean +} + +type ReturnType = { + products: any[] +} + +const getAllProducts = async (options: Options): Promise<ReturnType> => { + const { config } = options + + const client = Client.buildClient({ + storefrontAccessToken: config.apiToken, + domain: config.commerceUrl, + }) + + const res = (await client.product.fetchAll()) as Product[] + + const products = toCommerceProducts(res) + + return { + products, + } +} + +export default getAllProducts diff --git a/framework/shopify/product/get-product.ts b/framework/shopify/product/get-product.ts new file mode 100644 index 000000000..f71aa0213 --- /dev/null +++ b/framework/shopify/product/get-product.ts @@ -0,0 +1,37 @@ +import Client from 'shopify-buy' +import { ShopifyConfig } from '../api' +import { Product } from '../types' +import toCommerceProducts from '../utils/to-commerce-products' + +export type ProductNode = Product + +type Variables = { + slug: string +} + +type Options = { + variables: Variables + config: ShopifyConfig + preview?: boolean +} + +type ReturnType = { + product: any +} + +const getProduct = async (options: Options): Promise<ReturnType> => { + const { variables, config } = options + + const client = Client.buildClient({ + storefrontAccessToken: config.apiToken, + domain: config.commerceUrl, + }) + + const res = (await client.product.fetchByHandle(variables.slug)) as Product + + return { + product: toCommerceProducts([res])[0], + } +} + +export default getProduct diff --git a/framework/shopify/product/use-price.tsx b/framework/shopify/product/use-price.tsx new file mode 100644 index 000000000..a79940a76 --- /dev/null +++ b/framework/shopify/product/use-price.tsx @@ -0,0 +1,2 @@ +export * from '@commerce/use-price' +export { default } from '@commerce/use-price' diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx new file mode 100644 index 000000000..a2c32c896 --- /dev/null +++ b/framework/shopify/product/use-search.tsx @@ -0,0 +1,41 @@ +import type { HookFetcher } from '@commerce/utils/types' +import type { SwrOptions } from '@commerce/utils/use-data' +import useCommerceSearch from '@commerce/products/use-search' +import { ProductEdge } from '../types' + +const defaultOpts = {} + +export type SearchProductsInput = { + search?: string + categoryId?: number + brandId?: number + sort?: string +} + +export type SearchProductsData = { + products: ProductEdge[] + found: boolean +} + +export const fetcher: HookFetcher<SearchProductsData, SearchProductsInput> = ( + options, + { search, categoryId, brandId, sort }, + fetch +) => { + return { found: false, products: [] } +} + +export function extendHook( + customFetcher: typeof fetcher, + swrOptions?: SwrOptions<SearchProductsData, SearchProductsInput> +) { + const useSearch = (input: SearchProductsInput = {}) => { + return {} + } + + useSearch.extend = extendHook + + return useSearch +} + +export default extendHook(fetcher) diff --git a/framework/shopify/types.ts b/framework/shopify/types.ts new file mode 100644 index 000000000..47bb94e62 --- /dev/null +++ b/framework/shopify/types.ts @@ -0,0 +1,130 @@ +import { + Product as BaseProduct, + ProductVariant as BaseProductVariant, + Cart as BaseCart, + CheckoutResource as BaseCheckoutResource, + AttributeInput, + Client as BaseClient, + Shop as BaseShop, + Image as BaseImage, +} from 'shopify-buy' + +export type SelectedOptions = { + id: string + name: string + value: string +} + +export type PresentmentPrice = { + price: PriceV2 +} + +export type ProductVariant = BaseProductVariant & { + selectedOptions: Array<SelectedOptions> + presentmentPrices: Array<PresentmentPrice> +} + +// TODO +export type ProductOptions = { + node: { + __typename: string + displayName: string + values: { + edges: [ + { + node: { + label: string + id: string + } + } + ] + } + } +} + +// TODO +export type ProductEdge = { + node: Product +} + +export type Product = BaseProduct & { + handle: string + name: string + path: string + entityId: number + descriptionHtml: string + prices: { + price: { + value: number + currencyCode: string + } + retailPrice: { + value: number + currencyCode: string + } + } + images: { + edges: [{ node: { urlOriginal: string; altText: string } }] + } + productOptions: ProductOptions + variants: Array<ProductVariant> & { + edges: [ + { + node: { + productOptions: ProductOptions[] + entityId: number + } + } + ] + } +} + +export type PriceV2 = { + amount: number + currencyCode: string +} + +export type Cart = BaseCart & { + webUrl?: string + currencyCode?: string + lineItemsSubtotalPrice?: PriceV2 + totalPriceV2?: PriceV2 +} + +export type Shop = BaseShop & { + currencyCode?: string +} + +export type Create = { + presentmentCurrencyCode?: string +} + +export type CheckoutResource = BaseCheckoutResource & { + updateLineItems( + checkoutId: string | number, + lineItems: AttributeInput[] + ): Promise<Cart> + + create: (input: Create) => Promise<Cart> +} + +export type Client = BaseClient & { + checkout: CheckoutResource +} + +export type Page = { + id: string + title: string + name: string + handle: string + body: string + bodySummary: string + url: string + sort_order: number +} + +export type PageEdge = { + node: Page +} + +export type Image = BaseImage diff --git a/framework/shopify/utils/storage.ts b/framework/shopify/utils/storage.ts new file mode 100644 index 000000000..d46dadb21 --- /dev/null +++ b/framework/shopify/utils/storage.ts @@ -0,0 +1,13 @@ +export const getCheckoutIdFromStorage = (token: string) => { + if (window && window.sessionStorage) { + return window.sessionStorage.getItem(token) + } + + return null +} + +export const setCheckoutIdInStorage = (token: string, id: string | number) => { + if (window && window.sessionStorage) { + return window.sessionStorage.setItem(token, id + '') + } +} diff --git a/framework/shopify/utils/to-commerce-products.ts b/framework/shopify/utils/to-commerce-products.ts new file mode 100644 index 000000000..c0b411eb6 --- /dev/null +++ b/framework/shopify/utils/to-commerce-products.ts @@ -0,0 +1,60 @@ +import { Product, Image } from '../types' + +export default function toCommerceProducts(products: Product[]) { + return products.map((product: Product) => { + return { + id: product.id, + entityId: product.id, + name: product.title, + slug: product.handle, + title: product.title, + vendor: product.vendor, + description: product.descriptionHtml, + path: `/${product.handle}`, + price: { + value: +product.variants[0].price, + currencyCode: 'USD', // TODO + }, + images: product.images.map((image: Image) => { + return { + url: image.src, + } + }), + variants: product.variants.map((variant) => { + return { + id: variant.id, + options: variant.selectedOptions.map((selectedOption) => { + return { + __typename: 'MultipleChoiceOption', + displayName: selectedOption.name, + values: [ + { + node: { + id: variant.id, + label: selectedOption.value, + }, + }, + ], + } + }), + } + }), + productOptions: product.options.map((option) => { + return { + __typename: 'MultipleChoiceOption', + displayName: option.name, + values: option.values.map((value) => { + return { + node: { + entityId: 1, + label: value.value, + hexColors: [value.value], + }, + } + }), + } + }), + options: [], + } + }) +} diff --git a/framework/shopify/wishlist/use-add-item.tsx b/framework/shopify/wishlist/use-add-item.tsx new file mode 100644 index 000000000..75f067c3a --- /dev/null +++ b/framework/shopify/wishlist/use-add-item.tsx @@ -0,0 +1,13 @@ +import { useCallback } from 'react' + +export function emptyHook() { + const useEmptyHook = async (options = {}) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/shopify/wishlist/use-remove-item.tsx b/framework/shopify/wishlist/use-remove-item.tsx new file mode 100644 index 000000000..a2d3a8a05 --- /dev/null +++ b/framework/shopify/wishlist/use-remove-item.tsx @@ -0,0 +1,17 @@ +import { useCallback } from 'react' + +type Options = { + includeProducts?: boolean +} + +export function emptyHook(options?: Options) { + const useEmptyHook = async ({ id }: { id: string | number }) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/shopify/wishlist/use-wishlist.tsx b/framework/shopify/wishlist/use-wishlist.tsx new file mode 100644 index 000000000..2aac16810 --- /dev/null +++ b/framework/shopify/wishlist/use-wishlist.tsx @@ -0,0 +1,45 @@ +import { HookFetcher } from '@commerce/utils/types' +import { SwrOptions } from '@commerce/utils/use-data' +import useCommerceWishlist from '@commerce/wishlist/use-wishlist' +import { Product } from '../types' +import useCustomer from '../customer/use-customer' + +const defaultOpts = {} + +export type Wishlist = { + items: [ + { + product_id: number + variant_id: number + id: number + product: Product + } + ] +} + +export interface UseWishlistOptions { + includeProducts?: boolean +} + +export interface UseWishlistInput extends UseWishlistOptions { + customerId?: number +} + +export const fetcher: HookFetcher<Wishlist | null, UseWishlistInput> = () => { + return null +} + +export function extendHook( + customFetcher: typeof fetcher, + swrOptions?: SwrOptions<Wishlist | null, UseWishlistInput> +) { + const useWishlist = ({ includeProducts }: UseWishlistOptions = {}) => { + return { data: null } + } + + useWishlist.extend = extendHook + + return useWishlist +} + +export default extendHook(fetcher) diff --git a/next.config.js b/next.config.js index e732ef78a..3c9e37210 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,6 @@ module.exports = { images: { - domains: ['cdn11.bigcommerce.com'], + domains: ['cdn11.bigcommerce.com', 'cdn.shopify.com'], }, i18n: { locales: ['en-US', 'es'], From 1549368c888b0b59fd688ffcef09f7d2ece73d00 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 12 Feb 2021 12:17:36 -0500 Subject: [PATCH 113/221] Moved useSearch to the new hook --- framework/bigcommerce/product/use-search.tsx | 65 +------------------- framework/bigcommerce/provider.tsx | 52 ++++++++++++++++ framework/commerce/index.tsx | 5 +- framework/commerce/products/use-search.tsx | 58 ++++++++++++++++- framework/commerce/types.ts | 4 ++ 5 files changed, 118 insertions(+), 66 deletions(-) diff --git a/framework/bigcommerce/product/use-search.tsx b/framework/bigcommerce/product/use-search.tsx index 2b4ee7593..52db6a72d 100644 --- a/framework/bigcommerce/product/use-search.tsx +++ b/framework/bigcommerce/product/use-search.tsx @@ -1,63 +1,4 @@ -import type { HookFetcher } from '@commerce/utils/types' -import type { SwrOptions } from '@commerce/utils/use-data' -import useCommerceSearch from '@commerce/products/use-search' -import type { SearchProductsData } from '../api/catalog/products' +import useSearch, { UseSearch } from '@commerce/products/use-search' +import type { BigcommerceProvider } from '..' -const defaultOpts = { - url: '/api/bigcommerce/catalog/products', - method: 'GET', -} - -export type SearchProductsInput = { - search?: string - categoryId?: number - brandId?: number - sort?: string -} - -export const fetcher: HookFetcher<SearchProductsData, SearchProductsInput> = ( - options, - { search, categoryId, brandId, sort }, - fetch -) => { - // Use a dummy base as we only care about the relative path - const url = new URL(options?.url ?? defaultOpts.url, 'http://a') - - if (search) url.searchParams.set('search', search) - if (Number.isInteger(categoryId)) - url.searchParams.set('category', String(categoryId)) - if (Number.isInteger(brandId)) url.searchParams.set('brand', String(brandId)) - if (sort) url.searchParams.set('sort', sort) - - return fetch({ - url: url.pathname + url.search, - method: options?.method ?? defaultOpts.method, - }) -} - -export function extendHook( - customFetcher: typeof fetcher, - swrOptions?: SwrOptions<SearchProductsData, SearchProductsInput> -) { - const useSearch = (input: SearchProductsInput = {}) => { - const response = useCommerceSearch( - defaultOpts, - [ - ['search', input.search], - ['categoryId', input.categoryId], - ['brandId', input.brandId], - ['sort', input.sort], - ], - customFetcher, - { revalidateOnFocus: false, ...swrOptions } - ) - - return response - } - - useSearch.extend = extendHook - - return useSearch -} - -export default extendHook(fetcher) +export default useSearch as UseSearch<BigcommerceProvider> diff --git a/framework/bigcommerce/provider.tsx b/framework/bigcommerce/provider.tsx index d3ae4d171..653a28a87 100644 --- a/framework/bigcommerce/provider.tsx +++ b/framework/bigcommerce/provider.tsx @@ -5,6 +5,7 @@ import type { FetchCartInput } from '@commerce/cart/use-cart' import { normalizeCart } from './lib/normalize' import type { Wishlist } from './api/wishlist' import type { Customer, CustomerData } from './api/customers' +import type { SearchProductsData } from './api/catalog/products' import useCustomer from './customer/use-customer' import type { Cart } from './types' @@ -153,6 +154,56 @@ const useCustomerHandler: HookHandler< }, } +export type SearchProductsInput = { + search?: string + categoryId?: number + brandId?: number + sort?: string +} + +const useSearch: HookHandler< + SearchProductsData, + SearchProductsInput, + SearchProductsInput, + any, + any +> = { + fetchOptions: { + url: '/api/bigcommerce/catalog/products', + method: 'GET', + }, + fetcher({ input: { search, categoryId, brandId, sort }, options, fetch }) { + // Use a dummy base as we only care about the relative path + const url = new URL(options.url!, 'http://a') + + if (search) url.searchParams.set('search', search) + if (Number.isInteger(categoryId)) + url.searchParams.set('category', String(categoryId)) + if (Number.isInteger(brandId)) + url.searchParams.set('brand', String(brandId)) + if (sort) url.searchParams.set('sort', sort) + + return fetch({ + url: url.pathname + url.search, + method: options.method, + }) + }, + useHook({ input, useData }) { + return useData({ + input: [ + ['search', input.search], + ['categoryId', input.categoryId], + ['brandId', input.brandId], + ['sort', input.sort], + ], + swrOptions: { + revalidateOnFocus: false, + ...input.swrOptions, + }, + }) + }, +} + export const bigcommerceProvider = { locale: 'en-us', cartCookie: 'bc_cartId', @@ -161,6 +212,7 @@ export const bigcommerceProvider = { cart: { useCart }, wishlist: { useWishlist }, customer: { useCustomer: useCustomerHandler }, + products: { useSearch }, } export type BigcommerceProvider = typeof bigcommerceProvider diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index ccfc07e66..d8d882f93 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -8,7 +8,7 @@ import { } from 'react' import { Fetcher, HookHandler } from './utils/types' import type { FetchCartInput } from './cart/use-cart' -import type { Cart, Wishlist, Customer } from './types' +import type { Cart, Wishlist, Customer, SearchProductsData } from './types' const Commerce = createContext<CommerceContextValue<any> | {}>({}) @@ -23,6 +23,9 @@ export type Provider = CommerceConfig & { customer: { useCustomer?: HookHandler<Customer | null, any, any> } + products: { + useSearch?: HookHandler<SearchProductsData, any, any> + } } export type CommerceProps<P extends Provider> = { diff --git a/framework/commerce/products/use-search.tsx b/framework/commerce/products/use-search.tsx index 637c8a899..9971c309d 100644 --- a/framework/commerce/products/use-search.tsx +++ b/framework/commerce/products/use-search.tsx @@ -1,5 +1,57 @@ -import useData from '../utils/use-data' +import type { SearchProductsData } from '../types' +import type { + Prop, + HookFetcherFn, + UseHookInput, + UseHookResponse, +} from '../utils/types' +import defaultFetcher from '../utils/default-fetcher' +import useData from '../utils/use-data-2' +import { Provider, useCommerce } from '..' +import { BigcommerceProvider } from '@framework' -const useSearch = useData +export type UseSearchHandler<P extends Provider> = Prop< + Prop<P, 'products'>, + 'useSearch' +> -export default useSearch +export type UseSeachInput<P extends Provider> = UseHookInput< + UseSearchHandler<P> +> + +export type SearchResponse<P extends Provider> = UseHookResponse< + UseSearchHandler<P> +> + +export type UseSearch<P extends Provider> = Partial< + UseSeachInput<P> +> extends UseSeachInput<P> + ? (input?: UseSeachInput<P>) => SearchResponse<P> + : (input: UseSeachInput<P>) => SearchResponse<P> + +export const fetcher = defaultFetcher as HookFetcherFn<SearchProductsData> + +export default function useSearch<P extends Provider>( + input: UseSeachInput<P> = {} +) { + const { providerRef, fetcherRef } = useCommerce<P>() + + const provider = providerRef.current + const opts = provider.products?.useSearch + + const fetcherFn = opts?.fetcher ?? fetcher + const useHook = opts?.useHook ?? ((ctx) => ctx.useData()) + + return useHook({ + input, + useData(ctx) { + const response = useData( + { ...opts!, fetcher: fetcherFn }, + ctx?.input ?? [], + provider.fetcher ?? fetcherRef.current, + ctx?.swrOptions ?? input.swrOptions + ) + return response + }, + }) +} diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index 31aaa6fd7..41aedb228 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -1,5 +1,6 @@ import type { Wishlist as BCWishlist } from '@framework/api/wishlist' import type { Customer as BCCustomer } from '@framework/api/customers' +import type { SearchProductsData as BCSearchProductsData } from '@framework/api/catalog/products' export interface Discount { // The value of the discount, can be an amount or percentage @@ -96,6 +97,9 @@ export interface Wishlist extends BCWishlist {} // TODO: Properly define this type export interface Customer extends BCCustomer {} +// TODO: Properly define this type +export interface SearchProductsData extends BCSearchProductsData {} + /** * Cart mutations */ From 505d3fe04bbb21031ec67b43af56fbd0a384f120 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 12 Feb 2021 13:01:13 -0500 Subject: [PATCH 114/221] Removed old use-data lib --- framework/commerce/cart/use-cart.tsx | 2 +- framework/commerce/customer/use-customer.tsx | 2 +- framework/commerce/products/use-search.tsx | 2 +- framework/commerce/utils/use-data-2.ts | 84 -------------------- framework/commerce/utils/use-data.tsx | 58 ++++++++------ framework/commerce/wishlist/use-wishlist.tsx | 2 +- 6 files changed, 38 insertions(+), 112 deletions(-) delete mode 100644 framework/commerce/utils/use-data-2.ts diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index 6b1a3c789..a1f1d0f84 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -6,7 +6,7 @@ import type { UseHookInput, UseHookResponse, } from '../utils/types' -import useData from '../utils/use-data-2' +import useData from '../utils/use-data' import { Provider, useCommerce } from '..' export type FetchCartInput = { diff --git a/framework/commerce/customer/use-customer.tsx b/framework/commerce/customer/use-customer.tsx index 4fb9b430b..25112128e 100644 --- a/framework/commerce/customer/use-customer.tsx +++ b/framework/commerce/customer/use-customer.tsx @@ -6,7 +6,7 @@ import type { UseHookResponse, } from '../utils/types' import defaultFetcher from '../utils/default-fetcher' -import useData from '../utils/use-data-2' +import useData from '../utils/use-data' import { Provider, useCommerce } from '..' export type UseCustomerHandler<P extends Provider> = Prop< diff --git a/framework/commerce/products/use-search.tsx b/framework/commerce/products/use-search.tsx index 9971c309d..1f887f5fe 100644 --- a/framework/commerce/products/use-search.tsx +++ b/framework/commerce/products/use-search.tsx @@ -6,7 +6,7 @@ import type { UseHookResponse, } from '../utils/types' import defaultFetcher from '../utils/default-fetcher' -import useData from '../utils/use-data-2' +import useData from '../utils/use-data' import { Provider, useCommerce } from '..' import { BigcommerceProvider } from '@framework' diff --git a/framework/commerce/utils/use-data-2.ts b/framework/commerce/utils/use-data-2.ts deleted file mode 100644 index cc4d2cc5b..000000000 --- a/framework/commerce/utils/use-data-2.ts +++ /dev/null @@ -1,84 +0,0 @@ -import useSWR, { responseInterface } from 'swr' -import type { - HookHandler, - HookSwrInput, - HookFetchInput, - PickRequired, - Fetcher, - SwrOptions, -} from './types' -import defineProperty from './define-property' -import { CommerceError } from './errors' - -export type ResponseState<Result> = responseInterface<Result, CommerceError> & { - isLoading: boolean -} - -export type UseData = < - Data = any, - Input extends { [k: string]: unknown } = {}, - FetchInput extends HookFetchInput = {}, - Result = any, - Body = any ->( - options: PickRequired< - HookHandler<Data, Input, FetchInput, Result, Body>, - 'fetcher' - >, - input: HookFetchInput | HookSwrInput, - fetcherFn: Fetcher, - swrOptions?: SwrOptions<Data, FetchInput, Result> -) => ResponseState<Data> - -const useData: UseData = (options, input, fetcherFn, swrOptions) => { - const hookInput = Array.isArray(input) ? input : Object.entries(input) - const fetcher = async ( - url: string, - query?: string, - method?: string, - ...args: any[] - ) => { - try { - return await options.fetcher({ - options: { url, query, method }, - // Transform the input array into an object - input: args.reduce((obj, val, i) => { - obj[hookInput[i][0]!] = val - return obj - }, {}), - fetch: fetcherFn, - normalize: options.normalizer, - }) - } catch (error) { - // SWR will not log errors, but any error that's not an instance - // of CommerceError is not welcomed by this hook - if (!(error instanceof CommerceError)) { - console.error(error) - } - throw error - } - } - const response = useSWR( - () => { - const opts = options.fetchOptions - return opts - ? [opts.url, opts.query, opts.method, ...hookInput.map((e) => e[1])] - : null - }, - fetcher, - swrOptions - ) - - if (!('isLoading' in response)) { - defineProperty(response, 'isLoading', { - get() { - return response.data === undefined - }, - enumerable: true, - }) - } - - return response -} - -export default useData diff --git a/framework/commerce/utils/use-data.tsx b/framework/commerce/utils/use-data.tsx index 58a1a0a47..cc4d2cc5b 100644 --- a/framework/commerce/utils/use-data.tsx +++ b/framework/commerce/utils/use-data.tsx @@ -1,44 +1,54 @@ -import useSWR, { ConfigInterface, responseInterface } from 'swr' -import type { HookSwrInput, HookFetcher, HookFetcherOptions } from './types' +import useSWR, { responseInterface } from 'swr' +import type { + HookHandler, + HookSwrInput, + HookFetchInput, + PickRequired, + Fetcher, + SwrOptions, +} from './types' import defineProperty from './define-property' import { CommerceError } from './errors' -import { useCommerce } from '..' - -export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< - Data, - CommerceError, - HookFetcher<Data, Input, Result> -> export type ResponseState<Result> = responseInterface<Result, CommerceError> & { isLoading: boolean } -export type UseData = <Data = any, Input = null, Result = any>( - options: HookFetcherOptions | (() => HookFetcherOptions | null), - input: HookSwrInput, - fetcherFn: HookFetcher<Data, Input, Result>, - swrOptions?: SwrOptions<Data, Input, Result> +export type UseData = < + Data = any, + Input extends { [k: string]: unknown } = {}, + FetchInput extends HookFetchInput = {}, + Result = any, + Body = any +>( + options: PickRequired< + HookHandler<Data, Input, FetchInput, Result, Body>, + 'fetcher' + >, + input: HookFetchInput | HookSwrInput, + fetcherFn: Fetcher, + swrOptions?: SwrOptions<Data, FetchInput, Result> ) => ResponseState<Data> const useData: UseData = (options, input, fetcherFn, swrOptions) => { - const { fetcherRef } = useCommerce() + const hookInput = Array.isArray(input) ? input : Object.entries(input) const fetcher = async ( - url?: string, + url: string, query?: string, method?: string, ...args: any[] ) => { try { - return await fetcherFn( - { url, query, method }, + return await options.fetcher({ + options: { url, query, method }, // Transform the input array into an object - args.reduce((obj, val, i) => { - obj[input[i][0]!] = val + input: args.reduce((obj, val, i) => { + obj[hookInput[i][0]!] = val return obj }, {}), - fetcherRef.current - ) + fetch: fetcherFn, + normalize: options.normalizer, + }) } catch (error) { // SWR will not log errors, but any error that's not an instance // of CommerceError is not welcomed by this hook @@ -50,9 +60,9 @@ const useData: UseData = (options, input, fetcherFn, swrOptions) => { } const response = useSWR( () => { - const opts = typeof options === 'function' ? options() : options + const opts = options.fetchOptions return opts - ? [opts.url, opts.query, opts.method, ...input.map((e) => e[1])] + ? [opts.url, opts.query, opts.method, ...hookInput.map((e) => e[1])] : null }, fetcher, diff --git a/framework/commerce/wishlist/use-wishlist.tsx b/framework/commerce/wishlist/use-wishlist.tsx index 314f0a1c2..dc912bc98 100644 --- a/framework/commerce/wishlist/use-wishlist.tsx +++ b/framework/commerce/wishlist/use-wishlist.tsx @@ -6,7 +6,7 @@ import type { UseHookResponse, } from '../utils/types' import defaultFetcher from '../utils/default-fetcher' -import useData from '../utils/use-data-2' +import useData from '../utils/use-data' import { Provider, useCommerce } from '..' export type UseWishlistHandler<P extends Provider> = Prop< From b907c31ef2b67c813ce4f010cc43864e4fe259f3 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 12 Feb 2021 13:13:08 -0500 Subject: [PATCH 115/221] Removed generics for result and body --- framework/bigcommerce/provider.tsx | 26 ++++++++------------- framework/commerce/cart/use-cart.tsx | 4 +--- framework/commerce/utils/default-fetcher.ts | 12 +++------- framework/commerce/utils/types.ts | 12 +++------- framework/commerce/utils/use-data.tsx | 12 +++------- 5 files changed, 20 insertions(+), 46 deletions(-) diff --git a/framework/bigcommerce/provider.tsx b/framework/bigcommerce/provider.tsx index 653a28a87..6fd02e304 100644 --- a/framework/bigcommerce/provider.tsx +++ b/framework/bigcommerce/provider.tsx @@ -50,15 +50,16 @@ const useCart: HookHandler< Cart | null, {}, FetchCartInput, - any, - any, { isEmpty?: boolean } > = { fetchOptions: { url: '/api/bigcommerce/cart', method: 'GET', }, - normalizer: normalizeCart, + async fetcher({ input: { cartId }, options, fetch }) { + const data = cartId ? await fetch(options) : null + return data && normalizeCart(data) + }, useHook({ input, useData }) { const response = useData({ swrOptions: { revalidateOnFocus: false, ...input.swrOptions }, @@ -83,8 +84,6 @@ const useWishlist: HookHandler< Wishlist | null, { includeProducts?: boolean }, { customerId?: number; includeProducts: boolean }, - any, - any, { isEmpty?: boolean } > = { fetchOptions: { @@ -132,18 +131,15 @@ const useWishlist: HookHandler< }, } -const useCustomerHandler: HookHandler< - Customer | null, - {}, - {}, - CustomerData | null, - any -> = { +const useCustomerHandler: HookHandler<Customer | null> = { fetchOptions: { url: '/api/bigcommerce/customers', method: 'GET', }, - normalizer: (data) => data.customer, + async fetcher({ options, fetch }) { + const data = await fetch<CustomerData | null>(options) + return data?.customer ?? null + }, useHook({ input, useData }) { return useData({ swrOptions: { @@ -164,9 +160,7 @@ export type SearchProductsInput = { const useSearch: HookHandler< SearchProductsData, SearchProductsInput, - SearchProductsInput, - any, - any + SearchProductsInput > = { fetchOptions: { url: '/api/bigcommerce/catalog/products', diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index a1f1d0f84..f7b384047 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -34,10 +34,8 @@ export const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ options, input: { cartId }, fetch, - normalize, }) => { - const data = cartId ? await fetch({ ...options }) : null - return data && normalize ? normalize(data) : data + return cartId ? await fetch({ ...options }) : null } export default function useCart<P extends Provider>( diff --git a/framework/commerce/utils/default-fetcher.ts b/framework/commerce/utils/default-fetcher.ts index 25211a689..8dc9def75 100644 --- a/framework/commerce/utils/default-fetcher.ts +++ b/framework/commerce/utils/default-fetcher.ts @@ -1,12 +1,6 @@ -import { HookFetcherFn } from './types' +import type { HookFetcherFn } from './types' -const defaultFetcher: HookFetcherFn<any> = async ({ - options, - fetch, - normalize, -}) => { - const data = await fetch({ ...options }) - return data && normalize ? normalize(data) : data -} +const defaultFetcher: HookFetcherFn<any> = ({ options, fetch }) => + fetch(options) export default defaultFetcher diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 47da81a7f..98e4ebbae 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -40,7 +40,6 @@ export type HookFetcherFn< options: HookFetcherOptions input: Input fetch: <T = Result, B = Body>(options: FetcherOptions<B>) => Promise<T> - normalize?(data: Result): Data }) => Data | Promise<Data> export type HookFetcherOptions = { method?: string } & ( @@ -63,23 +62,18 @@ export type HookHandler< Input extends { [k: string]: unknown } = {}, // Input expected before doing a fetch operation FetchInput extends HookFetchInput = {}, - // Data returned by the API after a fetch operation - Result = any, - // Body expected by the API endpoint - Body = any, // Custom state added to the response object of SWR State = {} > = { useHook?(context: { - input: Input & { swrOptions?: SwrOptions<Data, FetchInput, Result> } + input: Input & { swrOptions?: SwrOptions<Data, FetchInput> } useData(context?: { input?: HookFetchInput | HookSwrInput - swrOptions?: SwrOptions<Data, FetchInput, Result> + swrOptions?: SwrOptions<Data, FetchInput> }): ResponseState<Data> }): ResponseState<Data> & State fetchOptions: HookFetcherOptions - fetcher?: HookFetcherFn<Data, FetchInput, Result, Body> - normalizer?(data: NonNullable<Result>): Data + fetcher?: HookFetcherFn<Data, FetchInput> } export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< diff --git a/framework/commerce/utils/use-data.tsx b/framework/commerce/utils/use-data.tsx index cc4d2cc5b..94679a0c6 100644 --- a/framework/commerce/utils/use-data.tsx +++ b/framework/commerce/utils/use-data.tsx @@ -17,17 +17,12 @@ export type ResponseState<Result> = responseInterface<Result, CommerceError> & { export type UseData = < Data = any, Input extends { [k: string]: unknown } = {}, - FetchInput extends HookFetchInput = {}, - Result = any, - Body = any + FetchInput extends HookFetchInput = {} >( - options: PickRequired< - HookHandler<Data, Input, FetchInput, Result, Body>, - 'fetcher' - >, + options: PickRequired<HookHandler<Data, Input, FetchInput>, 'fetcher'>, input: HookFetchInput | HookSwrInput, fetcherFn: Fetcher, - swrOptions?: SwrOptions<Data, FetchInput, Result> + swrOptions?: SwrOptions<Data, FetchInput> ) => ResponseState<Data> const useData: UseData = (options, input, fetcherFn, swrOptions) => { @@ -47,7 +42,6 @@ const useData: UseData = (options, input, fetcherFn, swrOptions) => { return obj }, {}), fetch: fetcherFn, - normalize: options.normalizer, }) } catch (error) { // SWR will not log errors, but any error that's not an instance From b116c0cfe1528ecb52c7507f03a4c215c6b08164 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 12 Feb 2021 13:20:17 -0500 Subject: [PATCH 116/221] Removed normalizr --- package.json | 1 - yarn.lock | 5 ----- 2 files changed, 6 deletions(-) diff --git a/package.json b/package.json index ff492a35e..287af0aa4 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,6 @@ "next": "^10.0.5", "next-seo": "^4.11.0", "next-themes": "^0.0.4", - "normalizr": "^3.6.1", "postcss-nesting": "^7.0.1", "react": "^16.14.0", "react-dom": "^16.14.0", diff --git a/yarn.lock b/yarn.lock index b46f7acce..55e40e4ae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5374,11 +5374,6 @@ normalize.css@^8.0.1: resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3" integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg== -normalizr@^3.6.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/normalizr/-/normalizr-3.6.1.tgz#d367ab840e031ff382141b8d81ce279292ff69fe" - integrity sha512-8iEmqXmPtll8PwbEFrbPoDxVw7MKnNvt3PZzR2Xvq9nggEEOgBlNICPXYzyZ4w4AkHUzCU998mdatER3n2VaMA== - npm-run-path@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" From 4dd2c3fde0a9809b668ff6f2bec3b8e192034812 Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Fri, 12 Feb 2021 16:49:32 -0300 Subject: [PATCH 117/221] Wishlist --- README.md | 118 ++++++++++-------- framework/bigcommerce/config.json | 5 + framework/bigcommerce/product/use-search.tsx | 2 +- .../{ => cart}/products/use-search.tsx | 10 +- framework/commerce/config.json | 5 + framework/commerce/types.ts | 7 ++ pages/_app.tsx | 5 +- pages/wishlist.tsx | 27 +++- 8 files changed, 113 insertions(+), 66 deletions(-) create mode 100644 framework/bigcommerce/config.json rename framework/commerce/{ => cart}/products/use-search.tsx (85%) create mode 100644 framework/commerce/config.json diff --git a/README.md b/README.md index 8be54816a..ea6248c51 100644 --- a/README.md +++ b/README.md @@ -42,57 +42,6 @@ Additionally, we need to ensure feature parity (not all providers have e.g. wish People actively working on this project: @okbel & @lfades. -## Troubleshoot - -<details> -<summary>I already own a BigCommerce store. What should I do?</summary> -<br> -First thing you do is: <b>set your environment variables</b> -<br> -<br> -.env.local - -```sh -BIGCOMMERCE_STOREFRONT_API_URL=<> -BIGCOMMERCE_STOREFRONT_API_TOKEN=<> -BIGCOMMERCE_STORE_API_URL=<> -BIGCOMMERCE_STORE_API_TOKEN=<> -BIGCOMMERCE_STORE_API_CLIENT_ID=<> -``` - -If your project was started with a "Deploy with Vercel" button, you can use Vercel's CLI to retrieve these credentials. - -1. Install Vercel CLI: `npm i -g vercel` -2. Link local instance with Vercel and Github accounts (creates .vercel file): `vercel link` -3. Download your environment variables: `vercel env pull .env.local` - -Next, you're free to customize the starter. More updates coming soon. Stay tuned. - -</details> - -<details> -<summary>BigCommerce shows a Coming Soon page and requests a Preview Code</summary> -<br> -After Email confirmation, Checkout should be manually enabled through BigCommerce platform. Look for "Review & test your store" section through BigCommerce's dashboard. -<br> -<br> -BigCommerce team has been notified and they plan to add more detailed about this subject. -</details> - -## Contribute - -Our commitment to Open Source can be found [here](https://vercel.com/oss). - -1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device. -2. Create a new branch `git checkout -b MY_BRANCH_NAME` -3. Install yarn: `npm install -g yarn` -4. Install the dependencies: `yarn` -5. Duplicate `.env.template` and rename it to `.env.local`. -6. Add proper store values to `.env.local`. -7. Run `yarn dev` to build and watch for code changes -8. The development branch is `canary` (this is the branch pull requests should be made against). - On a release, `canary` branch is rebased into `master`. - ## Framework Framework is where the data comes from. It contains mostly hooks and functions. @@ -132,3 +81,70 @@ import { useUI } from '@components/ui' import { useCustomer } from '@framework/customer' import { useAddItem, useWishlist, useRemoveItem } from '@framework/wishlist' ``` + +## Config + +### Features + +In order to make the UI entirely functional, we need to specify which features certain providers do not **provide**. + +**Disabling wishlist:** + +``` +{ + "features": { + "wishlist": false + } +} +``` + +## Contribute + +Our commitment to Open Source can be found [here](https://vercel.com/oss). + +1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device. +2. Create a new branch `git checkout -b MY_BRANCH_NAME` +3. Install yarn: `npm install -g yarn` +4. Install the dependencies: `yarn` +5. Duplicate `.env.template` and rename it to `.env.local`. +6. Add proper store values to `.env.local`. +7. Run `yarn dev` to build and watch for code changes +8. The development branch is `canary` (this is the branch pull requests should be made against). + On a release, `canary` branch is rebased into `master`. + +## Troubleshoot + +<details> +<summary>I already own a BigCommerce store. What should I do?</summary> +<br> +First thing you do is: <b>set your environment variables</b> +<br> +<br> +.env.local + +```sh +BIGCOMMERCE_STOREFRONT_API_URL=<> +BIGCOMMERCE_STOREFRONT_API_TOKEN=<> +BIGCOMMERCE_STORE_API_URL=<> +BIGCOMMERCE_STORE_API_TOKEN=<> +BIGCOMMERCE_STORE_API_CLIENT_ID=<> +``` + +If your project was started with a "Deploy with Vercel" button, you can use Vercel's CLI to retrieve these credentials. + +1. Install Vercel CLI: `npm i -g vercel` +2. Link local instance with Vercel and Github accounts (creates .vercel file): `vercel link` +3. Download your environment variables: `vercel env pull .env.local` + +Next, you're free to customize the starter. More updates coming soon. Stay tuned. + +</details> + +<details> +<summary>BigCommerce shows a Coming Soon page and requests a Preview Code</summary> +<br> +After Email confirmation, Checkout should be manually enabled through BigCommerce platform. Look for "Review & test your store" section through BigCommerce's dashboard. +<br> +<br> +BigCommerce team has been notified and they plan to add more detailed about this subject. +</details> diff --git a/framework/bigcommerce/config.json b/framework/bigcommerce/config.json new file mode 100644 index 000000000..17ef37e25 --- /dev/null +++ b/framework/bigcommerce/config.json @@ -0,0 +1,5 @@ +{ + "features": { + "wishlist": false + } +} diff --git a/framework/bigcommerce/product/use-search.tsx b/framework/bigcommerce/product/use-search.tsx index 52db6a72d..505630029 100644 --- a/framework/bigcommerce/product/use-search.tsx +++ b/framework/bigcommerce/product/use-search.tsx @@ -1,4 +1,4 @@ -import useSearch, { UseSearch } from '@commerce/products/use-search' +import useSearch, { UseSearch } from '@commerce/cart/products/use-search' import type { BigcommerceProvider } from '..' export default useSearch as UseSearch<BigcommerceProvider> diff --git a/framework/commerce/products/use-search.tsx b/framework/commerce/cart/products/use-search.tsx similarity index 85% rename from framework/commerce/products/use-search.tsx rename to framework/commerce/cart/products/use-search.tsx index 1f887f5fe..e43ca301a 100644 --- a/framework/commerce/products/use-search.tsx +++ b/framework/commerce/cart/products/use-search.tsx @@ -1,13 +1,13 @@ -import type { SearchProductsData } from '../types' +import type { SearchProductsData } from '../../types' import type { Prop, HookFetcherFn, UseHookInput, UseHookResponse, -} from '../utils/types' -import defaultFetcher from '../utils/default-fetcher' -import useData from '../utils/use-data' -import { Provider, useCommerce } from '..' +} from '../../utils/types' +import defaultFetcher from '../../utils/default-fetcher' +import useData from '../../utils/use-data' +import { Provider, useCommerce } from '../..' import { BigcommerceProvider } from '@framework' export type UseSearchHandler<P extends Provider> = Prop< diff --git a/framework/commerce/config.json b/framework/commerce/config.json new file mode 100644 index 000000000..a0e7afc5d --- /dev/null +++ b/framework/commerce/config.json @@ -0,0 +1,5 @@ +{ + "features": { + "wishlist": true + } +} diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index 41aedb228..c828e74f9 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -148,3 +148,10 @@ export interface RemoveCartItemBody { export interface RemoveCartItemHandlerBody extends Partial<RemoveCartItemBody> { cartId?: string } + +// Features API +type Features = 'wishlist' | 'checkout' + +export interface FrameworkConfig { + features: Record<Features, boolean> +} diff --git a/pages/_app.tsx b/pages/_app.tsx index 132ce5f18..dae0311b4 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,12 +1,11 @@ import '@assets/main.css' -import 'keen-slider/keen-slider.min.css' import '@assets/chrome-bug.css' +import 'keen-slider/keen-slider.min.css' import { FC, useEffect } from 'react' import type { AppProps } from 'next/app' - -import { ManagedUIContext } from '@components/ui/context' import { Head } from '@components/common' +import { ManagedUIContext } from '@components/ui/context' const Noop: FC = ({ children }) => <>{children}</> diff --git a/pages/wishlist.tsx b/pages/wishlist.tsx index a3f25d0e7..a1dd7a5fe 100644 --- a/pages/wishlist.tsx +++ b/pages/wishlist.tsx @@ -1,28 +1,43 @@ +import { useEffect } from 'react' +import { useRouter } from 'next/router' import type { GetStaticPropsContext } from 'next' -import { getConfig } from '@framework/api' -import getAllPages from '@framework/common/get-all-pages' -import useWishlist from '@framework/wishlist/use-wishlist' -import { Layout } from '@components/common' + import { Heart } from '@components/icons' +import { Layout } from '@components/common' import { Text, Container } from '@components/ui' -import { WishlistCard } from '@components/wishlist' import { defaultPageProps } from '@lib/defaults' +import { getConfig } from '@framework/api' import { useCustomer } from '@framework/customer' +import { WishlistCard } from '@components/wishlist' +import useWishlist from '@framework/wishlist/use-wishlist' +import getAllPages from '@framework/common/get-all-pages' +import frameworkConfig from '@framework/config.json' export async function getStaticProps({ preview, locale, }: GetStaticPropsContext) { + // Disabling page if Feature is not available + if (!frameworkConfig.features.wishlist) { + return { + notFound: true, + } + } + const config = getConfig({ locale }) const { pages } = await getAllPages({ config, preview }) return { - props: { ...defaultPageProps, pages }, + props: { + pages, + ...defaultPageProps, + }, } } export default function Wishlist() { const { data: customer } = useCustomer() const { data, isLoading, isEmpty } = useWishlist() + const router = useRouter() return ( <Container> From de64b6d99119362730f8f8d391a5e0cf1702a822 Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Fri, 12 Feb 2021 17:10:17 -0300 Subject: [PATCH 118/221] New changes and initial Features API --- components/common/UserNav/UserNav.tsx | 18 +++++++++++------- components/product/ProductCard/ProductCard.tsx | 10 +++++++--- .../wishlist/WishlistButton/WishlistButton.tsx | 2 +- components/wishlist/WishlistButton/index.ts | 1 + components/wishlist/index.ts | 1 + pages/index.tsx | 2 +- pages/orders.tsx | 6 +++--- tsconfig.json | 4 ++-- yarn.lock | 6 +++--- 9 files changed, 30 insertions(+), 20 deletions(-) create mode 100644 components/wishlist/WishlistButton/index.ts diff --git a/components/common/UserNav/UserNav.tsx b/components/common/UserNav/UserNav.tsx index c615c18b1..19151c38b 100644 --- a/components/common/UserNav/UserNav.tsx +++ b/components/common/UserNav/UserNav.tsx @@ -9,6 +9,7 @@ import { useUI } from '@components/ui/context' import DropdownMenu from './DropdownMenu' import s from './UserNav.module.css' import { Avatar } from '@components/common' +import frameworkConfig from '@framework/config.json' interface Props { className?: string @@ -21,6 +22,7 @@ const UserNav: FC<Props> = ({ className }) => { const { data: customer } = useCustomer() const { toggleSidebar, closeSidebarIfPresent, openModal } = useUI() const itemsCount = data?.lineItems.reduce(countItem, 0) ?? 0 + const isWishlistEnabled = !!frameworkConfig.features.wishlist return ( <nav className={cn(s.root, className)}> @@ -30,13 +32,15 @@ const UserNav: FC<Props> = ({ className }) => { <Bag /> {itemsCount > 0 && <span className={s.bagCount}>{itemsCount}</span>} </li> - <li className={s.item}> - <Link href="/wishlist"> - <a onClick={closeSidebarIfPresent} aria-label="Wishlist"> - <Heart /> - </a> - </Link> - </li> + {isWishlistEnabled && ( + <li className={s.item}> + <Link href="/wishlist"> + <a onClick={closeSidebarIfPresent} aria-label="Wishlist"> + <Heart /> + </a> + </Link> + </li> + )} <li className={s.item}> {customer ? ( <DropdownMenu /> diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index 464f097e8..66233df79 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -3,7 +3,9 @@ import cn from 'classnames' import Link from 'next/link' import s from './ProductCard.module.css' import Image, { ImageProps } from 'next/image' -// import WishlistButton from '@components/wishlist/WishlistButton' +import frameworkConfig from '@framework/config.json' +const isWishlistEnabled = !!frameworkConfig.features.wishlist +import WishlistButton from '@components/wishlist/WishlistButton' interface Props { className?: string @@ -56,11 +58,13 @@ const ProductCard: FC<Props> = ({ {product.price.currencyCode} </span> </div> - {/* <WishlistButton + {isWishlistEnabled && ( + <WishlistButton className={s.wishlistButton} productId={product.id} variant={product.variants[0]} - /> */} + /> + )} </div> <div className={s.imageContainer}> {product?.images && ( diff --git a/components/wishlist/WishlistButton/WishlistButton.tsx b/components/wishlist/WishlistButton/WishlistButton.tsx index dced18a89..f7e478293 100644 --- a/components/wishlist/WishlistButton/WishlistButton.tsx +++ b/components/wishlist/WishlistButton/WishlistButton.tsx @@ -7,7 +7,7 @@ import useCustomer from '@framework/customer/use-customer' import useAddItem from '@framework/wishlist/use-add-item' import useRemoveItem from '@framework/wishlist/use-remove-item' import useWishlist from '@framework/wishlist/use-add-item' - +import type { Product } from '@framework/types' type Props = { productId: Product['id'] variant: ProductVariant diff --git a/components/wishlist/WishlistButton/index.ts b/components/wishlist/WishlistButton/index.ts new file mode 100644 index 000000000..66e88074b --- /dev/null +++ b/components/wishlist/WishlistButton/index.ts @@ -0,0 +1 @@ +export { default } from './WishlistButton' diff --git a/components/wishlist/index.ts b/components/wishlist/index.ts index 470e6682c..8aee9f816 100644 --- a/components/wishlist/index.ts +++ b/components/wishlist/index.ts @@ -1 +1,2 @@ export { default as WishlistCard } from './WishlistCard' +export { default as WishlistButton } from './WishlistButton' diff --git a/pages/index.tsx b/pages/index.tsx index 811472674..3a84112e5 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,7 +1,7 @@ import { Layout } from '@components/common' import { Grid, Marquee, Hero } from '@components/ui' import { ProductCard } from '@components/product' -import HomeAllProductsGrid from '@components/common/HomeAllProductsGrid' +// import HomeAllProductsGrid from '@components/common/HomeAllProductsGrid' import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next' import { getConfig } from '@framework/api' diff --git a/pages/orders.tsx b/pages/orders.tsx index 08e32c2b2..db4ab55b2 100644 --- a/pages/orders.tsx +++ b/pages/orders.tsx @@ -1,9 +1,9 @@ import type { GetStaticPropsContext } from 'next' -import { getConfig } from '@framework/api' -import getAllPages from '@framework/common/get-all-pages' +import { Bag } from '@components/icons' import { Layout } from '@components/common' import { Container, Text } from '@components/ui' -import { Bag } from '@components/icons' +import { getConfig } from '@framework/api' +import getAllPages from '@framework/common/get-all-pages' export async function getStaticProps({ preview, diff --git a/tsconfig.json b/tsconfig.json index 67de1ee36..43dfd2a27 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,6 +26,6 @@ "@framework": ["framework/bigcommerce"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], - "exclude": ["node_modules", "components/wishlist"] + "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], + "exclude": ["node_modules"] } diff --git a/yarn.lock b/yarn.lock index b46f7acce..c23252b2b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2135,9 +2135,9 @@ camelcase@^6.0.0: integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001093, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001113, caniuse-lite@^1.0.30001154, caniuse-lite@^1.0.30001173: - version "1.0.30001174" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001174.tgz#0f2aca2153fd88ceb07a2bb982fc2acb787623c4" - integrity sha512-tqClL/4ThQq6cfFXH3oJL4rifFBeM6gTkphjao5kgwMaW9yn0tKgQLAEfKzDwj6HQWCB/aWo8kTFlSvIN8geEA== + version "1.0.30001187" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001187.tgz" + integrity sha512-w7/EP1JRZ9552CyrThUnay2RkZ1DXxKe/Q2swTC4+LElLh9RRYrL1Z+27LlakB8kzY0fSmHw9mc7XYDUKAKWMA== caseless@~0.12.0: version "0.12.0" From d9243880a3c81c65defa7f4699033cb0ed0c7439 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Sun, 14 Feb 2021 18:40:48 -0500 Subject: [PATCH 119/221] Fixed some types --- components/cart/CartItem/CartItem.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/components/cart/CartItem/CartItem.tsx b/components/cart/CartItem/CartItem.tsx index 87fb86d0b..bb57c3f25 100644 --- a/components/cart/CartItem/CartItem.tsx +++ b/components/cart/CartItem/CartItem.tsx @@ -75,6 +75,8 @@ const CartItem = ({ setRemoving(false) } } + // TODO: Add a type for this + const options = (item as any).options useEffect(() => { // Reset the quantity state if the item quantity changes @@ -95,8 +97,8 @@ const CartItem = ({ className={s.productImage} width={150} height={150} - src={item.variant.image.url} - alt={item.variant.image.altText} + src={item.variant.image!.url} + alt={item.variant.image!.altText} unoptimized /> </div> @@ -109,15 +111,15 @@ const CartItem = ({ {item.name} </span> </Link> - {item.options && item.options.length > 0 ? ( + {options && options.length > 0 ? ( <div className=""> - {item.options.map((option: ItemOption, i: number) => ( + {options.map((option: ItemOption, i: number) => ( <span key={`${item.id}-${option.name}`} className="text-sm font-semibold text-accents-7" > {option.value} - {i === item.options.length - 1 ? '' : ', '} + {i === options.length - 1 ? '' : ', '} </span> ))} </div> From c636fcbc4be2a364bb09c1e78b1218114d52dc82 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 15 Feb 2021 00:09:57 -0500 Subject: [PATCH 120/221] Fixed more types --- components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx | 3 ++- components/product/ProductCard/ProductCard.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx index d55f51f7a..2e01fa0cb 100644 --- a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx +++ b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx @@ -8,7 +8,8 @@ import { getCategoryPath, getDesignerPath } from '@lib/search' interface Props { categories?: any brands?: any - products?: Product[] + // TODO: use the product type here + products?: any[] } const Head: FC<Props> = ({ categories, brands, products = [] }) => { diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index 464f097e8..461524855 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -7,7 +7,8 @@ import Image, { ImageProps } from 'next/image' interface Props { className?: string - product: Product + // TODO: use the product type here + product: any variant?: 'slim' | 'simple' imgProps?: Omit<ImageProps, 'src'> } From 1b9ae58d372028840f8f3e6e17f9787ab5b0f5c3 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Mon, 15 Feb 2021 14:23:47 +0200 Subject: [PATCH 121/221] fixes after upstream changes --- framework/shopify/customer/use-customer.tsx | 42 +---------- framework/shopify/product/use-search.tsx | 74 +------------------ framework/shopify/provider.tsx | 80 ++++++++++++++++++--- framework/shopify/utils/index.ts | 9 +++ 4 files changed, 87 insertions(+), 118 deletions(-) create mode 100644 framework/shopify/utils/index.ts diff --git a/framework/shopify/customer/use-customer.tsx b/framework/shopify/customer/use-customer.tsx index 6f956d2c2..652188bbe 100644 --- a/framework/shopify/customer/use-customer.tsx +++ b/framework/shopify/customer/use-customer.tsx @@ -1,40 +1,4 @@ -import type { HookFetcher } from '@commerce/utils/types' -import type { SwrOptions } from '@commerce/utils/use-data' -import useCommerceCustomer from '@commerce/use-customer' -import getCustomerQuery from '@framework/utils/queries/get-customer-query' -import { getCustomerToken } from '@framework/utils/customer-token' +import useCustomer, { UseCustomer } from '@commerce/customer/use-customer' +import type { ShopifyProvider } from '..' -const defaultOpts = { - query: getCustomerQuery, -} - -export const fetcher: HookFetcher<any | null> = async (options, _, fetch) => { - const customerAccessToken = getCustomerToken() - if (customerAccessToken) { - const data = await fetch<any | null>({ - ...defaultOpts, - ...options, - variables: { customerAccessToken }, - }) - return data?.customer ?? null - } - return null -} - -export function extendHook( - customFetcher: typeof fetcher, - swrOptions?: SwrOptions<any | null> -) { - const useCustomer = () => { - return useCommerceCustomer(defaultOpts, [], customFetcher, { - revalidateOnFocus: false, - ...swrOptions, - }) - } - - useCustomer.extend = extendHook - - return useCustomer -} - -export default extendHook(fetcher) +export default useCustomer as UseCustomer<ShopifyProvider> diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx index ba833e517..4f83baa63 100644 --- a/framework/shopify/product/use-search.tsx +++ b/framework/shopify/product/use-search.tsx @@ -1,72 +1,4 @@ -import useCommerceSearch from '@commerce/products/use-search' -import { getAllProductsQuery } from '@framework/utils/queries' +import useSearch, { UseSearch } from '@commerce/products/use-search' +import type { ShopifyProvider } from '..' -import type { Product } from 'framework/bigcommerce/schema' -import type { HookFetcher } from '@commerce/utils/types' -import type { SwrOptions } from '@commerce/utils/use-data' -import type { ProductConnection, ProductEdge } from '@framework/schema' - -import getSearchVariables from '@framework/utils/get-search-variables' - -import { normalizeProduct } from '@framework/lib/normalize' - -export type SearchProductsInput = { - search?: string - categoryId?: string - brandId?: string - sort?: string -} - -export type SearchRequestProductsData = { - products?: ProductEdge[] -} - -export type SearchProductsData = { - products: Product[] - found: boolean -} - -export const fetcher: HookFetcher< - SearchProductsData, - SearchProductsInput -> = async (options, input, fetch) => { - const resp = await fetch({ - query: options?.query, - method: options?.method, - variables: getSearchVariables(input), - }) - const edges = resp.products?.edges - return { - products: edges?.map(({ node: p }: ProductEdge) => normalizeProduct(p)), - found: !!edges?.length, - } -} - -export function extendHook( - customFetcher: typeof fetcher, - swrOptions?: SwrOptions<SearchProductsData, SearchProductsInput> -) { - const useSearch = (input: SearchProductsInput = {}) => { - const response = useCommerceSearch( - { - query: getAllProductsQuery, - }, - [ - ['search', input.search], - ['categoryId', input.categoryId], - ['brandId', input.brandId], - ['sort', input.sort], - ], - customFetcher, - { revalidateOnFocus: false, ...swrOptions } - ) - - return response - } - - useSearch.extend = extendHook - - return useSearch -} - -export default extendHook(fetcher) +export default useSearch as UseSearch<ShopifyProvider> diff --git a/framework/shopify/provider.tsx b/framework/shopify/provider.tsx index e8770ac97..4a1f01525 100644 --- a/framework/shopify/provider.tsx +++ b/framework/shopify/provider.tsx @@ -9,12 +9,23 @@ import { } from './const' import { Cart } from './types' -import { normalizeCart } from './lib/normalize' -import handleFetchResponse from './utils/handle-fetch-response' -import getCheckoutQuery from './utils/queries/get-checkout-query' -import { FetchCartInput, UseCartInput } from '@commerce/cart/use-cart' +import { Customer } from '@commerce/types' +import { normalizeCart, normalizeProduct } from './lib/normalize' +import { FetchCartInput } from '@commerce/cart/use-cart' import { checkoutCreate, checkoutToCart } from './cart/utils' +import { + getAllProductsQuery, + getCustomerQuery, + getCheckoutQuery, + handleFetchResponse, + getSearchVariables, +} from './utils' + +import { ProductEdge } from './schema' +import { SearchProductsInput } from 'framework/bigcommerce/provider' +import { SearchProductsData } from 'framework/bigcommerce/api/catalog/products' + const fetcher: Fetcher = async ({ method = 'POST', variables, query }) => { return handleFetchResponse( await fetch(API_URL, { @@ -55,16 +66,13 @@ export const cartFetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ const useCart: HookHandler< Cart | null, {}, - any, - any, - any, + FetchCartInput, { isEmpty?: boolean } > = { fetchOptions: { query: getCheckoutQuery, }, fetcher: cartFetcher, - normalizer: normalizeCart, useHook({ input, useData }) { const response = useData({ swrOptions: { revalidateOnFocus: false, ...input.swrOptions }, @@ -85,6 +93,60 @@ const useCart: HookHandler< }, } +const useSearch: HookHandler< + SearchProductsData, + SearchProductsInput, + SearchProductsInput +> = { + fetchOptions: { + query: getAllProductsQuery, + }, + async fetcher({ input, options, fetch }) { + const resp = await fetch({ + query: options?.query, + method: options?.method, + variables: getSearchVariables(input), + }) + const edges = resp.products?.edges + return { + products: edges?.map(({ node: p }: ProductEdge) => normalizeProduct(p)), + found: !!edges?.length, + } + }, + useHook({ input, useData }) { + return useData({ + input: [ + ['search', input.search], + ['categoryId', input.categoryId], + ['brandId', input.brandId], + ['sort', input.sort], + ], + swrOptions: { + revalidateOnFocus: false, + ...input.swrOptions, + }, + }) + }, +} + +const useCustomerHandler: HookHandler<Customer | null> = { + fetchOptions: { + query: getCustomerQuery, + }, + async fetcher({ options, fetch }) { + const data = await fetch<any | null>(options) + return data?.customer ?? null + }, + useHook({ input, useData }) { + return useData({ + swrOptions: { + revalidateOnFocus: false, + ...input.swrOptions, + }, + }) + }, +} + export const shopifyProvider = { locale: 'en-us', cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, @@ -92,6 +154,8 @@ export const shopifyProvider = { fetcher, cartNormalizer: normalizeCart, cart: { useCart }, + customer: { useCustomer: useCustomerHandler }, + products: { useSearch }, } export type ShopifyProvider = typeof shopifyProvider diff --git a/framework/shopify/utils/index.ts b/framework/shopify/utils/index.ts new file mode 100644 index 000000000..cecf06fd1 --- /dev/null +++ b/framework/shopify/utils/index.ts @@ -0,0 +1,9 @@ +export { default as handleFetchResponse } from './handle-fetch-response' +export { default as getSearchVariables } from './get-search-variables' +export { default as getSortVariables } from './get-sort-variables' +export { default as getVendors } from './get-vendors' +export { default as getCategories } from './get-categories' + +export * from './customer-token' +export * from './queries' +export * from './mutations' From 62ed50a64641e1476b088ef69227cf62e3a4485b Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 15 Feb 2021 10:15:20 -0500 Subject: [PATCH 122/221] Fixed product types --- .../HomeAllProductsGrid.tsx | 4 +- .../product/ProductCard/ProductCard.tsx | 4 +- .../product/ProductView/ProductView.tsx | 3 +- components/product/helpers.ts | 2 + .../WishlistButton/WishlistButton.tsx | 1 + .../wishlist/WishlistCard/WishlistCard.tsx | 5 +- .../api/catalog/handlers/get-products.ts | 2 +- framework/bigcommerce/api/catalog/products.ts | 2 +- framework/bigcommerce/api/wishlist/index.ts | 3 +- framework/bigcommerce/lib/normalize.ts | 1 + .../bigcommerce/product/get-all-products.ts | 2 + framework/commerce/types.d.ts | 75 ------------------- framework/commerce/types.ts | 51 +++++++++++++ 13 files changed, 70 insertions(+), 85 deletions(-) delete mode 100644 framework/commerce/types.d.ts diff --git a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx index 2e01fa0cb..49e115df6 100644 --- a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx +++ b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx @@ -1,5 +1,6 @@ import { FC } from 'react' import Link from 'next/link' +import type { Product } from '@commerce/types' import { Grid } from '@components/ui' import { ProductCard } from '@components/product' import s from './HomeAllProductsGrid.module.css' @@ -8,8 +9,7 @@ import { getCategoryPath, getDesignerPath } from '@lib/search' interface Props { categories?: any brands?: any - // TODO: use the product type here - products?: any[] + products?: Product[] } const Head: FC<Props> = ({ categories, brands, products = [] }) => { diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index 461524855..a3bd73576 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -1,14 +1,14 @@ import { FC } from 'react' import cn from 'classnames' import Link from 'next/link' +import type { Product } from '@commerce/types' import s from './ProductCard.module.css' import Image, { ImageProps } from 'next/image' // import WishlistButton from '@components/wishlist/WishlistButton' interface Props { className?: string - // TODO: use the product type here - product: any + product: Product variant?: 'slim' | 'simple' imgProps?: Omit<ImageProps, 'src'> } diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index 65fa4d93f..b5452bef1 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -8,6 +8,7 @@ import { useUI } from '@components/ui' import { Swatch, ProductSlider } from '@components/product' import { Button, Container, Text } from '@components/ui' +import type { Product } from '@commerce/types' import usePrice from '@framework/product/use-price' import { useAddItem } from '@framework/cart' @@ -41,7 +42,7 @@ const ProductView: FC<Props> = ({ product }) => { setLoading(true) try { await addItem({ - productId: product.id, + productId: String(product.id), variantId: variant ? variant.id : product.variants[0].id, }) openSidebar() diff --git a/components/product/helpers.ts b/components/product/helpers.ts index ae0c43530..029476c92 100644 --- a/components/product/helpers.ts +++ b/components/product/helpers.ts @@ -1,3 +1,5 @@ +import type { Product } from '@commerce/types' + export type SelectedOptions = { size: string | null color: string | null diff --git a/components/wishlist/WishlistButton/WishlistButton.tsx b/components/wishlist/WishlistButton/WishlistButton.tsx index dced18a89..0c4c20194 100644 --- a/components/wishlist/WishlistButton/WishlistButton.tsx +++ b/components/wishlist/WishlistButton/WishlistButton.tsx @@ -3,6 +3,7 @@ import cn from 'classnames' import { Heart } from '@components/icons' import { useUI } from '@components/ui' +import type { Product, ProductVariant } from '@commerce/types' import useCustomer from '@framework/customer/use-customer' import useAddItem from '@framework/wishlist/use-add-item' import useRemoveItem from '@framework/wishlist/use-remove-item' diff --git a/components/wishlist/WishlistCard/WishlistCard.tsx b/components/wishlist/WishlistCard/WishlistCard.tsx index d1a9403b3..38663ab68 100644 --- a/components/wishlist/WishlistCard/WishlistCard.tsx +++ b/components/wishlist/WishlistCard/WishlistCard.tsx @@ -7,6 +7,7 @@ import { Trash } from '@components/icons' import { Button, Text } from '@components/ui' import { useUI } from '@components/ui/context' +import type { Product } from '@commerce/types' import usePrice from '@framework/product/use-price' import useAddItem from '@framework/cart/use-add-item' import useRemoveItem from '@framework/wishlist/use-remove-item' @@ -42,8 +43,8 @@ const WishlistCard: FC<Props> = ({ product }) => { setLoading(true) try { await addItem({ - productId: product.id, - variantId: product.variants[0].id, + productId: String(product.id), + variantId: String(product.variants[0].id), }) openSidebar() setLoading(false) diff --git a/framework/bigcommerce/api/catalog/handlers/get-products.ts b/framework/bigcommerce/api/catalog/handlers/get-products.ts index 894dc5cf3..20b9c5254 100644 --- a/framework/bigcommerce/api/catalog/handlers/get-products.ts +++ b/framework/bigcommerce/api/catalog/handlers/get-products.ts @@ -1,4 +1,4 @@ -import { Product } from 'framework/types' +import { Product } from '@commerce/types' import getAllProducts, { ProductEdge } from '../../../product/get-all-products' import type { ProductsHandlers } from '../products' diff --git a/framework/bigcommerce/api/catalog/products.ts b/framework/bigcommerce/api/catalog/products.ts index d13dcd3c3..d52b2de7e 100644 --- a/framework/bigcommerce/api/catalog/products.ts +++ b/framework/bigcommerce/api/catalog/products.ts @@ -1,3 +1,4 @@ +import type { Product } from '@commerce/types' import isAllowedMethod from '../utils/is-allowed-method' import createApiHandler, { BigcommerceApiHandler, @@ -5,7 +6,6 @@ import createApiHandler, { } from '../utils/create-api-handler' import { BigcommerceApiError } from '../utils/errors' import getProducts from './handlers/get-products' -import { Product } from 'framework/types' export type SearchProductsData = { products: Product[] diff --git a/framework/bigcommerce/api/wishlist/index.ts b/framework/bigcommerce/api/wishlist/index.ts index e892d2e78..7c700689c 100644 --- a/framework/bigcommerce/api/wishlist/index.ts +++ b/framework/bigcommerce/api/wishlist/index.ts @@ -11,6 +11,7 @@ import type { import getWishlist from './handlers/get-wishlist' import addItem from './handlers/add-item' import removeItem from './handlers/remove-item' +import type { Product, ProductVariant, Customer } from '@commerce/types' export type { Wishlist, WishlistItem } @@ -24,7 +25,7 @@ export type AddItemBody = { item: ItemBody } export type RemoveItemBody = { itemId: Product['id'] } export type WishlistBody = { - customer_id: Customer['id'] + customer_id: Customer['entityId'] is_public: number name: string items: any[] diff --git a/framework/bigcommerce/lib/normalize.ts b/framework/bigcommerce/lib/normalize.ts index 89aed2c38..cc7606099 100644 --- a/framework/bigcommerce/lib/normalize.ts +++ b/framework/bigcommerce/lib/normalize.ts @@ -1,3 +1,4 @@ +import type { Product } from '@commerce/types' import type { Cart, BigcommerceCart, LineItem } from '../types' import update from './immutability' diff --git a/framework/bigcommerce/product/get-all-products.ts b/framework/bigcommerce/product/get-all-products.ts index b7d728c4a..4c563bc62 100644 --- a/framework/bigcommerce/product/get-all-products.ts +++ b/framework/bigcommerce/product/get-all-products.ts @@ -2,6 +2,7 @@ import type { GetAllProductsQuery, GetAllProductsQueryVariables, } from '../schema' +import type { Product } from '@commerce/types' import type { RecursivePartial, RecursiveRequired } from '../api/utils/types' import filterEdges from '../api/utils/filter-edges' import setProductLocaleMeta from '../api/utils/set-product-locale-meta' @@ -94,6 +95,7 @@ async function getAllProducts({ variables?: ProductVariables config?: BigcommerceConfig preview?: boolean + // TODO: fix the product type here } = {}): Promise<{ products: Product[] | any[] }> { config = getConfig(config) diff --git a/framework/commerce/types.d.ts b/framework/commerce/types.d.ts deleted file mode 100644 index 9e69ec25d..000000000 --- a/framework/commerce/types.d.ts +++ /dev/null @@ -1,75 +0,0 @@ -interface Entity { - id: string | number - [prop: string]: any -} - -interface Product extends Entity { - name: string - description: string - slug?: string - path?: string - images: ProductImage[] - variants: ProductVariant[] - price: ProductPrice - options: ProductOption[] - sku?: string -} - -interface ProductOption extends Entity { - displayName: string - values: ProductOptionValues[] -} - -interface ProductOptionValues { - label: string - hexColors?: string[] -} - -interface ProductImage { - url: string - alt?: string -} - -interface ProductVariant { - id: string | number - options: ProductOption[] -} - -interface ProductPrice { - value: number - currencyCode: 'USD' | 'ARS' | string | undefined - retailPrice?: number - salePrice?: number - listPrice?: number - extendedSalePrice?: number - extendedListPrice?: number -} - -interface CartItem extends Entity { - quantity: number - productId: Product['id'] - variantId: ProductVariant['id'] - images: ProductImage[] -} - -interface Wishlist extends Entity { - products: Pick<Product, 'id' | 'name' | 'prices'>[] -} - -interface Order {} - -interface Customer extends Entity {} - -type UseCustomerResponse = { - customer: Customer -} | null - -interface Category extends Entity { - name: string -} - -interface Brand extends Entity { - name: string -} - -type Features = 'wishlist' | 'customer' diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index 41aedb228..1f8390535 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -148,3 +148,54 @@ export interface RemoveCartItemBody { export interface RemoveCartItemHandlerBody extends Partial<RemoveCartItemBody> { cartId?: string } + +/** + * Temporal types + */ + +interface Entity { + id: string | number + [prop: string]: any +} + +export interface Product extends Entity { + name: string + description: string + slug?: string + path?: string + images: ProductImage[] + variants: ProductVariant2[] + price: ProductPrice + options: ProductOption[] + sku?: string +} + +interface ProductOption extends Entity { + displayName: string + values: ProductOptionValues[] +} + +interface ProductOptionValues { + label: string + hexColors?: string[] +} + +interface ProductImage { + url: string + alt?: string +} + +interface ProductVariant2 { + id: string | number + options: ProductOption[] +} + +interface ProductPrice { + value: number + currencyCode: 'USD' | 'ARS' | string | undefined + retailPrice?: number + salePrice?: number + listPrice?: number + extendedSalePrice?: number + extendedListPrice?: number +} From c62187425053b6d347a76b40ce0a0ccba9019e11 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 15 Feb 2021 10:25:35 -0500 Subject: [PATCH 123/221] Fixed another product type --- components/product/ProductView/ProductView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index b5452bef1..61beda7fe 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -43,7 +43,7 @@ const ProductView: FC<Props> = ({ product }) => { try { await addItem({ productId: String(product.id), - variantId: variant ? variant.id : product.variants[0].id, + variantId: String(variant ? variant.id : product.variants[0].id), }) openSidebar() setLoading(false) From 499516e967833749518a03d5ef6fe9448c30de31 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 15 Feb 2021 10:41:57 -0500 Subject: [PATCH 124/221] Updated type --- framework/bigcommerce/api/catalog/handlers/get-products.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/bigcommerce/api/catalog/handlers/get-products.ts b/framework/bigcommerce/api/catalog/handlers/get-products.ts index 20b9c5254..bedd773b0 100644 --- a/framework/bigcommerce/api/catalog/handlers/get-products.ts +++ b/framework/bigcommerce/api/catalog/handlers/get-products.ts @@ -60,7 +60,7 @@ const getProducts: ProductsHandlers['getProducts'] = async ({ const productsById = graphqlData.products.reduce<{ [k: number]: Product }>((prods, p) => { - prods[p.id] = p + prods[Number(p.id)] = p return prods }, {}) From bb0b8d2771afc8450f86351c6cd481c01a129073 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 15 Feb 2021 12:02:24 -0500 Subject: [PATCH 125/221] Fixed remaining issues with types --- .../customer/get-customer-wishlist.ts | 2 +- framework/bigcommerce/product/get-product.ts | 1 + framework/bigcommerce/provider.tsx | 2 +- framework/bigcommerce/wishlist/use-add-item.tsx | 16 ++++++++++------ .../bigcommerce/wishlist/use-remove-item.tsx | 8 ++++---- framework/commerce/wishlist/use-add-item.tsx | 7 +++++++ pages/product/[slug].tsx | 2 +- pages/wishlist.tsx | 2 +- 8 files changed, 26 insertions(+), 14 deletions(-) diff --git a/framework/bigcommerce/customer/get-customer-wishlist.ts b/framework/bigcommerce/customer/get-customer-wishlist.ts index a3c7413cc..e854ff933 100644 --- a/framework/bigcommerce/customer/get-customer-wishlist.ts +++ b/framework/bigcommerce/customer/get-customer-wishlist.ts @@ -68,7 +68,7 @@ async function getCustomerWishlist({ const productsById = graphqlData.products.reduce<{ [k: number]: ProductEdge }>((prods, p) => { - prods[p.node.entityId] = p + prods[Number(p.node.entityId)] = p as any return prods }, {}) // Populate the wishlist items with the graphql products diff --git a/framework/bigcommerce/product/get-product.ts b/framework/bigcommerce/product/get-product.ts index 3624a9cca..7d77eb194 100644 --- a/framework/bigcommerce/product/get-product.ts +++ b/framework/bigcommerce/product/get-product.ts @@ -3,6 +3,7 @@ import setProductLocaleMeta from '../api/utils/set-product-locale-meta' import { productInfoFragment } from '../api/fragments/product' import { BigcommerceConfig, getConfig } from '../api' import { normalizeProduct } from '@framework/lib/normalize' +import type { Product } from '@commerce/types' export const getProductQuery = /* GraphQL */ ` query getProduct( diff --git a/framework/bigcommerce/provider.tsx b/framework/bigcommerce/provider.tsx index 6fd02e304..e8ac29cf5 100644 --- a/framework/bigcommerce/provider.tsx +++ b/framework/bigcommerce/provider.tsx @@ -107,7 +107,7 @@ const useWishlist: HookHandler< const { data: customer } = useCustomer() const response = useData({ input: [ - ['customerId', customer?.id], + ['customerId', (customer as any)?.id], ['includeProducts', input.includeProducts], ], swrOptions: { diff --git a/framework/bigcommerce/wishlist/use-add-item.tsx b/framework/bigcommerce/wishlist/use-add-item.tsx index 6e7d9de41..eb961951a 100644 --- a/framework/bigcommerce/wishlist/use-add-item.tsx +++ b/framework/bigcommerce/wishlist/use-add-item.tsx @@ -1,19 +1,23 @@ import { useCallback } from 'react' import { HookFetcher } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' -import useWishlistAddItem from '@commerce/wishlist/use-add-item' +import useWishlistAddItem, { + AddItemInput, +} from '@commerce/wishlist/use-add-item' +import { UseWishlistInput } from '@commerce/wishlist/use-wishlist' import type { ItemBody, AddItemBody } from '../api/wishlist' import useCustomer from '../customer/use-customer' -import useWishlist, { UseWishlistOptions, Wishlist } from './use-wishlist' +import useWishlist from './use-wishlist' +import type { BigcommerceProvider } from '..' const defaultOpts = { url: '/api/bigcommerce/wishlist', method: 'POST', } -export type AddItemInput = ItemBody +// export type AddItemInput = ItemBody -export const fetcher: HookFetcher<Wishlist, AddItemBody> = ( +export const fetcher: HookFetcher<any, AddItemBody> = ( options, { item }, fetch @@ -27,13 +31,13 @@ export const fetcher: HookFetcher<Wishlist, AddItemBody> = ( } export function extendHook(customFetcher: typeof fetcher) { - const useAddItem = (opts?: UseWishlistOptions) => { + const useAddItem = (opts?: UseWishlistInput<BigcommerceProvider>) => { const { data: customer } = useCustomer() const { revalidate } = useWishlist(opts) const fn = useWishlistAddItem(defaultOpts, customFetcher) return useCallback( - async function addItem(input: AddItemInput) { + async function addItem(input: AddItemInput<any>) { if (!customer) { // A signed customer is required in order to have a wishlist throw new CommerceError({ diff --git a/framework/bigcommerce/wishlist/use-remove-item.tsx b/framework/bigcommerce/wishlist/use-remove-item.tsx index 86614a21a..d00b3e78b 100644 --- a/framework/bigcommerce/wishlist/use-remove-item.tsx +++ b/framework/bigcommerce/wishlist/use-remove-item.tsx @@ -4,7 +4,7 @@ import { CommerceError } from '@commerce/utils/errors' import useWishlistRemoveItem from '@commerce/wishlist/use-remove-item' import type { RemoveItemBody } from '../api/wishlist' import useCustomer from '../customer/use-customer' -import useWishlist, { UseWishlistOptions, Wishlist } from './use-wishlist' +import useWishlist from './use-wishlist' const defaultOpts = { url: '/api/bigcommerce/wishlist', @@ -15,7 +15,7 @@ export type RemoveItemInput = { id: string | number } -export const fetcher: HookFetcher<Wishlist | null, RemoveItemBody> = ( +export const fetcher: HookFetcher<any | null, RemoveItemBody> = ( options, { itemId }, fetch @@ -28,10 +28,10 @@ export const fetcher: HookFetcher<Wishlist | null, RemoveItemBody> = ( } export function extendHook(customFetcher: typeof fetcher) { - const useRemoveItem = (opts?: UseWishlistOptions) => { + const useRemoveItem = (opts?: any) => { const { data: customer } = useCustomer() const { revalidate } = useWishlist(opts) - const fn = useWishlistRemoveItem<Wishlist | null, RemoveItemBody>( + const fn = useWishlistRemoveItem<any | null, RemoveItemBody>( defaultOpts, customFetcher ) diff --git a/framework/commerce/wishlist/use-add-item.tsx b/framework/commerce/wishlist/use-add-item.tsx index f6c069f2b..d9b513694 100644 --- a/framework/commerce/wishlist/use-add-item.tsx +++ b/framework/commerce/wishlist/use-add-item.tsx @@ -1,4 +1,11 @@ import useAction from '../utils/use-action' +import type { CartItemBody } from '../types' + +// Input expected by the action returned by the `useAddItem` hook +// export interface AddItemInput { +// includeProducts?: boolean +// } +export type AddItemInput<T extends CartItemBody> = T const useAddItem = useAction diff --git a/pages/product/[slug].tsx b/pages/product/[slug].tsx index 3d2971eed..83aeaa54c 100644 --- a/pages/product/[slug].tsx +++ b/pages/product/[slug].tsx @@ -61,7 +61,7 @@ export default function Slug({ return router.isFallback ? ( <h1>Loading...</h1> // TODO (BC) Add Skeleton Views ) : ( - <ProductView product={product} /> + <ProductView product={product as any} /> ) } diff --git a/pages/wishlist.tsx b/pages/wishlist.tsx index a3f25d0e7..6de798411 100644 --- a/pages/wishlist.tsx +++ b/pages/wishlist.tsx @@ -44,7 +44,7 @@ export default function Wishlist() { ) : ( data && data.items?.map((item) => ( - <WishlistCard key={item.id} item={item} /> + <WishlistCard key={item.id} product={item as any} /> )) )} </div> From fe7d6df04f39f15c471a3a2f7343866efd94abf2 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 15 Feb 2021 12:02:31 -0500 Subject: [PATCH 126/221] Added a MutationHandler --- framework/commerce/utils/types.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 98e4ebbae..a06aa0477 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -76,6 +76,29 @@ export type HookHandler< fetcher?: HookFetcherFn<Data, FetchInput> } +export type MutationHandler< + // Data obj returned by the hook and fetch operation + Data, + // Input expected by the hook + Input extends { [k: string]: unknown } = {}, + // Input expected before doing a fetch operation + FetchInput extends HookFetchInput = {}, + // Custom state added to the response object of SWR + State = {} +> = { + useHook?(context: { + input: Input & { swrOptions?: SwrOptions<Data, FetchInput> } + useCallback( + fn: (context?: { + input?: HookFetchInput | HookSwrInput + swrOptions?: SwrOptions<Data, FetchInput> + }) => Data + ): ResponseState<Data> + }): ResponseState<Data> & State + fetchOptions: HookFetcherOptions + fetcher?: HookFetcherFn<Data, FetchInput> +} + export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< Data, CommerceError, From c4870a05e8fd183c62e93ec8c389823480c52263 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 15 Feb 2021 18:48:47 -0500 Subject: [PATCH 127/221] Moved the handlers to each hook --- framework/bigcommerce/cart/use-cart.tsx | 40 ++++- .../bigcommerce/customer/use-customer.tsx | 21 +++ framework/bigcommerce/product/use-search.tsx | 50 ++++++ framework/bigcommerce/provider.tsx | 167 +----------------- .../bigcommerce/wishlist/use-wishlist.tsx | 55 ++++++ 5 files changed, 171 insertions(+), 162 deletions(-) diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index 4f8a5cbcd..b5cc0cccf 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -1,4 +1,42 @@ -import useCart, { UseCart } from '@commerce/cart/use-cart' +import { useMemo } from 'react' +import { HookHandler } from '@commerce/utils/types' +import useCart, { UseCart, FetchCartInput } from '@commerce/cart/use-cart' +import { normalizeCart } from '../lib/normalize' +import type { Cart } from '../types' import type { BigcommerceProvider } from '..' export default useCart as UseCart<BigcommerceProvider> + +export const handler: HookHandler< + Cart | null, + {}, + FetchCartInput, + { isEmpty?: boolean } +> = { + fetchOptions: { + url: '/api/bigcommerce/cart', + method: 'GET', + }, + async fetcher({ input: { cartId }, options, fetch }) { + const data = cartId ? await fetch(options) : null + return data && normalizeCart(data) + }, + useHook({ input, useData }) { + const response = useData({ + swrOptions: { revalidateOnFocus: false, ...input.swrOptions }, + }) + + return useMemo( + () => + Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, + }, + }), + [response] + ) + }, +} diff --git a/framework/bigcommerce/customer/use-customer.tsx b/framework/bigcommerce/customer/use-customer.tsx index 95adb6fb3..3929002f7 100644 --- a/framework/bigcommerce/customer/use-customer.tsx +++ b/framework/bigcommerce/customer/use-customer.tsx @@ -1,4 +1,25 @@ +import { HookHandler } from '@commerce/utils/types' import useCustomer, { UseCustomer } from '@commerce/customer/use-customer' +import type { Customer, CustomerData } from '../api/customers' import type { BigcommerceProvider } from '..' export default useCustomer as UseCustomer<BigcommerceProvider> + +export const handler: HookHandler<Customer | null> = { + fetchOptions: { + url: '/api/bigcommerce/customers', + method: 'GET', + }, + async fetcher({ options, fetch }) { + const data = await fetch<CustomerData | null>(options) + return data?.customer ?? null + }, + useHook({ input, useData }) { + return useData({ + swrOptions: { + revalidateOnFocus: false, + ...input.swrOptions, + }, + }) + }, +} diff --git a/framework/bigcommerce/product/use-search.tsx b/framework/bigcommerce/product/use-search.tsx index 52db6a72d..393a8c0b9 100644 --- a/framework/bigcommerce/product/use-search.tsx +++ b/framework/bigcommerce/product/use-search.tsx @@ -1,4 +1,54 @@ +import { HookHandler } from '@commerce/utils/types' import useSearch, { UseSearch } from '@commerce/products/use-search' +import type { SearchProductsData } from '../api/catalog/products' import type { BigcommerceProvider } from '..' export default useSearch as UseSearch<BigcommerceProvider> + +export type SearchProductsInput = { + search?: string + categoryId?: number + brandId?: number + sort?: string +} + +export const handler: HookHandler< + SearchProductsData, + SearchProductsInput, + SearchProductsInput +> = { + fetchOptions: { + url: '/api/bigcommerce/catalog/products', + method: 'GET', + }, + fetcher({ input: { search, categoryId, brandId, sort }, options, fetch }) { + // Use a dummy base as we only care about the relative path + const url = new URL(options.url!, 'http://a') + + if (search) url.searchParams.set('search', search) + if (Number.isInteger(categoryId)) + url.searchParams.set('category', String(categoryId)) + if (Number.isInteger(brandId)) + url.searchParams.set('brand', String(brandId)) + if (sort) url.searchParams.set('sort', sort) + + return fetch({ + url: url.pathname + url.search, + method: options.method, + }) + }, + useHook({ input, useData }) { + return useData({ + input: [ + ['search', input.search], + ['categoryId', input.categoryId], + ['brandId', input.brandId], + ['sort', input.sort], + ], + swrOptions: { + revalidateOnFocus: false, + ...input.swrOptions, + }, + }) + }, +} diff --git a/framework/bigcommerce/provider.tsx b/framework/bigcommerce/provider.tsx index e8ac29cf5..a54fab0bb 100644 --- a/framework/bigcommerce/provider.tsx +++ b/framework/bigcommerce/provider.tsx @@ -1,13 +1,10 @@ -import { useMemo } from 'react' import { FetcherError } from '@commerce/utils/errors' -import type { Fetcher, HookHandler } from '@commerce/utils/types' -import type { FetchCartInput } from '@commerce/cart/use-cart' +import type { Fetcher } from '@commerce/utils/types' import { normalizeCart } from './lib/normalize' -import type { Wishlist } from './api/wishlist' -import type { Customer, CustomerData } from './api/customers' -import type { SearchProductsData } from './api/catalog/products' -import useCustomer from './customer/use-customer' -import type { Cart } from './types' +import { handler as useCart } from './cart/use-cart' +import { handler as useWishlist } from './wishlist/use-wishlist' +import { handler as useCustomer } from './customer/use-customer' +import { handler as useSearch } from './product/use-search' async function getText(res: Response) { try { @@ -46,158 +43,6 @@ const fetcher: Fetcher = async ({ throw await getError(res) } -const useCart: HookHandler< - Cart | null, - {}, - FetchCartInput, - { isEmpty?: boolean } -> = { - fetchOptions: { - url: '/api/bigcommerce/cart', - method: 'GET', - }, - async fetcher({ input: { cartId }, options, fetch }) { - const data = cartId ? await fetch(options) : null - return data && normalizeCart(data) - }, - useHook({ input, useData }) { - const response = useData({ - swrOptions: { revalidateOnFocus: false, ...input.swrOptions }, - }) - - return useMemo( - () => - Object.create(response, { - isEmpty: { - get() { - return (response.data?.lineItems.length ?? 0) <= 0 - }, - enumerable: true, - }, - }), - [response] - ) - }, -} - -const useWishlist: HookHandler< - Wishlist | null, - { includeProducts?: boolean }, - { customerId?: number; includeProducts: boolean }, - { isEmpty?: boolean } -> = { - fetchOptions: { - url: '/api/bigcommerce/wishlist', - method: 'GET', - }, - fetcher({ input: { customerId, includeProducts }, options, fetch }) { - if (!customerId) return null - - // Use a dummy base as we only care about the relative path - const url = new URL(options.url!, 'http://a') - - if (includeProducts) url.searchParams.set('products', '1') - - return fetch({ - url: url.pathname + url.search, - method: options.method, - }) - }, - useHook({ input, useData }) { - const { data: customer } = useCustomer() - const response = useData({ - input: [ - ['customerId', (customer as any)?.id], - ['includeProducts', input.includeProducts], - ], - swrOptions: { - revalidateOnFocus: false, - ...input.swrOptions, - }, - }) - - return useMemo( - () => - Object.create(response, { - isEmpty: { - get() { - return (response.data?.items?.length || 0) <= 0 - }, - enumerable: true, - }, - }), - [response] - ) - }, -} - -const useCustomerHandler: HookHandler<Customer | null> = { - fetchOptions: { - url: '/api/bigcommerce/customers', - method: 'GET', - }, - async fetcher({ options, fetch }) { - const data = await fetch<CustomerData | null>(options) - return data?.customer ?? null - }, - useHook({ input, useData }) { - return useData({ - swrOptions: { - revalidateOnFocus: false, - ...input.swrOptions, - }, - }) - }, -} - -export type SearchProductsInput = { - search?: string - categoryId?: number - brandId?: number - sort?: string -} - -const useSearch: HookHandler< - SearchProductsData, - SearchProductsInput, - SearchProductsInput -> = { - fetchOptions: { - url: '/api/bigcommerce/catalog/products', - method: 'GET', - }, - fetcher({ input: { search, categoryId, brandId, sort }, options, fetch }) { - // Use a dummy base as we only care about the relative path - const url = new URL(options.url!, 'http://a') - - if (search) url.searchParams.set('search', search) - if (Number.isInteger(categoryId)) - url.searchParams.set('category', String(categoryId)) - if (Number.isInteger(brandId)) - url.searchParams.set('brand', String(brandId)) - if (sort) url.searchParams.set('sort', sort) - - return fetch({ - url: url.pathname + url.search, - method: options.method, - }) - }, - useHook({ input, useData }) { - return useData({ - input: [ - ['search', input.search], - ['categoryId', input.categoryId], - ['brandId', input.brandId], - ['sort', input.sort], - ], - swrOptions: { - revalidateOnFocus: false, - ...input.swrOptions, - }, - }) - }, -} - export const bigcommerceProvider = { locale: 'en-us', cartCookie: 'bc_cartId', @@ -205,7 +50,7 @@ export const bigcommerceProvider = { cartNormalizer: normalizeCart, cart: { useCart }, wishlist: { useWishlist }, - customer: { useCustomer: useCustomerHandler }, + customer: { useCustomer }, products: { useSearch }, } diff --git a/framework/bigcommerce/wishlist/use-wishlist.tsx b/framework/bigcommerce/wishlist/use-wishlist.tsx index dfa3d9dbc..a93f0f6a4 100644 --- a/framework/bigcommerce/wishlist/use-wishlist.tsx +++ b/framework/bigcommerce/wishlist/use-wishlist.tsx @@ -1,4 +1,59 @@ +import { useMemo } from 'react' +import { HookHandler } from '@commerce/utils/types' import useWishlist, { UseWishlist } from '@commerce/wishlist/use-wishlist' +import type { Wishlist } from '../api/wishlist' +import useCustomer from '../customer/use-customer' import type { BigcommerceProvider } from '..' export default useWishlist as UseWishlist<BigcommerceProvider> + +export const handler: HookHandler< + Wishlist | null, + { includeProducts?: boolean }, + { customerId?: number; includeProducts: boolean }, + { isEmpty?: boolean } +> = { + fetchOptions: { + url: '/api/bigcommerce/wishlist', + method: 'GET', + }, + fetcher({ input: { customerId, includeProducts }, options, fetch }) { + if (!customerId) return null + + // Use a dummy base as we only care about the relative path + const url = new URL(options.url!, 'http://a') + + if (includeProducts) url.searchParams.set('products', '1') + + return fetch({ + url: url.pathname + url.search, + method: options.method, + }) + }, + useHook({ input, useData }) { + const { data: customer } = useCustomer() + const response = useData({ + input: [ + ['customerId', (customer as any)?.id], + ['includeProducts', input.includeProducts], + ], + swrOptions: { + revalidateOnFocus: false, + ...input.swrOptions, + }, + }) + + return useMemo( + () => + Object.create(response, { + isEmpty: { + get() { + return (response.data?.items?.length || 0) <= 0 + }, + enumerable: true, + }, + }), + [response] + ) + }, +} From 75d485d35a14e5f81dc5ec6919b352a6d2c0b391 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 15 Feb 2021 18:50:52 -0500 Subject: [PATCH 128/221] Moved the fetcher to its own file --- .../bigcommerce/{provider.tsx => fetcher.ts} | 18 +----------------- framework/bigcommerce/provider.ts | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 17 deletions(-) rename framework/bigcommerce/{provider.tsx => fetcher.ts} (64%) create mode 100644 framework/bigcommerce/provider.ts diff --git a/framework/bigcommerce/provider.tsx b/framework/bigcommerce/fetcher.ts similarity index 64% rename from framework/bigcommerce/provider.tsx rename to framework/bigcommerce/fetcher.ts index a54fab0bb..f8ca0c578 100644 --- a/framework/bigcommerce/provider.tsx +++ b/framework/bigcommerce/fetcher.ts @@ -1,10 +1,5 @@ import { FetcherError } from '@commerce/utils/errors' import type { Fetcher } from '@commerce/utils/types' -import { normalizeCart } from './lib/normalize' -import { handler as useCart } from './cart/use-cart' -import { handler as useWishlist } from './wishlist/use-wishlist' -import { handler as useCustomer } from './customer/use-customer' -import { handler as useSearch } from './product/use-search' async function getText(res: Response) { try { @@ -43,15 +38,4 @@ const fetcher: Fetcher = async ({ throw await getError(res) } -export const bigcommerceProvider = { - locale: 'en-us', - cartCookie: 'bc_cartId', - fetcher, - cartNormalizer: normalizeCart, - cart: { useCart }, - wishlist: { useWishlist }, - customer: { useCustomer }, - products: { useSearch }, -} - -export type BigcommerceProvider = typeof bigcommerceProvider +export default fetcher diff --git a/framework/bigcommerce/provider.ts b/framework/bigcommerce/provider.ts new file mode 100644 index 000000000..ee5630813 --- /dev/null +++ b/framework/bigcommerce/provider.ts @@ -0,0 +1,17 @@ +import { handler as useCart } from './cart/use-cart' +import { handler as useWishlist } from './wishlist/use-wishlist' +import { handler as useCustomer } from './customer/use-customer' +import { handler as useSearch } from './product/use-search' +import fetcher from './fetcher' + +export const bigcommerceProvider = { + locale: 'en-us', + cartCookie: 'bc_cartId', + fetcher, + cart: { useCart }, + wishlist: { useWishlist }, + customer: { useCustomer }, + products: { useSearch }, +} + +export type BigcommerceProvider = typeof bigcommerceProvider From f698dea69808860b22036eef1aa95ae1b3637d2d Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Tue, 16 Feb 2021 10:15:18 +0200 Subject: [PATCH 129/221] Moved handler to each hook --- framework/bigcommerce/product/get-product.ts | 2 +- framework/shopify/cart/use-add-item.tsx | 7 +- framework/shopify/cart/use-cart.tsx | 42 ++++- .../shopify/cart/utils/checkout-to-cart.ts | 2 +- framework/shopify/cart/utils/fetcher.ts | 30 ++++ framework/shopify/cart/utils/index.ts | 1 + framework/shopify/customer/use-customer.tsx | 21 +++ framework/shopify/fetcher.ts | 18 ++ framework/shopify/product/get-all-products.ts | 2 +- framework/shopify/product/get-product.ts | 2 +- framework/shopify/product/use-search.tsx | 51 ++++++ framework/shopify/provider.ts | 18 ++ framework/shopify/provider.tsx | 161 ------------------ .../shopify/utils/get-search-variables.ts | 2 +- framework/shopify/utils/index.ts | 4 +- framework/shopify/utils/mutations/index.ts | 2 + framework/shopify/{lib => utils}/normalize.ts | 0 17 files changed, 195 insertions(+), 170 deletions(-) create mode 100644 framework/shopify/cart/utils/fetcher.ts create mode 100644 framework/shopify/fetcher.ts create mode 100644 framework/shopify/provider.ts delete mode 100644 framework/shopify/provider.tsx rename framework/shopify/{lib => utils}/normalize.ts (100%) diff --git a/framework/bigcommerce/product/get-product.ts b/framework/bigcommerce/product/get-product.ts index 7d77eb194..794d89bdf 100644 --- a/framework/bigcommerce/product/get-product.ts +++ b/framework/bigcommerce/product/get-product.ts @@ -2,7 +2,7 @@ import type { GetProductQuery, GetProductQueryVariables } from '../schema' import setProductLocaleMeta from '../api/utils/set-product-locale-meta' import { productInfoFragment } from '../api/fragments/product' import { BigcommerceConfig, getConfig } from '../api' -import { normalizeProduct } from '@framework/lib/normalize' +import { normalizeProduct } from '@framework/utils/normalize' import type { Product } from '@commerce/types' export const getProductQuery = /* GraphQL */ ` diff --git a/framework/shopify/cart/use-add-item.tsx b/framework/shopify/cart/use-add-item.tsx index b5b34adc3..162627057 100644 --- a/framework/shopify/cart/use-add-item.tsx +++ b/framework/shopify/cart/use-add-item.tsx @@ -1,13 +1,16 @@ import { useCallback } from 'react' import useCart from './use-cart' + import useCartAddItem, { AddItemInput as UseAddItemInput, } from '@commerce/cart/use-add-item' + import type { HookFetcher } from '@commerce/utils/types' import type { Cart } from '@commerce/types' -import checkoutLineItemAddMutation from '../utils/mutations/checkout-line-item-add' -import getCheckoutId from '@framework/utils/get-checkout-id' + +import { checkoutLineItemAddMutation, getCheckoutId } from '@framework/utils' import { checkoutToCart } from './utils' + import { AddCartItemBody, CartItemBody } from '@framework/types' import { MutationCheckoutLineItemsAddArgs } from '@framework/schema' diff --git a/framework/shopify/cart/use-cart.tsx b/framework/shopify/cart/use-cart.tsx index e5ab8cafb..f5749731f 100644 --- a/framework/shopify/cart/use-cart.tsx +++ b/framework/shopify/cart/use-cart.tsx @@ -1,4 +1,44 @@ -import useCommerceCart, { UseCart } from '@commerce/cart/use-cart' +import { useMemo } from 'react' import type { ShopifyProvider } from '..' +import useCommerceCart, { + FetchCartInput, + UseCart, +} from '@commerce/cart/use-cart' + +import { Cart } from '@commerce/types' +import { HookHandler } from '@commerce/utils/types' + +import fetcher from './utils/fetcher' +import getCheckoutQuery from '@framework/utils/queries/get-checkout-query' + export default useCommerceCart as UseCart<ShopifyProvider> + +export const handler: HookHandler< + Cart | null, + {}, + FetchCartInput, + { isEmpty?: boolean } +> = { + fetchOptions: { + query: getCheckoutQuery, + }, + fetcher, + useHook({ input, useData }) { + const response = useData({ + swrOptions: { revalidateOnFocus: false, ...input.swrOptions }, + }) + return useMemo( + () => + Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, + }, + }), + [response] + ) + }, +} diff --git a/framework/shopify/cart/utils/checkout-to-cart.ts b/framework/shopify/cart/utils/checkout-to-cart.ts index 4c64d23e6..a8e91fbe1 100644 --- a/framework/shopify/cart/utils/checkout-to-cart.ts +++ b/framework/shopify/cart/utils/checkout-to-cart.ts @@ -1,6 +1,6 @@ import { Cart } from '@commerce/types' import { CommerceError, ValidationError } from '@commerce/utils/errors' -import { normalizeCart } from '@framework/lib/normalize' +import { normalizeCart } from '@framework/utils/normalize' import { Checkout, Maybe, UserError } from '@framework/schema' const checkoutToCart = (checkoutResponse?: { diff --git a/framework/shopify/cart/utils/fetcher.ts b/framework/shopify/cart/utils/fetcher.ts new file mode 100644 index 000000000..6621b7fde --- /dev/null +++ b/framework/shopify/cart/utils/fetcher.ts @@ -0,0 +1,30 @@ +import { HookFetcherFn } from '@commerce/utils/types' +import { Cart } from '@commerce/types' +import { checkoutCreate, checkoutToCart } from '.' +import { FetchCartInput } from '@commerce/cart/use-cart' + +const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ + options, + input: { cartId }, + fetch, +}) => { + let checkout + + if (cartId) { + const data = await fetch({ + ...options, + variables: { + cartId, + }, + }) + checkout = data?.node + } + + if (checkout?.completedAt || !cartId) { + checkout = await checkoutCreate(fetch) + } + + return checkoutToCart({ checkout }) +} + +export default fetcher diff --git a/framework/shopify/cart/utils/index.ts b/framework/shopify/cart/utils/index.ts index 20d04955d..0f2b4a6ca 100644 --- a/framework/shopify/cart/utils/index.ts +++ b/framework/shopify/cart/utils/index.ts @@ -1,2 +1,3 @@ export { default as checkoutToCart } from './checkout-to-cart' export { default as checkoutCreate } from './checkout-create' +export { default as fetcher } from './fetcher' diff --git a/framework/shopify/customer/use-customer.tsx b/framework/shopify/customer/use-customer.tsx index 652188bbe..55151801b 100644 --- a/framework/shopify/customer/use-customer.tsx +++ b/framework/shopify/customer/use-customer.tsx @@ -1,4 +1,25 @@ import useCustomer, { UseCustomer } from '@commerce/customer/use-customer' +import { Customer } from '@commerce/types' +import { HookHandler } from '@commerce/utils/types' +import { getCustomerQuery } from '@framework/utils' import type { ShopifyProvider } from '..' export default useCustomer as UseCustomer<ShopifyProvider> + +export const handler: HookHandler<Customer | null> = { + fetchOptions: { + query: getCustomerQuery, + }, + async fetcher({ options, fetch }) { + const data = await fetch<any | null>(options) + return data?.customer ?? null + }, + useHook({ input, useData }) { + return useData({ + swrOptions: { + revalidateOnFocus: false, + ...input.swrOptions, + }, + }) + }, +} diff --git a/framework/shopify/fetcher.ts b/framework/shopify/fetcher.ts new file mode 100644 index 000000000..9c4fe9a9e --- /dev/null +++ b/framework/shopify/fetcher.ts @@ -0,0 +1,18 @@ +import { Fetcher } from '@commerce/utils/types' +import { API_TOKEN, API_URL } from './const' +import { handleFetchResponse } from './utils' + +const fetcher: Fetcher = async ({ method = 'POST', variables, query }) => { + return handleFetchResponse( + await fetch(API_URL, { + method, + body: JSON.stringify({ query, variables }), + headers: { + 'X-Shopify-Storefront-Access-Token': API_TOKEN!, + 'Content-Type': 'application/json', + }, + }) + ) +} + +export default fetcher diff --git a/framework/shopify/product/get-all-products.ts b/framework/shopify/product/get-all-products.ts index a7cc3043c..34480e90a 100644 --- a/framework/shopify/product/get-all-products.ts +++ b/framework/shopify/product/get-all-products.ts @@ -2,7 +2,7 @@ import { GraphQLFetcherResult } from '@commerce/api' import { getConfig, ShopifyConfig } from '../api' import { Product, ProductEdge } from '../schema' import { getAllProductsQuery } from '../utils/queries' -import { normalizeProduct } from '@framework/lib/normalize' +import { normalizeProduct } from '@framework/utils/normalize' export type ProductNode = Product diff --git a/framework/shopify/product/get-product.ts b/framework/shopify/product/get-product.ts index 84e74c611..191706123 100644 --- a/framework/shopify/product/get-product.ts +++ b/framework/shopify/product/get-product.ts @@ -3,7 +3,7 @@ import { GraphQLFetcherResult } from '@commerce/api' import { getConfig, ShopifyConfig } from '../api' import { Product } from '../schema' import getProductQuery from '../utils/queries/get-product-query' -import { normalizeProduct } from '@framework/lib/normalize' +import { normalizeProduct } from '@framework/utils/normalize' export type ProductNode = Product diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx index 4f83baa63..174466fdb 100644 --- a/framework/shopify/product/use-search.tsx +++ b/framework/shopify/product/use-search.tsx @@ -1,4 +1,55 @@ import useSearch, { UseSearch } from '@commerce/products/use-search' +import { SearchProductsData } from '@commerce/types' +import { HookHandler } from '@commerce/utils/types' +import { ProductEdge } from '@framework/schema' +import { + getAllProductsQuery, + getSearchVariables, + normalizeProduct, +} from '@framework/utils' import type { ShopifyProvider } from '..' export default useSearch as UseSearch<ShopifyProvider> + +export type SearchProductsInput = { + search?: string + categoryId?: number + brandId?: number + sort?: string +} + +export const handler: HookHandler< + SearchProductsData, + SearchProductsInput, + SearchProductsInput +> = { + fetchOptions: { + query: getAllProductsQuery, + }, + async fetcher({ input, options, fetch }) { + const resp = await fetch({ + query: options?.query, + method: options?.method, + variables: getSearchVariables(input), + }) + const edges = resp.products?.edges + return { + products: edges?.map(({ node: p }: ProductEdge) => normalizeProduct(p)), + found: !!edges?.length, + } + }, + useHook({ input, useData }) { + return useData({ + input: [ + ['search', input.search], + ['categoryId', input.categoryId], + ['brandId', input.brandId], + ['sort', input.sort], + ], + swrOptions: { + revalidateOnFocus: false, + ...input.swrOptions, + }, + }) + }, +} diff --git a/framework/shopify/provider.ts b/framework/shopify/provider.ts new file mode 100644 index 000000000..6da831e46 --- /dev/null +++ b/framework/shopify/provider.ts @@ -0,0 +1,18 @@ +import { SHOPIFY_CHECKOUT_ID_COOKIE, STORE_DOMAIN } from './const' + +import { handler as useCart } from '@framework/cart/use-cart' +import { handler as useSearch } from '@framework/product/use-search' +import { handler as useCustomer } from '@framework/customer/use-customer' +import fetcher from './fetcher' + +export const shopifyProvider = { + locale: 'en-us', + cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, + storeDomain: STORE_DOMAIN, + fetcher, + cart: { useCart }, + customer: { useCustomer }, + products: { useSearch }, +} + +export type ShopifyProvider = typeof shopifyProvider diff --git a/framework/shopify/provider.tsx b/framework/shopify/provider.tsx deleted file mode 100644 index 4a1f01525..000000000 --- a/framework/shopify/provider.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import { useMemo } from 'react' -import { Fetcher, HookFetcherFn, HookHandler } from '@commerce/utils/types' - -import { - API_TOKEN, - API_URL, - SHOPIFY_CHECKOUT_ID_COOKIE, - STORE_DOMAIN, -} from './const' - -import { Cart } from './types' -import { Customer } from '@commerce/types' -import { normalizeCart, normalizeProduct } from './lib/normalize' -import { FetchCartInput } from '@commerce/cart/use-cart' -import { checkoutCreate, checkoutToCart } from './cart/utils' - -import { - getAllProductsQuery, - getCustomerQuery, - getCheckoutQuery, - handleFetchResponse, - getSearchVariables, -} from './utils' - -import { ProductEdge } from './schema' -import { SearchProductsInput } from 'framework/bigcommerce/provider' -import { SearchProductsData } from 'framework/bigcommerce/api/catalog/products' - -const fetcher: Fetcher = async ({ method = 'POST', variables, query }) => { - return handleFetchResponse( - await fetch(API_URL, { - method, - body: JSON.stringify({ query, variables }), - headers: { - 'X-Shopify-Storefront-Access-Token': API_TOKEN!, - 'Content-Type': 'application/json', - }, - }) - ) -} - -export const cartFetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ - options, - input: { cartId }, - fetch, -}) => { - let checkout - - if (cartId) { - const data = await fetch({ - ...options, - variables: { - cartId, - }, - }) - checkout = data?.node - } - - if (checkout?.completedAt || !cartId) { - checkout = await checkoutCreate(fetch) - } - - return checkoutToCart({ checkout }) -} - -const useCart: HookHandler< - Cart | null, - {}, - FetchCartInput, - { isEmpty?: boolean } -> = { - fetchOptions: { - query: getCheckoutQuery, - }, - fetcher: cartFetcher, - useHook({ input, useData }) { - const response = useData({ - swrOptions: { revalidateOnFocus: false, ...input.swrOptions }, - }) - - return useMemo( - () => - Object.create(response, { - isEmpty: { - get() { - return (response.data?.lineItems.length ?? 0) <= 0 - }, - enumerable: true, - }, - }), - [response] - ) - }, -} - -const useSearch: HookHandler< - SearchProductsData, - SearchProductsInput, - SearchProductsInput -> = { - fetchOptions: { - query: getAllProductsQuery, - }, - async fetcher({ input, options, fetch }) { - const resp = await fetch({ - query: options?.query, - method: options?.method, - variables: getSearchVariables(input), - }) - const edges = resp.products?.edges - return { - products: edges?.map(({ node: p }: ProductEdge) => normalizeProduct(p)), - found: !!edges?.length, - } - }, - useHook({ input, useData }) { - return useData({ - input: [ - ['search', input.search], - ['categoryId', input.categoryId], - ['brandId', input.brandId], - ['sort', input.sort], - ], - swrOptions: { - revalidateOnFocus: false, - ...input.swrOptions, - }, - }) - }, -} - -const useCustomerHandler: HookHandler<Customer | null> = { - fetchOptions: { - query: getCustomerQuery, - }, - async fetcher({ options, fetch }) { - const data = await fetch<any | null>(options) - return data?.customer ?? null - }, - useHook({ input, useData }) { - return useData({ - swrOptions: { - revalidateOnFocus: false, - ...input.swrOptions, - }, - }) - }, -} - -export const shopifyProvider = { - locale: 'en-us', - cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, - storeDomain: STORE_DOMAIN, - fetcher, - cartNormalizer: normalizeCart, - cart: { useCart }, - customer: { useCustomer: useCustomerHandler }, - products: { useSearch }, -} - -export type ShopifyProvider = typeof shopifyProvider diff --git a/framework/shopify/utils/get-search-variables.ts b/framework/shopify/utils/get-search-variables.ts index 9bc91eca3..90d35ba50 100644 --- a/framework/shopify/utils/get-search-variables.ts +++ b/framework/shopify/utils/get-search-variables.ts @@ -1,5 +1,5 @@ -import { SearchProductsInput } from '@framework/product/use-search' import getSortVariables from './get-sort-variables' +import type { SearchProductsInput } from '@framework/product/use-search' export const getSearchVariables = ({ categoryId, diff --git a/framework/shopify/utils/index.ts b/framework/shopify/utils/index.ts index cecf06fd1..99aa9be68 100644 --- a/framework/shopify/utils/index.ts +++ b/framework/shopify/utils/index.ts @@ -3,7 +3,9 @@ export { default as getSearchVariables } from './get-search-variables' export { default as getSortVariables } from './get-sort-variables' export { default as getVendors } from './get-vendors' export { default as getCategories } from './get-categories' +export { default as getCheckoutId } from './get-checkout-id' -export * from './customer-token' export * from './queries' export * from './mutations' +export * from './normalize' +export * from './customer-token' diff --git a/framework/shopify/utils/mutations/index.ts b/framework/shopify/utils/mutations/index.ts index 5ccf5b1dd..c9c6ee100 100644 --- a/framework/shopify/utils/mutations/index.ts +++ b/framework/shopify/utils/mutations/index.ts @@ -1,8 +1,10 @@ export { default as createCustomerMutation } from './customer-create' export { default as checkoutCreateMutation } from './checkout-create' + export { default as checkoutLineItemAddMutation } from './checkout-line-item-add' export { default as checkoutLineItemUpdateMutation } from './checkout-create' export { default as checkoutLineItemRemoveMutation } from './checkout-line-item-remove' + export { default as customerCreateMutation } from './customer-create' export { default as customerAccessTokenCreateMutation } from './customer-access-token-create' export { default as customerAccessTokenDeleteMutation } from './customer-access-token-delete' diff --git a/framework/shopify/lib/normalize.ts b/framework/shopify/utils/normalize.ts similarity index 100% rename from framework/shopify/lib/normalize.ts rename to framework/shopify/utils/normalize.ts From c02d7fec62898917580bcf810d3205dcb82619ac Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Tue, 16 Feb 2021 21:14:11 -0500 Subject: [PATCH 130/221] Added initial version of useAddItem --- framework/bigcommerce/cart/use-add-item.tsx | 77 +++++++++------------ framework/bigcommerce/provider.ts | 3 +- framework/bigcommerce/types.ts | 27 ++++---- framework/commerce/cart/use-add-item.tsx | 70 +++++++++++++++++-- framework/commerce/index.tsx | 3 +- framework/commerce/types.ts | 34 ++++----- framework/commerce/utils/types.ts | 27 ++++---- 7 files changed, 145 insertions(+), 96 deletions(-) diff --git a/framework/bigcommerce/cart/use-add-item.tsx b/framework/bigcommerce/cart/use-add-item.tsx index c66ee462a..7aec2f9e0 100644 --- a/framework/bigcommerce/cart/use-add-item.tsx +++ b/framework/bigcommerce/cart/use-add-item.tsx @@ -1,9 +1,6 @@ -import { useCallback } from 'react' -import type { HookFetcher } from '@commerce/utils/types' +import type { MutationHandler } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' -import useCartAddItem, { - AddItemInput as UseAddItemInput, -} from '@commerce/cart/use-add-item' +import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item' import { normalizeCart } from '../lib/normalize' import type { AddCartItemBody, @@ -12,55 +9,45 @@ import type { CartItemBody, } from '../types' import useCart from './use-cart' +import { BigcommerceProvider } from '..' const defaultOpts = { url: '/api/bigcommerce/cart', method: 'POST', } -export type AddItemInput = UseAddItemInput<CartItemBody> +export default useAddItem as UseAddItem<BigcommerceProvider, CartItemBody> -export const fetcher: HookFetcher<Cart, AddCartItemBody> = async ( - options, - { item }, - fetch -) => { - if ( - item.quantity && - (!Number.isInteger(item.quantity) || item.quantity! < 1) - ) { - throw new CommerceError({ - message: 'The item quantity has to be a valid integer greater than 0', +export const handler: MutationHandler<Cart, {}, AddCartItemBody> = { + fetchOptions: { + url: '/api/bigcommerce/cart', + method: 'GET', + }, + async fetcher({ input: { item }, options, fetch }) { + if ( + item.quantity && + (!Number.isInteger(item.quantity) || item.quantity! < 1) + ) { + throw new CommerceError({ + message: 'The item quantity has to be a valid integer greater than 0', + }) + } + + const data = await fetch<BigcommerceCart, AddCartItemBody>({ + ...defaultOpts, + ...options, + body: { item }, }) - } - const data = await fetch<BigcommerceCart, AddCartItemBody>({ - ...defaultOpts, - ...options, - body: { item }, - }) - - return normalizeCart(data) -} - -export function extendHook(customFetcher: typeof fetcher) { - const useAddItem = () => { + return normalizeCart(data) + }, + useHook() { const { mutate } = useCart() - const fn = useCartAddItem(defaultOpts, customFetcher) - return useCallback( - async function addItem(input: AddItemInput) { - const data = await fn({ item: input }) - await mutate(data, false) - return data - }, - [fn, mutate] - ) - } - - useAddItem.extend = extendHook - - return useAddItem + return async function addItem({ input, fetch }) { + const data = await fetch({ input }) + await mutate(data, false) + return data + } + }, } - -export default extendHook(fetcher) diff --git a/framework/bigcommerce/provider.ts b/framework/bigcommerce/provider.ts index ee5630813..08192df37 100644 --- a/framework/bigcommerce/provider.ts +++ b/framework/bigcommerce/provider.ts @@ -1,4 +1,5 @@ import { handler as useCart } from './cart/use-cart' +import { handler as useAddItem } from './cart/use-add-item' import { handler as useWishlist } from './wishlist/use-wishlist' import { handler as useCustomer } from './customer/use-customer' import { handler as useSearch } from './product/use-search' @@ -8,7 +9,7 @@ export const bigcommerceProvider = { locale: 'en-us', cartCookie: 'bc_cartId', fetcher, - cart: { useCart }, + cart: { useCart, useAddItem }, wishlist: { useWishlist }, customer: { useCustomer }, products: { useSearch }, diff --git a/framework/bigcommerce/types.ts b/framework/bigcommerce/types.ts index 90afb425d..16d1ea07a 100644 --- a/framework/bigcommerce/types.ts +++ b/framework/bigcommerce/types.ts @@ -23,11 +23,11 @@ export type BigcommerceCart = { // TODO: add missing fields } -export interface Cart extends Core.Cart { +export type Cart = Core.Cart & { lineItems: LineItem[] } -export interface LineItem extends Core.LineItem {} +export type LineItem = Core.LineItem /** * Cart mutations @@ -38,25 +38,24 @@ export type OptionSelections = { option_value: number | string } -export interface CartItemBody extends Core.CartItemBody { +export type CartItemBody = Core.CartItemBody & { productId: string // The product id is always required for BC optionSelections?: OptionSelections } -export interface GetCartHandlerBody extends Core.GetCartHandlerBody {} +type X = Core.CartItemBody extends CartItemBody ? any : never +type Y = CartItemBody extends Core.CartItemBody ? any : never -export interface AddCartItemBody extends Core.AddCartItemBody<CartItemBody> {} +export type GetCartHandlerBody = Core.GetCartHandlerBody -export interface AddCartItemHandlerBody - extends Core.AddCartItemHandlerBody<CartItemBody> {} +export type AddCartItemBody = Core.AddCartItemBody<CartItemBody> -export interface UpdateCartItemBody - extends Core.UpdateCartItemBody<CartItemBody> {} +export type AddCartItemHandlerBody = Core.AddCartItemHandlerBody<CartItemBody> -export interface UpdateCartItemHandlerBody - extends Core.UpdateCartItemHandlerBody<CartItemBody> {} +export type UpdateCartItemBody = Core.UpdateCartItemBody<CartItemBody> -export interface RemoveCartItemBody extends Core.RemoveCartItemBody {} +export type UpdateCartItemHandlerBody = Core.UpdateCartItemHandlerBody<CartItemBody> -export interface RemoveCartItemHandlerBody - extends Core.RemoveCartItemHandlerBody {} +export type RemoveCartItemBody = Core.RemoveCartItemBody + +export type RemoveCartItemHandlerBody = Core.RemoveCartItemHandlerBody diff --git a/framework/commerce/cart/use-add-item.tsx b/framework/commerce/cart/use-add-item.tsx index 2f6422ab3..0a70ff30d 100644 --- a/framework/commerce/cart/use-add-item.tsx +++ b/framework/commerce/cart/use-add-item.tsx @@ -1,9 +1,69 @@ -import useAction from '../utils/use-action' -import type { CartItemBody } from '../types' +import { useCallback } from 'react' +import type { + Prop, + HookFetcherFn, + UseHookInput, + UseHookResponse, +} from '../utils/types' +import type { Cart, CartItemBody, AddCartItemBody } from '../types' +import { Provider, useCommerce } from '..' +import { BigcommerceProvider } from '@framework' + +export type UseAddItemHandler<P extends Provider> = Prop< + Prop<P, 'cart'>, + 'useAddItem' +> // Input expected by the action returned by the `useAddItem` hook -export type AddItemInput<T extends CartItemBody> = T +export type UseAddItemInput<P extends Provider> = UseHookInput< + UseAddItemHandler<P> +> -const useAddItem = useAction +export type UseAddItemResult<P extends Provider> = ReturnType< + UseHookResponse<UseAddItemHandler<P>> +> -export default useAddItem +export type UseAddItem<P extends Provider, Input> = Partial< + UseAddItemInput<P> +> extends UseAddItemInput<P> + ? (input?: UseAddItemInput<P>) => (input: Input) => UseAddItemResult<P> + : (input: UseAddItemInput<P>) => (input: Input) => UseAddItemResult<P> + +export const fetcher: HookFetcherFn< + Cart, + AddCartItemBody<CartItemBody> +> = async ({ options, input, fetch }) => { + return fetch({ ...options, body: input }) +} + +type X = UseAddItemResult<BigcommerceProvider> + +export default function useAddItem<P extends Provider, Input>( + input: UseAddItemInput<P> +) { + const { providerRef, fetcherRef } = useCommerce<P>() + + const provider = providerRef.current + const opts = provider.cart?.useAddItem + + const fetcherFn = opts?.fetcher ?? fetcher + const useHook = opts?.useHook ?? (() => () => {}) + const fetchFn = provider.fetcher ?? fetcherRef.current + const action = useHook({ input }) + + return useCallback( + function addItem(input: Input) { + return action({ + input, + fetch({ input }) { + return fetcherFn({ + input, + options: opts!.fetchOptions, + fetch: fetchFn, + }) + }, + }) + }, + [input, fetchFn, opts?.fetchOptions] + ) +} diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index d8d882f93..243fba2db 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -6,7 +6,7 @@ import { useMemo, useRef, } from 'react' -import { Fetcher, HookHandler } from './utils/types' +import { Fetcher, HookHandler, MutationHandler } from './utils/types' import type { FetchCartInput } from './cart/use-cart' import type { Cart, Wishlist, Customer, SearchProductsData } from './types' @@ -16,6 +16,7 @@ export type Provider = CommerceConfig & { fetcher: Fetcher cart?: { useCart?: HookHandler<Cart | null, any, FetchCartInput> + useAddItem?: MutationHandler<Cart, any, any> } wishlist?: { useWishlist?: HookHandler<Wishlist | null, any, any> diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index 1f8390535..bf635c9dc 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -2,12 +2,12 @@ import type { Wishlist as BCWishlist } from '@framework/api/wishlist' import type { Customer as BCCustomer } from '@framework/api/customers' import type { SearchProductsData as BCSearchProductsData } from '@framework/api/catalog/products' -export interface Discount { +export type Discount = { // The value of the discount, can be an amount or percentage value: number } -export interface LineItem { +export type LineItem = { id: string variantId: string productId: string @@ -19,19 +19,19 @@ export interface LineItem { variant: ProductVariant } -export interface Measurement { +export type Measurement = { value: number unit: 'KILOGRAMS' | 'GRAMS' | 'POUNDS' | 'OUNCES' } -export interface Image { +export type Image = { url: string altText?: string width?: number height?: number } -export interface ProductVariant { +export type ProductVariant = { id: string // The SKU (stock keeping unit) associated with the product variant. sku: string @@ -66,7 +66,7 @@ export interface ProductVariant { } // Shopping cart, a.k.a Checkout -export interface Cart { +export type Cart = { id: string // ID of the customer to which the cart belongs. customerId?: string @@ -105,47 +105,49 @@ export interface SearchProductsData extends BCSearchProductsData {} */ // Base cart item body used for cart mutations -export interface CartItemBody { +export type CartItemBody = { variantId: string productId?: string quantity?: number } // Body used by the `getCart` operation handler -export interface GetCartHandlerBody { +export type GetCartHandlerBody = { cartId?: string } // Body used by the add item to cart operation -export interface AddCartItemBody<T extends CartItemBody> { +export type AddCartItemBody<T extends CartItemBody> = { item: T } // Body expected by the add item to cart operation handler -export interface AddCartItemHandlerBody<T extends CartItemBody> - extends Partial<AddCartItemBody<T>> { +export type AddCartItemHandlerBody<T extends CartItemBody> = Partial< + AddCartItemBody<T> +> & { cartId?: string } // Body used by the update cart item operation -export interface UpdateCartItemBody<T extends CartItemBody> { +export type UpdateCartItemBody<T extends CartItemBody> = { itemId: string item: T } // Body expected by the update cart item operation handler -export interface UpdateCartItemHandlerBody<T extends CartItemBody> - extends Partial<UpdateCartItemBody<T>> { +export type UpdateCartItemHandlerBody<T extends CartItemBody> = Partial< + UpdateCartItemBody<T> +> & { cartId?: string } // Body used by the remove cart item operation -export interface RemoveCartItemBody { +export type RemoveCartItemBody = { itemId: string } // Body expected by the remove cart item operation handler -export interface RemoveCartItemHandlerBody extends Partial<RemoveCartItemBody> { +export type RemoveCartItemHandlerBody = Partial<RemoveCartItemBody> & { cartId?: string } diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index a06aa0477..1d3adef81 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -82,19 +82,14 @@ export type MutationHandler< // Input expected by the hook Input extends { [k: string]: unknown } = {}, // Input expected before doing a fetch operation - FetchInput extends HookFetchInput = {}, - // Custom state added to the response object of SWR - State = {} + FetchInput extends { [k: string]: unknown } = {} > = { useHook?(context: { - input: Input & { swrOptions?: SwrOptions<Data, FetchInput> } - useCallback( - fn: (context?: { - input?: HookFetchInput | HookSwrInput - swrOptions?: SwrOptions<Data, FetchInput> - }) => Data - ): ResponseState<Data> - }): ResponseState<Data> & State + input: Input + }): (context: { + input: FetchInput + fetch: (context: { input: FetchInput }) => Data | Promise<Data> + }) => Data | Promise<Data> fetchOptions: HookFetcherOptions fetcher?: HookFetcherFn<Data, FetchInput> } @@ -110,14 +105,18 @@ export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< */ export type Prop<T, K extends keyof T> = NonNullable<T[K]> -export type UseHookParameters<H extends HookHandler<any>> = Parameters< +export type HookHandlerType = + | HookHandler<any, any, any> + | MutationHandler<any, any, any> + +export type UseHookParameters<H extends HookHandlerType> = Parameters< Prop<H, 'useHook'> > -export type UseHookResponse<H extends HookHandler<any>> = ReturnType< +export type UseHookResponse<H extends HookHandlerType> = ReturnType< Prop<H, 'useHook'> > export type UseHookInput< - H extends HookHandler<any> + H extends HookHandlerType > = UseHookParameters<H>[0]['input'] From 74da22d5f46da286c0c035373fccde7933e5cbf4 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 18 Feb 2021 01:10:38 -0500 Subject: [PATCH 131/221] Added better mutation types, and moved some hooks --- components/cart/CartItem/CartItem.tsx | 2 +- framework/bigcommerce/cart/use-add-item.tsx | 35 +++--- .../bigcommerce/cart/use-update-item.tsx | 102 +++++++++--------- framework/bigcommerce/provider.ts | 3 +- framework/bigcommerce/types.ts | 3 - framework/commerce/cart/use-add-item.tsx | 69 +++--------- framework/commerce/cart/use-cart-actions.tsx | 6 +- framework/commerce/cart/use-update-item.tsx | 34 +++++- framework/commerce/index.tsx | 11 +- framework/commerce/utils/types.ts | 49 +++++++-- framework/commerce/utils/use-hook.ts | 43 ++++++++ 11 files changed, 207 insertions(+), 150 deletions(-) create mode 100644 framework/commerce/utils/use-hook.ts diff --git a/components/cart/CartItem/CartItem.tsx b/components/cart/CartItem/CartItem.tsx index bb57c3f25..cb7f8600e 100644 --- a/components/cart/CartItem/CartItem.tsx +++ b/components/cart/CartItem/CartItem.tsx @@ -33,7 +33,7 @@ const CartItem = ({ currencyCode, }) - const updateItem = useUpdateItem(item) + const updateItem = useUpdateItem({ item }) const removeItem = useRemoveItem() const [quantity, setQuantity] = useState(item.quantity) const [removing, setRemoving] = useState(false) diff --git a/framework/bigcommerce/cart/use-add-item.tsx b/framework/bigcommerce/cart/use-add-item.tsx index 7aec2f9e0..d74c23567 100644 --- a/framework/bigcommerce/cart/use-add-item.tsx +++ b/framework/bigcommerce/cart/use-add-item.tsx @@ -1,29 +1,24 @@ -import type { MutationHandler } from '@commerce/utils/types' +import { useCallback } from 'react' +import type { MutationHook } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item' import { normalizeCart } from '../lib/normalize' import type { - AddCartItemBody, Cart, BigcommerceCart, CartItemBody, + AddCartItemBody, } from '../types' import useCart from './use-cart' -import { BigcommerceProvider } from '..' -const defaultOpts = { - url: '/api/bigcommerce/cart', - method: 'POST', -} +export default useAddItem as UseAddItem<typeof handler> -export default useAddItem as UseAddItem<BigcommerceProvider, CartItemBody> - -export const handler: MutationHandler<Cart, {}, AddCartItemBody> = { +export const handler: MutationHook<Cart, {}, CartItemBody> = { fetchOptions: { url: '/api/bigcommerce/cart', - method: 'GET', + method: 'POST', }, - async fetcher({ input: { item }, options, fetch }) { + async fetcher({ input: item, options, fetch }) { if ( item.quantity && (!Number.isInteger(item.quantity) || item.quantity! < 1) @@ -34,20 +29,22 @@ export const handler: MutationHandler<Cart, {}, AddCartItemBody> = { } const data = await fetch<BigcommerceCart, AddCartItemBody>({ - ...defaultOpts, ...options, body: { item }, }) return normalizeCart(data) }, - useHook() { + useHook: ({ fetch }) => () => { const { mutate } = useCart() - return async function addItem({ input, fetch }) { - const data = await fetch({ input }) - await mutate(data, false) - return data - } + return useCallback( + async function addItem(input) { + const data = await fetch({ input }) + await mutate(data, false) + return data + }, + [fetch, mutate] + ) }, } diff --git a/framework/bigcommerce/cart/use-update-item.tsx b/framework/bigcommerce/cart/use-update-item.tsx index d1870c818..f5a09006a 100644 --- a/framework/bigcommerce/cart/use-update-item.tsx +++ b/framework/bigcommerce/cart/use-update-item.tsx @@ -1,9 +1,10 @@ import { useCallback } from 'react' import debounce from 'lodash.debounce' -import type { HookFetcher } from '@commerce/utils/types' +import type { HookContext, HookFetcherContext } from '@commerce/utils/types' import { ValidationError } from '@commerce/utils/errors' -import useCartUpdateItem, { - UpdateItemInput as UseUpdateItemInput, +import useUpdateItem, { + UpdateItemInput as UpdateItemInputBase, + UseUpdateItem, } from '@commerce/cart/use-update-item' import { normalizeCart } from '../lib/normalize' import type { @@ -15,49 +16,50 @@ import type { import { fetcher as removeFetcher } from './use-remove-item' import useCart from './use-cart' -const defaultOpts = { - url: '/api/bigcommerce/cart', - method: 'PUT', -} - export type UpdateItemInput<T = any> = T extends LineItem - ? Partial<UseUpdateItemInput<LineItem>> - : UseUpdateItemInput<LineItem> + ? Partial<UpdateItemInputBase<LineItem>> + : UpdateItemInputBase<LineItem> -export const fetcher: HookFetcher<Cart | null, UpdateCartItemBody> = async ( - options, - { itemId, item }, - fetch -) => { - if (Number.isInteger(item.quantity)) { - // Also allow the update hook to remove an item if the quantity is lower than 1 - if (item.quantity! < 1) { - return removeFetcher(null, { itemId }, fetch) +export default useUpdateItem as UseUpdateItem<typeof handler> + +export const handler = { + fetchOptions: { + url: '/api/bigcommerce/cart', + method: 'PUT', + }, + async fetcher({ + input: { itemId, item }, + options, + fetch, + }: HookFetcherContext<UpdateCartItemBody>) { + if (Number.isInteger(item.quantity)) { + // Also allow the update hook to remove an item if the quantity is lower than 1 + if (item.quantity! < 1) { + return removeFetcher(null, { itemId }, fetch) + } + } else if (item.quantity) { + throw new ValidationError({ + message: 'The item quantity has to be a valid integer', + }) } - } else if (item.quantity) { - throw new ValidationError({ - message: 'The item quantity has to be a valid integer', + + const data = await fetch<BigcommerceCart, UpdateCartItemBody>({ + ...options, + body: { itemId, item }, }) - } - const data = await fetch<BigcommerceCart, UpdateCartItemBody>({ - ...defaultOpts, - ...options, - body: { itemId, item }, - }) - - return normalizeCart(data) -} - -function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) { - const useUpdateItem = <T extends LineItem | undefined = undefined>( - item?: T + return normalizeCart(data) + }, + useHook: ({ fetch }: HookContext<Cart | null, UpdateCartItemBody>) => < + T extends LineItem | undefined = undefined + >( + ctx: { + item?: T + wait?: number + } = {} ) => { - const { mutate } = useCart() - const fn = useCartUpdateItem<Cart | null, UpdateCartItemBody>( - defaultOpts, - customFetcher - ) + const { item } = ctx + const { mutate } = useCart() as any return useCallback( debounce(async (input: UpdateItemInput<T>) => { @@ -71,20 +73,16 @@ function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) { }) } - const data = await fn({ - itemId, - item: { productId, variantId, quantity: input.quantity }, + const data = await fetch({ + input: { + itemId, + item: { productId, variantId, quantity: input.quantity }, + }, }) await mutate(data, false) return data - }, cfg?.wait ?? 500), - [fn, mutate] + }, ctx.wait ?? 500), + [fetch, mutate] ) - } - - useUpdateItem.extend = extendHook - - return useUpdateItem + }, } - -export default extendHook(fetcher) diff --git a/framework/bigcommerce/provider.ts b/framework/bigcommerce/provider.ts index 08192df37..434c1c39e 100644 --- a/framework/bigcommerce/provider.ts +++ b/framework/bigcommerce/provider.ts @@ -1,5 +1,6 @@ import { handler as useCart } from './cart/use-cart' import { handler as useAddItem } from './cart/use-add-item' +import { handler as useUpdateItem } from './cart/use-update-item' import { handler as useWishlist } from './wishlist/use-wishlist' import { handler as useCustomer } from './customer/use-customer' import { handler as useSearch } from './product/use-search' @@ -9,7 +10,7 @@ export const bigcommerceProvider = { locale: 'en-us', cartCookie: 'bc_cartId', fetcher, - cart: { useCart, useAddItem }, + cart: { useCart, useAddItem, useUpdateItem }, wishlist: { useWishlist }, customer: { useCustomer }, products: { useSearch }, diff --git a/framework/bigcommerce/types.ts b/framework/bigcommerce/types.ts index 16d1ea07a..beeab0223 100644 --- a/framework/bigcommerce/types.ts +++ b/framework/bigcommerce/types.ts @@ -43,9 +43,6 @@ export type CartItemBody = Core.CartItemBody & { optionSelections?: OptionSelections } -type X = Core.CartItemBody extends CartItemBody ? any : never -type Y = CartItemBody extends Core.CartItemBody ? any : never - export type GetCartHandlerBody = Core.GetCartHandlerBody export type AddCartItemBody = Core.AddCartItemBody<CartItemBody> diff --git a/framework/commerce/cart/use-add-item.tsx b/framework/commerce/cart/use-add-item.tsx index 0a70ff30d..715029d18 100644 --- a/framework/commerce/cart/use-add-item.tsx +++ b/framework/commerce/cart/use-add-item.tsx @@ -1,33 +1,7 @@ -import { useCallback } from 'react' -import type { - Prop, - HookFetcherFn, - UseHookInput, - UseHookResponse, -} from '../utils/types' +import useHook, { useHookHandler } from '../utils/use-hook' +import type { MutationHook, HookFetcherFn } from '../utils/types' import type { Cart, CartItemBody, AddCartItemBody } from '../types' -import { Provider, useCommerce } from '..' -import { BigcommerceProvider } from '@framework' - -export type UseAddItemHandler<P extends Provider> = Prop< - Prop<P, 'cart'>, - 'useAddItem' -> - -// Input expected by the action returned by the `useAddItem` hook -export type UseAddItemInput<P extends Provider> = UseHookInput< - UseAddItemHandler<P> -> - -export type UseAddItemResult<P extends Provider> = ReturnType< - UseHookResponse<UseAddItemHandler<P>> -> - -export type UseAddItem<P extends Provider, Input> = Partial< - UseAddItemInput<P> -> extends UseAddItemInput<P> - ? (input?: UseAddItemInput<P>) => (input: Input) => UseAddItemResult<P> - : (input: UseAddItemInput<P>) => (input: Input) => UseAddItemResult<P> +import type { Provider } from '..' export const fetcher: HookFetcherFn< Cart, @@ -36,34 +10,15 @@ export const fetcher: HookFetcherFn< return fetch({ ...options, body: input }) } -type X = UseAddItemResult<BigcommerceProvider> +export type UseAddItem< + H extends MutationHook<any, any, any> = MutationHook<Cart, {}, CartItemBody> +> = ReturnType<H['useHook']> -export default function useAddItem<P extends Provider, Input>( - input: UseAddItemInput<P> -) { - const { providerRef, fetcherRef } = useCommerce<P>() +const fn = (provider: Provider) => provider.cart?.useAddItem! - const provider = providerRef.current - const opts = provider.cart?.useAddItem - - const fetcherFn = opts?.fetcher ?? fetcher - const useHook = opts?.useHook ?? (() => () => {}) - const fetchFn = provider.fetcher ?? fetcherRef.current - const action = useHook({ input }) - - return useCallback( - function addItem(input: Input) { - return action({ - input, - fetch({ input }) { - return fetcherFn({ - input, - options: opts!.fetchOptions, - fetch: fetchFn, - }) - }, - }) - }, - [input, fetchFn, opts?.fetchOptions] - ) +const useAddItem: UseAddItem = (...args) => { + const handler = useHookHandler(fn, fetcher) + return handler(useHook(fn, fetcher))(...args) } + +export default useAddItem diff --git a/framework/commerce/cart/use-cart-actions.tsx b/framework/commerce/cart/use-cart-actions.tsx index 3ba4b2e1a..5d081f0a8 100644 --- a/framework/commerce/cart/use-cart-actions.tsx +++ b/framework/commerce/cart/use-cart-actions.tsx @@ -1,5 +1,5 @@ import type { HookFetcher, HookFetcherOptions } from '../utils/types' -import useAddItem from './use-add-item' +// import useAddItem from './use-add-item' import useRemoveItem from './use-remove-item' import useUpdateItem from './use-update-item' @@ -9,9 +9,9 @@ export default function useCartActions<T, Input>( options: HookFetcherOptions, fetcher: HookFetcher<T, Input> ) { - const addItem = useAddItem<T, Input>(options, fetcher) + // const addItem = useAddItem<T, Input>(options, fetcher) const updateItem = useUpdateItem<T, Input>(options, fetcher) const removeItem = useRemoveItem<T, Input>(options, fetcher) - return { addItem, updateItem, removeItem } + return { updateItem, removeItem } } diff --git a/framework/commerce/cart/use-update-item.tsx b/framework/commerce/cart/use-update-item.tsx index e1adcb5fb..cc904a14a 100644 --- a/framework/commerce/cart/use-update-item.tsx +++ b/framework/commerce/cart/use-update-item.tsx @@ -1,11 +1,39 @@ -import useAction from '../utils/use-action' -import type { CartItemBody } from '../types' +import useHook, { useHookHandler } from '../utils/use-hook' +import type { MutationHook, HookFetcherFn } from '../utils/types' +import type { Cart, CartItemBody, LineItem, UpdateCartItemBody } from '../types' +import type { Provider } from '..' +import debounce from 'lodash.debounce' // Input expected by the action returned by the `useUpdateItem` hook export type UpdateItemInput<T extends CartItemBody> = T & { id: string } -const useUpdateItem = useAction +export type UseUpdateItem< + H extends MutationHook<any, any, any> = MutationHook< + Cart, + { + item?: LineItem + wait?: number + }, + UpdateItemInput<CartItemBody>, + UpdateCartItemBody<CartItemBody> + > +> = ReturnType<H['useHook']> + +export const fetcher: HookFetcherFn<any> = async ({ + options, + input, + fetch, +}) => { + return fetch({ ...options, body: input }) +} + +const fn = (provider: Provider) => provider.cart?.useUpdateItem! + +const useUpdateItem: UseUpdateItem = (input = {}) => { + const handler = useHookHandler(fn, fetcher) + return debounce(handler(useHook(fn, fetcher))(input), input.wait ?? 500) +} export default useUpdateItem diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index 243fba2db..935e8610f 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -6,7 +6,12 @@ import { useMemo, useRef, } from 'react' -import { Fetcher, HookHandler, MutationHandler } from './utils/types' +import { + Fetcher, + HookHandler, + MutationHandler, + MutationHook, +} from './utils/types' import type { FetchCartInput } from './cart/use-cart' import type { Cart, Wishlist, Customer, SearchProductsData } from './types' @@ -16,7 +21,9 @@ export type Provider = CommerceConfig & { fetcher: Fetcher cart?: { useCart?: HookHandler<Cart | null, any, FetchCartInput> - useAddItem?: MutationHandler<Cart, any, any> + useAddItem?: MutationHook<any, any, any> + useUpdateItem?: MutationHook<any, any, any> + useRemoveItem?: MutationHook<any, any, any> } wishlist?: { useWishlist?: HookHandler<Wishlist | null, any, any> diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 1d3adef81..120d66366 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -1,3 +1,4 @@ +import { LineItem } from '@framework/types' import type { ConfigInterface } from 'swr' import type { CommerceError } from './errors' import type { ResponseState } from './use-data' @@ -31,16 +32,15 @@ export type HookFetcher<Data, Input = null, Result = any> = ( fetch: <T = Result, Body = any>(options: FetcherOptions<Body>) => Promise<T> ) => Data | Promise<Data> -export type HookFetcherFn< - Data, - Input = never, - Result = any, - Body = any -> = (context: { +export type HookFetcherFn<Data, Input = never, Result = any, Body = any> = ( + context: HookFetcherContext<Input, Result, Body> +) => Data | Promise<Data> + +export type HookFetcherContext<Input = never, Result = any, Body = any> = { options: HookFetcherOptions input: Input fetch: <T = Result, B = Body>(options: FetcherOptions<B>) => Promise<T> -}) => Data | Promise<Data> +} export type HookFetcherOptions = { method?: string } & ( | { query: string; url?: string } @@ -53,8 +53,6 @@ export type HookSwrInput = [string, HookInputValue][] export type HookFetchInput = { [k: string]: HookInputValue } -export type HookInput = {} - export type HookHandler< // Data obj returned by the hook and fetch operation Data, @@ -94,6 +92,39 @@ export type MutationHandler< fetcher?: HookFetcherFn<Data, FetchInput> } +export type HookFunction< + Input extends { [k: string]: unknown } | {}, + T +> = keyof Input extends never + ? () => T + : Partial<Input> extends Input + ? (input?: Input) => T + : (input: Input) => T + +export type MutationHook< + // Data obj returned by the hook and fetch operation + Data, + // Input expected by the hook + Input extends { [k: string]: unknown } = {}, + // Input expected by the action returned by the hook + ActionInput extends { [k: string]: unknown } = {}, + // Input expected before doing a fetch operation + FetchInput extends { [k: string]: unknown } = ActionInput +> = { + useHook( + context: HookContext<Data, FetchInput> + ): HookFunction<Input, HookFunction<ActionInput, Data | Promise<Data>>> + fetchOptions: HookFetcherOptions + fetcher?: HookFetcherFn<Data, FetchInput> +} + +export type HookContext< + Data, + FetchInput extends { [k: string]: unknown } = {} +> = { + fetch: (context: { input: FetchInput }) => Data | Promise<Data> +} + export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< Data, CommerceError, diff --git a/framework/commerce/utils/use-hook.ts b/framework/commerce/utils/use-hook.ts new file mode 100644 index 000000000..b37c33370 --- /dev/null +++ b/framework/commerce/utils/use-hook.ts @@ -0,0 +1,43 @@ +import { useCallback } from 'react' +import type { MutationHook } from './types' +import { Provider, useCommerce } from '..' + +export function useHookHandler<P extends Provider>( + fn: (provider: P) => MutationHook<any, any, any>, + fetcher: any +) { + const { providerRef } = useCommerce<P>() + const provider = providerRef.current + const opts = fn(provider) + const handler = + opts.useHook ?? + (() => { + const { fetch } = useHook(fn, fetcher) + return (input: any) => fetch({ input }) + }) + + return handler +} + +export default function useHook<P extends Provider>( + fn: (provider: P) => MutationHook<any, any, any>, + fetcher: any +) { + const { providerRef, fetcherRef } = useCommerce<P>() + const provider = providerRef.current + const opts = fn(provider) + const fetcherFn = opts.fetcher ?? fetcher + const fetchFn = provider.fetcher ?? fetcherRef.current + const fetch = useCallback( + ({ input }: { input: any }) => { + return fetcherFn({ + input, + options: opts.fetchOptions, + fetch: fetchFn, + }) + }, + [fetchFn, opts.fetchOptions] + ) + + return { fetch } +} From ff5b1e941416d77798cccac757afe12a246fe771 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 18 Feb 2021 01:55:32 -0500 Subject: [PATCH 132/221] Removed use-cart-actions --- framework/bigcommerce/cart/index.ts | 3 +-- framework/bigcommerce/cart/use-cart-actions.tsx | 13 ------------- framework/commerce/cart/use-cart-actions.tsx | 17 ----------------- 3 files changed, 1 insertion(+), 32 deletions(-) delete mode 100644 framework/bigcommerce/cart/use-cart-actions.tsx delete mode 100644 framework/commerce/cart/use-cart-actions.tsx diff --git a/framework/bigcommerce/cart/index.ts b/framework/bigcommerce/cart/index.ts index 43c6db2b7..3b8ba990e 100644 --- a/framework/bigcommerce/cart/index.ts +++ b/framework/bigcommerce/cart/index.ts @@ -1,5 +1,4 @@ export { default as useCart } from './use-cart' export { default as useAddItem } from './use-add-item' export { default as useRemoveItem } from './use-remove-item' -export { default as useWishlistActions } from './use-cart-actions' -export { default as useUpdateItem } from './use-cart-actions' +export { default as useUpdateItem } from './use-update-item' diff --git a/framework/bigcommerce/cart/use-cart-actions.tsx b/framework/bigcommerce/cart/use-cart-actions.tsx deleted file mode 100644 index abb4a998e..000000000 --- a/framework/bigcommerce/cart/use-cart-actions.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import useAddItem from './use-add-item' -import useRemoveItem from './use-remove-item' -import useUpdateItem from './use-update-item' - -// This hook is probably not going to be used, but it's here -// to show how a commerce should be structuring it -export default function useCartActions() { - const addItem = useAddItem() - const updateItem = useUpdateItem() - const removeItem = useRemoveItem() - - return { addItem, updateItem, removeItem } -} diff --git a/framework/commerce/cart/use-cart-actions.tsx b/framework/commerce/cart/use-cart-actions.tsx deleted file mode 100644 index 5d081f0a8..000000000 --- a/framework/commerce/cart/use-cart-actions.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { HookFetcher, HookFetcherOptions } from '../utils/types' -// import useAddItem from './use-add-item' -import useRemoveItem from './use-remove-item' -import useUpdateItem from './use-update-item' - -// This hook is probably not going to be used, but it's here -// to show how a commerce should be structuring it -export default function useCartActions<T, Input>( - options: HookFetcherOptions, - fetcher: HookFetcher<T, Input> -) { - // const addItem = useAddItem<T, Input>(options, fetcher) - const updateItem = useUpdateItem<T, Input>(options, fetcher) - const removeItem = useRemoveItem<T, Input>(options, fetcher) - - return { updateItem, removeItem } -} From 89cc3d060812786e3821b993291bd0108c8da1a2 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 18 Feb 2021 15:04:46 +0200 Subject: [PATCH 133/221] Added initial version of useAddItem --- framework/shopify/cart/use-add-item.tsx | 102 +++++++++--------- framework/shopify/cart/use-remove-item.tsx | 2 +- framework/shopify/cart/use-update-item.tsx | 2 +- .../shopify/cart/utils/checkout-to-cart.ts | 14 +-- framework/shopify/cart/utils/fetcher.ts | 8 +- framework/shopify/provider.ts | 9 +- framework/shopify/types.ts | 1 + framework/shopify/utils/normalize.ts | 2 +- 8 files changed, 65 insertions(+), 75 deletions(-) diff --git a/framework/shopify/cart/use-add-item.tsx b/framework/shopify/cart/use-add-item.tsx index 162627057..c17462cc0 100644 --- a/framework/shopify/cart/use-add-item.tsx +++ b/framework/shopify/cart/use-add-item.tsx @@ -1,66 +1,60 @@ -import { useCallback } from 'react' +import type { MutationHandler } from '@commerce/utils/types' +import { CommerceError } from '@commerce/utils/errors' +import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item' import useCart from './use-cart' - -import useCartAddItem, { - AddItemInput as UseAddItemInput, -} from '@commerce/cart/use-add-item' - -import type { HookFetcher } from '@commerce/utils/types' -import type { Cart } from '@commerce/types' - -import { checkoutLineItemAddMutation, getCheckoutId } from '@framework/utils' +import { ShopifyProvider } from '..' +import { AddCartItemBody, CartItemBody } from '@commerce/types' +import { Cart } from '@framework/types' +import { + checkoutLineItemAddMutation, + getCheckoutId, + getCheckoutQuery, +} from '@framework/utils' import { checkoutToCart } from './utils' -import { AddCartItemBody, CartItemBody } from '@framework/types' -import { MutationCheckoutLineItemsAddArgs } from '@framework/schema' - const defaultOpts = { query: checkoutLineItemAddMutation, } -export type AddItemInput = UseAddItemInput<CartItemBody> +export default useAddItem as UseAddItem<ShopifyProvider, CartItemBody> -export const fetcher: HookFetcher< - Cart, - MutationCheckoutLineItemsAddArgs -> = async (options, { checkoutId, lineItems }, fetch) => { - const data = await fetch<any, AddCartItemBody>({ - ...options, - variables: { - checkoutId, - lineItems, - }, - }) +export const handler: MutationHandler<Cart, {}, AddCartItemBody> = { + fetchOptions: { + query: checkoutLineItemAddMutation, + }, + async fetcher({ input, options, fetch }) { + const item = input.item ?? input + if ( + item.quantity && + (!Number.isInteger(item.quantity) || item.quantity! < 1) + ) { + throw new CommerceError({ + message: 'The item quantity has to be a valid integer greater than 0', + }) + } - return checkoutToCart(data?.checkoutLineItemsAdd) -} - -export function extendHook(customFetcher: typeof fetcher) { - const useAddItem = () => { - const { mutate, data: cart } = useCart() - const fn = useCartAddItem(defaultOpts, customFetcher) - - return useCallback( - async function addItem(input: AddItemInput) { - const data = await fn({ - lineItems: [ - { - variantId: input.variantId, - quantity: input.quantity ?? 1, - }, - ], - checkoutId: getCheckoutId(cart?.id)!, - }) - await mutate(data, false) - return data + const data = await fetch<any, any>({ + ...defaultOpts, + ...options, + variables: { + lineItems: [ + { + variantId: item.variantId, + quantity: item.quantity ?? 1, + }, + ], + checkoutId: getCheckoutId(), }, - [fn, mutate] - ) - } + }) - useAddItem.extend = extendHook - - return useAddItem + return checkoutToCart(data.checkoutLineItemsAdd) + }, + useHook() { + const { mutate } = useCart() + return async function addItem({ input, fetch }) { + const data = await fetch({ input }) + await mutate(data, false) + return data + } + }, } - -export default extendHook(fetcher) diff --git a/framework/shopify/cart/use-remove-item.tsx b/framework/shopify/cart/use-remove-item.tsx index 193adede6..5c2c2dbee 100644 --- a/framework/shopify/cart/use-remove-item.tsx +++ b/framework/shopify/cart/use-remove-item.tsx @@ -34,7 +34,7 @@ export const fetcher: HookFetcher<Cart | null, any> = async ( ...options, variables: { lineItemIds: [itemId], checkoutId }, }) - return checkoutToCart(data?.checkoutLineItemsRemove) + return checkoutToCart(data.checkoutLineItemsRemove) } export function extendHook(customFetcher: typeof fetcher) { diff --git a/framework/shopify/cart/use-update-item.tsx b/framework/shopify/cart/use-update-item.tsx index e29bf8b72..2b5e7c776 100644 --- a/framework/shopify/cart/use-update-item.tsx +++ b/framework/shopify/cart/use-update-item.tsx @@ -44,7 +44,7 @@ export const fetcher: HookFetcher<Cart | null, any> = async ( variables: { checkoutId, lineItems: [item] }, }) - return checkoutToCart(data?.checkoutLineItemsUpdate) + return checkoutToCart(data.checkoutLineItemsUpdate) } function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) { diff --git a/framework/shopify/cart/utils/checkout-to-cart.ts b/framework/shopify/cart/utils/checkout-to-cart.ts index a8e91fbe1..104240220 100644 --- a/framework/shopify/cart/utils/checkout-to-cart.ts +++ b/framework/shopify/cart/utils/checkout-to-cart.ts @@ -1,12 +1,12 @@ import { Cart } from '@commerce/types' -import { CommerceError, ValidationError } from '@commerce/utils/errors' +import { ValidationError } from '@commerce/utils/errors' import { normalizeCart } from '@framework/utils/normalize' -import { Checkout, Maybe, UserError } from '@framework/schema' +import { Checkout, UserError } from '@framework/schema' -const checkoutToCart = (checkoutResponse?: { +const checkoutToCart = (checkoutResponse: { checkout: Checkout userErrors?: UserError[] -}): Maybe<Cart> => { +}): Cart => { const checkout = checkoutResponse?.checkout const userErrors = checkoutResponse?.userErrors @@ -16,12 +16,6 @@ const checkoutToCart = (checkoutResponse?: { }) } - if (!checkout) { - throw new CommerceError({ - message: 'Missing checkout details from response cart Response', - }) - } - return normalizeCart(checkout) } diff --git a/framework/shopify/cart/utils/fetcher.ts b/framework/shopify/cart/utils/fetcher.ts index 6621b7fde..372860734 100644 --- a/framework/shopify/cart/utils/fetcher.ts +++ b/framework/shopify/cart/utils/fetcher.ts @@ -5,22 +5,22 @@ import { FetchCartInput } from '@commerce/cart/use-cart' const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ options, - input: { cartId }, + input: { cartId: checkoutId }, fetch, }) => { let checkout - if (cartId) { + if (checkoutId) { const data = await fetch({ ...options, variables: { - cartId, + checkoutId, }, }) checkout = data?.node } - if (checkout?.completedAt || !cartId) { + if (checkout?.completedAt || !checkoutId) { checkout = await checkoutCreate(fetch) } diff --git a/framework/shopify/provider.ts b/framework/shopify/provider.ts index 6da831e46..f11e9ff3f 100644 --- a/framework/shopify/provider.ts +++ b/framework/shopify/provider.ts @@ -1,8 +1,9 @@ import { SHOPIFY_CHECKOUT_ID_COOKIE, STORE_DOMAIN } from './const' -import { handler as useCart } from '@framework/cart/use-cart' -import { handler as useSearch } from '@framework/product/use-search' -import { handler as useCustomer } from '@framework/customer/use-customer' +import { handler as useCart } from './cart/use-cart' +import { handler as useAddItem } from './cart/use-add-item' +import { handler as useSearch } from './product/use-search' +import { handler as useCustomer } from './customer/use-customer' import fetcher from './fetcher' export const shopifyProvider = { @@ -10,7 +11,7 @@ export const shopifyProvider = { cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, storeDomain: STORE_DOMAIN, fetcher, - cart: { useCart }, + cart: { useCart, useAddItem }, customer: { useCustomer }, products: { useSearch }, } diff --git a/framework/shopify/types.ts b/framework/shopify/types.ts index 5d285d211..7f207b0c7 100644 --- a/framework/shopify/types.ts +++ b/framework/shopify/types.ts @@ -8,6 +8,7 @@ export type ShopifyCheckout = { } export interface Cart extends Core.Cart { + id: string lineItems: LineItem[] } diff --git a/framework/shopify/utils/normalize.ts b/framework/shopify/utils/normalize.ts index c39331cd9..edd295ddd 100644 --- a/framework/shopify/utils/normalize.ts +++ b/framework/shopify/utils/normalize.ts @@ -107,7 +107,7 @@ function normalizeLineItem({ variantId: String(variant?.id), productId: String(variant?.id), name: `${title} - ${variant?.title}`, - quantity: quantity, + quantity, variant: { id: String(variant?.id), sku: variant?.sku ?? '', From ce5fcb69750601d485051007d2494bedf0b34d0e Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 18 Feb 2021 15:49:44 +0200 Subject: [PATCH 134/221] Updated types --- framework/shopify/cart/use-add-item.tsx | 22 ++++---------- .../shopify/cart/utils/checkout-to-cart.ts | 30 +++++++++++++------ framework/shopify/types.ts | 23 +++++++------- framework/shopify/utils/normalize.ts | 18 +++++------ 4 files changed, 47 insertions(+), 46 deletions(-) diff --git a/framework/shopify/cart/use-add-item.tsx b/framework/shopify/cart/use-add-item.tsx index c17462cc0..6dc3b8d6a 100644 --- a/framework/shopify/cart/use-add-item.tsx +++ b/framework/shopify/cart/use-add-item.tsx @@ -3,18 +3,10 @@ import { CommerceError } from '@commerce/utils/errors' import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item' import useCart from './use-cart' import { ShopifyProvider } from '..' -import { AddCartItemBody, CartItemBody } from '@commerce/types' -import { Cart } from '@framework/types' -import { - checkoutLineItemAddMutation, - getCheckoutId, - getCheckoutQuery, -} from '@framework/utils' +import { Cart, AddCartItemBody, CartItemBody } from '../types' +import { checkoutLineItemAddMutation, getCheckoutId } from '../utils' import { checkoutToCart } from './utils' - -const defaultOpts = { - query: checkoutLineItemAddMutation, -} +import { Mutation } from '../schema' export default useAddItem as UseAddItem<ShopifyProvider, CartItemBody> @@ -22,8 +14,7 @@ export const handler: MutationHandler<Cart, {}, AddCartItemBody> = { fetchOptions: { query: checkoutLineItemAddMutation, }, - async fetcher({ input, options, fetch }) { - const item = input.item ?? input + async fetcher({ input: { item }, options, fetch }) { if ( item.quantity && (!Number.isInteger(item.quantity) || item.quantity! < 1) @@ -33,8 +24,7 @@ export const handler: MutationHandler<Cart, {}, AddCartItemBody> = { }) } - const data = await fetch<any, any>({ - ...defaultOpts, + const { checkoutLineItemsAdd }: Mutation = await fetch<any, any>({ ...options, variables: { lineItems: [ @@ -47,7 +37,7 @@ export const handler: MutationHandler<Cart, {}, AddCartItemBody> = { }, }) - return checkoutToCart(data.checkoutLineItemsAdd) + return checkoutToCart(checkoutLineItemsAdd) }, useHook() { const { mutate } = useCart() diff --git a/framework/shopify/cart/utils/checkout-to-cart.ts b/framework/shopify/cart/utils/checkout-to-cart.ts index 104240220..42d797652 100644 --- a/framework/shopify/cart/utils/checkout-to-cart.ts +++ b/framework/shopify/cart/utils/checkout-to-cart.ts @@ -1,14 +1,26 @@ import { Cart } from '@commerce/types' -import { ValidationError } from '@commerce/utils/errors' -import { normalizeCart } from '@framework/utils/normalize' -import { Checkout, UserError } from '@framework/schema' +import { CommerceError, ValidationError } from '@commerce/utils/errors' -const checkoutToCart = (checkoutResponse: { - checkout: Checkout - userErrors?: UserError[] -}): Cart => { - const checkout = checkoutResponse?.checkout - const userErrors = checkoutResponse?.userErrors +import { + CheckoutLineItemsAddPayload, + CheckoutLineItemsUpdatePayload, + Maybe, +} from '@framework/schema' +import { normalizeCart } from '@framework/utils' + +export type CheckoutPayload = + | CheckoutLineItemsAddPayload + | CheckoutLineItemsUpdatePayload + +const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => { + if (!checkoutPayload || !checkoutPayload?.checkout) { + throw new CommerceError({ + message: 'Invalid response from Shopify', + }) + } + + const checkout = checkoutPayload?.checkout + const userErrors = checkoutPayload?.userErrors if (userErrors && userErrors.length) { throw new ValidationError({ diff --git a/framework/shopify/types.ts b/framework/shopify/types.ts index 7f207b0c7..6ebfdf01d 100644 --- a/framework/shopify/types.ts +++ b/framework/shopify/types.ts @@ -23,25 +23,24 @@ export type OptionSelections = { option_value: number | string } -export interface CartItemBody extends Core.CartItemBody { +export type CartItemBody = Core.CartItemBody & { productId: string // The product id is always required for BC optionSelections?: OptionSelections } -export interface GetCartHandlerBody extends Core.GetCartHandlerBody {} +type X = Core.CartItemBody extends CartItemBody ? any : never +type Y = CartItemBody extends Core.CartItemBody ? any : never -export interface AddCartItemBody extends Core.AddCartItemBody<CartItemBody> {} +export type GetCartHandlerBody = Core.GetCartHandlerBody -export interface AddCartItemHandlerBody - extends Core.AddCartItemHandlerBody<CartItemBody> {} +export type AddCartItemBody = Core.AddCartItemBody<CartItemBody> -export interface UpdateCartItemBody - extends Core.UpdateCartItemBody<CartItemBody> {} +export type AddCartItemHandlerBody = Core.AddCartItemHandlerBody<CartItemBody> -export interface UpdateCartItemHandlerBody - extends Core.UpdateCartItemHandlerBody<CartItemBody> {} +export type UpdateCartItemBody = Core.UpdateCartItemBody<CartItemBody> -export interface RemoveCartItemBody extends Core.RemoveCartItemBody {} +export type UpdateCartItemHandlerBody = Core.UpdateCartItemHandlerBody<CartItemBody> -export interface RemoveCartItemHandlerBody - extends Core.RemoveCartItemHandlerBody {} +export type RemoveCartItemBody = Core.RemoveCartItemBody + +export type RemoveCartItemHandlerBody = Core.RemoveCartItemHandlerBody diff --git a/framework/shopify/utils/normalize.ts b/framework/shopify/utils/normalize.ts index edd295ddd..79074bf24 100644 --- a/framework/shopify/utils/normalize.ts +++ b/framework/shopify/utils/normalize.ts @@ -81,20 +81,20 @@ export function normalizeProduct(productNode: ShopifyProduct): any { } } -export function normalizeCart(data: Checkout): Cart { +export function normalizeCart(checkout: Checkout): Cart { return { - id: data.id, + id: checkout.id, customerId: '', email: '', - createdAt: data.createdAt, + createdAt: checkout.createdAt, currency: { - code: data.currencyCode, + code: checkout.currencyCode, }, - taxesIncluded: data.taxesIncluded, - lineItems: data.lineItems?.edges.map(normalizeLineItem), - lineItemsSubtotalPrice: data.subtotalPrice, - subtotalPrice: data.subtotalPrice, - totalPrice: data.totalPrice, + taxesIncluded: checkout.taxesIncluded, + lineItems: checkout.lineItems?.edges.map(normalizeLineItem), + lineItemsSubtotalPrice: checkout.subtotalPrice, + subtotalPrice: checkout.subtotalPrice, + totalPrice: checkout.totalPrice, discounts: [], } } From e1be0ac37dafc2d4dfd94b2cd5eeb6f12edee084 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 18 Feb 2021 15:52:18 +0200 Subject: [PATCH 135/221] Update use-add-item.tsx --- framework/shopify/cart/use-add-item.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/shopify/cart/use-add-item.tsx b/framework/shopify/cart/use-add-item.tsx index 6dc3b8d6a..11771f1b3 100644 --- a/framework/shopify/cart/use-add-item.tsx +++ b/framework/shopify/cart/use-add-item.tsx @@ -14,7 +14,9 @@ export const handler: MutationHandler<Cart, {}, AddCartItemBody> = { fetchOptions: { query: checkoutLineItemAddMutation, }, - async fetcher({ input: { item }, options, fetch }) { + async fetcher({ input, options, fetch }) { + const item = input?.item ?? input + if ( item.quantity && (!Number.isInteger(item.quantity) || item.quantity! < 1) From 7a300ad8a91c0eb5a7379b3714767faba1316d8a Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Thu, 18 Feb 2021 11:50:18 -0300 Subject: [PATCH 136/221] changes --- framework/bigcommerce/product/use-search.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/bigcommerce/product/use-search.tsx b/framework/bigcommerce/product/use-search.tsx index 393a8c0b9..0ff767d8a 100644 --- a/framework/bigcommerce/product/use-search.tsx +++ b/framework/bigcommerce/product/use-search.tsx @@ -1,5 +1,5 @@ import { HookHandler } from '@commerce/utils/types' -import useSearch, { UseSearch } from '@commerce/products/use-search' +import useSearch, { UseSearch } from '@commerce/product/use-search' import type { SearchProductsData } from '../api/catalog/products' import type { BigcommerceProvider } from '..' From d093c9cf9c6ec60c305c0e4c462dfcddc87ab350 Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Thu, 18 Feb 2021 12:05:25 -0300 Subject: [PATCH 137/221] Changes --- .../WishlistButton/WishlistButton.tsx | 2 +- framework/bigcommerce/product/use-search.tsx | 2 +- framework/commerce/products/use-search.tsx | 57 +++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 framework/commerce/products/use-search.tsx diff --git a/components/wishlist/WishlistButton/WishlistButton.tsx b/components/wishlist/WishlistButton/WishlistButton.tsx index 31775b566..0c4c20194 100644 --- a/components/wishlist/WishlistButton/WishlistButton.tsx +++ b/components/wishlist/WishlistButton/WishlistButton.tsx @@ -8,7 +8,7 @@ import useCustomer from '@framework/customer/use-customer' import useAddItem from '@framework/wishlist/use-add-item' import useRemoveItem from '@framework/wishlist/use-remove-item' import useWishlist from '@framework/wishlist/use-add-item' -import type { Product } from '@framework/types' + type Props = { productId: Product['id'] variant: ProductVariant diff --git a/framework/bigcommerce/product/use-search.tsx b/framework/bigcommerce/product/use-search.tsx index 0ff767d8a..393a8c0b9 100644 --- a/framework/bigcommerce/product/use-search.tsx +++ b/framework/bigcommerce/product/use-search.tsx @@ -1,5 +1,5 @@ import { HookHandler } from '@commerce/utils/types' -import useSearch, { UseSearch } from '@commerce/product/use-search' +import useSearch, { UseSearch } from '@commerce/products/use-search' import type { SearchProductsData } from '../api/catalog/products' import type { BigcommerceProvider } from '..' diff --git a/framework/commerce/products/use-search.tsx b/framework/commerce/products/use-search.tsx new file mode 100644 index 000000000..1f887f5fe --- /dev/null +++ b/framework/commerce/products/use-search.tsx @@ -0,0 +1,57 @@ +import type { SearchProductsData } from '../types' +import type { + Prop, + HookFetcherFn, + UseHookInput, + UseHookResponse, +} from '../utils/types' +import defaultFetcher from '../utils/default-fetcher' +import useData from '../utils/use-data' +import { Provider, useCommerce } from '..' +import { BigcommerceProvider } from '@framework' + +export type UseSearchHandler<P extends Provider> = Prop< + Prop<P, 'products'>, + 'useSearch' +> + +export type UseSeachInput<P extends Provider> = UseHookInput< + UseSearchHandler<P> +> + +export type SearchResponse<P extends Provider> = UseHookResponse< + UseSearchHandler<P> +> + +export type UseSearch<P extends Provider> = Partial< + UseSeachInput<P> +> extends UseSeachInput<P> + ? (input?: UseSeachInput<P>) => SearchResponse<P> + : (input: UseSeachInput<P>) => SearchResponse<P> + +export const fetcher = defaultFetcher as HookFetcherFn<SearchProductsData> + +export default function useSearch<P extends Provider>( + input: UseSeachInput<P> = {} +) { + const { providerRef, fetcherRef } = useCommerce<P>() + + const provider = providerRef.current + const opts = provider.products?.useSearch + + const fetcherFn = opts?.fetcher ?? fetcher + const useHook = opts?.useHook ?? ((ctx) => ctx.useData()) + + return useHook({ + input, + useData(ctx) { + const response = useData( + { ...opts!, fetcher: fetcherFn }, + ctx?.input ?? [], + provider.fetcher ?? fetcherRef.current, + ctx?.swrOptions ?? input.swrOptions + ) + return response + }, + }) +} From b7919ed0dee8b4a02fc3f92e704da1f027c67c5d Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Thu, 18 Feb 2021 12:11:45 -0300 Subject: [PATCH 138/221] Reordering and changes --- framework/bigcommerce/product/use-search.tsx | 2 +- .../commerce/cart/products/use-search.tsx | 57 ------------------- .../{products => product}/use-search.tsx | 0 3 files changed, 1 insertion(+), 58 deletions(-) delete mode 100644 framework/commerce/cart/products/use-search.tsx rename framework/commerce/{products => product}/use-search.tsx (100%) diff --git a/framework/bigcommerce/product/use-search.tsx b/framework/bigcommerce/product/use-search.tsx index 393a8c0b9..0ff767d8a 100644 --- a/framework/bigcommerce/product/use-search.tsx +++ b/framework/bigcommerce/product/use-search.tsx @@ -1,5 +1,5 @@ import { HookHandler } from '@commerce/utils/types' -import useSearch, { UseSearch } from '@commerce/products/use-search' +import useSearch, { UseSearch } from '@commerce/product/use-search' import type { SearchProductsData } from '../api/catalog/products' import type { BigcommerceProvider } from '..' diff --git a/framework/commerce/cart/products/use-search.tsx b/framework/commerce/cart/products/use-search.tsx deleted file mode 100644 index e43ca301a..000000000 --- a/framework/commerce/cart/products/use-search.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import type { SearchProductsData } from '../../types' -import type { - Prop, - HookFetcherFn, - UseHookInput, - UseHookResponse, -} from '../../utils/types' -import defaultFetcher from '../../utils/default-fetcher' -import useData from '../../utils/use-data' -import { Provider, useCommerce } from '../..' -import { BigcommerceProvider } from '@framework' - -export type UseSearchHandler<P extends Provider> = Prop< - Prop<P, 'products'>, - 'useSearch' -> - -export type UseSeachInput<P extends Provider> = UseHookInput< - UseSearchHandler<P> -> - -export type SearchResponse<P extends Provider> = UseHookResponse< - UseSearchHandler<P> -> - -export type UseSearch<P extends Provider> = Partial< - UseSeachInput<P> -> extends UseSeachInput<P> - ? (input?: UseSeachInput<P>) => SearchResponse<P> - : (input: UseSeachInput<P>) => SearchResponse<P> - -export const fetcher = defaultFetcher as HookFetcherFn<SearchProductsData> - -export default function useSearch<P extends Provider>( - input: UseSeachInput<P> = {} -) { - const { providerRef, fetcherRef } = useCommerce<P>() - - const provider = providerRef.current - const opts = provider.products?.useSearch - - const fetcherFn = opts?.fetcher ?? fetcher - const useHook = opts?.useHook ?? ((ctx) => ctx.useData()) - - return useHook({ - input, - useData(ctx) { - const response = useData( - { ...opts!, fetcher: fetcherFn }, - ctx?.input ?? [], - provider.fetcher ?? fetcherRef.current, - ctx?.swrOptions ?? input.swrOptions - ) - return response - }, - }) -} diff --git a/framework/commerce/products/use-search.tsx b/framework/commerce/product/use-search.tsx similarity index 100% rename from framework/commerce/products/use-search.tsx rename to framework/commerce/product/use-search.tsx From c620355448b4066f47cdc6a5e97670610e039de0 Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Thu, 18 Feb 2021 12:59:18 -0300 Subject: [PATCH 139/221] Adding Features APO --- .../product/ProductCard/ProductCard.tsx | 6 +-- .../product/ProductView/ProductView.tsx | 17 +++++---- framework/bigcommerce/config.json | 2 +- framework/commerce/types.ts | 4 ++ framework/commerce/utils/features.ts | 37 +++++++++++++++++++ package.json | 2 + pages/index.tsx | 10 +++++ pages/product/[slug].tsx | 17 +++++++-- yarn.lock | 12 ++++++ 9 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 framework/commerce/utils/features.ts diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index 46cd708fc..a9eaf8568 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -4,8 +4,6 @@ import Link from 'next/link' import type { Product } from '@commerce/types' import s from './ProductCard.module.css' import Image, { ImageProps } from 'next/image' -import frameworkConfig from '@framework/config.json' -const isWishlistEnabled = !!frameworkConfig.features.wishlist import WishlistButton from '@components/wishlist/WishlistButton' interface Props { @@ -13,6 +11,7 @@ interface Props { product: Product variant?: 'slim' | 'simple' imgProps?: Omit<ImageProps, 'src'> + wishlist?: boolean } const placeholderImg = '/product-img-placeholder.svg' @@ -22,6 +21,7 @@ const ProductCard: FC<Props> = ({ product, variant, imgProps, + wishlist = false, ...props }) => ( <Link href={`/product/${product.slug}`} {...props}> @@ -59,7 +59,7 @@ const ProductCard: FC<Props> = ({ {product.price.currencyCode} </span> </div> - {isWishlistEnabled && ( + {wishlist && ( <WishlistButton className={s.wishlistButton} productId={product.id} diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index 61beda7fe..c502303c4 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -13,15 +13,16 @@ import usePrice from '@framework/product/use-price' import { useAddItem } from '@framework/cart' import { getVariant, SelectedOptions } from '../helpers' -// import WishlistButton from '@components/wishlist/WishlistButton' +import WishlistButton from '@components/wishlist/WishlistButton' interface Props { className?: string children?: any product: Product + wishlist?: boolean } -const ProductView: FC<Props> = ({ product }) => { +const ProductView: FC<Props> = ({ product, wishlist = false }) => { const addItem = useAddItem() const { price } = usePrice({ amount: product.price.value, @@ -151,11 +152,13 @@ const ProductView: FC<Props> = ({ product }) => { </Button> </div> </div> - {/* <WishlistButton - className={s.wishlistButton} - productId={product.id} - variant={product.variants[0]!} - /> */} + {wishlist && ( + <WishlistButton + className={s.wishlistButton} + productId={product.id} + variant={product.variants[0]!} + /> + )} </div> </Container> ) diff --git a/framework/bigcommerce/config.json b/framework/bigcommerce/config.json index 17ef37e25..a0e7afc5d 100644 --- a/framework/bigcommerce/config.json +++ b/framework/bigcommerce/config.json @@ -1,5 +1,5 @@ { "features": { - "wishlist": false + "wishlist": true } } diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index bf635c9dc..0ae766095 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -2,6 +2,10 @@ import type { Wishlist as BCWishlist } from '@framework/api/wishlist' import type { Customer as BCCustomer } from '@framework/api/customers' import type { SearchProductsData as BCSearchProductsData } from '@framework/api/catalog/products' +export type CommerceProviderConfig = { + features: Record<string, boolean> +} + export type Discount = { // The value of the discount, can be an amount or percentage value: number diff --git a/framework/commerce/utils/features.ts b/framework/commerce/utils/features.ts new file mode 100644 index 000000000..d84321967 --- /dev/null +++ b/framework/commerce/utils/features.ts @@ -0,0 +1,37 @@ +import commerceProviderConfig from '@framework/config.json' +import type { CommerceProviderConfig } from '../types' +import memo from 'lodash.memoize' + +type FeaturesAPI = { + isEnabled: (desideredFeature: string) => boolean +} + +function isFeatureEnabled(config: CommerceProviderConfig) { + const features = config.features + return (desideredFeature: string) => + Object.keys(features) + .filter((k) => features[k]) + .includes(desideredFeature) +} + +function boostrap(): FeaturesAPI { + const basis = { + isEnabled: () => false, + } + + if (!commerceProviderConfig) { + console.log('No config.json found - Please add a config.json') + return basis + } + + if (commerceProviderConfig.features) { + return { + ...basis, + isEnabled: memo(isFeatureEnabled(commerceProviderConfig)), + } + } + + return basis +} + +export default boostrap() diff --git a/package.json b/package.json index 268b8d4a5..2d8e32772 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ }, "dependencies": { "@reach/portal": "^0.11.2", + "@types/lodash.memoize": "^4.1.6", "@vercel/fetch": "^6.1.0", "body-scroll-lock": "^3.1.5", "bowser": "^2.11.0", @@ -32,6 +33,7 @@ "js-cookie": "^2.2.1", "keen-slider": "^5.2.4", "lodash.debounce": "^4.0.8", + "lodash.memoize": "^4.1.2", "lodash.random": "^3.2.0", "lodash.throttle": "^4.1.1", "next": "^10.0.7-canary.3", diff --git a/pages/index.tsx b/pages/index.tsx index 3a84112e5..acb1474be 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -8,6 +8,7 @@ import { getConfig } from '@framework/api' import getAllProducts from '@framework/product/get-all-products' import getSiteInfo from '@framework/common/get-site-info' import getAllPages from '@framework/common/get-all-pages' +import Features from '@commerce/utils/features' export async function getStaticProps({ preview, @@ -23,6 +24,7 @@ export async function getStaticProps({ const { categories, brands } = await getSiteInfo({ config, preview }) const { pages } = await getAllPages({ config, preview }) + const isWishlistEnabled = Features.isEnabled('wishlist') return { props: { @@ -30,6 +32,9 @@ export async function getStaticProps({ categories, brands, pages, + commerceFeatures: { + wishlist: isWishlistEnabled, + }, }, revalidate: 14400, } @@ -39,6 +44,7 @@ export default function Home({ products, brands, categories, + commerceFeatures, }: InferGetStaticPropsType<typeof getStaticProps>) { return ( <> @@ -51,6 +57,7 @@ export default function Home({ width: i === 0 ? 1080 : 540, height: i === 0 ? 1080 : 540, }} + wishlist={commerceFeatures.wishlist} /> ))} </Grid> @@ -64,6 +71,7 @@ export default function Home({ width: 320, height: 320, }} + wishlist={commerceFeatures.wishlist} /> ))} </Marquee> @@ -86,6 +94,7 @@ export default function Home({ width: i === 0 ? 1080 : 540, height: i === 0 ? 1080 : 540, }} + wishlist={commerceFeatures.wishlist} /> ))} </Grid> @@ -99,6 +108,7 @@ export default function Home({ width: 320, height: 320, }} + wishlist={commerceFeatures.wishlist} /> ))} </Marquee> diff --git a/pages/product/[slug].tsx b/pages/product/[slug].tsx index 83aeaa54c..a705c001b 100644 --- a/pages/product/[slug].tsx +++ b/pages/product/[slug].tsx @@ -11,14 +11,15 @@ import { getConfig } from '@framework/api' import getProduct from '@framework/product/get-product' import getAllPages from '@framework/common/get-all-pages' import getAllProductPaths from '@framework/product/get-all-product-paths' +import Features from '@commerce/utils/features' export async function getStaticProps({ params, locale, preview, }: GetStaticPropsContext<{ slug: string }>) { + const isWishlistEnabled = Features.isEnabled('wishlist') const config = getConfig({ locale }) - const { pages } = await getAllPages({ config, preview }) const { product } = await getProduct({ variables: { slug: params!.slug }, @@ -31,7 +32,13 @@ export async function getStaticProps({ } return { - props: { pages, product }, + props: { + pages, + product, + commerceFeatures: { + wishlist: isWishlistEnabled, + }, + }, revalidate: 200, } } @@ -55,13 +62,17 @@ export async function getStaticPaths({ locales }: GetStaticPathsContext) { export default function Slug({ product, + commerceFeatures, }: InferGetStaticPropsType<typeof getStaticProps>) { const router = useRouter() return router.isFallback ? ( <h1>Loading...</h1> // TODO (BC) Add Skeleton Views ) : ( - <ProductView product={product as any} /> + <ProductView + product={product as any} + wishlist={commerceFeatures.wishlist} + /> ) } diff --git a/yarn.lock b/yarn.lock index a3e80eb6c..e7cd08438 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1060,6 +1060,13 @@ dependencies: "@types/lodash" "*" +"@types/lodash.memoize@^4.1.6": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@types/lodash.memoize/-/lodash.memoize-4.1.6.tgz#3221f981790a415cab1a239f25c17efd8b604c23" + integrity sha512-mYxjKiKzRadRJVClLKxS4wb3Iy9kzwJ1CkbyKiadVxejnswnRByyofmPMscFKscmYpl36BEEhCMPuWhA1R/1ZQ== + dependencies: + "@types/lodash" "*" + "@types/lodash.random@^3.2.6": version "3.2.6" resolved "https://registry.yarnpkg.com/@types/lodash.random/-/lodash.random-3.2.6.tgz#64b08abad168dca39c778ed40cce75b2f9e168eb" @@ -4232,6 +4239,11 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" From 4cfa0418dd0a0a5df20e4397be8793a2e6c7a68b Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Thu, 18 Feb 2021 13:11:57 -0300 Subject: [PATCH 140/221] Adding wishlist api --- .../HomeAllProductsGrid/HomeAllProductsGrid.tsx | 12 ++++++++++-- components/common/UserNav/UserNav.tsx | 4 ++-- pages/search.tsx | 16 ++++++++++++++-- pages/wishlist.tsx | 4 ++-- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx index 49e115df6..4b838e1a4 100644 --- a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx +++ b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx @@ -5,14 +5,21 @@ import { Grid } from '@components/ui' import { ProductCard } from '@components/product' import s from './HomeAllProductsGrid.module.css' import { getCategoryPath, getDesignerPath } from '@lib/search' +import wishlist from '@framework/api/wishlist' interface Props { categories?: any brands?: any products?: Product[] + wishlist?: boolean } -const Head: FC<Props> = ({ categories, brands, products = [] }) => { +const HomeAllProductsGrid: FC<Props> = ({ + categories, + brands, + products = [], + wishlist = false, +}) => { return ( <div className={s.root}> <div className={s.asideWrapper}> @@ -58,6 +65,7 @@ const Head: FC<Props> = ({ categories, brands, products = [] }) => { width: 480, height: 480, }} + wishlist={wishlist} /> ))} </Grid> @@ -66,4 +74,4 @@ const Head: FC<Props> = ({ categories, brands, products = [] }) => { ) } -export default Head +export default HomeAllProductsGrid diff --git a/components/common/UserNav/UserNav.tsx b/components/common/UserNav/UserNav.tsx index 19151c38b..ab8021063 100644 --- a/components/common/UserNav/UserNav.tsx +++ b/components/common/UserNav/UserNav.tsx @@ -9,20 +9,20 @@ import { useUI } from '@components/ui/context' import DropdownMenu from './DropdownMenu' import s from './UserNav.module.css' import { Avatar } from '@components/common' -import frameworkConfig from '@framework/config.json' +import Features from '@commerce/utils/features' interface Props { className?: string } const countItem = (count: number, item: LineItem) => count + item.quantity +const isWishlistEnabled = Features.isEnabled('wishlist') const UserNav: FC<Props> = ({ className }) => { const { data } = useCart() const { data: customer } = useCustomer() const { toggleSidebar, closeSidebarIfPresent, openModal } = useUI() const itemsCount = data?.lineItems.reduce(countItem, 0) ?? 0 - const isWishlistEnabled = !!frameworkConfig.features.wishlist return ( <nav className={cn(s.root, className)}> diff --git a/pages/search.tsx b/pages/search.tsx index 821b5a9f5..c9958a9f8 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -26,6 +26,8 @@ const SORT = Object.entries({ 'price-desc': 'Price: High to low', }) +import Features from '@commerce/utils/features' + import { filterQuery, getCategoryPath, @@ -40,14 +42,23 @@ export async function getStaticProps({ const config = getConfig({ locale }) const { pages } = await getAllPages({ config, preview }) const { categories, brands } = await getSiteInfo({ config, preview }) + const isWishlistEnabled = Features.isEnabled('wishlist') return { - props: { pages, categories, brands }, + props: { + pages, + categories, + brands, + commerceFeatures: { + wishlist: isWishlistEnabled, + }, + }, } } export default function Search({ categories, brands, + commerceFeatures: { wishlist }, }: InferGetStaticPropsType<typeof getStaticProps>) { const [activeFilter, setActiveFilter] = useState('') const [toggleFilter, setToggleFilter] = useState(false) @@ -337,7 +348,7 @@ export default function Search({ {data ? ( <Grid layout="normal"> - {data.products.map((product) => ( + {data.products.map((product: Product) => ( <ProductCard variant="simple" key={product.path} @@ -347,6 +358,7 @@ export default function Search({ width: 480, height: 480, }} + wishlist={wishlist} /> ))} </Grid> diff --git a/pages/wishlist.tsx b/pages/wishlist.tsx index 95a605c6a..ca11152f4 100644 --- a/pages/wishlist.tsx +++ b/pages/wishlist.tsx @@ -11,14 +11,14 @@ import { useCustomer } from '@framework/customer' import { WishlistCard } from '@components/wishlist' import useWishlist from '@framework/wishlist/use-wishlist' import getAllPages from '@framework/common/get-all-pages' -import frameworkConfig from '@framework/config.json' +import Features from '@commerce/utils/features' export async function getStaticProps({ preview, locale, }: GetStaticPropsContext) { // Disabling page if Feature is not available - if (!frameworkConfig.features.wishlist) { + if (Features.isEnabled('wishlist')) { return { notFound: true, } From cbc354e0b6984dfe81f5f2cda06daff5c3a36c11 Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Thu, 18 Feb 2021 13:21:43 -0300 Subject: [PATCH 141/221] Implementing FeaturesAPI with Wishlist --- components/cart/CartSidebarView/CartSidebarView.tsx | 4 ++-- components/common/Layout/Layout.tsx | 12 ++++++++---- components/common/Navbar/Navbar.tsx | 4 ++-- components/common/UserNav/UserNav.tsx | 7 +++---- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index c25bd7c95..5b28fde27 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -9,7 +9,7 @@ import usePrice from '@framework/product/use-price' import CartItem from '../CartItem' import s from './CartSidebarView.module.css' -const CartSidebarView: FC = () => { +const CartSidebarView: FC<{ wishlist?: boolean }> = ({ wishlist }) => { const { closeSidebar } = useUI() const { data, isLoading, isEmpty } = useCart() @@ -48,7 +48,7 @@ const CartSidebarView: FC = () => { </button> </div> <div className="space-y-1"> - <UserNav className="" /> + <UserNav wishlist={wishlist} /> </div> </div> </header> diff --git a/components/common/Layout/Layout.tsx b/components/common/Layout/Layout.tsx index 204c3a871..7eb235ed7 100644 --- a/components/common/Layout/Layout.tsx +++ b/components/common/Layout/Layout.tsx @@ -44,7 +44,11 @@ interface Props { } } -const Layout: FC<Props> = ({ children, pageProps }) => { +const Layout: FC<Props> = ({ + children, + pageProps: { commerceFeatures, ...pageProps }, +}) => { + console.log(pageProps) const { displaySidebar, displayModal, @@ -54,11 +58,11 @@ const Layout: FC<Props> = ({ children, pageProps }) => { } = useUI() const { acceptedCookies, onAcceptCookies } = useAcceptCookies() const { locale = 'en-US' } = useRouter() - + const isWishlistEnabled = commerceFeatures.wishlist return ( <CommerceProvider locale={locale}> <div className={cn(s.root)}> - <Navbar /> + <Navbar wishlist={isWishlistEnabled} /> <main className="fit">{children}</main> <Footer pages={pageProps.pages} /> @@ -69,7 +73,7 @@ const Layout: FC<Props> = ({ children, pageProps }) => { </Modal> <Sidebar open={displaySidebar} onClose={closeSidebar}> - <CartSidebarView /> + <CartSidebarView wishlist={isWishlistEnabled} /> </Sidebar> <FeatureBar diff --git a/components/common/Navbar/Navbar.tsx b/components/common/Navbar/Navbar.tsx index b2a372f66..ce9d91b38 100644 --- a/components/common/Navbar/Navbar.tsx +++ b/components/common/Navbar/Navbar.tsx @@ -5,7 +5,7 @@ import { Searchbar, UserNav } from '@components/common' import NavbarRoot from './NavbarRoot' import s from './Navbar.module.css' -const Navbar: FC = () => ( +const Navbar: FC<{ wishlist?: boolean }> = ({ wishlist }) => ( <NavbarRoot> <Container> <div className="relative flex flex-row justify-between py-4 align-center md:py-6"> @@ -33,7 +33,7 @@ const Navbar: FC = () => ( </div> <div className="flex justify-end flex-1 space-x-8"> - <UserNav /> + <UserNav wishlist={wishlist} /> </div> </div> diff --git a/components/common/UserNav/UserNav.tsx b/components/common/UserNav/UserNav.tsx index ab8021063..5d9d58fff 100644 --- a/components/common/UserNav/UserNav.tsx +++ b/components/common/UserNav/UserNav.tsx @@ -9,16 +9,15 @@ import { useUI } from '@components/ui/context' import DropdownMenu from './DropdownMenu' import s from './UserNav.module.css' import { Avatar } from '@components/common' -import Features from '@commerce/utils/features' interface Props { className?: string + wishlist?: boolean } const countItem = (count: number, item: LineItem) => count + item.quantity -const isWishlistEnabled = Features.isEnabled('wishlist') -const UserNav: FC<Props> = ({ className }) => { +const UserNav: FC<Props> = ({ className, wishlist = false }) => { const { data } = useCart() const { data: customer } = useCustomer() const { toggleSidebar, closeSidebarIfPresent, openModal } = useUI() @@ -32,7 +31,7 @@ const UserNav: FC<Props> = ({ className }) => { <Bag /> {itemsCount > 0 && <span className={s.bagCount}>{itemsCount}</span>} </li> - {isWishlistEnabled && ( + {wishlist && ( <li className={s.item}> <Link href="/wishlist"> <a onClick={closeSidebarIfPresent} aria-label="Wishlist"> From 19db5a7e1d393168f19f00ff69008395c5c0ce5c Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Thu, 18 Feb 2021 13:24:46 -0300 Subject: [PATCH 142/221] Removing bug with touchstart --- components/product/ProductSlider/ProductSlider.tsx | 10 ++++++---- framework/bigcommerce/config.json | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/components/product/ProductSlider/ProductSlider.tsx b/components/product/ProductSlider/ProductSlider.tsx index 4ea7d2ec4..02244f5ba 100644 --- a/components/product/ProductSlider/ProductSlider.tsx +++ b/components/product/ProductSlider/ProductSlider.tsx @@ -50,10 +50,12 @@ const ProductSlider: FC = ({ children }) => { ) return () => { - sliderContainerRef.current!.removeEventListener( - 'touchstart', - preventNavigation - ) + if (sliderContainerRef.current) { + sliderContainerRef.current!.removeEventListener( + 'touchstart', + preventNavigation + ) + } } }, []) diff --git a/framework/bigcommerce/config.json b/framework/bigcommerce/config.json index a0e7afc5d..17ef37e25 100644 --- a/framework/bigcommerce/config.json +++ b/framework/bigcommerce/config.json @@ -1,5 +1,5 @@ { "features": { - "wishlist": true + "wishlist": false } } From 05501c6f998683d2fc261f6111fe6c88142c2fa5 Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Thu, 18 Feb 2021 13:26:47 -0300 Subject: [PATCH 143/221] Adding tyni typing --- components/common/Layout/Layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/common/Layout/Layout.tsx b/components/common/Layout/Layout.tsx index 7eb235ed7..f4376bbf3 100644 --- a/components/common/Layout/Layout.tsx +++ b/components/common/Layout/Layout.tsx @@ -41,6 +41,7 @@ const FeatureBar = dynamic( interface Props { pageProps: { pages?: Page[] + commerceFeatures: Record<string, boolean> } } @@ -48,7 +49,6 @@ const Layout: FC<Props> = ({ children, pageProps: { commerceFeatures, ...pageProps }, }) => { - console.log(pageProps) const { displaySidebar, displayModal, From 6dc7e0b632e8a5ae35d7292dfd250b4b80a92d2a Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 18 Feb 2021 16:06:19 -0500 Subject: [PATCH 144/221] moved use-remove-item --- .../bigcommerce/cart/use-remove-item.tsx | 71 +++++++++---------- .../bigcommerce/cart/use-update-item.tsx | 8 ++- framework/bigcommerce/provider.ts | 3 +- framework/commerce/cart/use-remove-item.tsx | 30 ++++++-- framework/commerce/cart/use-update-item.tsx | 22 +++--- framework/commerce/utils/default-fetcher.ts | 6 ++ 6 files changed, 81 insertions(+), 59 deletions(-) diff --git a/framework/bigcommerce/cart/use-remove-item.tsx b/framework/bigcommerce/cart/use-remove-item.tsx index c8cdaeb0d..c1f1ab4c0 100644 --- a/framework/bigcommerce/cart/use-remove-item.tsx +++ b/framework/bigcommerce/cart/use-remove-item.tsx @@ -1,8 +1,9 @@ import { useCallback } from 'react' -import { HookFetcher } from '@commerce/utils/types' +import { HookContext, HookFetcherContext } from '@commerce/utils/types' import { ValidationError } from '@commerce/utils/errors' -import useCartRemoveItem, { - RemoveItemInput as UseRemoveItemInput, +import useRemoveItem, { + RemoveItemInput as RemoveItemInputBase, + UseRemoveItem, } from '@commerce/cart/use-remove-item' import { normalizeCart } from '../lib/normalize' import type { @@ -13,41 +14,39 @@ import type { } from '../types' import useCart from './use-cart' -const defaultOpts = { - url: '/api/bigcommerce/cart', - method: 'DELETE', -} - export type RemoveItemFn<T = any> = T extends LineItem ? (input?: RemoveItemInput<T>) => Promise<Cart | null> : (input: RemoveItemInput<T>) => Promise<Cart | null> export type RemoveItemInput<T = any> = T extends LineItem - ? Partial<UseRemoveItemInput> - : UseRemoveItemInput + ? Partial<RemoveItemInputBase> + : RemoveItemInputBase -export const fetcher: HookFetcher<Cart | null, RemoveCartItemBody> = async ( - options, - { itemId }, - fetch -) => { - const data = await fetch<BigcommerceCart>({ - ...defaultOpts, - ...options, - body: { itemId }, - }) - return normalizeCart(data) -} +export default useRemoveItem as UseRemoveItem<typeof handler> -export function extendHook(customFetcher: typeof fetcher) { - const useRemoveItem = <T extends LineItem | undefined = undefined>( - item?: T +export const handler = { + fetchOptions: { + url: '/api/bigcommerce/cart', + method: 'DELETE', + }, + async fetcher({ + input: { itemId }, + options, + fetch, + }: HookFetcherContext<RemoveCartItemBody>) { + const data = await fetch<BigcommerceCart>({ + ...options, + body: { itemId }, + }) + return normalizeCart(data) + }, + useHook: ({ fetch }: HookContext<Cart | null, RemoveCartItemBody>) => < + T extends LineItem | undefined = undefined + >( + ctx: { item?: T } = {} ) => { - const { mutate } = useCart() - const fn = useCartRemoveItem<Cart | null, RemoveCartItemBody>( - defaultOpts, - customFetcher - ) + const { item } = ctx + const { mutate } = useCart() as any const removeItem: RemoveItemFn<LineItem> = async (input) => { const itemId = input?.id ?? item?.id @@ -57,17 +56,11 @@ export function extendHook(customFetcher: typeof fetcher) { }) } - const data = await fn({ itemId }) + const data = await fetch({ input: { itemId } }) await mutate(data, false) return data } - return useCallback(removeItem as RemoveItemFn<T>, [fn, mutate]) - } - - useRemoveItem.extend = extendHook - - return useRemoveItem + return useCallback(removeItem as RemoveItemFn<T>, [fetch, mutate]) + }, } - -export default extendHook(fetcher) diff --git a/framework/bigcommerce/cart/use-update-item.tsx b/framework/bigcommerce/cart/use-update-item.tsx index f5a09006a..cfd6007d1 100644 --- a/framework/bigcommerce/cart/use-update-item.tsx +++ b/framework/bigcommerce/cart/use-update-item.tsx @@ -13,7 +13,7 @@ import type { BigcommerceCart, LineItem, } from '../types' -import { fetcher as removeFetcher } from './use-remove-item' +import { handler as removeItemHandler } from './use-remove-item' import useCart from './use-cart' export type UpdateItemInput<T = any> = T extends LineItem @@ -35,7 +35,11 @@ export const handler = { if (Number.isInteger(item.quantity)) { // Also allow the update hook to remove an item if the quantity is lower than 1 if (item.quantity! < 1) { - return removeFetcher(null, { itemId }, fetch) + return removeItemHandler.fetcher({ + options: removeItemHandler.fetchOptions, + input: { itemId }, + fetch, + }) } } else if (item.quantity) { throw new ValidationError({ diff --git a/framework/bigcommerce/provider.ts b/framework/bigcommerce/provider.ts index 434c1c39e..e233dad04 100644 --- a/framework/bigcommerce/provider.ts +++ b/framework/bigcommerce/provider.ts @@ -1,6 +1,7 @@ import { handler as useCart } from './cart/use-cart' import { handler as useAddItem } from './cart/use-add-item' import { handler as useUpdateItem } from './cart/use-update-item' +import { handler as useRemoveItem } from './cart/use-remove-item' import { handler as useWishlist } from './wishlist/use-wishlist' import { handler as useCustomer } from './customer/use-customer' import { handler as useSearch } from './product/use-search' @@ -10,7 +11,7 @@ export const bigcommerceProvider = { locale: 'en-us', cartCookie: 'bc_cartId', fetcher, - cart: { useCart, useAddItem, useUpdateItem }, + cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, wishlist: { useWishlist }, customer: { useCustomer }, products: { useSearch }, diff --git a/framework/commerce/cart/use-remove-item.tsx b/framework/commerce/cart/use-remove-item.tsx index 8a63b1b73..32d8cbf1c 100644 --- a/framework/commerce/cart/use-remove-item.tsx +++ b/framework/commerce/cart/use-remove-item.tsx @@ -1,10 +1,32 @@ -import useAction from '../utils/use-action' +import useHook, { useHookHandler } from '../utils/use-hook' +import { mutationFetcher } from '../utils/default-fetcher' +import type { MutationHook } from '../utils/types' +import type { Cart, LineItem, RemoveCartItemBody } from '../types' +import type { Provider } from '..' -// Input expected by the action returned by the `useRemoveItem` hook -export interface RemoveItemInput { +/** + * Input expected by the action returned by the `useRemoveItem` hook + */ +export type RemoveItemInput = { id: string } -const useRemoveItem = useAction +export type UseRemoveItem< + H extends MutationHook<any, any, any> = MutationHook< + Cart | null, + { item?: LineItem }, + RemoveItemInput, + RemoveCartItemBody + > +> = ReturnType<H['useHook']> + +export const fetcher = mutationFetcher + +const fn = (provider: Provider) => provider.cart?.useRemoveItem! + +const useRemoveItem: UseRemoveItem = (input) => { + const handler = useHookHandler(fn, fetcher) + return handler(useHook(fn, fetcher))(input) +} export default useRemoveItem diff --git a/framework/commerce/cart/use-update-item.tsx b/framework/commerce/cart/use-update-item.tsx index cc904a14a..93afdbb1e 100644 --- a/framework/commerce/cart/use-update-item.tsx +++ b/framework/commerce/cart/use-update-item.tsx @@ -1,17 +1,19 @@ import useHook, { useHookHandler } from '../utils/use-hook' -import type { MutationHook, HookFetcherFn } from '../utils/types' +import { mutationFetcher } from '../utils/default-fetcher' +import type { MutationHook } from '../utils/types' import type { Cart, CartItemBody, LineItem, UpdateCartItemBody } from '../types' import type { Provider } from '..' -import debounce from 'lodash.debounce' -// Input expected by the action returned by the `useUpdateItem` hook +/** + * Input expected by the action returned by the `useUpdateItem` hook + */ export type UpdateItemInput<T extends CartItemBody> = T & { id: string } export type UseUpdateItem< H extends MutationHook<any, any, any> = MutationHook< - Cart, + Cart | null, { item?: LineItem wait?: number @@ -21,19 +23,13 @@ export type UseUpdateItem< > > = ReturnType<H['useHook']> -export const fetcher: HookFetcherFn<any> = async ({ - options, - input, - fetch, -}) => { - return fetch({ ...options, body: input }) -} +export const fetcher = mutationFetcher const fn = (provider: Provider) => provider.cart?.useUpdateItem! -const useUpdateItem: UseUpdateItem = (input = {}) => { +const useUpdateItem: UseUpdateItem = (input) => { const handler = useHookHandler(fn, fetcher) - return debounce(handler(useHook(fn, fetcher))(input), input.wait ?? 500) + return handler(useHook(fn, fetcher))(input) } export default useUpdateItem diff --git a/framework/commerce/utils/default-fetcher.ts b/framework/commerce/utils/default-fetcher.ts index 8dc9def75..cdaf05516 100644 --- a/framework/commerce/utils/default-fetcher.ts +++ b/framework/commerce/utils/default-fetcher.ts @@ -3,4 +3,10 @@ import type { HookFetcherFn } from './types' const defaultFetcher: HookFetcherFn<any> = ({ options, fetch }) => fetch(options) +export const mutationFetcher: HookFetcherFn<any> = ({ + input, + options, + fetch, +}) => fetch({ ...options, body: input }) + export default defaultFetcher From a7df259bdadc4d44191b818bac02544a7f3b685a Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 18 Feb 2021 16:07:11 -0500 Subject: [PATCH 145/221] Removed MutationHandler type --- framework/commerce/index.tsx | 7 +------ framework/commerce/utils/types.ts | 18 ------------------ 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index 935e8610f..1a2ba878b 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -6,12 +6,7 @@ import { useMemo, useRef, } from 'react' -import { - Fetcher, - HookHandler, - MutationHandler, - MutationHook, -} from './utils/types' +import { Fetcher, HookHandler, MutationHook } from './utils/types' import type { FetchCartInput } from './cart/use-cart' import type { Cart, Wishlist, Customer, SearchProductsData } from './types' diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 120d66366..98e4d0f34 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -74,24 +74,6 @@ export type HookHandler< fetcher?: HookFetcherFn<Data, FetchInput> } -export type MutationHandler< - // Data obj returned by the hook and fetch operation - Data, - // Input expected by the hook - Input extends { [k: string]: unknown } = {}, - // Input expected before doing a fetch operation - FetchInput extends { [k: string]: unknown } = {} -> = { - useHook?(context: { - input: Input - }): (context: { - input: FetchInput - fetch: (context: { input: FetchInput }) => Data | Promise<Data> - }) => Data | Promise<Data> - fetchOptions: HookFetcherOptions - fetcher?: HookFetcherFn<Data, FetchInput> -} - export type HookFunction< Input extends { [k: string]: unknown } | {}, T From 2e1d2610bb26876e493d22655c4abd30dc51f511 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 18 Feb 2021 23:22:45 -0500 Subject: [PATCH 146/221] Moved more hooks and updated types to make them smaller --- framework/bigcommerce/cart/use-cart.tsx | 11 ++- .../bigcommerce/cart/use-remove-item.tsx | 2 +- framework/commerce/cart/use-add-item.tsx | 11 ++- framework/commerce/cart/use-cart.tsx | 64 +++++----------- framework/commerce/cart/use-remove-item.tsx | 13 ++-- framework/commerce/cart/use-update-item.tsx | 13 ++-- framework/commerce/index.tsx | 4 +- framework/commerce/utils/default-fetcher.ts | 2 +- framework/commerce/utils/types.ts | 38 +++++++++- framework/commerce/utils/use-data.tsx | 9 ++- framework/commerce/utils/use-hook.ts | 73 ++++++++++--------- 11 files changed, 128 insertions(+), 112 deletions(-) diff --git a/framework/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx index b5cc0cccf..2098e7431 100644 --- a/framework/bigcommerce/cart/use-cart.tsx +++ b/framework/bigcommerce/cart/use-cart.tsx @@ -1,13 +1,12 @@ import { useMemo } from 'react' -import { HookHandler } from '@commerce/utils/types' +import { SWRHook } from '@commerce/utils/types' import useCart, { UseCart, FetchCartInput } from '@commerce/cart/use-cart' import { normalizeCart } from '../lib/normalize' import type { Cart } from '../types' -import type { BigcommerceProvider } from '..' -export default useCart as UseCart<BigcommerceProvider> +export default useCart as UseCart<typeof handler> -export const handler: HookHandler< +export const handler: SWRHook< Cart | null, {}, FetchCartInput, @@ -21,9 +20,9 @@ export const handler: HookHandler< const data = cartId ? await fetch(options) : null return data && normalizeCart(data) }, - useHook({ input, useData }) { + useHook: ({ useData }) => (input) => { const response = useData({ - swrOptions: { revalidateOnFocus: false, ...input.swrOptions }, + swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, }) return useMemo( diff --git a/framework/bigcommerce/cart/use-remove-item.tsx b/framework/bigcommerce/cart/use-remove-item.tsx index c1f1ab4c0..f279b2dc6 100644 --- a/framework/bigcommerce/cart/use-remove-item.tsx +++ b/framework/bigcommerce/cart/use-remove-item.tsx @@ -46,7 +46,7 @@ export const handler = { ctx: { item?: T } = {} ) => { const { item } = ctx - const { mutate } = useCart() as any + const { mutate } = useCart() const removeItem: RemoveItemFn<LineItem> = async (input) => { const itemId = input?.id ?? item?.id diff --git a/framework/commerce/cart/use-add-item.tsx b/framework/commerce/cart/use-add-item.tsx index 715029d18..13da6b416 100644 --- a/framework/commerce/cart/use-add-item.tsx +++ b/framework/commerce/cart/use-add-item.tsx @@ -1,4 +1,5 @@ -import useHook, { useHookHandler } from '../utils/use-hook' +import { useHook, useMutationHook } from '../utils/use-hook' +import { mutationFetcher } from '../utils/default-fetcher' import type { MutationHook, HookFetcherFn } from '../utils/types' import type { Cart, CartItemBody, AddCartItemBody } from '../types' import type { Provider } from '..' @@ -6,9 +7,7 @@ import type { Provider } from '..' export const fetcher: HookFetcherFn< Cart, AddCartItemBody<CartItemBody> -> = async ({ options, input, fetch }) => { - return fetch({ ...options, body: input }) -} +> = mutationFetcher export type UseAddItem< H extends MutationHook<any, any, any> = MutationHook<Cart, {}, CartItemBody> @@ -17,8 +16,8 @@ export type UseAddItem< const fn = (provider: Provider) => provider.cart?.useAddItem! const useAddItem: UseAddItem = (...args) => { - const handler = useHookHandler(fn, fetcher) - return handler(useHook(fn, fetcher))(...args) + const hook = useHook(fn) + return useMutationHook({ fetcher, ...hook })(...args) } export default useAddItem diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index f7b384047..6053c91d7 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -1,34 +1,21 @@ import Cookies from 'js-cookie' import type { Cart } from '../types' -import type { - Prop, - HookFetcherFn, - UseHookInput, - UseHookResponse, -} from '../utils/types' -import useData from '../utils/use-data' +import type { HookFetcherFn, SWRHook } from '../utils/types' import { Provider, useCommerce } from '..' +import { useHook, useSWRHook } from '@commerce/utils/use-hook' export type FetchCartInput = { cartId?: Cart['id'] } -export type UseCartHandler<P extends Provider> = Prop< - Prop<P, 'cart'>, - 'useCart' -> - -export type UseCartInput<P extends Provider> = UseHookInput<UseCartHandler<P>> - -export type CartResponse<P extends Provider> = UseHookResponse< - UseCartHandler<P> -> - -export type UseCart<P extends Provider> = Partial< - UseCartInput<P> -> extends UseCartInput<P> - ? (input?: UseCartInput<P>) => CartResponse<P> - : (input: UseCartInput<P>) => CartResponse<P> +export type UseCart< + H extends SWRHook<any, any, any> = SWRHook< + Cart | null, + {}, + FetchCartInput, + { isEmpty?: boolean } + > +> = ReturnType<H['useHook']> export const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ options, @@ -38,32 +25,17 @@ export const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ return cartId ? await fetch({ ...options }) : null } -export default function useCart<P extends Provider>( - input: UseCartInput<P> = {} -) { - const { providerRef, fetcherRef, cartCookie } = useCommerce<P>() - - const provider = providerRef.current - const opts = provider.cart?.useCart - - const fetcherFn = opts?.fetcher ?? fetcher - const useHook = opts?.useHook ?? ((ctx) => ctx.useData()) +const fn = (provider: Provider) => provider.cart?.useCart! +const useCart: UseCart = (input) => { + const hook = useHook(fn) + const { cartCookie } = useCommerce() + const fetcherFn = hook.fetcher ?? fetcher const wrapper: typeof fetcher = (context) => { context.input.cartId = Cookies.get(cartCookie) return fetcherFn(context) } - - return useHook({ - input, - useData(ctx) { - const response = useData( - { ...opts!, fetcher: wrapper }, - ctx?.input ?? [], - provider.fetcher ?? fetcherRef.current, - ctx?.swrOptions ?? input.swrOptions - ) - return response - }, - }) + return useSWRHook({ ...hook, fetcher: wrapper })(input) } + +export default useCart diff --git a/framework/commerce/cart/use-remove-item.tsx b/framework/commerce/cart/use-remove-item.tsx index 32d8cbf1c..a9d1b37d2 100644 --- a/framework/commerce/cart/use-remove-item.tsx +++ b/framework/commerce/cart/use-remove-item.tsx @@ -1,6 +1,6 @@ -import useHook, { useHookHandler } from '../utils/use-hook' +import { useHook, useMutationHook } from '../utils/use-hook' import { mutationFetcher } from '../utils/default-fetcher' -import type { MutationHook } from '../utils/types' +import type { HookFetcherFn, MutationHook } from '../utils/types' import type { Cart, LineItem, RemoveCartItemBody } from '../types' import type { Provider } from '..' @@ -20,13 +20,16 @@ export type UseRemoveItem< > > = ReturnType<H['useHook']> -export const fetcher = mutationFetcher +export const fetcher: HookFetcherFn< + Cart | null, + RemoveCartItemBody +> = mutationFetcher const fn = (provider: Provider) => provider.cart?.useRemoveItem! const useRemoveItem: UseRemoveItem = (input) => { - const handler = useHookHandler(fn, fetcher) - return handler(useHook(fn, fetcher))(input) + const hook = useHook(fn) + return useMutationHook({ fetcher, ...hook })(input) } export default useRemoveItem diff --git a/framework/commerce/cart/use-update-item.tsx b/framework/commerce/cart/use-update-item.tsx index 93afdbb1e..f8d0f1a40 100644 --- a/framework/commerce/cart/use-update-item.tsx +++ b/framework/commerce/cart/use-update-item.tsx @@ -1,6 +1,6 @@ -import useHook, { useHookHandler } from '../utils/use-hook' +import { useHook, useMutationHook } from '../utils/use-hook' import { mutationFetcher } from '../utils/default-fetcher' -import type { MutationHook } from '../utils/types' +import type { HookFetcherFn, MutationHook } from '../utils/types' import type { Cart, CartItemBody, LineItem, UpdateCartItemBody } from '../types' import type { Provider } from '..' @@ -23,13 +23,16 @@ export type UseUpdateItem< > > = ReturnType<H['useHook']> -export const fetcher = mutationFetcher +export const fetcher: HookFetcherFn< + Cart | null, + UpdateCartItemBody<CartItemBody> +> = mutationFetcher const fn = (provider: Provider) => provider.cart?.useUpdateItem! const useUpdateItem: UseUpdateItem = (input) => { - const handler = useHookHandler(fn, fetcher) - return handler(useHook(fn, fetcher))(input) + const hook = useHook(fn) + return useMutationHook({ fetcher, ...hook })(input) } export default useUpdateItem diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index 1a2ba878b..6ce2c8176 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -6,7 +6,7 @@ import { useMemo, useRef, } from 'react' -import { Fetcher, HookHandler, MutationHook } from './utils/types' +import { Fetcher, HookHandler, SWRHook, MutationHook } from './utils/types' import type { FetchCartInput } from './cart/use-cart' import type { Cart, Wishlist, Customer, SearchProductsData } from './types' @@ -15,7 +15,7 @@ const Commerce = createContext<CommerceContextValue<any> | {}>({}) export type Provider = CommerceConfig & { fetcher: Fetcher cart?: { - useCart?: HookHandler<Cart | null, any, FetchCartInput> + useCart?: SWRHook<Cart | null, any, FetchCartInput> useAddItem?: MutationHook<any, any, any> useUpdateItem?: MutationHook<any, any, any> useRemoveItem?: MutationHook<any, any, any> diff --git a/framework/commerce/utils/default-fetcher.ts b/framework/commerce/utils/default-fetcher.ts index cdaf05516..654da2499 100644 --- a/framework/commerce/utils/default-fetcher.ts +++ b/framework/commerce/utils/default-fetcher.ts @@ -3,7 +3,7 @@ import type { HookFetcherFn } from './types' const defaultFetcher: HookFetcherFn<any> = ({ options, fetch }) => fetch(options) -export const mutationFetcher: HookFetcherFn<any> = ({ +export const mutationFetcher: HookFetcherFn<any, any> = ({ input, options, fetch, diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 98e4d0f34..01f1c2eb3 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -9,7 +9,9 @@ export type Override<T, K> = Omit<T, keyof K> & K * Returns the properties in T with the properties in type K changed from optional to required */ export type PickRequired<T, K extends keyof T> = Omit<T, K> & - Required<Pick<T, K>> + { + [P in K]-?: NonNullable<T[P]> + } /** * Core fetcher added by CommerceProvider @@ -83,6 +85,36 @@ export type HookFunction< ? (input?: Input) => T : (input: Input) => T +export type SWRHook< + // Data obj returned by the hook and fetch operation + Data, + // Input expected by the hook + Input extends { [k: string]: unknown } = {}, + // Input expected before doing a fetch operation + FetchInput extends HookFetchInput = {}, + // Custom state added to the response object of SWR + State = {} +> = { + useHook( + context: SWRHookContext<Data, FetchInput> + ): HookFunction< + Input & { swrOptions?: SwrOptions<Data, FetchInput> }, + ResponseState<Data> & State + > + fetchOptions: HookFetcherOptions + fetcher?: HookFetcherFn<Data, FetchInput> +} + +export type SWRHookContext< + Data, + FetchInput extends { [k: string]: unknown } = {} +> = { + useData(context?: { + input?: HookFetchInput | HookSwrInput + swrOptions?: SwrOptions<Data, FetchInput> + }): ResponseState<Data> +} + export type MutationHook< // Data obj returned by the hook and fetch operation Data, @@ -118,9 +150,7 @@ export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< */ export type Prop<T, K extends keyof T> = NonNullable<T[K]> -export type HookHandlerType = - | HookHandler<any, any, any> - | MutationHandler<any, any, any> +export type HookHandlerType = HookHandler<any, any, any> export type UseHookParameters<H extends HookHandlerType> = Parameters< Prop<H, 'useHook'> diff --git a/framework/commerce/utils/use-data.tsx b/framework/commerce/utils/use-data.tsx index 94679a0c6..fe21bd3d3 100644 --- a/framework/commerce/utils/use-data.tsx +++ b/framework/commerce/utils/use-data.tsx @@ -1,11 +1,11 @@ import useSWR, { responseInterface } from 'swr' import type { - HookHandler, HookSwrInput, HookFetchInput, - PickRequired, Fetcher, SwrOptions, + HookFetcherOptions, + HookFetcherFn, } from './types' import defineProperty from './define-property' import { CommerceError } from './errors' @@ -19,7 +19,10 @@ export type UseData = < Input extends { [k: string]: unknown } = {}, FetchInput extends HookFetchInput = {} >( - options: PickRequired<HookHandler<Data, Input, FetchInput>, 'fetcher'>, + options: { + fetchOptions: HookFetcherOptions + fetcher: HookFetcherFn<Data, FetchInput> + }, input: HookFetchInput | HookSwrInput, fetcherFn: Fetcher, swrOptions?: SwrOptions<Data, FetchInput> diff --git a/framework/commerce/utils/use-hook.ts b/framework/commerce/utils/use-hook.ts index b37c33370..830918de5 100644 --- a/framework/commerce/utils/use-hook.ts +++ b/framework/commerce/utils/use-hook.ts @@ -1,43 +1,50 @@ import { useCallback } from 'react' -import type { MutationHook } from './types' +import type { Fetcher, MutationHook, PickRequired, SWRHook } from './types' import { Provider, useCommerce } from '..' +import useData from './use-data' -export function useHookHandler<P extends Provider>( - fn: (provider: P) => MutationHook<any, any, any>, - fetcher: any -) { +export function useFetcher() { + const { providerRef, fetcherRef } = useCommerce() + return providerRef.current.fetcher ?? fetcherRef.current +} + +export function useHook< + P extends Provider, + H extends MutationHook<any, any, any> | SWRHook<any, any, any> +>(fn: (provider: P) => H) { const { providerRef } = useCommerce<P>() const provider = providerRef.current - const opts = fn(provider) - const handler = - opts.useHook ?? - (() => { - const { fetch } = useHook(fn, fetcher) - return (input: any) => fetch({ input }) - }) - - return handler + return fn(provider) } -export default function useHook<P extends Provider>( - fn: (provider: P) => MutationHook<any, any, any>, - fetcher: any +export function useSWRHook<H extends SWRHook<any, any, any>>( + hook: PickRequired<H, 'fetcher'> ) { - const { providerRef, fetcherRef } = useCommerce<P>() - const provider = providerRef.current - const opts = fn(provider) - const fetcherFn = opts.fetcher ?? fetcher - const fetchFn = provider.fetcher ?? fetcherRef.current - const fetch = useCallback( - ({ input }: { input: any }) => { - return fetcherFn({ - input, - options: opts.fetchOptions, - fetch: fetchFn, - }) - }, - [fetchFn, opts.fetchOptions] - ) + const fetcher = useFetcher() - return { fetch } + return hook.useHook({ + useData(ctx) { + const response = useData(hook, ctx?.input ?? [], fetcher, ctx?.swrOptions) + return response + }, + }) +} + +export function useMutationHook<H extends MutationHook<any, any, any>>( + hook: PickRequired<H, 'fetcher'> +) { + const fetcher = useFetcher() + + return hook.useHook({ + fetch: useCallback( + ({ input }) => { + return hook.fetcher({ + input, + options: hook.fetchOptions, + fetch: fetcher, + }) + }, + [fetcher, hook.fetchOptions] + ), + }) } From 6721ef736b20466848dc59fce4e08fe1a312f5ea Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 19 Feb 2021 13:02:49 -0500 Subject: [PATCH 147/221] Moved data hooks to new format --- .../bigcommerce/customer/use-customer.tsx | 11 ++- framework/bigcommerce/product/use-search.tsx | 9 ++- .../bigcommerce/wishlist/use-wishlist.tsx | 13 ++-- framework/commerce/cart/use-cart.tsx | 4 +- framework/commerce/customer/use-customer.tsx | 64 ++++------------- framework/commerce/index.tsx | 6 +- framework/commerce/product/use-search.tsx | 65 ++++------------- framework/commerce/utils/default-fetcher.ts | 4 +- framework/commerce/utils/use-hook.ts | 2 +- framework/commerce/wishlist/use-wishlist.tsx | 69 +++++-------------- 10 files changed, 70 insertions(+), 177 deletions(-) diff --git a/framework/bigcommerce/customer/use-customer.tsx b/framework/bigcommerce/customer/use-customer.tsx index 3929002f7..093007824 100644 --- a/framework/bigcommerce/customer/use-customer.tsx +++ b/framework/bigcommerce/customer/use-customer.tsx @@ -1,11 +1,10 @@ -import { HookHandler } from '@commerce/utils/types' +import { SWRHook } from '@commerce/utils/types' import useCustomer, { UseCustomer } from '@commerce/customer/use-customer' import type { Customer, CustomerData } from '../api/customers' -import type { BigcommerceProvider } from '..' -export default useCustomer as UseCustomer<BigcommerceProvider> +export default useCustomer as UseCustomer<typeof handler> -export const handler: HookHandler<Customer | null> = { +export const handler: SWRHook<Customer | null> = { fetchOptions: { url: '/api/bigcommerce/customers', method: 'GET', @@ -14,11 +13,11 @@ export const handler: HookHandler<Customer | null> = { const data = await fetch<CustomerData | null>(options) return data?.customer ?? null }, - useHook({ input, useData }) { + useHook: ({ useData }) => (input) => { return useData({ swrOptions: { revalidateOnFocus: false, - ...input.swrOptions, + ...input?.swrOptions, }, }) }, diff --git a/framework/bigcommerce/product/use-search.tsx b/framework/bigcommerce/product/use-search.tsx index 0ff767d8a..0ee135032 100644 --- a/framework/bigcommerce/product/use-search.tsx +++ b/framework/bigcommerce/product/use-search.tsx @@ -1,9 +1,8 @@ -import { HookHandler } from '@commerce/utils/types' +import { SWRHook } from '@commerce/utils/types' import useSearch, { UseSearch } from '@commerce/product/use-search' import type { SearchProductsData } from '../api/catalog/products' -import type { BigcommerceProvider } from '..' -export default useSearch as UseSearch<BigcommerceProvider> +export default useSearch as UseSearch<typeof handler> export type SearchProductsInput = { search?: string @@ -12,7 +11,7 @@ export type SearchProductsInput = { sort?: string } -export const handler: HookHandler< +export const handler: SWRHook< SearchProductsData, SearchProductsInput, SearchProductsInput @@ -37,7 +36,7 @@ export const handler: HookHandler< method: options.method, }) }, - useHook({ input, useData }) { + useHook: ({ useData }) => (input = {}) => { return useData({ input: [ ['search', input.search], diff --git a/framework/bigcommerce/wishlist/use-wishlist.tsx b/framework/bigcommerce/wishlist/use-wishlist.tsx index a93f0f6a4..877a857c6 100644 --- a/framework/bigcommerce/wishlist/use-wishlist.tsx +++ b/framework/bigcommerce/wishlist/use-wishlist.tsx @@ -1,13 +1,12 @@ import { useMemo } from 'react' -import { HookHandler } from '@commerce/utils/types' +import { SWRHook } from '@commerce/utils/types' import useWishlist, { UseWishlist } from '@commerce/wishlist/use-wishlist' import type { Wishlist } from '../api/wishlist' import useCustomer from '../customer/use-customer' -import type { BigcommerceProvider } from '..' -export default useWishlist as UseWishlist<BigcommerceProvider> +export default useWishlist as UseWishlist<typeof handler> -export const handler: HookHandler< +export const handler: SWRHook< Wishlist | null, { includeProducts?: boolean }, { customerId?: number; includeProducts: boolean }, @@ -30,16 +29,16 @@ export const handler: HookHandler< method: options.method, }) }, - useHook({ input, useData }) { + useHook: ({ useData }) => (input) => { const { data: customer } = useCustomer() const response = useData({ input: [ ['customerId', (customer as any)?.id], - ['includeProducts', input.includeProducts], + ['includeProducts', input?.includeProducts], ], swrOptions: { revalidateOnFocus: false, - ...input.swrOptions, + ...input?.swrOptions, }, }) diff --git a/framework/commerce/cart/use-cart.tsx b/framework/commerce/cart/use-cart.tsx index 6053c91d7..fbed715c8 100644 --- a/framework/commerce/cart/use-cart.tsx +++ b/framework/commerce/cart/use-cart.tsx @@ -1,8 +1,8 @@ import Cookies from 'js-cookie' -import type { Cart } from '../types' +import { useHook, useSWRHook } from '../utils/use-hook' import type { HookFetcherFn, SWRHook } from '../utils/types' +import type { Cart } from '../types' import { Provider, useCommerce } from '..' -import { useHook, useSWRHook } from '@commerce/utils/use-hook' export type FetchCartInput = { cartId?: Cart['id'] diff --git a/framework/commerce/customer/use-customer.tsx b/framework/commerce/customer/use-customer.tsx index 25112128e..5d6416a4b 100644 --- a/framework/commerce/customer/use-customer.tsx +++ b/framework/commerce/customer/use-customer.tsx @@ -1,56 +1,20 @@ +import { useHook, useSWRHook } from '../utils/use-hook' +import { SWRFetcher } from '../utils/default-fetcher' +import type { HookFetcherFn, SWRHook } from '../utils/types' import type { Customer } from '../types' -import type { - Prop, - HookFetcherFn, - UseHookInput, - UseHookResponse, -} from '../utils/types' -import defaultFetcher from '../utils/default-fetcher' -import useData from '../utils/use-data' -import { Provider, useCommerce } from '..' +import { Provider } from '..' -export type UseCustomerHandler<P extends Provider> = Prop< - Prop<P, 'customer'>, - 'useCustomer' -> +export type UseCustomer< + H extends SWRHook<any, any, any> = SWRHook<Customer | null> +> = ReturnType<H['useHook']> -export type UseCustomerInput<P extends Provider> = UseHookInput< - UseCustomerHandler<P> -> +export const fetcher: HookFetcherFn<Customer | null, any> = SWRFetcher -export type CustomerResponse<P extends Provider> = UseHookResponse< - UseCustomerHandler<P> -> +const fn = (provider: Provider) => provider.customer?.useCustomer! -export type UseCustomer<P extends Provider> = Partial< - UseCustomerInput<P> -> extends UseCustomerInput<P> - ? (input?: UseCustomerInput<P>) => CustomerResponse<P> - : (input: UseCustomerInput<P>) => CustomerResponse<P> - -export const fetcher = defaultFetcher as HookFetcherFn<Customer | null> - -export default function useCustomer<P extends Provider>( - input: UseCustomerInput<P> = {} -) { - const { providerRef, fetcherRef } = useCommerce<P>() - - const provider = providerRef.current - const opts = provider.customer?.useCustomer - - const fetcherFn = opts?.fetcher ?? fetcher - const useHook = opts?.useHook ?? ((ctx) => ctx.useData()) - - return useHook({ - input, - useData(ctx) { - const response = useData( - { ...opts!, fetcher: fetcherFn }, - ctx?.input ?? [], - provider.fetcher ?? fetcherRef.current, - ctx?.swrOptions ?? input.swrOptions - ) - return response - }, - }) +const useCustomer: UseCustomer = (input) => { + const hook = useHook(fn) + return useSWRHook({ fetcher, ...hook })(input) } + +export default useCustomer diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index 6ce2c8176..baaf65406 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -21,13 +21,13 @@ export type Provider = CommerceConfig & { useRemoveItem?: MutationHook<any, any, any> } wishlist?: { - useWishlist?: HookHandler<Wishlist | null, any, any> + useWishlist?: SWRHook<Wishlist | null, any, any> } customer: { - useCustomer?: HookHandler<Customer | null, any, any> + useCustomer?: SWRHook<Customer | null, any, any> } products: { - useSearch?: HookHandler<SearchProductsData, any, any> + useSearch?: SWRHook<SearchProductsData, any, any> } } diff --git a/framework/commerce/product/use-search.tsx b/framework/commerce/product/use-search.tsx index 1f887f5fe..d2b782045 100644 --- a/framework/commerce/product/use-search.tsx +++ b/framework/commerce/product/use-search.tsx @@ -1,57 +1,20 @@ +import { useHook, useSWRHook } from '../utils/use-hook' +import { SWRFetcher } from '../utils/default-fetcher' +import type { HookFetcherFn, SWRHook } from '../utils/types' import type { SearchProductsData } from '../types' -import type { - Prop, - HookFetcherFn, - UseHookInput, - UseHookResponse, -} from '../utils/types' -import defaultFetcher from '../utils/default-fetcher' -import useData from '../utils/use-data' -import { Provider, useCommerce } from '..' -import { BigcommerceProvider } from '@framework' +import { Provider } from '..' -export type UseSearchHandler<P extends Provider> = Prop< - Prop<P, 'products'>, - 'useSearch' -> +export type UseSearch< + H extends SWRHook<any, any, any> = SWRHook<SearchProductsData> +> = ReturnType<H['useHook']> -export type UseSeachInput<P extends Provider> = UseHookInput< - UseSearchHandler<P> -> +export const fetcher: HookFetcherFn<SearchProductsData, any> = SWRFetcher -export type SearchResponse<P extends Provider> = UseHookResponse< - UseSearchHandler<P> -> +const fn = (provider: Provider) => provider.products?.useSearch! -export type UseSearch<P extends Provider> = Partial< - UseSeachInput<P> -> extends UseSeachInput<P> - ? (input?: UseSeachInput<P>) => SearchResponse<P> - : (input: UseSeachInput<P>) => SearchResponse<P> - -export const fetcher = defaultFetcher as HookFetcherFn<SearchProductsData> - -export default function useSearch<P extends Provider>( - input: UseSeachInput<P> = {} -) { - const { providerRef, fetcherRef } = useCommerce<P>() - - const provider = providerRef.current - const opts = provider.products?.useSearch - - const fetcherFn = opts?.fetcher ?? fetcher - const useHook = opts?.useHook ?? ((ctx) => ctx.useData()) - - return useHook({ - input, - useData(ctx) { - const response = useData( - { ...opts!, fetcher: fetcherFn }, - ctx?.input ?? [], - provider.fetcher ?? fetcherRef.current, - ctx?.swrOptions ?? input.swrOptions - ) - return response - }, - }) +const useSearch: UseSearch = (input) => { + const hook = useHook(fn) + return useSWRHook({ fetcher, ...hook })(input) } + +export default useSearch diff --git a/framework/commerce/utils/default-fetcher.ts b/framework/commerce/utils/default-fetcher.ts index 654da2499..493a9b5f9 100644 --- a/framework/commerce/utils/default-fetcher.ts +++ b/framework/commerce/utils/default-fetcher.ts @@ -1,6 +1,6 @@ import type { HookFetcherFn } from './types' -const defaultFetcher: HookFetcherFn<any> = ({ options, fetch }) => +export const SWRFetcher: HookFetcherFn<any, any> = ({ options, fetch }) => fetch(options) export const mutationFetcher: HookFetcherFn<any, any> = ({ @@ -9,4 +9,4 @@ export const mutationFetcher: HookFetcherFn<any, any> = ({ fetch, }) => fetch({ ...options, body: input }) -export default defaultFetcher +export default SWRFetcher diff --git a/framework/commerce/utils/use-hook.ts b/framework/commerce/utils/use-hook.ts index 830918de5..0ee1f852e 100644 --- a/framework/commerce/utils/use-hook.ts +++ b/framework/commerce/utils/use-hook.ts @@ -1,5 +1,5 @@ import { useCallback } from 'react' -import type { Fetcher, MutationHook, PickRequired, SWRHook } from './types' +import type { MutationHook, PickRequired, SWRHook } from './types' import { Provider, useCommerce } from '..' import useData from './use-data' diff --git a/framework/commerce/wishlist/use-wishlist.tsx b/framework/commerce/wishlist/use-wishlist.tsx index dc912bc98..7a93b20b1 100644 --- a/framework/commerce/wishlist/use-wishlist.tsx +++ b/framework/commerce/wishlist/use-wishlist.tsx @@ -1,56 +1,25 @@ +import { useHook, useSWRHook } from '../utils/use-hook' +import { SWRFetcher } from '../utils/default-fetcher' +import type { HookFetcherFn, SWRHook } from '../utils/types' import type { Wishlist } from '../types' -import type { - Prop, - HookFetcherFn, - UseHookInput, - UseHookResponse, -} from '../utils/types' -import defaultFetcher from '../utils/default-fetcher' -import useData from '../utils/use-data' -import { Provider, useCommerce } from '..' +import type { Provider } from '..' -export type UseWishlistHandler<P extends Provider> = Prop< - Prop<P, 'wishlist'>, - 'useWishlist' -> +export type UseWishlist< + H extends SWRHook<any, any, any> = SWRHook< + Wishlist | null, + { includeProducts?: boolean }, + { customerId?: number; includeProducts: boolean }, + { isEmpty?: boolean } + > +> = ReturnType<H['useHook']> -export type UseWishlistInput<P extends Provider> = UseHookInput< - UseWishlistHandler<P> -> +export const fetcher: HookFetcherFn<Wishlist | null, any> = SWRFetcher -export type WishlistResponse<P extends Provider> = UseHookResponse< - UseWishlistHandler<P> -> +const fn = (provider: Provider) => provider.wishlist?.useWishlist! -export type UseWishlist<P extends Provider> = Partial< - UseWishlistInput<P> -> extends UseWishlistInput<P> - ? (input?: UseWishlistInput<P>) => WishlistResponse<P> - : (input: UseWishlistInput<P>) => WishlistResponse<P> - -export const fetcher = defaultFetcher as HookFetcherFn<Wishlist | null> - -export default function useWishlist<P extends Provider>( - input: UseWishlistInput<P> = {} -) { - const { providerRef, fetcherRef } = useCommerce<P>() - - const provider = providerRef.current - const opts = provider.wishlist?.useWishlist - - const fetcherFn = opts?.fetcher ?? fetcher - const useHook = opts?.useHook ?? ((ctx) => ctx.useData()) - - return useHook({ - input, - useData(ctx) { - const response = useData( - { ...opts!, fetcher: fetcherFn }, - ctx?.input ?? [], - provider.fetcher ?? fetcherRef.current, - ctx?.swrOptions ?? input.swrOptions - ) - return response - }, - }) +const useWishlist: UseWishlist = (input) => { + const hook = useHook(fn) + return useSWRHook({ fetcher, ...hook })(input) } + +export default useWishlist From 8fc549bf556b679dc56a6ee37eafc76d06bb7dd6 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 19 Feb 2021 13:09:43 -0500 Subject: [PATCH 148/221] Removed no longer required types --- .../bigcommerce/cart/use-remove-item.tsx | 9 +++- .../bigcommerce/cart/use-update-item.tsx | 9 +++- framework/commerce/index.tsx | 2 +- framework/commerce/utils/types.ts | 52 +++---------------- framework/commerce/utils/use-data.tsx | 4 +- 5 files changed, 24 insertions(+), 52 deletions(-) diff --git a/framework/bigcommerce/cart/use-remove-item.tsx b/framework/bigcommerce/cart/use-remove-item.tsx index f279b2dc6..186780d6a 100644 --- a/framework/bigcommerce/cart/use-remove-item.tsx +++ b/framework/bigcommerce/cart/use-remove-item.tsx @@ -1,5 +1,8 @@ import { useCallback } from 'react' -import { HookContext, HookFetcherContext } from '@commerce/utils/types' +import type { + MutationHookContext, + HookFetcherContext, +} from '@commerce/utils/types' import { ValidationError } from '@commerce/utils/errors' import useRemoveItem, { RemoveItemInput as RemoveItemInputBase, @@ -40,7 +43,9 @@ export const handler = { }) return normalizeCart(data) }, - useHook: ({ fetch }: HookContext<Cart | null, RemoveCartItemBody>) => < + useHook: ({ + fetch, + }: MutationHookContext<Cart | null, RemoveCartItemBody>) => < T extends LineItem | undefined = undefined >( ctx: { item?: T } = {} diff --git a/framework/bigcommerce/cart/use-update-item.tsx b/framework/bigcommerce/cart/use-update-item.tsx index cfd6007d1..f1840f806 100644 --- a/framework/bigcommerce/cart/use-update-item.tsx +++ b/framework/bigcommerce/cart/use-update-item.tsx @@ -1,6 +1,9 @@ import { useCallback } from 'react' import debounce from 'lodash.debounce' -import type { HookContext, HookFetcherContext } from '@commerce/utils/types' +import type { + MutationHookContext, + HookFetcherContext, +} from '@commerce/utils/types' import { ValidationError } from '@commerce/utils/errors' import useUpdateItem, { UpdateItemInput as UpdateItemInputBase, @@ -54,7 +57,9 @@ export const handler = { return normalizeCart(data) }, - useHook: ({ fetch }: HookContext<Cart | null, UpdateCartItemBody>) => < + useHook: ({ + fetch, + }: MutationHookContext<Cart | null, UpdateCartItemBody>) => < T extends LineItem | undefined = undefined >( ctx: { diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index baaf65406..22b8de8ed 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -6,7 +6,7 @@ import { useMemo, useRef, } from 'react' -import { Fetcher, HookHandler, SWRHook, MutationHook } from './utils/types' +import { Fetcher, SWRHook, MutationHook } from './utils/types' import type { FetchCartInput } from './cart/use-cart' import type { Cart, Wishlist, Customer, SearchProductsData } from './types' diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 01f1c2eb3..2bab3f800 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -1,8 +1,10 @@ -import { LineItem } from '@framework/types' import type { ConfigInterface } from 'swr' import type { CommerceError } from './errors' import type { ResponseState } from './use-data' +/** + * Returns the properties in T with the properties in type K, overriding properties defined in T + */ export type Override<T, K> = Omit<T, keyof K> & K /** @@ -51,31 +53,10 @@ export type HookFetcherOptions = { method?: string } & ( export type HookInputValue = string | number | boolean | undefined -export type HookSwrInput = [string, HookInputValue][] +export type HookSWRInput = [string, HookInputValue][] export type HookFetchInput = { [k: string]: HookInputValue } -export type HookHandler< - // Data obj returned by the hook and fetch operation - Data, - // Input expected by the hook - Input extends { [k: string]: unknown } = {}, - // Input expected before doing a fetch operation - FetchInput extends HookFetchInput = {}, - // Custom state added to the response object of SWR - State = {} -> = { - useHook?(context: { - input: Input & { swrOptions?: SwrOptions<Data, FetchInput> } - useData(context?: { - input?: HookFetchInput | HookSwrInput - swrOptions?: SwrOptions<Data, FetchInput> - }): ResponseState<Data> - }): ResponseState<Data> & State - fetchOptions: HookFetcherOptions - fetcher?: HookFetcherFn<Data, FetchInput> -} - export type HookFunction< Input extends { [k: string]: unknown } | {}, T @@ -110,7 +91,7 @@ export type SWRHookContext< FetchInput extends { [k: string]: unknown } = {} > = { useData(context?: { - input?: HookFetchInput | HookSwrInput + input?: HookFetchInput | HookSWRInput swrOptions?: SwrOptions<Data, FetchInput> }): ResponseState<Data> } @@ -126,13 +107,13 @@ export type MutationHook< FetchInput extends { [k: string]: unknown } = ActionInput > = { useHook( - context: HookContext<Data, FetchInput> + context: MutationHookContext<Data, FetchInput> ): HookFunction<Input, HookFunction<ActionInput, Data | Promise<Data>>> fetchOptions: HookFetcherOptions fetcher?: HookFetcherFn<Data, FetchInput> } -export type HookContext< +export type MutationHookContext< Data, FetchInput extends { [k: string]: unknown } = {} > = { @@ -144,22 +125,3 @@ export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< CommerceError, HookFetcher<Data, Input, Result> > - -/** - * Returns the property K from type T excluding nullables - */ -export type Prop<T, K extends keyof T> = NonNullable<T[K]> - -export type HookHandlerType = HookHandler<any, any, any> - -export type UseHookParameters<H extends HookHandlerType> = Parameters< - Prop<H, 'useHook'> -> - -export type UseHookResponse<H extends HookHandlerType> = ReturnType< - Prop<H, 'useHook'> -> - -export type UseHookInput< - H extends HookHandlerType -> = UseHookParameters<H>[0]['input'] diff --git a/framework/commerce/utils/use-data.tsx b/framework/commerce/utils/use-data.tsx index fe21bd3d3..43db9b534 100644 --- a/framework/commerce/utils/use-data.tsx +++ b/framework/commerce/utils/use-data.tsx @@ -1,6 +1,6 @@ import useSWR, { responseInterface } from 'swr' import type { - HookSwrInput, + HookSWRInput, HookFetchInput, Fetcher, SwrOptions, @@ -23,7 +23,7 @@ export type UseData = < fetchOptions: HookFetcherOptions fetcher: HookFetcherFn<Data, FetchInput> }, - input: HookFetchInput | HookSwrInput, + input: HookFetchInput | HookSWRInput, fetcherFn: Fetcher, swrOptions?: SwrOptions<Data, FetchInput> ) => ResponseState<Data> From 0816bc967dd25637ca2fd49f4bdd3b68675e9241 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 19 Feb 2021 13:19:43 -0500 Subject: [PATCH 149/221] Removed useResponse helper --- framework/commerce/utils/use-response.tsx | 40 ----------------------- 1 file changed, 40 deletions(-) delete mode 100644 framework/commerce/utils/use-response.tsx diff --git a/framework/commerce/utils/use-response.tsx b/framework/commerce/utils/use-response.tsx deleted file mode 100644 index de1b5088c..000000000 --- a/framework/commerce/utils/use-response.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { useMemo } from 'react' -import { responseInterface } from 'swr' -import { CommerceError } from './errors' -import { Override } from './types' - -export type UseResponseOptions< - D, - R extends responseInterface<any, CommerceError> -> = { - descriptors?: PropertyDescriptorMap - normalizer?: (data: R['data']) => D -} - -export type UseResponse = <D, R extends responseInterface<any, CommerceError>>( - response: R, - options: UseResponseOptions<D, R> -) => D extends object ? Override<R, { data?: D }> : R - -const useResponse: UseResponse = (response, { descriptors, normalizer }) => { - const memoizedResponse = useMemo( - () => - Object.create(response, { - ...descriptors, - ...(normalizer - ? { - data: { - get() { - return response.data && normalizer(response.data) - }, - enumerable: true, - }, - } - : {}), - }), - [response] - ) - return memoizedResponse -} - -export default useResponse From 9186fe5a666f2d7dea14ee163039acb3d0d2a268 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 19 Feb 2021 13:22:41 -0500 Subject: [PATCH 150/221] Updated useData type --- framework/commerce/utils/use-data.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/framework/commerce/utils/use-data.tsx b/framework/commerce/utils/use-data.tsx index 43db9b534..9224b612c 100644 --- a/framework/commerce/utils/use-data.tsx +++ b/framework/commerce/utils/use-data.tsx @@ -14,11 +14,7 @@ export type ResponseState<Result> = responseInterface<Result, CommerceError> & { isLoading: boolean } -export type UseData = < - Data = any, - Input extends { [k: string]: unknown } = {}, - FetchInput extends HookFetchInput = {} ->( +export type UseData = <Data = any, FetchInput extends HookFetchInput = {}>( options: { fetchOptions: HookFetcherOptions fetcher: HookFetcherFn<Data, FetchInput> From f4c067982acecdc2cbd75eef601ab7fffa689c27 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 19 Feb 2021 16:44:13 -0500 Subject: [PATCH 151/221] Moved wishlist use-add-item --- .../bigcommerce/wishlist/use-add-item.tsx | 54 ++++++------------- framework/commerce/index.tsx | 2 + framework/commerce/utils/use-hook.ts | 2 +- framework/commerce/wishlist/use-add-item.tsx | 23 +++++--- 4 files changed, 33 insertions(+), 48 deletions(-) diff --git a/framework/bigcommerce/wishlist/use-add-item.tsx b/framework/bigcommerce/wishlist/use-add-item.tsx index eb961951a..402e7da8b 100644 --- a/framework/bigcommerce/wishlist/use-add-item.tsx +++ b/framework/bigcommerce/wishlist/use-add-item.tsx @@ -1,43 +1,24 @@ import { useCallback } from 'react' -import { HookFetcher } from '@commerce/utils/types' +import type { MutationHook } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' -import useWishlistAddItem, { - AddItemInput, -} from '@commerce/wishlist/use-add-item' -import { UseWishlistInput } from '@commerce/wishlist/use-wishlist' +import useAddItem, { UseAddItem } from '@commerce/wishlist/use-add-item' import type { ItemBody, AddItemBody } from '../api/wishlist' import useCustomer from '../customer/use-customer' import useWishlist from './use-wishlist' -import type { BigcommerceProvider } from '..' -const defaultOpts = { - url: '/api/bigcommerce/wishlist', - method: 'POST', -} +export default useAddItem as UseAddItem<typeof handler> -// export type AddItemInput = ItemBody - -export const fetcher: HookFetcher<any, AddItemBody> = ( - options, - { item }, - fetch -) => { - // TODO: add validations before doing the fetch - return fetch({ - ...defaultOpts, - ...options, - body: { item }, - }) -} - -export function extendHook(customFetcher: typeof fetcher) { - const useAddItem = (opts?: UseWishlistInput<BigcommerceProvider>) => { +export const handler: MutationHook<any, {}, ItemBody, AddItemBody> = { + fetchOptions: { + url: '/api/bigcommerce/wishlist', + method: 'POST', + }, + useHook: ({ fetch }) => () => { const { data: customer } = useCustomer() - const { revalidate } = useWishlist(opts) - const fn = useWishlistAddItem(defaultOpts, customFetcher) + const { revalidate } = useWishlist() return useCallback( - async function addItem(input: AddItemInput<any>) { + async function addItem(item) { if (!customer) { // A signed customer is required in order to have a wishlist throw new CommerceError({ @@ -45,17 +26,12 @@ export function extendHook(customFetcher: typeof fetcher) { }) } - const data = await fn({ item: input }) + // TODO: add validations before doing the fetch + const data = await fetch({ input: { item } }) await revalidate() return data }, - [fn, revalidate, customer] + [fetch, revalidate, customer] ) - } - - useAddItem.extend = extendHook - - return useAddItem + }, } - -export default extendHook(fetcher) diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index 22b8de8ed..bcaecc1a3 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -22,6 +22,8 @@ export type Provider = CommerceConfig & { } wishlist?: { useWishlist?: SWRHook<Wishlist | null, any, any> + useAddItem?: MutationHook<any, any, any> + useRemoveItem?: MutationHook<any, any, any> } customer: { useCustomer?: SWRHook<Customer | null, any, any> diff --git a/framework/commerce/utils/use-hook.ts b/framework/commerce/utils/use-hook.ts index 0ee1f852e..79c0808be 100644 --- a/framework/commerce/utils/use-hook.ts +++ b/framework/commerce/utils/use-hook.ts @@ -1,6 +1,6 @@ import { useCallback } from 'react' -import type { MutationHook, PickRequired, SWRHook } from './types' import { Provider, useCommerce } from '..' +import type { MutationHook, PickRequired, SWRHook } from './types' import useData from './use-data' export function useFetcher() { diff --git a/framework/commerce/wishlist/use-add-item.tsx b/framework/commerce/wishlist/use-add-item.tsx index d9b513694..11c8cc241 100644 --- a/framework/commerce/wishlist/use-add-item.tsx +++ b/framework/commerce/wishlist/use-add-item.tsx @@ -1,12 +1,19 @@ -import useAction from '../utils/use-action' -import type { CartItemBody } from '../types' +import { useHook, useMutationHook } from '../utils/use-hook' +import { mutationFetcher } from '../utils/default-fetcher' +import type { MutationHook } from '../utils/types' +import type { Provider } from '..' -// Input expected by the action returned by the `useAddItem` hook -// export interface AddItemInput { -// includeProducts?: boolean -// } -export type AddItemInput<T extends CartItemBody> = T +export type UseAddItem< + H extends MutationHook<any, any, any> = MutationHook<any, {}, {}> +> = ReturnType<H['useHook']> -const useAddItem = useAction +export const fetcher = mutationFetcher + +const fn = (provider: Provider) => provider.wishlist?.useAddItem! + +const useAddItem: UseAddItem = (...args) => { + const hook = useHook(fn) + return useMutationHook({ fetcher, ...hook })(...args) +} export default useAddItem From aa227e3de2010ccfb576fabfd4cf5e7eba950053 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 19 Feb 2021 17:04:58 -0500 Subject: [PATCH 152/221] Moved wishlist use-remove-item to provider --- .../wishlist/WishlistCard/WishlistCard.tsx | 2 +- framework/bigcommerce/provider.ts | 10 ++- .../bigcommerce/wishlist/use-remove-item.tsx | 65 +++++++------------ .../bigcommerce/wishlist/use-wishlist.tsx | 6 +- .../commerce/wishlist/use-remove-item.tsx | 27 +++++++- 5 files changed, 63 insertions(+), 47 deletions(-) diff --git a/components/wishlist/WishlistCard/WishlistCard.tsx b/components/wishlist/WishlistCard/WishlistCard.tsx index 38663ab68..5e4cce72a 100644 --- a/components/wishlist/WishlistCard/WishlistCard.tsx +++ b/components/wishlist/WishlistCard/WishlistCard.tsx @@ -22,7 +22,7 @@ const WishlistCard: FC<Props> = ({ product }) => { baseAmount: product.prices?.retailPrice?.value, currencyCode: product.prices?.price?.currencyCode!, }) - const removeItem = useRemoveItem({ includeProducts: true }) + const removeItem = useRemoveItem({ wishlist: { includeProducts: true } }) const [loading, setLoading] = useState(false) const [removing, setRemoving] = useState(false) const addItem = useAddItem() diff --git a/framework/bigcommerce/provider.ts b/framework/bigcommerce/provider.ts index e233dad04..bd2fbe34f 100644 --- a/framework/bigcommerce/provider.ts +++ b/framework/bigcommerce/provider.ts @@ -2,7 +2,11 @@ import { handler as useCart } from './cart/use-cart' import { handler as useAddItem } from './cart/use-add-item' import { handler as useUpdateItem } from './cart/use-update-item' import { handler as useRemoveItem } from './cart/use-remove-item' + import { handler as useWishlist } from './wishlist/use-wishlist' +import { handler as useWishlistAddItem } from './wishlist/use-add-item' +import { handler as useWishlistRemoveItem } from './wishlist/use-remove-item' + import { handler as useCustomer } from './customer/use-customer' import { handler as useSearch } from './product/use-search' import fetcher from './fetcher' @@ -12,7 +16,11 @@ export const bigcommerceProvider = { cartCookie: 'bc_cartId', fetcher, cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, - wishlist: { useWishlist }, + wishlist: { + useWishlist, + useAddItem: useWishlistAddItem, + useRemoveItem: useWishlistRemoveItem, + }, customer: { useCustomer }, products: { useSearch }, } diff --git a/framework/bigcommerce/wishlist/use-remove-item.tsx b/framework/bigcommerce/wishlist/use-remove-item.tsx index d00b3e78b..622f321db 100644 --- a/framework/bigcommerce/wishlist/use-remove-item.tsx +++ b/framework/bigcommerce/wishlist/use-remove-item.tsx @@ -1,43 +1,32 @@ import { useCallback } from 'react' -import { HookFetcher } from '@commerce/utils/types' +import type { MutationHook } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' -import useWishlistRemoveItem from '@commerce/wishlist/use-remove-item' -import type { RemoveItemBody } from '../api/wishlist' +import useRemoveItem, { + RemoveItemInput, + UseRemoveItem, +} from '@commerce/wishlist/use-remove-item' +import type { RemoveItemBody, Wishlist } from '../api/wishlist' import useCustomer from '../customer/use-customer' -import useWishlist from './use-wishlist' +import useWishlist, { UseWishlistInput } from './use-wishlist' -const defaultOpts = { - url: '/api/bigcommerce/wishlist', - method: 'DELETE', -} +export default useRemoveItem as UseRemoveItem<typeof handler> -export type RemoveItemInput = { - id: string | number -} - -export const fetcher: HookFetcher<any | null, RemoveItemBody> = ( - options, - { itemId }, - fetch -) => { - return fetch({ - ...defaultOpts, - ...options, - body: { itemId }, - }) -} - -export function extendHook(customFetcher: typeof fetcher) { - const useRemoveItem = (opts?: any) => { +export const handler: MutationHook< + Wishlist | null, + { wishlist?: UseWishlistInput }, + RemoveItemInput, + RemoveItemBody +> = { + fetchOptions: { + url: '/api/bigcommerce/wishlist', + method: 'DELETE', + }, + useHook: ({ fetch }) => ({ wishlist } = {}) => { const { data: customer } = useCustomer() - const { revalidate } = useWishlist(opts) - const fn = useWishlistRemoveItem<any | null, RemoveItemBody>( - defaultOpts, - customFetcher - ) + const { revalidate } = useWishlist(wishlist) return useCallback( - async function removeItem(input: RemoveItemInput) { + async function removeItem(input) { if (!customer) { // A signed customer is required in order to have a wishlist throw new CommerceError({ @@ -45,17 +34,11 @@ export function extendHook(customFetcher: typeof fetcher) { }) } - const data = await fn({ itemId: String(input.id) }) + const data = await fetch({ input: { itemId: String(input.id) } }) await revalidate() return data }, - [fn, revalidate, customer] + [fetch, revalidate, customer] ) - } - - useRemoveItem.extend = extendHook - - return useRemoveItem + }, } - -export default extendHook(fetcher) diff --git a/framework/bigcommerce/wishlist/use-wishlist.tsx b/framework/bigcommerce/wishlist/use-wishlist.tsx index 877a857c6..3efba7ffd 100644 --- a/framework/bigcommerce/wishlist/use-wishlist.tsx +++ b/framework/bigcommerce/wishlist/use-wishlist.tsx @@ -4,12 +4,14 @@ import useWishlist, { UseWishlist } from '@commerce/wishlist/use-wishlist' import type { Wishlist } from '../api/wishlist' import useCustomer from '../customer/use-customer' +export type UseWishlistInput = { includeProducts?: boolean } + export default useWishlist as UseWishlist<typeof handler> export const handler: SWRHook< Wishlist | null, - { includeProducts?: boolean }, - { customerId?: number; includeProducts: boolean }, + UseWishlistInput, + { customerId?: number } & UseWishlistInput, { isEmpty?: boolean } > = { fetchOptions: { diff --git a/framework/commerce/wishlist/use-remove-item.tsx b/framework/commerce/wishlist/use-remove-item.tsx index dfa60c363..c8c34a5af 100644 --- a/framework/commerce/wishlist/use-remove-item.tsx +++ b/framework/commerce/wishlist/use-remove-item.tsx @@ -1,5 +1,28 @@ -import useAction from '../utils/use-action' +import { useHook, useMutationHook } from '../utils/use-hook' +import { mutationFetcher } from '../utils/default-fetcher' +import type { HookFetcherFn, MutationHook } from '../utils/types' +import type { Provider } from '..' -const useRemoveItem = useAction +export type RemoveItemInput = { + id: string | number +} + +export type UseRemoveItem< + H extends MutationHook<any, any, any> = MutationHook< + any | null, + { wishlist?: any }, + RemoveItemInput, + {} + > +> = ReturnType<H['useHook']> + +export const fetcher: HookFetcherFn<any | null, {}> = mutationFetcher + +const fn = (provider: Provider) => provider.wishlist?.useRemoveItem! + +const useRemoveItem: UseRemoveItem = (input) => { + const hook = useHook(fn) + return useMutationHook({ fetcher, ...hook })(input) +} export default useRemoveItem From 7cd64d2c2a6800b6b85f1550aac88d887c7dd537 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 19 Feb 2021 19:43:15 -0500 Subject: [PATCH 153/221] Moved use-login and use-logout --- framework/bigcommerce/auth/use-login.tsx | 62 +++++++++-------------- framework/bigcommerce/auth/use-logout.tsx | 39 +++++--------- framework/bigcommerce/provider.ts | 4 ++ framework/commerce/cart/use-add-item.tsx | 10 ++-- framework/commerce/index.tsx | 9 +++- framework/commerce/use-login.tsx | 18 ++++++- framework/commerce/use-logout.tsx | 18 ++++++- framework/commerce/utils/types.ts | 14 +++-- framework/commerce/utils/use-hook.ts | 2 +- 9 files changed, 95 insertions(+), 81 deletions(-) diff --git a/framework/bigcommerce/auth/use-login.tsx b/framework/bigcommerce/auth/use-login.tsx index fa2294666..b66fca493 100644 --- a/framework/bigcommerce/auth/use-login.tsx +++ b/framework/bigcommerce/auth/use-login.tsx @@ -1,54 +1,40 @@ import { useCallback } from 'react' -import type { HookFetcher } from '@commerce/utils/types' +import type { MutationHook } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' -import useCommerceLogin from '@commerce/use-login' +import useLogin, { UseLogin } from '@commerce/use-login' import type { LoginBody } from '../api/customers/login' import useCustomer from '../customer/use-customer' -const defaultOpts = { - url: '/api/bigcommerce/customers/login', - method: 'POST', -} +export default useLogin as UseLogin<typeof handler> -export type LoginInput = LoginBody +export const handler: MutationHook<null, {}, LoginBody> = { + fetchOptions: { + url: '/api/bigcommerce/customers/login', + method: 'POST', + }, + async fetcher({ input: { email, password }, options, fetch }) { + if (!(email && password)) { + throw new CommerceError({ + message: + 'A first name, last name, email and password are required to login', + }) + } -export const fetcher: HookFetcher<null, LoginBody> = ( - options, - { email, password }, - fetch -) => { - if (!(email && password)) { - throw new CommerceError({ - message: - 'A first name, last name, email and password are required to login', + return fetch({ + ...options, + body: { email, password }, }) - } - - return fetch({ - ...defaultOpts, - ...options, - body: { email, password }, - }) -} - -export function extendHook(customFetcher: typeof fetcher) { - const useLogin = () => { + }, + useHook: ({ fetch }) => () => { const { revalidate } = useCustomer() - const fn = useCommerceLogin<null, LoginInput>(defaultOpts, customFetcher) return useCallback( - async function login(input: LoginInput) { - const data = await fn(input) + async function login(input) { + const data = await fetch({ input }) await revalidate() return data }, - [fn] + [fetch, revalidate] ) - } - - useLogin.extend = extendHook - - return useLogin + }, } - -export default extendHook(fetcher) diff --git a/framework/bigcommerce/auth/use-logout.tsx b/framework/bigcommerce/auth/use-logout.tsx index 6aaee29f9..6278a4dd1 100644 --- a/framework/bigcommerce/auth/use-logout.tsx +++ b/framework/bigcommerce/auth/use-logout.tsx @@ -1,38 +1,25 @@ import { useCallback } from 'react' -import type { HookFetcher } from '@commerce/utils/types' -import useCommerceLogout from '@commerce/use-logout' +import type { MutationHook } from '@commerce/utils/types' +import useLogout, { UseLogout } from '@commerce/use-logout' import useCustomer from '../customer/use-customer' -const defaultOpts = { - url: '/api/bigcommerce/customers/logout', - method: 'GET', -} +export default useLogout as UseLogout<typeof handler> -export const fetcher: HookFetcher<null> = (options, _, fetch) => { - return fetch({ - ...defaultOpts, - ...options, - }) -} - -export function extendHook(customFetcher: typeof fetcher) { - const useLogout = () => { +export const handler: MutationHook<null> = { + fetchOptions: { + url: '/api/bigcommerce/customers/logout', + method: 'GET', + }, + useHook: ({ fetch }) => () => { const { mutate } = useCustomer() - const fn = useCommerceLogout<null>(defaultOpts, customFetcher) return useCallback( - async function login() { - const data = await fn(null) + async function logout() { + const data = await fetch() await mutate(null, false) return data }, - [fn] + [fetch, mutate] ) - } - - useLogout.extend = extendHook - - return useLogout + }, } - -export default extendHook(fetcher) diff --git a/framework/bigcommerce/provider.ts b/framework/bigcommerce/provider.ts index bd2fbe34f..40bdc2c06 100644 --- a/framework/bigcommerce/provider.ts +++ b/framework/bigcommerce/provider.ts @@ -9,6 +9,9 @@ import { handler as useWishlistRemoveItem } from './wishlist/use-remove-item' import { handler as useCustomer } from './customer/use-customer' import { handler as useSearch } from './product/use-search' + +import { handler as useLogin } from './auth/use-login' + import fetcher from './fetcher' export const bigcommerceProvider = { @@ -23,6 +26,7 @@ export const bigcommerceProvider = { }, customer: { useCustomer }, products: { useSearch }, + auth: { useLogin }, } export type BigcommerceProvider = typeof bigcommerceProvider diff --git a/framework/commerce/cart/use-add-item.tsx b/framework/commerce/cart/use-add-item.tsx index 13da6b416..324464656 100644 --- a/framework/commerce/cart/use-add-item.tsx +++ b/framework/commerce/cart/use-add-item.tsx @@ -1,18 +1,18 @@ import { useHook, useMutationHook } from '../utils/use-hook' import { mutationFetcher } from '../utils/default-fetcher' -import type { MutationHook, HookFetcherFn } from '../utils/types' +import type { HookFetcherFn, MutationHook } from '../utils/types' import type { Cart, CartItemBody, AddCartItemBody } from '../types' import type { Provider } from '..' +export type UseAddItem< + H extends MutationHook<any, any, any> = MutationHook<Cart, {}, CartItemBody> +> = ReturnType<H['useHook']> + export const fetcher: HookFetcherFn< Cart, AddCartItemBody<CartItemBody> > = mutationFetcher -export type UseAddItem< - H extends MutationHook<any, any, any> = MutationHook<Cart, {}, CartItemBody> -> = ReturnType<H['useHook']> - const fn = (provider: Provider) => provider.cart?.useAddItem! const useAddItem: UseAddItem = (...args) => { diff --git a/framework/commerce/index.tsx b/framework/commerce/index.tsx index bcaecc1a3..07bf74a22 100644 --- a/framework/commerce/index.tsx +++ b/framework/commerce/index.tsx @@ -25,12 +25,17 @@ export type Provider = CommerceConfig & { useAddItem?: MutationHook<any, any, any> useRemoveItem?: MutationHook<any, any, any> } - customer: { + customer?: { useCustomer?: SWRHook<Customer | null, any, any> } - products: { + products?: { useSearch?: SWRHook<SearchProductsData, any, any> } + auth?: { + useSignup?: MutationHook<any, any, any> + useLogin?: MutationHook<any, any, any> + useLogout?: MutationHook<any, any, any> + } } export type CommerceProps<P extends Provider> = { diff --git a/framework/commerce/use-login.tsx b/framework/commerce/use-login.tsx index 2a251fea3..755e10fd9 100644 --- a/framework/commerce/use-login.tsx +++ b/framework/commerce/use-login.tsx @@ -1,5 +1,19 @@ -import useAction from './utils/use-action' +import { useHook, useMutationHook } from './utils/use-hook' +import { mutationFetcher } from './utils/default-fetcher' +import type { MutationHook, HookFetcherFn } from './utils/types' +import type { Provider } from '.' -const useLogin = useAction +export type UseLogin< + H extends MutationHook<any, any, any> = MutationHook<null, {}, {}> +> = ReturnType<H['useHook']> + +export const fetcher: HookFetcherFn<null, {}> = mutationFetcher + +const fn = (provider: Provider) => provider.auth?.useLogin! + +const useLogin: UseLogin = (...args) => { + const hook = useHook(fn) + return useMutationHook({ fetcher, ...hook })(...args) +} export default useLogin diff --git a/framework/commerce/use-logout.tsx b/framework/commerce/use-logout.tsx index ef3fc4135..0a80c318b 100644 --- a/framework/commerce/use-logout.tsx +++ b/framework/commerce/use-logout.tsx @@ -1,5 +1,19 @@ -import useAction from './utils/use-action' +import { useHook, useMutationHook } from './utils/use-hook' +import { mutationFetcher } from './utils/default-fetcher' +import type { HookFetcherFn, MutationHook } from './utils/types' +import type { Provider } from '.' -const useLogout = useAction +export type UseLogout< + H extends MutationHook<any, any, any> = MutationHook<null> +> = ReturnType<H['useHook']> + +export const fetcher: HookFetcherFn<null> = mutationFetcher + +const fn = (provider: Provider) => provider.auth?.useLogout! + +const useLogout: UseLogout = (...args) => { + const hook = useHook(fn) + return useMutationHook({ fetcher, ...hook })(...args) +} export default useLogout diff --git a/framework/commerce/utils/types.ts b/framework/commerce/utils/types.ts index 2bab3f800..852afb208 100644 --- a/framework/commerce/utils/types.ts +++ b/framework/commerce/utils/types.ts @@ -36,11 +36,11 @@ export type HookFetcher<Data, Input = null, Result = any> = ( fetch: <T = Result, Body = any>(options: FetcherOptions<Body>) => Promise<T> ) => Data | Promise<Data> -export type HookFetcherFn<Data, Input = never, Result = any, Body = any> = ( +export type HookFetcherFn<Data, Input = undefined, Result = any, Body = any> = ( context: HookFetcherContext<Input, Result, Body> ) => Data | Promise<Data> -export type HookFetcherContext<Input = never, Result = any, Body = any> = { +export type HookFetcherContext<Input = undefined, Result = any, Body = any> = { options: HookFetcherOptions input: Input fetch: <T = Result, B = Body>(options: FetcherOptions<B>) => Promise<T> @@ -58,7 +58,7 @@ export type HookSWRInput = [string, HookInputValue][] export type HookFetchInput = { [k: string]: HookInputValue } export type HookFunction< - Input extends { [k: string]: unknown } | {}, + Input extends { [k: string]: unknown } | null, T > = keyof Input extends never ? () => T @@ -115,9 +115,13 @@ export type MutationHook< export type MutationHookContext< Data, - FetchInput extends { [k: string]: unknown } = {} + FetchInput extends { [k: string]: unknown } | null = {} > = { - fetch: (context: { input: FetchInput }) => Data | Promise<Data> + fetch: keyof FetchInput extends never + ? () => Data | Promise<Data> + : Partial<FetchInput> extends FetchInput + ? (context?: { input?: FetchInput }) => Data | Promise<Data> + : (context: { input: FetchInput }) => Data | Promise<Data> } export type SwrOptions<Data, Input = null, Result = any> = ConfigInterface< diff --git a/framework/commerce/utils/use-hook.ts b/framework/commerce/utils/use-hook.ts index 79c0808be..da3431e3c 100644 --- a/framework/commerce/utils/use-hook.ts +++ b/framework/commerce/utils/use-hook.ts @@ -37,7 +37,7 @@ export function useMutationHook<H extends MutationHook<any, any, any>>( return hook.useHook({ fetch: useCallback( - ({ input }) => { + ({ input } = {}) => { return hook.fetcher({ input, options: hook.fetchOptions, From 683794bb330d523e4fc4beeeec6a74de193ed968 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 19 Feb 2021 20:20:41 -0500 Subject: [PATCH 154/221] Update use-signup --- framework/bigcommerce/auth/use-signup.tsx | 66 ++++++++++------------- framework/bigcommerce/provider.ts | 4 +- framework/commerce/use-signup.tsx | 18 ++++++- 3 files changed, 47 insertions(+), 41 deletions(-) diff --git a/framework/bigcommerce/auth/use-signup.tsx b/framework/bigcommerce/auth/use-signup.tsx index c68ce7b7a..23b7ce9c6 100644 --- a/framework/bigcommerce/auth/use-signup.tsx +++ b/framework/bigcommerce/auth/use-signup.tsx @@ -1,54 +1,44 @@ import { useCallback } from 'react' -import type { HookFetcher } from '@commerce/utils/types' +import type { MutationHook } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' -import useCommerceSignup from '@commerce/use-signup' +import useSignup, { UseSignup } from '@commerce/use-signup' import type { SignupBody } from '../api/customers/signup' import useCustomer from '../customer/use-customer' -const defaultOpts = { - url: '/api/bigcommerce/customers/signup', - method: 'POST', -} +export default useSignup as UseSignup<typeof handler> -export type SignupInput = SignupBody +export const handler: MutationHook<null, {}, SignupBody, SignupBody> = { + fetchOptions: { + url: '/api/bigcommerce/customers/signup', + method: 'POST', + }, + async fetcher({ + input: { firstName, lastName, email, password }, + options, + fetch, + }) { + if (!(firstName && lastName && email && password)) { + throw new CommerceError({ + message: + 'A first name, last name, email and password are required to signup', + }) + } -export const fetcher: HookFetcher<null, SignupBody> = ( - options, - { firstName, lastName, email, password }, - fetch -) => { - if (!(firstName && lastName && email && password)) { - throw new CommerceError({ - message: - 'A first name, last name, email and password are required to signup', + return fetch({ + ...options, + body: { firstName, lastName, email, password }, }) - } - - return fetch({ - ...defaultOpts, - ...options, - body: { firstName, lastName, email, password }, - }) -} - -export function extendHook(customFetcher: typeof fetcher) { - const useSignup = () => { + }, + useHook: ({ fetch }) => () => { const { revalidate } = useCustomer() - const fn = useCommerceSignup<null, SignupInput>(defaultOpts, customFetcher) return useCallback( - async function signup(input: SignupInput) { - const data = await fn(input) + async function signup(input) { + const data = await fetch({ input }) await revalidate() return data }, - [fn] + [fetch, revalidate] ) - } - - useSignup.extend = extendHook - - return useSignup + }, } - -export default extendHook(fetcher) diff --git a/framework/bigcommerce/provider.ts b/framework/bigcommerce/provider.ts index 40bdc2c06..196855438 100644 --- a/framework/bigcommerce/provider.ts +++ b/framework/bigcommerce/provider.ts @@ -11,6 +11,8 @@ import { handler as useCustomer } from './customer/use-customer' import { handler as useSearch } from './product/use-search' import { handler as useLogin } from './auth/use-login' +import { handler as useLogout } from './auth/use-logout' +import { handler as useSignup } from './auth/use-signup' import fetcher from './fetcher' @@ -26,7 +28,7 @@ export const bigcommerceProvider = { }, customer: { useCustomer }, products: { useSearch }, - auth: { useLogin }, + auth: { useLogin, useLogout, useSignup }, } export type BigcommerceProvider = typeof bigcommerceProvider diff --git a/framework/commerce/use-signup.tsx b/framework/commerce/use-signup.tsx index 08ddb22c0..be3c32000 100644 --- a/framework/commerce/use-signup.tsx +++ b/framework/commerce/use-signup.tsx @@ -1,5 +1,19 @@ -import useAction from './utils/use-action' +import { useHook, useMutationHook } from './utils/use-hook' +import { mutationFetcher } from './utils/default-fetcher' +import type { HookFetcherFn, MutationHook } from './utils/types' +import type { Provider } from '.' -const useSignup = useAction +export type UseSignup< + H extends MutationHook<any, any, any> = MutationHook<null> +> = ReturnType<H['useHook']> + +export const fetcher: HookFetcherFn<null> = mutationFetcher + +const fn = (provider: Provider) => provider.auth?.useSignup! + +const useSignup: UseSignup = (...args) => { + const hook = useHook(fn) + return useMutationHook({ fetcher, ...hook })(...args) +} export default useSignup From 4b4d804d03648cee4cb023f85cb5078afff89286 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 19 Feb 2021 21:55:18 -0500 Subject: [PATCH 155/221] Removed use-action helper --- framework/commerce/utils/use-action.tsx | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 framework/commerce/utils/use-action.tsx diff --git a/framework/commerce/utils/use-action.tsx b/framework/commerce/utils/use-action.tsx deleted file mode 100644 index 24593383f..000000000 --- a/framework/commerce/utils/use-action.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { useCallback } from 'react' -import type { HookFetcher, HookFetcherOptions } from './types' -import { useCommerce } from '..' - -export default function useAction<T, Input = null>( - options: HookFetcherOptions, - fetcher: HookFetcher<T, Input> -) { - const { fetcherRef } = useCommerce() - - return useCallback( - (input: Input) => fetcher(options, input, fetcherRef.current), - [fetcher] - ) -} From 528d7556a8ef8b473776ed7dd000857219aed43b Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Mon, 22 Feb 2021 14:06:34 +0200 Subject: [PATCH 156/221] Moved auth & cart hooks + several fixes --- assets/base.css | 8 +- assets/components.css | 2 +- components/common/Avatar/Avatar.tsx | 13 +- .../HomeAllProductsGrid.tsx | 16 +- components/common/Layout/Layout.tsx | 2 +- .../common/Searchbar/Searchbar.module.css | 2 +- components/common/UserNav/UserNav.module.css | 6 +- .../ProductCard/ProductCard.module.css | 2 +- .../product/ProductCard/ProductCard.tsx | 4 +- .../ProductSlider/ProductSlider.module.css | 4 +- components/ui/Button/Button.module.css | 2 +- components/ui/Input/Input.module.css | 2 +- components/ui/context.tsx | 6 + .../api/utils/fetch-graphql-api.ts | 1 + framework/bigcommerce/product/get-product.ts | 2 +- .../shopify/api/utils/fetch-all-products.ts | 4 +- .../shopify/api/utils/fetch-graphql-api.ts | 2 +- framework/shopify/auth/use-login.tsx | 102 ++++++++----- framework/shopify/auth/use-logout.tsx | 50 +++---- framework/shopify/auth/use-signup.tsx | 83 +++++------ framework/shopify/cart/use-add-item.tsx | 39 ++--- framework/shopify/cart/use-cart.tsx | 32 +++- framework/shopify/cart/use-remove-item.tsx | 89 ++++++------ framework/shopify/cart/use-update-item.tsx | 137 +++++++++++------- .../shopify/cart/utils/checkout-create.ts | 2 +- .../shopify/cart/utils/checkout-to-cart.ts | 10 +- framework/shopify/cart/utils/fetcher.ts | 2 +- framework/shopify/cart/utils/index.ts | 1 - framework/shopify/common/get-all-pages.ts | 2 +- framework/shopify/common/get-page.ts | 2 +- framework/shopify/config.json | 5 + framework/shopify/customer/get-customer-id.ts | 2 +- framework/shopify/customer/use-customer.tsx | 18 ++- .../shopify/product/get-all-collections.ts | 2 +- framework/shopify/product/get-all-products.ts | 2 +- framework/shopify/product/get-product.ts | 2 +- framework/shopify/product/use-search.tsx | 16 +- framework/shopify/provider.ts | 16 +- framework/shopify/utils/get-categories.ts | 2 +- framework/shopify/utils/handle-login.ts | 2 +- framework/shopify/utils/index.ts | 1 - framework/shopify/utils/mutations/index.ts | 11 +- .../utils/queries/get-checkout-query.ts | 2 +- framework/shopify/utils/queries/index.ts | 2 +- lib/hooks/useUserAvatar.ts | 26 ++++ lib/logger.ts | 18 --- next.config.js | 3 - pages/api/bigcommerce/customers/index.ts | 2 +- pages/index.tsx | 2 +- pages/search.tsx | 6 +- tailwind.config.js | 3 +- tsconfig.json | 4 +- yarn.lock | 2 +- 53 files changed, 447 insertions(+), 331 deletions(-) create mode 100644 framework/shopify/config.json create mode 100644 lib/hooks/useUserAvatar.ts delete mode 100644 lib/logger.ts diff --git a/assets/base.css b/assets/base.css index f854065ba..dfdaf1475 100644 --- a/assets/base.css +++ b/assets/base.css @@ -3,7 +3,6 @@ --primary-2: #f1f3f5; --secondary: #000000; --secondary-2: #111; - --selection: var(--cyan); --text-base: #000000; @@ -13,18 +12,14 @@ --hover: rgba(0, 0, 0, 0.075); --hover-1: rgba(0, 0, 0, 0.15); --hover-2: rgba(0, 0, 0, 0.25); - --cyan: #22b8cf; --green: #37b679; --red: #da3c3c; --pink: #e64980; --purple: #f81ce5; - --blue: #0070f3; - - --violet-light: #7048e8; --violet: #5f3dc4; - + --violet-light: #7048e8; --accents-0: #f8f9fa; --accents-1: #f1f3f5; --accents-2: #e9ecef; @@ -132,3 +127,4 @@ a { opacity: 1; } } + diff --git a/assets/components.css b/assets/components.css index ebebcc238..8c4c5a357 100644 --- a/assets/components.css +++ b/assets/components.css @@ -1,3 +1,3 @@ .fit { min-height: calc(100vh - 88px); -} +} \ No newline at end of file diff --git a/components/common/Avatar/Avatar.tsx b/components/common/Avatar/Avatar.tsx index 351a117ec..f78aa1d01 100644 --- a/components/common/Avatar/Avatar.tsx +++ b/components/common/Avatar/Avatar.tsx @@ -1,5 +1,5 @@ -import { FC, useState, useMemo, useRef, useEffect } from 'react' -import { getRandomPairOfColors } from '@lib/colors' +import { FC, useRef, useEffect } from 'react' +import { useUserAvatar } from '@lib/hooks/useUserAvatar' interface Props { className?: string @@ -7,18 +7,13 @@ interface Props { } const Avatar: FC<Props> = ({}) => { - const [bg] = useState(useMemo(() => getRandomPairOfColors, [])) let ref = useRef() as React.MutableRefObject<HTMLInputElement> - - useEffect(() => { - if (ref && ref.current) { - ref.current.style.backgroundImage = `linear-gradient(140deg, ${bg[0]}, ${bg[1]} 100%)` - } - }, [bg]) + let { userAvatar } = useUserAvatar() return ( <div ref={ref} + style={{ backgroundImage: userAvatar }} className="inline-block h-8 w-8 rounded-full border-2 border-primary hover:border-secondary focus:border-secondary transition linear-out duration-150" > {/* Add an image - We're generating a gradient as placeholder <img></img> */} diff --git a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx index cafcdd1d3..4b838e1a4 100644 --- a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx +++ b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx @@ -27,7 +27,7 @@ const HomeAllProductsGrid: FC<Props> = ({ <ul className="mb-10"> <li className="py-1 text-base font-bold tracking-wide"> <Link href={getCategoryPath('')}> - <a>All Collections</a> + <a>All Categories</a> </Link> </li> {categories.map((cat: any) => ( @@ -38,6 +38,20 @@ const HomeAllProductsGrid: FC<Props> = ({ </li> ))} </ul> + <ul className=""> + <li className="py-1 text-base font-bold tracking-wide"> + <Link href={getDesignerPath('')}> + <a>All Designers</a> + </Link> + </li> + {brands.flatMap(({ node }: any) => ( + <li key={node.path} className="py-1 text-accents-8 text-base"> + <Link href={getDesignerPath(node.path)}> + <a>{node.name}</a> + </Link> + </li> + ))} + </ul> </div> </div> <div className="flex-1"> diff --git a/components/common/Layout/Layout.tsx b/components/common/Layout/Layout.tsx index f4376bbf3..2542e2b28 100644 --- a/components/common/Layout/Layout.tsx +++ b/components/common/Layout/Layout.tsx @@ -58,7 +58,7 @@ const Layout: FC<Props> = ({ } = useUI() const { acceptedCookies, onAcceptCookies } = useAcceptCookies() const { locale = 'en-US' } = useRouter() - const isWishlistEnabled = commerceFeatures.wishlist + const isWishlistEnabled = commerceFeatures?.wishlist return ( <CommerceProvider locale={locale}> <div className={cn(s.root)}> diff --git a/components/common/Searchbar/Searchbar.module.css b/components/common/Searchbar/Searchbar.module.css index 500483195..071a14ef0 100644 --- a/components/common/Searchbar/Searchbar.module.css +++ b/components/common/Searchbar/Searchbar.module.css @@ -7,7 +7,7 @@ } .input:focus { - @apply outline-none shadow-outline-2; + @apply outline-none shadow-outline-normal; } .iconContainer { diff --git a/components/common/UserNav/UserNav.module.css b/components/common/UserNav/UserNav.module.css index a319e3dac..cd1a6ce1f 100644 --- a/components/common/UserNav/UserNav.module.css +++ b/components/common/UserNav/UserNav.module.css @@ -24,7 +24,11 @@ } .bagCount { - @apply border border-accents-1 bg-secondary text-secondary h-4 w-4 absolute rounded-full right-3 top-3 flex items-center justify-center font-bold text-xs; + @apply border border-accents-1 bg-secondary text-secondary absolute rounded-full right-3 top-3 flex items-center justify-center font-bold text-xs; + padding-left: 2.5px; + padding-right: 2.5px; + min-width: 1.25rem; + min-height: 1.25rem; } .avatarButton { diff --git a/components/product/ProductCard/ProductCard.module.css b/components/product/ProductCard/ProductCard.module.css index 082b22e56..1484cfaa4 100644 --- a/components/product/ProductCard/ProductCard.module.css +++ b/components/product/ProductCard/ProductCard.module.css @@ -132,5 +132,5 @@ } .productImage { - @apply transform transition-transform duration-500 object-cover; + @apply transform transition-transform duration-500 object-cover scale-120; } diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index b97937cd6..a9eaf8568 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -28,8 +28,8 @@ const ProductCard: FC<Props> = ({ <a className={cn(s.root, { [s.simple]: variant === 'simple' }, className)}> {variant === 'slim' ? ( <div className="relative overflow-hidden box-border"> - <div className="absolute inset-0 flex items-start justify-end m-1 z-20"> - <span className="text-black inline-block p-3 font-bold text-xl break-words"> + <div className="absolute inset-0 flex items-center justify-end mr-8 z-20"> + <span className="bg-black text-white inline-block p-3 font-bold text-xl break-words"> {product.name} </span> </div> diff --git a/components/product/ProductSlider/ProductSlider.module.css b/components/product/ProductSlider/ProductSlider.module.css index 5ad48cc3d..259d15801 100644 --- a/components/product/ProductSlider/ProductSlider.module.css +++ b/components/product/ProductSlider/ProductSlider.module.css @@ -15,7 +15,7 @@ .leftControl:hover, .rightControl:hover { - @apply outline-none shadow-outline-blue; + @apply outline-none shadow-outline-normal; } .leftControl { @@ -70,7 +70,7 @@ } .positionIndicator:focus .dot { - @apply shadow-outline-blue; + @apply shadow-outline-normal; } .positionIndicatorActive .dot { diff --git a/components/ui/Button/Button.module.css b/components/ui/Button/Button.module.css index df2be8802..5b563f496 100644 --- a/components/ui/Button/Button.module.css +++ b/components/ui/Button/Button.module.css @@ -7,7 +7,7 @@ } .root:focus { - @apply shadow-outline outline-none; + @apply shadow-outline-normal outline-none; } .root[data-active] { diff --git a/components/ui/Input/Input.module.css b/components/ui/Input/Input.module.css index 9ace85277..9daee1418 100644 --- a/components/ui/Input/Input.module.css +++ b/components/ui/Input/Input.module.css @@ -3,5 +3,5 @@ } .root:focus { - @apply outline-none shadow-outline-gray; + @apply outline-none shadow-outline-normal; } diff --git a/components/ui/context.tsx b/components/ui/context.tsx index b5321ae50..013589941 100644 --- a/components/ui/context.tsx +++ b/components/ui/context.tsx @@ -127,6 +127,12 @@ function uiReducer(state: State, action: Action) { toastText: action.text, } } + case 'SET_USER_AVATAR': { + return { + ...state, + userAvatar: action.value, + } + } } } diff --git a/framework/bigcommerce/api/utils/fetch-graphql-api.ts b/framework/bigcommerce/api/utils/fetch-graphql-api.ts index aa211dd88..a449b81e0 100644 --- a/framework/bigcommerce/api/utils/fetch-graphql-api.ts +++ b/framework/bigcommerce/api/utils/fetch-graphql-api.ts @@ -8,6 +8,7 @@ const fetchGraphqlApi: GraphQLFetcher = async ( { variables, preview } = {}, fetchOptions ) => { + // log.warn(query) const config = getConfig() const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), { ...fetchOptions, diff --git a/framework/bigcommerce/product/get-product.ts b/framework/bigcommerce/product/get-product.ts index 794d89bdf..7d77eb194 100644 --- a/framework/bigcommerce/product/get-product.ts +++ b/framework/bigcommerce/product/get-product.ts @@ -2,7 +2,7 @@ import type { GetProductQuery, GetProductQueryVariables } from '../schema' import setProductLocaleMeta from '../api/utils/set-product-locale-meta' import { productInfoFragment } from '../api/fragments/product' import { BigcommerceConfig, getConfig } from '../api' -import { normalizeProduct } from '@framework/utils/normalize' +import { normalizeProduct } from '@framework/lib/normalize' import type { Product } from '@commerce/types' export const getProductQuery = /* GraphQL */ ` diff --git a/framework/shopify/api/utils/fetch-all-products.ts b/framework/shopify/api/utils/fetch-all-products.ts index 0f6660cd5..efeb809f1 100644 --- a/framework/shopify/api/utils/fetch-all-products.ts +++ b/framework/shopify/api/utils/fetch-all-products.ts @@ -18,8 +18,8 @@ const fetchAllProducts = async ({ variables: { ...variables, cursor }, }) - const edges: ProductEdge[] = data?.products?.edges ?? [] - const hasNextPage = data?.products?.pageInfo?.hasNextPage + const edges: ProductEdge[] = data.products?.edges ?? [] + const hasNextPage = data.products?.pageInfo?.hasNextPage acc = acc.concat(edges) if (hasNextPage) { diff --git a/framework/shopify/api/utils/fetch-graphql-api.ts b/framework/shopify/api/utils/fetch-graphql-api.ts index a78eeed74..92d4f2cf6 100644 --- a/framework/shopify/api/utils/fetch-graphql-api.ts +++ b/framework/shopify/api/utils/fetch-graphql-api.ts @@ -29,6 +29,6 @@ const fetchGraphqlApi: GraphQLFetcher = async ( throw getError(errors, status) } - return { data: data, res } + return { data, res } } export default fetchGraphqlApi diff --git a/framework/shopify/auth/use-login.tsx b/framework/shopify/auth/use-login.tsx index 4811bbe8d..9ed8d404f 100644 --- a/framework/shopify/auth/use-login.tsx +++ b/framework/shopify/auth/use-login.tsx @@ -1,56 +1,80 @@ import { useCallback } from 'react' -import type { HookFetcher } from '@commerce/utils/types' +import type { MutationHook } from '@commerce/utils/types' import { CommerceError, ValidationError } from '@commerce/utils/errors' -import useCommerceLogin from '@commerce/use-login' import useCustomer from '../customer/use-customer' import createCustomerAccessTokenMutation from '../utils/mutations/customer-access-token-create' -import { CustomerAccessTokenCreateInput } from '@framework/schema' -import handleLogin from '@framework/utils/handle-login' +import { + CustomerAccessToken, + CustomerAccessTokenCreateInput, + CustomerAccessTokenCreatePayload, + CustomerUserError, + Mutation, + MutationCheckoutCreateArgs, +} from '@framework/schema' +import useLogin, { UseLogin } from '@commerce/use-login' +import { setCustomerToken } from '@framework/utils' -const defaultOpts = { - query: createCustomerAccessTokenMutation, -} +export default useLogin as UseLogin<typeof handler> -export const fetcher: HookFetcher<null, CustomerAccessTokenCreateInput> = ( - options, - input, - fetch -) => { - if (!(input.email && input.password)) { - throw new CommerceError({ - message: - 'A first name, last name, email and password are required to login', - }) +const getErrorMessage = ({ code, message }: CustomerUserError) => { + console.log(code) + + switch (code) { + case 'UNIDENTIFIED_CUSTOMER': + message = 'Cannot find an account that matches the provided credentials' + break } - - return fetch({ - ...defaultOpts, - ...options, - variables: { input }, - }).then(handleLogin) + return message } -export function extendHook(customFetcher: typeof fetcher) { - const useLogin = () => { +export const handler: MutationHook<null, {}, CustomerAccessTokenCreateInput> = { + fetchOptions: { + query: createCustomerAccessTokenMutation, + }, + async fetcher({ input: { email, password }, options, fetch }) { + if (!(email && password)) { + throw new CommerceError({ + message: + 'A first name, last name, email and password are required to login', + }) + } + + const { customerAccessTokenCreate } = await fetch< + Mutation, + MutationCheckoutCreateArgs + >({ + ...options, + variables: { + input: { email, password }, + }, + }) + + const errors = customerAccessTokenCreate?.customerUserErrors + + if (errors && errors.length) { + throw new ValidationError({ + message: getErrorMessage(errors[0]), + }) + } + const customerAccessToken = customerAccessTokenCreate?.customerAccessToken + const accessToken = customerAccessToken?.accessToken + + if (accessToken) { + setCustomerToken(accessToken) + } + + return null + }, + useHook: ({ fetch }) => () => { const { revalidate } = useCustomer() - const fn = useCommerceLogin<null, CustomerAccessTokenCreateInput>( - defaultOpts, - customFetcher - ) return useCallback( - async function login(input: CustomerAccessTokenCreateInput) { - const data = await fn(input) + async function login(input) { + const data = await fetch({ input }) await revalidate() return data }, - [fn] + [fetch, revalidate] ) - } - - useLogin.extend = extendHook - - return useLogin + }, } - -export default extendHook(fetcher) diff --git a/framework/shopify/auth/use-logout.tsx b/framework/shopify/auth/use-logout.tsx index f8bd55579..d5bf5f6b0 100644 --- a/framework/shopify/auth/use-logout.tsx +++ b/framework/shopify/auth/use-logout.tsx @@ -1,6 +1,6 @@ import { useCallback } from 'react' -import type { HookFetcher } from '@commerce/utils/types' -import useCommerceLogout from '@commerce/use-logout' +import type { MutationHook } from '@commerce/utils/types' +import useLogout, { UseLogout } from '@commerce/use-logout' import useCustomer from '../customer/use-customer' import customerAccessTokenDeleteMutation from '@framework/utils/mutations/customer-access-token-delete' import { @@ -8,38 +8,32 @@ import { setCustomerToken, } from '@framework/utils/customer-token' -const defaultOpts = { - query: customerAccessTokenDeleteMutation, -} +export default useLogout as UseLogout<typeof handler> -export const fetcher: HookFetcher<null> = (options, _, fetch) => { - return fetch({ - ...defaultOpts, - ...options, - variables: { - customerAccessToken: getCustomerToken(), - }, - }).then((d) => setCustomerToken(null)) -} - -export function extendHook(customFetcher: typeof fetcher) { - const useLogout = () => { +export const handler: MutationHook<null> = { + fetchOptions: { + query: customerAccessTokenDeleteMutation, + }, + async fetcher({ options, fetch }) { + await fetch({ + ...options, + variables: { + customerAccessToken: getCustomerToken(), + }, + }) + setCustomerToken(null) + return null + }, + useHook: ({ fetch }) => () => { const { mutate } = useCustomer() - const fn = useCommerceLogout<null>(defaultOpts, customFetcher) return useCallback( - async function login() { - const data = await fn(null) + async function logout() { + const data = await fetch() await mutate(null, false) return data }, - [fn] + [fetch, mutate] ) - } - - useLogout.extend = extendHook - - return useLogout + }, } - -export default extendHook(fetcher) diff --git a/framework/shopify/auth/use-signup.tsx b/framework/shopify/auth/use-signup.tsx index c1b40e60c..7ad9e996d 100644 --- a/framework/shopify/auth/use-signup.tsx +++ b/framework/shopify/auth/use-signup.tsx @@ -1,7 +1,7 @@ import { useCallback } from 'react' -import type { HookFetcher } from '@commerce/utils/types' +import type { MutationHook } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' -import useCommerceSignup from '@commerce/use-signup' +import useSignup, { UseSignup } from '@commerce/use-signup' import useCustomer from '../customer/use-customer' import { CustomerCreateInput } from '@framework/schema' @@ -11,63 +11,64 @@ import { } from '@framework/utils/mutations' import handleLogin from '@framework/utils/handle-login' -const defaultOpts = { - query: customerCreateMutation, -} +export default useSignup as UseSignup<typeof handler> -export const fetcher: HookFetcher<null, CustomerCreateInput> = ( - options, - input, - fetch -) => { - if (!(input.firstName && input.lastName && input.email && input.password)) { - throw new CommerceError({ - message: - 'A first name, last name, email and password are required to signup', +export const handler: MutationHook< + null, + {}, + CustomerCreateInput, + CustomerCreateInput +> = { + fetchOptions: { + query: customerCreateMutation, + }, + async fetcher({ + input: { firstName, lastName, email, password }, + options, + fetch, + }) { + if (!(firstName && lastName && email && password)) { + throw new CommerceError({ + message: + 'A first name, last name, email and password are required to signup', + }) + } + const data = await fetch({ + ...options, + variables: { + input: { + firstName, + lastName, + email, + password, + }, + }, }) - } - return fetch({ - ...defaultOpts, - ...options, - variables: { input }, - }).then(async (data) => { + try { const loginData = await fetch({ query: customerAccessTokenCreateMutation, variables: { input: { - email: input.email, - password: input.password, + email, + password, }, }, }) handleLogin(loginData) } catch (error) {} return data - }) -} - -export function extendHook(customFetcher: typeof fetcher) { - const useSignup = () => { + }, + useHook: ({ fetch }) => () => { const { revalidate } = useCustomer() - const fn = useCommerceSignup<null, CustomerCreateInput>( - defaultOpts, - customFetcher - ) return useCallback( - async function signup(input: CustomerCreateInput) { - const data = await fn(input) + async function signup(input) { + const data = await fetch({ input }) await revalidate() return data }, - [fn] + [fetch, revalidate] ) - } - - useSignup.extend = extendHook - - return useSignup + }, } - -export default extendHook(fetcher) diff --git a/framework/shopify/cart/use-add-item.tsx b/framework/shopify/cart/use-add-item.tsx index 11771f1b3..36f02847b 100644 --- a/framework/shopify/cart/use-add-item.tsx +++ b/framework/shopify/cart/use-add-item.tsx @@ -1,22 +1,20 @@ -import type { MutationHandler } from '@commerce/utils/types' +import type { MutationHook } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item' import useCart from './use-cart' -import { ShopifyProvider } from '..' -import { Cart, AddCartItemBody, CartItemBody } from '../types' +import { Cart, CartItemBody } from '../types' import { checkoutLineItemAddMutation, getCheckoutId } from '../utils' import { checkoutToCart } from './utils' -import { Mutation } from '../schema' +import { Mutation, MutationCheckoutLineItemsAddArgs } from '../schema' +import { useCallback } from 'react' -export default useAddItem as UseAddItem<ShopifyProvider, CartItemBody> +export default useAddItem as UseAddItem<typeof handler> -export const handler: MutationHandler<Cart, {}, AddCartItemBody> = { +export const handler: MutationHook<Cart, {}, CartItemBody> = { fetchOptions: { query: checkoutLineItemAddMutation, }, - async fetcher({ input, options, fetch }) { - const item = input?.item ?? input - + async fetcher({ input: item, options, fetch }) { if ( item.quantity && (!Number.isInteger(item.quantity) || item.quantity! < 1) @@ -26,27 +24,34 @@ export const handler: MutationHandler<Cart, {}, AddCartItemBody> = { }) } - const { checkoutLineItemsAdd }: Mutation = await fetch<any, any>({ + const { checkoutLineItemsAdd } = await fetch< + Mutation, + MutationCheckoutLineItemsAddArgs + >({ ...options, variables: { + checkoutId: getCheckoutId(), lineItems: [ { variantId: item.variantId, quantity: item.quantity ?? 1, }, ], - checkoutId: getCheckoutId(), }, }) return checkoutToCart(checkoutLineItemsAdd) }, - useHook() { + useHook: ({ fetch }) => () => { const { mutate } = useCart() - return async function addItem({ input, fetch }) { - const data = await fetch({ input }) - await mutate(data, false) - return data - } + + return useCallback( + async function addItem(input) { + const data = await fetch({ input }) + await mutate(data, false) + return data + }, + [fetch, mutate] + ) }, } diff --git a/framework/shopify/cart/use-cart.tsx b/framework/shopify/cart/use-cart.tsx index f5749731f..2cf3a3e95 100644 --- a/framework/shopify/cart/use-cart.tsx +++ b/framework/shopify/cart/use-cart.tsx @@ -7,14 +7,13 @@ import useCommerceCart, { } from '@commerce/cart/use-cart' import { Cart } from '@commerce/types' -import { HookHandler } from '@commerce/utils/types' - -import fetcher from './utils/fetcher' -import getCheckoutQuery from '@framework/utils/queries/get-checkout-query' +import { SWRHook } from '@commerce/utils/types' +import { checkoutCreate, checkoutToCart } from './utils' +import getCheckoutQuery from '../utils/queries/get-checkout-query' export default useCommerceCart as UseCart<ShopifyProvider> -export const handler: HookHandler< +export const handler: SWRHook< Cart | null, {}, FetchCartInput, @@ -23,10 +22,27 @@ export const handler: HookHandler< fetchOptions: { query: getCheckoutQuery, }, - fetcher, - useHook({ input, useData }) { + async fetcher({ input: { cartId: checkoutId }, options, fetch }) { + let checkout + if (checkoutId) { + const data = await fetch({ + ...options, + variables: { + checkoutId, + }, + }) + checkout = data.node + } + + if (checkout?.completedAt || !checkoutId) { + checkout = await checkoutCreate(fetch) + } + + return checkoutToCart({ checkout }) + }, + useHook: ({ useData }) => (input) => { const response = useData({ - swrOptions: { revalidateOnFocus: false, ...input.swrOptions }, + swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, }) return useMemo( () => diff --git a/framework/shopify/cart/use-remove-item.tsx b/framework/shopify/cart/use-remove-item.tsx index 5c2c2dbee..1963176c8 100644 --- a/framework/shopify/cart/use-remove-item.tsx +++ b/framework/shopify/cart/use-remove-item.tsx @@ -1,48 +1,61 @@ import { useCallback } from 'react' -import { HookFetcher } from '@commerce/utils/types' + +import type { + MutationHookContext, + HookFetcherContext, +} from '@commerce/utils/types' + import { ValidationError } from '@commerce/utils/errors' -import useCartRemoveItem, { - RemoveItemInput as UseRemoveItemInput, + +import useRemoveItem, { + RemoveItemInput as RemoveItemInputBase, + UseRemoveItem, } from '@commerce/cart/use-remove-item' import useCart from './use-cart' - -import type { Cart, LineItem, RemoveCartItemBody } from '@commerce/types' -import { checkoutLineItemRemoveMutation } from '@framework/utils/mutations' -import getCheckoutId from '@framework/utils/get-checkout-id' +import { checkoutLineItemRemoveMutation, getCheckoutId } from '@framework/utils' import { checkoutToCart } from './utils' - -const defaultOpts = { - query: checkoutLineItemRemoveMutation, -} +import { Cart, LineItem } from '@framework/types' +import { + Mutation, + MutationCheckoutLineItemsRemoveArgs, +} from '@framework/schema' +import { RemoveCartItemBody } from '@commerce/types' export type RemoveItemFn<T = any> = T extends LineItem ? (input?: RemoveItemInput<T>) => Promise<Cart | null> : (input: RemoveItemInput<T>) => Promise<Cart | null> export type RemoveItemInput<T = any> = T extends LineItem - ? Partial<UseRemoveItemInput> - : UseRemoveItemInput + ? Partial<RemoveItemInputBase> + : RemoveItemInputBase -export const fetcher: HookFetcher<Cart | null, any> = async ( - options, - { itemId, checkoutId }, - fetch -) => { - const data = await fetch<any>({ - ...defaultOpts, - ...options, - variables: { lineItemIds: [itemId], checkoutId }, - }) - return checkoutToCart(data.checkoutLineItemsRemove) -} +export default useRemoveItem as UseRemoveItem<typeof handler> -export function extendHook(customFetcher: typeof fetcher) { - const useRemoveItem = <T extends LineItem | undefined = undefined>( - item?: T +export const handler = { + fetchOptions: { + query: checkoutLineItemRemoveMutation, + }, + async fetcher({ + input: { itemId }, + options, + fetch, + }: HookFetcherContext<RemoveCartItemBody>) { + const data = await fetch<Mutation, MutationCheckoutLineItemsRemoveArgs>({ + ...options, + variables: { checkoutId: getCheckoutId(), lineItemIds: [itemId] }, + }) + return checkoutToCart(data.checkoutLineItemsRemove) + }, + useHook: ({ + fetch, + }: MutationHookContext<Cart | null, RemoveCartItemBody>) => < + T extends LineItem | undefined = undefined + >( + ctx: { item?: T } = {} ) => { - const { mutate, data: cart } = useCart() - const fn = useCartRemoveItem<Cart | null, any>(defaultOpts, customFetcher) + const { item } = ctx + const { mutate } = useCart() const removeItem: RemoveItemFn<LineItem> = async (input) => { const itemId = input?.id ?? item?.id @@ -52,21 +65,11 @@ export function extendHook(customFetcher: typeof fetcher) { }) } - const data = await fn({ - checkoutId: getCheckoutId(cart?.id), - itemId, - }) - + const data = await fetch({ input: { itemId } }) await mutate(data, false) return data } - return useCallback(removeItem as RemoveItemFn<T>, [fn, mutate]) - } - - useRemoveItem.extend = extendHook - - return useRemoveItem + return useCallback(removeItem as RemoveItemFn<T>, [fetch, mutate]) + }, } - -export default extendHook(fetcher) diff --git a/framework/shopify/cart/use-update-item.tsx b/framework/shopify/cart/use-update-item.tsx index 2b5e7c776..9e89d0aca 100644 --- a/framework/shopify/cart/use-update-item.tsx +++ b/framework/shopify/cart/use-update-item.tsx @@ -1,85 +1,110 @@ import { useCallback } from 'react' import debounce from 'lodash.debounce' -import type { HookFetcher } from '@commerce/utils/types' +import type { + HookFetcherContext, + MutationHookContext, +} from '@commerce/utils/types' import { ValidationError } from '@commerce/utils/errors' -import useCartUpdateItem, { - UpdateItemInput as UseUpdateItemInput, +import useUpdateItem, { + UpdateItemInput as UpdateItemInputBase, + UseUpdateItem, } from '@commerce/cart/use-update-item' -import { fetcher as removeFetcher } from './use-remove-item' - import useCart from './use-cart' - -import type { Cart, LineItem, UpdateCartItemBody } from '@commerce/types' +import { handler as removeItemHandler } from './use-remove-item' +import type { Cart, LineItem, UpdateCartItemBody } from '../types' import { checkoutToCart } from './utils' -import checkoutLineItemUpdateMutation from '@framework/utils/mutations/checkout-line-item-update' -import getCheckoutId from '@framework/utils/get-checkout-id' - -const defaultOpts = { - query: checkoutLineItemUpdateMutation, -} +import { getCheckoutId, checkoutLineItemUpdateMutation } from '../utils' +import { + Mutation, + MutationCheckoutLineItemsUpdateArgs, +} from '@framework/schema' export type UpdateItemInput<T = any> = T extends LineItem - ? Partial<UseUpdateItemInput<LineItem>> - : UseUpdateItemInput<LineItem> + ? Partial<UpdateItemInputBase<LineItem>> + : UpdateItemInputBase<LineItem> -export const fetcher: HookFetcher<Cart | null, any> = async ( - options, - { item, checkoutId }, - fetch -) => { - if (Number.isInteger(item.quantity)) { - // Also allow the update hook to remove an item if the quantity is lower than 1 - if (item.quantity! < 1) { - return removeFetcher(null, { itemId: item.id, checkoutId }, fetch) +export default useUpdateItem as UseUpdateItem<typeof handler> + +export const handler = { + fetchOptions: { + query: checkoutLineItemUpdateMutation, + }, + async fetcher({ + input: { itemId, item }, + options, + fetch, + }: HookFetcherContext<UpdateCartItemBody>) { + if (Number.isInteger(item.quantity)) { + // Also allow the update hook to remove an item if the quantity is lower than 1 + if (item.quantity! < 1) { + return removeItemHandler.fetcher({ + options: removeItemHandler.fetchOptions, + input: { itemId }, + fetch, + }) + } + } else if (item.quantity) { + throw new ValidationError({ + message: 'The item quantity has to be a valid integer', + }) } - } else if (item.quantity) { - throw new ValidationError({ - message: 'The item quantity has to be a valid integer', + const { checkoutLineItemsUpdate } = await fetch< + Mutation, + MutationCheckoutLineItemsUpdateArgs + >({ + ...options, + variables: { + checkoutId: getCheckoutId(), + lineItems: [ + { + id: itemId, + quantity: item.quantity, + }, + ], + }, }) - } - const data = await fetch<any, any>({ - ...defaultOpts, - ...options, - variables: { checkoutId, lineItems: [item] }, - }) - return checkoutToCart(data.checkoutLineItemsUpdate) -} - -function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) { - const useUpdateItem = <T extends LineItem | undefined = undefined>( - item?: T + return checkoutToCart(checkoutLineItemsUpdate) + }, + useHook: ({ + fetch, + }: MutationHookContext<Cart | null, UpdateCartItemBody>) => < + T extends LineItem | undefined = undefined + >( + ctx: { + item?: T + wait?: number + } = {} ) => { - const { mutate, data: cart } = useCart() - const fn = useCartUpdateItem<Cart | null, any>(defaultOpts, customFetcher) + const { item } = ctx + const { mutate } = useCart() as any return useCallback( debounce(async (input: UpdateItemInput<T>) => { const itemId = input.id ?? item?.id + const productId = input.productId ?? item?.productId const variantId = input.productId ?? item?.variantId - - if (!itemId || !variantId) { + if (!itemId || !productId || !variantId) { throw new ValidationError({ message: 'Invalid input used for this operation', }) } - const data = await fn({ - item: { id: itemId, variantId, quantity: input.quantity }, - checkoutId: getCheckoutId(cart?.id), + const data = await fetch({ + input: { + item: { + productId, + variantId, + quantity: input.quantity, + }, + itemId, + }, }) - await mutate(data, false) return data - }, cfg?.wait ?? 500), - [fn, mutate] + }, ctx.wait ?? 500), + [fetch, mutate] ) - } - - useUpdateItem.extend = extendHook - - return useUpdateItem + }, } - -export default extendHook(fetcher) diff --git a/framework/shopify/cart/utils/checkout-create.ts b/framework/shopify/cart/utils/checkout-create.ts index 9975befe8..0e71be62f 100644 --- a/framework/shopify/cart/utils/checkout-create.ts +++ b/framework/shopify/cart/utils/checkout-create.ts @@ -11,7 +11,7 @@ export const checkoutCreate = async (fetch: any) => { query: checkoutCreateMutation, }) - const checkout = data?.checkoutCreate?.checkout + const checkout = data.checkoutCreate?.checkout const checkoutId = checkout?.id if (checkoutId) { diff --git a/framework/shopify/cart/utils/checkout-to-cart.ts b/framework/shopify/cart/utils/checkout-to-cart.ts index 42d797652..662db1c45 100644 --- a/framework/shopify/cart/utils/checkout-to-cart.ts +++ b/framework/shopify/cart/utils/checkout-to-cart.ts @@ -3,6 +3,7 @@ import { CommerceError, ValidationError } from '@commerce/utils/errors' import { CheckoutLineItemsAddPayload, + CheckoutLineItemsRemovePayload, CheckoutLineItemsUpdatePayload, Maybe, } from '@framework/schema' @@ -11,9 +12,10 @@ import { normalizeCart } from '@framework/utils' export type CheckoutPayload = | CheckoutLineItemsAddPayload | CheckoutLineItemsUpdatePayload + | CheckoutLineItemsRemovePayload const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => { - if (!checkoutPayload || !checkoutPayload?.checkout) { + if (!checkoutPayload) { throw new CommerceError({ message: 'Invalid response from Shopify', }) @@ -28,6 +30,12 @@ const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => { }) } + if (!checkout) { + throw new CommerceError({ + message: 'Invalid response from Shopify', + }) + } + return normalizeCart(checkout) } diff --git a/framework/shopify/cart/utils/fetcher.ts b/framework/shopify/cart/utils/fetcher.ts index 372860734..a69492f0d 100644 --- a/framework/shopify/cart/utils/fetcher.ts +++ b/framework/shopify/cart/utils/fetcher.ts @@ -17,7 +17,7 @@ const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ checkoutId, }, }) - checkout = data?.node + checkout = data.node } if (checkout?.completedAt || !checkoutId) { diff --git a/framework/shopify/cart/utils/index.ts b/framework/shopify/cart/utils/index.ts index 0f2b4a6ca..20d04955d 100644 --- a/framework/shopify/cart/utils/index.ts +++ b/framework/shopify/cart/utils/index.ts @@ -1,3 +1,2 @@ export { default as checkoutToCart } from './checkout-to-cart' export { default as checkoutCreate } from './checkout-create' -export { default as fetcher } from './fetcher' diff --git a/framework/shopify/common/get-all-pages.ts b/framework/shopify/common/get-all-pages.ts index 6cec2ef73..082950634 100644 --- a/framework/shopify/common/get-all-pages.ts +++ b/framework/shopify/common/get-all-pages.ts @@ -19,7 +19,7 @@ const getAllPages = async (options?: { config = getConfig(config) const { data } = await config.fetch(getAllPagesQuery, { variables }) - const edges = data?.pages?.edges + const edges = data.pages?.edges const pages = edges?.map(({ node }: PageEdge) => ({ ...node, diff --git a/framework/shopify/common/get-page.ts b/framework/shopify/common/get-page.ts index 803272918..524cece77 100644 --- a/framework/shopify/common/get-page.ts +++ b/framework/shopify/common/get-page.ts @@ -24,7 +24,7 @@ const getPage = async (options: { variables, }) - const page: Page = data?.pageByHandle + const page: Page = data.pageByHandle return { page: page diff --git a/framework/shopify/config.json b/framework/shopify/config.json new file mode 100644 index 000000000..17ef37e25 --- /dev/null +++ b/framework/shopify/config.json @@ -0,0 +1,5 @@ +{ + "features": { + "wishlist": false + } +} diff --git a/framework/shopify/customer/get-customer-id.ts b/framework/shopify/customer/get-customer-id.ts index 39a9e2572..78309a8ec 100644 --- a/framework/shopify/customer/get-customer-id.ts +++ b/framework/shopify/customer/get-customer-id.ts @@ -18,7 +18,7 @@ async function getCustomerId({ }, }) - return data?.customer?.id + return data.customer?.id } export default getCustomerId diff --git a/framework/shopify/customer/use-customer.tsx b/framework/shopify/customer/use-customer.tsx index 55151801b..91b7281af 100644 --- a/framework/shopify/customer/use-customer.tsx +++ b/framework/shopify/customer/use-customer.tsx @@ -1,24 +1,26 @@ import useCustomer, { UseCustomer } from '@commerce/customer/use-customer' import { Customer } from '@commerce/types' -import { HookHandler } from '@commerce/utils/types' -import { getCustomerQuery } from '@framework/utils' +import { SWRHook } from '@commerce/utils/types' +import { getCustomerQuery, getCustomerToken } from '../utils' import type { ShopifyProvider } from '..' export default useCustomer as UseCustomer<ShopifyProvider> - -export const handler: HookHandler<Customer | null> = { +export const handler: SWRHook<Customer | null> = { fetchOptions: { query: getCustomerQuery, }, async fetcher({ options, fetch }) { - const data = await fetch<any | null>(options) - return data?.customer ?? null + const data = await fetch<any | null>({ + ...options, + variables: { customerAccessToken: getCustomerToken() }, + }) + return data.customer ?? null }, - useHook({ input, useData }) { + useHook: ({ useData }) => (input) => { return useData({ swrOptions: { revalidateOnFocus: false, - ...input.swrOptions, + ...input?.swrOptions, }, }) }, diff --git a/framework/shopify/product/get-all-collections.ts b/framework/shopify/product/get-all-collections.ts index b63adf159..bf3fee392 100644 --- a/framework/shopify/product/get-all-collections.ts +++ b/framework/shopify/product/get-all-collections.ts @@ -11,7 +11,7 @@ const getAllCollections = async (options?: { config = getConfig(config) const { data } = await config.fetch(getAllCollectionsQuery, { variables }) - const edges = data?.collections?.edges ?? [] + const edges = data.collections?.edges ?? [] const categories = edges.map( ({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({ diff --git a/framework/shopify/product/get-all-products.ts b/framework/shopify/product/get-all-products.ts index 34480e90a..b777efc10 100644 --- a/framework/shopify/product/get-all-products.ts +++ b/framework/shopify/product/get-all-products.ts @@ -28,7 +28,7 @@ const getAllProducts = async (options: { { variables } ) - const products = data?.products?.edges?.map(({ node: p }: ProductEdge) => + const products = data.products?.edges?.map(({ node: p }: ProductEdge) => normalizeProduct(p) ) diff --git a/framework/shopify/product/get-product.ts b/framework/shopify/product/get-product.ts index 191706123..33f38a427 100644 --- a/framework/shopify/product/get-product.ts +++ b/framework/shopify/product/get-product.ts @@ -27,7 +27,7 @@ const getProduct = async (options: { variables, }) - const product = data?.productByHandle + const { productByHandle: product } = data return { product: product ? normalizeProduct(product) : null, diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx index 174466fdb..7ca37916b 100644 --- a/framework/shopify/product/use-search.tsx +++ b/framework/shopify/product/use-search.tsx @@ -1,6 +1,6 @@ -import useSearch, { UseSearch } from '@commerce/products/use-search' -import { SearchProductsData } from '@commerce/types' -import { HookHandler } from '@commerce/utils/types' +import { SWRHook } from '@commerce/utils/types' +import useSearch, { UseSearch } from '@commerce/product/use-search' + import { ProductEdge } from '@framework/schema' import { getAllProductsQuery, @@ -9,6 +9,8 @@ import { } from '@framework/utils' import type { ShopifyProvider } from '..' +import { Product } from '@commerce/types' + export default useSearch as UseSearch<ShopifyProvider> export type SearchProductsInput = { @@ -18,7 +20,11 @@ export type SearchProductsInput = { sort?: string } -export const handler: HookHandler< +export type SearchProductsData = { + products: Product[] + found: boolean +} +export const handler: SWRHook< SearchProductsData, SearchProductsInput, SearchProductsInput @@ -38,7 +44,7 @@ export const handler: HookHandler< found: !!edges?.length, } }, - useHook({ input, useData }) { + useHook: ({ useData }) => (input = {}) => { return useData({ input: [ ['search', input.search], diff --git a/framework/shopify/provider.ts b/framework/shopify/provider.ts index f11e9ff3f..383822baa 100644 --- a/framework/shopify/provider.ts +++ b/framework/shopify/provider.ts @@ -2,8 +2,16 @@ import { SHOPIFY_CHECKOUT_ID_COOKIE, STORE_DOMAIN } from './const' import { handler as useCart } from './cart/use-cart' import { handler as useAddItem } from './cart/use-add-item' -import { handler as useSearch } from './product/use-search' +import { handler as useUpdateItem } from './cart/use-update-item' +import { handler as useRemoveItem } from './cart/use-remove-item' + import { handler as useCustomer } from './customer/use-customer' +import { handler as useSearch } from './product/use-search' + +import { handler as useLogin } from './auth/use-login' +import { handler as useLogout } from './auth/use-logout' +import { handler as useSignup } from './auth/use-signup' + import fetcher from './fetcher' export const shopifyProvider = { @@ -11,9 +19,13 @@ export const shopifyProvider = { cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, storeDomain: STORE_DOMAIN, fetcher, - cart: { useCart, useAddItem }, + cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, customer: { useCustomer }, products: { useSearch }, + auth: { useLogin, useLogout, useSignup }, + features: { + wishlist: false, + }, } export type ShopifyProvider = typeof shopifyProvider diff --git a/framework/shopify/utils/get-categories.ts b/framework/shopify/utils/get-categories.ts index 3319d827d..942ec9c62 100644 --- a/framework/shopify/utils/get-categories.ts +++ b/framework/shopify/utils/get-categories.ts @@ -16,7 +16,7 @@ const getCategories = async (config: ShopifyConfig): Promise<Category[]> => { }) return ( - data?.collections?.edges?.map( + data.collections?.edges?.map( ({ node: { title: name, handle } }: CollectionEdge) => ({ entityId: handle, name, diff --git a/framework/shopify/utils/handle-login.ts b/framework/shopify/utils/handle-login.ts index 70725b75b..77b6873e3 100644 --- a/framework/shopify/utils/handle-login.ts +++ b/framework/shopify/utils/handle-login.ts @@ -17,7 +17,7 @@ const getErrorMessage = ({ } const handleLogin = (data: any) => { - const response = data?.customerAccessTokenCreate + const response = data.customerAccessTokenCreate const errors = response?.customerUserErrors if (errors && errors.length) { diff --git a/framework/shopify/utils/index.ts b/framework/shopify/utils/index.ts index 99aa9be68..2d59aa506 100644 --- a/framework/shopify/utils/index.ts +++ b/framework/shopify/utils/index.ts @@ -4,7 +4,6 @@ export { default as getSortVariables } from './get-sort-variables' export { default as getVendors } from './get-vendors' export { default as getCategories } from './get-categories' export { default as getCheckoutId } from './get-checkout-id' - export * from './queries' export * from './mutations' export * from './normalize' diff --git a/framework/shopify/utils/mutations/index.ts b/framework/shopify/utils/mutations/index.ts index c9c6ee100..3a16d7cec 100644 --- a/framework/shopify/utils/mutations/index.ts +++ b/framework/shopify/utils/mutations/index.ts @@ -1,10 +1,7 @@ -export { default as createCustomerMutation } from './customer-create' -export { default as checkoutCreateMutation } from './checkout-create' - -export { default as checkoutLineItemAddMutation } from './checkout-line-item-add' -export { default as checkoutLineItemUpdateMutation } from './checkout-create' -export { default as checkoutLineItemRemoveMutation } from './checkout-line-item-remove' - export { default as customerCreateMutation } from './customer-create' +export { default as checkoutCreateMutation } from './checkout-create' +export { default as checkoutLineItemAddMutation } from './checkout-line-item-add' +export { default as checkoutLineItemUpdateMutation } from './checkout-line-item-update' +export { default as checkoutLineItemRemoveMutation } from './checkout-line-item-remove' export { default as customerAccessTokenCreateMutation } from './customer-access-token-create' export { default as customerAccessTokenDeleteMutation } from './customer-access-token-delete' diff --git a/framework/shopify/utils/queries/get-checkout-query.ts b/framework/shopify/utils/queries/get-checkout-query.ts index b582ca3bb..f25542329 100644 --- a/framework/shopify/utils/queries/get-checkout-query.ts +++ b/framework/shopify/utils/queries/get-checkout-query.ts @@ -1,4 +1,4 @@ -export const checkoutDetailsFragment = /* GraphQL */ ` +export const checkoutDetailsFragment = ` id webUrl subtotalPrice diff --git a/framework/shopify/utils/queries/index.ts b/framework/shopify/utils/queries/index.ts index f41cb2797..4f10d7b65 100644 --- a/framework/shopify/utils/queries/index.ts +++ b/framework/shopify/utils/queries/index.ts @@ -7,4 +7,4 @@ export { default as getCollectionProductsQuery } from './get-collection-products export { default as getCheckoutQuery } from './get-checkout-query' export { default as getAllPagesQuery } from './get-all-pages-query' export { default as getPageQuery } from './get-page-query' -export { default as getCustomerQuery } from './get-checkout-query' +export { default as getCustomerQuery } from './get-customer-query' diff --git a/lib/hooks/useUserAvatar.ts b/lib/hooks/useUserAvatar.ts new file mode 100644 index 000000000..840daae6d --- /dev/null +++ b/lib/hooks/useUserAvatar.ts @@ -0,0 +1,26 @@ +import { useEffect } from 'react' +import { useUI } from '@components/ui/context' +import { getRandomPairOfColors } from '@lib/colors' + +export const useUserAvatar = (name = 'userAvatar') => { + const { userAvatar, setUserAvatar } = useUI() + + useEffect(() => { + if (!userAvatar && localStorage.getItem(name)) { + // Get bg from localStorage and push it to the context. + setUserAvatar(localStorage.getItem(name)) + } + if (!localStorage.getItem(name)) { + // bg not set locally, generating one, setting localStorage and context to persist. + const bg = getRandomPairOfColors() + const value = `linear-gradient(140deg, ${bg[0]}, ${bg[1]} 100%)` + localStorage.setItem(name, value) + setUserAvatar(value) + } + }, []) + + return { + userAvatar, + setUserAvatar, + } +} diff --git a/lib/logger.ts b/lib/logger.ts deleted file mode 100644 index eeda2c325..000000000 --- a/lib/logger.ts +++ /dev/null @@ -1,18 +0,0 @@ -import bunyan from 'bunyan' -import PrettyStream from 'bunyan-prettystream' - -const prettyStdOut = new PrettyStream() - -const log = bunyan.createLogger({ - name: 'Next.js - Commerce', - level: 'debug', - streams: [ - { - level: 'debug', - type: 'raw', - stream: prettyStdOut, - }, - ], -}) - -export default log diff --git a/next.config.js b/next.config.js index abdfe251c..3c9e37210 100644 --- a/next.config.js +++ b/next.config.js @@ -34,7 +34,4 @@ module.exports = { }, ] }, - typescript: { - ignoreBuildErrors: true, - }, } diff --git a/pages/api/bigcommerce/customers/index.ts b/pages/api/bigcommerce/customers/index.ts index 911a4521a..7b55d3aa8 100644 --- a/pages/api/bigcommerce/customers/index.ts +++ b/pages/api/bigcommerce/customers/index.ts @@ -1,3 +1,3 @@ -import customersApi from '@framework/api/customer' +import customersApi from '@framework/api/customers' export default customersApi() diff --git a/pages/index.tsx b/pages/index.tsx index 8f6ce5f2a..acb1474be 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -36,7 +36,7 @@ export async function getStaticProps({ wishlist: isWishlistEnabled, }, }, - revalidate: 1440, + revalidate: 14400, } } diff --git a/pages/search.tsx b/pages/search.tsx index 7c0a4e140..c9958a9f8 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -106,7 +106,7 @@ export default function Search({ <button type="button" onClick={(e) => handleClick(e, 'categories')} - className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150" + className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-normal active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150" id="options-menu" aria-haspopup="true" aria-expanded="true" @@ -205,7 +205,7 @@ export default function Search({ <button type="button" onClick={(e) => handleClick(e, 'brands')} - className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-900 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150" + className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-900 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-normal active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150" id="options-menu" aria-haspopup="true" aria-expanded="true" @@ -383,7 +383,7 @@ export default function Search({ <button type="button" onClick={(e) => handleClick(e, 'sort')} - className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150" + className="flex justify-between w-full rounded-sm border border-gray-300 px-4 py-3 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-normal active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150" id="options-menu" aria-haspopup="true" aria-expanded="true" diff --git a/tailwind.config.js b/tailwind.config.js index 4793c44a1..1ee8cad1d 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -51,7 +51,7 @@ module.exports = { secondary: 'var(--text-secondary)', }, boxShadow: { - 'outline-2': '0 0 0 2px var(--accents-2)', + 'outline-normal': '0 0 0 2px var(--accents-2)', magical: 'rgba(0, 0, 0, 0.02) 0px 30px 30px, rgba(0, 0, 0, 0.03) 0px 0px 8px, rgba(0, 0, 0, 0.05) 0px 1px 0px', }, @@ -63,5 +63,4 @@ module.exports = { }, }, }, - plugins: [require('@tailwindcss/ui')], } diff --git a/tsconfig.json b/tsconfig.json index 7aec4729c..43dfd2a27 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,8 +22,8 @@ "@utils/*": ["utils/*"], "@commerce/*": ["framework/commerce/*"], "@commerce": ["framework/commerce"], - "@framework/*": ["framework/shopify/*"], - "@framework": ["framework/shopify"] + "@framework/*": ["framework/bigcommerce/*"], + "@framework": ["framework/bigcommerce"] } }, "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], diff --git a/yarn.lock b/yarn.lock index aad5af97d..e7cd08438 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7257,4 +7257,4 @@ yn@3.1.1: yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== \ No newline at end of file + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 849b0275f073bd35d30d3d146d26bff1374949f2 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Mon, 22 Feb 2021 15:00:32 +0200 Subject: [PATCH 157/221] Updated cart item, fixed deprecations --- framework/shopify/types.ts | 4 ++- framework/shopify/utils/normalize.ts | 29 ++++++++++++------- .../utils/queries/get-checkout-query.ts | 25 ++++++++++++---- .../utils/queries/get-product-query.ts | 10 +++++-- tsconfig.json | 4 +-- 5 files changed, 52 insertions(+), 20 deletions(-) diff --git a/framework/shopify/types.ts b/framework/shopify/types.ts index 6ebfdf01d..9ad9fd016 100644 --- a/framework/shopify/types.ts +++ b/framework/shopify/types.ts @@ -12,7 +12,9 @@ export interface Cart extends Core.Cart { lineItems: LineItem[] } -export interface LineItem extends Core.LineItem {} +export interface LineItem extends Core.LineItem { + options: any[] +} /** * Cart mutations diff --git a/framework/shopify/utils/normalize.ts b/framework/shopify/utils/normalize.ts index 79074bf24..df8c7402b 100644 --- a/framework/shopify/utils/normalize.ts +++ b/framework/shopify/utils/normalize.ts @@ -40,8 +40,9 @@ const normalizeProductImages = ({ edges }: ImageConnection) => ...rest, })) -const normalizeProductVariants = ({ edges }: ProductVariantConnection) => - edges?.map(({ node: { id, selectedOptions } }) => ({ +const normalizeProductVariants = ({ edges }: ProductVariantConnection) => { + console.log(edges) + return edges?.map(({ node: { id, selectedOptions } }) => ({ id, options: selectedOptions.map(({ name, value }: SelectedOption) => normalizeProductOption({ @@ -51,6 +52,7 @@ const normalizeProductVariants = ({ edges }: ProductVariantConnection) => }) ), })) +} export function normalizeProduct(productNode: ShopifyProduct): any { const { @@ -66,7 +68,7 @@ export function normalizeProduct(productNode: ShopifyProduct): any { ...rest } = productNode - return { + const product = { id, name, vendor, @@ -79,6 +81,8 @@ export function normalizeProduct(productNode: ShopifyProduct): any { options: options ? options.map((o) => normalizeProductOption(o)) : [], ...rest, } + + return product } export function normalizeCart(checkout: Checkout): Cart { @@ -88,13 +92,13 @@ export function normalizeCart(checkout: Checkout): Cart { email: '', createdAt: checkout.createdAt, currency: { - code: checkout.currencyCode, + code: checkout.totalPriceV2?.currencyCode, }, taxesIncluded: checkout.taxesIncluded, lineItems: checkout.lineItems?.edges.map(normalizeLineItem), - lineItemsSubtotalPrice: checkout.subtotalPrice, - subtotalPrice: checkout.subtotalPrice, - totalPrice: checkout.totalPrice, + lineItemsSubtotalPrice: checkout.subtotalPriceV2?.amount, + subtotalPrice: checkout.subtotalPriceV2?.amount, + totalPrice: checkout.totalPriceV2?.amount, discounts: [], } } @@ -106,7 +110,7 @@ function normalizeLineItem({ id, variantId: String(variant?.id), productId: String(variant?.id), - name: `${title} - ${variant?.title}`, + name: `${title}`, quantity, variant: { id: String(variant?.id), @@ -116,10 +120,15 @@ function normalizeLineItem({ url: variant?.image?.originalSrc, }, requiresShipping: variant?.requiresShipping ?? false, - price: variant?.price, - listPrice: variant?.compareAtPrice, + price: variant?.priceV2?.amount, + listPrice: variant?.compareAtPriceV2?.amount, }, path: '', discounts: [], + options: [ + { + value: variant?.title, + }, + ], } } diff --git a/framework/shopify/utils/queries/get-checkout-query.ts b/framework/shopify/utils/queries/get-checkout-query.ts index f25542329..194e1619a 100644 --- a/framework/shopify/utils/queries/get-checkout-query.ts +++ b/framework/shopify/utils/queries/get-checkout-query.ts @@ -1,10 +1,18 @@ export const checkoutDetailsFragment = ` id webUrl - subtotalPrice - totalTax - totalPrice - currencyCode + subtotalPriceV2{ + amount + currencyCode + } + totalTaxV2 { + amount + currencyCode + } + totalPriceV2 { + amount + currencyCode + } completedAt createdAt taxesIncluded @@ -27,7 +35,14 @@ export const checkoutDetailsFragment = ` width height } - price + priceV2{ + amount + currencyCode + } + compareAtPriceV2{ + amount + currencyCode + } } quantity } diff --git a/framework/shopify/utils/queries/get-product-query.ts b/framework/shopify/utils/queries/get-product-query.ts index d61824ed5..d054c023d 100644 --- a/framework/shopify/utils/queries/get-product-query.ts +++ b/framework/shopify/utils/queries/get-product-query.ts @@ -36,8 +36,14 @@ const getProductQuery = /* GraphQL */ ` name value } - price - compareAtPrice + priceV2 { + amount + currencyCode + } + compareAtPriceV2 { + amount + currencyCode + } } } } diff --git a/tsconfig.json b/tsconfig.json index 43dfd2a27..7aec4729c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,8 +22,8 @@ "@utils/*": ["utils/*"], "@commerce/*": ["framework/commerce/*"], "@commerce": ["framework/commerce"], - "@framework/*": ["framework/bigcommerce/*"], - "@framework": ["framework/bigcommerce"] + "@framework/*": ["framework/shopify/*"], + "@framework": ["framework/shopify"] } }, "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], From 43df2ba0f22204d0dbe28001d40cf66aaa95cded Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Mon, 22 Feb 2021 16:29:38 +0200 Subject: [PATCH 158/221] Update next.config.js --- next.config.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/next.config.js b/next.config.js index 3c9e37210..9ee64e00e 100644 --- a/next.config.js +++ b/next.config.js @@ -34,4 +34,11 @@ module.exports = { }, ] }, + typescript: { + // !! WARN !! + // Dangerously allow production builds to successfully complete even if + // your project has type errors. + // !! WARN !! + ignoreBuildErrors: true, + }, } From a8607f24cd215129527418dc6b81748baa1faae4 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Mon, 22 Feb 2021 19:06:03 -0500 Subject: [PATCH 159/221] Updates to wishlist feature --- components/common/Layout/Layout.tsx | 5 ++--- components/product/ProductCard/ProductCard.tsx | 4 ++-- components/product/ProductView/ProductView.tsx | 4 ++-- components/wishlist/WishlistButton/WishlistButton.tsx | 5 +++-- framework/commerce/utils/features.ts | 2 +- next.config.js | 3 +++ package.json | 1 + pages/index.tsx | 8 ++++---- pages/product/[slug].tsx | 2 +- pages/search.tsx | 1 + 10 files changed, 20 insertions(+), 15 deletions(-) diff --git a/components/common/Layout/Layout.tsx b/components/common/Layout/Layout.tsx index f4376bbf3..82e045474 100644 --- a/components/common/Layout/Layout.tsx +++ b/components/common/Layout/Layout.tsx @@ -58,11 +58,10 @@ const Layout: FC<Props> = ({ } = useUI() const { acceptedCookies, onAcceptCookies } = useAcceptCookies() const { locale = 'en-US' } = useRouter() - const isWishlistEnabled = commerceFeatures.wishlist return ( <CommerceProvider locale={locale}> <div className={cn(s.root)}> - <Navbar wishlist={isWishlistEnabled} /> + <Navbar wishlist={!!process.env.WISHLIST_ENABLED} /> <main className="fit">{children}</main> <Footer pages={pageProps.pages} /> @@ -73,7 +72,7 @@ const Layout: FC<Props> = ({ </Modal> <Sidebar open={displaySidebar} onClose={closeSidebar}> - <CartSidebarView wishlist={isWishlistEnabled} /> + <CartSidebarView wishlist={!!process.env.WISHLIST_ENABLED} /> </Sidebar> <FeatureBar diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index a9eaf8568..52ce129dd 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -59,11 +59,11 @@ const ProductCard: FC<Props> = ({ {product.price.currencyCode} </span> </div> - {wishlist && ( + {process.env.WISHLIST_ENABLED && ( <WishlistButton className={s.wishlistButton} productId={product.id} - variant={product.variants[0]} + variant={product.variants[0] as any} /> )} </div> diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index c502303c4..e666e1a08 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -152,11 +152,11 @@ const ProductView: FC<Props> = ({ product, wishlist = false }) => { </Button> </div> </div> - {wishlist && ( + {process.env.WISHLIST_ENABLED && ( <WishlistButton className={s.wishlistButton} productId={product.id} - variant={product.variants[0]!} + variant={product.variants[0]! as any} /> )} </div> diff --git a/components/wishlist/WishlistButton/WishlistButton.tsx b/components/wishlist/WishlistButton/WishlistButton.tsx index 0c4c20194..e22215363 100644 --- a/components/wishlist/WishlistButton/WishlistButton.tsx +++ b/components/wishlist/WishlistButton/WishlistButton.tsx @@ -7,7 +7,7 @@ import type { Product, ProductVariant } from '@commerce/types' import useCustomer from '@framework/customer/use-customer' import useAddItem from '@framework/wishlist/use-add-item' import useRemoveItem from '@framework/wishlist/use-remove-item' -import useWishlist from '@framework/wishlist/use-add-item' +import useWishlist from '@framework/wishlist/use-wishlist' type Props = { productId: Product['id'] @@ -28,7 +28,8 @@ const WishlistButton: FC<Props> = ({ const [loading, setLoading] = useState(false) const itemInWishlist = data?.items?.find( - (item) => item.product_id === productId && item.variant_id === variant.id + (item) => + item.product_id === productId && (item.variant_id as any) === variant.id ) const handleWishlistChange = async (e: any) => { diff --git a/framework/commerce/utils/features.ts b/framework/commerce/utils/features.ts index d84321967..98a53ed54 100644 --- a/framework/commerce/utils/features.ts +++ b/framework/commerce/utils/features.ts @@ -1,4 +1,4 @@ -import commerceProviderConfig from '@framework/config.json' +import commerceProviderConfig from '../config.json' import type { CommerceProviderConfig } from '../types' import memo from 'lodash.memoize' diff --git a/next.config.js b/next.config.js index e732ef78a..939031884 100644 --- a/next.config.js +++ b/next.config.js @@ -6,6 +6,9 @@ module.exports = { locales: ['en-US', 'es'], defaultLocale: 'en-US', }, + env: { + WISHLIST_ENABLED: false, + }, rewrites() { return [ { diff --git a/package.json b/package.json index 2d8e32772..d1bfbb574 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "generate": "graphql-codegen", "generate:definitions": "node framework/bigcommerce/scripts/generate-definitions.js" }, + "sideEffects": false, "license": "MIT", "engines": { "node": "12.x" diff --git a/pages/index.tsx b/pages/index.tsx index acb1474be..c4fb68252 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -57,7 +57,7 @@ export default function Home({ width: i === 0 ? 1080 : 540, height: i === 0 ? 1080 : 540, }} - wishlist={commerceFeatures.wishlist} + wishlist={!!process.env.WISHLIST_ENABLED} /> ))} </Grid> @@ -71,7 +71,7 @@ export default function Home({ width: 320, height: 320, }} - wishlist={commerceFeatures.wishlist} + wishlist={!!process.env.WISHLIST_ENABLED} /> ))} </Marquee> @@ -94,7 +94,7 @@ export default function Home({ width: i === 0 ? 1080 : 540, height: i === 0 ? 1080 : 540, }} - wishlist={commerceFeatures.wishlist} + wishlist={!!process.env.WISHLIST_ENABLED} /> ))} </Grid> @@ -108,7 +108,7 @@ export default function Home({ width: 320, height: 320, }} - wishlist={commerceFeatures.wishlist} + wishlist={!!process.env.WISHLIST_ENABLED} /> ))} </Marquee> diff --git a/pages/product/[slug].tsx b/pages/product/[slug].tsx index a705c001b..90da202b2 100644 --- a/pages/product/[slug].tsx +++ b/pages/product/[slug].tsx @@ -71,7 +71,7 @@ export default function Slug({ ) : ( <ProductView product={product as any} - wishlist={commerceFeatures.wishlist} + wishlist={!!process.env.WISHLIST_ENABLED} /> ) } diff --git a/pages/search.tsx b/pages/search.tsx index c9958a9f8..d62ce22d4 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -34,6 +34,7 @@ import { getDesignerPath, useSearchMeta, } from '@lib/search' +import { Product } from '@commerce/types' export async function getStaticProps({ preview, From 67d05ea165c22a8cedf98b38d73ca9593b73a70b Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Tue, 23 Feb 2021 11:32:54 -0500 Subject: [PATCH 160/221] Moved the features to be environment variable only --- components/product/ProductCard/ProductCard.tsx | 2 -- components/product/ProductView/ProductView.tsx | 3 +-- next.config.js | 2 +- pages/index.tsx | 10 ---------- pages/product/[slug].tsx | 11 +---------- pages/search.tsx | 8 -------- pages/wishlist.tsx | 3 +-- 7 files changed, 4 insertions(+), 35 deletions(-) diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index 52ce129dd..3c28e6663 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -11,7 +11,6 @@ interface Props { product: Product variant?: 'slim' | 'simple' imgProps?: Omit<ImageProps, 'src'> - wishlist?: boolean } const placeholderImg = '/product-img-placeholder.svg' @@ -21,7 +20,6 @@ const ProductCard: FC<Props> = ({ product, variant, imgProps, - wishlist = false, ...props }) => ( <Link href={`/product/${product.slug}`} {...props}> diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index e666e1a08..51b9888b0 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -19,10 +19,9 @@ interface Props { className?: string children?: any product: Product - wishlist?: boolean } -const ProductView: FC<Props> = ({ product, wishlist = false }) => { +const ProductView: FC<Props> = ({ product }) => { const addItem = useAddItem() const { price } = usePrice({ amount: product.price.value, diff --git a/next.config.js b/next.config.js index 939031884..fe4441dda 100644 --- a/next.config.js +++ b/next.config.js @@ -7,7 +7,7 @@ module.exports = { defaultLocale: 'en-US', }, env: { - WISHLIST_ENABLED: false, + WISHLIST_ENABLED: true, }, rewrites() { return [ diff --git a/pages/index.tsx b/pages/index.tsx index c4fb68252..3a84112e5 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -8,7 +8,6 @@ import { getConfig } from '@framework/api' import getAllProducts from '@framework/product/get-all-products' import getSiteInfo from '@framework/common/get-site-info' import getAllPages from '@framework/common/get-all-pages' -import Features from '@commerce/utils/features' export async function getStaticProps({ preview, @@ -24,7 +23,6 @@ export async function getStaticProps({ const { categories, brands } = await getSiteInfo({ config, preview }) const { pages } = await getAllPages({ config, preview }) - const isWishlistEnabled = Features.isEnabled('wishlist') return { props: { @@ -32,9 +30,6 @@ export async function getStaticProps({ categories, brands, pages, - commerceFeatures: { - wishlist: isWishlistEnabled, - }, }, revalidate: 14400, } @@ -44,7 +39,6 @@ export default function Home({ products, brands, categories, - commerceFeatures, }: InferGetStaticPropsType<typeof getStaticProps>) { return ( <> @@ -57,7 +51,6 @@ export default function Home({ width: i === 0 ? 1080 : 540, height: i === 0 ? 1080 : 540, }} - wishlist={!!process.env.WISHLIST_ENABLED} /> ))} </Grid> @@ -71,7 +64,6 @@ export default function Home({ width: 320, height: 320, }} - wishlist={!!process.env.WISHLIST_ENABLED} /> ))} </Marquee> @@ -94,7 +86,6 @@ export default function Home({ width: i === 0 ? 1080 : 540, height: i === 0 ? 1080 : 540, }} - wishlist={!!process.env.WISHLIST_ENABLED} /> ))} </Grid> @@ -108,7 +99,6 @@ export default function Home({ width: 320, height: 320, }} - wishlist={!!process.env.WISHLIST_ENABLED} /> ))} </Marquee> diff --git a/pages/product/[slug].tsx b/pages/product/[slug].tsx index 90da202b2..61420a8d9 100644 --- a/pages/product/[slug].tsx +++ b/pages/product/[slug].tsx @@ -11,14 +11,12 @@ import { getConfig } from '@framework/api' import getProduct from '@framework/product/get-product' import getAllPages from '@framework/common/get-all-pages' import getAllProductPaths from '@framework/product/get-all-product-paths' -import Features from '@commerce/utils/features' export async function getStaticProps({ params, locale, preview, }: GetStaticPropsContext<{ slug: string }>) { - const isWishlistEnabled = Features.isEnabled('wishlist') const config = getConfig({ locale }) const { pages } = await getAllPages({ config, preview }) const { product } = await getProduct({ @@ -35,9 +33,6 @@ export async function getStaticProps({ props: { pages, product, - commerceFeatures: { - wishlist: isWishlistEnabled, - }, }, revalidate: 200, } @@ -62,17 +57,13 @@ export async function getStaticPaths({ locales }: GetStaticPathsContext) { export default function Slug({ product, - commerceFeatures, }: InferGetStaticPropsType<typeof getStaticProps>) { const router = useRouter() return router.isFallback ? ( <h1>Loading...</h1> // TODO (BC) Add Skeleton Views ) : ( - <ProductView - product={product as any} - wishlist={!!process.env.WISHLIST_ENABLED} - /> + <ProductView product={product as any} /> ) } diff --git a/pages/search.tsx b/pages/search.tsx index d62ce22d4..a05203892 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -26,8 +26,6 @@ const SORT = Object.entries({ 'price-desc': 'Price: High to low', }) -import Features from '@commerce/utils/features' - import { filterQuery, getCategoryPath, @@ -43,15 +41,11 @@ export async function getStaticProps({ const config = getConfig({ locale }) const { pages } = await getAllPages({ config, preview }) const { categories, brands } = await getSiteInfo({ config, preview }) - const isWishlistEnabled = Features.isEnabled('wishlist') return { props: { pages, categories, brands, - commerceFeatures: { - wishlist: isWishlistEnabled, - }, }, } } @@ -59,7 +53,6 @@ export async function getStaticProps({ export default function Search({ categories, brands, - commerceFeatures: { wishlist }, }: InferGetStaticPropsType<typeof getStaticProps>) { const [activeFilter, setActiveFilter] = useState('') const [toggleFilter, setToggleFilter] = useState(false) @@ -359,7 +352,6 @@ export default function Search({ width: 480, height: 480, }} - wishlist={wishlist} /> ))} </Grid> diff --git a/pages/wishlist.tsx b/pages/wishlist.tsx index ca11152f4..dcda912c6 100644 --- a/pages/wishlist.tsx +++ b/pages/wishlist.tsx @@ -11,14 +11,13 @@ import { useCustomer } from '@framework/customer' import { WishlistCard } from '@components/wishlist' import useWishlist from '@framework/wishlist/use-wishlist' import getAllPages from '@framework/common/get-all-pages' -import Features from '@commerce/utils/features' export async function getStaticProps({ preview, locale, }: GetStaticPropsContext) { // Disabling page if Feature is not available - if (Features.isEnabled('wishlist')) { + if (!process.env.WISHLIST_ENABLED) { return { notFound: true, } From 412f2681487dd46917197172221d442dd12cddcc Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Tue, 23 Feb 2021 13:21:25 -0500 Subject: [PATCH 161/221] More changes for wishlist config --- components/cart/CartSidebarView/CartSidebarView.tsx | 4 ++-- .../common/HomeAllProductsGrid/HomeAllProductsGrid.tsx | 4 ---- components/common/Layout/Layout.tsx | 4 ++-- components/common/Navbar/Navbar.tsx | 4 ++-- components/common/UserNav/UserNav.tsx | 5 ++--- 5 files changed, 8 insertions(+), 13 deletions(-) diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index 5b28fde27..3ceda44fe 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -9,7 +9,7 @@ import usePrice from '@framework/product/use-price' import CartItem from '../CartItem' import s from './CartSidebarView.module.css' -const CartSidebarView: FC<{ wishlist?: boolean }> = ({ wishlist }) => { +const CartSidebarView: FC = () => { const { closeSidebar } = useUI() const { data, isLoading, isEmpty } = useCart() @@ -48,7 +48,7 @@ const CartSidebarView: FC<{ wishlist?: boolean }> = ({ wishlist }) => { </button> </div> <div className="space-y-1"> - <UserNav wishlist={wishlist} /> + <UserNav /> </div> </div> </header> diff --git a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx index 4b838e1a4..423048f75 100644 --- a/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx +++ b/components/common/HomeAllProductsGrid/HomeAllProductsGrid.tsx @@ -5,20 +5,17 @@ import { Grid } from '@components/ui' import { ProductCard } from '@components/product' import s from './HomeAllProductsGrid.module.css' import { getCategoryPath, getDesignerPath } from '@lib/search' -import wishlist from '@framework/api/wishlist' interface Props { categories?: any brands?: any products?: Product[] - wishlist?: boolean } const HomeAllProductsGrid: FC<Props> = ({ categories, brands, products = [], - wishlist = false, }) => { return ( <div className={s.root}> @@ -65,7 +62,6 @@ const HomeAllProductsGrid: FC<Props> = ({ width: 480, height: 480, }} - wishlist={wishlist} /> ))} </Grid> diff --git a/components/common/Layout/Layout.tsx b/components/common/Layout/Layout.tsx index 82e045474..54749c46b 100644 --- a/components/common/Layout/Layout.tsx +++ b/components/common/Layout/Layout.tsx @@ -61,7 +61,7 @@ const Layout: FC<Props> = ({ return ( <CommerceProvider locale={locale}> <div className={cn(s.root)}> - <Navbar wishlist={!!process.env.WISHLIST_ENABLED} /> + <Navbar /> <main className="fit">{children}</main> <Footer pages={pageProps.pages} /> @@ -72,7 +72,7 @@ const Layout: FC<Props> = ({ </Modal> <Sidebar open={displaySidebar} onClose={closeSidebar}> - <CartSidebarView wishlist={!!process.env.WISHLIST_ENABLED} /> + <CartSidebarView /> </Sidebar> <FeatureBar diff --git a/components/common/Navbar/Navbar.tsx b/components/common/Navbar/Navbar.tsx index ce9d91b38..b2a372f66 100644 --- a/components/common/Navbar/Navbar.tsx +++ b/components/common/Navbar/Navbar.tsx @@ -5,7 +5,7 @@ import { Searchbar, UserNav } from '@components/common' import NavbarRoot from './NavbarRoot' import s from './Navbar.module.css' -const Navbar: FC<{ wishlist?: boolean }> = ({ wishlist }) => ( +const Navbar: FC = () => ( <NavbarRoot> <Container> <div className="relative flex flex-row justify-between py-4 align-center md:py-6"> @@ -33,7 +33,7 @@ const Navbar: FC<{ wishlist?: boolean }> = ({ wishlist }) => ( </div> <div className="flex justify-end flex-1 space-x-8"> - <UserNav wishlist={wishlist} /> + <UserNav /> </div> </div> diff --git a/components/common/UserNav/UserNav.tsx b/components/common/UserNav/UserNav.tsx index 5d9d58fff..d2dfc11dd 100644 --- a/components/common/UserNav/UserNav.tsx +++ b/components/common/UserNav/UserNav.tsx @@ -12,12 +12,11 @@ import { Avatar } from '@components/common' interface Props { className?: string - wishlist?: boolean } const countItem = (count: number, item: LineItem) => count + item.quantity -const UserNav: FC<Props> = ({ className, wishlist = false }) => { +const UserNav: FC<Props> = ({ className }) => { const { data } = useCart() const { data: customer } = useCustomer() const { toggleSidebar, closeSidebarIfPresent, openModal } = useUI() @@ -31,7 +30,7 @@ const UserNav: FC<Props> = ({ className, wishlist = false }) => { <Bag /> {itemsCount > 0 && <span className={s.bagCount}>{itemsCount}</span>} </li> - {wishlist && ( + {process.env.WISHLIST_ENABLED && ( <li className={s.item}> <Link href="/wishlist"> <a onClick={closeSidebarIfPresent} aria-label="Wishlist"> From b931bc47afe20c923381316d36c6e3b4a4358bc7 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Tue, 23 Feb 2021 13:59:11 -0500 Subject: [PATCH 162/221] Disable wishlist --- next.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/next.config.js b/next.config.js index fe4441dda..939031884 100644 --- a/next.config.js +++ b/next.config.js @@ -7,7 +7,7 @@ module.exports = { defaultLocale: 'en-US', }, env: { - WISHLIST_ENABLED: true, + WISHLIST_ENABLED: false, }, rewrites() { return [ From 9b251a10c6b844af7c2078b08bddc66fd1019e34 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Tue, 23 Feb 2021 23:03:23 -0500 Subject: [PATCH 163/221] Removed useWishlistActions --- framework/bigcommerce/wishlist/index.ts | 1 - .../bigcommerce/wishlist/use-wishlist-actions.tsx | 11 ----------- 2 files changed, 12 deletions(-) delete mode 100644 framework/bigcommerce/wishlist/use-wishlist-actions.tsx diff --git a/framework/bigcommerce/wishlist/index.ts b/framework/bigcommerce/wishlist/index.ts index 9ea28291c..241af3c7e 100644 --- a/framework/bigcommerce/wishlist/index.ts +++ b/framework/bigcommerce/wishlist/index.ts @@ -1,4 +1,3 @@ export { default as useAddItem } from './use-add-item' export { default as useWishlist } from './use-wishlist' export { default as useRemoveItem } from './use-remove-item' -export { default as useWishlistActions } from './use-wishlist-actions' diff --git a/framework/bigcommerce/wishlist/use-wishlist-actions.tsx b/framework/bigcommerce/wishlist/use-wishlist-actions.tsx deleted file mode 100644 index 711d00516..000000000 --- a/framework/bigcommerce/wishlist/use-wishlist-actions.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import useAddItem from './use-add-item' -import useRemoveItem from './use-remove-item' - -// This hook is probably not going to be used, but it's here -// to show how a commerce should be structuring it -export default function useWishlistActions() { - const addItem = useAddItem() - const removeItem = useRemoveItem() - - return { addItem, removeItem } -} From bbfca29217540df58ff34f604970e2bb16e856d4 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Tue, 23 Feb 2021 23:07:49 -0500 Subject: [PATCH 164/221] Updated readme --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ea6248c51..9d2108cfe 100644 --- a/README.md +++ b/README.md @@ -57,18 +57,21 @@ Main folder and its exposed functions - getAllProducts - `wishlist` - useWishlist - - addWishlistItem - - removeWishlistItem + - useAddItem + - useRemoveItem - `auth` - useLogin - useLogout - useSignup +- `customer` + - useCustomer + - getCustomerId + - getCustomerWistlist - `cart` - useCart - useAddItem - useRemoveItem - - useCartActions - useUpdateItem - `config.json` From bcd1d46e30d0cfc7078544afcf82532d03b586d6 Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Wed, 24 Feb 2021 14:37:25 -0300 Subject: [PATCH 165/221] updates --- .vscode/extensions.json | 3 ++ components/common/UserNav/UserNav.tsx | 2 +- .../product/ProductCard/ProductCard.tsx | 2 +- .../product/ProductView/ProductView.tsx | 2 +- framework/bigcommerce/auth/use-login.tsx | 2 +- framework/bigcommerce/auth/use-logout.tsx | 2 +- framework/bigcommerce/auth/use-signup.tsx | 2 +- framework/bigcommerce/next.config.js | 37 +++++++++++++++ framework/bigcommerce/product/use-price.tsx | 4 +- framework/commerce/{ => auth}/use-login.tsx | 8 ++-- framework/commerce/{ => auth}/use-logout.tsx | 8 ++-- framework/commerce/{ => auth}/use-signup.tsx | 8 ++-- framework/commerce/config.json | 5 -- .../commerce/{ => product}/use-price.tsx | 2 +- framework/commerce/types.ts | 4 +- framework/commerce/utils/bootstrap.js | 11 +++++ framework/commerce/utils/features.ts | 10 ++++ next.config.js | 46 +++---------------- package.json | 1 + pages/wishlist.tsx | 2 +- yarn.lock | 5 ++ 21 files changed, 98 insertions(+), 68 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 framework/bigcommerce/next.config.js rename framework/commerce/{ => auth}/use-login.tsx (64%) rename framework/commerce/{ => auth}/use-logout.tsx (64%) rename framework/commerce/{ => auth}/use-signup.tsx (64%) delete mode 100644 framework/commerce/config.json rename framework/commerce/{ => product}/use-price.tsx (97%) create mode 100644 framework/commerce/utils/bootstrap.js diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..c83e26348 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["esbenp.prettier-vscode"] +} diff --git a/components/common/UserNav/UserNav.tsx b/components/common/UserNav/UserNav.tsx index d2dfc11dd..9d143a572 100644 --- a/components/common/UserNav/UserNav.tsx +++ b/components/common/UserNav/UserNav.tsx @@ -30,7 +30,7 @@ const UserNav: FC<Props> = ({ className }) => { <Bag /> {itemsCount > 0 && <span className={s.bagCount}>{itemsCount}</span>} </li> - {process.env.WISHLIST_ENABLED && ( + {process.env.COMMERCE_WISHLIST_ENABLED && ( <li className={s.item}> <Link href="/wishlist"> <a onClick={closeSidebarIfPresent} aria-label="Wishlist"> diff --git a/components/product/ProductCard/ProductCard.tsx b/components/product/ProductCard/ProductCard.tsx index 3c28e6663..ade53380c 100644 --- a/components/product/ProductCard/ProductCard.tsx +++ b/components/product/ProductCard/ProductCard.tsx @@ -57,7 +57,7 @@ const ProductCard: FC<Props> = ({ {product.price.currencyCode} </span> </div> - {process.env.WISHLIST_ENABLED && ( + {process.env.COMMERCE_WISHLIST_ENABLED && ( <WishlistButton className={s.wishlistButton} productId={product.id} diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index 51b9888b0..2d31aaafc 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -151,7 +151,7 @@ const ProductView: FC<Props> = ({ product }) => { </Button> </div> </div> - {process.env.WISHLIST_ENABLED && ( + {process.env.COMMERCE_WISHLIST_ENABLED && ( <WishlistButton className={s.wishlistButton} productId={product.id} diff --git a/framework/bigcommerce/auth/use-login.tsx b/framework/bigcommerce/auth/use-login.tsx index b66fca493..1be96a58c 100644 --- a/framework/bigcommerce/auth/use-login.tsx +++ b/framework/bigcommerce/auth/use-login.tsx @@ -1,7 +1,7 @@ import { useCallback } from 'react' import type { MutationHook } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' -import useLogin, { UseLogin } from '@commerce/use-login' +import useLogin, { UseLogin } from '@commerce/auth/use-login' import type { LoginBody } from '../api/customers/login' import useCustomer from '../customer/use-customer' diff --git a/framework/bigcommerce/auth/use-logout.tsx b/framework/bigcommerce/auth/use-logout.tsx index 6278a4dd1..71015a1c1 100644 --- a/framework/bigcommerce/auth/use-logout.tsx +++ b/framework/bigcommerce/auth/use-logout.tsx @@ -1,6 +1,6 @@ import { useCallback } from 'react' import type { MutationHook } from '@commerce/utils/types' -import useLogout, { UseLogout } from '@commerce/use-logout' +import useLogout, { UseLogout } from '@commerce/auth/use-logout' import useCustomer from '../customer/use-customer' export default useLogout as UseLogout<typeof handler> diff --git a/framework/bigcommerce/auth/use-signup.tsx b/framework/bigcommerce/auth/use-signup.tsx index 23b7ce9c6..28f7024ef 100644 --- a/framework/bigcommerce/auth/use-signup.tsx +++ b/framework/bigcommerce/auth/use-signup.tsx @@ -1,7 +1,7 @@ import { useCallback } from 'react' import type { MutationHook } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' -import useSignup, { UseSignup } from '@commerce/use-signup' +import useSignup, { UseSignup } from '@commerce/auth/use-signup' import type { SignupBody } from '../api/customers/signup' import useCustomer from '../customer/use-customer' diff --git a/framework/bigcommerce/next.config.js b/framework/bigcommerce/next.config.js new file mode 100644 index 000000000..e732ef78a --- /dev/null +++ b/framework/bigcommerce/next.config.js @@ -0,0 +1,37 @@ +module.exports = { + images: { + domains: ['cdn11.bigcommerce.com'], + }, + i18n: { + locales: ['en-US', 'es'], + defaultLocale: 'en-US', + }, + rewrites() { + return [ + { + source: '/checkout', + destination: '/api/bigcommerce/checkout', + }, + // The logout is also an action so this route is not required, but it's also another way + // you can allow a logout! + { + source: '/logout', + destination: '/api/bigcommerce/customers/logout?redirect_to=/', + }, + // Rewrites for /search + { + source: '/search/designers/:name', + destination: '/search', + }, + { + source: '/search/designers/:name/:category', + destination: '/search', + }, + { + // This rewrite will also handle `/search/designers` + source: '/search/:category', + destination: '/search', + }, + ] + }, +} diff --git a/framework/bigcommerce/product/use-price.tsx b/framework/bigcommerce/product/use-price.tsx index a79940a76..0174faf5e 100644 --- a/framework/bigcommerce/product/use-price.tsx +++ b/framework/bigcommerce/product/use-price.tsx @@ -1,2 +1,2 @@ -export * from '@commerce/use-price' -export { default } from '@commerce/use-price' +export * from '@commerce/product/use-price' +export { default } from '@commerce/product/use-price' diff --git a/framework/commerce/use-login.tsx b/framework/commerce/auth/use-login.tsx similarity index 64% rename from framework/commerce/use-login.tsx rename to framework/commerce/auth/use-login.tsx index 755e10fd9..cc4cf6a73 100644 --- a/framework/commerce/use-login.tsx +++ b/framework/commerce/auth/use-login.tsx @@ -1,7 +1,7 @@ -import { useHook, useMutationHook } from './utils/use-hook' -import { mutationFetcher } from './utils/default-fetcher' -import type { MutationHook, HookFetcherFn } from './utils/types' -import type { Provider } from '.' +import { useHook, useMutationHook } from '../utils/use-hook' +import { mutationFetcher } from '../utils/default-fetcher' +import type { MutationHook, HookFetcherFn } from '../utils/types' +import type { Provider } from '..' export type UseLogin< H extends MutationHook<any, any, any> = MutationHook<null, {}, {}> diff --git a/framework/commerce/use-logout.tsx b/framework/commerce/auth/use-logout.tsx similarity index 64% rename from framework/commerce/use-logout.tsx rename to framework/commerce/auth/use-logout.tsx index 0a80c318b..d0f7e3ae0 100644 --- a/framework/commerce/use-logout.tsx +++ b/framework/commerce/auth/use-logout.tsx @@ -1,7 +1,7 @@ -import { useHook, useMutationHook } from './utils/use-hook' -import { mutationFetcher } from './utils/default-fetcher' -import type { HookFetcherFn, MutationHook } from './utils/types' -import type { Provider } from '.' +import { useHook, useMutationHook } from '../utils/use-hook' +import { mutationFetcher } from '../utils/default-fetcher' +import type { HookFetcherFn, MutationHook } from '../utils/types' +import type { Provider } from '..' export type UseLogout< H extends MutationHook<any, any, any> = MutationHook<null> diff --git a/framework/commerce/use-signup.tsx b/framework/commerce/auth/use-signup.tsx similarity index 64% rename from framework/commerce/use-signup.tsx rename to framework/commerce/auth/use-signup.tsx index be3c32000..72e242209 100644 --- a/framework/commerce/use-signup.tsx +++ b/framework/commerce/auth/use-signup.tsx @@ -1,7 +1,7 @@ -import { useHook, useMutationHook } from './utils/use-hook' -import { mutationFetcher } from './utils/default-fetcher' -import type { HookFetcherFn, MutationHook } from './utils/types' -import type { Provider } from '.' +import { useHook, useMutationHook } from '../utils/use-hook' +import { mutationFetcher } from '../utils/default-fetcher' +import type { HookFetcherFn, MutationHook } from '../utils/types' +import type { Provider } from '..' export type UseSignup< H extends MutationHook<any, any, any> = MutationHook<null> diff --git a/framework/commerce/config.json b/framework/commerce/config.json deleted file mode 100644 index a0e7afc5d..000000000 --- a/framework/commerce/config.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "features": { - "wishlist": true - } -} diff --git a/framework/commerce/use-price.tsx b/framework/commerce/product/use-price.tsx similarity index 97% rename from framework/commerce/use-price.tsx rename to framework/commerce/product/use-price.tsx index 9a3fc4b6e..9c09e3487 100644 --- a/framework/commerce/use-price.tsx +++ b/framework/commerce/product/use-price.tsx @@ -1,5 +1,5 @@ import { useMemo } from 'react' -import { useCommerce } from '.' +import { useCommerce } from '..' export function formatPrice({ amount, diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index 0ae766095..57e707f35 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -2,8 +2,10 @@ import type { Wishlist as BCWishlist } from '@framework/api/wishlist' import type { Customer as BCCustomer } from '@framework/api/customers' import type { SearchProductsData as BCSearchProductsData } from '@framework/api/catalog/products' +export type Features = 'wishlist' | 'checkout' | string + export type CommerceProviderConfig = { - features: Record<string, boolean> + features: Record<Features, boolean> } export type Discount = { diff --git a/framework/commerce/utils/bootstrap.js b/framework/commerce/utils/bootstrap.js new file mode 100644 index 000000000..6a604b7dd --- /dev/null +++ b/framework/commerce/utils/bootstrap.js @@ -0,0 +1,11 @@ +module.exports = ({ features }) => { + let output = { + env: {}, + } + if (!!Object.keys(features).length) { + Object.keys(features).map( + (r) => (output.env[`COMMERCE_${r.toUpperCase()}_ENABLED`] = features[r]) + ) + } + return output +} diff --git a/framework/commerce/utils/features.ts b/framework/commerce/utils/features.ts index 98a53ed54..72ed0d7f5 100644 --- a/framework/commerce/utils/features.ts +++ b/framework/commerce/utils/features.ts @@ -14,6 +14,16 @@ function isFeatureEnabled(config: CommerceProviderConfig) { .includes(desideredFeature) } +export function toEnvConfig( + configMap: CommerceProviderConfig['features'] +): Map<string, boolean> { + let toEnvConfigMap = new Map<string, boolean>() + Object.keys(configMap).map((r) => + toEnvConfigMap.set(`${r.toUpperCase()}_ENABLED`, configMap[r]) + ) + return toEnvConfigMap +} + function boostrap(): FeaturesAPI { const basis = { isEnabled: () => false, diff --git a/next.config.js b/next.config.js index 939031884..749fd1b0a 100644 --- a/next.config.js +++ b/next.config.js @@ -1,40 +1,6 @@ -module.exports = { - images: { - domains: ['cdn11.bigcommerce.com'], - }, - i18n: { - locales: ['en-US', 'es'], - defaultLocale: 'en-US', - }, - env: { - WISHLIST_ENABLED: false, - }, - rewrites() { - return [ - { - source: '/checkout', - destination: '/api/bigcommerce/checkout', - }, - // The logout is also an action so this route is not required, but it's also another way - // you can allow a logout! - { - source: '/logout', - destination: '/api/bigcommerce/customers/logout?redirect_to=/', - }, - // Rewrites for /search - { - source: '/search/designers/:name', - destination: '/search', - }, - { - source: '/search/designers/:name/:category', - destination: '/search', - }, - { - // This rewrite will also handle `/search/designers` - source: '/search/:category', - destination: '/search', - }, - ] - }, -} +const providerConfig = require('./framework/bigcommerce/config.json') +const providerNextConfig = require('./framework/bigcommerce/next.config') +const bootstrap = require('./framework/commerce/utils/bootstrap') +const d = require('deepmerge') + +module.exports = d(providerNextConfig, bootstrap(providerConfig)) diff --git a/package.json b/package.json index d1bfbb574..8a7d64e6a 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "@types/lodash.throttle": "^4.1.6", "@types/node": "^14.14.16", "@types/react": "^17.0.0", + "deepmerge": "^4.2.2", "graphql": "^15.4.0", "husky": "^4.3.8", "lint-staged": "^10.5.3", diff --git a/pages/wishlist.tsx b/pages/wishlist.tsx index dcda912c6..ce97532b0 100644 --- a/pages/wishlist.tsx +++ b/pages/wishlist.tsx @@ -17,7 +17,7 @@ export async function getStaticProps({ locale, }: GetStaticPropsContext) { // Disabling page if Feature is not available - if (!process.env.WISHLIST_ENABLED) { + if (!process.env.COMMERCE_WISHLIST_ENABLED) { return { notFound: true, } diff --git a/yarn.lock b/yarn.lock index e7cd08438..3497602b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2532,6 +2532,11 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" From 4de9b906f4f15db74a792cd1fa743f56ea92767b Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Wed, 24 Feb 2021 19:32:03 -0300 Subject: [PATCH 166/221] typos --- framework/bigcommerce/config.json | 2 +- framework/shopify/api/index.ts | 2 +- framework/shopify/customer/use-customer.tsx | 2 +- framework/shopify/product/use-search.tsx | 2 +- package.json | 1 + yarn.lock | 5 +++++ 6 files changed, 10 insertions(+), 4 deletions(-) diff --git a/framework/bigcommerce/config.json b/framework/bigcommerce/config.json index 17ef37e25..a0e7afc5d 100644 --- a/framework/bigcommerce/config.json +++ b/framework/bigcommerce/config.json @@ -1,5 +1,5 @@ { "features": { - "wishlist": false + "wishlist": true } } diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts index dcb0fc2ba..0402cfccc 100644 --- a/framework/shopify/api/index.ts +++ b/framework/shopify/api/index.ts @@ -3,7 +3,7 @@ import fetchGraphqlApi from './utils/fetch-graphql-api' export interface ShopifyConfig extends CommerceAPIConfig {} -// No I don't like this - will fix it later +// TODO(bc) const API_URL = process.env.SHOPIFY_STORE_DOMAIN || process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN diff --git a/framework/shopify/customer/use-customer.tsx b/framework/shopify/customer/use-customer.tsx index a909443ff..7ea2ae679 100644 --- a/framework/shopify/customer/use-customer.tsx +++ b/framework/shopify/customer/use-customer.tsx @@ -1,6 +1,6 @@ import type { HookFetcher } from '@commerce/utils/types' import type { SwrOptions } from '@commerce/utils/use-data' -import useCommerceCustomer from '@commerce/use-customer' +import useCommerceCustomer from '@commerce/customer/use-customer' const defaultOpts = {} diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx index a2c32c896..04f6a3536 100644 --- a/framework/shopify/product/use-search.tsx +++ b/framework/shopify/product/use-search.tsx @@ -1,6 +1,6 @@ import type { HookFetcher } from '@commerce/utils/types' import type { SwrOptions } from '@commerce/utils/use-data' -import useCommerceSearch from '@commerce/products/use-search' +import useCommerceSearch from '@commerce/product/use-search' import { ProductEdge } from '../types' const defaultOpts = {} diff --git a/package.json b/package.json index 8a7d64e6a..85bd9c063 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "react-dom": "^17.0.1", "react-merge-refs": "^1.1.0", "react-ticker": "^1.2.2", + "shopify-buy": "^2.11.0", "swr": "^0.4.0", "tabbable": "^5.1.5", "tailwindcss": "^2.0.2" diff --git a/yarn.lock b/yarn.lock index 3497602b9..1255b9d62 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6226,6 +6226,11 @@ shell-quote@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== +shopify-buy@^2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/shopify-buy/-/shopify-buy-2.11.0.tgz#0f7cb52741395e4ae778c336f32ddf3fe67c2f35" + integrity sha512-bGjS1b/VCPvCjazSstlKwgLtK1WBotWom06/12loja8yfo/cWkLuJsakBbQe1uEIDiOLhKaR0M0CAXZFheYDug== + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" From 0cc443db06c3293b1a3697eee7175d9b14bf0784 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Wed, 24 Feb 2021 21:55:45 -0500 Subject: [PATCH 167/221] Updated the way the provider config is set --- .env.template | 3 +- README.md | 5 +-- framework/bigcommerce/README.md | 1 + framework/bigcommerce/next.config.js | 38 ++++---------------- framework/commerce/with-config.js | 40 +++++++++++++++++++++ next.config.js | 53 +++++++++++++++++++++++++--- 6 files changed, 100 insertions(+), 40 deletions(-) create mode 100644 framework/commerce/with-config.js diff --git a/.env.template b/.env.template index 73a8a6e3b..83e7dd403 100644 --- a/.env.template +++ b/.env.template @@ -2,4 +2,5 @@ BIGCOMMERCE_STOREFRONT_API_URL= BIGCOMMERCE_STOREFRONT_API_TOKEN= BIGCOMMERCE_STORE_API_URL= BIGCOMMERCE_STORE_API_TOKEN= -BIGCOMMERCE_STORE_API_CLIENT_ID= \ No newline at end of file +BIGCOMMERCE_STORE_API_CLIENT_ID= +BIGCOMMERCE_CHANNEL_ID= \ No newline at end of file diff --git a/README.md b/README.md index 9d2108cfe..f254b1b07 100644 --- a/README.md +++ b/README.md @@ -77,12 +77,12 @@ Main folder and its exposed functions - `config.json` - README.md -#### Example of correct usage of Commece Framework +#### Example of correct usage of Commerce Framework ```js import { useUI } from '@components/ui' import { useCustomer } from '@framework/customer' -import { useAddItem, useWishlist, useRemoveItem } from '@framework/wishlist' +import { useWishlist, useAddItem, useRemoveItem } from '@framework/wishlist' ``` ## Config @@ -131,6 +131,7 @@ BIGCOMMERCE_STOREFRONT_API_TOKEN=<> BIGCOMMERCE_STORE_API_URL=<> BIGCOMMERCE_STORE_API_TOKEN=<> BIGCOMMERCE_STORE_API_CLIENT_ID=<> +BIGCOMMERCE_CHANNEL_ID=<> ``` If your project was started with a "Deploy with Vercel" button, you can use Vercel's CLI to retrieve these credentials. diff --git a/framework/bigcommerce/README.md b/framework/bigcommerce/README.md index 86fbac91d..2609b1544 100644 --- a/framework/bigcommerce/README.md +++ b/framework/bigcommerce/README.md @@ -47,6 +47,7 @@ BIGCOMMERCE_STOREFRONT_API_TOKEN=<> BIGCOMMERCE_STORE_API_URL=<> BIGCOMMERCE_STORE_API_TOKEN=<> BIGCOMMERCE_STORE_API_CLIENT_ID=<> +BIGCOMMERCE_CHANNEL_ID=<> ``` ## General Usage diff --git a/framework/bigcommerce/next.config.js b/framework/bigcommerce/next.config.js index e732ef78a..5703f2343 100644 --- a/framework/bigcommerce/next.config.js +++ b/framework/bigcommerce/next.config.js @@ -1,37 +1,11 @@ +const providerConfig = require('./config.json') + module.exports = { + commerce: { + provider: 'bigcommerce', + ...providerConfig, + }, images: { domains: ['cdn11.bigcommerce.com'], }, - i18n: { - locales: ['en-US', 'es'], - defaultLocale: 'en-US', - }, - rewrites() { - return [ - { - source: '/checkout', - destination: '/api/bigcommerce/checkout', - }, - // The logout is also an action so this route is not required, but it's also another way - // you can allow a logout! - { - source: '/logout', - destination: '/api/bigcommerce/customers/logout?redirect_to=/', - }, - // Rewrites for /search - { - source: '/search/designers/:name', - destination: '/search', - }, - { - source: '/search/designers/:name/:category', - destination: '/search', - }, - { - // This rewrite will also handle `/search/designers` - source: '/search/:category', - destination: '/search', - }, - ] - }, } diff --git a/framework/commerce/with-config.js b/framework/commerce/with-config.js new file mode 100644 index 000000000..6dcf08986 --- /dev/null +++ b/framework/commerce/with-config.js @@ -0,0 +1,40 @@ +/** + * This file is expected to be used in next.config.js only + */ + +const merge = require('deepmerge') + +const PROVIDERS = ['bigcommerce'] + +function getProviderName() { + return process.env.BIGCOMMERCE_STOREFRONT_API_URL ? 'bigcommerce' : null +} + +module.exports = (nextConfig = {}) => { + const commerce = nextConfig.commerce || {} + const name = commerce.provider || getProviderName() + + if (!name) { + throw new Error( + `The commerce provider is missing, please add a valid provider name or its environment variables` + ) + } + if (!PROVIDERS.includes(name)) { + throw new Error( + `The commerce provider "${name}" can't be found, please use one of "${PROVIDERS.join( + ', ' + )}"` + ) + } + + const commerceNextConfig = require(`../${name}/next.config`) + const config = merge(commerceNextConfig, nextConfig) + + config.env = config.env || {} + + Object.entries(config.commerce.features).forEach(([k, v]) => { + if (v) config.env[`COMMERCE_${k.toUpperCase()}_ENABLED`] = true + }) + + return config +} diff --git a/next.config.js b/next.config.js index 749fd1b0a..74be36a5d 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,49 @@ -const providerConfig = require('./framework/bigcommerce/config.json') -const providerNextConfig = require('./framework/bigcommerce/next.config') -const bootstrap = require('./framework/commerce/utils/bootstrap') -const d = require('deepmerge') +// const providerConfig = require('./framework/bigcommerce/config.json') +// const providerNextConfig = require('./framework/bigcommerce/next.config') +// const bootstrap = require('./framework/commerce/utils/bootstrap') +// const d = require('deepmerge') -module.exports = d(providerNextConfig, bootstrap(providerConfig)) +// module.exports = d(providerNextConfig, bootstrap(providerConfig)) + +const withCommerceConfig = require('./framework/commerce/with-config') + +const commerce = { provider: 'bigcommerce' } +const isBC = commerce.provider === 'bigcommerce' + +module.exports = withCommerceConfig({ + commerce, + i18n: { + locales: ['en-US', 'es'], + defaultLocale: 'en-US', + }, + rewrites() { + return [ + isBC && { + source: '/checkout', + destination: '/api/bigcommerce/checkout', + }, + // The logout is also an action so this route is not required, but it's also another way + // you can allow a logout! + isBC && { + source: '/logout', + destination: '/api/bigcommerce/customers/logout?redirect_to=/', + }, + // Rewrites for /search + { + source: '/search/designers/:name', + destination: '/search', + }, + { + source: '/search/designers/:name/:category', + destination: '/search', + }, + { + // This rewrite will also handle `/search/designers` + source: '/search/:category', + destination: '/search', + }, + ].filter((x) => x) + }, +}) + +console.log('RESULT', module.exports) From 4c156422a6b60242b86bb32401ee01a0df94872b Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Wed, 24 Feb 2021 21:57:38 -0500 Subject: [PATCH 168/221] Removed features.ts --- framework/commerce/types.ts | 6 ---- framework/commerce/utils/features.ts | 47 ---------------------------- package.json | 2 -- yarn.lock | 12 ------- 4 files changed, 67 deletions(-) delete mode 100644 framework/commerce/utils/features.ts diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index 57e707f35..bf635c9dc 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -2,12 +2,6 @@ import type { Wishlist as BCWishlist } from '@framework/api/wishlist' import type { Customer as BCCustomer } from '@framework/api/customers' import type { SearchProductsData as BCSearchProductsData } from '@framework/api/catalog/products' -export type Features = 'wishlist' | 'checkout' | string - -export type CommerceProviderConfig = { - features: Record<Features, boolean> -} - export type Discount = { // The value of the discount, can be an amount or percentage value: number diff --git a/framework/commerce/utils/features.ts b/framework/commerce/utils/features.ts deleted file mode 100644 index 72ed0d7f5..000000000 --- a/framework/commerce/utils/features.ts +++ /dev/null @@ -1,47 +0,0 @@ -import commerceProviderConfig from '../config.json' -import type { CommerceProviderConfig } from '../types' -import memo from 'lodash.memoize' - -type FeaturesAPI = { - isEnabled: (desideredFeature: string) => boolean -} - -function isFeatureEnabled(config: CommerceProviderConfig) { - const features = config.features - return (desideredFeature: string) => - Object.keys(features) - .filter((k) => features[k]) - .includes(desideredFeature) -} - -export function toEnvConfig( - configMap: CommerceProviderConfig['features'] -): Map<string, boolean> { - let toEnvConfigMap = new Map<string, boolean>() - Object.keys(configMap).map((r) => - toEnvConfigMap.set(`${r.toUpperCase()}_ENABLED`, configMap[r]) - ) - return toEnvConfigMap -} - -function boostrap(): FeaturesAPI { - const basis = { - isEnabled: () => false, - } - - if (!commerceProviderConfig) { - console.log('No config.json found - Please add a config.json') - return basis - } - - if (commerceProviderConfig.features) { - return { - ...basis, - isEnabled: memo(isFeatureEnabled(commerceProviderConfig)), - } - } - - return basis -} - -export default boostrap() diff --git a/package.json b/package.json index 8a7d64e6a..a8a698112 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ }, "dependencies": { "@reach/portal": "^0.11.2", - "@types/lodash.memoize": "^4.1.6", "@vercel/fetch": "^6.1.0", "body-scroll-lock": "^3.1.5", "bowser": "^2.11.0", @@ -34,7 +33,6 @@ "js-cookie": "^2.2.1", "keen-slider": "^5.2.4", "lodash.debounce": "^4.0.8", - "lodash.memoize": "^4.1.2", "lodash.random": "^3.2.0", "lodash.throttle": "^4.1.1", "next": "^10.0.7-canary.3", diff --git a/yarn.lock b/yarn.lock index 3497602b9..47c5781fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1060,13 +1060,6 @@ dependencies: "@types/lodash" "*" -"@types/lodash.memoize@^4.1.6": - version "4.1.6" - resolved "https://registry.yarnpkg.com/@types/lodash.memoize/-/lodash.memoize-4.1.6.tgz#3221f981790a415cab1a239f25c17efd8b604c23" - integrity sha512-mYxjKiKzRadRJVClLKxS4wb3Iy9kzwJ1CkbyKiadVxejnswnRByyofmPMscFKscmYpl36BEEhCMPuWhA1R/1ZQ== - dependencies: - "@types/lodash" "*" - "@types/lodash.random@^3.2.6": version "3.2.6" resolved "https://registry.yarnpkg.com/@types/lodash.random/-/lodash.random-3.2.6.tgz#64b08abad168dca39c778ed40cce75b2f9e168eb" @@ -4244,11 +4237,6 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= -lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= - lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" From 764e0db322696f5c3851dbf3dc7ae48c271d3d3d Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Wed, 24 Feb 2021 21:58:39 -0500 Subject: [PATCH 169/221] Removed bootstrap.js --- framework/commerce/utils/bootstrap.js | 11 ----------- next.config.js | 9 --------- 2 files changed, 20 deletions(-) delete mode 100644 framework/commerce/utils/bootstrap.js diff --git a/framework/commerce/utils/bootstrap.js b/framework/commerce/utils/bootstrap.js deleted file mode 100644 index 6a604b7dd..000000000 --- a/framework/commerce/utils/bootstrap.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = ({ features }) => { - let output = { - env: {}, - } - if (!!Object.keys(features).length) { - Object.keys(features).map( - (r) => (output.env[`COMMERCE_${r.toUpperCase()}_ENABLED`] = features[r]) - ) - } - return output -} diff --git a/next.config.js b/next.config.js index 74be36a5d..725024066 100644 --- a/next.config.js +++ b/next.config.js @@ -1,10 +1,3 @@ -// const providerConfig = require('./framework/bigcommerce/config.json') -// const providerNextConfig = require('./framework/bigcommerce/next.config') -// const bootstrap = require('./framework/commerce/utils/bootstrap') -// const d = require('deepmerge') - -// module.exports = d(providerNextConfig, bootstrap(providerConfig)) - const withCommerceConfig = require('./framework/commerce/with-config') const commerce = { provider: 'bigcommerce' } @@ -45,5 +38,3 @@ module.exports = withCommerceConfig({ ].filter((x) => x) }, }) - -console.log('RESULT', module.exports) From f242f3c58859caee30ebbecbb0e8387c197d26d6 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 25 Feb 2021 09:55:02 +0200 Subject: [PATCH 170/221] Aligned with upstream changes --- .../cart/CartSidebarView/CartSidebarView.tsx | 3 ++- framework/commerce/with-config.js | 2 +- framework/shopify/api/checkout/index.ts | 21 +++++++++++-------- framework/shopify/auth/use-login.tsx | 4 +--- framework/shopify/auth/use-logout.tsx | 2 +- framework/shopify/auth/use-signup.tsx | 2 +- framework/shopify/common/get-all-pages.ts | 21 ++++++++++++++----- framework/shopify/common/get-page.ts | 13 ++++++------ framework/shopify/next.config.js | 11 ++++++++++ .../shopify/product/get-all-product-paths.ts | 11 +++++++++- framework/shopify/product/get-all-products.ts | 14 ++++++------- framework/shopify/product/use-price.tsx | 4 ++-- framework/shopify/utils/normalize.ts | 2 +- .../utils/queries/get-all-pages-query.ts | 3 --- .../shopify/utils/queries/get-page-query.ts | 1 - next.config.js | 5 +++-- tsconfig.json | 5 +++-- 17 files changed, 77 insertions(+), 47 deletions(-) create mode 100644 framework/shopify/next.config.js diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index 3ceda44fe..cb932247f 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -8,6 +8,7 @@ import useCart from '@framework/cart/use-cart' import usePrice from '@framework/product/use-price' import CartItem from '../CartItem' import s from './CartSidebarView.module.css' +import { LineItem } from '@commerce/types' const CartSidebarView: FC = () => { const { closeSidebar } = useUI() @@ -91,7 +92,7 @@ const CartSidebarView: FC = () => { My Cart </h2> <ul className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accents-3 border-t border-accents-3"> - {data!.lineItems.map((item) => ( + {data!.lineItems.map((item: any) => ( <CartItem key={item.id} item={item} diff --git a/framework/commerce/with-config.js b/framework/commerce/with-config.js index 6dcf08986..da6705cef 100644 --- a/framework/commerce/with-config.js +++ b/framework/commerce/with-config.js @@ -4,7 +4,7 @@ const merge = require('deepmerge') -const PROVIDERS = ['bigcommerce'] +const PROVIDERS = ['bigcommerce', 'shopify'] function getProviderName() { return process.env.BIGCOMMERCE_STOREFRONT_API_URL ? 'bigcommerce' : null diff --git a/framework/shopify/api/checkout/index.ts b/framework/shopify/api/checkout/index.ts index 0d0e752f0..d5d6d7f6e 100644 --- a/framework/shopify/api/checkout/index.ts +++ b/framework/shopify/api/checkout/index.ts @@ -21,16 +21,19 @@ const checkoutApi: ShopifyApiHandler<any> = async (req, res, config) => { const { cookies } = req const checkoutUrl = cookies[SHOPIFY_CHECKOUT_URL_COOKIE] + const customerCookie = cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE] - try { - await config.fetch(associateCustomerWithCheckoutMutation, { - variables: { - checkoutId: cookies[SHOPIFY_CHECKOUT_ID_COOKIE], - customerAccessToken: cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE], - }, - }) - } catch (error) { - console.error(error) + if (customerCookie) { + try { + await config.fetch(associateCustomerWithCheckoutMutation, { + variables: { + checkoutId: cookies[SHOPIFY_CHECKOUT_ID_COOKIE], + customerAccessToken: cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE], + }, + }) + } catch (error) { + console.error(error) + } } if (checkoutUrl) { diff --git a/framework/shopify/auth/use-login.tsx b/framework/shopify/auth/use-login.tsx index 9ed8d404f..2143907d8 100644 --- a/framework/shopify/auth/use-login.tsx +++ b/framework/shopify/auth/use-login.tsx @@ -4,14 +4,12 @@ import { CommerceError, ValidationError } from '@commerce/utils/errors' import useCustomer from '../customer/use-customer' import createCustomerAccessTokenMutation from '../utils/mutations/customer-access-token-create' import { - CustomerAccessToken, CustomerAccessTokenCreateInput, - CustomerAccessTokenCreatePayload, CustomerUserError, Mutation, MutationCheckoutCreateArgs, } from '@framework/schema' -import useLogin, { UseLogin } from '@commerce/use-login' +import useLogin, { UseLogin } from '@commerce/auth/use-login' import { setCustomerToken } from '@framework/utils' export default useLogin as UseLogin<typeof handler> diff --git a/framework/shopify/auth/use-logout.tsx b/framework/shopify/auth/use-logout.tsx index d5bf5f6b0..ccbeb8166 100644 --- a/framework/shopify/auth/use-logout.tsx +++ b/framework/shopify/auth/use-logout.tsx @@ -1,6 +1,6 @@ import { useCallback } from 'react' import type { MutationHook } from '@commerce/utils/types' -import useLogout, { UseLogout } from '@commerce/use-logout' +import useLogout, { UseLogout } from '@commerce/auth/use-logout' import useCustomer from '../customer/use-customer' import customerAccessTokenDeleteMutation from '@framework/utils/mutations/customer-access-token-delete' import { diff --git a/framework/shopify/auth/use-signup.tsx b/framework/shopify/auth/use-signup.tsx index 7ad9e996d..f072f1925 100644 --- a/framework/shopify/auth/use-signup.tsx +++ b/framework/shopify/auth/use-signup.tsx @@ -1,7 +1,7 @@ import { useCallback } from 'react' import type { MutationHook } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' -import useSignup, { UseSignup } from '@commerce/use-signup' +import useSignup, { UseSignup } from '@commerce/auth/use-signup' import useCustomer from '../customer/use-customer' import { CustomerCreateInput } from '@framework/schema' diff --git a/framework/shopify/common/get-all-pages.ts b/framework/shopify/common/get-all-pages.ts index 082950634..0be05ee47 100644 --- a/framework/shopify/common/get-all-pages.ts +++ b/framework/shopify/common/get-all-pages.ts @@ -1,5 +1,5 @@ import { getConfig, ShopifyConfig } from '../api' -import { Page, PageEdge } from '../schema' +import { PageEdge } from '../schema' import { getAllPagesQuery } from '../utils/queries' type Variables = { @@ -10,6 +10,14 @@ type ReturnType = { pages: Page[] } +export type Page = { + id: string + name: string + url: string + sort_order?: number + body: string +} + const getAllPages = async (options?: { variables?: Variables config: ShopifyConfig @@ -21,10 +29,13 @@ const getAllPages = async (options?: { const { data } = await config.fetch(getAllPagesQuery, { variables }) const edges = data.pages?.edges - const pages = edges?.map(({ node }: PageEdge) => ({ - ...node, - url: node.handle, - })) + const pages = edges?.map( + ({ node: { title: name, handle: url, ...node } }: PageEdge) => ({ + ...node, + url, + name, + }) + ) return { pages } } diff --git a/framework/shopify/common/get-page.ts b/framework/shopify/common/get-page.ts index 524cece77..2a52c02d5 100644 --- a/framework/shopify/common/get-page.ts +++ b/framework/shopify/common/get-page.ts @@ -1,15 +1,13 @@ -import { GraphQLFetcherResult } from '@commerce/api' - import { getConfig, ShopifyConfig } from '../api' -import getPageQuery from '@framework/utils/queries/get-page-query' -import { Page, PageEdge } from '@framework/schema' +import getPageQuery from '../utils/queries/get-page-query' +import { Page } from './get-all-pages' type Variables = { slug: string } type ReturnType = { - page: any + page: Page } const getPage = async (options: { @@ -20,16 +18,17 @@ const getPage = async (options: { let { config, variables } = options ?? {} config = getConfig(config) - const { data }: GraphQLFetcherResult = await config.fetch(getPageQuery, { + const { data } = await config.fetch(getPageQuery, { variables, }) - const page: Page = data.pageByHandle + const page = data.pageByHandle return { page: page ? { ...page, + name: page.title, url: page?.handle, } : null, diff --git a/framework/shopify/next.config.js b/framework/shopify/next.config.js new file mode 100644 index 000000000..0f9bc31ff --- /dev/null +++ b/framework/shopify/next.config.js @@ -0,0 +1,11 @@ +const providerConfig = require('./config.json') + +module.exports = { + commerce: { + provider: 'shopify', + ...providerConfig, + }, + images: { + domains: ['cdn.shopify.com'], + }, +} diff --git a/framework/shopify/product/get-all-product-paths.ts b/framework/shopify/product/get-all-product-paths.ts index 7eff4e657..4431d1e53 100644 --- a/framework/shopify/product/get-all-product-paths.ts +++ b/framework/shopify/product/get-all-product-paths.ts @@ -1,10 +1,19 @@ +import { Product } from '@commerce/types' import { getConfig, ShopifyConfig } from '../api' import fetchAllProducts from '../api/utils/fetch-all-products' import { ProductEdge } from '../schema' import getAllProductsPathsQuery from '../utils/queries/get-all-products-paths-query' +type ProductPath = { + path: string +} + +export type ProductPathNode = { + node: ProductPath +} + type ReturnType = { - products: any[] + products: ProductPathNode[] } const getAllProductPaths = async (options?: { diff --git a/framework/shopify/product/get-all-products.ts b/framework/shopify/product/get-all-products.ts index b777efc10..e1eb96aac 100644 --- a/framework/shopify/product/get-all-products.ts +++ b/framework/shopify/product/get-all-products.ts @@ -1,10 +1,9 @@ import { GraphQLFetcherResult } from '@commerce/api' import { getConfig, ShopifyConfig } from '../api' -import { Product, ProductEdge } from '../schema' +import { ProductEdge } from '../schema' import { getAllProductsQuery } from '../utils/queries' import { normalizeProduct } from '@framework/utils/normalize' - -export type ProductNode = Product +import { Product } from '@commerce/types' type Variables = { first?: number @@ -12,7 +11,7 @@ type Variables = { } type ReturnType = { - products: any[] + products: Product[] } const getAllProducts = async (options: { @@ -28,9 +27,10 @@ const getAllProducts = async (options: { { variables } ) - const products = data.products?.edges?.map(({ node: p }: ProductEdge) => - normalizeProduct(p) - ) + const products = + data.products?.edges?.map(({ node: p }: ProductEdge) => + normalizeProduct(p) + ) ?? [] return { products, diff --git a/framework/shopify/product/use-price.tsx b/framework/shopify/product/use-price.tsx index a79940a76..0174faf5e 100644 --- a/framework/shopify/product/use-price.tsx +++ b/framework/shopify/product/use-price.tsx @@ -1,2 +1,2 @@ -export * from '@commerce/use-price' -export { default } from '@commerce/use-price' +export * from '@commerce/product/use-price' +export { default } from '@commerce/product/use-price' diff --git a/framework/shopify/utils/normalize.ts b/framework/shopify/utils/normalize.ts index df8c7402b..7c3464f44 100644 --- a/framework/shopify/utils/normalize.ts +++ b/framework/shopify/utils/normalize.ts @@ -77,7 +77,7 @@ export function normalizeProduct(productNode: ShopifyProduct): any { slug: handle?.replace(/^\/+|\/+$/g, ''), price: money(priceRange?.minVariantPrice), images: normalizeProductImages(images), - variants: variants ? normalizeProductVariants(variants) : null, + variants: variants ? normalizeProductVariants(variants) : [], options: options ? options.map((o) => normalizeProductOption(o)) : [], ...rest, } diff --git a/framework/shopify/utils/queries/get-all-pages-query.ts b/framework/shopify/utils/queries/get-all-pages-query.ts index 7d1d71459..7f979dd24 100644 --- a/framework/shopify/utils/queries/get-all-pages-query.ts +++ b/framework/shopify/utils/queries/get-all-pages-query.ts @@ -6,9 +6,6 @@ export const getAllPagesQuery = /* GraphQL */ ` id title handle - body - bodySummary - url } } } diff --git a/framework/shopify/utils/queries/get-page-query.ts b/framework/shopify/utils/queries/get-page-query.ts index 91f80f9e7..01db22e43 100644 --- a/framework/shopify/utils/queries/get-page-query.ts +++ b/framework/shopify/utils/queries/get-page-query.ts @@ -7,7 +7,6 @@ export const getPageQuery = /* GraphQL */ ` title handle body - bodySummary url } } diff --git a/next.config.js b/next.config.js index 725024066..742a0690e 100644 --- a/next.config.js +++ b/next.config.js @@ -1,7 +1,8 @@ const withCommerceConfig = require('./framework/commerce/with-config') -const commerce = { provider: 'bigcommerce' } +const commerce = { provider: 'shopify' } const isBC = commerce.provider === 'bigcommerce' +const isShopify = commerce.provider === 'shopify' module.exports = withCommerceConfig({ commerce, @@ -11,7 +12,7 @@ module.exports = withCommerceConfig({ }, rewrites() { return [ - isBC && { + (isBC || isShopify) && { source: '/checkout', destination: '/api/bigcommerce/checkout', }, diff --git a/tsconfig.json b/tsconfig.json index ace763dbe..e20f37099 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,8 +21,9 @@ "@assets/*": ["assets/*"], "@components/*": ["components/*"], "@commerce": ["framework/commerce"], - "@framework/*": ["framework/shopify/*"], - "@framework": ["framework/shopify"] + "@commerce/*": ["framework/commerce/*"], + "@framework": ["framework/shopify"], + "@framework/*": ["framework/shopify/*"] } }, "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], From e6792d6dd07112d13872cdc39347fcc64b27e9e5 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 25 Feb 2021 11:16:44 +0200 Subject: [PATCH 171/221] Updates --- framework/shopify/auth/use-login.tsx | 2 -- framework/shopify/common/get-all-pages.ts | 8 +++----- framework/shopify/common/get-page.ts | 2 +- framework/shopify/next.config.js | 3 +++ framework/shopify/product/get-product.ts | 6 +----- framework/shopify/utils/normalize.ts | 1 - .../utils/queries/get-all-pages-query.ts | 2 +- .../queries/get-all-products-paths-query.ts | 2 +- .../shopify/utils/queries/get-page-query.ts | 19 ++++++++----------- framework/shopify/utils/queries/index.ts | 2 +- 10 files changed, 19 insertions(+), 28 deletions(-) diff --git a/framework/shopify/auth/use-login.tsx b/framework/shopify/auth/use-login.tsx index 2143907d8..32dd91920 100644 --- a/framework/shopify/auth/use-login.tsx +++ b/framework/shopify/auth/use-login.tsx @@ -15,8 +15,6 @@ import { setCustomerToken } from '@framework/utils' export default useLogin as UseLogin<typeof handler> const getErrorMessage = ({ code, message }: CustomerUserError) => { - console.log(code) - switch (code) { case 'UNIDENTIFIED_CUSTOMER': message = 'Cannot find an account that matches the provided credentials' diff --git a/framework/shopify/common/get-all-pages.ts b/framework/shopify/common/get-all-pages.ts index 0be05ee47..6f06185e2 100644 --- a/framework/shopify/common/get-all-pages.ts +++ b/framework/shopify/common/get-all-pages.ts @@ -25,14 +25,12 @@ const getAllPages = async (options?: { }): Promise<ReturnType> => { let { config, variables = { first: 250 } } = options ?? {} config = getConfig(config) - const { data } = await config.fetch(getAllPagesQuery, { variables }) - const edges = data.pages?.edges - const pages = edges?.map( - ({ node: { title: name, handle: url, ...node } }: PageEdge) => ({ + const pages = data.pages?.edges?.map( + ({ node: { title: name, handle, ...node } }: PageEdge) => ({ ...node, - url, + url: `/${handle}`, name, }) ) diff --git a/framework/shopify/common/get-page.ts b/framework/shopify/common/get-page.ts index 2a52c02d5..6016c8c9a 100644 --- a/framework/shopify/common/get-page.ts +++ b/framework/shopify/common/get-page.ts @@ -22,7 +22,7 @@ const getPage = async (options: { variables, }) - const page = data.pageByHandle + const { pageByHandle: page } = data return { page: page diff --git a/framework/shopify/next.config.js b/framework/shopify/next.config.js index 0f9bc31ff..fa80f3be2 100644 --- a/framework/shopify/next.config.js +++ b/framework/shopify/next.config.js @@ -8,4 +8,7 @@ module.exports = { images: { domains: ['cdn.shopify.com'], }, + typescript: { + ignoreBuildErrors: true, + }, } diff --git a/framework/shopify/product/get-product.ts b/framework/shopify/product/get-product.ts index abb7834a6..1f00288c7 100644 --- a/framework/shopify/product/get-product.ts +++ b/framework/shopify/product/get-product.ts @@ -1,10 +1,6 @@ import { GraphQLFetcherResult } from '@commerce/api' import { getConfig, ShopifyConfig } from '../api' -import { Product } from '../schema' -import getProductQuery from '../utils/queries/get-product-query' -import { normalizeProduct } from '@framework/utils/normalize' - -export type ProductNode = Product +import { normalizeProduct, getProductQuery } from '../utils' type Variables = { slug: string diff --git a/framework/shopify/utils/normalize.ts b/framework/shopify/utils/normalize.ts index 7c3464f44..1eee9f336 100644 --- a/framework/shopify/utils/normalize.ts +++ b/framework/shopify/utils/normalize.ts @@ -41,7 +41,6 @@ const normalizeProductImages = ({ edges }: ImageConnection) => })) const normalizeProductVariants = ({ edges }: ProductVariantConnection) => { - console.log(edges) return edges?.map(({ node: { id, selectedOptions } }) => ({ id, options: selectedOptions.map(({ name, value }: SelectedOption) => diff --git a/framework/shopify/utils/queries/get-all-pages-query.ts b/framework/shopify/utils/queries/get-all-pages-query.ts index 7f979dd24..e3aee1f10 100644 --- a/framework/shopify/utils/queries/get-all-pages-query.ts +++ b/framework/shopify/utils/queries/get-all-pages-query.ts @@ -1,5 +1,5 @@ export const getAllPagesQuery = /* GraphQL */ ` - query($first: Int!) { + query getAllPages($first: Int = 250) { pages(first: $first) { edges { node { diff --git a/framework/shopify/utils/queries/get-all-products-paths-query.ts b/framework/shopify/utils/queries/get-all-products-paths-query.ts index b8fe23b5b..56298c204 100644 --- a/framework/shopify/utils/queries/get-all-products-paths-query.ts +++ b/framework/shopify/utils/queries/get-all-products-paths-query.ts @@ -1,5 +1,5 @@ const getAllProductsPathsQuery = /* GraphQL */ ` - query getAllProductPaths($first: Int!, $cursor: String) { + query getAllProductPaths($first: Int = 250, $cursor: String) { products(first: $first, after: $cursor) { pageInfo { hasNextPage diff --git a/framework/shopify/utils/queries/get-page-query.ts b/framework/shopify/utils/queries/get-page-query.ts index 01db22e43..dcafdc30d 100644 --- a/framework/shopify/utils/queries/get-page-query.ts +++ b/framework/shopify/utils/queries/get-page-query.ts @@ -1,15 +1,12 @@ export const getPageQuery = /* GraphQL */ ` - query($first: Int!) { - pages(first: $first) { - edges { - node { - id - title - handle - body - url - } - } + query getPageBySlug($slug: String!) { + pageByHandle(handle: $slug) { + id + title + handle + body + bodySummary + url } } ` diff --git a/framework/shopify/utils/queries/index.ts b/framework/shopify/utils/queries/index.ts index 4f10d7b65..e19be9c8c 100644 --- a/framework/shopify/utils/queries/index.ts +++ b/framework/shopify/utils/queries/index.ts @@ -1,5 +1,5 @@ export { default as getSiteCollectionsQuery } from './get-all-collections-query' -export { default as getProductQuery } from './get-all-products-paths-query' +export { default as getProductQuery } from './get-product-query' export { default as getAllProductsQuery } from './get-all-products-query' export { default as getAllProductsPathtsQuery } from './get-all-products-paths-query' export { default as getAllProductVendors } from './get-all-product-vendors-query' From e10d50c55a98820d738d0c795cdd32f1015336ba Mon Sep 17 00:00:00 2001 From: Bel Curcio <bel@vercel.com> Date: Thu, 25 Feb 2021 06:27:56 -0300 Subject: [PATCH 172/221] shopify: changes --- framework/commerce/with-config.js | 2 +- framework/shopify/api/index.ts | 1 - .../shopify/api/utils/fetch-graphql-api.ts | 2 +- framework/shopify/cart/use-cart.tsx | 11 ++++-- framework/shopify/config.json | 5 +++ framework/shopify/next.config.js | 11 ++++++ framework/shopify/product/use-price.tsx | 4 +-- framework/shopify/provider.ts | 34 +++++++++++++++++++ 8 files changed, 62 insertions(+), 8 deletions(-) create mode 100644 framework/shopify/config.json create mode 100644 framework/shopify/next.config.js create mode 100644 framework/shopify/provider.ts diff --git a/framework/commerce/with-config.js b/framework/commerce/with-config.js index 6dcf08986..da6705cef 100644 --- a/framework/commerce/with-config.js +++ b/framework/commerce/with-config.js @@ -4,7 +4,7 @@ const merge = require('deepmerge') -const PROVIDERS = ['bigcommerce'] +const PROVIDERS = ['bigcommerce', 'shopify'] function getProviderName() { return process.env.BIGCOMMERCE_STOREFRONT_API_URL ? 'bigcommerce' : null diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts index 0402cfccc..1da7f31c6 100644 --- a/framework/shopify/api/index.ts +++ b/framework/shopify/api/index.ts @@ -3,7 +3,6 @@ import fetchGraphqlApi from './utils/fetch-graphql-api' export interface ShopifyConfig extends CommerceAPIConfig {} -// TODO(bc) const API_URL = process.env.SHOPIFY_STORE_DOMAIN || process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN diff --git a/framework/shopify/api/utils/fetch-graphql-api.ts b/framework/shopify/api/utils/fetch-graphql-api.ts index 946242c93..f3f8b9a17 100644 --- a/framework/shopify/api/utils/fetch-graphql-api.ts +++ b/framework/shopify/api/utils/fetch-graphql-api.ts @@ -21,7 +21,7 @@ const fetchGraphqlApi: GraphQLFetcher = async ( fetchOptions ) => { const config = getConfig() - const url = `https://${config.commerceUrl}/api/2020-10/graphql.json` + const url = `https://${config.commerceUrl}/api/2021-01/graphql.json` const res = await fetch(url, { ...fetchOptions, diff --git a/framework/shopify/cart/use-cart.tsx b/framework/shopify/cart/use-cart.tsx index f067b520d..e69afda0c 100644 --- a/framework/shopify/cart/use-cart.tsx +++ b/framework/shopify/cart/use-cart.tsx @@ -1,9 +1,16 @@ import { useCommerce } from '../index' +import useCart, { UseCart, FetchCartInput } from '@commerce/cart/use-cart' +import type { Cart } from '../types' -export function emptyHook() { +// export default useCart as UseCart<typeof handler> +export default useCart as UseCart + +export const handler = () => { const { checkout } = useCommerce() const { lineItems, totalPriceV2 } = checkout || {} + console.log(checkout) + return { data: { subTotal: totalPriceV2?.amount || 0, @@ -38,5 +45,3 @@ export function emptyHook() { isLoading: false, } } - -export default emptyHook diff --git a/framework/shopify/config.json b/framework/shopify/config.json new file mode 100644 index 000000000..17ef37e25 --- /dev/null +++ b/framework/shopify/config.json @@ -0,0 +1,5 @@ +{ + "features": { + "wishlist": false + } +} diff --git a/framework/shopify/next.config.js b/framework/shopify/next.config.js new file mode 100644 index 000000000..0f9bc31ff --- /dev/null +++ b/framework/shopify/next.config.js @@ -0,0 +1,11 @@ +const providerConfig = require('./config.json') + +module.exports = { + commerce: { + provider: 'shopify', + ...providerConfig, + }, + images: { + domains: ['cdn.shopify.com'], + }, +} diff --git a/framework/shopify/product/use-price.tsx b/framework/shopify/product/use-price.tsx index a79940a76..0174faf5e 100644 --- a/framework/shopify/product/use-price.tsx +++ b/framework/shopify/product/use-price.tsx @@ -1,2 +1,2 @@ -export * from '@commerce/use-price' -export { default } from '@commerce/use-price' +export * from '@commerce/product/use-price' +export { default } from '@commerce/product/use-price' diff --git a/framework/shopify/provider.ts b/framework/shopify/provider.ts new file mode 100644 index 000000000..31b958a21 --- /dev/null +++ b/framework/shopify/provider.ts @@ -0,0 +1,34 @@ +import { handler as useCart } from './cart/use-cart' +import { handler as useAddItem } from './cart/use-add-item' +import { handler as useUpdateItem } from './cart/use-update-item' +import { handler as useRemoveItem } from './cart/use-remove-item' + +import { handler as useWishlist } from './wishlist/use-wishlist' +import { handler as useWishlistAddItem } from './wishlist/use-add-item' +import { handler as useWishlistRemoveItem } from './wishlist/use-remove-item' + +import { handler as useCustomer } from './customer/use-customer' +import { handler as useSearch } from './product/use-search' + +import { handler as useLogin } from './auth/use-login' +import { handler as useLogout } from './auth/use-logout' +import { handler as useSignup } from './auth/use-signup' + +import fetcher from './fetcher' + +export const shopifyProvider = { + locale: 'en-us', + cartCookie: 'sp_cartId', + fetcher, + cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, + wishlist: { + useWishlist, + useAddItem: useWishlistAddItem, + useRemoveItem: useWishlistRemoveItem, + }, + customer: { useCustomer }, + products: { useSearch }, + auth: { useLogin, useLogout, useSignup }, +} + +export type ShopifyProvider = typeof shopifyProvider From 3b386e3d55dddcf94d62ca19e8411be49d6378f6 Mon Sep 17 00:00:00 2001 From: Bel Curcio <bel@vercel.com> Date: Thu, 25 Feb 2021 07:48:14 -0300 Subject: [PATCH 173/221] shopify: changes --- .../WishlistButton/WishlistButton.tsx | 6 +-- framework/shopify/api/index.ts | 9 ++-- framework/shopify/fetcher.ts | 51 ++++++++++++++++++ package.json | 3 +- yarn.lock | 53 ++++++++++--------- 5 files changed, 88 insertions(+), 34 deletions(-) create mode 100644 framework/shopify/fetcher.ts diff --git a/components/wishlist/WishlistButton/WishlistButton.tsx b/components/wishlist/WishlistButton/WishlistButton.tsx index 5a1f17e5c..57f769e3d 100644 --- a/components/wishlist/WishlistButton/WishlistButton.tsx +++ b/components/wishlist/WishlistButton/WishlistButton.tsx @@ -1,12 +1,12 @@ import React, { FC, useState } from 'react' import cn from 'classnames' - import { useUI } from '@components/ui' -import type { Product, ProductVariant } from '@commerce/types' +import { Heart } from '@components/icons' import useAddItem from '@framework/wishlist/use-add-item' import useCustomer from '@framework/customer/use-customer' -import useRemoveItem from '@framework/wishlist/use-remove-item' import useWishlist from '@framework/wishlist/use-wishlist' +import useRemoveItem from '@framework/wishlist/use-remove-item' +import type { Product, ProductVariant } from '@commerce/types' type Props = { productId: Product['id'] diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts index 1da7f31c6..8646d0136 100644 --- a/framework/shopify/api/index.ts +++ b/framework/shopify/api/index.ts @@ -3,14 +3,11 @@ import fetchGraphqlApi from './utils/fetch-graphql-api' export interface ShopifyConfig extends CommerceAPIConfig {} -const API_URL = - process.env.SHOPIFY_STORE_DOMAIN || - process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN -const API_TOKEN = - process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN || - process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN +const API_URL = process.env.SHOPIFY_STORE_DOMAIN +const API_TOKEN = process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN if (!API_URL) { + console.log(process.env) throw new Error( `The environment variable SHOPIFY_STORE_DOMAIN is missing and it's required to access your store` ) diff --git a/framework/shopify/fetcher.ts b/framework/shopify/fetcher.ts new file mode 100644 index 000000000..73500ee93 --- /dev/null +++ b/framework/shopify/fetcher.ts @@ -0,0 +1,51 @@ +import { FetcherError } from '@commerce/utils/errors' +import type { Fetcher } from '@commerce/utils/types' + +async function getText(res: Response) { + try { + return (await res.text()) || res.statusText + } catch (error) { + return res.statusText + } +} + +async function getError(res: Response) { + if (res.headers.get('Content-Type')?.includes('application/json')) { + const data = await res.json() + return new FetcherError({ errors: data.errors, status: res.status }) + } + return new FetcherError({ message: await getText(res), status: res.status }) +} + +const fetcher: Fetcher = async ({ + url, + query, + method = 'POST', + variables, + body: bodyObj, +}) => { + // const config = getConfig() + // url = `https://${process.env.SHOPIFY_STORE_DOMAIN}/api/2021-01/graphql.json` + + const hasBody = Boolean(variables || bodyObj) + const body = hasBody + ? JSON.stringify(variables ? { query, variables } : bodyObj) + : undefined + const headers = hasBody + ? { + 'X-Shopify-Storefront-Access-Token': config.apiToken, + 'Content-Type': 'application/json', + } + : undefined + + const res = await fetch(url!, { method, body, headers }) + + if (res.ok) { + const { data } = await res.json() + return data + } + + throw await getError(res) +} + +export default fetcher diff --git a/package.json b/package.json index d2ed966c3..906d950dc 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "lodash.debounce": "^4.0.8", "lodash.random": "^3.2.0", "lodash.throttle": "^4.1.1", - "next": "^10.0.7-canary.3", + "next": "^10.0.7", "next-seo": "^4.11.0", "next-themes": "^0.0.4", "postcss": "^8.2.4", @@ -65,6 +65,7 @@ "@types/lodash.throttle": "^4.1.6", "@types/node": "^14.14.16", "@types/react": "^17.0.0", + "@types/shopify-buy": "^2.10.5", "deepmerge": "^4.2.2", "graphql": "^15.4.0", "husky": "^4.3.8", diff --git a/yarn.lock b/yarn.lock index c6114e0f5..9238b1f03 100644 --- a/yarn.lock +++ b/yarn.lock @@ -862,20 +862,20 @@ dependencies: webpack-bundle-analyzer "4.3.0" -"@next/env@10.0.7-canary.8": - version "10.0.7-canary.8" - resolved "https://registry.yarnpkg.com/@next/env/-/env-10.0.7-canary.8.tgz#b241d5a9f8824bd3f1c6ca066decc46ec78012da" - integrity sha512-4rNuMMj+eG+vP6hr61Fo6GAjhtyHV+4fgLYcvd3HAnsfulU2TX/1cufZH04N5qXgK1IzgO0t0ZUweIXTn6vj7A== +"@next/env@10.0.7": + version "10.0.7" + resolved "https://registry.yarnpkg.com/@next/env/-/env-10.0.7.tgz#7b3e87a9029ca37491e2ec25c27593f0906725f9" + integrity sha512-/vnz2SL/mk3Tei58WfRtVnvz5xHmAqcBmZL5sTBEy1CZG6OtZGNx0qAFCjtVkeJ5m1Bh4Ut+WFh/RF333wx8Sg== -"@next/polyfill-module@10.0.7-canary.8": - version "10.0.7-canary.8" - resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-10.0.7-canary.8.tgz#d2222f4f686953ae9983a3612b061199352584ff" - integrity sha512-3dX9HZ1N8KDI0kmS6SIS5z7a0V8QULwVLR2YH7a2q/JcpFMs4h6AadCOwgjb8G2P6eRKj0VN9tUS/pDdCsiqjw== +"@next/polyfill-module@10.0.7": + version "10.0.7" + resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-10.0.7.tgz#ec45ec1f28f47beed15ed67dffc907edd7143094" + integrity sha512-HxqzRpoSgmZP0kRIWwH+e0SgtAXqJ0VkYtwWcsQFED8+xF4Eqn+7Twyp4uE6hutC8gr8IFSFqH+DEYhRtg1ltQ== -"@next/react-dev-overlay@10.0.7-canary.8": - version "10.0.7-canary.8" - resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-10.0.7-canary.8.tgz#0ec70bb80debd770e66ef002144b43eefc36cff9" - integrity sha512-PUjmUH+mMA+uQMlPT30y7UJg92bZY//xujKchvZqj8AOngQhZCjr8cdd97MtwwWOFYBB5ALUXXN8zkjmgcvDtw== +"@next/react-dev-overlay@10.0.7": + version "10.0.7" + resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-10.0.7.tgz#5fe777011cab75ec09ad539ee61bb95ab5a2bdeb" + integrity sha512-yq71MDHVqN2N+IqOvZDiFsMpQrBcymrdpTx1ShhAADX7cWQvW4dhcIir4BbfrS10vS1LLz/3a8uKZkGdNoJj3w== dependencies: "@babel/code-frame" "7.12.11" anser "1.4.9" @@ -889,10 +889,10 @@ stacktrace-parser "0.1.10" strip-ansi "6.0.0" -"@next/react-refresh-utils@10.0.7-canary.8": - version "10.0.7-canary.8" - resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-10.0.7-canary.8.tgz#2dc4b584749fd961d77eb88d7cd7f7239671fdb9" - integrity sha512-1KkWSXIjaCg9mrj4sYl/mZfuP+tsJjvgsbpECt8tE6phPrablZgxlpJfMx/CtFxh+JYE2kHNxAeunQoIQi73zw== +"@next/react-refresh-utils@10.0.7": + version "10.0.7" + resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-10.0.7.tgz#866ce30fe2f321e011255e81ed5d55eeda05894b" + integrity sha512-d/71vtQglv6m7sh4W1O9drc2hYti7UnAdEXfBLZAS354g2S80lvCRGIhbDrMx4w0rpShoxBIZboE2++LihAESg== "@nodelib/fs.scandir@2.1.4": version "2.1.4" @@ -1129,6 +1129,11 @@ "@types/prop-types" "*" csstype "^3.0.2" +"@types/shopify-buy@^2.10.5": + version "2.10.5" + resolved "https://registry.yarnpkg.com/@types/shopify-buy/-/shopify-buy-2.10.5.tgz#c7184b792989a968af879224e8990cde4db45519" + integrity sha512-12Le/iXPynrONntux/OaXf9+yx0zbMBKhwywdej9mfR8YhXB82pPZYzU3o6j8cjK1uCQ/wWkqLTftpaXpeMKig== + "@types/warning@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52" @@ -4656,17 +4661,17 @@ next-unused@^0.0.3: madge "^3.8.0" ts-loader "^7.0.0" -next@^10.0.7-canary.3: - version "10.0.7-canary.8" - resolved "https://registry.yarnpkg.com/next/-/next-10.0.7-canary.8.tgz#7c37efb5c2c3d231b674e382b9bb0b30f533aaf9" - integrity sha512-F0AmkgDq9Eq43YzP1aclz0s8J8x/8TzOzeOaeGY6nzblnJ3EjdzdkVM5YAKYC738MAQ6zR7Wat9VCs96yWZ7WA== +next@^10.0.7: + version "10.0.7" + resolved "https://registry.yarnpkg.com/next/-/next-10.0.7.tgz#442f8e1da7454de33b0bbcc1ce5684b923597ee6" + integrity sha512-We0utmwwfkvO12eLyUZd3tX9VLDE3FPpOaHpH3kqKdUTxJzUKt8FLBXCTm0mwsTKW5XColWG8mJvz2OLu3+3QA== dependencies: "@babel/runtime" "7.12.5" "@hapi/accept" "5.0.1" - "@next/env" "10.0.7-canary.8" - "@next/polyfill-module" "10.0.7-canary.8" - "@next/react-dev-overlay" "10.0.7-canary.8" - "@next/react-refresh-utils" "10.0.7-canary.8" + "@next/env" "10.0.7" + "@next/polyfill-module" "10.0.7" + "@next/react-dev-overlay" "10.0.7" + "@next/react-refresh-utils" "10.0.7" "@opentelemetry/api" "0.14.0" ast-types "0.13.2" browserslist "4.16.1" From f431ba6dbcca783d079bbc7e054f24ce3049b933 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 25 Feb 2021 16:04:16 +0200 Subject: [PATCH 174/221] Update next.config.js --- framework/shopify/next.config.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/framework/shopify/next.config.js b/framework/shopify/next.config.js index fa80f3be2..0f9bc31ff 100644 --- a/framework/shopify/next.config.js +++ b/framework/shopify/next.config.js @@ -8,7 +8,4 @@ module.exports = { images: { domains: ['cdn.shopify.com'], }, - typescript: { - ignoreBuildErrors: true, - }, } From 73349246942fd402eb1776b7efa8319bb18bc3df Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 25 Feb 2021 23:05:47 +0200 Subject: [PATCH 175/221] Shopify Provider Updates (#209) * Implement Shopify Provider * Update README.md * Update README.md * normalizations & missing files * Update index.ts * fixes * Update normalize.ts * fix: cart error on first load * shopify checkout redirect & api handler * Update get-checkout-id.ts * Fix: color option * Update normalize.ts * changes * Update next.config.js * start customer auth & signup * Update config.ts * Login, Sign Up, Log Out, and checkout & customer association * Automatic login after sign-up * Update handle-login.ts * changes * Revert "Merge branch 'agnostic' of https://github.com/vercel/commerce into agnostic" This reverts commit 23c8ed7c2d48d30e74ad94216f9910650fadf30c, reversing changes made to bf50965a39ef0b1b956461ebe62070809fbe1d63. * change readme * Revert "Merge branch 'master' of https://github.com/vercel/commerce into agnostic" This reverts commit bf50965a39ef0b1b956461ebe62070809fbe1d63, reversing changes made to 0dad4ddedbf0bff2d0b5800ca469fda0073889ea. * Revert "Revert "Merge branch 'agnostic' of https://github.com/vercel/commerce into agnostic"" This reverts commit c9a43f1bce0572d0eff41f3af893be8bdb00bedd. * align with upstream changes * query all products for vendors & paths, improve search * Update use-search.tsx * fix cart after upstream changes * fixes after upstream changes * Moved handler to each hook * Added initial version of useAddItem * Updated types * Update use-add-item.tsx * Moved auth & cart hooks + several fixes * Updated cart item, fixed deprecations * Update next.config.js * Aligned with upstream changes * Updates * Update next.config.js --- .../cart/CartSidebarView/CartSidebarView.tsx | 3 +- components/common/Navbar/Navbar.tsx | 3 + framework/shopify/api/checkout/index.ts | 47 +- framework/shopify/api/customer.ts | 1 + framework/shopify/api/index.ts | 27 +- .../shopify/api/utils/create-api-handler.ts | 58 + .../shopify/api/utils/fetch-all-products.ts | 41 + .../shopify/api/utils/fetch-graphql-api.ts | 39 +- framework/shopify/api/utils/fetch.ts | 2 + .../shopify/api/utils/is-allowed-method.ts | 28 + framework/shopify/auth/use-login.tsx | 79 +- framework/shopify/auth/use-logout.tsx | 44 +- framework/shopify/auth/use-signup.tsx | 79 +- framework/shopify/cart/index.ts | 2 - framework/shopify/cart/use-add-item.tsx | 81 +- framework/shopify/cart/use-cart.tsx | 95 +- framework/shopify/cart/use-remove-item.tsx | 84 +- framework/shopify/cart/use-update-item.tsx | 120 +- .../shopify/cart/utils/checkout-create.ts | 25 + .../shopify/cart/utils/checkout-to-cart.ts | 42 + framework/shopify/cart/utils/fetcher.ts | 30 + framework/shopify/cart/utils/index.ts | 2 + framework/shopify/common/get-all-pages.ts | 56 +- framework/shopify/common/get-page.ts | 38 + framework/shopify/common/get-site-info.ts | 43 +- framework/shopify/const.ts | 11 + framework/shopify/customer/get-customer-id.ts | 24 + framework/shopify/customer/index.ts | 1 + framework/shopify/customer/use-customer.tsx | 55 +- framework/shopify/fetcher.ts | 59 +- framework/shopify/index.tsx | 130 +- .../shopify/product/get-all-collections.ts | 29 + .../shopify/product/get-all-product-paths.ts | 51 +- framework/shopify/product/get-all-products.ts | 44 +- framework/shopify/product/get-product.ts | 33 +- framework/shopify/product/use-search.tsx | 76 +- framework/shopify/provider.ts | 17 +- framework/shopify/schema.d.ts | 4985 +++++++++ framework/shopify/schema.graphql | 9631 +++++++++++++++++ framework/shopify/types.ts | 160 +- framework/shopify/utils/customer-token.ts | 12 + framework/shopify/utils/get-categories.ts | 29 + framework/shopify/utils/get-checkout-id.ts | 8 + .../shopify/utils/get-search-variables.ts | 30 + framework/shopify/utils/get-sort-variables.ts | 32 + framework/shopify/utils/get-vendors.ts | 36 + .../shopify/utils/handle-fetch-response.ts | 27 + framework/shopify/utils/handle-login.ts | 39 + framework/shopify/utils/index.ts | 10 + .../associate-customer-with-checkout.ts | 18 + .../utils/mutations/checkout-create.ts | 16 + .../utils/mutations/checkout-line-item-add.ts | 16 + .../mutations/checkout-line-item-remove.ts | 19 + .../mutations/checkout-line-item-update.ts | 16 + .../mutations/customer-access-token-create.ts | 16 + .../mutations/customer-access-token-delete.ts | 14 + .../utils/mutations/customer-create.ts | 15 + framework/shopify/utils/mutations/index.ts | 7 + framework/shopify/utils/normalize.ts | 133 + .../queries/get-all-collections-query.ts | 14 + .../utils/queries/get-all-pages-query.ts | 14 + .../queries/get-all-product-vendors-query.ts | 17 + .../queries/get-all-products-paths-query.ts | 17 + .../utils/queries/get-all-products-query.ts | 54 + .../utils/queries/get-checkout-query.ts | 62 + .../queries/get-collection-products-query.ts | 17 + .../utils/queries/get-customer-id-query.ts | 8 + .../utils/queries/get-customer-query.ts | 16 + .../shopify/utils/queries/get-page-query.ts | 13 + .../utils/queries/get-product-query.ts | 68 + framework/shopify/utils/queries/index.ts | 10 + framework/shopify/wishlist/use-wishlist.tsx | 2 +- next.config.js | 5 +- tsconfig.json | 4 +- 74 files changed, 16565 insertions(+), 624 deletions(-) create mode 100644 framework/shopify/api/customer.ts create mode 100644 framework/shopify/api/utils/create-api-handler.ts create mode 100644 framework/shopify/api/utils/fetch-all-products.ts create mode 100644 framework/shopify/api/utils/fetch.ts create mode 100644 framework/shopify/api/utils/is-allowed-method.ts create mode 100644 framework/shopify/cart/utils/checkout-create.ts create mode 100644 framework/shopify/cart/utils/checkout-to-cart.ts create mode 100644 framework/shopify/cart/utils/fetcher.ts create mode 100644 framework/shopify/cart/utils/index.ts create mode 100644 framework/shopify/common/get-page.ts create mode 100644 framework/shopify/const.ts create mode 100644 framework/shopify/customer/get-customer-id.ts create mode 100644 framework/shopify/customer/index.ts create mode 100644 framework/shopify/product/get-all-collections.ts create mode 100644 framework/shopify/schema.d.ts create mode 100644 framework/shopify/schema.graphql create mode 100644 framework/shopify/utils/customer-token.ts create mode 100644 framework/shopify/utils/get-categories.ts create mode 100644 framework/shopify/utils/get-checkout-id.ts create mode 100644 framework/shopify/utils/get-search-variables.ts create mode 100644 framework/shopify/utils/get-sort-variables.ts create mode 100644 framework/shopify/utils/get-vendors.ts create mode 100644 framework/shopify/utils/handle-fetch-response.ts create mode 100644 framework/shopify/utils/handle-login.ts create mode 100644 framework/shopify/utils/index.ts create mode 100644 framework/shopify/utils/mutations/associate-customer-with-checkout.ts create mode 100644 framework/shopify/utils/mutations/checkout-create.ts create mode 100644 framework/shopify/utils/mutations/checkout-line-item-add.ts create mode 100644 framework/shopify/utils/mutations/checkout-line-item-remove.ts create mode 100644 framework/shopify/utils/mutations/checkout-line-item-update.ts create mode 100644 framework/shopify/utils/mutations/customer-access-token-create.ts create mode 100644 framework/shopify/utils/mutations/customer-access-token-delete.ts create mode 100644 framework/shopify/utils/mutations/customer-create.ts create mode 100644 framework/shopify/utils/mutations/index.ts create mode 100644 framework/shopify/utils/normalize.ts create mode 100644 framework/shopify/utils/queries/get-all-collections-query.ts create mode 100644 framework/shopify/utils/queries/get-all-pages-query.ts create mode 100644 framework/shopify/utils/queries/get-all-product-vendors-query.ts create mode 100644 framework/shopify/utils/queries/get-all-products-paths-query.ts create mode 100644 framework/shopify/utils/queries/get-all-products-query.ts create mode 100644 framework/shopify/utils/queries/get-checkout-query.ts create mode 100644 framework/shopify/utils/queries/get-collection-products-query.ts create mode 100644 framework/shopify/utils/queries/get-customer-id-query.ts create mode 100644 framework/shopify/utils/queries/get-customer-query.ts create mode 100644 framework/shopify/utils/queries/get-page-query.ts create mode 100644 framework/shopify/utils/queries/get-product-query.ts create mode 100644 framework/shopify/utils/queries/index.ts diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index 3ceda44fe..cb932247f 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -8,6 +8,7 @@ import useCart from '@framework/cart/use-cart' import usePrice from '@framework/product/use-price' import CartItem from '../CartItem' import s from './CartSidebarView.module.css' +import { LineItem } from '@commerce/types' const CartSidebarView: FC = () => { const { closeSidebar } = useUI() @@ -91,7 +92,7 @@ const CartSidebarView: FC = () => { My Cart </h2> <ul className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accents-3 border-t border-accents-3"> - {data!.lineItems.map((item) => ( + {data!.lineItems.map((item: any) => ( <CartItem key={item.id} item={item} diff --git a/components/common/Navbar/Navbar.tsx b/components/common/Navbar/Navbar.tsx index b2a372f66..fcf9f1e10 100644 --- a/components/common/Navbar/Navbar.tsx +++ b/components/common/Navbar/Navbar.tsx @@ -25,6 +25,9 @@ const Navbar: FC = () => ( <Link href="/search?q=accessories"> <a className={s.link}>Accessories</a> </Link> + <Link href="/search?q=shoes"> + <a className={s.link}>Shoes</a> + </Link> </nav> </div> diff --git a/framework/shopify/api/checkout/index.ts b/framework/shopify/api/checkout/index.ts index ea9b101e1..d5d6d7f6e 100644 --- a/framework/shopify/api/checkout/index.ts +++ b/framework/shopify/api/checkout/index.ts @@ -1 +1,46 @@ -export default function () {} +import isAllowedMethod from '../utils/is-allowed-method' +import createApiHandler, { + ShopifyApiHandler, +} from '../utils/create-api-handler' + +import { + SHOPIFY_CHECKOUT_ID_COOKIE, + SHOPIFY_CHECKOUT_URL_COOKIE, + SHOPIFY_CUSTOMER_TOKEN_COOKIE, +} from '@framework/const' + +import { getConfig } from '..' +import associateCustomerWithCheckoutMutation from '@framework/utils/mutations/associate-customer-with-checkout' + +const METHODS = ['GET'] + +const checkoutApi: ShopifyApiHandler<any> = async (req, res, config) => { + if (!isAllowedMethod(req, res, METHODS)) return + + config = getConfig() + + const { cookies } = req + const checkoutUrl = cookies[SHOPIFY_CHECKOUT_URL_COOKIE] + const customerCookie = cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE] + + if (customerCookie) { + try { + await config.fetch(associateCustomerWithCheckoutMutation, { + variables: { + checkoutId: cookies[SHOPIFY_CHECKOUT_ID_COOKIE], + customerAccessToken: cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE], + }, + }) + } catch (error) { + console.error(error) + } + } + + if (checkoutUrl) { + res.redirect(checkoutUrl) + } else { + res.redirect('/cart') + } +} + +export default createApiHandler(checkoutApi, {}, {}) diff --git a/framework/shopify/api/customer.ts b/framework/shopify/api/customer.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/shopify/api/customer.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts index 8646d0136..184d03748 100644 --- a/framework/shopify/api/index.ts +++ b/framework/shopify/api/index.ts @@ -1,24 +1,29 @@ import type { CommerceAPIConfig } from '@commerce/api' -import fetchGraphqlApi from './utils/fetch-graphql-api' -export interface ShopifyConfig extends CommerceAPIConfig {} - -const API_URL = process.env.SHOPIFY_STORE_DOMAIN -const API_TOKEN = process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN +import { + API_URL, + API_TOKEN, + SHOPIFY_CHECKOUT_ID_COOKIE, + SHOPIFY_CUSTOMER_TOKEN_COOKIE, +} from '@framework/const' if (!API_URL) { console.log(process.env) throw new Error( - `The environment variable SHOPIFY_STORE_DOMAIN is missing and it's required to access your store` + `The environment variable NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN is missing and it's required to access your store` ) } if (!API_TOKEN) { throw new Error( - `The environment variable SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store` + `The environment variable NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store` ) } +import fetchGraphqlApi from './utils/fetch-graphql-api' + +export interface ShopifyConfig extends CommerceAPIConfig {} + export class Config { private config: ShopifyConfig @@ -40,11 +45,11 @@ export class Config { const config = new Config({ commerceUrl: API_URL, - apiToken: API_TOKEN, - // TODO - // @ts-ignore + apiToken: API_TOKEN!, + cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, + cartCookieMaxAge: 60 * 60 * 24 * 30, fetch: fetchGraphqlApi, - customerCookie: 'SHOP_TOKEN', + customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE, }) export function getConfig(userConfig?: Partial<ShopifyConfig>) { diff --git a/framework/shopify/api/utils/create-api-handler.ts b/framework/shopify/api/utils/create-api-handler.ts new file mode 100644 index 000000000..8820aeabc --- /dev/null +++ b/framework/shopify/api/utils/create-api-handler.ts @@ -0,0 +1,58 @@ +import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next' +import { ShopifyConfig, getConfig } from '..' + +export type ShopifyApiHandler< + T = any, + H extends ShopifyHandlers = {}, + Options extends {} = {} +> = ( + req: NextApiRequest, + res: NextApiResponse<ShopifyApiResponse<T>>, + config: ShopifyConfig, + handlers: H, + // Custom configs that may be used by a particular handler + options: Options +) => void | Promise<void> + +export type ShopifyHandler<T = any, Body = null> = (options: { + req: NextApiRequest + res: NextApiResponse<ShopifyApiResponse<T>> + config: ShopifyConfig + body: Body +}) => void | Promise<void> + +export type ShopifyHandlers<T = any> = { + [k: string]: ShopifyHandler<T, any> +} + +export type ShopifyApiResponse<T> = { + data: T | null + errors?: { message: string; code?: string }[] +} + +export default function createApiHandler< + T = any, + H extends ShopifyHandlers = {}, + Options extends {} = {} +>( + handler: ShopifyApiHandler<T, H, Options>, + handlers: H, + defaultOptions: Options +) { + return function getApiHandler({ + config, + operations, + options, + }: { + config?: ShopifyConfig + operations?: Partial<H> + options?: Options extends {} ? Partial<Options> : never + } = {}): NextApiHandler { + const ops = { ...operations, ...handlers } + const opts = { ...defaultOptions, ...options } + + return function apiHandler(req, res) { + return handler(req, res, getConfig(config), ops, opts) + } + } +} diff --git a/framework/shopify/api/utils/fetch-all-products.ts b/framework/shopify/api/utils/fetch-all-products.ts new file mode 100644 index 000000000..efeb809f1 --- /dev/null +++ b/framework/shopify/api/utils/fetch-all-products.ts @@ -0,0 +1,41 @@ +import { ProductEdge } from '@framework/schema' +import { ShopifyConfig } from '..' + +const fetchAllProducts = async ({ + config, + query, + variables, + acc = [], + cursor, +}: { + config: ShopifyConfig + query: string + acc?: ProductEdge[] + variables?: any + cursor?: string +}): Promise<ProductEdge[]> => { + const { data } = await config.fetch(query, { + variables: { ...variables, cursor }, + }) + + const edges: ProductEdge[] = data.products?.edges ?? [] + const hasNextPage = data.products?.pageInfo?.hasNextPage + acc = acc.concat(edges) + + if (hasNextPage) { + const cursor = edges.pop()?.cursor + if (cursor) { + return fetchAllProducts({ + config, + query, + variables, + acc, + cursor, + }) + } + } + + return acc +} + +export default fetchAllProducts diff --git a/framework/shopify/api/utils/fetch-graphql-api.ts b/framework/shopify/api/utils/fetch-graphql-api.ts index f3f8b9a17..92d4f2cf6 100644 --- a/framework/shopify/api/utils/fetch-graphql-api.ts +++ b/framework/shopify/api/utils/fetch-graphql-api.ts @@ -1,33 +1,19 @@ -import { CommerceAPIFetchOptions } from '@commerce/api' -import { FetcherError } from '@commerce/utils/errors' -import { getConfig } from '../index' +import type { GraphQLFetcher } from '@commerce/api' +import fetch from './fetch' -export interface GraphQLFetcherResult<Data = any> { - data: Data - res: Response -} -export type GraphQLFetcher< - Data extends GraphQLFetcherResult = GraphQLFetcherResult, - Variables = any -> = ( - query: string, - queryData?: CommerceAPIFetchOptions<Variables>, - fetchOptions?: RequestInit -) => Promise<Data> +import { API_URL, API_TOKEN } from '../../const' +import { getError } from '@framework/utils/handle-fetch-response' const fetchGraphqlApi: GraphQLFetcher = async ( query: string, { variables } = {}, fetchOptions ) => { - const config = getConfig() - const url = `https://${config.commerceUrl}/api/2021-01/graphql.json` - - const res = await fetch(url, { + const res = await fetch(API_URL, { ...fetchOptions, method: 'POST', headers: { - 'X-Shopify-Storefront-Access-Token': config.apiToken, + 'X-Shopify-Storefront-Access-Token': API_TOKEN!, ...fetchOptions?.headers, 'Content-Type': 'application/json', }, @@ -37,15 +23,12 @@ const fetchGraphqlApi: GraphQLFetcher = async ( }), }) - const json = await res.json() - if (json.errors) { - throw new FetcherError({ - errors: json.errors ?? [{ message: 'Failed to fetch Shopify API' }], - status: res.status, - }) + const { data, errors, status } = await res.json() + + if (errors) { + throw getError(errors, status) } - return { data: json.data, res } + return { data, res } } - export default fetchGraphqlApi diff --git a/framework/shopify/api/utils/fetch.ts b/framework/shopify/api/utils/fetch.ts new file mode 100644 index 000000000..0b8367102 --- /dev/null +++ b/framework/shopify/api/utils/fetch.ts @@ -0,0 +1,2 @@ +import zeitFetch from '@vercel/fetch' +export default zeitFetch() diff --git a/framework/shopify/api/utils/is-allowed-method.ts b/framework/shopify/api/utils/is-allowed-method.ts new file mode 100644 index 000000000..78bbba568 --- /dev/null +++ b/framework/shopify/api/utils/is-allowed-method.ts @@ -0,0 +1,28 @@ +import type { NextApiRequest, NextApiResponse } from 'next' + +export default function isAllowedMethod( + req: NextApiRequest, + res: NextApiResponse, + allowedMethods: string[] +) { + const methods = allowedMethods.includes('OPTIONS') + ? allowedMethods + : [...allowedMethods, 'OPTIONS'] + + if (!req.method || !methods.includes(req.method)) { + res.status(405) + res.setHeader('Allow', methods.join(', ')) + res.end() + return false + } + + if (req.method === 'OPTIONS') { + res.status(200) + res.setHeader('Allow', methods.join(', ')) + res.setHeader('Content-Length', '0') + res.end() + return false + } + + return true +} diff --git a/framework/shopify/auth/use-login.tsx b/framework/shopify/auth/use-login.tsx index 75f067c3a..32dd91920 100644 --- a/framework/shopify/auth/use-login.tsx +++ b/framework/shopify/auth/use-login.tsx @@ -1,13 +1,76 @@ import { useCallback } from 'react' +import type { MutationHook } from '@commerce/utils/types' +import { CommerceError, ValidationError } from '@commerce/utils/errors' +import useCustomer from '../customer/use-customer' +import createCustomerAccessTokenMutation from '../utils/mutations/customer-access-token-create' +import { + CustomerAccessTokenCreateInput, + CustomerUserError, + Mutation, + MutationCheckoutCreateArgs, +} from '@framework/schema' +import useLogin, { UseLogin } from '@commerce/auth/use-login' +import { setCustomerToken } from '@framework/utils' -export function emptyHook() { - const useEmptyHook = async (options = {}) => { - return useCallback(async function () { - return Promise.resolve() - }, []) +export default useLogin as UseLogin<typeof handler> + +const getErrorMessage = ({ code, message }: CustomerUserError) => { + switch (code) { + case 'UNIDENTIFIED_CUSTOMER': + message = 'Cannot find an account that matches the provided credentials' + break } - - return useEmptyHook + return message } -export default emptyHook +export const handler: MutationHook<null, {}, CustomerAccessTokenCreateInput> = { + fetchOptions: { + query: createCustomerAccessTokenMutation, + }, + async fetcher({ input: { email, password }, options, fetch }) { + if (!(email && password)) { + throw new CommerceError({ + message: + 'A first name, last name, email and password are required to login', + }) + } + + const { customerAccessTokenCreate } = await fetch< + Mutation, + MutationCheckoutCreateArgs + >({ + ...options, + variables: { + input: { email, password }, + }, + }) + + const errors = customerAccessTokenCreate?.customerUserErrors + + if (errors && errors.length) { + throw new ValidationError({ + message: getErrorMessage(errors[0]), + }) + } + const customerAccessToken = customerAccessTokenCreate?.customerAccessToken + const accessToken = customerAccessToken?.accessToken + + if (accessToken) { + setCustomerToken(accessToken) + } + + return null + }, + useHook: ({ fetch }) => () => { + const { revalidate } = useCustomer() + + return useCallback( + async function login(input) { + const data = await fetch({ input }) + await revalidate() + return data + }, + [fetch, revalidate] + ) + }, +} diff --git a/framework/shopify/auth/use-logout.tsx b/framework/shopify/auth/use-logout.tsx index 75f067c3a..ccbeb8166 100644 --- a/framework/shopify/auth/use-logout.tsx +++ b/framework/shopify/auth/use-logout.tsx @@ -1,13 +1,39 @@ import { useCallback } from 'react' +import type { MutationHook } from '@commerce/utils/types' +import useLogout, { UseLogout } from '@commerce/auth/use-logout' +import useCustomer from '../customer/use-customer' +import customerAccessTokenDeleteMutation from '@framework/utils/mutations/customer-access-token-delete' +import { + getCustomerToken, + setCustomerToken, +} from '@framework/utils/customer-token' -export function emptyHook() { - const useEmptyHook = async (options = {}) => { - return useCallback(async function () { - return Promise.resolve() - }, []) - } +export default useLogout as UseLogout<typeof handler> - return useEmptyHook +export const handler: MutationHook<null> = { + fetchOptions: { + query: customerAccessTokenDeleteMutation, + }, + async fetcher({ options, fetch }) { + await fetch({ + ...options, + variables: { + customerAccessToken: getCustomerToken(), + }, + }) + setCustomerToken(null) + return null + }, + useHook: ({ fetch }) => () => { + const { mutate } = useCustomer() + + return useCallback( + async function logout() { + const data = await fetch() + await mutate(null, false) + return data + }, + [fetch, mutate] + ) + }, } - -export default emptyHook diff --git a/framework/shopify/auth/use-signup.tsx b/framework/shopify/auth/use-signup.tsx index 75f067c3a..f072f1925 100644 --- a/framework/shopify/auth/use-signup.tsx +++ b/framework/shopify/auth/use-signup.tsx @@ -1,13 +1,74 @@ import { useCallback } from 'react' +import type { MutationHook } from '@commerce/utils/types' +import { CommerceError } from '@commerce/utils/errors' +import useSignup, { UseSignup } from '@commerce/auth/use-signup' +import useCustomer from '../customer/use-customer' +import { CustomerCreateInput } from '@framework/schema' -export function emptyHook() { - const useEmptyHook = async (options = {}) => { - return useCallback(async function () { - return Promise.resolve() - }, []) - } +import { + customerCreateMutation, + customerAccessTokenCreateMutation, +} from '@framework/utils/mutations' +import handleLogin from '@framework/utils/handle-login' - return useEmptyHook +export default useSignup as UseSignup<typeof handler> + +export const handler: MutationHook< + null, + {}, + CustomerCreateInput, + CustomerCreateInput +> = { + fetchOptions: { + query: customerCreateMutation, + }, + async fetcher({ + input: { firstName, lastName, email, password }, + options, + fetch, + }) { + if (!(firstName && lastName && email && password)) { + throw new CommerceError({ + message: + 'A first name, last name, email and password are required to signup', + }) + } + const data = await fetch({ + ...options, + variables: { + input: { + firstName, + lastName, + email, + password, + }, + }, + }) + + try { + const loginData = await fetch({ + query: customerAccessTokenCreateMutation, + variables: { + input: { + email, + password, + }, + }, + }) + handleLogin(loginData) + } catch (error) {} + return data + }, + useHook: ({ fetch }) => () => { + const { revalidate } = useCustomer() + + return useCallback( + async function signup(input) { + const data = await fetch({ input }) + await revalidate() + return data + }, + [fetch, revalidate] + ) + }, } - -export default emptyHook diff --git a/framework/shopify/cart/index.ts b/framework/shopify/cart/index.ts index e1c6ef823..3d288b1df 100644 --- a/framework/shopify/cart/index.ts +++ b/framework/shopify/cart/index.ts @@ -1,5 +1,3 @@ export { default as useCart } from './use-cart' export { default as useAddItem } from './use-add-item' export { default as useRemoveItem } from './use-remove-item' -// export { default as useWishlistActions } from './use-cart-actions' -// export { default as useUpdateItem } from './use-cart-actions' diff --git a/framework/shopify/cart/use-add-item.tsx b/framework/shopify/cart/use-add-item.tsx index 276d66e30..36f02847b 100644 --- a/framework/shopify/cart/use-add-item.tsx +++ b/framework/shopify/cart/use-add-item.tsx @@ -1,30 +1,57 @@ +import type { MutationHook } from '@commerce/utils/types' +import { CommerceError } from '@commerce/utils/errors' +import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item' +import useCart from './use-cart' +import { Cart, CartItemBody } from '../types' +import { checkoutLineItemAddMutation, getCheckoutId } from '../utils' +import { checkoutToCart } from './utils' +import { Mutation, MutationCheckoutLineItemsAddArgs } from '../schema' import { useCallback } from 'react' -import { LineItemToAdd } from 'shopify-buy' -import { useCommerce } from '../index' -type Options = { - productId: number - variantId: string | number +export default useAddItem as UseAddItem<typeof handler> + +export const handler: MutationHook<Cart, {}, CartItemBody> = { + fetchOptions: { + query: checkoutLineItemAddMutation, + }, + async fetcher({ input: item, options, fetch }) { + if ( + item.quantity && + (!Number.isInteger(item.quantity) || item.quantity! < 1) + ) { + throw new CommerceError({ + message: 'The item quantity has to be a valid integer greater than 0', + }) + } + + const { checkoutLineItemsAdd } = await fetch< + Mutation, + MutationCheckoutLineItemsAddArgs + >({ + ...options, + variables: { + checkoutId: getCheckoutId(), + lineItems: [ + { + variantId: item.variantId, + quantity: item.quantity ?? 1, + }, + ], + }, + }) + + return checkoutToCart(checkoutLineItemsAdd) + }, + useHook: ({ fetch }) => () => { + const { mutate } = useCart() + + return useCallback( + async function addItem(input) { + const data = await fetch({ input }) + await mutate(data, false) + return data + }, + [fetch, mutate] + ) + }, } - -const useAddItem = () => { - const { checkout, client, updateCheckout } = useCommerce() - - return useCallback( - async function addItem(options: Options) { - const lineItems: LineItemToAdd[] = [ - { - variantId: `${options.variantId}`, - quantity: 1, - }, - ] - - const cart = await client?.checkout.addLineItems(checkout.id, lineItems) - updateCheckout(cart) - return cart - }, - [checkout, client] - ) -} - -export default useAddItem diff --git a/framework/shopify/cart/use-cart.tsx b/framework/shopify/cart/use-cart.tsx index e69afda0c..2cf3a3e95 100644 --- a/framework/shopify/cart/use-cart.tsx +++ b/framework/shopify/cart/use-cart.tsx @@ -1,47 +1,60 @@ -import { useCommerce } from '../index' -import useCart, { UseCart, FetchCartInput } from '@commerce/cart/use-cart' -import type { Cart } from '../types' +import { useMemo } from 'react' +import type { ShopifyProvider } from '..' -// export default useCart as UseCart<typeof handler> -export default useCart as UseCart +import useCommerceCart, { + FetchCartInput, + UseCart, +} from '@commerce/cart/use-cart' -export const handler = () => { - const { checkout } = useCommerce() - const { lineItems, totalPriceV2 } = checkout || {} +import { Cart } from '@commerce/types' +import { SWRHook } from '@commerce/utils/types' +import { checkoutCreate, checkoutToCart } from './utils' +import getCheckoutQuery from '../utils/queries/get-checkout-query' - console.log(checkout) +export default useCommerceCart as UseCart<ShopifyProvider> - return { - data: { - subTotal: totalPriceV2?.amount || 0, - total: totalPriceV2?.amount || 0, - currency: { - code: '', - }, - line_items: - lineItems?.map((item) => { - return [ - { - id: item.id, - name: item.title, - quantity: item.quantity, +export const handler: SWRHook< + Cart | null, + {}, + FetchCartInput, + { isEmpty?: boolean } +> = { + fetchOptions: { + query: getCheckoutQuery, + }, + async fetcher({ input: { cartId: checkoutId }, options, fetch }) { + let checkout + if (checkoutId) { + const data = await fetch({ + ...options, + variables: { + checkoutId, + }, + }) + checkout = data.node + } + + if (checkout?.completedAt || !checkoutId) { + checkout = await checkoutCreate(fetch) + } + + return checkoutToCart({ checkout }) + }, + useHook: ({ useData }) => (input) => { + const response = useData({ + swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, + }) + return useMemo( + () => + Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 }, - ] - }) || [], - items: - lineItems?.map((item) => { - return { - id: item.id, - name: item.title, - images: [{ url: '/jacket.png' }], - url: '/', - quantity: item.quantity, - productId: item.id, - variantId: item.id, - } - }) || [], - }, - isEmpty: false, - isLoading: false, - } + enumerable: true, + }, + }), + [response] + ) + }, } diff --git a/framework/shopify/cart/use-remove-item.tsx b/framework/shopify/cart/use-remove-item.tsx index c0ce93bd5..1963176c8 100644 --- a/framework/shopify/cart/use-remove-item.tsx +++ b/framework/shopify/cart/use-remove-item.tsx @@ -1,17 +1,75 @@ import { useCallback } from 'react' -import { useCommerce } from '../index' -const useRemoveItem = () => { - const { checkout, client, updateCheckout } = useCommerce() +import type { + MutationHookContext, + HookFetcherContext, +} from '@commerce/utils/types' - return useCallback( - async function removeItem({ id }: { id: string }) { - const cart = await client?.checkout.removeLineItems(checkout.id, [id]) - updateCheckout(cart) - return cart - }, - [checkout, client] - ) +import { ValidationError } from '@commerce/utils/errors' + +import useRemoveItem, { + RemoveItemInput as RemoveItemInputBase, + UseRemoveItem, +} from '@commerce/cart/use-remove-item' + +import useCart from './use-cart' +import { checkoutLineItemRemoveMutation, getCheckoutId } from '@framework/utils' +import { checkoutToCart } from './utils' +import { Cart, LineItem } from '@framework/types' +import { + Mutation, + MutationCheckoutLineItemsRemoveArgs, +} from '@framework/schema' +import { RemoveCartItemBody } from '@commerce/types' + +export type RemoveItemFn<T = any> = T extends LineItem + ? (input?: RemoveItemInput<T>) => Promise<Cart | null> + : (input: RemoveItemInput<T>) => Promise<Cart | null> + +export type RemoveItemInput<T = any> = T extends LineItem + ? Partial<RemoveItemInputBase> + : RemoveItemInputBase + +export default useRemoveItem as UseRemoveItem<typeof handler> + +export const handler = { + fetchOptions: { + query: checkoutLineItemRemoveMutation, + }, + async fetcher({ + input: { itemId }, + options, + fetch, + }: HookFetcherContext<RemoveCartItemBody>) { + const data = await fetch<Mutation, MutationCheckoutLineItemsRemoveArgs>({ + ...options, + variables: { checkoutId: getCheckoutId(), lineItemIds: [itemId] }, + }) + return checkoutToCart(data.checkoutLineItemsRemove) + }, + useHook: ({ + fetch, + }: MutationHookContext<Cart | null, RemoveCartItemBody>) => < + T extends LineItem | undefined = undefined + >( + ctx: { item?: T } = {} + ) => { + const { item } = ctx + const { mutate } = useCart() + const removeItem: RemoveItemFn<LineItem> = async (input) => { + const itemId = input?.id ?? item?.id + + if (!itemId) { + throw new ValidationError({ + message: 'Invalid input used for this operation', + }) + } + + const data = await fetch({ input: { itemId } }) + await mutate(data, false) + return data + } + + return useCallback(removeItem as RemoveItemFn<T>, [fetch, mutate]) + }, } - -export default useRemoveItem diff --git a/framework/shopify/cart/use-update-item.tsx b/framework/shopify/cart/use-update-item.tsx index 05118a65b..9e89d0aca 100644 --- a/framework/shopify/cart/use-update-item.tsx +++ b/framework/shopify/cart/use-update-item.tsx @@ -1,24 +1,110 @@ import { useCallback } from 'react' -import { useCommerce } from '../index' +import debounce from 'lodash.debounce' +import type { + HookFetcherContext, + MutationHookContext, +} from '@commerce/utils/types' +import { ValidationError } from '@commerce/utils/errors' +import useUpdateItem, { + UpdateItemInput as UpdateItemInputBase, + UseUpdateItem, +} from '@commerce/cart/use-update-item' -const useUpdateItem = (item: CartItem) => { - const { checkout, client, updateCheckout } = useCommerce() +import useCart from './use-cart' +import { handler as removeItemHandler } from './use-remove-item' +import type { Cart, LineItem, UpdateCartItemBody } from '../types' +import { checkoutToCart } from './utils' +import { getCheckoutId, checkoutLineItemUpdateMutation } from '../utils' +import { + Mutation, + MutationCheckoutLineItemsUpdateArgs, +} from '@framework/schema' - return useCallback( - async function updateItem({ quantity }: { quantity: number }) { - const lineItemsToUpdate = [{ id: item.id, quantity }] +export type UpdateItemInput<T = any> = T extends LineItem + ? Partial<UpdateItemInputBase<LineItem>> + : UpdateItemInputBase<LineItem> - const cart = await client?.checkout.updateLineItems( - checkout.id, - lineItemsToUpdate - ) +export default useUpdateItem as UseUpdateItem<typeof handler> - updateCheckout(cart) +export const handler = { + fetchOptions: { + query: checkoutLineItemUpdateMutation, + }, + async fetcher({ + input: { itemId, item }, + options, + fetch, + }: HookFetcherContext<UpdateCartItemBody>) { + if (Number.isInteger(item.quantity)) { + // Also allow the update hook to remove an item if the quantity is lower than 1 + if (item.quantity! < 1) { + return removeItemHandler.fetcher({ + options: removeItemHandler.fetchOptions, + input: { itemId }, + fetch, + }) + } + } else if (item.quantity) { + throw new ValidationError({ + message: 'The item quantity has to be a valid integer', + }) + } + const { checkoutLineItemsUpdate } = await fetch< + Mutation, + MutationCheckoutLineItemsUpdateArgs + >({ + ...options, + variables: { + checkoutId: getCheckoutId(), + lineItems: [ + { + id: itemId, + quantity: item.quantity, + }, + ], + }, + }) - return cart - }, - [checkout, client] - ) + return checkoutToCart(checkoutLineItemsUpdate) + }, + useHook: ({ + fetch, + }: MutationHookContext<Cart | null, UpdateCartItemBody>) => < + T extends LineItem | undefined = undefined + >( + ctx: { + item?: T + wait?: number + } = {} + ) => { + const { item } = ctx + const { mutate } = useCart() as any + + return useCallback( + debounce(async (input: UpdateItemInput<T>) => { + const itemId = input.id ?? item?.id + const productId = input.productId ?? item?.productId + const variantId = input.productId ?? item?.variantId + if (!itemId || !productId || !variantId) { + throw new ValidationError({ + message: 'Invalid input used for this operation', + }) + } + + const data = await fetch({ + input: { + item: { + productId, + variantId, + quantity: input.quantity, + }, + itemId, + }, + }) + await mutate(data, false) + return data + }, ctx.wait ?? 500), + [fetch, mutate] + ) + }, } - -export default useUpdateItem diff --git a/framework/shopify/cart/utils/checkout-create.ts b/framework/shopify/cart/utils/checkout-create.ts new file mode 100644 index 000000000..0e71be62f --- /dev/null +++ b/framework/shopify/cart/utils/checkout-create.ts @@ -0,0 +1,25 @@ +import { + SHOPIFY_CHECKOUT_ID_COOKIE, + SHOPIFY_CHECKOUT_URL_COOKIE, +} from '@framework/const' + +import checkoutCreateMutation from '@framework/utils/mutations/checkout-create' +import Cookies from 'js-cookie' + +export const checkoutCreate = async (fetch: any) => { + const data = await fetch({ + query: checkoutCreateMutation, + }) + + const checkout = data.checkoutCreate?.checkout + const checkoutId = checkout?.id + + if (checkoutId) { + Cookies.set(SHOPIFY_CHECKOUT_ID_COOKIE, checkoutId) + Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout?.webUrl) + } + + return checkout +} + +export default checkoutCreate diff --git a/framework/shopify/cart/utils/checkout-to-cart.ts b/framework/shopify/cart/utils/checkout-to-cart.ts new file mode 100644 index 000000000..662db1c45 --- /dev/null +++ b/framework/shopify/cart/utils/checkout-to-cart.ts @@ -0,0 +1,42 @@ +import { Cart } from '@commerce/types' +import { CommerceError, ValidationError } from '@commerce/utils/errors' + +import { + CheckoutLineItemsAddPayload, + CheckoutLineItemsRemovePayload, + CheckoutLineItemsUpdatePayload, + Maybe, +} from '@framework/schema' +import { normalizeCart } from '@framework/utils' + +export type CheckoutPayload = + | CheckoutLineItemsAddPayload + | CheckoutLineItemsUpdatePayload + | CheckoutLineItemsRemovePayload + +const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => { + if (!checkoutPayload) { + throw new CommerceError({ + message: 'Invalid response from Shopify', + }) + } + + const checkout = checkoutPayload?.checkout + const userErrors = checkoutPayload?.userErrors + + if (userErrors && userErrors.length) { + throw new ValidationError({ + message: userErrors[0].message, + }) + } + + if (!checkout) { + throw new CommerceError({ + message: 'Invalid response from Shopify', + }) + } + + return normalizeCart(checkout) +} + +export default checkoutToCart diff --git a/framework/shopify/cart/utils/fetcher.ts b/framework/shopify/cart/utils/fetcher.ts new file mode 100644 index 000000000..a69492f0d --- /dev/null +++ b/framework/shopify/cart/utils/fetcher.ts @@ -0,0 +1,30 @@ +import { HookFetcherFn } from '@commerce/utils/types' +import { Cart } from '@commerce/types' +import { checkoutCreate, checkoutToCart } from '.' +import { FetchCartInput } from '@commerce/cart/use-cart' + +const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ + options, + input: { cartId: checkoutId }, + fetch, +}) => { + let checkout + + if (checkoutId) { + const data = await fetch({ + ...options, + variables: { + checkoutId, + }, + }) + checkout = data.node + } + + if (checkout?.completedAt || !checkoutId) { + checkout = await checkoutCreate(fetch) + } + + return checkoutToCart({ checkout }) +} + +export default fetcher diff --git a/framework/shopify/cart/utils/index.ts b/framework/shopify/cart/utils/index.ts new file mode 100644 index 000000000..20d04955d --- /dev/null +++ b/framework/shopify/cart/utils/index.ts @@ -0,0 +1,2 @@ +export { default as checkoutToCart } from './checkout-to-cart' +export { default as checkoutCreate } from './checkout-create' diff --git a/framework/shopify/common/get-all-pages.ts b/framework/shopify/common/get-all-pages.ts index 02db3fdc3..6f06185e2 100644 --- a/framework/shopify/common/get-all-pages.ts +++ b/framework/shopify/common/get-all-pages.ts @@ -1,53 +1,39 @@ import { getConfig, ShopifyConfig } from '../api' -import { Page as PageType, PageEdge } from '../types' - -export type Page = PageType - -export const getAllPagesQuery = /* GraphQL */ ` - query($first: Int!) { - pages(first: $first) { - edges { - node { - id - title - handle - body - bodySummary - url - } - } - } - } -` +import { PageEdge } from '../schema' +import { getAllPagesQuery } from '../utils/queries' type Variables = { first?: number } -type Options = { - variables?: Variables - config: ShopifyConfig - preview?: boolean -} - type ReturnType = { pages: Page[] } -const getAllPages = async (options?: Options): Promise<ReturnType> => { - let { config, variables = { first: 250 } } = options || {} +export type Page = { + id: string + name: string + url: string + sort_order?: number + body: string +} +const getAllPages = async (options?: { + variables?: Variables + config: ShopifyConfig + preview?: boolean +}): Promise<ReturnType> => { + let { config, variables = { first: 250 } } = options ?? {} config = getConfig(config) - const { data } = await config.fetch(getAllPagesQuery, { variables }) - const pages = data.pages.edges.map(({ node }: PageEdge) => { - return { + const pages = data.pages?.edges?.map( + ({ node: { title: name, handle, ...node } }: PageEdge) => ({ ...node, - name: node.handle, - url: `${config!.locale}/${node.handle}`, - } - }) + url: `/${handle}`, + name, + }) + ) return { pages } } diff --git a/framework/shopify/common/get-page.ts b/framework/shopify/common/get-page.ts new file mode 100644 index 000000000..6016c8c9a --- /dev/null +++ b/framework/shopify/common/get-page.ts @@ -0,0 +1,38 @@ +import { getConfig, ShopifyConfig } from '../api' +import getPageQuery from '../utils/queries/get-page-query' +import { Page } from './get-all-pages' + +type Variables = { + slug: string +} + +type ReturnType = { + page: Page +} + +const getPage = async (options: { + variables: Variables + config: ShopifyConfig + preview?: boolean +}): Promise<ReturnType> => { + let { config, variables } = options ?? {} + config = getConfig(config) + + const { data } = await config.fetch(getPageQuery, { + variables, + }) + + const { pageByHandle: page } = data + + return { + page: page + ? { + ...page, + name: page.title, + url: page?.handle, + } + : null, + } +} + +export default getPage diff --git a/framework/shopify/common/get-site-info.ts b/framework/shopify/common/get-site-info.ts index c08ae2b92..f6cdaad85 100644 --- a/framework/shopify/common/get-site-info.ts +++ b/framework/shopify/common/get-site-info.ts @@ -1,29 +1,30 @@ -import { ShopifyConfig } from '../index' +import getCategories, { Category } from '@framework/utils/get-categories' +import getVendors, { Brands } from '@framework/utils/get-vendors' -type Options = { +import { getConfig, ShopifyConfig } from '../api' + +export type GetSiteInfoResult< + T extends { categories: any[]; brands: any[] } = { + categories: Category[] + brands: Brands + } +> = T + +const getSiteInfo = async (options?: { + variables?: any config: ShopifyConfig preview?: boolean -} +}): Promise<GetSiteInfoResult> => { + let { config } = options ?? {} + + config = getConfig(config) + + const categories = await getCategories(config) + const brands = await getVendors(config) -const getSiteInfo = async (options: Options) => { - // TODO return { - categories: [ - { - path: '', - name: '', - entityId: 0, - }, - ], - brands: [ - { - node: { - path: '', - name: '', - entityId: 0, - }, - }, - ], + categories, + brands, } } diff --git a/framework/shopify/const.ts b/framework/shopify/const.ts new file mode 100644 index 000000000..a6e9e8d90 --- /dev/null +++ b/framework/shopify/const.ts @@ -0,0 +1,11 @@ +export const SHOPIFY_CHECKOUT_ID_COOKIE = 'shopify_checkoutId' + +export const SHOPIFY_CHECKOUT_URL_COOKIE = 'shopify_checkoutUrl' + +export const SHOPIFY_CUSTOMER_TOKEN_COOKIE = 'shopify_customerToken' + +export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN + +export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json` + +export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN diff --git a/framework/shopify/customer/get-customer-id.ts b/framework/shopify/customer/get-customer-id.ts new file mode 100644 index 000000000..78309a8ec --- /dev/null +++ b/framework/shopify/customer/get-customer-id.ts @@ -0,0 +1,24 @@ +import { getConfig, ShopifyConfig } from '@framework/api' +import getCustomerIdQuery from '@framework/utils/queries/get-customer-id-query' +import Cookies from 'js-cookie' + +async function getCustomerId({ + customerToken: customerAccesToken, + config, +}: { + customerToken: string + config?: ShopifyConfig +}): Promise<number | undefined> { + config = getConfig(config) + + const { data } = await config.fetch(getCustomerIdQuery, { + variables: { + customerAccesToken: + customerAccesToken || Cookies.get(config.customerCookie), + }, + }) + + return data.customer?.id +} + +export default getCustomerId diff --git a/framework/shopify/customer/index.ts b/framework/shopify/customer/index.ts new file mode 100644 index 000000000..6c903ecc5 --- /dev/null +++ b/framework/shopify/customer/index.ts @@ -0,0 +1 @@ +export { default as useCustomer } from './use-customer' diff --git a/framework/shopify/customer/use-customer.tsx b/framework/shopify/customer/use-customer.tsx index 7ea2ae679..91b7281af 100644 --- a/framework/shopify/customer/use-customer.tsx +++ b/framework/shopify/customer/use-customer.tsx @@ -1,32 +1,27 @@ -import type { HookFetcher } from '@commerce/utils/types' -import type { SwrOptions } from '@commerce/utils/use-data' -import useCommerceCustomer from '@commerce/customer/use-customer' +import useCustomer, { UseCustomer } from '@commerce/customer/use-customer' +import { Customer } from '@commerce/types' +import { SWRHook } from '@commerce/utils/types' +import { getCustomerQuery, getCustomerToken } from '../utils' +import type { ShopifyProvider } from '..' -const defaultOpts = {} - -export type Customer = { - entityId: number - firstName: string - lastName: string - email: string +export default useCustomer as UseCustomer<ShopifyProvider> +export const handler: SWRHook<Customer | null> = { + fetchOptions: { + query: getCustomerQuery, + }, + async fetcher({ options, fetch }) { + const data = await fetch<any | null>({ + ...options, + variables: { customerAccessToken: getCustomerToken() }, + }) + return data.customer ?? null + }, + useHook: ({ useData }) => (input) => { + return useData({ + swrOptions: { + revalidateOnFocus: false, + ...input?.swrOptions, + }, + }) + }, } -export type CustomerData = {} - -export const fetcher: HookFetcher<Customer | null> = async () => { - return null -} - -export function extendHook( - customFetcher: typeof fetcher, - swrOptions?: SwrOptions<Customer | null> -) { - const useCustomer = () => { - return { data: { firstName: null, lastName: null, email: null } } - } - - useCustomer.extend = extendHook - - return useCustomer -} - -export default extendHook(fetcher) diff --git a/framework/shopify/fetcher.ts b/framework/shopify/fetcher.ts index 73500ee93..9c4fe9a9e 100644 --- a/framework/shopify/fetcher.ts +++ b/framework/shopify/fetcher.ts @@ -1,51 +1,18 @@ -import { FetcherError } from '@commerce/utils/errors' -import type { Fetcher } from '@commerce/utils/types' +import { Fetcher } from '@commerce/utils/types' +import { API_TOKEN, API_URL } from './const' +import { handleFetchResponse } from './utils' -async function getText(res: Response) { - try { - return (await res.text()) || res.statusText - } catch (error) { - return res.statusText - } -} - -async function getError(res: Response) { - if (res.headers.get('Content-Type')?.includes('application/json')) { - const data = await res.json() - return new FetcherError({ errors: data.errors, status: res.status }) - } - return new FetcherError({ message: await getText(res), status: res.status }) -} - -const fetcher: Fetcher = async ({ - url, - query, - method = 'POST', - variables, - body: bodyObj, -}) => { - // const config = getConfig() - // url = `https://${process.env.SHOPIFY_STORE_DOMAIN}/api/2021-01/graphql.json` - - const hasBody = Boolean(variables || bodyObj) - const body = hasBody - ? JSON.stringify(variables ? { query, variables } : bodyObj) - : undefined - const headers = hasBody - ? { - 'X-Shopify-Storefront-Access-Token': config.apiToken, +const fetcher: Fetcher = async ({ method = 'POST', variables, query }) => { + return handleFetchResponse( + await fetch(API_URL, { + method, + body: JSON.stringify({ query, variables }), + headers: { + 'X-Shopify-Storefront-Access-Token': API_TOKEN!, 'Content-Type': 'application/json', - } - : undefined - - const res = await fetch(url!, { method, body, headers }) - - if (res.ok) { - const { data } = await res.json() - return data - } - - throw await getError(res) + }, + }) + ) } export default fetcher diff --git a/framework/shopify/index.tsx b/framework/shopify/index.tsx index 5fd08e0d9..5b25d6b21 100644 --- a/framework/shopify/index.tsx +++ b/framework/shopify/index.tsx @@ -1,109 +1,39 @@ -import React, { - ReactNode, - createContext, - useContext, - useMemo, - useState, - useEffect, -} from 'react' -import Client from 'shopify-buy' -import { Shop, Cart, Client as ClientType } from './types' +import * as React from 'react' +import { ReactNode } from 'react' + import { - getCheckoutIdFromStorage, - setCheckoutIdInStorage, -} from './utils/storage' -import { getConfig } from '@framework/api' + CommerceConfig, + CommerceProvider as CoreCommerceProvider, + useCommerce as useCoreCommerce, +} from '@commerce' -const Commerce = createContext<CommerceContextValue | {}>({}) +import { shopifyProvider, ShopifyProvider } from './provider' +import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const' -type CommerceProps = { +export { shopifyProvider } +export type { ShopifyProvider } + +export const shopifyConfig: CommerceConfig = { + locale: 'en-us', + cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, +} + +export type ShopifyConfig = Partial<CommerceConfig> + +export type ShopifyProps = { children?: ReactNode locale: string -} +} & ShopifyConfig -type CommerceContextValue = { - client: ClientType - shop: Shop - checkout: Cart - updateCheckout: (cart: Cart | undefined) => void - currencyCode: string - locale: string - sessionToken: string -} - -export function CommerceProvider({ - children, - locale = 'en-US', -}: CommerceProps) { - const sessionToken = 'nextjs-commerce-shopify-token' - - const config = getConfig() - - const client = Client.buildClient({ - storefrontAccessToken: config.apiToken, - domain: config.commerceUrl, - language: locale, - }) as ClientType - - const [shop, setShop] = useState<Shop>() - const [checkout, setCheckout] = useState<Cart>() - - const fetchShopify = async () => { - const shopInfo: Shop = await client.shop.fetchInfo() - let checkoutResource: Cart - - const checkoutOptions = { - presentmentCurrencyCode: - /*config.currencyCode ||*/ shopInfo?.currencyCode, - } - - let checkoutId = getCheckoutIdFromStorage(sessionToken) - - // we could have a cart id stored in session storage - // user could be refreshing or navigating back and forth - if (checkoutId) { - checkoutResource = await client.checkout.fetch(checkoutId) - - // could be expired order - we will create a new order - if (checkoutResource.completedAt) { - checkoutResource = await client.checkout.create(checkoutOptions) - } - } else { - checkoutResource = await client.checkout.create(checkoutOptions) - } - - setCheckoutIdInStorage(sessionToken, checkoutResource.id) - - setShop(shopInfo) - setCheckout(checkoutResource) - } - - useEffect(() => { - fetchShopify() - }, []) - - const updateCheckout = (newCheckout: Cart) => { - setCheckout(newCheckout) - } - - // Because the config is an object, if the parent re-renders this provider - // will re-render every consumer unless we memoize the config - const cfg = useMemo( - () => ({ - client, - checkout, - shop, - updateCheckout: updateCheckout, - currencyCode: /*config.currencyCode ||*/ checkout?.currencyCode, - locale, - sessionToken, - }), - [client] +export function CommerceProvider({ children, ...config }: ShopifyProps) { + return ( + <CoreCommerceProvider + provider={shopifyProvider} + config={{ ...shopifyConfig, ...config }} + > + {children} + </CoreCommerceProvider> ) - - return <Commerce.Provider value={cfg}>{children}</Commerce.Provider> } -export function useCommerce<T extends CommerceContextValue>() { - return useContext(Commerce) as T -} +export const useCommerce = () => useCoreCommerce() diff --git a/framework/shopify/product/get-all-collections.ts b/framework/shopify/product/get-all-collections.ts new file mode 100644 index 000000000..bf3fee392 --- /dev/null +++ b/framework/shopify/product/get-all-collections.ts @@ -0,0 +1,29 @@ +import { CollectionEdge } from '@framework/schema' +import { getConfig, ShopifyConfig } from '../api' +import getAllCollectionsQuery from '../utils/queries/get-all-collections-query' + +const getAllCollections = async (options?: { + variables?: any + config: ShopifyConfig + preview?: boolean +}) => { + let { config, variables = { first: 250 } } = options ?? {} + config = getConfig(config) + + const { data } = await config.fetch(getAllCollectionsQuery, { variables }) + const edges = data.collections?.edges ?? [] + + const categories = edges.map( + ({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({ + entityId, + name, + path: `/${handle}`, + }) + ) + + return { + categories, + } +} + +export default getAllCollections diff --git a/framework/shopify/product/get-all-product-paths.ts b/framework/shopify/product/get-all-product-paths.ts index 3d4f0ef7a..4431d1e53 100644 --- a/framework/shopify/product/get-all-product-paths.ts +++ b/framework/shopify/product/get-all-product-paths.ts @@ -1,30 +1,41 @@ -import Client from 'shopify-buy' -import { getConfig } from '../api' -import { Product } from '../types' -import toCommerceProducts from '../utils/to-commerce-products' +import { Product } from '@commerce/types' +import { getConfig, ShopifyConfig } from '../api' +import fetchAllProducts from '../api/utils/fetch-all-products' +import { ProductEdge } from '../schema' +import getAllProductsPathsQuery from '../utils/queries/get-all-products-paths-query' -type ReturnType = { - products: any[] +type ProductPath = { + path: string } -const getAllProductPaths = async (): Promise<ReturnType> => { - const config = getConfig() +export type ProductPathNode = { + node: ProductPath +} - const client = Client.buildClient({ - storefrontAccessToken: config.apiToken, - domain: config.commerceUrl, +type ReturnType = { + products: ProductPathNode[] +} + +const getAllProductPaths = async (options?: { + variables?: any + config?: ShopifyConfig + preview?: boolean +}): Promise<ReturnType> => { + let { config, variables = { first: 250 } } = options ?? {} + config = getConfig(config) + + const products = await fetchAllProducts({ + config, + query: getAllProductsPathsQuery, + variables, }) - const res = (await client.product.fetchAll()) as Product[] - - const products = toCommerceProducts(res) - return { - products: products.map((product) => { - return { - node: { ...product }, - } - }), + products: products?.map(({ node: { handle } }: ProductEdge) => ({ + node: { + path: `/${handle}`, + }, + })), } } diff --git a/framework/shopify/product/get-all-products.ts b/framework/shopify/product/get-all-products.ts index 6e4881e99..e1eb96aac 100644 --- a/framework/shopify/product/get-all-products.ts +++ b/framework/shopify/product/get-all-products.ts @@ -1,36 +1,36 @@ -import Client from 'shopify-buy' -import { ShopifyConfig } from '../api' -import { Product } from '../types' -import toCommerceProducts from '../utils/to-commerce-products' - -export type ProductNode = Product +import { GraphQLFetcherResult } from '@commerce/api' +import { getConfig, ShopifyConfig } from '../api' +import { ProductEdge } from '../schema' +import { getAllProductsQuery } from '../utils/queries' +import { normalizeProduct } from '@framework/utils/normalize' +import { Product } from '@commerce/types' type Variables = { first?: number field?: string } -type Options = { - variables: Variables - config: ShopifyConfig - preview?: boolean -} - type ReturnType = { - products: any[] + products: Product[] } -const getAllProducts = async (options: Options): Promise<ReturnType> => { - const { config } = options +const getAllProducts = async (options: { + variables?: Variables + config?: ShopifyConfig + preview?: boolean +}): Promise<ReturnType> => { + let { config, variables = { first: 250 } } = options ?? {} + config = getConfig(config) - const client = Client.buildClient({ - storefrontAccessToken: config.apiToken, - domain: config.commerceUrl, - }) + const { data }: GraphQLFetcherResult = await config.fetch( + getAllProductsQuery, + { variables } + ) - const res = (await client.product.fetchAll()) as Product[] - - const products = toCommerceProducts(res) + const products = + data.products?.edges?.map(({ node: p }: ProductEdge) => + normalizeProduct(p) + ) ?? [] return { products, diff --git a/framework/shopify/product/get-product.ts b/framework/shopify/product/get-product.ts index f71aa0213..1f00288c7 100644 --- a/framework/shopify/product/get-product.ts +++ b/framework/shopify/product/get-product.ts @@ -1,36 +1,31 @@ -import Client from 'shopify-buy' -import { ShopifyConfig } from '../api' -import { Product } from '../types' -import toCommerceProducts from '../utils/to-commerce-products' - -export type ProductNode = Product +import { GraphQLFetcherResult } from '@commerce/api' +import { getConfig, ShopifyConfig } from '../api' +import { normalizeProduct, getProductQuery } from '../utils' type Variables = { slug: string } -type Options = { - variables: Variables - config: ShopifyConfig - preview?: boolean -} - type ReturnType = { product: any } -const getProduct = async (options: Options): Promise<ReturnType> => { - const { variables, config } = options +const getProduct = async (options: { + variables: Variables + config: ShopifyConfig + preview?: boolean +}): Promise<ReturnType> => { + let { config, variables } = options ?? {} + config = getConfig(config) - const client = Client.buildClient({ - storefrontAccessToken: config.apiToken, - domain: config.commerceUrl, + const { data }: GraphQLFetcherResult = await config.fetch(getProductQuery, { + variables, }) - const res = (await client.product.fetchByHandle(variables.slug)) as Product + const { productByHandle: product } = data return { - product: toCommerceProducts([res])[0], + product: product ? normalizeProduct(product) : null, } } diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx index 04f6a3536..7ca37916b 100644 --- a/framework/shopify/product/use-search.tsx +++ b/framework/shopify/product/use-search.tsx @@ -1,9 +1,17 @@ -import type { HookFetcher } from '@commerce/utils/types' -import type { SwrOptions } from '@commerce/utils/use-data' -import useCommerceSearch from '@commerce/product/use-search' -import { ProductEdge } from '../types' +import { SWRHook } from '@commerce/utils/types' +import useSearch, { UseSearch } from '@commerce/product/use-search' -const defaultOpts = {} +import { ProductEdge } from '@framework/schema' +import { + getAllProductsQuery, + getSearchVariables, + normalizeProduct, +} from '@framework/utils' +import type { ShopifyProvider } from '..' + +import { Product } from '@commerce/types' + +export default useSearch as UseSearch<ShopifyProvider> export type SearchProductsInput = { search?: string @@ -13,29 +21,41 @@ export type SearchProductsInput = { } export type SearchProductsData = { - products: ProductEdge[] + products: Product[] found: boolean } - -export const fetcher: HookFetcher<SearchProductsData, SearchProductsInput> = ( - options, - { search, categoryId, brandId, sort }, - fetch -) => { - return { found: false, products: [] } +export const handler: SWRHook< + SearchProductsData, + SearchProductsInput, + SearchProductsInput +> = { + fetchOptions: { + query: getAllProductsQuery, + }, + async fetcher({ input, options, fetch }) { + const resp = await fetch({ + query: options?.query, + method: options?.method, + variables: getSearchVariables(input), + }) + const edges = resp.products?.edges + return { + products: edges?.map(({ node: p }: ProductEdge) => normalizeProduct(p)), + found: !!edges?.length, + } + }, + useHook: ({ useData }) => (input = {}) => { + return useData({ + input: [ + ['search', input.search], + ['categoryId', input.categoryId], + ['brandId', input.brandId], + ['sort', input.sort], + ], + swrOptions: { + revalidateOnFocus: false, + ...input.swrOptions, + }, + }) + }, } - -export function extendHook( - customFetcher: typeof fetcher, - swrOptions?: SwrOptions<SearchProductsData, SearchProductsInput> -) { - const useSearch = (input: SearchProductsInput = {}) => { - return {} - } - - useSearch.extend = extendHook - - return useSearch -} - -export default extendHook(fetcher) diff --git a/framework/shopify/provider.ts b/framework/shopify/provider.ts index 31b958a21..383822baa 100644 --- a/framework/shopify/provider.ts +++ b/framework/shopify/provider.ts @@ -1,12 +1,10 @@ +import { SHOPIFY_CHECKOUT_ID_COOKIE, STORE_DOMAIN } from './const' + import { handler as useCart } from './cart/use-cart' import { handler as useAddItem } from './cart/use-add-item' import { handler as useUpdateItem } from './cart/use-update-item' import { handler as useRemoveItem } from './cart/use-remove-item' -import { handler as useWishlist } from './wishlist/use-wishlist' -import { handler as useWishlistAddItem } from './wishlist/use-add-item' -import { handler as useWishlistRemoveItem } from './wishlist/use-remove-item' - import { handler as useCustomer } from './customer/use-customer' import { handler as useSearch } from './product/use-search' @@ -18,17 +16,16 @@ import fetcher from './fetcher' export const shopifyProvider = { locale: 'en-us', - cartCookie: 'sp_cartId', + cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, + storeDomain: STORE_DOMAIN, fetcher, cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, - wishlist: { - useWishlist, - useAddItem: useWishlistAddItem, - useRemoveItem: useWishlistRemoveItem, - }, customer: { useCustomer }, products: { useSearch }, auth: { useLogin, useLogout, useSignup }, + features: { + wishlist: false, + }, } export type ShopifyProvider = typeof shopifyProvider diff --git a/framework/shopify/schema.d.ts b/framework/shopify/schema.d.ts new file mode 100644 index 000000000..b1b23a3e5 --- /dev/null +++ b/framework/shopify/schema.d.ts @@ -0,0 +1,4985 @@ +export type Maybe<T> = T | null +export type Exact<T extends { [key: string]: unknown }> = { + [K in keyof T]: T[K] +} +export type MakeOptional<T, K extends keyof T> = Omit<T, K> & + { [SubKey in K]?: Maybe<T[SubKey]> } +export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & + { [SubKey in K]: Maybe<T[SubKey]> } +/** All built-in and custom scalars, mapped to their actual values */ +export type Scalars = { + ID: string + String: string + Boolean: boolean + Int: number + Float: number + /** An ISO-8601 encoded UTC date time string. Example value: `"2019-07-03T20:47:55Z"`. */ + DateTime: any + /** A signed decimal number, which supports arbitrary precision and is serialized as a string. Example value: `"29.99"`. */ + Decimal: any + /** A string containing HTML code. Example value: `"<p>Grey cotton knit sweater.</p>"`. */ + HTML: any + /** A monetary value string. Example value: `"100.57"`. */ + Money: any + /** + * An RFC 3986 and RFC 3987 compliant URI string. + * + * Example value: `"https://johns-apparel.myshopify.com"`. + * + */ + URL: any +} + +/** A version of the API. */ +export type ApiVersion = { + __typename?: 'ApiVersion' + /** The human-readable name of the version. */ + displayName: Scalars['String'] + /** The unique identifier of an ApiVersion. All supported API versions have a date-based (YYYY-MM) or `unstable` handle. */ + handle: Scalars['String'] + /** Whether the version is supported by Shopify. */ + supported: Scalars['Boolean'] +} + +/** Details about the gift card used on the checkout. */ +export type AppliedGiftCard = Node & { + __typename?: 'AppliedGiftCard' + /** + * The amount that was taken from the gift card by applying it. + * @deprecated Use `amountUsedV2` instead + */ + amountUsed: Scalars['Money'] + /** The amount that was taken from the gift card by applying it. */ + amountUsedV2: MoneyV2 + /** + * The amount left on the gift card. + * @deprecated Use `balanceV2` instead + */ + balance: Scalars['Money'] + /** The amount left on the gift card. */ + balanceV2: MoneyV2 + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The last characters of the gift card. */ + lastCharacters: Scalars['String'] + /** The amount that was applied to the checkout in its currency. */ + presentmentAmountUsed: MoneyV2 +} + +/** An article in an online store blog. */ +export type Article = Node & { + __typename?: 'Article' + /** + * The article's author. + * @deprecated Use `authorV2` instead + */ + author: ArticleAuthor + /** The article's author. */ + authorV2?: Maybe<ArticleAuthor> + /** The blog that the article belongs to. */ + blog: Blog + /** List of comments posted on the article. */ + comments: CommentConnection + /** Stripped content of the article, single line with HTML tags removed. */ + content: Scalars['String'] + /** The content of the article, complete with HTML formatting. */ + contentHtml: Scalars['HTML'] + /** Stripped excerpt of the article, single line with HTML tags removed. */ + excerpt?: Maybe<Scalars['String']> + /** The excerpt of the article, complete with HTML formatting. */ + excerptHtml?: Maybe<Scalars['HTML']> + /** A human-friendly unique string for the Article automatically generated from its title. */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The image associated with the article. */ + image?: Maybe<Image> + /** The date and time when the article was published. */ + publishedAt: Scalars['DateTime'] + /** The article’s SEO information. */ + seo?: Maybe<Seo> + /** A categorization that a article can be tagged with. */ + tags: Array<Scalars['String']> + /** The article’s name. */ + title: Scalars['String'] + /** The url pointing to the article accessible from the web. */ + url: Scalars['URL'] +} + +/** An article in an online store blog. */ +export type ArticleCommentsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** An article in an online store blog. */ +export type ArticleContentArgs = { + truncateAt?: Maybe<Scalars['Int']> +} + +/** An article in an online store blog. */ +export type ArticleExcerptArgs = { + truncateAt?: Maybe<Scalars['Int']> +} + +/** An article in an online store blog. */ +export type ArticleImageArgs = { + maxWidth?: Maybe<Scalars['Int']> + maxHeight?: Maybe<Scalars['Int']> + crop?: Maybe<CropRegion> + scale?: Maybe<Scalars['Int']> +} + +/** The author of an article. */ +export type ArticleAuthor = { + __typename?: 'ArticleAuthor' + /** The author's bio. */ + bio?: Maybe<Scalars['String']> + /** The author’s email. */ + email: Scalars['String'] + /** The author's first name. */ + firstName: Scalars['String'] + /** The author's last name. */ + lastName: Scalars['String'] + /** The author's full name. */ + name: Scalars['String'] +} + +/** An auto-generated type for paginating through multiple Articles. */ +export type ArticleConnection = { + __typename?: 'ArticleConnection' + /** A list of edges. */ + edges: Array<ArticleEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Article and a cursor during pagination. */ +export type ArticleEdge = { + __typename?: 'ArticleEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ArticleEdge. */ + node: Article +} + +/** The set of valid sort keys for the Article query. */ +export enum ArticleSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `blog_title` value. */ + BlogTitle = 'BLOG_TITLE', + /** Sort by the `author` value. */ + Author = 'AUTHOR', + /** Sort by the `updated_at` value. */ + UpdatedAt = 'UPDATED_AT', + /** Sort by the `published_at` value. */ + PublishedAt = 'PUBLISHED_AT', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** Represents a generic custom attribute. */ +export type Attribute = { + __typename?: 'Attribute' + /** Key or name of the attribute. */ + key: Scalars['String'] + /** Value of the attribute. */ + value?: Maybe<Scalars['String']> +} + +/** Specifies the input fields required for an attribute. */ +export type AttributeInput = { + /** Key or name of the attribute. */ + key: Scalars['String'] + /** Value of the attribute. */ + value: Scalars['String'] +} + +/** Automatic discount applications capture the intentions of a discount that was automatically applied. */ +export type AutomaticDiscountApplication = DiscountApplication & { + __typename?: 'AutomaticDiscountApplication' + /** The method by which the discount's value is allocated to its entitled items. */ + allocationMethod: DiscountApplicationAllocationMethod + /** Which lines of targetType that the discount is allocated over. */ + targetSelection: DiscountApplicationTargetSelection + /** The type of line that the discount is applicable towards. */ + targetType: DiscountApplicationTargetType + /** The title of the application. */ + title: Scalars['String'] + /** The value of the discount application. */ + value: PricingValue +} + +/** A collection of available shipping rates for a checkout. */ +export type AvailableShippingRates = { + __typename?: 'AvailableShippingRates' + /** + * Whether or not the shipping rates are ready. + * The `shippingRates` field is `null` when this value is `false`. + * This field should be polled until its value becomes `true`. + */ + ready: Scalars['Boolean'] + /** The fetched shipping rates. `null` until the `ready` field is `true`. */ + shippingRates?: Maybe<Array<ShippingRate>> +} + +/** An online store blog. */ +export type Blog = Node & { + __typename?: 'Blog' + /** Find an article by its handle. */ + articleByHandle?: Maybe<Article> + /** List of the blog's articles. */ + articles: ArticleConnection + /** The authors who have contributed to the blog. */ + authors: Array<ArticleAuthor> + /** A human-friendly unique string for the Blog automatically generated from its title. */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The blog's SEO information. */ + seo?: Maybe<Seo> + /** The blogs’s title. */ + title: Scalars['String'] + /** The url pointing to the blog accessible from the web. */ + url: Scalars['URL'] +} + +/** An online store blog. */ +export type BlogArticleByHandleArgs = { + handle: Scalars['String'] +} + +/** An online store blog. */ +export type BlogArticlesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ArticleSortKeys> + query?: Maybe<Scalars['String']> +} + +/** An auto-generated type for paginating through multiple Blogs. */ +export type BlogConnection = { + __typename?: 'BlogConnection' + /** A list of edges. */ + edges: Array<BlogEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Blog and a cursor during pagination. */ +export type BlogEdge = { + __typename?: 'BlogEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of BlogEdge. */ + node: Blog +} + +/** The set of valid sort keys for the Blog query. */ +export enum BlogSortKeys { + /** Sort by the `handle` value. */ + Handle = 'HANDLE', + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** Card brand, such as Visa or Mastercard, which can be used for payments. */ +export enum CardBrand { + /** Visa */ + Visa = 'VISA', + /** Mastercard */ + Mastercard = 'MASTERCARD', + /** Discover */ + Discover = 'DISCOVER', + /** American Express */ + AmericanExpress = 'AMERICAN_EXPRESS', + /** Diners Club */ + DinersClub = 'DINERS_CLUB', + /** JCB */ + Jcb = 'JCB', +} + +/** A container for all the information required to checkout items and pay. */ +export type Checkout = Node & { + __typename?: 'Checkout' + /** The gift cards used on the checkout. */ + appliedGiftCards: Array<AppliedGiftCard> + /** + * The available shipping rates for this Checkout. + * Should only be used when checkout `requiresShipping` is `true` and + * the shipping address is valid. + */ + availableShippingRates?: Maybe<AvailableShippingRates> + /** The date and time when the checkout was completed. */ + completedAt?: Maybe<Scalars['DateTime']> + /** The date and time when the checkout was created. */ + createdAt: Scalars['DateTime'] + /** The currency code for the Checkout. */ + currencyCode: CurrencyCode + /** A list of extra information that is added to the checkout. */ + customAttributes: Array<Attribute> + /** + * The customer associated with the checkout. + * @deprecated This field will always return null. If you have an authentication token for the customer, you can use the `customer` field on the query root to retrieve it. + */ + customer?: Maybe<Customer> + /** Discounts that have been applied on the checkout. */ + discountApplications: DiscountApplicationConnection + /** The email attached to this checkout. */ + email?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** A list of line item objects, each one containing information about an item in the checkout. */ + lineItems: CheckoutLineItemConnection + /** The sum of all the prices of all the items in the checkout. Duties, taxes, shipping and discounts excluded. */ + lineItemsSubtotalPrice: MoneyV2 + /** The note associated with the checkout. */ + note?: Maybe<Scalars['String']> + /** The resulting order from a paid checkout. */ + order?: Maybe<Order> + /** The Order Status Page for this Checkout, null when checkout is not completed. */ + orderStatusUrl?: Maybe<Scalars['URL']> + /** + * The amount left to be paid. This is equal to the cost of the line items, taxes and shipping minus discounts and gift cards. + * @deprecated Use `paymentDueV2` instead + */ + paymentDue: Scalars['Money'] + /** The amount left to be paid. This is equal to the cost of the line items, duties, taxes and shipping minus discounts and gift cards. */ + paymentDueV2: MoneyV2 + /** + * Whether or not the Checkout is ready and can be completed. Checkouts may + * have asynchronous operations that can take time to finish. If you want + * to complete a checkout or ensure all the fields are populated and up to + * date, polling is required until the value is true. + */ + ready: Scalars['Boolean'] + /** States whether or not the fulfillment requires shipping. */ + requiresShipping: Scalars['Boolean'] + /** The shipping address to where the line items will be shipped. */ + shippingAddress?: Maybe<MailingAddress> + /** The discounts that have been allocated onto the shipping line by discount applications. */ + shippingDiscountAllocations: Array<DiscountAllocation> + /** Once a shipping rate is selected by the customer it is transitioned to a `shipping_line` object. */ + shippingLine?: Maybe<ShippingRate> + /** + * Price of the checkout before shipping and taxes. + * @deprecated Use `subtotalPriceV2` instead + */ + subtotalPrice: Scalars['Money'] + /** Price of the checkout before duties, shipping and taxes. */ + subtotalPriceV2: MoneyV2 + /** Specifies if the Checkout is tax exempt. */ + taxExempt: Scalars['Boolean'] + /** Specifies if taxes are included in the line item and shipping line prices. */ + taxesIncluded: Scalars['Boolean'] + /** + * The sum of all the prices of all the items in the checkout, taxes and discounts included. + * @deprecated Use `totalPriceV2` instead + */ + totalPrice: Scalars['Money'] + /** The sum of all the prices of all the items in the checkout, duties, taxes and discounts included. */ + totalPriceV2: MoneyV2 + /** + * The sum of all the taxes applied to the line items and shipping lines in the checkout. + * @deprecated Use `totalTaxV2` instead + */ + totalTax: Scalars['Money'] + /** The sum of all the taxes applied to the line items and shipping lines in the checkout. */ + totalTaxV2: MoneyV2 + /** The date and time when the checkout was last updated. */ + updatedAt: Scalars['DateTime'] + /** The url pointing to the checkout accessible from the web. */ + webUrl: Scalars['URL'] +} + +/** A container for all the information required to checkout items and pay. */ +export type CheckoutDiscountApplicationsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** A container for all the information required to checkout items and pay. */ +export type CheckoutLineItemsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** Specifies the fields required to update a checkout's attributes. */ +export type CheckoutAttributesUpdateInput = { + /** The text of an optional note that a shop owner can attach to the checkout. */ + note?: Maybe<Scalars['String']> + /** A list of extra information that is added to the checkout. */ + customAttributes?: Maybe<Array<AttributeInput>> + /** + * Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + * The required attributes are city, province, and country. + * Full validation of the addresses is still done at complete time. + */ + allowPartialAddresses?: Maybe<Scalars['Boolean']> +} + +/** Return type for `checkoutAttributesUpdate` mutation. */ +export type CheckoutAttributesUpdatePayload = { + __typename?: 'CheckoutAttributesUpdatePayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Specifies the fields required to update a checkout's attributes. */ +export type CheckoutAttributesUpdateV2Input = { + /** The text of an optional note that a shop owner can attach to the checkout. */ + note?: Maybe<Scalars['String']> + /** A list of extra information that is added to the checkout. */ + customAttributes?: Maybe<Array<AttributeInput>> + /** + * Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + * The required attributes are city, province, and country. + * Full validation of the addresses is still done at complete time. + */ + allowPartialAddresses?: Maybe<Scalars['Boolean']> +} + +/** Return type for `checkoutAttributesUpdateV2` mutation. */ +export type CheckoutAttributesUpdateV2Payload = { + __typename?: 'CheckoutAttributesUpdateV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteFree` mutation. */ +export type CheckoutCompleteFreePayload = { + __typename?: 'CheckoutCompleteFreePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteWithCreditCard` mutation. */ +export type CheckoutCompleteWithCreditCardPayload = { + __typename?: 'CheckoutCompleteWithCreditCardPayload' + /** The checkout on which the payment was applied. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** A representation of the attempted payment. */ + payment?: Maybe<Payment> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteWithCreditCardV2` mutation. */ +export type CheckoutCompleteWithCreditCardV2Payload = { + __typename?: 'CheckoutCompleteWithCreditCardV2Payload' + /** The checkout on which the payment was applied. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** A representation of the attempted payment. */ + payment?: Maybe<Payment> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteWithTokenizedPayment` mutation. */ +export type CheckoutCompleteWithTokenizedPaymentPayload = { + __typename?: 'CheckoutCompleteWithTokenizedPaymentPayload' + /** The checkout on which the payment was applied. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** A representation of the attempted payment. */ + payment?: Maybe<Payment> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteWithTokenizedPaymentV2` mutation. */ +export type CheckoutCompleteWithTokenizedPaymentV2Payload = { + __typename?: 'CheckoutCompleteWithTokenizedPaymentV2Payload' + /** The checkout on which the payment was applied. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** A representation of the attempted payment. */ + payment?: Maybe<Payment> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteWithTokenizedPaymentV3` mutation. */ +export type CheckoutCompleteWithTokenizedPaymentV3Payload = { + __typename?: 'CheckoutCompleteWithTokenizedPaymentV3Payload' + /** The checkout on which the payment was applied. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** A representation of the attempted payment. */ + payment?: Maybe<Payment> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Specifies the fields required to create a checkout. */ +export type CheckoutCreateInput = { + /** The email with which the customer wants to checkout. */ + email?: Maybe<Scalars['String']> + /** A list of line item objects, each one containing information about an item in the checkout. */ + lineItems?: Maybe<Array<CheckoutLineItemInput>> + /** The shipping address to where the line items will be shipped. */ + shippingAddress?: Maybe<MailingAddressInput> + /** The text of an optional note that a shop owner can attach to the checkout. */ + note?: Maybe<Scalars['String']> + /** A list of extra information that is added to the checkout. */ + customAttributes?: Maybe<Array<AttributeInput>> + /** + * Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + * The required attributes are city, province, and country. + * Full validation of addresses is still done at complete time. + */ + allowPartialAddresses?: Maybe<Scalars['Boolean']> + /** + * The three-letter currency code of one of the shop's enabled presentment currencies. + * Including this field creates a checkout in the specified currency. By default, new + * checkouts are created in the shop's primary currency. + */ + presentmentCurrencyCode?: Maybe<CurrencyCode> +} + +/** Return type for `checkoutCreate` mutation. */ +export type CheckoutCreatePayload = { + __typename?: 'CheckoutCreatePayload' + /** The new checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCustomerAssociate` mutation. */ +export type CheckoutCustomerAssociatePayload = { + __typename?: 'CheckoutCustomerAssociatePayload' + /** The updated checkout object. */ + checkout: Checkout + /** The associated customer object. */ + customer?: Maybe<Customer> + /** List of errors that occurred executing the mutation. */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCustomerAssociateV2` mutation. */ +export type CheckoutCustomerAssociateV2Payload = { + __typename?: 'CheckoutCustomerAssociateV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** The associated customer object. */ + customer?: Maybe<Customer> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCustomerDisassociate` mutation. */ +export type CheckoutCustomerDisassociatePayload = { + __typename?: 'CheckoutCustomerDisassociatePayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCustomerDisassociateV2` mutation. */ +export type CheckoutCustomerDisassociateV2Payload = { + __typename?: 'CheckoutCustomerDisassociateV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutDiscountCodeApply` mutation. */ +export type CheckoutDiscountCodeApplyPayload = { + __typename?: 'CheckoutDiscountCodeApplyPayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutDiscountCodeApplyV2` mutation. */ +export type CheckoutDiscountCodeApplyV2Payload = { + __typename?: 'CheckoutDiscountCodeApplyV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutDiscountCodeRemove` mutation. */ +export type CheckoutDiscountCodeRemovePayload = { + __typename?: 'CheckoutDiscountCodeRemovePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutEmailUpdate` mutation. */ +export type CheckoutEmailUpdatePayload = { + __typename?: 'CheckoutEmailUpdatePayload' + /** The checkout object with the updated email. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutEmailUpdateV2` mutation. */ +export type CheckoutEmailUpdateV2Payload = { + __typename?: 'CheckoutEmailUpdateV2Payload' + /** The checkout object with the updated email. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Possible error codes that could be returned by CheckoutUserError. */ +export enum CheckoutErrorCode { + /** Input value is blank. */ + Blank = 'BLANK', + /** Input value is invalid. */ + Invalid = 'INVALID', + /** Input value is too long. */ + TooLong = 'TOO_LONG', + /** Input value is not present. */ + Present = 'PRESENT', + /** Input value should be less than maximum allowed value. */ + LessThan = 'LESS_THAN', + /** Input value should be greater than or equal to minimum allowed value. */ + GreaterThanOrEqualTo = 'GREATER_THAN_OR_EQUAL_TO', + /** Input value should be less or equal to maximum allowed value. */ + LessThanOrEqualTo = 'LESS_THAN_OR_EQUAL_TO', + /** Checkout is already completed. */ + AlreadyCompleted = 'ALREADY_COMPLETED', + /** Checkout is locked. */ + Locked = 'LOCKED', + /** Input value is not supported. */ + NotSupported = 'NOT_SUPPORTED', + /** Input email contains an invalid domain name. */ + BadDomain = 'BAD_DOMAIN', + /** Input Zip is invalid for country provided. */ + InvalidForCountry = 'INVALID_FOR_COUNTRY', + /** Input Zip is invalid for country and province provided. */ + InvalidForCountryAndProvince = 'INVALID_FOR_COUNTRY_AND_PROVINCE', + /** Invalid state in country. */ + InvalidStateInCountry = 'INVALID_STATE_IN_COUNTRY', + /** Invalid province in country. */ + InvalidProvinceInCountry = 'INVALID_PROVINCE_IN_COUNTRY', + /** Invalid region in country. */ + InvalidRegionInCountry = 'INVALID_REGION_IN_COUNTRY', + /** Shipping rate expired. */ + ShippingRateExpired = 'SHIPPING_RATE_EXPIRED', + /** Gift card cannot be applied to a checkout that contains a gift card. */ + GiftCardUnusable = 'GIFT_CARD_UNUSABLE', + /** Gift card is disabled. */ + GiftCardDisabled = 'GIFT_CARD_DISABLED', + /** Gift card code is invalid. */ + GiftCardCodeInvalid = 'GIFT_CARD_CODE_INVALID', + /** Gift card has already been applied. */ + GiftCardAlreadyApplied = 'GIFT_CARD_ALREADY_APPLIED', + /** Gift card currency does not match checkout currency. */ + GiftCardCurrencyMismatch = 'GIFT_CARD_CURRENCY_MISMATCH', + /** Gift card is expired. */ + GiftCardExpired = 'GIFT_CARD_EXPIRED', + /** Gift card has no funds left. */ + GiftCardDepleted = 'GIFT_CARD_DEPLETED', + /** Gift card was not found. */ + GiftCardNotFound = 'GIFT_CARD_NOT_FOUND', + /** Cart does not meet discount requirements notice. */ + CartDoesNotMeetDiscountRequirementsNotice = 'CART_DOES_NOT_MEET_DISCOUNT_REQUIREMENTS_NOTICE', + /** Discount expired. */ + DiscountExpired = 'DISCOUNT_EXPIRED', + /** Discount disabled. */ + DiscountDisabled = 'DISCOUNT_DISABLED', + /** Discount limit reached. */ + DiscountLimitReached = 'DISCOUNT_LIMIT_REACHED', + /** Discount not found. */ + DiscountNotFound = 'DISCOUNT_NOT_FOUND', + /** Customer already used once per customer discount notice. */ + CustomerAlreadyUsedOncePerCustomerDiscountNotice = 'CUSTOMER_ALREADY_USED_ONCE_PER_CUSTOMER_DISCOUNT_NOTICE', + /** Checkout is already completed. */ + Empty = 'EMPTY', + /** Not enough in stock. */ + NotEnoughInStock = 'NOT_ENOUGH_IN_STOCK', + /** Missing payment input. */ + MissingPaymentInput = 'MISSING_PAYMENT_INPUT', + /** The amount of the payment does not match the value to be paid. */ + TotalPriceMismatch = 'TOTAL_PRICE_MISMATCH', + /** Line item was not found in checkout. */ + LineItemNotFound = 'LINE_ITEM_NOT_FOUND', + /** Unable to apply discount. */ + UnableToApply = 'UNABLE_TO_APPLY', + /** Discount already applied. */ + DiscountAlreadyApplied = 'DISCOUNT_ALREADY_APPLIED', +} + +/** Return type for `checkoutGiftCardApply` mutation. */ +export type CheckoutGiftCardApplyPayload = { + __typename?: 'CheckoutGiftCardApplyPayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutGiftCardRemove` mutation. */ +export type CheckoutGiftCardRemovePayload = { + __typename?: 'CheckoutGiftCardRemovePayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutGiftCardRemoveV2` mutation. */ +export type CheckoutGiftCardRemoveV2Payload = { + __typename?: 'CheckoutGiftCardRemoveV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutGiftCardsAppend` mutation. */ +export type CheckoutGiftCardsAppendPayload = { + __typename?: 'CheckoutGiftCardsAppendPayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** A single line item in the checkout, grouped by variant and attributes. */ +export type CheckoutLineItem = Node & { + __typename?: 'CheckoutLineItem' + /** Extra information in the form of an array of Key-Value pairs about the line item. */ + customAttributes: Array<Attribute> + /** The discounts that have been allocated onto the checkout line item by discount applications. */ + discountAllocations: Array<DiscountAllocation> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The quantity of the line item. */ + quantity: Scalars['Int'] + /** Title of the line item. Defaults to the product's title. */ + title: Scalars['String'] + /** Unit price of the line item. */ + unitPrice?: Maybe<MoneyV2> + /** Product variant of the line item. */ + variant?: Maybe<ProductVariant> +} + +/** An auto-generated type for paginating through multiple CheckoutLineItems. */ +export type CheckoutLineItemConnection = { + __typename?: 'CheckoutLineItemConnection' + /** A list of edges. */ + edges: Array<CheckoutLineItemEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one CheckoutLineItem and a cursor during pagination. */ +export type CheckoutLineItemEdge = { + __typename?: 'CheckoutLineItemEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of CheckoutLineItemEdge. */ + node: CheckoutLineItem +} + +/** Specifies the input fields to create a line item on a checkout. */ +export type CheckoutLineItemInput = { + /** Extra information in the form of an array of Key-Value pairs about the line item. */ + customAttributes?: Maybe<Array<AttributeInput>> + /** The quantity of the line item. */ + quantity: Scalars['Int'] + /** The identifier of the product variant for the line item. */ + variantId: Scalars['ID'] +} + +/** Specifies the input fields to update a line item on the checkout. */ +export type CheckoutLineItemUpdateInput = { + /** The identifier of the line item. */ + id?: Maybe<Scalars['ID']> + /** The variant identifier of the line item. */ + variantId?: Maybe<Scalars['ID']> + /** The quantity of the line item. */ + quantity?: Maybe<Scalars['Int']> + /** Extra information in the form of an array of Key-Value pairs about the line item. */ + customAttributes?: Maybe<Array<AttributeInput>> +} + +/** Return type for `checkoutLineItemsAdd` mutation. */ +export type CheckoutLineItemsAddPayload = { + __typename?: 'CheckoutLineItemsAddPayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutLineItemsRemove` mutation. */ +export type CheckoutLineItemsRemovePayload = { + __typename?: 'CheckoutLineItemsRemovePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutLineItemsReplace` mutation. */ +export type CheckoutLineItemsReplacePayload = { + __typename?: 'CheckoutLineItemsReplacePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + userErrors: Array<CheckoutUserError> +} + +/** Return type for `checkoutLineItemsUpdate` mutation. */ +export type CheckoutLineItemsUpdatePayload = { + __typename?: 'CheckoutLineItemsUpdatePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutShippingAddressUpdate` mutation. */ +export type CheckoutShippingAddressUpdatePayload = { + __typename?: 'CheckoutShippingAddressUpdatePayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutShippingAddressUpdateV2` mutation. */ +export type CheckoutShippingAddressUpdateV2Payload = { + __typename?: 'CheckoutShippingAddressUpdateV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutShippingLineUpdate` mutation. */ +export type CheckoutShippingLineUpdatePayload = { + __typename?: 'CheckoutShippingLineUpdatePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Represents an error that happens during execution of a checkout mutation. */ +export type CheckoutUserError = DisplayableError & { + __typename?: 'CheckoutUserError' + /** Error code to uniquely identify the error. */ + code?: Maybe<CheckoutErrorCode> + /** Path to the input field which caused the error. */ + field?: Maybe<Array<Scalars['String']>> + /** The error message. */ + message: Scalars['String'] +} + +/** A collection represents a grouping of products that a shop owner can create to organize them or make their shops easier to browse. */ +export type Collection = Node & { + __typename?: 'Collection' + /** Stripped description of the collection, single line with HTML tags removed. */ + description: Scalars['String'] + /** The description of the collection, complete with HTML formatting. */ + descriptionHtml: Scalars['HTML'] + /** + * A human-friendly unique string for the collection automatically generated from its title. + * Limit of 255 characters. + */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** Image associated with the collection. */ + image?: Maybe<Image> + /** List of products in the collection. */ + products: ProductConnection + /** The collection’s name. Limit of 255 characters. */ + title: Scalars['String'] + /** The date and time when the collection was last modified. */ + updatedAt: Scalars['DateTime'] +} + +/** A collection represents a grouping of products that a shop owner can create to organize them or make their shops easier to browse. */ +export type CollectionDescriptionArgs = { + truncateAt?: Maybe<Scalars['Int']> +} + +/** A collection represents a grouping of products that a shop owner can create to organize them or make their shops easier to browse. */ +export type CollectionImageArgs = { + maxWidth?: Maybe<Scalars['Int']> + maxHeight?: Maybe<Scalars['Int']> + crop?: Maybe<CropRegion> + scale?: Maybe<Scalars['Int']> +} + +/** A collection represents a grouping of products that a shop owner can create to organize them or make their shops easier to browse. */ +export type CollectionProductsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductCollectionSortKeys> +} + +/** An auto-generated type for paginating through multiple Collections. */ +export type CollectionConnection = { + __typename?: 'CollectionConnection' + /** A list of edges. */ + edges: Array<CollectionEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Collection and a cursor during pagination. */ +export type CollectionEdge = { + __typename?: 'CollectionEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of CollectionEdge. */ + node: Collection +} + +/** The set of valid sort keys for the Collection query. */ +export enum CollectionSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `updated_at` value. */ + UpdatedAt = 'UPDATED_AT', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** A comment on an article. */ +export type Comment = Node & { + __typename?: 'Comment' + /** The comment’s author. */ + author: CommentAuthor + /** Stripped content of the comment, single line with HTML tags removed. */ + content: Scalars['String'] + /** The content of the comment, complete with HTML formatting. */ + contentHtml: Scalars['HTML'] + /** Globally unique identifier. */ + id: Scalars['ID'] +} + +/** A comment on an article. */ +export type CommentContentArgs = { + truncateAt?: Maybe<Scalars['Int']> +} + +/** The author of a comment. */ +export type CommentAuthor = { + __typename?: 'CommentAuthor' + /** The author's email. */ + email: Scalars['String'] + /** The author’s name. */ + name: Scalars['String'] +} + +/** An auto-generated type for paginating through multiple Comments. */ +export type CommentConnection = { + __typename?: 'CommentConnection' + /** A list of edges. */ + edges: Array<CommentEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Comment and a cursor during pagination. */ +export type CommentEdge = { + __typename?: 'CommentEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of CommentEdge. */ + node: Comment +} + +/** ISO 3166-1 alpha-2 country codes with some differences. */ +export enum CountryCode { + /** Afghanistan. */ + Af = 'AF', + /** Åland Islands. */ + Ax = 'AX', + /** Albania. */ + Al = 'AL', + /** Algeria. */ + Dz = 'DZ', + /** Andorra. */ + Ad = 'AD', + /** Angola. */ + Ao = 'AO', + /** Anguilla. */ + Ai = 'AI', + /** Antigua & Barbuda. */ + Ag = 'AG', + /** Argentina. */ + Ar = 'AR', + /** Armenia. */ + Am = 'AM', + /** Aruba. */ + Aw = 'AW', + /** Australia. */ + Au = 'AU', + /** Austria. */ + At = 'AT', + /** Azerbaijan. */ + Az = 'AZ', + /** Bahamas. */ + Bs = 'BS', + /** Bahrain. */ + Bh = 'BH', + /** Bangladesh. */ + Bd = 'BD', + /** Barbados. */ + Bb = 'BB', + /** Belarus. */ + By = 'BY', + /** Belgium. */ + Be = 'BE', + /** Belize. */ + Bz = 'BZ', + /** Benin. */ + Bj = 'BJ', + /** Bermuda. */ + Bm = 'BM', + /** Bhutan. */ + Bt = 'BT', + /** Bolivia. */ + Bo = 'BO', + /** Bosnia & Herzegovina. */ + Ba = 'BA', + /** Botswana. */ + Bw = 'BW', + /** Bouvet Island. */ + Bv = 'BV', + /** Brazil. */ + Br = 'BR', + /** British Indian Ocean Territory. */ + Io = 'IO', + /** Brunei. */ + Bn = 'BN', + /** Bulgaria. */ + Bg = 'BG', + /** Burkina Faso. */ + Bf = 'BF', + /** Burundi. */ + Bi = 'BI', + /** Cambodia. */ + Kh = 'KH', + /** Canada. */ + Ca = 'CA', + /** Cape Verde. */ + Cv = 'CV', + /** Caribbean Netherlands. */ + Bq = 'BQ', + /** Cayman Islands. */ + Ky = 'KY', + /** Central African Republic. */ + Cf = 'CF', + /** Chad. */ + Td = 'TD', + /** Chile. */ + Cl = 'CL', + /** China. */ + Cn = 'CN', + /** Christmas Island. */ + Cx = 'CX', + /** Cocos (Keeling) Islands. */ + Cc = 'CC', + /** Colombia. */ + Co = 'CO', + /** Comoros. */ + Km = 'KM', + /** Congo - Brazzaville. */ + Cg = 'CG', + /** Congo - Kinshasa. */ + Cd = 'CD', + /** Cook Islands. */ + Ck = 'CK', + /** Costa Rica. */ + Cr = 'CR', + /** Croatia. */ + Hr = 'HR', + /** Cuba. */ + Cu = 'CU', + /** Curaçao. */ + Cw = 'CW', + /** Cyprus. */ + Cy = 'CY', + /** Czechia. */ + Cz = 'CZ', + /** Côte d’Ivoire. */ + Ci = 'CI', + /** Denmark. */ + Dk = 'DK', + /** Djibouti. */ + Dj = 'DJ', + /** Dominica. */ + Dm = 'DM', + /** Dominican Republic. */ + Do = 'DO', + /** Ecuador. */ + Ec = 'EC', + /** Egypt. */ + Eg = 'EG', + /** El Salvador. */ + Sv = 'SV', + /** Equatorial Guinea. */ + Gq = 'GQ', + /** Eritrea. */ + Er = 'ER', + /** Estonia. */ + Ee = 'EE', + /** Eswatini. */ + Sz = 'SZ', + /** Ethiopia. */ + Et = 'ET', + /** Falkland Islands. */ + Fk = 'FK', + /** Faroe Islands. */ + Fo = 'FO', + /** Fiji. */ + Fj = 'FJ', + /** Finland. */ + Fi = 'FI', + /** France. */ + Fr = 'FR', + /** French Guiana. */ + Gf = 'GF', + /** French Polynesia. */ + Pf = 'PF', + /** French Southern Territories. */ + Tf = 'TF', + /** Gabon. */ + Ga = 'GA', + /** Gambia. */ + Gm = 'GM', + /** Georgia. */ + Ge = 'GE', + /** Germany. */ + De = 'DE', + /** Ghana. */ + Gh = 'GH', + /** Gibraltar. */ + Gi = 'GI', + /** Greece. */ + Gr = 'GR', + /** Greenland. */ + Gl = 'GL', + /** Grenada. */ + Gd = 'GD', + /** Guadeloupe. */ + Gp = 'GP', + /** Guatemala. */ + Gt = 'GT', + /** Guernsey. */ + Gg = 'GG', + /** Guinea. */ + Gn = 'GN', + /** Guinea-Bissau. */ + Gw = 'GW', + /** Guyana. */ + Gy = 'GY', + /** Haiti. */ + Ht = 'HT', + /** Heard & McDonald Islands. */ + Hm = 'HM', + /** Vatican City. */ + Va = 'VA', + /** Honduras. */ + Hn = 'HN', + /** Hong Kong SAR. */ + Hk = 'HK', + /** Hungary. */ + Hu = 'HU', + /** Iceland. */ + Is = 'IS', + /** India. */ + In = 'IN', + /** Indonesia. */ + Id = 'ID', + /** Iran. */ + Ir = 'IR', + /** Iraq. */ + Iq = 'IQ', + /** Ireland. */ + Ie = 'IE', + /** Isle of Man. */ + Im = 'IM', + /** Israel. */ + Il = 'IL', + /** Italy. */ + It = 'IT', + /** Jamaica. */ + Jm = 'JM', + /** Japan. */ + Jp = 'JP', + /** Jersey. */ + Je = 'JE', + /** Jordan. */ + Jo = 'JO', + /** Kazakhstan. */ + Kz = 'KZ', + /** Kenya. */ + Ke = 'KE', + /** Kiribati. */ + Ki = 'KI', + /** North Korea. */ + Kp = 'KP', + /** Kosovo. */ + Xk = 'XK', + /** Kuwait. */ + Kw = 'KW', + /** Kyrgyzstan. */ + Kg = 'KG', + /** Laos. */ + La = 'LA', + /** Latvia. */ + Lv = 'LV', + /** Lebanon. */ + Lb = 'LB', + /** Lesotho. */ + Ls = 'LS', + /** Liberia. */ + Lr = 'LR', + /** Libya. */ + Ly = 'LY', + /** Liechtenstein. */ + Li = 'LI', + /** Lithuania. */ + Lt = 'LT', + /** Luxembourg. */ + Lu = 'LU', + /** Macao SAR. */ + Mo = 'MO', + /** Madagascar. */ + Mg = 'MG', + /** Malawi. */ + Mw = 'MW', + /** Malaysia. */ + My = 'MY', + /** Maldives. */ + Mv = 'MV', + /** Mali. */ + Ml = 'ML', + /** Malta. */ + Mt = 'MT', + /** Martinique. */ + Mq = 'MQ', + /** Mauritania. */ + Mr = 'MR', + /** Mauritius. */ + Mu = 'MU', + /** Mayotte. */ + Yt = 'YT', + /** Mexico. */ + Mx = 'MX', + /** Moldova. */ + Md = 'MD', + /** Monaco. */ + Mc = 'MC', + /** Mongolia. */ + Mn = 'MN', + /** Montenegro. */ + Me = 'ME', + /** Montserrat. */ + Ms = 'MS', + /** Morocco. */ + Ma = 'MA', + /** Mozambique. */ + Mz = 'MZ', + /** Myanmar (Burma). */ + Mm = 'MM', + /** Namibia. */ + Na = 'NA', + /** Nauru. */ + Nr = 'NR', + /** Nepal. */ + Np = 'NP', + /** Netherlands. */ + Nl = 'NL', + /** Netherlands Antilles. */ + An = 'AN', + /** New Caledonia. */ + Nc = 'NC', + /** New Zealand. */ + Nz = 'NZ', + /** Nicaragua. */ + Ni = 'NI', + /** Niger. */ + Ne = 'NE', + /** Nigeria. */ + Ng = 'NG', + /** Niue. */ + Nu = 'NU', + /** Norfolk Island. */ + Nf = 'NF', + /** North Macedonia. */ + Mk = 'MK', + /** Norway. */ + No = 'NO', + /** Oman. */ + Om = 'OM', + /** Pakistan. */ + Pk = 'PK', + /** Palestinian Territories. */ + Ps = 'PS', + /** Panama. */ + Pa = 'PA', + /** Papua New Guinea. */ + Pg = 'PG', + /** Paraguay. */ + Py = 'PY', + /** Peru. */ + Pe = 'PE', + /** Philippines. */ + Ph = 'PH', + /** Pitcairn Islands. */ + Pn = 'PN', + /** Poland. */ + Pl = 'PL', + /** Portugal. */ + Pt = 'PT', + /** Qatar. */ + Qa = 'QA', + /** Cameroon. */ + Cm = 'CM', + /** Réunion. */ + Re = 'RE', + /** Romania. */ + Ro = 'RO', + /** Russia. */ + Ru = 'RU', + /** Rwanda. */ + Rw = 'RW', + /** St. Barthélemy. */ + Bl = 'BL', + /** St. Helena. */ + Sh = 'SH', + /** St. Kitts & Nevis. */ + Kn = 'KN', + /** St. Lucia. */ + Lc = 'LC', + /** St. Martin. */ + Mf = 'MF', + /** St. Pierre & Miquelon. */ + Pm = 'PM', + /** Samoa. */ + Ws = 'WS', + /** San Marino. */ + Sm = 'SM', + /** São Tomé & Príncipe. */ + St = 'ST', + /** Saudi Arabia. */ + Sa = 'SA', + /** Senegal. */ + Sn = 'SN', + /** Serbia. */ + Rs = 'RS', + /** Seychelles. */ + Sc = 'SC', + /** Sierra Leone. */ + Sl = 'SL', + /** Singapore. */ + Sg = 'SG', + /** Sint Maarten. */ + Sx = 'SX', + /** Slovakia. */ + Sk = 'SK', + /** Slovenia. */ + Si = 'SI', + /** Solomon Islands. */ + Sb = 'SB', + /** Somalia. */ + So = 'SO', + /** South Africa. */ + Za = 'ZA', + /** South Georgia & South Sandwich Islands. */ + Gs = 'GS', + /** South Korea. */ + Kr = 'KR', + /** South Sudan. */ + Ss = 'SS', + /** Spain. */ + Es = 'ES', + /** Sri Lanka. */ + Lk = 'LK', + /** St. Vincent & Grenadines. */ + Vc = 'VC', + /** Sudan. */ + Sd = 'SD', + /** Suriname. */ + Sr = 'SR', + /** Svalbard & Jan Mayen. */ + Sj = 'SJ', + /** Sweden. */ + Se = 'SE', + /** Switzerland. */ + Ch = 'CH', + /** Syria. */ + Sy = 'SY', + /** Taiwan. */ + Tw = 'TW', + /** Tajikistan. */ + Tj = 'TJ', + /** Tanzania. */ + Tz = 'TZ', + /** Thailand. */ + Th = 'TH', + /** Timor-Leste. */ + Tl = 'TL', + /** Togo. */ + Tg = 'TG', + /** Tokelau. */ + Tk = 'TK', + /** Tonga. */ + To = 'TO', + /** Trinidad & Tobago. */ + Tt = 'TT', + /** Tunisia. */ + Tn = 'TN', + /** Turkey. */ + Tr = 'TR', + /** Turkmenistan. */ + Tm = 'TM', + /** Turks & Caicos Islands. */ + Tc = 'TC', + /** Tuvalu. */ + Tv = 'TV', + /** Uganda. */ + Ug = 'UG', + /** Ukraine. */ + Ua = 'UA', + /** United Arab Emirates. */ + Ae = 'AE', + /** United Kingdom. */ + Gb = 'GB', + /** United States. */ + Us = 'US', + /** U.S. Outlying Islands. */ + Um = 'UM', + /** Uruguay. */ + Uy = 'UY', + /** Uzbekistan. */ + Uz = 'UZ', + /** Vanuatu. */ + Vu = 'VU', + /** Venezuela. */ + Ve = 'VE', + /** Vietnam. */ + Vn = 'VN', + /** British Virgin Islands. */ + Vg = 'VG', + /** Wallis & Futuna. */ + Wf = 'WF', + /** Western Sahara. */ + Eh = 'EH', + /** Yemen. */ + Ye = 'YE', + /** Zambia. */ + Zm = 'ZM', + /** Zimbabwe. */ + Zw = 'ZW', +} + +/** Credit card information used for a payment. */ +export type CreditCard = { + __typename?: 'CreditCard' + /** The brand of the credit card. */ + brand?: Maybe<Scalars['String']> + /** The expiry month of the credit card. */ + expiryMonth?: Maybe<Scalars['Int']> + /** The expiry year of the credit card. */ + expiryYear?: Maybe<Scalars['Int']> + /** The credit card's BIN number. */ + firstDigits?: Maybe<Scalars['String']> + /** The first name of the card holder. */ + firstName?: Maybe<Scalars['String']> + /** The last 4 digits of the credit card. */ + lastDigits?: Maybe<Scalars['String']> + /** The last name of the card holder. */ + lastName?: Maybe<Scalars['String']> + /** The masked credit card number with only the last 4 digits displayed. */ + maskedNumber?: Maybe<Scalars['String']> +} + +/** + * Specifies the fields required to complete a checkout with + * a Shopify vaulted credit card payment. + */ +export type CreditCardPaymentInput = { + /** The amount of the payment. */ + amount: Scalars['Money'] + /** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */ + idempotencyKey: Scalars['String'] + /** The billing address for the payment. */ + billingAddress: MailingAddressInput + /** The ID returned by Shopify's Card Vault. */ + vaultId: Scalars['String'] + /** Executes the payment in test mode if possible. Defaults to `false`. */ + test?: Maybe<Scalars['Boolean']> +} + +/** + * Specifies the fields required to complete a checkout with + * a Shopify vaulted credit card payment. + */ +export type CreditCardPaymentInputV2 = { + /** The amount and currency of the payment. */ + paymentAmount: MoneyInput + /** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */ + idempotencyKey: Scalars['String'] + /** The billing address for the payment. */ + billingAddress: MailingAddressInput + /** The ID returned by Shopify's Card Vault. */ + vaultId: Scalars['String'] + /** Executes the payment in test mode if possible. Defaults to `false`. */ + test?: Maybe<Scalars['Boolean']> +} + +/** The part of the image that should remain after cropping. */ +export enum CropRegion { + /** Keep the center of the image. */ + Center = 'CENTER', + /** Keep the top of the image. */ + Top = 'TOP', + /** Keep the bottom of the image. */ + Bottom = 'BOTTOM', + /** Keep the left of the image. */ + Left = 'LEFT', + /** Keep the right of the image. */ + Right = 'RIGHT', +} + +/** Currency codes. */ +export enum CurrencyCode { + /** United States Dollars (USD). */ + Usd = 'USD', + /** Euro (EUR). */ + Eur = 'EUR', + /** United Kingdom Pounds (GBP). */ + Gbp = 'GBP', + /** Canadian Dollars (CAD). */ + Cad = 'CAD', + /** Afghan Afghani (AFN). */ + Afn = 'AFN', + /** Albanian Lek (ALL). */ + All = 'ALL', + /** Algerian Dinar (DZD). */ + Dzd = 'DZD', + /** Angolan Kwanza (AOA). */ + Aoa = 'AOA', + /** Argentine Pesos (ARS). */ + Ars = 'ARS', + /** Armenian Dram (AMD). */ + Amd = 'AMD', + /** Aruban Florin (AWG). */ + Awg = 'AWG', + /** Australian Dollars (AUD). */ + Aud = 'AUD', + /** Barbadian Dollar (BBD). */ + Bbd = 'BBD', + /** Azerbaijani Manat (AZN). */ + Azn = 'AZN', + /** Bangladesh Taka (BDT). */ + Bdt = 'BDT', + /** Bahamian Dollar (BSD). */ + Bsd = 'BSD', + /** Bahraini Dinar (BHD). */ + Bhd = 'BHD', + /** Burundian Franc (BIF). */ + Bif = 'BIF', + /** Belarusian Ruble (BYN). */ + Byn = 'BYN', + /** Belarusian Ruble (BYR). */ + Byr = 'BYR', + /** Belize Dollar (BZD). */ + Bzd = 'BZD', + /** Bermudian Dollar (BMD). */ + Bmd = 'BMD', + /** Bhutanese Ngultrum (BTN). */ + Btn = 'BTN', + /** Bosnia and Herzegovina Convertible Mark (BAM). */ + Bam = 'BAM', + /** Brazilian Real (BRL). */ + Brl = 'BRL', + /** Bolivian Boliviano (BOB). */ + Bob = 'BOB', + /** Botswana Pula (BWP). */ + Bwp = 'BWP', + /** Brunei Dollar (BND). */ + Bnd = 'BND', + /** Bulgarian Lev (BGN). */ + Bgn = 'BGN', + /** Burmese Kyat (MMK). */ + Mmk = 'MMK', + /** Cambodian Riel. */ + Khr = 'KHR', + /** Cape Verdean escudo (CVE). */ + Cve = 'CVE', + /** Cayman Dollars (KYD). */ + Kyd = 'KYD', + /** Central African CFA Franc (XAF). */ + Xaf = 'XAF', + /** Chilean Peso (CLP). */ + Clp = 'CLP', + /** Chinese Yuan Renminbi (CNY). */ + Cny = 'CNY', + /** Colombian Peso (COP). */ + Cop = 'COP', + /** Comorian Franc (KMF). */ + Kmf = 'KMF', + /** Congolese franc (CDF). */ + Cdf = 'CDF', + /** Costa Rican Colones (CRC). */ + Crc = 'CRC', + /** Croatian Kuna (HRK). */ + Hrk = 'HRK', + /** Czech Koruny (CZK). */ + Czk = 'CZK', + /** Danish Kroner (DKK). */ + Dkk = 'DKK', + /** Djiboutian Franc (DJF). */ + Djf = 'DJF', + /** Dominican Peso (DOP). */ + Dop = 'DOP', + /** East Caribbean Dollar (XCD). */ + Xcd = 'XCD', + /** Egyptian Pound (EGP). */ + Egp = 'EGP', + /** Eritrean Nakfa (ERN). */ + Ern = 'ERN', + /** Ethiopian Birr (ETB). */ + Etb = 'ETB', + /** Falkland Islands Pounds (FKP). */ + Fkp = 'FKP', + /** CFP Franc (XPF). */ + Xpf = 'XPF', + /** Fijian Dollars (FJD). */ + Fjd = 'FJD', + /** Gibraltar Pounds (GIP). */ + Gip = 'GIP', + /** Gambian Dalasi (GMD). */ + Gmd = 'GMD', + /** Ghanaian Cedi (GHS). */ + Ghs = 'GHS', + /** Guatemalan Quetzal (GTQ). */ + Gtq = 'GTQ', + /** Guyanese Dollar (GYD). */ + Gyd = 'GYD', + /** Georgian Lari (GEL). */ + Gel = 'GEL', + /** Guinean Franc (GNF). */ + Gnf = 'GNF', + /** Haitian Gourde (HTG). */ + Htg = 'HTG', + /** Honduran Lempira (HNL). */ + Hnl = 'HNL', + /** Hong Kong Dollars (HKD). */ + Hkd = 'HKD', + /** Hungarian Forint (HUF). */ + Huf = 'HUF', + /** Icelandic Kronur (ISK). */ + Isk = 'ISK', + /** Indian Rupees (INR). */ + Inr = 'INR', + /** Indonesian Rupiah (IDR). */ + Idr = 'IDR', + /** Israeli New Shekel (NIS). */ + Ils = 'ILS', + /** Iranian Rial (IRR). */ + Irr = 'IRR', + /** Iraqi Dinar (IQD). */ + Iqd = 'IQD', + /** Jamaican Dollars (JMD). */ + Jmd = 'JMD', + /** Japanese Yen (JPY). */ + Jpy = 'JPY', + /** Jersey Pound. */ + Jep = 'JEP', + /** Jordanian Dinar (JOD). */ + Jod = 'JOD', + /** Kazakhstani Tenge (KZT). */ + Kzt = 'KZT', + /** Kenyan Shilling (KES). */ + Kes = 'KES', + /** Kiribati Dollar (KID). */ + Kid = 'KID', + /** Kuwaiti Dinar (KWD). */ + Kwd = 'KWD', + /** Kyrgyzstani Som (KGS). */ + Kgs = 'KGS', + /** Laotian Kip (LAK). */ + Lak = 'LAK', + /** Latvian Lati (LVL). */ + Lvl = 'LVL', + /** Lebanese Pounds (LBP). */ + Lbp = 'LBP', + /** Lesotho Loti (LSL). */ + Lsl = 'LSL', + /** Liberian Dollar (LRD). */ + Lrd = 'LRD', + /** Libyan Dinar (LYD). */ + Lyd = 'LYD', + /** Lithuanian Litai (LTL). */ + Ltl = 'LTL', + /** Malagasy Ariary (MGA). */ + Mga = 'MGA', + /** Macedonia Denar (MKD). */ + Mkd = 'MKD', + /** Macanese Pataca (MOP). */ + Mop = 'MOP', + /** Malawian Kwacha (MWK). */ + Mwk = 'MWK', + /** Maldivian Rufiyaa (MVR). */ + Mvr = 'MVR', + /** Mauritanian Ouguiya (MRU). */ + Mru = 'MRU', + /** Mexican Pesos (MXN). */ + Mxn = 'MXN', + /** Malaysian Ringgits (MYR). */ + Myr = 'MYR', + /** Mauritian Rupee (MUR). */ + Mur = 'MUR', + /** Moldovan Leu (MDL). */ + Mdl = 'MDL', + /** Moroccan Dirham. */ + Mad = 'MAD', + /** Mongolian Tugrik. */ + Mnt = 'MNT', + /** Mozambican Metical. */ + Mzn = 'MZN', + /** Namibian Dollar. */ + Nad = 'NAD', + /** Nepalese Rupee (NPR). */ + Npr = 'NPR', + /** Netherlands Antillean Guilder. */ + Ang = 'ANG', + /** New Zealand Dollars (NZD). */ + Nzd = 'NZD', + /** Nicaraguan Córdoba (NIO). */ + Nio = 'NIO', + /** Nigerian Naira (NGN). */ + Ngn = 'NGN', + /** Norwegian Kroner (NOK). */ + Nok = 'NOK', + /** Omani Rial (OMR). */ + Omr = 'OMR', + /** Panamian Balboa (PAB). */ + Pab = 'PAB', + /** Pakistani Rupee (PKR). */ + Pkr = 'PKR', + /** Papua New Guinean Kina (PGK). */ + Pgk = 'PGK', + /** Paraguayan Guarani (PYG). */ + Pyg = 'PYG', + /** Peruvian Nuevo Sol (PEN). */ + Pen = 'PEN', + /** Philippine Peso (PHP). */ + Php = 'PHP', + /** Polish Zlotych (PLN). */ + Pln = 'PLN', + /** Qatari Rial (QAR). */ + Qar = 'QAR', + /** Romanian Lei (RON). */ + Ron = 'RON', + /** Russian Rubles (RUB). */ + Rub = 'RUB', + /** Rwandan Franc (RWF). */ + Rwf = 'RWF', + /** Samoan Tala (WST). */ + Wst = 'WST', + /** Saint Helena Pounds (SHP). */ + Shp = 'SHP', + /** Saudi Riyal (SAR). */ + Sar = 'SAR', + /** Sao Tome And Principe Dobra (STD). */ + Std = 'STD', + /** Serbian dinar (RSD). */ + Rsd = 'RSD', + /** Seychellois Rupee (SCR). */ + Scr = 'SCR', + /** Sierra Leonean Leone (SLL). */ + Sll = 'SLL', + /** Singapore Dollars (SGD). */ + Sgd = 'SGD', + /** Sudanese Pound (SDG). */ + Sdg = 'SDG', + /** Somali Shilling (SOS). */ + Sos = 'SOS', + /** Syrian Pound (SYP). */ + Syp = 'SYP', + /** South African Rand (ZAR). */ + Zar = 'ZAR', + /** South Korean Won (KRW). */ + Krw = 'KRW', + /** South Sudanese Pound (SSP). */ + Ssp = 'SSP', + /** Solomon Islands Dollar (SBD). */ + Sbd = 'SBD', + /** Sri Lankan Rupees (LKR). */ + Lkr = 'LKR', + /** Surinamese Dollar (SRD). */ + Srd = 'SRD', + /** Swazi Lilangeni (SZL). */ + Szl = 'SZL', + /** Swedish Kronor (SEK). */ + Sek = 'SEK', + /** Swiss Francs (CHF). */ + Chf = 'CHF', + /** Taiwan Dollars (TWD). */ + Twd = 'TWD', + /** Thai baht (THB). */ + Thb = 'THB', + /** Tajikistani Somoni (TJS). */ + Tjs = 'TJS', + /** Tanzanian Shilling (TZS). */ + Tzs = 'TZS', + /** Tongan Pa'anga (TOP). */ + Top = 'TOP', + /** Trinidad and Tobago Dollars (TTD). */ + Ttd = 'TTD', + /** Tunisian Dinar (TND). */ + Tnd = 'TND', + /** Turkish Lira (TRY). */ + Try = 'TRY', + /** Turkmenistani Manat (TMT). */ + Tmt = 'TMT', + /** Ugandan Shilling (UGX). */ + Ugx = 'UGX', + /** Ukrainian Hryvnia (UAH). */ + Uah = 'UAH', + /** United Arab Emirates Dirham (AED). */ + Aed = 'AED', + /** Uruguayan Pesos (UYU). */ + Uyu = 'UYU', + /** Uzbekistan som (UZS). */ + Uzs = 'UZS', + /** Vanuatu Vatu (VUV). */ + Vuv = 'VUV', + /** Venezuelan Bolivares (VEF). */ + Vef = 'VEF', + /** Venezuelan Bolivares (VES). */ + Ves = 'VES', + /** Vietnamese đồng (VND). */ + Vnd = 'VND', + /** West African CFA franc (XOF). */ + Xof = 'XOF', + /** Yemeni Rial (YER). */ + Yer = 'YER', + /** Zambian Kwacha (ZMW). */ + Zmw = 'ZMW', +} + +/** A customer represents a customer account with the shop. Customer accounts store contact information for the customer, saving logged-in customers the trouble of having to provide it at every checkout. */ +export type Customer = { + __typename?: 'Customer' + /** Indicates whether the customer has consented to be sent marketing material via email. */ + acceptsMarketing: Scalars['Boolean'] + /** A list of addresses for the customer. */ + addresses: MailingAddressConnection + /** The date and time when the customer was created. */ + createdAt: Scalars['DateTime'] + /** The customer’s default address. */ + defaultAddress?: Maybe<MailingAddress> + /** The customer’s name, email or phone number. */ + displayName: Scalars['String'] + /** The customer’s email address. */ + email?: Maybe<Scalars['String']> + /** The customer’s first name. */ + firstName?: Maybe<Scalars['String']> + /** A unique identifier for the customer. */ + id: Scalars['ID'] + /** The customer's most recently updated, incomplete checkout. */ + lastIncompleteCheckout?: Maybe<Checkout> + /** The customer’s last name. */ + lastName?: Maybe<Scalars['String']> + /** The orders associated with the customer. */ + orders: OrderConnection + /** The customer’s phone number. */ + phone?: Maybe<Scalars['String']> + /** + * A comma separated list of tags that have been added to the customer. + * Additional access scope required: unauthenticated_read_customer_tags. + */ + tags: Array<Scalars['String']> + /** The date and time when the customer information was updated. */ + updatedAt: Scalars['DateTime'] +} + +/** A customer represents a customer account with the shop. Customer accounts store contact information for the customer, saving logged-in customers the trouble of having to provide it at every checkout. */ +export type CustomerAddressesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** A customer represents a customer account with the shop. Customer accounts store contact information for the customer, saving logged-in customers the trouble of having to provide it at every checkout. */ +export type CustomerOrdersArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<OrderSortKeys> + query?: Maybe<Scalars['String']> +} + +/** A CustomerAccessToken represents the unique token required to make modifications to the customer object. */ +export type CustomerAccessToken = { + __typename?: 'CustomerAccessToken' + /** The customer’s access token. */ + accessToken: Scalars['String'] + /** The date and time when the customer access token expires. */ + expiresAt: Scalars['DateTime'] +} + +/** Specifies the input fields required to create a customer access token. */ +export type CustomerAccessTokenCreateInput = { + /** The email associated to the customer. */ + email: Scalars['String'] + /** The login password to be used by the customer. */ + password: Scalars['String'] +} + +/** Return type for `customerAccessTokenCreate` mutation. */ +export type CustomerAccessTokenCreatePayload = { + __typename?: 'CustomerAccessTokenCreatePayload' + /** The newly created customer access token object. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerAccessTokenCreateWithMultipass` mutation. */ +export type CustomerAccessTokenCreateWithMultipassPayload = { + __typename?: 'CustomerAccessTokenCreateWithMultipassPayload' + /** An access token object associated with the customer. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> +} + +/** Return type for `customerAccessTokenDelete` mutation. */ +export type CustomerAccessTokenDeletePayload = { + __typename?: 'CustomerAccessTokenDeletePayload' + /** The destroyed access token. */ + deletedAccessToken?: Maybe<Scalars['String']> + /** ID of the destroyed customer access token. */ + deletedCustomerAccessTokenId?: Maybe<Scalars['String']> + /** List of errors that occurred executing the mutation. */ + userErrors: Array<UserError> +} + +/** Return type for `customerAccessTokenRenew` mutation. */ +export type CustomerAccessTokenRenewPayload = { + __typename?: 'CustomerAccessTokenRenewPayload' + /** The renewed customer access token object. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + userErrors: Array<UserError> +} + +/** Return type for `customerActivateByUrl` mutation. */ +export type CustomerActivateByUrlPayload = { + __typename?: 'CustomerActivateByUrlPayload' + /** The customer that was activated. */ + customer?: Maybe<Customer> + /** A new customer access token for the customer. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> +} + +/** Specifies the input fields required to activate a customer. */ +export type CustomerActivateInput = { + /** The activation token required to activate the customer. */ + activationToken: Scalars['String'] + /** New password that will be set during activation. */ + password: Scalars['String'] +} + +/** Return type for `customerActivate` mutation. */ +export type CustomerActivatePayload = { + __typename?: 'CustomerActivatePayload' + /** The customer object. */ + customer?: Maybe<Customer> + /** A newly created customer access token object for the customer. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerAddressCreate` mutation. */ +export type CustomerAddressCreatePayload = { + __typename?: 'CustomerAddressCreatePayload' + /** The new customer address object. */ + customerAddress?: Maybe<MailingAddress> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerAddressDelete` mutation. */ +export type CustomerAddressDeletePayload = { + __typename?: 'CustomerAddressDeletePayload' + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** ID of the deleted customer address. */ + deletedCustomerAddressId?: Maybe<Scalars['String']> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerAddressUpdate` mutation. */ +export type CustomerAddressUpdatePayload = { + __typename?: 'CustomerAddressUpdatePayload' + /** The customer’s updated mailing address. */ + customerAddress?: Maybe<MailingAddress> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Specifies the fields required to create a new customer. */ +export type CustomerCreateInput = { + /** The customer’s first name. */ + firstName?: Maybe<Scalars['String']> + /** The customer’s last name. */ + lastName?: Maybe<Scalars['String']> + /** The customer’s email. */ + email: Scalars['String'] + /** + * A unique phone number for the customer. + * + * Formatted using E.164 standard. For example, _+16135551111_. + */ + phone?: Maybe<Scalars['String']> + /** The login password used by the customer. */ + password: Scalars['String'] + /** Indicates whether the customer has consented to be sent marketing material via email. */ + acceptsMarketing?: Maybe<Scalars['Boolean']> +} + +/** Return type for `customerCreate` mutation. */ +export type CustomerCreatePayload = { + __typename?: 'CustomerCreatePayload' + /** The created customer object. */ + customer?: Maybe<Customer> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerDefaultAddressUpdate` mutation. */ +export type CustomerDefaultAddressUpdatePayload = { + __typename?: 'CustomerDefaultAddressUpdatePayload' + /** The updated customer object. */ + customer?: Maybe<Customer> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Possible error codes that could be returned by CustomerUserError. */ +export enum CustomerErrorCode { + /** Input value is blank. */ + Blank = 'BLANK', + /** Input value is invalid. */ + Invalid = 'INVALID', + /** Input value is already taken. */ + Taken = 'TAKEN', + /** Input value is too long. */ + TooLong = 'TOO_LONG', + /** Input value is too short. */ + TooShort = 'TOO_SHORT', + /** Unidentified customer. */ + UnidentifiedCustomer = 'UNIDENTIFIED_CUSTOMER', + /** Customer is disabled. */ + CustomerDisabled = 'CUSTOMER_DISABLED', + /** Input password starts or ends with whitespace. */ + PasswordStartsOrEndsWithWhitespace = 'PASSWORD_STARTS_OR_ENDS_WITH_WHITESPACE', + /** Input contains HTML tags. */ + ContainsHtmlTags = 'CONTAINS_HTML_TAGS', + /** Input contains URL. */ + ContainsUrl = 'CONTAINS_URL', + /** Invalid activation token. */ + TokenInvalid = 'TOKEN_INVALID', + /** Customer already enabled. */ + AlreadyEnabled = 'ALREADY_ENABLED', + /** Address does not exist. */ + NotFound = 'NOT_FOUND', + /** Input email contains an invalid domain name. */ + BadDomain = 'BAD_DOMAIN', + /** Multipass token is not valid. */ + InvalidMultipassRequest = 'INVALID_MULTIPASS_REQUEST', +} + +/** Return type for `customerRecover` mutation. */ +export type CustomerRecoverPayload = { + __typename?: 'CustomerRecoverPayload' + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerResetByUrl` mutation. */ +export type CustomerResetByUrlPayload = { + __typename?: 'CustomerResetByUrlPayload' + /** The customer object which was reset. */ + customer?: Maybe<Customer> + /** A newly created customer access token object for the customer. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Specifies the fields required to reset a customer’s password. */ +export type CustomerResetInput = { + /** The reset token required to reset the customer’s password. */ + resetToken: Scalars['String'] + /** New password that will be set as part of the reset password process. */ + password: Scalars['String'] +} + +/** Return type for `customerReset` mutation. */ +export type CustomerResetPayload = { + __typename?: 'CustomerResetPayload' + /** The customer object which was reset. */ + customer?: Maybe<Customer> + /** A newly created customer access token object for the customer. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Specifies the fields required to update the Customer information. */ +export type CustomerUpdateInput = { + /** The customer’s first name. */ + firstName?: Maybe<Scalars['String']> + /** The customer’s last name. */ + lastName?: Maybe<Scalars['String']> + /** The customer’s email. */ + email?: Maybe<Scalars['String']> + /** + * A unique phone number for the customer. + * + * Formatted using E.164 standard. For example, _+16135551111_. To remove the phone number, specify `null`. + */ + phone?: Maybe<Scalars['String']> + /** The login password used by the customer. */ + password?: Maybe<Scalars['String']> + /** Indicates whether the customer has consented to be sent marketing material via email. */ + acceptsMarketing?: Maybe<Scalars['Boolean']> +} + +/** Return type for `customerUpdate` mutation. */ +export type CustomerUpdatePayload = { + __typename?: 'CustomerUpdatePayload' + /** The updated customer object. */ + customer?: Maybe<Customer> + /** + * The newly created customer access token. If the customer's password is updated, all previous access tokens + * (including the one used to perform this mutation) become invalid, and a new token is generated. + */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Represents an error that happens during execution of a customer mutation. */ +export type CustomerUserError = DisplayableError & { + __typename?: 'CustomerUserError' + /** Error code to uniquely identify the error. */ + code?: Maybe<CustomerErrorCode> + /** Path to the input field which caused the error. */ + field?: Maybe<Array<Scalars['String']>> + /** The error message. */ + message: Scalars['String'] +} + +/** Digital wallet, such as Apple Pay, which can be used for accelerated checkouts. */ +export enum DigitalWallet { + /** Apple Pay. */ + ApplePay = 'APPLE_PAY', + /** Android Pay. */ + AndroidPay = 'ANDROID_PAY', + /** Google Pay. */ + GooglePay = 'GOOGLE_PAY', + /** Shopify Pay. */ + ShopifyPay = 'SHOPIFY_PAY', +} + +/** An amount discounting the line that has been allocated by a discount. */ +export type DiscountAllocation = { + __typename?: 'DiscountAllocation' + /** Amount of discount allocated. */ + allocatedAmount: MoneyV2 + /** The discount this allocated amount originated from. */ + discountApplication: DiscountApplication +} + +/** + * Discount applications capture the intentions of a discount source at + * the time of application. + */ +export type DiscountApplication = { + /** The method by which the discount's value is allocated to its entitled items. */ + allocationMethod: DiscountApplicationAllocationMethod + /** Which lines of targetType that the discount is allocated over. */ + targetSelection: DiscountApplicationTargetSelection + /** The type of line that the discount is applicable towards. */ + targetType: DiscountApplicationTargetType + /** The value of the discount application. */ + value: PricingValue +} + +/** The method by which the discount's value is allocated onto its entitled lines. */ +export enum DiscountApplicationAllocationMethod { + /** The value is spread across all entitled lines. */ + Across = 'ACROSS', + /** The value is applied onto every entitled line. */ + Each = 'EACH', + /** The value is specifically applied onto a particular line. */ + One = 'ONE', +} + +/** An auto-generated type for paginating through multiple DiscountApplications. */ +export type DiscountApplicationConnection = { + __typename?: 'DiscountApplicationConnection' + /** A list of edges. */ + edges: Array<DiscountApplicationEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one DiscountApplication and a cursor during pagination. */ +export type DiscountApplicationEdge = { + __typename?: 'DiscountApplicationEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of DiscountApplicationEdge. */ + node: DiscountApplication +} + +/** + * Which lines on the order that the discount is allocated over, of the type + * defined by the Discount Application's target_type. + */ +export enum DiscountApplicationTargetSelection { + /** The discount is allocated onto all the lines. */ + All = 'ALL', + /** The discount is allocated onto only the lines it is entitled for. */ + Entitled = 'ENTITLED', + /** The discount is allocated onto explicitly chosen lines. */ + Explicit = 'EXPLICIT', +} + +/** The type of line (i.e. line item or shipping line) on an order that the discount is applicable towards. */ +export enum DiscountApplicationTargetType { + /** The discount applies onto line items. */ + LineItem = 'LINE_ITEM', + /** The discount applies onto shipping lines. */ + ShippingLine = 'SHIPPING_LINE', +} + +/** + * Discount code applications capture the intentions of a discount code at + * the time that it is applied. + */ +export type DiscountCodeApplication = DiscountApplication & { + __typename?: 'DiscountCodeApplication' + /** The method by which the discount's value is allocated to its entitled items. */ + allocationMethod: DiscountApplicationAllocationMethod + /** Specifies whether the discount code was applied successfully. */ + applicable: Scalars['Boolean'] + /** The string identifying the discount code that was used at the time of application. */ + code: Scalars['String'] + /** Which lines of targetType that the discount is allocated over. */ + targetSelection: DiscountApplicationTargetSelection + /** The type of line that the discount is applicable towards. */ + targetType: DiscountApplicationTargetType + /** The value of the discount application. */ + value: PricingValue +} + +/** Represents an error in the input of a mutation. */ +export type DisplayableError = { + /** Path to the input field which caused the error. */ + field?: Maybe<Array<Scalars['String']>> + /** The error message. */ + message: Scalars['String'] +} + +/** Represents a web address. */ +export type Domain = { + __typename?: 'Domain' + /** The host name of the domain (eg: `example.com`). */ + host: Scalars['String'] + /** Whether SSL is enabled or not. */ + sslEnabled: Scalars['Boolean'] + /** The URL of the domain (eg: `https://example.com`). */ + url: Scalars['URL'] +} + +/** Represents a video hosted outside of Shopify. */ +export type ExternalVideo = Node & + Media & { + __typename?: 'ExternalVideo' + /** A word or phrase to share the nature or contents of a media. */ + alt?: Maybe<Scalars['String']> + /** The URL. */ + embeddedUrl: Scalars['URL'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The media content type. */ + mediaContentType: MediaContentType + /** The preview image for the media. */ + previewImage?: Maybe<Image> + } + +/** Represents a single fulfillment in an order. */ +export type Fulfillment = { + __typename?: 'Fulfillment' + /** List of the fulfillment's line items. */ + fulfillmentLineItems: FulfillmentLineItemConnection + /** The name of the tracking company. */ + trackingCompany?: Maybe<Scalars['String']> + /** + * Tracking information associated with the fulfillment, + * such as the tracking number and tracking URL. + */ + trackingInfo: Array<FulfillmentTrackingInfo> +} + +/** Represents a single fulfillment in an order. */ +export type FulfillmentFulfillmentLineItemsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** Represents a single fulfillment in an order. */ +export type FulfillmentTrackingInfoArgs = { + first?: Maybe<Scalars['Int']> +} + +/** Represents a single line item in a fulfillment. There is at most one fulfillment line item for each order line item. */ +export type FulfillmentLineItem = { + __typename?: 'FulfillmentLineItem' + /** The associated order's line item. */ + lineItem: OrderLineItem + /** The amount fulfilled in this fulfillment. */ + quantity: Scalars['Int'] +} + +/** An auto-generated type for paginating through multiple FulfillmentLineItems. */ +export type FulfillmentLineItemConnection = { + __typename?: 'FulfillmentLineItemConnection' + /** A list of edges. */ + edges: Array<FulfillmentLineItemEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one FulfillmentLineItem and a cursor during pagination. */ +export type FulfillmentLineItemEdge = { + __typename?: 'FulfillmentLineItemEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of FulfillmentLineItemEdge. */ + node: FulfillmentLineItem +} + +/** Tracking information associated with the fulfillment. */ +export type FulfillmentTrackingInfo = { + __typename?: 'FulfillmentTrackingInfo' + /** The tracking number of the fulfillment. */ + number?: Maybe<Scalars['String']> + /** The URL to track the fulfillment. */ + url?: Maybe<Scalars['URL']> +} + +/** Represents information about the metafields associated to the specified resource. */ +export type HasMetafields = { + /** The metafield associated with the resource. */ + metafield?: Maybe<Metafield> + /** A paginated list of metafields associated with the resource. */ + metafields: MetafieldConnection +} + +/** Represents information about the metafields associated to the specified resource. */ +export type HasMetafieldsMetafieldArgs = { + namespace: Scalars['String'] + key: Scalars['String'] +} + +/** Represents information about the metafields associated to the specified resource. */ +export type HasMetafieldsMetafieldsArgs = { + namespace?: Maybe<Scalars['String']> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** Represents an image resource. */ +export type Image = { + __typename?: 'Image' + /** A word or phrase to share the nature or contents of an image. */ + altText?: Maybe<Scalars['String']> + /** The original height of the image in pixels. Returns `null` if the image is not hosted by Shopify. */ + height?: Maybe<Scalars['Int']> + /** A unique identifier for the image. */ + id?: Maybe<Scalars['ID']> + /** + * The location of the original image as a URL. + * + * If there are any existing transformations in the original source URL, they will remain and not be stripped. + */ + originalSrc: Scalars['URL'] + /** + * The location of the image as a URL. + * @deprecated Previously an image had a single `src` field. This could either return the original image + * location or a URL that contained transformations such as sizing or scale. + * + * These transformations were specified by arguments on the parent field. + * + * Now an image has two distinct URL fields: `originalSrc` and `transformedSrc`. + * + * * `originalSrc` - the original unmodified image URL + * * `transformedSrc` - the image URL with the specified transformations included + * + * To migrate to the new fields, image transformations should be moved from the parent field to `transformedSrc`. + * + * Before: + * ```graphql + * { + * shop { + * productImages(maxWidth: 200, scale: 2) { + * edges { + * node { + * src + * } + * } + * } + * } + * } + * ``` + * + * After: + * ```graphql + * { + * shop { + * productImages { + * edges { + * node { + * transformedSrc(maxWidth: 200, scale: 2) + * } + * } + * } + * } + * } + * ``` + * + */ + src: Scalars['URL'] + /** + * The location of the transformed image as a URL. + * + * All transformation arguments are considered "best-effort". If they can be applied to an image, they will be. + * Otherwise any transformations which an image type does not support will be ignored. + */ + transformedSrc: Scalars['URL'] + /** The original width of the image in pixels. Returns `null` if the image is not hosted by Shopify. */ + width?: Maybe<Scalars['Int']> +} + +/** Represents an image resource. */ +export type ImageTransformedSrcArgs = { + maxWidth?: Maybe<Scalars['Int']> + maxHeight?: Maybe<Scalars['Int']> + crop?: Maybe<CropRegion> + scale?: Maybe<Scalars['Int']> + preferredContentType?: Maybe<ImageContentType> +} + +/** An auto-generated type for paginating through multiple Images. */ +export type ImageConnection = { + __typename?: 'ImageConnection' + /** A list of edges. */ + edges: Array<ImageEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** List of supported image content types. */ +export enum ImageContentType { + /** A PNG image. */ + Png = 'PNG', + /** A JPG image. */ + Jpg = 'JPG', + /** A WEBP image. */ + Webp = 'WEBP', +} + +/** An auto-generated type which holds one Image and a cursor during pagination. */ +export type ImageEdge = { + __typename?: 'ImageEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ImageEdge. */ + node: Image +} + +/** Represents a mailing address for customers and shipping. */ +export type MailingAddress = Node & { + __typename?: 'MailingAddress' + /** The first line of the address. Typically the street address or PO Box number. */ + address1?: Maybe<Scalars['String']> + /** The second line of the address. Typically the number of the apartment, suite, or unit. */ + address2?: Maybe<Scalars['String']> + /** The name of the city, district, village, or town. */ + city?: Maybe<Scalars['String']> + /** The name of the customer's company or organization. */ + company?: Maybe<Scalars['String']> + /** The name of the country. */ + country?: Maybe<Scalars['String']> + /** + * The two-letter code for the country of the address. + * + * For example, US. + * @deprecated Use `countryCodeV2` instead + */ + countryCode?: Maybe<Scalars['String']> + /** + * The two-letter code for the country of the address. + * + * For example, US. + */ + countryCodeV2?: Maybe<CountryCode> + /** The first name of the customer. */ + firstName?: Maybe<Scalars['String']> + /** A formatted version of the address, customized by the provided arguments. */ + formatted: Array<Scalars['String']> + /** A comma-separated list of the values for city, province, and country. */ + formattedArea?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The last name of the customer. */ + lastName?: Maybe<Scalars['String']> + /** The latitude coordinate of the customer address. */ + latitude?: Maybe<Scalars['Float']> + /** The longitude coordinate of the customer address. */ + longitude?: Maybe<Scalars['Float']> + /** The full name of the customer, based on firstName and lastName. */ + name?: Maybe<Scalars['String']> + /** + * A unique phone number for the customer. + * + * Formatted using E.164 standard. For example, _+16135551111_. + */ + phone?: Maybe<Scalars['String']> + /** The region of the address, such as the province, state, or district. */ + province?: Maybe<Scalars['String']> + /** + * The two-letter code for the region. + * + * For example, ON. + */ + provinceCode?: Maybe<Scalars['String']> + /** The zip or postal code of the address. */ + zip?: Maybe<Scalars['String']> +} + +/** Represents a mailing address for customers and shipping. */ +export type MailingAddressFormattedArgs = { + withName?: Maybe<Scalars['Boolean']> + withCompany?: Maybe<Scalars['Boolean']> +} + +/** An auto-generated type for paginating through multiple MailingAddresses. */ +export type MailingAddressConnection = { + __typename?: 'MailingAddressConnection' + /** A list of edges. */ + edges: Array<MailingAddressEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one MailingAddress and a cursor during pagination. */ +export type MailingAddressEdge = { + __typename?: 'MailingAddressEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of MailingAddressEdge. */ + node: MailingAddress +} + +/** Specifies the fields accepted to create or update a mailing address. */ +export type MailingAddressInput = { + /** The first line of the address. Typically the street address or PO Box number. */ + address1?: Maybe<Scalars['String']> + /** The second line of the address. Typically the number of the apartment, suite, or unit. */ + address2?: Maybe<Scalars['String']> + /** The name of the city, district, village, or town. */ + city?: Maybe<Scalars['String']> + /** The name of the customer's company or organization. */ + company?: Maybe<Scalars['String']> + /** The name of the country. */ + country?: Maybe<Scalars['String']> + /** The first name of the customer. */ + firstName?: Maybe<Scalars['String']> + /** The last name of the customer. */ + lastName?: Maybe<Scalars['String']> + /** + * A unique phone number for the customer. + * + * Formatted using E.164 standard. For example, _+16135551111_. + */ + phone?: Maybe<Scalars['String']> + /** The region of the address, such as the province, state, or district. */ + province?: Maybe<Scalars['String']> + /** The zip or postal code of the address. */ + zip?: Maybe<Scalars['String']> +} + +/** Manual discount applications capture the intentions of a discount that was manually created. */ +export type ManualDiscountApplication = DiscountApplication & { + __typename?: 'ManualDiscountApplication' + /** The method by which the discount's value is allocated to its entitled items. */ + allocationMethod: DiscountApplicationAllocationMethod + /** The description of the application. */ + description?: Maybe<Scalars['String']> + /** Which lines of targetType that the discount is allocated over. */ + targetSelection: DiscountApplicationTargetSelection + /** The type of line that the discount is applicable towards. */ + targetType: DiscountApplicationTargetType + /** The title of the application. */ + title: Scalars['String'] + /** The value of the discount application. */ + value: PricingValue +} + +/** Represents a media interface. */ +export type Media = { + /** A word or phrase to share the nature or contents of a media. */ + alt?: Maybe<Scalars['String']> + /** The media content type. */ + mediaContentType: MediaContentType + /** The preview image for the media. */ + previewImage?: Maybe<Image> +} + +/** An auto-generated type for paginating through multiple Media. */ +export type MediaConnection = { + __typename?: 'MediaConnection' + /** A list of edges. */ + edges: Array<MediaEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** The possible content types for a media object. */ +export enum MediaContentType { + /** An externally hosted video. */ + ExternalVideo = 'EXTERNAL_VIDEO', + /** A Shopify hosted image. */ + Image = 'IMAGE', + /** A 3d model. */ + Model_3D = 'MODEL_3D', + /** A Shopify hosted video. */ + Video = 'VIDEO', +} + +/** An auto-generated type which holds one Media and a cursor during pagination. */ +export type MediaEdge = { + __typename?: 'MediaEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of MediaEdge. */ + node: Media +} + +/** Represents a Shopify hosted image. */ +export type MediaImage = Node & + Media & { + __typename?: 'MediaImage' + /** A word or phrase to share the nature or contents of a media. */ + alt?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The image for the media. */ + image?: Maybe<Image> + /** The media content type. */ + mediaContentType: MediaContentType + /** The preview image for the media. */ + previewImage?: Maybe<Image> + } + +/** + * Metafields represent custom metadata attached to a resource. Metafields can be sorted into namespaces and are + * comprised of keys, values, and value types. + */ +export type Metafield = Node & { + __typename?: 'Metafield' + /** The date and time when the storefront metafield was created. */ + createdAt: Scalars['DateTime'] + /** The description of a metafield. */ + description?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The key name for a metafield. */ + key: Scalars['String'] + /** The namespace for a metafield. */ + namespace: Scalars['String'] + /** The parent object that the metafield belongs to. */ + parentResource: MetafieldParentResource + /** The date and time when the storefront metafield was updated. */ + updatedAt: Scalars['DateTime'] + /** The value of a metafield. */ + value: Scalars['String'] + /** Represents the metafield value type. */ + valueType: MetafieldValueType +} + +/** An auto-generated type for paginating through multiple Metafields. */ +export type MetafieldConnection = { + __typename?: 'MetafieldConnection' + /** A list of edges. */ + edges: Array<MetafieldEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Metafield and a cursor during pagination. */ +export type MetafieldEdge = { + __typename?: 'MetafieldEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of MetafieldEdge. */ + node: Metafield +} + +/** A resource that the metafield belongs to. */ +export type MetafieldParentResource = Product | ProductVariant + +/** Metafield value types. */ +export enum MetafieldValueType { + /** A string metafield. */ + String = 'STRING', + /** An integer metafield. */ + Integer = 'INTEGER', + /** A json string metafield. */ + JsonString = 'JSON_STRING', +} + +/** Represents a Shopify hosted 3D model. */ +export type Model3d = Node & + Media & { + __typename?: 'Model3d' + /** A word or phrase to share the nature or contents of a media. */ + alt?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The media content type. */ + mediaContentType: MediaContentType + /** The preview image for the media. */ + previewImage?: Maybe<Image> + /** The sources for a 3d model. */ + sources: Array<Model3dSource> + } + +/** Represents a source for a Shopify hosted 3d model. */ +export type Model3dSource = { + __typename?: 'Model3dSource' + /** The filesize of the 3d model. */ + filesize: Scalars['Int'] + /** The format of the 3d model. */ + format: Scalars['String'] + /** The MIME type of the 3d model. */ + mimeType: Scalars['String'] + /** The URL of the 3d model. */ + url: Scalars['String'] +} + +/** Specifies the fields for a monetary value with currency. */ +export type MoneyInput = { + /** Decimal money amount. */ + amount: Scalars['Decimal'] + /** Currency of the money. */ + currencyCode: CurrencyCode +} + +/** + * A monetary value with currency. + * + * To format currencies, combine this type's amount and currencyCode fields with your client's locale. + * + * For example, in JavaScript you could use Intl.NumberFormat: + * + * ```js + * new Intl.NumberFormat(locale, { + * style: 'currency', + * currency: currencyCode + * }).format(amount); + * ``` + * + * Other formatting libraries include: + * + * * iOS - [NumberFormatter](https://developer.apple.com/documentation/foundation/numberformatter) + * * Android - [NumberFormat](https://developer.android.com/reference/java/text/NumberFormat.html) + * * PHP - [NumberFormatter](http://php.net/manual/en/class.numberformatter.php) + * + * For a more general solution, the [Unicode CLDR number formatting database] is available with many implementations + * (such as [TwitterCldr](https://github.com/twitter/twitter-cldr-rb)). + */ +export type MoneyV2 = { + __typename?: 'MoneyV2' + /** Decimal money amount. */ + amount: Scalars['Decimal'] + /** Currency of the money. */ + currencyCode: CurrencyCode +} + +/** An auto-generated type for paginating through multiple MoneyV2s. */ +export type MoneyV2Connection = { + __typename?: 'MoneyV2Connection' + /** A list of edges. */ + edges: Array<MoneyV2Edge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one MoneyV2 and a cursor during pagination. */ +export type MoneyV2Edge = { + __typename?: 'MoneyV2Edge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of MoneyV2Edge. */ + node: MoneyV2 +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type Mutation = { + __typename?: 'Mutation' + /** + * Updates the attributes of a checkout. + * @deprecated Use `checkoutAttributesUpdateV2` instead + */ + checkoutAttributesUpdate?: Maybe<CheckoutAttributesUpdatePayload> + /** Updates the attributes of a checkout. */ + checkoutAttributesUpdateV2?: Maybe<CheckoutAttributesUpdateV2Payload> + /** Completes a checkout without providing payment information. You can use this mutation for free items or items whose purchase price is covered by a gift card. */ + checkoutCompleteFree?: Maybe<CheckoutCompleteFreePayload> + /** + * Completes a checkout using a credit card token from Shopify's Vault. + * @deprecated Use `checkoutCompleteWithCreditCardV2` instead + */ + checkoutCompleteWithCreditCard?: Maybe<CheckoutCompleteWithCreditCardPayload> + /** Completes a checkout using a credit card token from Shopify's card vault. Before you can complete checkouts using CheckoutCompleteWithCreditCardV2, you need to [_request payment processing_](https://help.shopify.com/api/guides/sales-channel-sdk/getting-started#request-payment-processing). */ + checkoutCompleteWithCreditCardV2?: Maybe<CheckoutCompleteWithCreditCardV2Payload> + /** + * Completes a checkout with a tokenized payment. + * @deprecated Use `checkoutCompleteWithTokenizedPaymentV2` instead + */ + checkoutCompleteWithTokenizedPayment?: Maybe<CheckoutCompleteWithTokenizedPaymentPayload> + /** + * Completes a checkout with a tokenized payment. + * @deprecated Use `checkoutCompleteWithTokenizedPaymentV3` instead + */ + checkoutCompleteWithTokenizedPaymentV2?: Maybe<CheckoutCompleteWithTokenizedPaymentV2Payload> + /** Completes a checkout with a tokenized payment. */ + checkoutCompleteWithTokenizedPaymentV3?: Maybe<CheckoutCompleteWithTokenizedPaymentV3Payload> + /** Creates a new checkout. */ + checkoutCreate?: Maybe<CheckoutCreatePayload> + /** + * Associates a customer to the checkout. + * @deprecated Use `checkoutCustomerAssociateV2` instead + */ + checkoutCustomerAssociate?: Maybe<CheckoutCustomerAssociatePayload> + /** Associates a customer to the checkout. */ + checkoutCustomerAssociateV2?: Maybe<CheckoutCustomerAssociateV2Payload> + /** + * Disassociates the current checkout customer from the checkout. + * @deprecated Use `checkoutCustomerDisassociateV2` instead + */ + checkoutCustomerDisassociate?: Maybe<CheckoutCustomerDisassociatePayload> + /** Disassociates the current checkout customer from the checkout. */ + checkoutCustomerDisassociateV2?: Maybe<CheckoutCustomerDisassociateV2Payload> + /** + * Applies a discount to an existing checkout using a discount code. + * @deprecated Use `checkoutDiscountCodeApplyV2` instead + */ + checkoutDiscountCodeApply?: Maybe<CheckoutDiscountCodeApplyPayload> + /** Applies a discount to an existing checkout using a discount code. */ + checkoutDiscountCodeApplyV2?: Maybe<CheckoutDiscountCodeApplyV2Payload> + /** Removes the applied discount from an existing checkout. */ + checkoutDiscountCodeRemove?: Maybe<CheckoutDiscountCodeRemovePayload> + /** + * Updates the email on an existing checkout. + * @deprecated Use `checkoutEmailUpdateV2` instead + */ + checkoutEmailUpdate?: Maybe<CheckoutEmailUpdatePayload> + /** Updates the email on an existing checkout. */ + checkoutEmailUpdateV2?: Maybe<CheckoutEmailUpdateV2Payload> + /** + * Applies a gift card to an existing checkout using a gift card code. This will replace all currently applied gift cards. + * @deprecated Use `checkoutGiftCardsAppend` instead + */ + checkoutGiftCardApply?: Maybe<CheckoutGiftCardApplyPayload> + /** + * Removes an applied gift card from the checkout. + * @deprecated Use `checkoutGiftCardRemoveV2` instead + */ + checkoutGiftCardRemove?: Maybe<CheckoutGiftCardRemovePayload> + /** Removes an applied gift card from the checkout. */ + checkoutGiftCardRemoveV2?: Maybe<CheckoutGiftCardRemoveV2Payload> + /** Appends gift cards to an existing checkout. */ + checkoutGiftCardsAppend?: Maybe<CheckoutGiftCardsAppendPayload> + /** Adds a list of line items to a checkout. */ + checkoutLineItemsAdd?: Maybe<CheckoutLineItemsAddPayload> + /** Removes line items from an existing checkout. */ + checkoutLineItemsRemove?: Maybe<CheckoutLineItemsRemovePayload> + /** Sets a list of line items to a checkout. */ + checkoutLineItemsReplace?: Maybe<CheckoutLineItemsReplacePayload> + /** Updates line items on a checkout. */ + checkoutLineItemsUpdate?: Maybe<CheckoutLineItemsUpdatePayload> + /** + * Updates the shipping address of an existing checkout. + * @deprecated Use `checkoutShippingAddressUpdateV2` instead + */ + checkoutShippingAddressUpdate?: Maybe<CheckoutShippingAddressUpdatePayload> + /** Updates the shipping address of an existing checkout. */ + checkoutShippingAddressUpdateV2?: Maybe<CheckoutShippingAddressUpdateV2Payload> + /** Updates the shipping lines on an existing checkout. */ + checkoutShippingLineUpdate?: Maybe<CheckoutShippingLineUpdatePayload> + /** + * Creates a customer access token. + * The customer access token is required to modify the customer object in any way. + */ + customerAccessTokenCreate?: Maybe<CustomerAccessTokenCreatePayload> + /** + * Creates a customer access token using a multipass token instead of email and password. + * A customer record is created if customer does not exist. If a customer record already + * exists but the record is disabled, then it's enabled. + */ + customerAccessTokenCreateWithMultipass?: Maybe<CustomerAccessTokenCreateWithMultipassPayload> + /** Permanently destroys a customer access token. */ + customerAccessTokenDelete?: Maybe<CustomerAccessTokenDeletePayload> + /** + * Renews a customer access token. + * + * Access token renewal must happen *before* a token expires. + * If a token has already expired, a new one should be created instead via `customerAccessTokenCreate`. + */ + customerAccessTokenRenew?: Maybe<CustomerAccessTokenRenewPayload> + /** Activates a customer. */ + customerActivate?: Maybe<CustomerActivatePayload> + /** Activates a customer with the activation url received from `customerCreate`. */ + customerActivateByUrl?: Maybe<CustomerActivateByUrlPayload> + /** Creates a new address for a customer. */ + customerAddressCreate?: Maybe<CustomerAddressCreatePayload> + /** Permanently deletes the address of an existing customer. */ + customerAddressDelete?: Maybe<CustomerAddressDeletePayload> + /** Updates the address of an existing customer. */ + customerAddressUpdate?: Maybe<CustomerAddressUpdatePayload> + /** Creates a new customer. */ + customerCreate?: Maybe<CustomerCreatePayload> + /** Updates the default address of an existing customer. */ + customerDefaultAddressUpdate?: Maybe<CustomerDefaultAddressUpdatePayload> + /** Sends a reset password email to the customer, as the first step in the reset password process. */ + customerRecover?: Maybe<CustomerRecoverPayload> + /** Resets a customer’s password with a token received from `CustomerRecover`. */ + customerReset?: Maybe<CustomerResetPayload> + /** Resets a customer’s password with the reset password url received from `CustomerRecover`. */ + customerResetByUrl?: Maybe<CustomerResetByUrlPayload> + /** Updates an existing customer. */ + customerUpdate?: Maybe<CustomerUpdatePayload> +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutAttributesUpdateArgs = { + checkoutId: Scalars['ID'] + input: CheckoutAttributesUpdateInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutAttributesUpdateV2Args = { + checkoutId: Scalars['ID'] + input: CheckoutAttributesUpdateV2Input +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteFreeArgs = { + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteWithCreditCardArgs = { + checkoutId: Scalars['ID'] + payment: CreditCardPaymentInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteWithCreditCardV2Args = { + checkoutId: Scalars['ID'] + payment: CreditCardPaymentInputV2 +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteWithTokenizedPaymentArgs = { + checkoutId: Scalars['ID'] + payment: TokenizedPaymentInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteWithTokenizedPaymentV2Args = { + checkoutId: Scalars['ID'] + payment: TokenizedPaymentInputV2 +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteWithTokenizedPaymentV3Args = { + checkoutId: Scalars['ID'] + payment: TokenizedPaymentInputV3 +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCreateArgs = { + input: CheckoutCreateInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCustomerAssociateArgs = { + checkoutId: Scalars['ID'] + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCustomerAssociateV2Args = { + checkoutId: Scalars['ID'] + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCustomerDisassociateArgs = { + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCustomerDisassociateV2Args = { + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutDiscountCodeApplyArgs = { + discountCode: Scalars['String'] + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutDiscountCodeApplyV2Args = { + discountCode: Scalars['String'] + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutDiscountCodeRemoveArgs = { + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutEmailUpdateArgs = { + checkoutId: Scalars['ID'] + email: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutEmailUpdateV2Args = { + checkoutId: Scalars['ID'] + email: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutGiftCardApplyArgs = { + giftCardCode: Scalars['String'] + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutGiftCardRemoveArgs = { + appliedGiftCardId: Scalars['ID'] + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutGiftCardRemoveV2Args = { + appliedGiftCardId: Scalars['ID'] + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutGiftCardsAppendArgs = { + giftCardCodes: Array<Scalars['String']> + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutLineItemsAddArgs = { + lineItems: Array<CheckoutLineItemInput> + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutLineItemsRemoveArgs = { + checkoutId: Scalars['ID'] + lineItemIds: Array<Scalars['ID']> +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutLineItemsReplaceArgs = { + lineItems: Array<CheckoutLineItemInput> + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutLineItemsUpdateArgs = { + checkoutId: Scalars['ID'] + lineItems: Array<CheckoutLineItemUpdateInput> +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutShippingAddressUpdateArgs = { + shippingAddress: MailingAddressInput + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutShippingAddressUpdateV2Args = { + shippingAddress: MailingAddressInput + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutShippingLineUpdateArgs = { + checkoutId: Scalars['ID'] + shippingRateHandle: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAccessTokenCreateArgs = { + input: CustomerAccessTokenCreateInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAccessTokenCreateWithMultipassArgs = { + multipassToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAccessTokenDeleteArgs = { + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAccessTokenRenewArgs = { + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerActivateArgs = { + id: Scalars['ID'] + input: CustomerActivateInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerActivateByUrlArgs = { + activationUrl: Scalars['URL'] + password: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAddressCreateArgs = { + customerAccessToken: Scalars['String'] + address: MailingAddressInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAddressDeleteArgs = { + id: Scalars['ID'] + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAddressUpdateArgs = { + customerAccessToken: Scalars['String'] + id: Scalars['ID'] + address: MailingAddressInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerCreateArgs = { + input: CustomerCreateInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerDefaultAddressUpdateArgs = { + customerAccessToken: Scalars['String'] + addressId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerRecoverArgs = { + email: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerResetArgs = { + id: Scalars['ID'] + input: CustomerResetInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerResetByUrlArgs = { + resetUrl: Scalars['URL'] + password: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerUpdateArgs = { + customerAccessToken: Scalars['String'] + customer: CustomerUpdateInput +} + +/** An object with an ID to support global identification. */ +export type Node = { + /** Globally unique identifier. */ + id: Scalars['ID'] +} + +/** An order is a customer’s completed request to purchase one or more products from a shop. An order is created when a customer completes the checkout process, during which time they provides an email address, billing address and payment information. */ +export type Order = Node & { + __typename?: 'Order' + /** The reason for the order's cancellation. Returns `null` if the order wasn't canceled. */ + cancelReason?: Maybe<OrderCancelReason> + /** The date and time when the order was canceled. Returns null if the order wasn't canceled. */ + canceledAt?: Maybe<Scalars['DateTime']> + /** The code of the currency used for the payment. */ + currencyCode: CurrencyCode + /** The subtotal of line items and their discounts, excluding line items that have been removed. Does not contain order-level discounts, duties, shipping costs, or shipping discounts. Taxes are not included unless the order is a taxes-included order. */ + currentSubtotalPrice: MoneyV2 + /** The total amount of the order, including duties, taxes and discounts, minus amounts for line items that have been removed. */ + currentTotalPrice: MoneyV2 + /** The total of all taxes applied to the order, excluding taxes for returned line items. */ + currentTotalTax: MoneyV2 + /** The locale code in which this specific order happened. */ + customerLocale?: Maybe<Scalars['String']> + /** The unique URL that the customer can use to access the order. */ + customerUrl?: Maybe<Scalars['URL']> + /** Discounts that have been applied on the order. */ + discountApplications: DiscountApplicationConnection + /** Whether the order has had any edits applied or not. */ + edited: Scalars['Boolean'] + /** The customer's email address. */ + email?: Maybe<Scalars['String']> + /** The financial status of the order. */ + financialStatus?: Maybe<OrderFinancialStatus> + /** The fulfillment status for the order. */ + fulfillmentStatus: OrderFulfillmentStatus + /** Globally unique identifier. */ + id: Scalars['ID'] + /** List of the order’s line items. */ + lineItems: OrderLineItemConnection + /** + * Unique identifier for the order that appears on the order. + * For example, _#1000_ or _Store1001. + */ + name: Scalars['String'] + /** A unique numeric identifier for the order for use by shop owner and customer. */ + orderNumber: Scalars['Int'] + /** The total price of the order before any applied edits. */ + originalTotalPrice: MoneyV2 + /** The customer's phone number for receiving SMS notifications. */ + phone?: Maybe<Scalars['String']> + /** + * The date and time when the order was imported. + * This value can be set to dates in the past when importing from other systems. + * If no value is provided, it will be auto-generated based on current date and time. + */ + processedAt: Scalars['DateTime'] + /** The address to where the order will be shipped. */ + shippingAddress?: Maybe<MailingAddress> + /** The discounts that have been allocated onto the shipping line by discount applications. */ + shippingDiscountAllocations: Array<DiscountAllocation> + /** The unique URL for the order's status page. */ + statusUrl: Scalars['URL'] + /** + * Price of the order before shipping and taxes. + * @deprecated Use `subtotalPriceV2` instead + */ + subtotalPrice?: Maybe<Scalars['Money']> + /** Price of the order before duties, shipping and taxes. */ + subtotalPriceV2?: Maybe<MoneyV2> + /** List of the order’s successful fulfillments. */ + successfulFulfillments?: Maybe<Array<Fulfillment>> + /** + * The sum of all the prices of all the items in the order, taxes and discounts included (must be positive). + * @deprecated Use `totalPriceV2` instead + */ + totalPrice: Scalars['Money'] + /** The sum of all the prices of all the items in the order, duties, taxes and discounts included (must be positive). */ + totalPriceV2: MoneyV2 + /** + * The total amount that has been refunded. + * @deprecated Use `totalRefundedV2` instead + */ + totalRefunded: Scalars['Money'] + /** The total amount that has been refunded. */ + totalRefundedV2: MoneyV2 + /** + * The total cost of shipping. + * @deprecated Use `totalShippingPriceV2` instead + */ + totalShippingPrice: Scalars['Money'] + /** The total cost of shipping. */ + totalShippingPriceV2: MoneyV2 + /** + * The total cost of taxes. + * @deprecated Use `totalTaxV2` instead + */ + totalTax?: Maybe<Scalars['Money']> + /** The total cost of taxes. */ + totalTaxV2?: Maybe<MoneyV2> +} + +/** An order is a customer’s completed request to purchase one or more products from a shop. An order is created when a customer completes the checkout process, during which time they provides an email address, billing address and payment information. */ +export type OrderDiscountApplicationsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** An order is a customer’s completed request to purchase one or more products from a shop. An order is created when a customer completes the checkout process, during which time they provides an email address, billing address and payment information. */ +export type OrderLineItemsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** An order is a customer’s completed request to purchase one or more products from a shop. An order is created when a customer completes the checkout process, during which time they provides an email address, billing address and payment information. */ +export type OrderSuccessfulFulfillmentsArgs = { + first?: Maybe<Scalars['Int']> +} + +/** Represents the reason for the order's cancellation. */ +export enum OrderCancelReason { + /** The customer wanted to cancel the order. */ + Customer = 'CUSTOMER', + /** The order was fraudulent. */ + Fraud = 'FRAUD', + /** There was insufficient inventory. */ + Inventory = 'INVENTORY', + /** Payment was declined. */ + Declined = 'DECLINED', + /** The order was canceled for an unlisted reason. */ + Other = 'OTHER', +} + +/** An auto-generated type for paginating through multiple Orders. */ +export type OrderConnection = { + __typename?: 'OrderConnection' + /** A list of edges. */ + edges: Array<OrderEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Order and a cursor during pagination. */ +export type OrderEdge = { + __typename?: 'OrderEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of OrderEdge. */ + node: Order +} + +/** Represents the order's current financial status. */ +export enum OrderFinancialStatus { + /** Displayed as **Pending**. */ + Pending = 'PENDING', + /** Displayed as **Authorized**. */ + Authorized = 'AUTHORIZED', + /** Displayed as **Partially paid**. */ + PartiallyPaid = 'PARTIALLY_PAID', + /** Displayed as **Partially refunded**. */ + PartiallyRefunded = 'PARTIALLY_REFUNDED', + /** Displayed as **Voided**. */ + Voided = 'VOIDED', + /** Displayed as **Paid**. */ + Paid = 'PAID', + /** Displayed as **Refunded**. */ + Refunded = 'REFUNDED', +} + +/** Represents the order's current fulfillment status. */ +export enum OrderFulfillmentStatus { + /** Displayed as **Unfulfilled**. */ + Unfulfilled = 'UNFULFILLED', + /** Displayed as **Partially fulfilled**. */ + PartiallyFulfilled = 'PARTIALLY_FULFILLED', + /** Displayed as **Fulfilled**. */ + Fulfilled = 'FULFILLED', + /** Displayed as **Restocked**. */ + Restocked = 'RESTOCKED', + /** Displayed as **Pending fulfillment**. */ + PendingFulfillment = 'PENDING_FULFILLMENT', + /** Displayed as **Open**. */ + Open = 'OPEN', + /** Displayed as **In progress**. */ + InProgress = 'IN_PROGRESS', + /** Displayed as **Scheduled**. */ + Scheduled = 'SCHEDULED', +} + +/** Represents a single line in an order. There is one line item for each distinct product variant. */ +export type OrderLineItem = { + __typename?: 'OrderLineItem' + /** The number of entries associated to the line item minus the items that have been removed. */ + currentQuantity: Scalars['Int'] + /** List of custom attributes associated to the line item. */ + customAttributes: Array<Attribute> + /** The discounts that have been allocated onto the order line item by discount applications. */ + discountAllocations: Array<DiscountAllocation> + /** The total price of the line item, including discounts, and displayed in the presentment currency. */ + discountedTotalPrice: MoneyV2 + /** The total price of the line item, not including any discounts. The total price is calculated using the original unit price multiplied by the quantity, and it is displayed in the presentment currency. */ + originalTotalPrice: MoneyV2 + /** The number of products variants associated to the line item. */ + quantity: Scalars['Int'] + /** The title of the product combined with title of the variant. */ + title: Scalars['String'] + /** The product variant object associated to the line item. */ + variant?: Maybe<ProductVariant> +} + +/** An auto-generated type for paginating through multiple OrderLineItems. */ +export type OrderLineItemConnection = { + __typename?: 'OrderLineItemConnection' + /** A list of edges. */ + edges: Array<OrderLineItemEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one OrderLineItem and a cursor during pagination. */ +export type OrderLineItemEdge = { + __typename?: 'OrderLineItemEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of OrderLineItemEdge. */ + node: OrderLineItem +} + +/** The set of valid sort keys for the Order query. */ +export enum OrderSortKeys { + /** Sort by the `processed_at` value. */ + ProcessedAt = 'PROCESSED_AT', + /** Sort by the `total_price` value. */ + TotalPrice = 'TOTAL_PRICE', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** Shopify merchants can create pages to hold static HTML content. Each Page object represents a custom page on the online store. */ +export type Page = Node & { + __typename?: 'Page' + /** The description of the page, complete with HTML formatting. */ + body: Scalars['HTML'] + /** Summary of the page body. */ + bodySummary: Scalars['String'] + /** The timestamp of the page creation. */ + createdAt: Scalars['DateTime'] + /** A human-friendly unique string for the page automatically generated from its title. */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The page's SEO information. */ + seo?: Maybe<Seo> + /** The title of the page. */ + title: Scalars['String'] + /** The timestamp of the latest page update. */ + updatedAt: Scalars['DateTime'] + /** The url pointing to the page accessible from the web. */ + url: Scalars['URL'] +} + +/** An auto-generated type for paginating through multiple Pages. */ +export type PageConnection = { + __typename?: 'PageConnection' + /** A list of edges. */ + edges: Array<PageEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Page and a cursor during pagination. */ +export type PageEdge = { + __typename?: 'PageEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of PageEdge. */ + node: Page +} + +/** Information about pagination in a connection. */ +export type PageInfo = { + __typename?: 'PageInfo' + /** Indicates if there are more pages to fetch. */ + hasNextPage: Scalars['Boolean'] + /** Indicates if there are any pages prior to the current page. */ + hasPreviousPage: Scalars['Boolean'] +} + +/** The set of valid sort keys for the Page query. */ +export enum PageSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `updated_at` value. */ + UpdatedAt = 'UPDATED_AT', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** A payment applied to a checkout. */ +export type Payment = Node & { + __typename?: 'Payment' + /** + * The amount of the payment. + * @deprecated Use `amountV2` instead + */ + amount: Scalars['Money'] + /** The amount of the payment. */ + amountV2: MoneyV2 + /** The billing address for the payment. */ + billingAddress?: Maybe<MailingAddress> + /** The checkout to which the payment belongs. */ + checkout: Checkout + /** The credit card used for the payment in the case of direct payments. */ + creditCard?: Maybe<CreditCard> + /** A message describing a processing error during asynchronous processing. */ + errorMessage?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** A client-side generated token to identify a payment and perform idempotent operations. */ + idempotencyKey?: Maybe<Scalars['String']> + /** The URL where the customer needs to be redirected so they can complete the 3D Secure payment flow. */ + nextActionUrl?: Maybe<Scalars['URL']> + /** Whether or not the payment is still processing asynchronously. */ + ready: Scalars['Boolean'] + /** A flag to indicate if the payment is to be done in test mode for gateways that support it. */ + test: Scalars['Boolean'] + /** The actual transaction recorded by Shopify after having processed the payment with the gateway. */ + transaction?: Maybe<Transaction> +} + +/** Settings related to payments. */ +export type PaymentSettings = { + __typename?: 'PaymentSettings' + /** List of the card brands which the shop accepts. */ + acceptedCardBrands: Array<CardBrand> + /** The url pointing to the endpoint to vault credit cards. */ + cardVaultUrl: Scalars['URL'] + /** The country where the shop is located. */ + countryCode: CountryCode + /** The three-letter code for the shop's primary currency. */ + currencyCode: CurrencyCode + /** A list of enabled currencies (ISO 4217 format) that the shop accepts. Merchants can enable currencies from their Shopify Payments settings in the Shopify admin. */ + enabledPresentmentCurrencies: Array<CurrencyCode> + /** The shop’s Shopify Payments account id. */ + shopifyPaymentsAccountId?: Maybe<Scalars['String']> + /** List of the digital wallets which the shop supports. */ + supportedDigitalWallets: Array<DigitalWallet> +} + +/** The valid values for the types of payment token. */ +export enum PaymentTokenType { + /** Apple Pay token type. */ + ApplePay = 'APPLE_PAY', + /** Vault payment token type. */ + Vault = 'VAULT', + /** Shopify Pay token type. */ + ShopifyPay = 'SHOPIFY_PAY', + /** Google Pay token type. */ + GooglePay = 'GOOGLE_PAY', +} + +/** The value of the percentage pricing object. */ +export type PricingPercentageValue = { + __typename?: 'PricingPercentageValue' + /** The percentage value of the object. */ + percentage: Scalars['Float'] +} + +/** The price value (fixed or percentage) for a discount application. */ +export type PricingValue = MoneyV2 | PricingPercentageValue + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type Product = Node & + HasMetafields & { + __typename?: 'Product' + /** Indicates if at least one product variant is available for sale. */ + availableForSale: Scalars['Boolean'] + /** List of collections a product belongs to. */ + collections: CollectionConnection + /** The compare at price of the product across all variants. */ + compareAtPriceRange: ProductPriceRange + /** The date and time when the product was created. */ + createdAt: Scalars['DateTime'] + /** Stripped description of the product, single line with HTML tags removed. */ + description: Scalars['String'] + /** The description of the product, complete with HTML formatting. */ + descriptionHtml: Scalars['HTML'] + /** + * A human-friendly unique string for the Product automatically generated from its title. + * They are used by the Liquid templating language to refer to objects. + */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** List of images associated with the product. */ + images: ImageConnection + /** The media associated with the product. */ + media: MediaConnection + /** The metafield associated with the resource. */ + metafield?: Maybe<Metafield> + /** A paginated list of metafields associated with the resource. */ + metafields: MetafieldConnection + /** + * The online store URL for the product. + * A value of `null` indicates that the product is not published to the Online Store sales channel. + */ + onlineStoreUrl?: Maybe<Scalars['URL']> + /** List of product options. */ + options: Array<ProductOption> + /** List of price ranges in the presentment currencies for this shop. */ + presentmentPriceRanges: ProductPriceRangeConnection + /** The price range. */ + priceRange: ProductPriceRange + /** A categorization that a product can be tagged with, commonly used for filtering and searching. */ + productType: Scalars['String'] + /** The date and time when the product was published to the channel. */ + publishedAt: Scalars['DateTime'] + /** The product's SEO information. */ + seo: Seo + /** + * A comma separated list of tags that have been added to the product. + * Additional access scope required for private apps: unauthenticated_read_product_tags. + */ + tags: Array<Scalars['String']> + /** The product’s title. */ + title: Scalars['String'] + /** The total quantity of inventory in stock for this Product. */ + totalInventory?: Maybe<Scalars['Int']> + /** + * The date and time when the product was last modified. + * A product's `updatedAt` value can change for different reasons. For example, if an order + * is placed for a product that has inventory tracking set up, then the inventory adjustment + * is counted as an update. + */ + updatedAt: Scalars['DateTime'] + /** + * Find a product’s variant based on its selected options. + * This is useful for converting a user’s selection of product options into a single matching variant. + * If there is not a variant for the selected options, `null` will be returned. + */ + variantBySelectedOptions?: Maybe<ProductVariant> + /** List of the product’s variants. */ + variants: ProductVariantConnection + /** The product’s vendor name. */ + vendor: Scalars['String'] + } + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductCollectionsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductDescriptionArgs = { + truncateAt?: Maybe<Scalars['Int']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductImagesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductImageSortKeys> + maxWidth?: Maybe<Scalars['Int']> + maxHeight?: Maybe<Scalars['Int']> + crop?: Maybe<CropRegion> + scale?: Maybe<Scalars['Int']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductMediaArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductMediaSortKeys> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductMetafieldArgs = { + namespace: Scalars['String'] + key: Scalars['String'] +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductMetafieldsArgs = { + namespace?: Maybe<Scalars['String']> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductOptionsArgs = { + first?: Maybe<Scalars['Int']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductPresentmentPriceRangesArgs = { + presentmentCurrencies?: Maybe<Array<CurrencyCode>> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductVariantBySelectedOptionsArgs = { + selectedOptions: Array<SelectedOptionInput> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductVariantsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductVariantSortKeys> +} + +/** The set of valid sort keys for the ProductCollection query. */ +export enum ProductCollectionSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `price` value. */ + Price = 'PRICE', + /** Sort by the `best-selling` value. */ + BestSelling = 'BEST_SELLING', + /** Sort by the `created` value. */ + Created = 'CREATED', + /** Sort by the `id` value. */ + Id = 'ID', + /** Sort by the `manual` value. */ + Manual = 'MANUAL', + /** Sort by the `collection-default` value. */ + CollectionDefault = 'COLLECTION_DEFAULT', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** An auto-generated type for paginating through multiple Products. */ +export type ProductConnection = { + __typename?: 'ProductConnection' + /** A list of edges. */ + edges: Array<ProductEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Product and a cursor during pagination. */ +export type ProductEdge = { + __typename?: 'ProductEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ProductEdge. */ + node: Product +} + +/** The set of valid sort keys for the ProductImage query. */ +export enum ProductImageSortKeys { + /** Sort by the `created_at` value. */ + CreatedAt = 'CREATED_AT', + /** Sort by the `position` value. */ + Position = 'POSITION', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** The set of valid sort keys for the ProductMedia query. */ +export enum ProductMediaSortKeys { + /** Sort by the `position` value. */ + Position = 'POSITION', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** + * Product property names like "Size", "Color", and "Material" that the customers can select. + * Variants are selected based on permutations of these options. + * 255 characters limit each. + */ +export type ProductOption = Node & { + __typename?: 'ProductOption' + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The product option’s name. */ + name: Scalars['String'] + /** The corresponding value to the product option name. */ + values: Array<Scalars['String']> +} + +/** The price range of the product. */ +export type ProductPriceRange = { + __typename?: 'ProductPriceRange' + /** The highest variant's price. */ + maxVariantPrice: MoneyV2 + /** The lowest variant's price. */ + minVariantPrice: MoneyV2 +} + +/** An auto-generated type for paginating through multiple ProductPriceRanges. */ +export type ProductPriceRangeConnection = { + __typename?: 'ProductPriceRangeConnection' + /** A list of edges. */ + edges: Array<ProductPriceRangeEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one ProductPriceRange and a cursor during pagination. */ +export type ProductPriceRangeEdge = { + __typename?: 'ProductPriceRangeEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ProductPriceRangeEdge. */ + node: ProductPriceRange +} + +/** The set of valid sort keys for the Product query. */ +export enum ProductSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `product_type` value. */ + ProductType = 'PRODUCT_TYPE', + /** Sort by the `vendor` value. */ + Vendor = 'VENDOR', + /** Sort by the `updated_at` value. */ + UpdatedAt = 'UPDATED_AT', + /** Sort by the `created_at` value. */ + CreatedAt = 'CREATED_AT', + /** Sort by the `best_selling` value. */ + BestSelling = 'BEST_SELLING', + /** Sort by the `price` value. */ + Price = 'PRICE', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariant = Node & + HasMetafields & { + __typename?: 'ProductVariant' + /** + * Indicates if the product variant is in stock. + * @deprecated Use `availableForSale` instead + */ + available?: Maybe<Scalars['Boolean']> + /** Indicates if the product variant is available for sale. */ + availableForSale: Scalars['Boolean'] + /** + * The compare at price of the variant. This can be used to mark a variant as on sale, when `compareAtPrice` is higher than `price`. + * @deprecated Use `compareAtPriceV2` instead + */ + compareAtPrice?: Maybe<Scalars['Money']> + /** The compare at price of the variant. This can be used to mark a variant as on sale, when `compareAtPriceV2` is higher than `priceV2`. */ + compareAtPriceV2?: Maybe<MoneyV2> + /** Whether a product is out of stock but still available for purchase (used for backorders). */ + currentlyNotInStock: Scalars['Boolean'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** Image associated with the product variant. This field falls back to the product image if no image is available. */ + image?: Maybe<Image> + /** The metafield associated with the resource. */ + metafield?: Maybe<Metafield> + /** A paginated list of metafields associated with the resource. */ + metafields: MetafieldConnection + /** List of prices and compare-at prices in the presentment currencies for this shop. */ + presentmentPrices: ProductVariantPricePairConnection + /** List of unit prices in the presentment currencies for this shop. */ + presentmentUnitPrices: MoneyV2Connection + /** + * The product variant’s price. + * @deprecated Use `priceV2` instead + */ + price: Scalars['Money'] + /** The product variant’s price. */ + priceV2: MoneyV2 + /** The product object that the product variant belongs to. */ + product: Product + /** The total sellable quantity of the variant for online sales channels. */ + quantityAvailable?: Maybe<Scalars['Int']> + /** Whether a customer needs to provide a shipping address when placing an order for the product variant. */ + requiresShipping: Scalars['Boolean'] + /** List of product options applied to the variant. */ + selectedOptions: Array<SelectedOption> + /** The SKU (stock keeping unit) associated with the variant. */ + sku?: Maybe<Scalars['String']> + /** The product variant’s title. */ + title: Scalars['String'] + /** The unit price value for the variant based on the variant's measurement. */ + unitPrice?: Maybe<MoneyV2> + /** The unit price measurement for the variant. */ + unitPriceMeasurement?: Maybe<UnitPriceMeasurement> + /** The weight of the product variant in the unit system specified with `weight_unit`. */ + weight?: Maybe<Scalars['Float']> + /** Unit of measurement for weight. */ + weightUnit: WeightUnit + } + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariantImageArgs = { + maxWidth?: Maybe<Scalars['Int']> + maxHeight?: Maybe<Scalars['Int']> + crop?: Maybe<CropRegion> + scale?: Maybe<Scalars['Int']> +} + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariantMetafieldArgs = { + namespace: Scalars['String'] + key: Scalars['String'] +} + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariantMetafieldsArgs = { + namespace?: Maybe<Scalars['String']> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariantPresentmentPricesArgs = { + presentmentCurrencies?: Maybe<Array<CurrencyCode>> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariantPresentmentUnitPricesArgs = { + presentmentCurrencies?: Maybe<Array<CurrencyCode>> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** An auto-generated type for paginating through multiple ProductVariants. */ +export type ProductVariantConnection = { + __typename?: 'ProductVariantConnection' + /** A list of edges. */ + edges: Array<ProductVariantEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one ProductVariant and a cursor during pagination. */ +export type ProductVariantEdge = { + __typename?: 'ProductVariantEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ProductVariantEdge. */ + node: ProductVariant +} + +/** The compare-at price and price of a variant sharing a currency. */ +export type ProductVariantPricePair = { + __typename?: 'ProductVariantPricePair' + /** The compare-at price of the variant with associated currency. */ + compareAtPrice?: Maybe<MoneyV2> + /** The price of the variant with associated currency. */ + price: MoneyV2 +} + +/** An auto-generated type for paginating through multiple ProductVariantPricePairs. */ +export type ProductVariantPricePairConnection = { + __typename?: 'ProductVariantPricePairConnection' + /** A list of edges. */ + edges: Array<ProductVariantPricePairEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one ProductVariantPricePair and a cursor during pagination. */ +export type ProductVariantPricePairEdge = { + __typename?: 'ProductVariantPricePairEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ProductVariantPricePairEdge. */ + node: ProductVariantPricePair +} + +/** The set of valid sort keys for the ProductVariant query. */ +export enum ProductVariantSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `sku` value. */ + Sku = 'SKU', + /** Sort by the `position` value. */ + Position = 'POSITION', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRoot = { + __typename?: 'QueryRoot' + /** List of the shop's articles. */ + articles: ArticleConnection + /** Find a blog by its handle. */ + blogByHandle?: Maybe<Blog> + /** List of the shop's blogs. */ + blogs: BlogConnection + /** Find a collection by its handle. */ + collectionByHandle?: Maybe<Collection> + /** List of the shop’s collections. */ + collections: CollectionConnection + /** Find a customer by its access token. */ + customer?: Maybe<Customer> + node?: Maybe<Node> + nodes: Array<Maybe<Node>> + /** Find a page by its handle. */ + pageByHandle?: Maybe<Page> + /** List of the shop's pages. */ + pages: PageConnection + /** Find a product by its handle. */ + productByHandle?: Maybe<Product> + /** + * Find recommended products related to a given `product_id`. + * To learn more about how recommendations are generated, see + * [*Showing product recommendations on product pages*](https://help.shopify.com/themes/development/recommended-products). + */ + productRecommendations?: Maybe<Array<Product>> + /** + * Tags added to products. + * Additional access scope required: unauthenticated_read_product_tags. + */ + productTags: StringConnection + /** List of product types for the shop's products that are published to your app. */ + productTypes: StringConnection + /** List of the shop’s products. */ + products: ProductConnection + /** The list of public Storefront API versions, including supported, release candidate and unstable versions. */ + publicApiVersions: Array<ApiVersion> + /** The shop associated with the storefront access token. */ + shop: Shop +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootArticlesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ArticleSortKeys> + query?: Maybe<Scalars['String']> +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootBlogByHandleArgs = { + handle: Scalars['String'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootBlogsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<BlogSortKeys> + query?: Maybe<Scalars['String']> +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootCollectionByHandleArgs = { + handle: Scalars['String'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootCollectionsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<CollectionSortKeys> + query?: Maybe<Scalars['String']> +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootCustomerArgs = { + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootNodeArgs = { + id: Scalars['ID'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootNodesArgs = { + ids: Array<Scalars['ID']> +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootPageByHandleArgs = { + handle: Scalars['String'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootPagesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<PageSortKeys> + query?: Maybe<Scalars['String']> +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootProductByHandleArgs = { + handle: Scalars['String'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootProductRecommendationsArgs = { + productId: Scalars['ID'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootProductTagsArgs = { + first: Scalars['Int'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootProductTypesArgs = { + first: Scalars['Int'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootProductsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductSortKeys> + query?: Maybe<Scalars['String']> +} + +/** SEO information. */ +export type Seo = { + __typename?: 'SEO' + /** The meta description. */ + description?: Maybe<Scalars['String']> + /** The SEO title. */ + title?: Maybe<Scalars['String']> +} + +/** + * Script discount applications capture the intentions of a discount that + * was created by a Shopify Script. + */ +export type ScriptDiscountApplication = DiscountApplication & { + __typename?: 'ScriptDiscountApplication' + /** The method by which the discount's value is allocated to its entitled items. */ + allocationMethod: DiscountApplicationAllocationMethod + /** + * The description of the application as defined by the Script. + * @deprecated Use `title` instead + */ + description: Scalars['String'] + /** Which lines of targetType that the discount is allocated over. */ + targetSelection: DiscountApplicationTargetSelection + /** The type of line that the discount is applicable towards. */ + targetType: DiscountApplicationTargetType + /** The title of the application as defined by the Script. */ + title: Scalars['String'] + /** The value of the discount application. */ + value: PricingValue +} + +/** + * Properties used by customers to select a product variant. + * Products can have multiple options, like different sizes or colors. + */ +export type SelectedOption = { + __typename?: 'SelectedOption' + /** The product option’s name. */ + name: Scalars['String'] + /** The product option’s value. */ + value: Scalars['String'] +} + +/** Specifies the input fields required for a selected option. */ +export type SelectedOptionInput = { + /** The product option’s name. */ + name: Scalars['String'] + /** The product option’s value. */ + value: Scalars['String'] +} + +/** A shipping rate to be applied to a checkout. */ +export type ShippingRate = { + __typename?: 'ShippingRate' + /** Human-readable unique identifier for this shipping rate. */ + handle: Scalars['String'] + /** + * Price of this shipping rate. + * @deprecated Use `priceV2` instead + */ + price: Scalars['Money'] + /** Price of this shipping rate. */ + priceV2: MoneyV2 + /** Title of this shipping rate. */ + title: Scalars['String'] +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type Shop = { + __typename?: 'Shop' + /** + * List of the shop' articles. + * @deprecated Use `QueryRoot.articles` instead. + */ + articles: ArticleConnection + /** + * List of the shop' blogs. + * @deprecated Use `QueryRoot.blogs` instead. + */ + blogs: BlogConnection + /** + * Find a collection by its handle. + * @deprecated Use `QueryRoot.collectionByHandle` instead. + */ + collectionByHandle?: Maybe<Collection> + /** + * List of the shop’s collections. + * @deprecated Use `QueryRoot.collections` instead. + */ + collections: CollectionConnection + /** + * The three-letter code for the currency that the shop accepts. + * @deprecated Use `paymentSettings` instead + */ + currencyCode: CurrencyCode + /** A description of the shop. */ + description?: Maybe<Scalars['String']> + /** A string representing the way currency is formatted when the currency isn’t specified. */ + moneyFormat: Scalars['String'] + /** The shop’s name. */ + name: Scalars['String'] + /** Settings related to payments. */ + paymentSettings: PaymentSettings + /** The shop’s primary domain. */ + primaryDomain: Domain + /** The shop’s privacy policy. */ + privacyPolicy?: Maybe<ShopPolicy> + /** + * Find a product by its handle. + * @deprecated Use `QueryRoot.productByHandle` instead. + */ + productByHandle?: Maybe<Product> + /** + * A list of tags that have been added to products. + * Additional access scope required: unauthenticated_read_product_tags. + * @deprecated Use `QueryRoot.productTags` instead. + */ + productTags: StringConnection + /** + * List of the shop’s product types. + * @deprecated Use `QueryRoot.productTypes` instead. + */ + productTypes: StringConnection + /** + * List of the shop’s products. + * @deprecated Use `QueryRoot.products` instead. + */ + products: ProductConnection + /** The shop’s refund policy. */ + refundPolicy?: Maybe<ShopPolicy> + /** The shop’s shipping policy. */ + shippingPolicy?: Maybe<ShopPolicy> + /** Countries that the shop ships to. */ + shipsToCountries: Array<CountryCode> + /** + * The shop’s Shopify Payments account id. + * @deprecated Use `paymentSettings` instead + */ + shopifyPaymentsAccountId?: Maybe<Scalars['String']> + /** The shop’s terms of service. */ + termsOfService?: Maybe<ShopPolicy> +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopArticlesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ArticleSortKeys> + query?: Maybe<Scalars['String']> +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopBlogsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<BlogSortKeys> + query?: Maybe<Scalars['String']> +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopCollectionByHandleArgs = { + handle: Scalars['String'] +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopCollectionsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<CollectionSortKeys> + query?: Maybe<Scalars['String']> +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopProductByHandleArgs = { + handle: Scalars['String'] +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopProductTagsArgs = { + first: Scalars['Int'] +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopProductTypesArgs = { + first: Scalars['Int'] +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopProductsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductSortKeys> + query?: Maybe<Scalars['String']> +} + +/** Policy that a merchant has configured for their store, such as their refund or privacy policy. */ +export type ShopPolicy = Node & { + __typename?: 'ShopPolicy' + /** Policy text, maximum size of 64kb. */ + body: Scalars['String'] + /** Policy’s handle. */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** Policy’s title. */ + title: Scalars['String'] + /** Public URL to the policy. */ + url: Scalars['URL'] +} + +/** An auto-generated type for paginating through multiple Strings. */ +export type StringConnection = { + __typename?: 'StringConnection' + /** A list of edges. */ + edges: Array<StringEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one String and a cursor during pagination. */ +export type StringEdge = { + __typename?: 'StringEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of StringEdge. */ + node: Scalars['String'] +} + +/** + * Specifies the fields required to complete a checkout with + * a tokenized payment. + */ +export type TokenizedPaymentInput = { + /** The amount of the payment. */ + amount: Scalars['Money'] + /** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */ + idempotencyKey: Scalars['String'] + /** The billing address for the payment. */ + billingAddress: MailingAddressInput + /** The type of payment token. */ + type: Scalars['String'] + /** A simple string or JSON containing the required payment data for the tokenized payment. */ + paymentData: Scalars['String'] + /** Executes the payment in test mode if possible. Defaults to `false`. */ + test?: Maybe<Scalars['Boolean']> + /** Public Hash Key used for AndroidPay payments only. */ + identifier?: Maybe<Scalars['String']> +} + +/** + * Specifies the fields required to complete a checkout with + * a tokenized payment. + */ +export type TokenizedPaymentInputV2 = { + /** The amount and currency of the payment. */ + paymentAmount: MoneyInput + /** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */ + idempotencyKey: Scalars['String'] + /** The billing address for the payment. */ + billingAddress: MailingAddressInput + /** A simple string or JSON containing the required payment data for the tokenized payment. */ + paymentData: Scalars['String'] + /** Whether to execute the payment in test mode, if possible. Test mode is not supported in production stores. Defaults to `false`. */ + test?: Maybe<Scalars['Boolean']> + /** Public Hash Key used for AndroidPay payments only. */ + identifier?: Maybe<Scalars['String']> + /** The type of payment token. */ + type: Scalars['String'] +} + +/** + * Specifies the fields required to complete a checkout with + * a tokenized payment. + */ +export type TokenizedPaymentInputV3 = { + /** The amount and currency of the payment. */ + paymentAmount: MoneyInput + /** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */ + idempotencyKey: Scalars['String'] + /** The billing address for the payment. */ + billingAddress: MailingAddressInput + /** A simple string or JSON containing the required payment data for the tokenized payment. */ + paymentData: Scalars['String'] + /** Whether to execute the payment in test mode, if possible. Test mode is not supported in production stores. Defaults to `false`. */ + test?: Maybe<Scalars['Boolean']> + /** Public Hash Key used for AndroidPay payments only. */ + identifier?: Maybe<Scalars['String']> + /** The type of payment token. */ + type: PaymentTokenType +} + +/** An object representing exchange of money for a product or service. */ +export type Transaction = { + __typename?: 'Transaction' + /** + * The amount of money that the transaction was for. + * @deprecated Use `amountV2` instead + */ + amount: Scalars['Money'] + /** The amount of money that the transaction was for. */ + amountV2: MoneyV2 + /** The kind of the transaction. */ + kind: TransactionKind + /** + * The status of the transaction. + * @deprecated Use `statusV2` instead + */ + status: TransactionStatus + /** The status of the transaction. */ + statusV2?: Maybe<TransactionStatus> + /** Whether the transaction was done in test mode or not. */ + test: Scalars['Boolean'] +} + +export enum TransactionKind { + Sale = 'SALE', + Capture = 'CAPTURE', + Authorization = 'AUTHORIZATION', + EmvAuthorization = 'EMV_AUTHORIZATION', + Change = 'CHANGE', +} + +export enum TransactionStatus { + Pending = 'PENDING', + Success = 'SUCCESS', + Failure = 'FAILURE', + Error = 'ERROR', +} + +/** The measurement used to calculate a unit price for a product variant (e.g. $9.99 / 100ml). */ +export type UnitPriceMeasurement = { + __typename?: 'UnitPriceMeasurement' + /** The type of unit of measurement for the unit price measurement. */ + measuredType?: Maybe<UnitPriceMeasurementMeasuredType> + /** The quantity unit for the unit price measurement. */ + quantityUnit?: Maybe<UnitPriceMeasurementMeasuredUnit> + /** The quantity value for the unit price measurement. */ + quantityValue: Scalars['Float'] + /** The reference unit for the unit price measurement. */ + referenceUnit?: Maybe<UnitPriceMeasurementMeasuredUnit> + /** The reference value for the unit price measurement. */ + referenceValue: Scalars['Int'] +} + +/** The accepted types of unit of measurement. */ +export enum UnitPriceMeasurementMeasuredType { + /** Unit of measurements representing volumes. */ + Volume = 'VOLUME', + /** Unit of measurements representing weights. */ + Weight = 'WEIGHT', + /** Unit of measurements representing lengths. */ + Length = 'LENGTH', + /** Unit of measurements representing areas. */ + Area = 'AREA', +} + +/** The valid units of measurement for a unit price measurement. */ +export enum UnitPriceMeasurementMeasuredUnit { + /** 1000 milliliters equals 1 liter. */ + Ml = 'ML', + /** 100 centiliters equals 1 liter. */ + Cl = 'CL', + /** Metric system unit of volume. */ + L = 'L', + /** 1 cubic meter equals 1000 liters. */ + M3 = 'M3', + /** 1000 milligrams equals 1 gram. */ + Mg = 'MG', + /** Metric system unit of weight. */ + G = 'G', + /** 1 kilogram equals 1000 grams. */ + Kg = 'KG', + /** 1000 millimeters equals 1 meter. */ + Mm = 'MM', + /** 100 centimeters equals 1 meter. */ + Cm = 'CM', + /** Metric system unit of length. */ + M = 'M', + /** Metric system unit of area. */ + M2 = 'M2', +} + +/** Represents an error in the input of a mutation. */ +export type UserError = DisplayableError & { + __typename?: 'UserError' + /** Path to the input field which caused the error. */ + field?: Maybe<Array<Scalars['String']>> + /** The error message. */ + message: Scalars['String'] +} + +/** Represents a Shopify hosted video. */ +export type Video = Node & + Media & { + __typename?: 'Video' + /** A word or phrase to share the nature or contents of a media. */ + alt?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The media content type. */ + mediaContentType: MediaContentType + /** The preview image for the media. */ + previewImage?: Maybe<Image> + /** The sources for a video. */ + sources: Array<VideoSource> + } + +/** Represents a source for a Shopify hosted video. */ +export type VideoSource = { + __typename?: 'VideoSource' + /** The format of the video source. */ + format: Scalars['String'] + /** The height of the video. */ + height: Scalars['Int'] + /** The video MIME type. */ + mimeType: Scalars['String'] + /** The URL of the video. */ + url: Scalars['String'] + /** The width of the video. */ + width: Scalars['Int'] +} + +/** Units of measurement for weight. */ +export enum WeightUnit { + /** 1 kilogram equals 1000 grams. */ + Kilograms = 'KILOGRAMS', + /** Metric system unit of mass. */ + Grams = 'GRAMS', + /** 1 pound equals 16 ounces. */ + Pounds = 'POUNDS', + /** Imperial system unit of mass. */ + Ounces = 'OUNCES', +} + +export type Unnamed_1_QueryVariables = Exact<{ + first: Scalars['Int'] +}> + +export type Unnamed_1_Query = { __typename?: 'QueryRoot' } & { + pages: { __typename?: 'PageConnection' } & { + edges: Array< + { __typename?: 'PageEdge' } & { + node: { __typename?: 'Page' } & Pick< + Page, + 'id' | 'title' | 'handle' | 'body' | 'bodySummary' | 'url' + > + } + > + } +} diff --git a/framework/shopify/schema.graphql b/framework/shopify/schema.graphql new file mode 100644 index 000000000..822e6007e --- /dev/null +++ b/framework/shopify/schema.graphql @@ -0,0 +1,9631 @@ +schema { + query: QueryRoot + mutation: Mutation +} + +""" +Marks an element of a GraphQL schema as having restricted access. +""" +directive @accessRestricted( + """ + Explains the reason around this restriction + """ + reason: String = null +) on FIELD_DEFINITION | OBJECT + +""" +A version of the API. +""" +type ApiVersion { + """ + The human-readable name of the version. + """ + displayName: String! + + """ + The unique identifier of an ApiVersion. All supported API versions have a date-based (YYYY-MM) or `unstable` handle. + """ + handle: String! + + """ + Whether the version is supported by Shopify. + """ + supported: Boolean! +} + +""" +Details about the gift card used on the checkout. +""" +type AppliedGiftCard implements Node { + """ + The amount that was taken from the gift card by applying it. + """ + amountUsed: Money! @deprecated(reason: "Use `amountUsedV2` instead") + + """ + The amount that was taken from the gift card by applying it. + """ + amountUsedV2: MoneyV2! + + """ + The amount left on the gift card. + """ + balance: Money! @deprecated(reason: "Use `balanceV2` instead") + + """ + The amount left on the gift card. + """ + balanceV2: MoneyV2! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The last characters of the gift card. + """ + lastCharacters: String! + + """ + The amount that was applied to the checkout in its currency. + """ + presentmentAmountUsed: MoneyV2! +} + +""" +An article in an online store blog. +""" +type Article implements Node { + """ + The article's author. + """ + author: ArticleAuthor! @deprecated(reason: "Use `authorV2` instead") + + """ + The article's author. + """ + authorV2: ArticleAuthor + + """ + The blog that the article belongs to. + """ + blog: Blog! + + """ + List of comments posted on the article. + """ + comments( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): CommentConnection! + + """ + Stripped content of the article, single line with HTML tags removed. + """ + content( + """ + Truncates string after the given length. + """ + truncateAt: Int + ): String! + + """ + The content of the article, complete with HTML formatting. + """ + contentHtml: HTML! + + """ + Stripped excerpt of the article, single line with HTML tags removed. + """ + excerpt( + """ + Truncates string after the given length. + """ + truncateAt: Int + ): String + + """ + The excerpt of the article, complete with HTML formatting. + """ + excerptHtml: HTML + + """ + A human-friendly unique string for the Article automatically generated from its title. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The image associated with the article. + """ + image( + """ + Image width in pixels between 1 and 2048. This argument is deprecated: Use `maxWidth` on `Image.transformedSrc` instead. + """ + maxWidth: Int + + """ + Image height in pixels between 1 and 2048. This argument is deprecated: Use `maxHeight` on `Image.transformedSrc` instead. + """ + maxHeight: Int + + """ + Crops the image according to the specified region. This argument is deprecated: Use `crop` on `Image.transformedSrc` instead. + """ + crop: CropRegion + + """ + Image size multiplier for high-resolution retina displays. Must be between 1 and 3. This argument is deprecated: Use `scale` on `Image.transformedSrc` instead. + """ + scale: Int = 1 + ): Image + + """ + The date and time when the article was published. + """ + publishedAt: DateTime! + + """ + The article’s SEO information. + """ + seo: SEO + + """ + A categorization that a article can be tagged with. + """ + tags: [String!]! + + """ + The article’s name. + """ + title: String! + + """ + The url pointing to the article accessible from the web. + """ + url: URL! +} + +""" +The author of an article. +""" +type ArticleAuthor { + """ + The author's bio. + """ + bio: String + + """ + The author’s email. + """ + email: String! + + """ + The author's first name. + """ + firstName: String! + + """ + The author's last name. + """ + lastName: String! + + """ + The author's full name. + """ + name: String! +} + +""" +An auto-generated type for paginating through multiple Articles. +""" +type ArticleConnection { + """ + A list of edges. + """ + edges: [ArticleEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Article and a cursor during pagination. +""" +type ArticleEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ArticleEdge. + """ + node: Article! +} + +""" +The set of valid sort keys for the Article query. +""" +enum ArticleSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `blog_title` value. + """ + BLOG_TITLE + + """ + Sort by the `author` value. + """ + AUTHOR + + """ + Sort by the `updated_at` value. + """ + UPDATED_AT + + """ + Sort by the `published_at` value. + """ + PUBLISHED_AT + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +Represents a generic custom attribute. +""" +type Attribute { + """ + Key or name of the attribute. + """ + key: String! + + """ + Value of the attribute. + """ + value: String +} + +""" +Specifies the input fields required for an attribute. +""" +input AttributeInput { + """ + Key or name of the attribute. + """ + key: String! + + """ + Value of the attribute. + """ + value: String! +} + +""" +Automatic discount applications capture the intentions of a discount that was automatically applied. +""" +type AutomaticDiscountApplication implements DiscountApplication { + """ + The method by which the discount's value is allocated to its entitled items. + """ + allocationMethod: DiscountApplicationAllocationMethod! + + """ + Which lines of targetType that the discount is allocated over. + """ + targetSelection: DiscountApplicationTargetSelection! + + """ + The type of line that the discount is applicable towards. + """ + targetType: DiscountApplicationTargetType! + + """ + The title of the application. + """ + title: String! + + """ + The value of the discount application. + """ + value: PricingValue! +} + +""" +A collection of available shipping rates for a checkout. +""" +type AvailableShippingRates { + """ + Whether or not the shipping rates are ready. + The `shippingRates` field is `null` when this value is `false`. + This field should be polled until its value becomes `true`. + """ + ready: Boolean! + + """ + The fetched shipping rates. `null` until the `ready` field is `true`. + """ + shippingRates: [ShippingRate!] +} + +""" +An online store blog. +""" +type Blog implements Node { + """ + Find an article by its handle. + """ + articleByHandle( + """ + The handle of the article. + """ + handle: String! + ): Article + + """ + List of the blog's articles. + """ + articles( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ArticleSortKeys = ID + + """ + Supported filter parameters: + - `author` + - `blog_title` + - `created_at` + - `tag` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): ArticleConnection! + + """ + The authors who have contributed to the blog. + """ + authors: [ArticleAuthor!]! + + """ + A human-friendly unique string for the Blog automatically generated from its title. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The blog's SEO information. + """ + seo: SEO + + """ + The blogs’s title. + """ + title: String! + + """ + The url pointing to the blog accessible from the web. + """ + url: URL! +} + +""" +An auto-generated type for paginating through multiple Blogs. +""" +type BlogConnection { + """ + A list of edges. + """ + edges: [BlogEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Blog and a cursor during pagination. +""" +type BlogEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of BlogEdge. + """ + node: Blog! +} + +""" +The set of valid sort keys for the Blog query. +""" +enum BlogSortKeys { + """ + Sort by the `handle` value. + """ + HANDLE + + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +Card brand, such as Visa or Mastercard, which can be used for payments. +""" +enum CardBrand { + """ + Visa + """ + VISA + + """ + Mastercard + """ + MASTERCARD + + """ + Discover + """ + DISCOVER + + """ + American Express + """ + AMERICAN_EXPRESS + + """ + Diners Club + """ + DINERS_CLUB + + """ + JCB + """ + JCB +} + +""" +A container for all the information required to checkout items and pay. +""" +type Checkout implements Node { + """ + The gift cards used on the checkout. + """ + appliedGiftCards: [AppliedGiftCard!]! + + """ + The available shipping rates for this Checkout. + Should only be used when checkout `requiresShipping` is `true` and + the shipping address is valid. + """ + availableShippingRates: AvailableShippingRates + + """ + The date and time when the checkout was completed. + """ + completedAt: DateTime + + """ + The date and time when the checkout was created. + """ + createdAt: DateTime! + + """ + The currency code for the Checkout. + """ + currencyCode: CurrencyCode! + + """ + A list of extra information that is added to the checkout. + """ + customAttributes: [Attribute!]! + + """ + The customer associated with the checkout. + """ + customer: Customer + @deprecated( + reason: "This field will always return null. If you have an authentication token for the customer, you can use the `customer` field on the query root to retrieve it." + ) + + """ + Discounts that have been applied on the checkout. + """ + discountApplications( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): DiscountApplicationConnection! + + """ + The email attached to this checkout. + """ + email: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + A list of line item objects, each one containing information about an item in the checkout. + """ + lineItems( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): CheckoutLineItemConnection! + + """ + The sum of all the prices of all the items in the checkout. Duties, taxes, shipping and discounts excluded. + """ + lineItemsSubtotalPrice: MoneyV2! + + """ + The note associated with the checkout. + """ + note: String + + """ + The resulting order from a paid checkout. + """ + order: Order + + """ + The Order Status Page for this Checkout, null when checkout is not completed. + """ + orderStatusUrl: URL + + """ + The amount left to be paid. This is equal to the cost of the line items, taxes and shipping minus discounts and gift cards. + """ + paymentDue: Money! @deprecated(reason: "Use `paymentDueV2` instead") + + """ + The amount left to be paid. This is equal to the cost of the line items, duties, taxes and shipping minus discounts and gift cards. + """ + paymentDueV2: MoneyV2! + + """ + Whether or not the Checkout is ready and can be completed. Checkouts may + have asynchronous operations that can take time to finish. If you want + to complete a checkout or ensure all the fields are populated and up to + date, polling is required until the value is true. + """ + ready: Boolean! + + """ + States whether or not the fulfillment requires shipping. + """ + requiresShipping: Boolean! + + """ + The shipping address to where the line items will be shipped. + """ + shippingAddress: MailingAddress + + """ + The discounts that have been allocated onto the shipping line by discount applications. + """ + shippingDiscountAllocations: [DiscountAllocation!]! + + """ + Once a shipping rate is selected by the customer it is transitioned to a `shipping_line` object. + """ + shippingLine: ShippingRate + + """ + Price of the checkout before shipping and taxes. + """ + subtotalPrice: Money! @deprecated(reason: "Use `subtotalPriceV2` instead") + + """ + Price of the checkout before duties, shipping and taxes. + """ + subtotalPriceV2: MoneyV2! + + """ + Specifies if the Checkout is tax exempt. + """ + taxExempt: Boolean! + + """ + Specifies if taxes are included in the line item and shipping line prices. + """ + taxesIncluded: Boolean! + + """ + The sum of all the prices of all the items in the checkout, taxes and discounts included. + """ + totalPrice: Money! @deprecated(reason: "Use `totalPriceV2` instead") + + """ + The sum of all the prices of all the items in the checkout, duties, taxes and discounts included. + """ + totalPriceV2: MoneyV2! + + """ + The sum of all the taxes applied to the line items and shipping lines in the checkout. + """ + totalTax: Money! @deprecated(reason: "Use `totalTaxV2` instead") + + """ + The sum of all the taxes applied to the line items and shipping lines in the checkout. + """ + totalTaxV2: MoneyV2! + + """ + The date and time when the checkout was last updated. + """ + updatedAt: DateTime! + + """ + The url pointing to the checkout accessible from the web. + """ + webUrl: URL! +} + +""" +Specifies the fields required to update a checkout's attributes. +""" +input CheckoutAttributesUpdateInput { + """ + The text of an optional note that a shop owner can attach to the checkout. + """ + note: String + + """ + A list of extra information that is added to the checkout. + """ + customAttributes: [AttributeInput!] + + """ + Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + The required attributes are city, province, and country. + Full validation of the addresses is still done at complete time. + """ + allowPartialAddresses: Boolean +} + +""" +Return type for `checkoutAttributesUpdate` mutation. +""" +type CheckoutAttributesUpdatePayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Specifies the fields required to update a checkout's attributes. +""" +input CheckoutAttributesUpdateV2Input { + """ + The text of an optional note that a shop owner can attach to the checkout. + """ + note: String + + """ + A list of extra information that is added to the checkout. + """ + customAttributes: [AttributeInput!] + + """ + Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + The required attributes are city, province, and country. + Full validation of the addresses is still done at complete time. + """ + allowPartialAddresses: Boolean +} + +""" +Return type for `checkoutAttributesUpdateV2` mutation. +""" +type CheckoutAttributesUpdateV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteFree` mutation. +""" +type CheckoutCompleteFreePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteWithCreditCard` mutation. +""" +type CheckoutCompleteWithCreditCardPayload { + """ + The checkout on which the payment was applied. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + A representation of the attempted payment. + """ + payment: Payment + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteWithCreditCardV2` mutation. +""" +type CheckoutCompleteWithCreditCardV2Payload { + """ + The checkout on which the payment was applied. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + A representation of the attempted payment. + """ + payment: Payment + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteWithTokenizedPayment` mutation. +""" +type CheckoutCompleteWithTokenizedPaymentPayload { + """ + The checkout on which the payment was applied. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + A representation of the attempted payment. + """ + payment: Payment + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteWithTokenizedPaymentV2` mutation. +""" +type CheckoutCompleteWithTokenizedPaymentV2Payload { + """ + The checkout on which the payment was applied. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + A representation of the attempted payment. + """ + payment: Payment + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteWithTokenizedPaymentV3` mutation. +""" +type CheckoutCompleteWithTokenizedPaymentV3Payload { + """ + The checkout on which the payment was applied. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + A representation of the attempted payment. + """ + payment: Payment + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Specifies the fields required to create a checkout. +""" +input CheckoutCreateInput { + """ + The email with which the customer wants to checkout. + """ + email: String + + """ + A list of line item objects, each one containing information about an item in the checkout. + """ + lineItems: [CheckoutLineItemInput!] + + """ + The shipping address to where the line items will be shipped. + """ + shippingAddress: MailingAddressInput + + """ + The text of an optional note that a shop owner can attach to the checkout. + """ + note: String + + """ + A list of extra information that is added to the checkout. + """ + customAttributes: [AttributeInput!] + + """ + Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + The required attributes are city, province, and country. + Full validation of addresses is still done at complete time. + """ + allowPartialAddresses: Boolean + + """ + The three-letter currency code of one of the shop's enabled presentment currencies. + Including this field creates a checkout in the specified currency. By default, new + checkouts are created in the shop's primary currency. + """ + presentmentCurrencyCode: CurrencyCode +} + +""" +Return type for `checkoutCreate` mutation. +""" +type CheckoutCreatePayload { + """ + The new checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCustomerAssociate` mutation. +""" +type CheckoutCustomerAssociatePayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + The associated customer object. + """ + customer: Customer + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! +} + +""" +Return type for `checkoutCustomerAssociateV2` mutation. +""" +type CheckoutCustomerAssociateV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + The associated customer object. + """ + customer: Customer + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCustomerDisassociate` mutation. +""" +type CheckoutCustomerDisassociatePayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCustomerDisassociateV2` mutation. +""" +type CheckoutCustomerDisassociateV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutDiscountCodeApply` mutation. +""" +type CheckoutDiscountCodeApplyPayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutDiscountCodeApplyV2` mutation. +""" +type CheckoutDiscountCodeApplyV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutDiscountCodeRemove` mutation. +""" +type CheckoutDiscountCodeRemovePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutEmailUpdate` mutation. +""" +type CheckoutEmailUpdatePayload { + """ + The checkout object with the updated email. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutEmailUpdateV2` mutation. +""" +type CheckoutEmailUpdateV2Payload { + """ + The checkout object with the updated email. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Possible error codes that could be returned by CheckoutUserError. +""" +enum CheckoutErrorCode { + """ + Input value is blank. + """ + BLANK + + """ + Input value is invalid. + """ + INVALID + + """ + Input value is too long. + """ + TOO_LONG + + """ + Input value is not present. + """ + PRESENT + + """ + Input value should be less than maximum allowed value. + """ + LESS_THAN + + """ + Input value should be greater than or equal to minimum allowed value. + """ + GREATER_THAN_OR_EQUAL_TO + + """ + Input value should be less or equal to maximum allowed value. + """ + LESS_THAN_OR_EQUAL_TO + + """ + Checkout is already completed. + """ + ALREADY_COMPLETED + + """ + Checkout is locked. + """ + LOCKED + + """ + Input value is not supported. + """ + NOT_SUPPORTED + + """ + Input email contains an invalid domain name. + """ + BAD_DOMAIN + + """ + Input Zip is invalid for country provided. + """ + INVALID_FOR_COUNTRY + + """ + Input Zip is invalid for country and province provided. + """ + INVALID_FOR_COUNTRY_AND_PROVINCE + + """ + Invalid state in country. + """ + INVALID_STATE_IN_COUNTRY + + """ + Invalid province in country. + """ + INVALID_PROVINCE_IN_COUNTRY + + """ + Invalid region in country. + """ + INVALID_REGION_IN_COUNTRY + + """ + Shipping rate expired. + """ + SHIPPING_RATE_EXPIRED + + """ + Gift card cannot be applied to a checkout that contains a gift card. + """ + GIFT_CARD_UNUSABLE + + """ + Gift card is disabled. + """ + GIFT_CARD_DISABLED + + """ + Gift card code is invalid. + """ + GIFT_CARD_CODE_INVALID + + """ + Gift card has already been applied. + """ + GIFT_CARD_ALREADY_APPLIED + + """ + Gift card currency does not match checkout currency. + """ + GIFT_CARD_CURRENCY_MISMATCH + + """ + Gift card is expired. + """ + GIFT_CARD_EXPIRED + + """ + Gift card has no funds left. + """ + GIFT_CARD_DEPLETED + + """ + Gift card was not found. + """ + GIFT_CARD_NOT_FOUND + + """ + Cart does not meet discount requirements notice. + """ + CART_DOES_NOT_MEET_DISCOUNT_REQUIREMENTS_NOTICE + + """ + Discount expired. + """ + DISCOUNT_EXPIRED + + """ + Discount disabled. + """ + DISCOUNT_DISABLED + + """ + Discount limit reached. + """ + DISCOUNT_LIMIT_REACHED + + """ + Discount not found. + """ + DISCOUNT_NOT_FOUND + + """ + Customer already used once per customer discount notice. + """ + CUSTOMER_ALREADY_USED_ONCE_PER_CUSTOMER_DISCOUNT_NOTICE + + """ + Checkout is already completed. + """ + EMPTY + + """ + Not enough in stock. + """ + NOT_ENOUGH_IN_STOCK + + """ + Missing payment input. + """ + MISSING_PAYMENT_INPUT + + """ + The amount of the payment does not match the value to be paid. + """ + TOTAL_PRICE_MISMATCH + + """ + Line item was not found in checkout. + """ + LINE_ITEM_NOT_FOUND + + """ + Unable to apply discount. + """ + UNABLE_TO_APPLY + + """ + Discount already applied. + """ + DISCOUNT_ALREADY_APPLIED +} + +""" +Return type for `checkoutGiftCardApply` mutation. +""" +type CheckoutGiftCardApplyPayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutGiftCardRemove` mutation. +""" +type CheckoutGiftCardRemovePayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutGiftCardRemoveV2` mutation. +""" +type CheckoutGiftCardRemoveV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutGiftCardsAppend` mutation. +""" +type CheckoutGiftCardsAppendPayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +A single line item in the checkout, grouped by variant and attributes. +""" +type CheckoutLineItem implements Node { + """ + Extra information in the form of an array of Key-Value pairs about the line item. + """ + customAttributes: [Attribute!]! + + """ + The discounts that have been allocated onto the checkout line item by discount applications. + """ + discountAllocations: [DiscountAllocation!]! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The quantity of the line item. + """ + quantity: Int! + + """ + Title of the line item. Defaults to the product's title. + """ + title: String! + + """ + Unit price of the line item. + """ + unitPrice: MoneyV2 + + """ + Product variant of the line item. + """ + variant: ProductVariant +} + +""" +An auto-generated type for paginating through multiple CheckoutLineItems. +""" +type CheckoutLineItemConnection { + """ + A list of edges. + """ + edges: [CheckoutLineItemEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one CheckoutLineItem and a cursor during pagination. +""" +type CheckoutLineItemEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of CheckoutLineItemEdge. + """ + node: CheckoutLineItem! +} + +""" +Specifies the input fields to create a line item on a checkout. +""" +input CheckoutLineItemInput { + """ + Extra information in the form of an array of Key-Value pairs about the line item. + """ + customAttributes: [AttributeInput!] + + """ + The quantity of the line item. + """ + quantity: Int! + + """ + The identifier of the product variant for the line item. + """ + variantId: ID! +} + +""" +Specifies the input fields to update a line item on the checkout. +""" +input CheckoutLineItemUpdateInput { + """ + The identifier of the line item. + """ + id: ID + + """ + The variant identifier of the line item. + """ + variantId: ID + + """ + The quantity of the line item. + """ + quantity: Int + + """ + Extra information in the form of an array of Key-Value pairs about the line item. + """ + customAttributes: [AttributeInput!] +} + +""" +Return type for `checkoutLineItemsAdd` mutation. +""" +type CheckoutLineItemsAddPayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutLineItemsRemove` mutation. +""" +type CheckoutLineItemsRemovePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutLineItemsReplace` mutation. +""" +type CheckoutLineItemsReplacePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [CheckoutUserError!]! +} + +""" +Return type for `checkoutLineItemsUpdate` mutation. +""" +type CheckoutLineItemsUpdatePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutShippingAddressUpdate` mutation. +""" +type CheckoutShippingAddressUpdatePayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutShippingAddressUpdateV2` mutation. +""" +type CheckoutShippingAddressUpdateV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutShippingLineUpdate` mutation. +""" +type CheckoutShippingLineUpdatePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Represents an error that happens during execution of a checkout mutation. +""" +type CheckoutUserError implements DisplayableError { + """ + Error code to uniquely identify the error. + """ + code: CheckoutErrorCode + + """ + Path to the input field which caused the error. + """ + field: [String!] + + """ + The error message. + """ + message: String! +} + +""" +A collection represents a grouping of products that a shop owner can create to organize them or make their shops easier to browse. +""" +type Collection implements Node { + """ + Stripped description of the collection, single line with HTML tags removed. + """ + description( + """ + Truncates string after the given length. + """ + truncateAt: Int + ): String! + + """ + The description of the collection, complete with HTML formatting. + """ + descriptionHtml: HTML! + + """ + A human-friendly unique string for the collection automatically generated from its title. + Limit of 255 characters. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + Image associated with the collection. + """ + image( + """ + Image width in pixels between 1 and 2048. This argument is deprecated: Use `maxWidth` on `Image.transformedSrc` instead. + """ + maxWidth: Int + + """ + Image height in pixels between 1 and 2048. This argument is deprecated: Use `maxHeight` on `Image.transformedSrc` instead. + """ + maxHeight: Int + + """ + Crops the image according to the specified region. This argument is deprecated: Use `crop` on `Image.transformedSrc` instead. + """ + crop: CropRegion + + """ + Image size multiplier for high-resolution retina displays. Must be between 1 and 3. This argument is deprecated: Use `scale` on `Image.transformedSrc` instead. + """ + scale: Int = 1 + ): Image + + """ + List of products in the collection. + """ + products( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductCollectionSortKeys = COLLECTION_DEFAULT + ): ProductConnection! + + """ + The collection’s name. Limit of 255 characters. + """ + title: String! + + """ + The date and time when the collection was last modified. + """ + updatedAt: DateTime! +} + +""" +An auto-generated type for paginating through multiple Collections. +""" +type CollectionConnection { + """ + A list of edges. + """ + edges: [CollectionEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Collection and a cursor during pagination. +""" +type CollectionEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of CollectionEdge. + """ + node: Collection! +} + +""" +The set of valid sort keys for the Collection query. +""" +enum CollectionSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `updated_at` value. + """ + UPDATED_AT + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +A comment on an article. +""" +type Comment implements Node { + """ + The comment’s author. + """ + author: CommentAuthor! + + """ + Stripped content of the comment, single line with HTML tags removed. + """ + content( + """ + Truncates string after the given length. + """ + truncateAt: Int + ): String! + + """ + The content of the comment, complete with HTML formatting. + """ + contentHtml: HTML! + + """ + Globally unique identifier. + """ + id: ID! +} + +""" +The author of a comment. +""" +type CommentAuthor { + """ + The author's email. + """ + email: String! + + """ + The author’s name. + """ + name: String! +} + +""" +An auto-generated type for paginating through multiple Comments. +""" +type CommentConnection { + """ + A list of edges. + """ + edges: [CommentEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Comment and a cursor during pagination. +""" +type CommentEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of CommentEdge. + """ + node: Comment! +} + +""" +ISO 3166-1 alpha-2 country codes with some differences. +""" +enum CountryCode { + """ + Afghanistan. + """ + AF + + """ + Åland Islands. + """ + AX + + """ + Albania. + """ + AL + + """ + Algeria. + """ + DZ + + """ + Andorra. + """ + AD + + """ + Angola. + """ + AO + + """ + Anguilla. + """ + AI + + """ + Antigua & Barbuda. + """ + AG + + """ + Argentina. + """ + AR + + """ + Armenia. + """ + AM + + """ + Aruba. + """ + AW + + """ + Australia. + """ + AU + + """ + Austria. + """ + AT + + """ + Azerbaijan. + """ + AZ + + """ + Bahamas. + """ + BS + + """ + Bahrain. + """ + BH + + """ + Bangladesh. + """ + BD + + """ + Barbados. + """ + BB + + """ + Belarus. + """ + BY + + """ + Belgium. + """ + BE + + """ + Belize. + """ + BZ + + """ + Benin. + """ + BJ + + """ + Bermuda. + """ + BM + + """ + Bhutan. + """ + BT + + """ + Bolivia. + """ + BO + + """ + Bosnia & Herzegovina. + """ + BA + + """ + Botswana. + """ + BW + + """ + Bouvet Island. + """ + BV + + """ + Brazil. + """ + BR + + """ + British Indian Ocean Territory. + """ + IO + + """ + Brunei. + """ + BN + + """ + Bulgaria. + """ + BG + + """ + Burkina Faso. + """ + BF + + """ + Burundi. + """ + BI + + """ + Cambodia. + """ + KH + + """ + Canada. + """ + CA + + """ + Cape Verde. + """ + CV + + """ + Caribbean Netherlands. + """ + BQ + + """ + Cayman Islands. + """ + KY + + """ + Central African Republic. + """ + CF + + """ + Chad. + """ + TD + + """ + Chile. + """ + CL + + """ + China. + """ + CN + + """ + Christmas Island. + """ + CX + + """ + Cocos (Keeling) Islands. + """ + CC + + """ + Colombia. + """ + CO + + """ + Comoros. + """ + KM + + """ + Congo - Brazzaville. + """ + CG + + """ + Congo - Kinshasa. + """ + CD + + """ + Cook Islands. + """ + CK + + """ + Costa Rica. + """ + CR + + """ + Croatia. + """ + HR + + """ + Cuba. + """ + CU + + """ + Curaçao. + """ + CW + + """ + Cyprus. + """ + CY + + """ + Czechia. + """ + CZ + + """ + Côte d’Ivoire. + """ + CI + + """ + Denmark. + """ + DK + + """ + Djibouti. + """ + DJ + + """ + Dominica. + """ + DM + + """ + Dominican Republic. + """ + DO + + """ + Ecuador. + """ + EC + + """ + Egypt. + """ + EG + + """ + El Salvador. + """ + SV + + """ + Equatorial Guinea. + """ + GQ + + """ + Eritrea. + """ + ER + + """ + Estonia. + """ + EE + + """ + Eswatini. + """ + SZ + + """ + Ethiopia. + """ + ET + + """ + Falkland Islands. + """ + FK + + """ + Faroe Islands. + """ + FO + + """ + Fiji. + """ + FJ + + """ + Finland. + """ + FI + + """ + France. + """ + FR + + """ + French Guiana. + """ + GF + + """ + French Polynesia. + """ + PF + + """ + French Southern Territories. + """ + TF + + """ + Gabon. + """ + GA + + """ + Gambia. + """ + GM + + """ + Georgia. + """ + GE + + """ + Germany. + """ + DE + + """ + Ghana. + """ + GH + + """ + Gibraltar. + """ + GI + + """ + Greece. + """ + GR + + """ + Greenland. + """ + GL + + """ + Grenada. + """ + GD + + """ + Guadeloupe. + """ + GP + + """ + Guatemala. + """ + GT + + """ + Guernsey. + """ + GG + + """ + Guinea. + """ + GN + + """ + Guinea-Bissau. + """ + GW + + """ + Guyana. + """ + GY + + """ + Haiti. + """ + HT + + """ + Heard & McDonald Islands. + """ + HM + + """ + Vatican City. + """ + VA + + """ + Honduras. + """ + HN + + """ + Hong Kong SAR. + """ + HK + + """ + Hungary. + """ + HU + + """ + Iceland. + """ + IS + + """ + India. + """ + IN + + """ + Indonesia. + """ + ID + + """ + Iran. + """ + IR + + """ + Iraq. + """ + IQ + + """ + Ireland. + """ + IE + + """ + Isle of Man. + """ + IM + + """ + Israel. + """ + IL + + """ + Italy. + """ + IT + + """ + Jamaica. + """ + JM + + """ + Japan. + """ + JP + + """ + Jersey. + """ + JE + + """ + Jordan. + """ + JO + + """ + Kazakhstan. + """ + KZ + + """ + Kenya. + """ + KE + + """ + Kiribati. + """ + KI + + """ + North Korea. + """ + KP + + """ + Kosovo. + """ + XK + + """ + Kuwait. + """ + KW + + """ + Kyrgyzstan. + """ + KG + + """ + Laos. + """ + LA + + """ + Latvia. + """ + LV + + """ + Lebanon. + """ + LB + + """ + Lesotho. + """ + LS + + """ + Liberia. + """ + LR + + """ + Libya. + """ + LY + + """ + Liechtenstein. + """ + LI + + """ + Lithuania. + """ + LT + + """ + Luxembourg. + """ + LU + + """ + Macao SAR. + """ + MO + + """ + Madagascar. + """ + MG + + """ + Malawi. + """ + MW + + """ + Malaysia. + """ + MY + + """ + Maldives. + """ + MV + + """ + Mali. + """ + ML + + """ + Malta. + """ + MT + + """ + Martinique. + """ + MQ + + """ + Mauritania. + """ + MR + + """ + Mauritius. + """ + MU + + """ + Mayotte. + """ + YT + + """ + Mexico. + """ + MX + + """ + Moldova. + """ + MD + + """ + Monaco. + """ + MC + + """ + Mongolia. + """ + MN + + """ + Montenegro. + """ + ME + + """ + Montserrat. + """ + MS + + """ + Morocco. + """ + MA + + """ + Mozambique. + """ + MZ + + """ + Myanmar (Burma). + """ + MM + + """ + Namibia. + """ + NA + + """ + Nauru. + """ + NR + + """ + Nepal. + """ + NP + + """ + Netherlands. + """ + NL + + """ + Netherlands Antilles. + """ + AN + + """ + New Caledonia. + """ + NC + + """ + New Zealand. + """ + NZ + + """ + Nicaragua. + """ + NI + + """ + Niger. + """ + NE + + """ + Nigeria. + """ + NG + + """ + Niue. + """ + NU + + """ + Norfolk Island. + """ + NF + + """ + North Macedonia. + """ + MK + + """ + Norway. + """ + NO + + """ + Oman. + """ + OM + + """ + Pakistan. + """ + PK + + """ + Palestinian Territories. + """ + PS + + """ + Panama. + """ + PA + + """ + Papua New Guinea. + """ + PG + + """ + Paraguay. + """ + PY + + """ + Peru. + """ + PE + + """ + Philippines. + """ + PH + + """ + Pitcairn Islands. + """ + PN + + """ + Poland. + """ + PL + + """ + Portugal. + """ + PT + + """ + Qatar. + """ + QA + + """ + Cameroon. + """ + CM + + """ + Réunion. + """ + RE + + """ + Romania. + """ + RO + + """ + Russia. + """ + RU + + """ + Rwanda. + """ + RW + + """ + St. Barthélemy. + """ + BL + + """ + St. Helena. + """ + SH + + """ + St. Kitts & Nevis. + """ + KN + + """ + St. Lucia. + """ + LC + + """ + St. Martin. + """ + MF + + """ + St. Pierre & Miquelon. + """ + PM + + """ + Samoa. + """ + WS + + """ + San Marino. + """ + SM + + """ + São Tomé & Príncipe. + """ + ST + + """ + Saudi Arabia. + """ + SA + + """ + Senegal. + """ + SN + + """ + Serbia. + """ + RS + + """ + Seychelles. + """ + SC + + """ + Sierra Leone. + """ + SL + + """ + Singapore. + """ + SG + + """ + Sint Maarten. + """ + SX + + """ + Slovakia. + """ + SK + + """ + Slovenia. + """ + SI + + """ + Solomon Islands. + """ + SB + + """ + Somalia. + """ + SO + + """ + South Africa. + """ + ZA + + """ + South Georgia & South Sandwich Islands. + """ + GS + + """ + South Korea. + """ + KR + + """ + South Sudan. + """ + SS + + """ + Spain. + """ + ES + + """ + Sri Lanka. + """ + LK + + """ + St. Vincent & Grenadines. + """ + VC + + """ + Sudan. + """ + SD + + """ + Suriname. + """ + SR + + """ + Svalbard & Jan Mayen. + """ + SJ + + """ + Sweden. + """ + SE + + """ + Switzerland. + """ + CH + + """ + Syria. + """ + SY + + """ + Taiwan. + """ + TW + + """ + Tajikistan. + """ + TJ + + """ + Tanzania. + """ + TZ + + """ + Thailand. + """ + TH + + """ + Timor-Leste. + """ + TL + + """ + Togo. + """ + TG + + """ + Tokelau. + """ + TK + + """ + Tonga. + """ + TO + + """ + Trinidad & Tobago. + """ + TT + + """ + Tunisia. + """ + TN + + """ + Turkey. + """ + TR + + """ + Turkmenistan. + """ + TM + + """ + Turks & Caicos Islands. + """ + TC + + """ + Tuvalu. + """ + TV + + """ + Uganda. + """ + UG + + """ + Ukraine. + """ + UA + + """ + United Arab Emirates. + """ + AE + + """ + United Kingdom. + """ + GB + + """ + United States. + """ + US + + """ + U.S. Outlying Islands. + """ + UM + + """ + Uruguay. + """ + UY + + """ + Uzbekistan. + """ + UZ + + """ + Vanuatu. + """ + VU + + """ + Venezuela. + """ + VE + + """ + Vietnam. + """ + VN + + """ + British Virgin Islands. + """ + VG + + """ + Wallis & Futuna. + """ + WF + + """ + Western Sahara. + """ + EH + + """ + Yemen. + """ + YE + + """ + Zambia. + """ + ZM + + """ + Zimbabwe. + """ + ZW +} + +""" +Credit card information used for a payment. +""" +type CreditCard { + """ + The brand of the credit card. + """ + brand: String + + """ + The expiry month of the credit card. + """ + expiryMonth: Int + + """ + The expiry year of the credit card. + """ + expiryYear: Int + + """ + The credit card's BIN number. + """ + firstDigits: String + + """ + The first name of the card holder. + """ + firstName: String + + """ + The last 4 digits of the credit card. + """ + lastDigits: String + + """ + The last name of the card holder. + """ + lastName: String + + """ + The masked credit card number with only the last 4 digits displayed. + """ + maskedNumber: String +} + +""" +Specifies the fields required to complete a checkout with +a Shopify vaulted credit card payment. +""" +input CreditCardPaymentInput { + """ + The amount of the payment. + """ + amount: Money! + + """ + A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. + """ + idempotencyKey: String! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddressInput! + + """ + The ID returned by Shopify's Card Vault. + """ + vaultId: String! + + """ + Executes the payment in test mode if possible. Defaults to `false`. + """ + test: Boolean +} + +""" +Specifies the fields required to complete a checkout with +a Shopify vaulted credit card payment. +""" +input CreditCardPaymentInputV2 { + """ + The amount and currency of the payment. + """ + paymentAmount: MoneyInput! + + """ + A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. + """ + idempotencyKey: String! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddressInput! + + """ + The ID returned by Shopify's Card Vault. + """ + vaultId: String! + + """ + Executes the payment in test mode if possible. Defaults to `false`. + """ + test: Boolean +} + +""" +The part of the image that should remain after cropping. +""" +enum CropRegion { + """ + Keep the center of the image. + """ + CENTER + + """ + Keep the top of the image. + """ + TOP + + """ + Keep the bottom of the image. + """ + BOTTOM + + """ + Keep the left of the image. + """ + LEFT + + """ + Keep the right of the image. + """ + RIGHT +} + +""" +Currency codes. +""" +enum CurrencyCode { + """ + United States Dollars (USD). + """ + USD + + """ + Euro (EUR). + """ + EUR + + """ + United Kingdom Pounds (GBP). + """ + GBP + + """ + Canadian Dollars (CAD). + """ + CAD + + """ + Afghan Afghani (AFN). + """ + AFN + + """ + Albanian Lek (ALL). + """ + ALL + + """ + Algerian Dinar (DZD). + """ + DZD + + """ + Angolan Kwanza (AOA). + """ + AOA + + """ + Argentine Pesos (ARS). + """ + ARS + + """ + Armenian Dram (AMD). + """ + AMD + + """ + Aruban Florin (AWG). + """ + AWG + + """ + Australian Dollars (AUD). + """ + AUD + + """ + Barbadian Dollar (BBD). + """ + BBD + + """ + Azerbaijani Manat (AZN). + """ + AZN + + """ + Bangladesh Taka (BDT). + """ + BDT + + """ + Bahamian Dollar (BSD). + """ + BSD + + """ + Bahraini Dinar (BHD). + """ + BHD + + """ + Burundian Franc (BIF). + """ + BIF + + """ + Belarusian Ruble (BYN). + """ + BYN + + """ + Belarusian Ruble (BYR). + """ + BYR + + """ + Belize Dollar (BZD). + """ + BZD + + """ + Bermudian Dollar (BMD). + """ + BMD + + """ + Bhutanese Ngultrum (BTN). + """ + BTN + + """ + Bosnia and Herzegovina Convertible Mark (BAM). + """ + BAM + + """ + Brazilian Real (BRL). + """ + BRL + + """ + Bolivian Boliviano (BOB). + """ + BOB + + """ + Botswana Pula (BWP). + """ + BWP + + """ + Brunei Dollar (BND). + """ + BND + + """ + Bulgarian Lev (BGN). + """ + BGN + + """ + Burmese Kyat (MMK). + """ + MMK + + """ + Cambodian Riel. + """ + KHR + + """ + Cape Verdean escudo (CVE). + """ + CVE + + """ + Cayman Dollars (KYD). + """ + KYD + + """ + Central African CFA Franc (XAF). + """ + XAF + + """ + Chilean Peso (CLP). + """ + CLP + + """ + Chinese Yuan Renminbi (CNY). + """ + CNY + + """ + Colombian Peso (COP). + """ + COP + + """ + Comorian Franc (KMF). + """ + KMF + + """ + Congolese franc (CDF). + """ + CDF + + """ + Costa Rican Colones (CRC). + """ + CRC + + """ + Croatian Kuna (HRK). + """ + HRK + + """ + Czech Koruny (CZK). + """ + CZK + + """ + Danish Kroner (DKK). + """ + DKK + + """ + Djiboutian Franc (DJF). + """ + DJF + + """ + Dominican Peso (DOP). + """ + DOP + + """ + East Caribbean Dollar (XCD). + """ + XCD + + """ + Egyptian Pound (EGP). + """ + EGP + + """ + Eritrean Nakfa (ERN). + """ + ERN + + """ + Ethiopian Birr (ETB). + """ + ETB + + """ + Falkland Islands Pounds (FKP). + """ + FKP + + """ + CFP Franc (XPF). + """ + XPF + + """ + Fijian Dollars (FJD). + """ + FJD + + """ + Gibraltar Pounds (GIP). + """ + GIP + + """ + Gambian Dalasi (GMD). + """ + GMD + + """ + Ghanaian Cedi (GHS). + """ + GHS + + """ + Guatemalan Quetzal (GTQ). + """ + GTQ + + """ + Guyanese Dollar (GYD). + """ + GYD + + """ + Georgian Lari (GEL). + """ + GEL + + """ + Guinean Franc (GNF). + """ + GNF + + """ + Haitian Gourde (HTG). + """ + HTG + + """ + Honduran Lempira (HNL). + """ + HNL + + """ + Hong Kong Dollars (HKD). + """ + HKD + + """ + Hungarian Forint (HUF). + """ + HUF + + """ + Icelandic Kronur (ISK). + """ + ISK + + """ + Indian Rupees (INR). + """ + INR + + """ + Indonesian Rupiah (IDR). + """ + IDR + + """ + Israeli New Shekel (NIS). + """ + ILS + + """ + Iranian Rial (IRR). + """ + IRR + + """ + Iraqi Dinar (IQD). + """ + IQD + + """ + Jamaican Dollars (JMD). + """ + JMD + + """ + Japanese Yen (JPY). + """ + JPY + + """ + Jersey Pound. + """ + JEP + + """ + Jordanian Dinar (JOD). + """ + JOD + + """ + Kazakhstani Tenge (KZT). + """ + KZT + + """ + Kenyan Shilling (KES). + """ + KES + + """ + Kiribati Dollar (KID). + """ + KID + + """ + Kuwaiti Dinar (KWD). + """ + KWD + + """ + Kyrgyzstani Som (KGS). + """ + KGS + + """ + Laotian Kip (LAK). + """ + LAK + + """ + Latvian Lati (LVL). + """ + LVL + + """ + Lebanese Pounds (LBP). + """ + LBP + + """ + Lesotho Loti (LSL). + """ + LSL + + """ + Liberian Dollar (LRD). + """ + LRD + + """ + Libyan Dinar (LYD). + """ + LYD + + """ + Lithuanian Litai (LTL). + """ + LTL + + """ + Malagasy Ariary (MGA). + """ + MGA + + """ + Macedonia Denar (MKD). + """ + MKD + + """ + Macanese Pataca (MOP). + """ + MOP + + """ + Malawian Kwacha (MWK). + """ + MWK + + """ + Maldivian Rufiyaa (MVR). + """ + MVR + + """ + Mauritanian Ouguiya (MRU). + """ + MRU + + """ + Mexican Pesos (MXN). + """ + MXN + + """ + Malaysian Ringgits (MYR). + """ + MYR + + """ + Mauritian Rupee (MUR). + """ + MUR + + """ + Moldovan Leu (MDL). + """ + MDL + + """ + Moroccan Dirham. + """ + MAD + + """ + Mongolian Tugrik. + """ + MNT + + """ + Mozambican Metical. + """ + MZN + + """ + Namibian Dollar. + """ + NAD + + """ + Nepalese Rupee (NPR). + """ + NPR + + """ + Netherlands Antillean Guilder. + """ + ANG + + """ + New Zealand Dollars (NZD). + """ + NZD + + """ + Nicaraguan Córdoba (NIO). + """ + NIO + + """ + Nigerian Naira (NGN). + """ + NGN + + """ + Norwegian Kroner (NOK). + """ + NOK + + """ + Omani Rial (OMR). + """ + OMR + + """ + Panamian Balboa (PAB). + """ + PAB + + """ + Pakistani Rupee (PKR). + """ + PKR + + """ + Papua New Guinean Kina (PGK). + """ + PGK + + """ + Paraguayan Guarani (PYG). + """ + PYG + + """ + Peruvian Nuevo Sol (PEN). + """ + PEN + + """ + Philippine Peso (PHP). + """ + PHP + + """ + Polish Zlotych (PLN). + """ + PLN + + """ + Qatari Rial (QAR). + """ + QAR + + """ + Romanian Lei (RON). + """ + RON + + """ + Russian Rubles (RUB). + """ + RUB + + """ + Rwandan Franc (RWF). + """ + RWF + + """ + Samoan Tala (WST). + """ + WST + + """ + Saint Helena Pounds (SHP). + """ + SHP + + """ + Saudi Riyal (SAR). + """ + SAR + + """ + Sao Tome And Principe Dobra (STD). + """ + STD + + """ + Serbian dinar (RSD). + """ + RSD + + """ + Seychellois Rupee (SCR). + """ + SCR + + """ + Sierra Leonean Leone (SLL). + """ + SLL + + """ + Singapore Dollars (SGD). + """ + SGD + + """ + Sudanese Pound (SDG). + """ + SDG + + """ + Somali Shilling (SOS). + """ + SOS + + """ + Syrian Pound (SYP). + """ + SYP + + """ + South African Rand (ZAR). + """ + ZAR + + """ + South Korean Won (KRW). + """ + KRW + + """ + South Sudanese Pound (SSP). + """ + SSP + + """ + Solomon Islands Dollar (SBD). + """ + SBD + + """ + Sri Lankan Rupees (LKR). + """ + LKR + + """ + Surinamese Dollar (SRD). + """ + SRD + + """ + Swazi Lilangeni (SZL). + """ + SZL + + """ + Swedish Kronor (SEK). + """ + SEK + + """ + Swiss Francs (CHF). + """ + CHF + + """ + Taiwan Dollars (TWD). + """ + TWD + + """ + Thai baht (THB). + """ + THB + + """ + Tajikistani Somoni (TJS). + """ + TJS + + """ + Tanzanian Shilling (TZS). + """ + TZS + + """ + Tongan Pa'anga (TOP). + """ + TOP + + """ + Trinidad and Tobago Dollars (TTD). + """ + TTD + + """ + Tunisian Dinar (TND). + """ + TND + + """ + Turkish Lira (TRY). + """ + TRY + + """ + Turkmenistani Manat (TMT). + """ + TMT + + """ + Ugandan Shilling (UGX). + """ + UGX + + """ + Ukrainian Hryvnia (UAH). + """ + UAH + + """ + United Arab Emirates Dirham (AED). + """ + AED + + """ + Uruguayan Pesos (UYU). + """ + UYU + + """ + Uzbekistan som (UZS). + """ + UZS + + """ + Vanuatu Vatu (VUV). + """ + VUV + + """ + Venezuelan Bolivares (VEF). + """ + VEF + + """ + Venezuelan Bolivares (VES). + """ + VES + + """ + Vietnamese đồng (VND). + """ + VND + + """ + West African CFA franc (XOF). + """ + XOF + + """ + Yemeni Rial (YER). + """ + YER + + """ + Zambian Kwacha (ZMW). + """ + ZMW +} + +""" +A customer represents a customer account with the shop. Customer accounts store contact information for the customer, saving logged-in customers the trouble of having to provide it at every checkout. +""" +type Customer { + """ + Indicates whether the customer has consented to be sent marketing material via email. + """ + acceptsMarketing: Boolean! + + """ + A list of addresses for the customer. + """ + addresses( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): MailingAddressConnection! + + """ + The date and time when the customer was created. + """ + createdAt: DateTime! + + """ + The customer’s default address. + """ + defaultAddress: MailingAddress + + """ + The customer’s name, email or phone number. + """ + displayName: String! + + """ + The customer’s email address. + """ + email: String + + """ + The customer’s first name. + """ + firstName: String + + """ + A unique identifier for the customer. + """ + id: ID! + + """ + The customer's most recently updated, incomplete checkout. + """ + lastIncompleteCheckout: Checkout + + """ + The customer’s last name. + """ + lastName: String + + """ + The orders associated with the customer. + """ + orders( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: OrderSortKeys = ID + + """ + Supported filter parameters: + - `processed_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): OrderConnection! + + """ + The customer’s phone number. + """ + phone: String + + """ + A comma separated list of tags that have been added to the customer. + Additional access scope required: unauthenticated_read_customer_tags. + """ + tags: [String!]! + + """ + The date and time when the customer information was updated. + """ + updatedAt: DateTime! +} + +""" +A CustomerAccessToken represents the unique token required to make modifications to the customer object. +""" +type CustomerAccessToken { + """ + The customer’s access token. + """ + accessToken: String! + + """ + The date and time when the customer access token expires. + """ + expiresAt: DateTime! +} + +""" +Specifies the input fields required to create a customer access token. +""" +input CustomerAccessTokenCreateInput { + """ + The email associated to the customer. + """ + email: String! + + """ + The login password to be used by the customer. + """ + password: String! +} + +""" +Return type for `customerAccessTokenCreate` mutation. +""" +type CustomerAccessTokenCreatePayload { + """ + The newly created customer access token object. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerAccessTokenCreateWithMultipass` mutation. +""" +type CustomerAccessTokenCreateWithMultipassPayload { + """ + An access token object associated with the customer. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! +} + +""" +Return type for `customerAccessTokenDelete` mutation. +""" +type CustomerAccessTokenDeletePayload { + """ + The destroyed access token. + """ + deletedAccessToken: String + + """ + ID of the destroyed customer access token. + """ + deletedCustomerAccessTokenId: String + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! +} + +""" +Return type for `customerAccessTokenRenew` mutation. +""" +type CustomerAccessTokenRenewPayload { + """ + The renewed customer access token object. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! +} + +""" +Return type for `customerActivateByUrl` mutation. +""" +type CustomerActivateByUrlPayload { + """ + The customer that was activated. + """ + customer: Customer + + """ + A new customer access token for the customer. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! +} + +""" +Specifies the input fields required to activate a customer. +""" +input CustomerActivateInput { + """ + The activation token required to activate the customer. + """ + activationToken: String! + + """ + New password that will be set during activation. + """ + password: String! +} + +""" +Return type for `customerActivate` mutation. +""" +type CustomerActivatePayload { + """ + The customer object. + """ + customer: Customer + + """ + A newly created customer access token object for the customer. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerAddressCreate` mutation. +""" +type CustomerAddressCreatePayload { + """ + The new customer address object. + """ + customerAddress: MailingAddress + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerAddressDelete` mutation. +""" +type CustomerAddressDeletePayload { + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + ID of the deleted customer address. + """ + deletedCustomerAddressId: String + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerAddressUpdate` mutation. +""" +type CustomerAddressUpdatePayload { + """ + The customer’s updated mailing address. + """ + customerAddress: MailingAddress + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Specifies the fields required to create a new customer. +""" +input CustomerCreateInput { + """ + The customer’s first name. + """ + firstName: String + + """ + The customer’s last name. + """ + lastName: String + + """ + The customer’s email. + """ + email: String! + + """ + A unique phone number for the customer. + + Formatted using E.164 standard. For example, _+16135551111_. + """ + phone: String + + """ + The login password used by the customer. + """ + password: String! + + """ + Indicates whether the customer has consented to be sent marketing material via email. + """ + acceptsMarketing: Boolean +} + +""" +Return type for `customerCreate` mutation. +""" +type CustomerCreatePayload { + """ + The created customer object. + """ + customer: Customer + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerDefaultAddressUpdate` mutation. +""" +type CustomerDefaultAddressUpdatePayload { + """ + The updated customer object. + """ + customer: Customer + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Possible error codes that could be returned by CustomerUserError. +""" +enum CustomerErrorCode { + """ + Input value is blank. + """ + BLANK + + """ + Input value is invalid. + """ + INVALID + + """ + Input value is already taken. + """ + TAKEN + + """ + Input value is too long. + """ + TOO_LONG + + """ + Input value is too short. + """ + TOO_SHORT + + """ + Unidentified customer. + """ + UNIDENTIFIED_CUSTOMER + + """ + Customer is disabled. + """ + CUSTOMER_DISABLED + + """ + Input password starts or ends with whitespace. + """ + PASSWORD_STARTS_OR_ENDS_WITH_WHITESPACE + + """ + Input contains HTML tags. + """ + CONTAINS_HTML_TAGS + + """ + Input contains URL. + """ + CONTAINS_URL + + """ + Invalid activation token. + """ + TOKEN_INVALID + + """ + Customer already enabled. + """ + ALREADY_ENABLED + + """ + Address does not exist. + """ + NOT_FOUND + + """ + Input email contains an invalid domain name. + """ + BAD_DOMAIN + + """ + Multipass token is not valid. + """ + INVALID_MULTIPASS_REQUEST +} + +""" +Return type for `customerRecover` mutation. +""" +type CustomerRecoverPayload { + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerResetByUrl` mutation. +""" +type CustomerResetByUrlPayload { + """ + The customer object which was reset. + """ + customer: Customer + + """ + A newly created customer access token object for the customer. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Specifies the fields required to reset a customer’s password. +""" +input CustomerResetInput { + """ + The reset token required to reset the customer’s password. + """ + resetToken: String! + + """ + New password that will be set as part of the reset password process. + """ + password: String! +} + +""" +Return type for `customerReset` mutation. +""" +type CustomerResetPayload { + """ + The customer object which was reset. + """ + customer: Customer + + """ + A newly created customer access token object for the customer. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Specifies the fields required to update the Customer information. +""" +input CustomerUpdateInput { + """ + The customer’s first name. + """ + firstName: String + + """ + The customer’s last name. + """ + lastName: String + + """ + The customer’s email. + """ + email: String + + """ + A unique phone number for the customer. + + Formatted using E.164 standard. For example, _+16135551111_. To remove the phone number, specify `null`. + """ + phone: String + + """ + The login password used by the customer. + """ + password: String + + """ + Indicates whether the customer has consented to be sent marketing material via email. + """ + acceptsMarketing: Boolean +} + +""" +Return type for `customerUpdate` mutation. +""" +type CustomerUpdatePayload { + """ + The updated customer object. + """ + customer: Customer + + """ + The newly created customer access token. If the customer's password is updated, all previous access tokens + (including the one used to perform this mutation) become invalid, and a new token is generated. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Represents an error that happens during execution of a customer mutation. +""" +type CustomerUserError implements DisplayableError { + """ + Error code to uniquely identify the error. + """ + code: CustomerErrorCode + + """ + Path to the input field which caused the error. + """ + field: [String!] + + """ + The error message. + """ + message: String! +} + +""" +An ISO-8601 encoded UTC date time string. Example value: `"2019-07-03T20:47:55Z"`. +""" +scalar DateTime + +""" +A signed decimal number, which supports arbitrary precision and is serialized as a string. Example value: `"29.99"`. +""" +scalar Decimal + +""" +Digital wallet, such as Apple Pay, which can be used for accelerated checkouts. +""" +enum DigitalWallet { + """ + Apple Pay. + """ + APPLE_PAY + + """ + Android Pay. + """ + ANDROID_PAY + + """ + Google Pay. + """ + GOOGLE_PAY + + """ + Shopify Pay. + """ + SHOPIFY_PAY +} + +""" +An amount discounting the line that has been allocated by a discount. +""" +type DiscountAllocation { + """ + Amount of discount allocated. + """ + allocatedAmount: MoneyV2! + + """ + The discount this allocated amount originated from. + """ + discountApplication: DiscountApplication! +} + +""" +Discount applications capture the intentions of a discount source at +the time of application. +""" +interface DiscountApplication { + """ + The method by which the discount's value is allocated to its entitled items. + """ + allocationMethod: DiscountApplicationAllocationMethod! + + """ + Which lines of targetType that the discount is allocated over. + """ + targetSelection: DiscountApplicationTargetSelection! + + """ + The type of line that the discount is applicable towards. + """ + targetType: DiscountApplicationTargetType! + + """ + The value of the discount application. + """ + value: PricingValue! +} + +""" +The method by which the discount's value is allocated onto its entitled lines. +""" +enum DiscountApplicationAllocationMethod { + """ + The value is spread across all entitled lines. + """ + ACROSS + + """ + The value is applied onto every entitled line. + """ + EACH + + """ + The value is specifically applied onto a particular line. + """ + ONE +} + +""" +An auto-generated type for paginating through multiple DiscountApplications. +""" +type DiscountApplicationConnection { + """ + A list of edges. + """ + edges: [DiscountApplicationEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one DiscountApplication and a cursor during pagination. +""" +type DiscountApplicationEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of DiscountApplicationEdge. + """ + node: DiscountApplication! +} + +""" +Which lines on the order that the discount is allocated over, of the type +defined by the Discount Application's target_type. +""" +enum DiscountApplicationTargetSelection { + """ + The discount is allocated onto all the lines. + """ + ALL + + """ + The discount is allocated onto only the lines it is entitled for. + """ + ENTITLED + + """ + The discount is allocated onto explicitly chosen lines. + """ + EXPLICIT +} + +""" +The type of line (i.e. line item or shipping line) on an order that the discount is applicable towards. +""" +enum DiscountApplicationTargetType { + """ + The discount applies onto line items. + """ + LINE_ITEM + + """ + The discount applies onto shipping lines. + """ + SHIPPING_LINE +} + +""" +Discount code applications capture the intentions of a discount code at +the time that it is applied. +""" +type DiscountCodeApplication implements DiscountApplication { + """ + The method by which the discount's value is allocated to its entitled items. + """ + allocationMethod: DiscountApplicationAllocationMethod! + + """ + Specifies whether the discount code was applied successfully. + """ + applicable: Boolean! + + """ + The string identifying the discount code that was used at the time of application. + """ + code: String! + + """ + Which lines of targetType that the discount is allocated over. + """ + targetSelection: DiscountApplicationTargetSelection! + + """ + The type of line that the discount is applicable towards. + """ + targetType: DiscountApplicationTargetType! + + """ + The value of the discount application. + """ + value: PricingValue! +} + +""" +Represents an error in the input of a mutation. +""" +interface DisplayableError { + """ + Path to the input field which caused the error. + """ + field: [String!] + + """ + The error message. + """ + message: String! +} + +""" +Represents a web address. +""" +type Domain { + """ + The host name of the domain (eg: `example.com`). + """ + host: String! + + """ + Whether SSL is enabled or not. + """ + sslEnabled: Boolean! + + """ + The URL of the domain (eg: `https://example.com`). + """ + url: URL! +} + +""" +Represents a video hosted outside of Shopify. +""" +type ExternalVideo implements Node & Media { + """ + A word or phrase to share the nature or contents of a media. + """ + alt: String + + """ + The URL. + """ + embeddedUrl: URL! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The media content type. + """ + mediaContentType: MediaContentType! + + """ + The preview image for the media. + """ + previewImage: Image +} + +""" +Represents a single fulfillment in an order. +""" +type Fulfillment { + """ + List of the fulfillment's line items. + """ + fulfillmentLineItems( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): FulfillmentLineItemConnection! + + """ + The name of the tracking company. + """ + trackingCompany: String + + """ + Tracking information associated with the fulfillment, + such as the tracking number and tracking URL. + """ + trackingInfo( + """ + Truncate the array result to this size. + """ + first: Int + ): [FulfillmentTrackingInfo!]! +} + +""" +Represents a single line item in a fulfillment. There is at most one fulfillment line item for each order line item. +""" +type FulfillmentLineItem { + """ + The associated order's line item. + """ + lineItem: OrderLineItem! + + """ + The amount fulfilled in this fulfillment. + """ + quantity: Int! +} + +""" +An auto-generated type for paginating through multiple FulfillmentLineItems. +""" +type FulfillmentLineItemConnection { + """ + A list of edges. + """ + edges: [FulfillmentLineItemEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one FulfillmentLineItem and a cursor during pagination. +""" +type FulfillmentLineItemEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of FulfillmentLineItemEdge. + """ + node: FulfillmentLineItem! +} + +""" +Tracking information associated with the fulfillment. +""" +type FulfillmentTrackingInfo { + """ + The tracking number of the fulfillment. + """ + number: String + + """ + The URL to track the fulfillment. + """ + url: URL +} + +""" +A string containing HTML code. Example value: `"<p>Grey cotton knit sweater.</p>"`. +""" +scalar HTML + +""" +Represents information about the metafields associated to the specified resource. +""" +interface HasMetafields { + """ + The metafield associated with the resource. + """ + metafield( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String! + + """ + Identifier for the metafield (maximum of 30 characters). + """ + key: String! + ): Metafield + + """ + A paginated list of metafields associated with the resource. + """ + metafields( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): MetafieldConnection! +} + +""" +Represents an image resource. +""" +type Image { + """ + A word or phrase to share the nature or contents of an image. + """ + altText: String + + """ + The original height of the image in pixels. Returns `null` if the image is not hosted by Shopify. + """ + height: Int + + """ + A unique identifier for the image. + """ + id: ID + + """ + The location of the original image as a URL. + + If there are any existing transformations in the original source URL, they will remain and not be stripped. + """ + originalSrc: URL! + + """ + The location of the image as a URL. + """ + src: URL! + @deprecated( + reason: "Previously an image had a single `src` field. This could either return the original image\nlocation or a URL that contained transformations such as sizing or scale.\n\nThese transformations were specified by arguments on the parent field.\n\nNow an image has two distinct URL fields: `originalSrc` and `transformedSrc`.\n\n* `originalSrc` - the original unmodified image URL\n* `transformedSrc` - the image URL with the specified transformations included\n\nTo migrate to the new fields, image transformations should be moved from the parent field to `transformedSrc`.\n\nBefore:\n```graphql\n{\n shop {\n productImages(maxWidth: 200, scale: 2) {\n edges {\n node {\n src\n }\n }\n }\n }\n}\n```\n\nAfter:\n```graphql\n{\n shop {\n productImages {\n edges {\n node {\n transformedSrc(maxWidth: 200, scale: 2)\n }\n }\n }\n }\n}\n```\n" + ) + + """ + The location of the transformed image as a URL. + + All transformation arguments are considered "best-effort". If they can be applied to an image, they will be. + Otherwise any transformations which an image type does not support will be ignored. + """ + transformedSrc( + """ + Image width in pixels between 1 and 5760. + """ + maxWidth: Int + + """ + Image height in pixels between 1 and 5760. + """ + maxHeight: Int + + """ + Crops the image according to the specified region. + """ + crop: CropRegion + + """ + Image size multiplier for high-resolution retina displays. Must be between 1 and 3. + """ + scale: Int = 1 + + """ + Best effort conversion of image into content type (SVG -> PNG, Anything -> JGP, Anything -> WEBP are supported). + """ + preferredContentType: ImageContentType + ): URL! + + """ + The original width of the image in pixels. Returns `null` if the image is not hosted by Shopify. + """ + width: Int +} + +""" +An auto-generated type for paginating through multiple Images. +""" +type ImageConnection { + """ + A list of edges. + """ + edges: [ImageEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +List of supported image content types. +""" +enum ImageContentType { + """ + A PNG image. + """ + PNG + + """ + A JPG image. + """ + JPG + + """ + A WEBP image. + """ + WEBP +} + +""" +An auto-generated type which holds one Image and a cursor during pagination. +""" +type ImageEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ImageEdge. + """ + node: Image! +} + +""" +Represents a mailing address for customers and shipping. +""" +type MailingAddress implements Node { + """ + The first line of the address. Typically the street address or PO Box number. + """ + address1: String + + """ + The second line of the address. Typically the number of the apartment, suite, or unit. + """ + address2: String + + """ + The name of the city, district, village, or town. + """ + city: String + + """ + The name of the customer's company or organization. + """ + company: String + + """ + The name of the country. + """ + country: String + + """ + The two-letter code for the country of the address. + + For example, US. + """ + countryCode: String @deprecated(reason: "Use `countryCodeV2` instead") + + """ + The two-letter code for the country of the address. + + For example, US. + """ + countryCodeV2: CountryCode + + """ + The first name of the customer. + """ + firstName: String + + """ + A formatted version of the address, customized by the provided arguments. + """ + formatted( + """ + Whether to include the customer's name in the formatted address. + """ + withName: Boolean = false + + """ + Whether to include the customer's company in the formatted address. + """ + withCompany: Boolean = true + ): [String!]! + + """ + A comma-separated list of the values for city, province, and country. + """ + formattedArea: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + The last name of the customer. + """ + lastName: String + + """ + The latitude coordinate of the customer address. + """ + latitude: Float + + """ + The longitude coordinate of the customer address. + """ + longitude: Float + + """ + The full name of the customer, based on firstName and lastName. + """ + name: String + + """ + A unique phone number for the customer. + + Formatted using E.164 standard. For example, _+16135551111_. + """ + phone: String + + """ + The region of the address, such as the province, state, or district. + """ + province: String + + """ + The two-letter code for the region. + + For example, ON. + """ + provinceCode: String + + """ + The zip or postal code of the address. + """ + zip: String +} + +""" +An auto-generated type for paginating through multiple MailingAddresses. +""" +type MailingAddressConnection { + """ + A list of edges. + """ + edges: [MailingAddressEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one MailingAddress and a cursor during pagination. +""" +type MailingAddressEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of MailingAddressEdge. + """ + node: MailingAddress! +} + +""" +Specifies the fields accepted to create or update a mailing address. +""" +input MailingAddressInput { + """ + The first line of the address. Typically the street address or PO Box number. + """ + address1: String + + """ + The second line of the address. Typically the number of the apartment, suite, or unit. + """ + address2: String + + """ + The name of the city, district, village, or town. + """ + city: String + + """ + The name of the customer's company or organization. + """ + company: String + + """ + The name of the country. + """ + country: String + + """ + The first name of the customer. + """ + firstName: String + + """ + The last name of the customer. + """ + lastName: String + + """ + A unique phone number for the customer. + + Formatted using E.164 standard. For example, _+16135551111_. + """ + phone: String + + """ + The region of the address, such as the province, state, or district. + """ + province: String + + """ + The zip or postal code of the address. + """ + zip: String +} + +""" +Manual discount applications capture the intentions of a discount that was manually created. +""" +type ManualDiscountApplication implements DiscountApplication { + """ + The method by which the discount's value is allocated to its entitled items. + """ + allocationMethod: DiscountApplicationAllocationMethod! + + """ + The description of the application. + """ + description: String + + """ + Which lines of targetType that the discount is allocated over. + """ + targetSelection: DiscountApplicationTargetSelection! + + """ + The type of line that the discount is applicable towards. + """ + targetType: DiscountApplicationTargetType! + + """ + The title of the application. + """ + title: String! + + """ + The value of the discount application. + """ + value: PricingValue! +} + +""" +Represents a media interface. +""" +interface Media { + """ + A word or phrase to share the nature or contents of a media. + """ + alt: String + + """ + The media content type. + """ + mediaContentType: MediaContentType! + + """ + The preview image for the media. + """ + previewImage: Image +} + +""" +An auto-generated type for paginating through multiple Media. +""" +type MediaConnection { + """ + A list of edges. + """ + edges: [MediaEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +The possible content types for a media object. +""" +enum MediaContentType { + """ + An externally hosted video. + """ + EXTERNAL_VIDEO + + """ + A Shopify hosted image. + """ + IMAGE + + """ + A 3d model. + """ + MODEL_3D + + """ + A Shopify hosted video. + """ + VIDEO +} + +""" +An auto-generated type which holds one Media and a cursor during pagination. +""" +type MediaEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of MediaEdge. + """ + node: Media! +} + +""" +Represents a Shopify hosted image. +""" +type MediaImage implements Node & Media { + """ + A word or phrase to share the nature or contents of a media. + """ + alt: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + The image for the media. + """ + image: Image + + """ + The media content type. + """ + mediaContentType: MediaContentType! + + """ + The preview image for the media. + """ + previewImage: Image +} + +""" +Metafields represent custom metadata attached to a resource. Metafields can be sorted into namespaces and are +comprised of keys, values, and value types. +""" +type Metafield implements Node { + """ + The date and time when the storefront metafield was created. + """ + createdAt: DateTime! + + """ + The description of a metafield. + """ + description: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + The key name for a metafield. + """ + key: String! + + """ + The namespace for a metafield. + """ + namespace: String! + + """ + The parent object that the metafield belongs to. + """ + parentResource: MetafieldParentResource! + + """ + The date and time when the storefront metafield was updated. + """ + updatedAt: DateTime! + + """ + The value of a metafield. + """ + value: String! + + """ + Represents the metafield value type. + """ + valueType: MetafieldValueType! +} + +""" +An auto-generated type for paginating through multiple Metafields. +""" +type MetafieldConnection { + """ + A list of edges. + """ + edges: [MetafieldEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Metafield and a cursor during pagination. +""" +type MetafieldEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of MetafieldEdge. + """ + node: Metafield! +} + +""" +A resource that the metafield belongs to. +""" +union MetafieldParentResource = Product | ProductVariant + +""" +Metafield value types. +""" +enum MetafieldValueType { + """ + A string metafield. + """ + STRING + + """ + An integer metafield. + """ + INTEGER + + """ + A json string metafield. + """ + JSON_STRING +} + +""" +Represents a Shopify hosted 3D model. +""" +type Model3d implements Node & Media { + """ + A word or phrase to share the nature or contents of a media. + """ + alt: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + The media content type. + """ + mediaContentType: MediaContentType! + + """ + The preview image for the media. + """ + previewImage: Image + + """ + The sources for a 3d model. + """ + sources: [Model3dSource!]! +} + +""" +Represents a source for a Shopify hosted 3d model. +""" +type Model3dSource { + """ + The filesize of the 3d model. + """ + filesize: Int! + + """ + The format of the 3d model. + """ + format: String! + + """ + The MIME type of the 3d model. + """ + mimeType: String! + + """ + The URL of the 3d model. + """ + url: String! +} + +""" +A monetary value string. Example value: `"100.57"`. +""" +scalar Money + +""" +Specifies the fields for a monetary value with currency. +""" +input MoneyInput { + """ + Decimal money amount. + """ + amount: Decimal! + + """ + Currency of the money. + """ + currencyCode: CurrencyCode! +} + +""" +A monetary value with currency. + +To format currencies, combine this type's amount and currencyCode fields with your client's locale. + +For example, in JavaScript you could use Intl.NumberFormat: + +```js +new Intl.NumberFormat(locale, { + style: 'currency', + currency: currencyCode +}).format(amount); +``` + +Other formatting libraries include: + +* iOS - [NumberFormatter](https://developer.apple.com/documentation/foundation/numberformatter) +* Android - [NumberFormat](https://developer.android.com/reference/java/text/NumberFormat.html) +* PHP - [NumberFormatter](http://php.net/manual/en/class.numberformatter.php) + +For a more general solution, the [Unicode CLDR number formatting database] is available with many implementations +(such as [TwitterCldr](https://github.com/twitter/twitter-cldr-rb)). +""" +type MoneyV2 { + """ + Decimal money amount. + """ + amount: Decimal! + + """ + Currency of the money. + """ + currencyCode: CurrencyCode! +} + +""" +An auto-generated type for paginating through multiple MoneyV2s. +""" +type MoneyV2Connection { + """ + A list of edges. + """ + edges: [MoneyV2Edge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one MoneyV2 and a cursor during pagination. +""" +type MoneyV2Edge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of MoneyV2Edge. + """ + node: MoneyV2! +} + +""" +The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. +""" +type Mutation { + """ + Updates the attributes of a checkout. + """ + checkoutAttributesUpdate( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The fields used to update a checkout's attributes. + """ + input: CheckoutAttributesUpdateInput! + ): CheckoutAttributesUpdatePayload + @deprecated(reason: "Use `checkoutAttributesUpdateV2` instead") + + """ + Updates the attributes of a checkout. + """ + checkoutAttributesUpdateV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The checkout attributes to update. + """ + input: CheckoutAttributesUpdateV2Input! + ): CheckoutAttributesUpdateV2Payload + + """ + Completes a checkout without providing payment information. You can use this mutation for free items or items whose purchase price is covered by a gift card. + """ + checkoutCompleteFree( + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutCompleteFreePayload + + """ + Completes a checkout using a credit card token from Shopify's Vault. + """ + checkoutCompleteWithCreditCard( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The credit card info to apply as a payment. + """ + payment: CreditCardPaymentInput! + ): CheckoutCompleteWithCreditCardPayload + @deprecated(reason: "Use `checkoutCompleteWithCreditCardV2` instead") + + """ + Completes a checkout using a credit card token from Shopify's card vault. Before you can complete checkouts using CheckoutCompleteWithCreditCardV2, you need to [_request payment processing_](https://help.shopify.com/api/guides/sales-channel-sdk/getting-started#request-payment-processing). + """ + checkoutCompleteWithCreditCardV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The credit card info to apply as a payment. + """ + payment: CreditCardPaymentInputV2! + ): CheckoutCompleteWithCreditCardV2Payload + + """ + Completes a checkout with a tokenized payment. + """ + checkoutCompleteWithTokenizedPayment( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The info to apply as a tokenized payment. + """ + payment: TokenizedPaymentInput! + ): CheckoutCompleteWithTokenizedPaymentPayload + @deprecated(reason: "Use `checkoutCompleteWithTokenizedPaymentV2` instead") + + """ + Completes a checkout with a tokenized payment. + """ + checkoutCompleteWithTokenizedPaymentV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The info to apply as a tokenized payment. + """ + payment: TokenizedPaymentInputV2! + ): CheckoutCompleteWithTokenizedPaymentV2Payload + @deprecated(reason: "Use `checkoutCompleteWithTokenizedPaymentV3` instead") + + """ + Completes a checkout with a tokenized payment. + """ + checkoutCompleteWithTokenizedPaymentV3( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The info to apply as a tokenized payment. + """ + payment: TokenizedPaymentInputV3! + ): CheckoutCompleteWithTokenizedPaymentV3Payload + + """ + Creates a new checkout. + """ + checkoutCreate( + """ + The fields used to create a checkout. + """ + input: CheckoutCreateInput! + ): CheckoutCreatePayload + + """ + Associates a customer to the checkout. + """ + checkoutCustomerAssociate( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The customer access token of the customer to associate. + """ + customerAccessToken: String! + ): CheckoutCustomerAssociatePayload + @deprecated(reason: "Use `checkoutCustomerAssociateV2` instead") + + """ + Associates a customer to the checkout. + """ + checkoutCustomerAssociateV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The customer access token of the customer to associate. + """ + customerAccessToken: String! + ): CheckoutCustomerAssociateV2Payload + + """ + Disassociates the current checkout customer from the checkout. + """ + checkoutCustomerDisassociate( + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutCustomerDisassociatePayload + @deprecated(reason: "Use `checkoutCustomerDisassociateV2` instead") + + """ + Disassociates the current checkout customer from the checkout. + """ + checkoutCustomerDisassociateV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutCustomerDisassociateV2Payload + + """ + Applies a discount to an existing checkout using a discount code. + """ + checkoutDiscountCodeApply( + """ + The discount code to apply to the checkout. + """ + discountCode: String! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutDiscountCodeApplyPayload + @deprecated(reason: "Use `checkoutDiscountCodeApplyV2` instead") + + """ + Applies a discount to an existing checkout using a discount code. + """ + checkoutDiscountCodeApplyV2( + """ + The discount code to apply to the checkout. + """ + discountCode: String! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutDiscountCodeApplyV2Payload + + """ + Removes the applied discount from an existing checkout. + """ + checkoutDiscountCodeRemove( + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutDiscountCodeRemovePayload + + """ + Updates the email on an existing checkout. + """ + checkoutEmailUpdate( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The email to update the checkout with. + """ + email: String! + ): CheckoutEmailUpdatePayload + @deprecated(reason: "Use `checkoutEmailUpdateV2` instead") + + """ + Updates the email on an existing checkout. + """ + checkoutEmailUpdateV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The email to update the checkout with. + """ + email: String! + ): CheckoutEmailUpdateV2Payload + + """ + Applies a gift card to an existing checkout using a gift card code. This will replace all currently applied gift cards. + """ + checkoutGiftCardApply( + """ + The code of the gift card to apply on the checkout. + """ + giftCardCode: String! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutGiftCardApplyPayload + @deprecated(reason: "Use `checkoutGiftCardsAppend` instead") + + """ + Removes an applied gift card from the checkout. + """ + checkoutGiftCardRemove( + """ + The ID of the Applied Gift Card to remove from the Checkout. + """ + appliedGiftCardId: ID! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutGiftCardRemovePayload + @deprecated(reason: "Use `checkoutGiftCardRemoveV2` instead") + + """ + Removes an applied gift card from the checkout. + """ + checkoutGiftCardRemoveV2( + """ + The ID of the Applied Gift Card to remove from the Checkout. + """ + appliedGiftCardId: ID! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutGiftCardRemoveV2Payload + + """ + Appends gift cards to an existing checkout. + """ + checkoutGiftCardsAppend( + """ + A list of gift card codes to append to the checkout. + """ + giftCardCodes: [String!]! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutGiftCardsAppendPayload + + """ + Adds a list of line items to a checkout. + """ + checkoutLineItemsAdd( + """ + A list of line item objects to add to the checkout. + """ + lineItems: [CheckoutLineItemInput!]! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutLineItemsAddPayload + + """ + Removes line items from an existing checkout. + """ + checkoutLineItemsRemove( + """ + The checkout on which to remove line items. + """ + checkoutId: ID! + + """ + Line item ids to remove. + """ + lineItemIds: [ID!]! + ): CheckoutLineItemsRemovePayload + + """ + Sets a list of line items to a checkout. + """ + checkoutLineItemsReplace( + """ + A list of line item objects to set on the checkout. + """ + lineItems: [CheckoutLineItemInput!]! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutLineItemsReplacePayload + + """ + Updates line items on a checkout. + """ + checkoutLineItemsUpdate( + """ + The checkout on which to update line items. + """ + checkoutId: ID! + + """ + Line items to update. + """ + lineItems: [CheckoutLineItemUpdateInput!]! + ): CheckoutLineItemsUpdatePayload + + """ + Updates the shipping address of an existing checkout. + """ + checkoutShippingAddressUpdate( + """ + The shipping address to where the line items will be shipped. + """ + shippingAddress: MailingAddressInput! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutShippingAddressUpdatePayload + @deprecated(reason: "Use `checkoutShippingAddressUpdateV2` instead") + + """ + Updates the shipping address of an existing checkout. + """ + checkoutShippingAddressUpdateV2( + """ + The shipping address to where the line items will be shipped. + """ + shippingAddress: MailingAddressInput! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutShippingAddressUpdateV2Payload + + """ + Updates the shipping lines on an existing checkout. + """ + checkoutShippingLineUpdate( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + A unique identifier to a Checkout’s shipping provider, price, and title combination, enabling the customer to select the availableShippingRates. + """ + shippingRateHandle: String! + ): CheckoutShippingLineUpdatePayload + + """ + Creates a customer access token. + The customer access token is required to modify the customer object in any way. + """ + customerAccessTokenCreate( + """ + The fields used to create a customer access token. + """ + input: CustomerAccessTokenCreateInput! + ): CustomerAccessTokenCreatePayload + + """ + Creates a customer access token using a multipass token instead of email and password. + A customer record is created if customer does not exist. If a customer record already + exists but the record is disabled, then it's enabled. + """ + customerAccessTokenCreateWithMultipass( + """ + A valid multipass token to be authenticated. + """ + multipassToken: String! + ): CustomerAccessTokenCreateWithMultipassPayload + + """ + Permanently destroys a customer access token. + """ + customerAccessTokenDelete( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + ): CustomerAccessTokenDeletePayload + + """ + Renews a customer access token. + + Access token renewal must happen *before* a token expires. + If a token has already expired, a new one should be created instead via `customerAccessTokenCreate`. + """ + customerAccessTokenRenew( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + ): CustomerAccessTokenRenewPayload + + """ + Activates a customer. + """ + customerActivate( + """ + Specifies the customer to activate. + """ + id: ID! + + """ + The fields used to activate a customer. + """ + input: CustomerActivateInput! + ): CustomerActivatePayload + + """ + Activates a customer with the activation url received from `customerCreate`. + """ + customerActivateByUrl( + """ + The customer activation URL. + """ + activationUrl: URL! + + """ + A new password set during activation. + """ + password: String! + ): CustomerActivateByUrlPayload + + """ + Creates a new address for a customer. + """ + customerAddressCreate( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + + """ + The customer mailing address to create. + """ + address: MailingAddressInput! + ): CustomerAddressCreatePayload + + """ + Permanently deletes the address of an existing customer. + """ + customerAddressDelete( + """ + Specifies the address to delete. + """ + id: ID! + + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + ): CustomerAddressDeletePayload + + """ + Updates the address of an existing customer. + """ + customerAddressUpdate( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + + """ + Specifies the customer address to update. + """ + id: ID! + + """ + The customer’s mailing address. + """ + address: MailingAddressInput! + ): CustomerAddressUpdatePayload + + """ + Creates a new customer. + """ + customerCreate( + """ + The fields used to create a new customer. + """ + input: CustomerCreateInput! + ): CustomerCreatePayload + + """ + Updates the default address of an existing customer. + """ + customerDefaultAddressUpdate( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + + """ + ID of the address to set as the new default for the customer. + """ + addressId: ID! + ): CustomerDefaultAddressUpdatePayload + + """ + Sends a reset password email to the customer, as the first step in the reset password process. + """ + customerRecover( + """ + The email address of the customer to recover. + """ + email: String! + ): CustomerRecoverPayload + + """ + Resets a customer’s password with a token received from `CustomerRecover`. + """ + customerReset( + """ + Specifies the customer to reset. + """ + id: ID! + + """ + The fields used to reset a customer’s password. + """ + input: CustomerResetInput! + ): CustomerResetPayload + + """ + Resets a customer’s password with the reset password url received from `CustomerRecover`. + """ + customerResetByUrl( + """ + The customer's reset password url. + """ + resetUrl: URL! + + """ + New password that will be set as part of the reset password process. + """ + password: String! + ): CustomerResetByUrlPayload + + """ + Updates an existing customer. + """ + customerUpdate( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + + """ + The customer object input. + """ + customer: CustomerUpdateInput! + ): CustomerUpdatePayload +} + +""" +An object with an ID to support global identification. +""" +interface Node { + """ + Globally unique identifier. + """ + id: ID! +} + +""" +An order is a customer’s completed request to purchase one or more products from a shop. An order is created when a customer completes the checkout process, during which time they provides an email address, billing address and payment information. +""" +type Order implements Node { + """ + The reason for the order's cancellation. Returns `null` if the order wasn't canceled. + """ + cancelReason: OrderCancelReason + + """ + The date and time when the order was canceled. Returns null if the order wasn't canceled. + """ + canceledAt: DateTime + + """ + The code of the currency used for the payment. + """ + currencyCode: CurrencyCode! + + """ + The subtotal of line items and their discounts, excluding line items that have been removed. Does not contain order-level discounts, duties, shipping costs, or shipping discounts. Taxes are not included unless the order is a taxes-included order. + """ + currentSubtotalPrice: MoneyV2! + + """ + The total amount of the order, including duties, taxes and discounts, minus amounts for line items that have been removed. + """ + currentTotalPrice: MoneyV2! + + """ + The total of all taxes applied to the order, excluding taxes for returned line items. + """ + currentTotalTax: MoneyV2! + + """ + The locale code in which this specific order happened. + """ + customerLocale: String + + """ + The unique URL that the customer can use to access the order. + """ + customerUrl: URL + + """ + Discounts that have been applied on the order. + """ + discountApplications( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): DiscountApplicationConnection! + + """ + Whether the order has had any edits applied or not. + """ + edited: Boolean! + + """ + The customer's email address. + """ + email: String + + """ + The financial status of the order. + """ + financialStatus: OrderFinancialStatus + + """ + The fulfillment status for the order. + """ + fulfillmentStatus: OrderFulfillmentStatus! + + """ + Globally unique identifier. + """ + id: ID! + + """ + List of the order’s line items. + """ + lineItems( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): OrderLineItemConnection! + + """ + Unique identifier for the order that appears on the order. + For example, _#1000_ or _Store1001. + """ + name: String! + + """ + A unique numeric identifier for the order for use by shop owner and customer. + """ + orderNumber: Int! + + """ + The total price of the order before any applied edits. + """ + originalTotalPrice: MoneyV2! + + """ + The customer's phone number for receiving SMS notifications. + """ + phone: String + + """ + The date and time when the order was imported. + This value can be set to dates in the past when importing from other systems. + If no value is provided, it will be auto-generated based on current date and time. + """ + processedAt: DateTime! + + """ + The address to where the order will be shipped. + """ + shippingAddress: MailingAddress + + """ + The discounts that have been allocated onto the shipping line by discount applications. + """ + shippingDiscountAllocations: [DiscountAllocation!]! + + """ + The unique URL for the order's status page. + """ + statusUrl: URL! + + """ + Price of the order before shipping and taxes. + """ + subtotalPrice: Money @deprecated(reason: "Use `subtotalPriceV2` instead") + + """ + Price of the order before duties, shipping and taxes. + """ + subtotalPriceV2: MoneyV2 + + """ + List of the order’s successful fulfillments. + """ + successfulFulfillments( + """ + Truncate the array result to this size. + """ + first: Int + ): [Fulfillment!] + + """ + The sum of all the prices of all the items in the order, taxes and discounts included (must be positive). + """ + totalPrice: Money! @deprecated(reason: "Use `totalPriceV2` instead") + + """ + The sum of all the prices of all the items in the order, duties, taxes and discounts included (must be positive). + """ + totalPriceV2: MoneyV2! + + """ + The total amount that has been refunded. + """ + totalRefunded: Money! @deprecated(reason: "Use `totalRefundedV2` instead") + + """ + The total amount that has been refunded. + """ + totalRefundedV2: MoneyV2! + + """ + The total cost of shipping. + """ + totalShippingPrice: Money! + @deprecated(reason: "Use `totalShippingPriceV2` instead") + + """ + The total cost of shipping. + """ + totalShippingPriceV2: MoneyV2! + + """ + The total cost of taxes. + """ + totalTax: Money @deprecated(reason: "Use `totalTaxV2` instead") + + """ + The total cost of taxes. + """ + totalTaxV2: MoneyV2 +} + +""" +Represents the reason for the order's cancellation. +""" +enum OrderCancelReason { + """ + The customer wanted to cancel the order. + """ + CUSTOMER + + """ + The order was fraudulent. + """ + FRAUD + + """ + There was insufficient inventory. + """ + INVENTORY + + """ + Payment was declined. + """ + DECLINED + + """ + The order was canceled for an unlisted reason. + """ + OTHER +} + +""" +An auto-generated type for paginating through multiple Orders. +""" +type OrderConnection { + """ + A list of edges. + """ + edges: [OrderEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Order and a cursor during pagination. +""" +type OrderEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of OrderEdge. + """ + node: Order! +} + +""" +Represents the order's current financial status. +""" +enum OrderFinancialStatus { + """ + Displayed as **Pending**. + """ + PENDING + + """ + Displayed as **Authorized**. + """ + AUTHORIZED + + """ + Displayed as **Partially paid**. + """ + PARTIALLY_PAID + + """ + Displayed as **Partially refunded**. + """ + PARTIALLY_REFUNDED + + """ + Displayed as **Voided**. + """ + VOIDED + + """ + Displayed as **Paid**. + """ + PAID + + """ + Displayed as **Refunded**. + """ + REFUNDED +} + +""" +Represents the order's current fulfillment status. +""" +enum OrderFulfillmentStatus { + """ + Displayed as **Unfulfilled**. + """ + UNFULFILLED + + """ + Displayed as **Partially fulfilled**. + """ + PARTIALLY_FULFILLED + + """ + Displayed as **Fulfilled**. + """ + FULFILLED + + """ + Displayed as **Restocked**. + """ + RESTOCKED + + """ + Displayed as **Pending fulfillment**. + """ + PENDING_FULFILLMENT + + """ + Displayed as **Open**. + """ + OPEN + + """ + Displayed as **In progress**. + """ + IN_PROGRESS + + """ + Displayed as **Scheduled**. + """ + SCHEDULED +} + +""" +Represents a single line in an order. There is one line item for each distinct product variant. +""" +type OrderLineItem { + """ + The number of entries associated to the line item minus the items that have been removed. + """ + currentQuantity: Int! + + """ + List of custom attributes associated to the line item. + """ + customAttributes: [Attribute!]! + + """ + The discounts that have been allocated onto the order line item by discount applications. + """ + discountAllocations: [DiscountAllocation!]! + + """ + The total price of the line item, including discounts, and displayed in the presentment currency. + """ + discountedTotalPrice: MoneyV2! + + """ + The total price of the line item, not including any discounts. The total price is calculated using the original unit price multiplied by the quantity, and it is displayed in the presentment currency. + """ + originalTotalPrice: MoneyV2! + + """ + The number of products variants associated to the line item. + """ + quantity: Int! + + """ + The title of the product combined with title of the variant. + """ + title: String! + + """ + The product variant object associated to the line item. + """ + variant: ProductVariant +} + +""" +An auto-generated type for paginating through multiple OrderLineItems. +""" +type OrderLineItemConnection { + """ + A list of edges. + """ + edges: [OrderLineItemEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one OrderLineItem and a cursor during pagination. +""" +type OrderLineItemEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of OrderLineItemEdge. + """ + node: OrderLineItem! +} + +""" +The set of valid sort keys for the Order query. +""" +enum OrderSortKeys { + """ + Sort by the `processed_at` value. + """ + PROCESSED_AT + + """ + Sort by the `total_price` value. + """ + TOTAL_PRICE + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +Shopify merchants can create pages to hold static HTML content. Each Page object represents a custom page on the online store. +""" +type Page implements Node { + """ + The description of the page, complete with HTML formatting. + """ + body: HTML! + + """ + Summary of the page body. + """ + bodySummary: String! + + """ + The timestamp of the page creation. + """ + createdAt: DateTime! + + """ + A human-friendly unique string for the page automatically generated from its title. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The page's SEO information. + """ + seo: SEO + + """ + The title of the page. + """ + title: String! + + """ + The timestamp of the latest page update. + """ + updatedAt: DateTime! + + """ + The url pointing to the page accessible from the web. + """ + url: URL! +} + +""" +An auto-generated type for paginating through multiple Pages. +""" +type PageConnection { + """ + A list of edges. + """ + edges: [PageEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Page and a cursor during pagination. +""" +type PageEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of PageEdge. + """ + node: Page! +} + +""" +Information about pagination in a connection. +""" +type PageInfo { + """ + Indicates if there are more pages to fetch. + """ + hasNextPage: Boolean! + + """ + Indicates if there are any pages prior to the current page. + """ + hasPreviousPage: Boolean! +} + +""" +The set of valid sort keys for the Page query. +""" +enum PageSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `updated_at` value. + """ + UPDATED_AT + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +A payment applied to a checkout. +""" +type Payment implements Node { + """ + The amount of the payment. + """ + amount: Money! @deprecated(reason: "Use `amountV2` instead") + + """ + The amount of the payment. + """ + amountV2: MoneyV2! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddress + + """ + The checkout to which the payment belongs. + """ + checkout: Checkout! + + """ + The credit card used for the payment in the case of direct payments. + """ + creditCard: CreditCard + + """ + A message describing a processing error during asynchronous processing. + """ + errorMessage: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + A client-side generated token to identify a payment and perform idempotent operations. + """ + idempotencyKey: String + + """ + The URL where the customer needs to be redirected so they can complete the 3D Secure payment flow. + """ + nextActionUrl: URL + + """ + Whether or not the payment is still processing asynchronously. + """ + ready: Boolean! + + """ + A flag to indicate if the payment is to be done in test mode for gateways that support it. + """ + test: Boolean! + + """ + The actual transaction recorded by Shopify after having processed the payment with the gateway. + """ + transaction: Transaction +} + +""" +Settings related to payments. +""" +type PaymentSettings { + """ + List of the card brands which the shop accepts. + """ + acceptedCardBrands: [CardBrand!]! + + """ + The url pointing to the endpoint to vault credit cards. + """ + cardVaultUrl: URL! + + """ + The country where the shop is located. + """ + countryCode: CountryCode! + + """ + The three-letter code for the shop's primary currency. + """ + currencyCode: CurrencyCode! + + """ + A list of enabled currencies (ISO 4217 format) that the shop accepts. Merchants can enable currencies from their Shopify Payments settings in the Shopify admin. + """ + enabledPresentmentCurrencies: [CurrencyCode!]! + + """ + The shop’s Shopify Payments account id. + """ + shopifyPaymentsAccountId: String + + """ + List of the digital wallets which the shop supports. + """ + supportedDigitalWallets: [DigitalWallet!]! +} + +""" +The valid values for the types of payment token. +""" +enum PaymentTokenType { + """ + Apple Pay token type. + """ + APPLE_PAY + + """ + Vault payment token type. + """ + VAULT + + """ + Shopify Pay token type. + """ + SHOPIFY_PAY + + """ + Google Pay token type. + """ + GOOGLE_PAY +} + +""" +The value of the percentage pricing object. +""" +type PricingPercentageValue { + """ + The percentage value of the object. + """ + percentage: Float! +} + +""" +The price value (fixed or percentage) for a discount application. +""" +union PricingValue = MoneyV2 | PricingPercentageValue + +""" +A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. +For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). +""" +type Product implements Node & HasMetafields { + """ + Indicates if at least one product variant is available for sale. + """ + availableForSale: Boolean! + + """ + List of collections a product belongs to. + """ + collections( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): CollectionConnection! + + """ + The compare at price of the product across all variants. + """ + compareAtPriceRange: ProductPriceRange! + + """ + The date and time when the product was created. + """ + createdAt: DateTime! + + """ + Stripped description of the product, single line with HTML tags removed. + """ + description( + """ + Truncates string after the given length. + """ + truncateAt: Int + ): String! + + """ + The description of the product, complete with HTML formatting. + """ + descriptionHtml: HTML! + + """ + A human-friendly unique string for the Product automatically generated from its title. + They are used by the Liquid templating language to refer to objects. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + List of images associated with the product. + """ + images( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductImageSortKeys = POSITION + + """ + Image width in pixels between 1 and 2048. This argument is deprecated: Use `maxWidth` on `Image.transformedSrc` instead. + """ + maxWidth: Int + + """ + Image height in pixels between 1 and 2048. This argument is deprecated: Use `maxHeight` on `Image.transformedSrc` instead. + """ + maxHeight: Int + + """ + Crops the image according to the specified region. This argument is deprecated: Use `crop` on `Image.transformedSrc` instead. + """ + crop: CropRegion + + """ + Image size multiplier for high-resolution retina displays. Must be between 1 and 3. This argument is deprecated: Use `scale` on `Image.transformedSrc` instead. + """ + scale: Int = 1 + ): ImageConnection! + + """ + The media associated with the product. + """ + media( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductMediaSortKeys = POSITION + ): MediaConnection! + + """ + The metafield associated with the resource. + """ + metafield( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String! + + """ + Identifier for the metafield (maximum of 30 characters). + """ + key: String! + ): Metafield + + """ + A paginated list of metafields associated with the resource. + """ + metafields( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): MetafieldConnection! + + """ + The online store URL for the product. + A value of `null` indicates that the product is not published to the Online Store sales channel. + """ + onlineStoreUrl: URL + + """ + List of product options. + """ + options( + """ + Truncate the array result to this size. + """ + first: Int + ): [ProductOption!]! + + """ + List of price ranges in the presentment currencies for this shop. + """ + presentmentPriceRanges( + """ + Specifies the presentment currencies to return a price range in. + """ + presentmentCurrencies: [CurrencyCode!] + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): ProductPriceRangeConnection! + + """ + The price range. + """ + priceRange: ProductPriceRange! + + """ + A categorization that a product can be tagged with, commonly used for filtering and searching. + """ + productType: String! + + """ + The date and time when the product was published to the channel. + """ + publishedAt: DateTime! + + """ + The product's SEO information. + """ + seo: SEO! + + """ + A comma separated list of tags that have been added to the product. + Additional access scope required for private apps: unauthenticated_read_product_tags. + """ + tags: [String!]! + + """ + The product’s title. + """ + title: String! + + """ + The total quantity of inventory in stock for this Product. + """ + totalInventory: Int + + """ + The date and time when the product was last modified. + A product's `updatedAt` value can change for different reasons. For example, if an order + is placed for a product that has inventory tracking set up, then the inventory adjustment + is counted as an update. + """ + updatedAt: DateTime! + + """ + Find a product’s variant based on its selected options. + This is useful for converting a user’s selection of product options into a single matching variant. + If there is not a variant for the selected options, `null` will be returned. + """ + variantBySelectedOptions( + """ + The input fields used for a selected option. + """ + selectedOptions: [SelectedOptionInput!]! + ): ProductVariant + + """ + List of the product’s variants. + """ + variants( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductVariantSortKeys = POSITION + ): ProductVariantConnection! + + """ + The product’s vendor name. + """ + vendor: String! +} + +""" +The set of valid sort keys for the ProductCollection query. +""" +enum ProductCollectionSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `price` value. + """ + PRICE + + """ + Sort by the `best-selling` value. + """ + BEST_SELLING + + """ + Sort by the `created` value. + """ + CREATED + + """ + Sort by the `id` value. + """ + ID + + """ + Sort by the `manual` value. + """ + MANUAL + + """ + Sort by the `collection-default` value. + """ + COLLECTION_DEFAULT + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +An auto-generated type for paginating through multiple Products. +""" +type ProductConnection { + """ + A list of edges. + """ + edges: [ProductEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Product and a cursor during pagination. +""" +type ProductEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ProductEdge. + """ + node: Product! +} + +""" +The set of valid sort keys for the ProductImage query. +""" +enum ProductImageSortKeys { + """ + Sort by the `created_at` value. + """ + CREATED_AT + + """ + Sort by the `position` value. + """ + POSITION + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +The set of valid sort keys for the ProductMedia query. +""" +enum ProductMediaSortKeys { + """ + Sort by the `position` value. + """ + POSITION + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +Product property names like "Size", "Color", and "Material" that the customers can select. +Variants are selected based on permutations of these options. +255 characters limit each. +""" +type ProductOption implements Node { + """ + Globally unique identifier. + """ + id: ID! + + """ + The product option’s name. + """ + name: String! + + """ + The corresponding value to the product option name. + """ + values: [String!]! +} + +""" +The price range of the product. +""" +type ProductPriceRange { + """ + The highest variant's price. + """ + maxVariantPrice: MoneyV2! + + """ + The lowest variant's price. + """ + minVariantPrice: MoneyV2! +} + +""" +An auto-generated type for paginating through multiple ProductPriceRanges. +""" +type ProductPriceRangeConnection { + """ + A list of edges. + """ + edges: [ProductPriceRangeEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one ProductPriceRange and a cursor during pagination. +""" +type ProductPriceRangeEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ProductPriceRangeEdge. + """ + node: ProductPriceRange! +} + +""" +The set of valid sort keys for the Product query. +""" +enum ProductSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `product_type` value. + """ + PRODUCT_TYPE + + """ + Sort by the `vendor` value. + """ + VENDOR + + """ + Sort by the `updated_at` value. + """ + UPDATED_AT + + """ + Sort by the `created_at` value. + """ + CREATED_AT + + """ + Sort by the `best_selling` value. + """ + BEST_SELLING + + """ + Sort by the `price` value. + """ + PRICE + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +A product variant represents a different version of a product, such as differing sizes or differing colors. +""" +type ProductVariant implements Node & HasMetafields { + """ + Indicates if the product variant is in stock. + """ + available: Boolean @deprecated(reason: "Use `availableForSale` instead") + + """ + Indicates if the product variant is available for sale. + """ + availableForSale: Boolean! + + """ + The compare at price of the variant. This can be used to mark a variant as on sale, when `compareAtPrice` is higher than `price`. + """ + compareAtPrice: Money @deprecated(reason: "Use `compareAtPriceV2` instead") + + """ + The compare at price of the variant. This can be used to mark a variant as on sale, when `compareAtPriceV2` is higher than `priceV2`. + """ + compareAtPriceV2: MoneyV2 + + """ + Whether a product is out of stock but still available for purchase (used for backorders). + """ + currentlyNotInStock: Boolean! + + """ + Globally unique identifier. + """ + id: ID! + + """ + Image associated with the product variant. This field falls back to the product image if no image is available. + """ + image( + """ + Image width in pixels between 1 and 2048. This argument is deprecated: Use `maxWidth` on `Image.transformedSrc` instead. + """ + maxWidth: Int + + """ + Image height in pixels between 1 and 2048. This argument is deprecated: Use `maxHeight` on `Image.transformedSrc` instead. + """ + maxHeight: Int + + """ + Crops the image according to the specified region. This argument is deprecated: Use `crop` on `Image.transformedSrc` instead. + """ + crop: CropRegion + + """ + Image size multiplier for high-resolution retina displays. Must be between 1 and 3. This argument is deprecated: Use `scale` on `Image.transformedSrc` instead. + """ + scale: Int = 1 + ): Image + + """ + The metafield associated with the resource. + """ + metafield( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String! + + """ + Identifier for the metafield (maximum of 30 characters). + """ + key: String! + ): Metafield + + """ + A paginated list of metafields associated with the resource. + """ + metafields( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): MetafieldConnection! + + """ + List of prices and compare-at prices in the presentment currencies for this shop. + """ + presentmentPrices( + """ + The presentment currencies prices should return in. + """ + presentmentCurrencies: [CurrencyCode!] + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): ProductVariantPricePairConnection! + + """ + List of unit prices in the presentment currencies for this shop. + """ + presentmentUnitPrices( + """ + Specify the currencies in which to return presentment unit prices. + """ + presentmentCurrencies: [CurrencyCode!] + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): MoneyV2Connection! + + """ + The product variant’s price. + """ + price: Money! @deprecated(reason: "Use `priceV2` instead") + + """ + The product variant’s price. + """ + priceV2: MoneyV2! + + """ + The product object that the product variant belongs to. + """ + product: Product! + + """ + The total sellable quantity of the variant for online sales channels. + """ + quantityAvailable: Int + + """ + Whether a customer needs to provide a shipping address when placing an order for the product variant. + """ + requiresShipping: Boolean! + + """ + List of product options applied to the variant. + """ + selectedOptions: [SelectedOption!]! + + """ + The SKU (stock keeping unit) associated with the variant. + """ + sku: String + + """ + The product variant’s title. + """ + title: String! + + """ + The unit price value for the variant based on the variant's measurement. + """ + unitPrice: MoneyV2 + + """ + The unit price measurement for the variant. + """ + unitPriceMeasurement: UnitPriceMeasurement + + """ + The weight of the product variant in the unit system specified with `weight_unit`. + """ + weight: Float + + """ + Unit of measurement for weight. + """ + weightUnit: WeightUnit! +} + +""" +An auto-generated type for paginating through multiple ProductVariants. +""" +type ProductVariantConnection { + """ + A list of edges. + """ + edges: [ProductVariantEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one ProductVariant and a cursor during pagination. +""" +type ProductVariantEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ProductVariantEdge. + """ + node: ProductVariant! +} + +""" +The compare-at price and price of a variant sharing a currency. +""" +type ProductVariantPricePair { + """ + The compare-at price of the variant with associated currency. + """ + compareAtPrice: MoneyV2 + + """ + The price of the variant with associated currency. + """ + price: MoneyV2! +} + +""" +An auto-generated type for paginating through multiple ProductVariantPricePairs. +""" +type ProductVariantPricePairConnection { + """ + A list of edges. + """ + edges: [ProductVariantPricePairEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one ProductVariantPricePair and a cursor during pagination. +""" +type ProductVariantPricePairEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ProductVariantPricePairEdge. + """ + node: ProductVariantPricePair! +} + +""" +The set of valid sort keys for the ProductVariant query. +""" +enum ProductVariantSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `sku` value. + """ + SKU + + """ + Sort by the `position` value. + """ + POSITION + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. +""" +type QueryRoot { + """ + List of the shop's articles. + """ + articles( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ArticleSortKeys = ID + + """ + Supported filter parameters: + - `author` + - `blog_title` + - `created_at` + - `tag` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): ArticleConnection! + + """ + Find a blog by its handle. + """ + blogByHandle( + """ + The handle of the blog. + """ + handle: String! + ): Blog + + """ + List of the shop's blogs. + """ + blogs( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: BlogSortKeys = ID + + """ + Supported filter parameters: + - `created_at` + - `handle` + - `title` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): BlogConnection! + + """ + Find a collection by its handle. + """ + collectionByHandle( + """ + The handle of the collection. + """ + handle: String! + ): Collection + + """ + List of the shop’s collections. + """ + collections( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: CollectionSortKeys = ID + + """ + Supported filter parameters: + - `collection_type` + - `title` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): CollectionConnection! + + """ + Find a customer by its access token. + """ + customer( + """ + The customer access token. + """ + customerAccessToken: String! + ): Customer + node( + """ + The ID of the Node to return. + """ + id: ID! + ): Node + nodes( + """ + The IDs of the Nodes to return. + """ + ids: [ID!]! + ): [Node]! + + """ + Find a page by its handle. + """ + pageByHandle( + """ + The handle of the page. + """ + handle: String! + ): Page + + """ + List of the shop's pages. + """ + pages( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: PageSortKeys = ID + + """ + Supported filter parameters: + - `created_at` + - `handle` + - `title` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): PageConnection! + + """ + Find a product by its handle. + """ + productByHandle( + """ + The handle of the product. + """ + handle: String! + ): Product + + """ + Find recommended products related to a given `product_id`. + To learn more about how recommendations are generated, see + [*Showing product recommendations on product pages*](https://help.shopify.com/themes/development/recommended-products). + """ + productRecommendations( + """ + The id of the product. + """ + productId: ID! + ): [Product!] + + """ + Tags added to products. + Additional access scope required: unauthenticated_read_product_tags. + """ + productTags( + """ + Returns up to the first `n` elements from the list. + """ + first: Int! + ): StringConnection! + + """ + List of product types for the shop's products that are published to your app. + """ + productTypes( + """ + Returns up to the first `n` elements from the list. + """ + first: Int! + ): StringConnection! + + """ + List of the shop’s products. + """ + products( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductSortKeys = ID + + """ + Supported filter parameters: + - `available_for_sale` + - `created_at` + - `product_type` + - `tag` + - `title` + - `updated_at` + - `variants.price` + - `vendor` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): ProductConnection! + + """ + The list of public Storefront API versions, including supported, release candidate and unstable versions. + """ + publicApiVersions: [ApiVersion!]! + + """ + The shop associated with the storefront access token. + """ + shop: Shop! +} + +""" +SEO information. +""" +type SEO { + """ + The meta description. + """ + description: String + + """ + The SEO title. + """ + title: String +} + +""" +Script discount applications capture the intentions of a discount that +was created by a Shopify Script. +""" +type ScriptDiscountApplication implements DiscountApplication { + """ + The method by which the discount's value is allocated to its entitled items. + """ + allocationMethod: DiscountApplicationAllocationMethod! + + """ + The description of the application as defined by the Script. + """ + description: String! @deprecated(reason: "Use `title` instead") + + """ + Which lines of targetType that the discount is allocated over. + """ + targetSelection: DiscountApplicationTargetSelection! + + """ + The type of line that the discount is applicable towards. + """ + targetType: DiscountApplicationTargetType! + + """ + The title of the application as defined by the Script. + """ + title: String! + + """ + The value of the discount application. + """ + value: PricingValue! +} + +""" +Properties used by customers to select a product variant. +Products can have multiple options, like different sizes or colors. +""" +type SelectedOption { + """ + The product option’s name. + """ + name: String! + + """ + The product option’s value. + """ + value: String! +} + +""" +Specifies the input fields required for a selected option. +""" +input SelectedOptionInput { + """ + The product option’s name. + """ + name: String! + + """ + The product option’s value. + """ + value: String! +} + +""" +A shipping rate to be applied to a checkout. +""" +type ShippingRate { + """ + Human-readable unique identifier for this shipping rate. + """ + handle: String! + + """ + Price of this shipping rate. + """ + price: Money! @deprecated(reason: "Use `priceV2` instead") + + """ + Price of this shipping rate. + """ + priceV2: MoneyV2! + + """ + Title of this shipping rate. + """ + title: String! +} + +""" +Shop represents a collection of the general settings and information about the shop. +""" +type Shop { + """ + List of the shop' articles. + """ + articles( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ArticleSortKeys = ID + + """ + Supported filter parameters: + - `author` + - `blog_title` + - `created_at` + - `tag` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): ArticleConnection! @deprecated(reason: "Use `QueryRoot.articles` instead.") + + """ + List of the shop' blogs. + """ + blogs( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: BlogSortKeys = ID + + """ + Supported filter parameters: + - `created_at` + - `handle` + - `title` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): BlogConnection! @deprecated(reason: "Use `QueryRoot.blogs` instead.") + + """ + Find a collection by its handle. + """ + collectionByHandle( + """ + The handle of the collection. + """ + handle: String! + ): Collection + @deprecated(reason: "Use `QueryRoot.collectionByHandle` instead.") + + """ + List of the shop’s collections. + """ + collections( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: CollectionSortKeys = ID + + """ + Supported filter parameters: + - `collection_type` + - `title` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): CollectionConnection! + @deprecated(reason: "Use `QueryRoot.collections` instead.") + + """ + The three-letter code for the currency that the shop accepts. + """ + currencyCode: CurrencyCode! + @deprecated(reason: "Use `paymentSettings` instead") + + """ + A description of the shop. + """ + description: String + + """ + A string representing the way currency is formatted when the currency isn’t specified. + """ + moneyFormat: String! + + """ + The shop’s name. + """ + name: String! + + """ + Settings related to payments. + """ + paymentSettings: PaymentSettings! + + """ + The shop’s primary domain. + """ + primaryDomain: Domain! + + """ + The shop’s privacy policy. + """ + privacyPolicy: ShopPolicy + + """ + Find a product by its handle. + """ + productByHandle( + """ + The handle of the product. + """ + handle: String! + ): Product @deprecated(reason: "Use `QueryRoot.productByHandle` instead.") + + """ + A list of tags that have been added to products. + Additional access scope required: unauthenticated_read_product_tags. + """ + productTags( + """ + Returns up to the first `n` elements from the list. + """ + first: Int! + ): StringConnection! + @deprecated(reason: "Use `QueryRoot.productTags` instead.") + + """ + List of the shop’s product types. + """ + productTypes( + """ + Returns up to the first `n` elements from the list. + """ + first: Int! + ): StringConnection! + @deprecated(reason: "Use `QueryRoot.productTypes` instead.") + + """ + List of the shop’s products. + """ + products( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductSortKeys = ID + + """ + Supported filter parameters: + - `available_for_sale` + - `created_at` + - `product_type` + - `tag` + - `title` + - `updated_at` + - `variants.price` + - `vendor` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): ProductConnection! @deprecated(reason: "Use `QueryRoot.products` instead.") + + """ + The shop’s refund policy. + """ + refundPolicy: ShopPolicy + + """ + The shop’s shipping policy. + """ + shippingPolicy: ShopPolicy + + """ + Countries that the shop ships to. + """ + shipsToCountries: [CountryCode!]! + + """ + The shop’s Shopify Payments account id. + """ + shopifyPaymentsAccountId: String + @deprecated(reason: "Use `paymentSettings` instead") + + """ + The shop’s terms of service. + """ + termsOfService: ShopPolicy +} + +""" +Policy that a merchant has configured for their store, such as their refund or privacy policy. +""" +type ShopPolicy implements Node { + """ + Policy text, maximum size of 64kb. + """ + body: String! + + """ + Policy’s handle. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + Policy’s title. + """ + title: String! + + """ + Public URL to the policy. + """ + url: URL! +} + +""" +An auto-generated type for paginating through multiple Strings. +""" +type StringConnection { + """ + A list of edges. + """ + edges: [StringEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one String and a cursor during pagination. +""" +type StringEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of StringEdge. + """ + node: String! +} + +""" +Specifies the fields required to complete a checkout with +a tokenized payment. +""" +input TokenizedPaymentInput { + """ + The amount of the payment. + """ + amount: Money! + + """ + A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. + """ + idempotencyKey: String! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddressInput! + + """ + The type of payment token. + """ + type: String! + + """ + A simple string or JSON containing the required payment data for the tokenized payment. + """ + paymentData: String! + + """ + Executes the payment in test mode if possible. Defaults to `false`. + """ + test: Boolean + + """ + Public Hash Key used for AndroidPay payments only. + """ + identifier: String +} + +""" +Specifies the fields required to complete a checkout with +a tokenized payment. +""" +input TokenizedPaymentInputV2 { + """ + The amount and currency of the payment. + """ + paymentAmount: MoneyInput! + + """ + A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. + """ + idempotencyKey: String! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddressInput! + + """ + A simple string or JSON containing the required payment data for the tokenized payment. + """ + paymentData: String! + + """ + Whether to execute the payment in test mode, if possible. Test mode is not supported in production stores. Defaults to `false`. + """ + test: Boolean + + """ + Public Hash Key used for AndroidPay payments only. + """ + identifier: String + + """ + The type of payment token. + """ + type: String! +} + +""" +Specifies the fields required to complete a checkout with +a tokenized payment. +""" +input TokenizedPaymentInputV3 { + """ + The amount and currency of the payment. + """ + paymentAmount: MoneyInput! + + """ + A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. + """ + idempotencyKey: String! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddressInput! + + """ + A simple string or JSON containing the required payment data for the tokenized payment. + """ + paymentData: String! + + """ + Whether to execute the payment in test mode, if possible. Test mode is not supported in production stores. Defaults to `false`. + """ + test: Boolean + + """ + Public Hash Key used for AndroidPay payments only. + """ + identifier: String + + """ + The type of payment token. + """ + type: PaymentTokenType! +} + +""" +An object representing exchange of money for a product or service. +""" +type Transaction { + """ + The amount of money that the transaction was for. + """ + amount: Money! @deprecated(reason: "Use `amountV2` instead") + + """ + The amount of money that the transaction was for. + """ + amountV2: MoneyV2! + + """ + The kind of the transaction. + """ + kind: TransactionKind! + + """ + The status of the transaction. + """ + status: TransactionStatus! @deprecated(reason: "Use `statusV2` instead") + + """ + The status of the transaction. + """ + statusV2: TransactionStatus + + """ + Whether the transaction was done in test mode or not. + """ + test: Boolean! +} + +enum TransactionKind { + SALE + CAPTURE + AUTHORIZATION + EMV_AUTHORIZATION + CHANGE +} + +enum TransactionStatus { + PENDING + SUCCESS + FAILURE + ERROR +} + +""" +An RFC 3986 and RFC 3987 compliant URI string. + +Example value: `"https://johns-apparel.myshopify.com"`. +""" +scalar URL + +""" +The measurement used to calculate a unit price for a product variant (e.g. $9.99 / 100ml). +""" +type UnitPriceMeasurement { + """ + The type of unit of measurement for the unit price measurement. + """ + measuredType: UnitPriceMeasurementMeasuredType + + """ + The quantity unit for the unit price measurement. + """ + quantityUnit: UnitPriceMeasurementMeasuredUnit + + """ + The quantity value for the unit price measurement. + """ + quantityValue: Float! + + """ + The reference unit for the unit price measurement. + """ + referenceUnit: UnitPriceMeasurementMeasuredUnit + + """ + The reference value for the unit price measurement. + """ + referenceValue: Int! +} + +""" +The accepted types of unit of measurement. +""" +enum UnitPriceMeasurementMeasuredType { + """ + Unit of measurements representing volumes. + """ + VOLUME + + """ + Unit of measurements representing weights. + """ + WEIGHT + + """ + Unit of measurements representing lengths. + """ + LENGTH + + """ + Unit of measurements representing areas. + """ + AREA +} + +""" +The valid units of measurement for a unit price measurement. +""" +enum UnitPriceMeasurementMeasuredUnit { + """ + 1000 milliliters equals 1 liter. + """ + ML + + """ + 100 centiliters equals 1 liter. + """ + CL + + """ + Metric system unit of volume. + """ + L + + """ + 1 cubic meter equals 1000 liters. + """ + M3 + + """ + 1000 milligrams equals 1 gram. + """ + MG + + """ + Metric system unit of weight. + """ + G + + """ + 1 kilogram equals 1000 grams. + """ + KG + + """ + 1000 millimeters equals 1 meter. + """ + MM + + """ + 100 centimeters equals 1 meter. + """ + CM + + """ + Metric system unit of length. + """ + M + + """ + Metric system unit of area. + """ + M2 +} + +""" +Represents an error in the input of a mutation. +""" +type UserError implements DisplayableError { + """ + Path to the input field which caused the error. + """ + field: [String!] + + """ + The error message. + """ + message: String! +} + +""" +Represents a Shopify hosted video. +""" +type Video implements Node & Media { + """ + A word or phrase to share the nature or contents of a media. + """ + alt: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + The media content type. + """ + mediaContentType: MediaContentType! + + """ + The preview image for the media. + """ + previewImage: Image + + """ + The sources for a video. + """ + sources: [VideoSource!]! +} + +""" +Represents a source for a Shopify hosted video. +""" +type VideoSource { + """ + The format of the video source. + """ + format: String! + + """ + The height of the video. + """ + height: Int! + + """ + The video MIME type. + """ + mimeType: String! + + """ + The URL of the video. + """ + url: String! + + """ + The width of the video. + """ + width: Int! +} + +""" +Units of measurement for weight. +""" +enum WeightUnit { + """ + 1 kilogram equals 1000 grams. + """ + KILOGRAMS + + """ + Metric system unit of mass. + """ + GRAMS + + """ + 1 pound equals 16 ounces. + """ + POUNDS + + """ + Imperial system unit of mass. + """ + OUNCES +} diff --git a/framework/shopify/types.ts b/framework/shopify/types.ts index 47bb94e62..9ad9fd016 100644 --- a/framework/shopify/types.ts +++ b/framework/shopify/types.ts @@ -1,130 +1,48 @@ -import { - Product as BaseProduct, - ProductVariant as BaseProductVariant, - Cart as BaseCart, - CheckoutResource as BaseCheckoutResource, - AttributeInput, - Client as BaseClient, - Shop as BaseShop, - Image as BaseImage, -} from 'shopify-buy' +import * as Core from '@commerce/types' +import { CheckoutLineItem } from './schema' -export type SelectedOptions = { +export type ShopifyCheckout = { id: string - name: string - value: string + webUrl: string + lineItems: CheckoutLineItem[] } -export type PresentmentPrice = { - price: PriceV2 -} - -export type ProductVariant = BaseProductVariant & { - selectedOptions: Array<SelectedOptions> - presentmentPrices: Array<PresentmentPrice> -} - -// TODO -export type ProductOptions = { - node: { - __typename: string - displayName: string - values: { - edges: [ - { - node: { - label: string - id: string - } - } - ] - } - } -} - -// TODO -export type ProductEdge = { - node: Product -} - -export type Product = BaseProduct & { - handle: string - name: string - path: string - entityId: number - descriptionHtml: string - prices: { - price: { - value: number - currencyCode: string - } - retailPrice: { - value: number - currencyCode: string - } - } - images: { - edges: [{ node: { urlOriginal: string; altText: string } }] - } - productOptions: ProductOptions - variants: Array<ProductVariant> & { - edges: [ - { - node: { - productOptions: ProductOptions[] - entityId: number - } - } - ] - } -} - -export type PriceV2 = { - amount: number - currencyCode: string -} - -export type Cart = BaseCart & { - webUrl?: string - currencyCode?: string - lineItemsSubtotalPrice?: PriceV2 - totalPriceV2?: PriceV2 -} - -export type Shop = BaseShop & { - currencyCode?: string -} - -export type Create = { - presentmentCurrencyCode?: string -} - -export type CheckoutResource = BaseCheckoutResource & { - updateLineItems( - checkoutId: string | number, - lineItems: AttributeInput[] - ): Promise<Cart> - - create: (input: Create) => Promise<Cart> -} - -export type Client = BaseClient & { - checkout: CheckoutResource -} - -export type Page = { +export interface Cart extends Core.Cart { id: string - title: string - name: string - handle: string - body: string - bodySummary: string - url: string - sort_order: number + lineItems: LineItem[] } -export type PageEdge = { - node: Page +export interface LineItem extends Core.LineItem { + options: any[] } -export type Image = BaseImage +/** + * Cart mutations + */ + +export type OptionSelections = { + option_id: number + option_value: number | string +} + +export type CartItemBody = Core.CartItemBody & { + productId: string // The product id is always required for BC + optionSelections?: OptionSelections +} + +type X = Core.CartItemBody extends CartItemBody ? any : never +type Y = CartItemBody extends Core.CartItemBody ? any : never + +export type GetCartHandlerBody = Core.GetCartHandlerBody + +export type AddCartItemBody = Core.AddCartItemBody<CartItemBody> + +export type AddCartItemHandlerBody = Core.AddCartItemHandlerBody<CartItemBody> + +export type UpdateCartItemBody = Core.UpdateCartItemBody<CartItemBody> + +export type UpdateCartItemHandlerBody = Core.UpdateCartItemHandlerBody<CartItemBody> + +export type RemoveCartItemBody = Core.RemoveCartItemBody + +export type RemoveCartItemHandlerBody = Core.RemoveCartItemHandlerBody diff --git a/framework/shopify/utils/customer-token.ts b/framework/shopify/utils/customer-token.ts new file mode 100644 index 000000000..beae54765 --- /dev/null +++ b/framework/shopify/utils/customer-token.ts @@ -0,0 +1,12 @@ +import Cookies from 'js-cookie' +import { SHOPIFY_CUSTOMER_TOKEN_COOKIE } from '../const' + +export const getCustomerToken = () => Cookies.get(SHOPIFY_CUSTOMER_TOKEN_COOKIE) + +export const setCustomerToken = (token: string | null, options?: any) => { + if (!token) { + Cookies.remove(SHOPIFY_CUSTOMER_TOKEN_COOKIE) + } else { + Cookies.set(SHOPIFY_CUSTOMER_TOKEN_COOKIE, token, options) + } +} diff --git a/framework/shopify/utils/get-categories.ts b/framework/shopify/utils/get-categories.ts new file mode 100644 index 000000000..942ec9c62 --- /dev/null +++ b/framework/shopify/utils/get-categories.ts @@ -0,0 +1,29 @@ +import { ShopifyConfig } from '@framework/api' +import { CollectionEdge } from '@framework/schema' +import getSiteCollectionsQuery from './queries/get-all-collections-query' + +export type Category = { + endityId: string + name: string + path: string +} + +const getCategories = async (config: ShopifyConfig): Promise<Category[]> => { + const { data } = await config.fetch(getSiteCollectionsQuery, { + variables: { + first: 250, + }, + }) + + return ( + data.collections?.edges?.map( + ({ node: { title: name, handle } }: CollectionEdge) => ({ + entityId: handle, + name, + path: `/${handle}`, + }) + ) ?? [] + ) +} + +export default getCategories diff --git a/framework/shopify/utils/get-checkout-id.ts b/framework/shopify/utils/get-checkout-id.ts new file mode 100644 index 000000000..11e3802d9 --- /dev/null +++ b/framework/shopify/utils/get-checkout-id.ts @@ -0,0 +1,8 @@ +import Cookies from 'js-cookie' +import { SHOPIFY_CHECKOUT_ID_COOKIE } from '../const' + +const getCheckoutId = (id?: string) => { + return id ?? Cookies.get(SHOPIFY_CHECKOUT_ID_COOKIE) +} + +export default getCheckoutId diff --git a/framework/shopify/utils/get-search-variables.ts b/framework/shopify/utils/get-search-variables.ts new file mode 100644 index 000000000..90d35ba50 --- /dev/null +++ b/framework/shopify/utils/get-search-variables.ts @@ -0,0 +1,30 @@ +import getSortVariables from './get-sort-variables' +import type { SearchProductsInput } from '@framework/product/use-search' + +export const getSearchVariables = ({ + categoryId, + brandId, + search, + sort, +}: SearchProductsInput) => { + let query = '' + + if (search) { + query += `product_type:${search} OR title:${search} OR tag:${search}` + } + + if (categoryId) { + query += `tag:${categoryId}` + } + + if (brandId) { + query += `${categoryId ? ' AND ' : ''}vendor:${brandId}` + } + + return { + query, + ...getSortVariables(sort), + } +} + +export default getSearchVariables diff --git a/framework/shopify/utils/get-sort-variables.ts b/framework/shopify/utils/get-sort-variables.ts new file mode 100644 index 000000000..47650c0d7 --- /dev/null +++ b/framework/shopify/utils/get-sort-variables.ts @@ -0,0 +1,32 @@ +const getSortVariables = (sort?: string) => { + let output = {} + switch (sort) { + case 'price-asc': + output = { + sortKey: 'PRICE', + reverse: false, + } + break + case 'price-desc': + output = { + sortKey: 'PRICE', + reverse: true, + } + break + case 'trending-desc': + output = { + sortKey: 'BEST_SELLING', + reverse: false, + } + break + case 'latest-desc': + output = { + sortKey: 'CREATED_AT', + reverse: true, + } + break + } + return output +} + +export default getSortVariables diff --git a/framework/shopify/utils/get-vendors.ts b/framework/shopify/utils/get-vendors.ts new file mode 100644 index 000000000..d3ebce194 --- /dev/null +++ b/framework/shopify/utils/get-vendors.ts @@ -0,0 +1,36 @@ +import { ShopifyConfig } from '@framework/api' +import fetchAllProducts from '@framework/api/utils/fetch-all-products' +import getAllProductVendors from './queries/get-all-product-vendors-query' + +export type BrandNode = { + name: string + path: string +} + +export type BrandEdge = { + node: BrandNode +} + +export type Brands = BrandEdge[] + +const getVendors = async (config: ShopifyConfig): Promise<BrandEdge[]> => { + const vendors = await fetchAllProducts({ + config, + query: getAllProductVendors, + variables: { + first: 250, + }, + }) + + let vendorsStrings = vendors.map(({ node: { vendor } }) => vendor) + + return [...new Set(vendorsStrings)].map((v) => ({ + node: { + entityId: v, + name: v, + path: `brands/${v}`, + }, + })) +} + +export default getVendors diff --git a/framework/shopify/utils/handle-fetch-response.ts b/framework/shopify/utils/handle-fetch-response.ts new file mode 100644 index 000000000..8d7427d91 --- /dev/null +++ b/framework/shopify/utils/handle-fetch-response.ts @@ -0,0 +1,27 @@ +import { FetcherError } from '@commerce/utils/errors' + +export function getError(errors: any[], status: number) { + errors = errors ?? [{ message: 'Failed to fetch Shopify API' }] + return new FetcherError({ errors, status }) +} + +export async function getAsyncError(res: Response) { + const data = await res.json() + return getError(data.errors, res.status) +} + +const handleFetchResponse = async (res: Response) => { + if (res.ok) { + const { data, errors } = await res.json() + + if (errors && errors.length) { + throw getError(errors, res.status) + } + + return data + } + + throw await getAsyncError(res) +} + +export default handleFetchResponse diff --git a/framework/shopify/utils/handle-login.ts b/framework/shopify/utils/handle-login.ts new file mode 100644 index 000000000..77b6873e3 --- /dev/null +++ b/framework/shopify/utils/handle-login.ts @@ -0,0 +1,39 @@ +import { ValidationError } from '@commerce/utils/errors' +import { setCustomerToken } from './customer-token' + +const getErrorMessage = ({ + code, + message, +}: { + code: string + message: string +}) => { + switch (code) { + case 'UNIDENTIFIED_CUSTOMER': + message = 'Cannot find an account that matches the provided credentials' + break + } + return message +} + +const handleLogin = (data: any) => { + const response = data.customerAccessTokenCreate + const errors = response?.customerUserErrors + + if (errors && errors.length) { + throw new ValidationError({ + message: getErrorMessage(errors[0]), + }) + } + + const customerAccessToken = response?.customerAccessToken + const accessToken = customerAccessToken?.accessToken + + if (accessToken) { + setCustomerToken(accessToken) + } + + return customerAccessToken +} + +export default handleLogin diff --git a/framework/shopify/utils/index.ts b/framework/shopify/utils/index.ts new file mode 100644 index 000000000..2d59aa506 --- /dev/null +++ b/framework/shopify/utils/index.ts @@ -0,0 +1,10 @@ +export { default as handleFetchResponse } from './handle-fetch-response' +export { default as getSearchVariables } from './get-search-variables' +export { default as getSortVariables } from './get-sort-variables' +export { default as getVendors } from './get-vendors' +export { default as getCategories } from './get-categories' +export { default as getCheckoutId } from './get-checkout-id' +export * from './queries' +export * from './mutations' +export * from './normalize' +export * from './customer-token' diff --git a/framework/shopify/utils/mutations/associate-customer-with-checkout.ts b/framework/shopify/utils/mutations/associate-customer-with-checkout.ts new file mode 100644 index 000000000..6b1350e05 --- /dev/null +++ b/framework/shopify/utils/mutations/associate-customer-with-checkout.ts @@ -0,0 +1,18 @@ +const associateCustomerWithCheckoutMutation = /* GraphQl */ ` +mutation associateCustomerWithCheckout($checkoutId: ID!, $customerAccessToken: String!) { + checkoutCustomerAssociateV2(checkoutId: $checkoutId, customerAccessToken: $customerAccessToken) { + checkout { + id + } + checkoutUserErrors { + code + field + message + } + customer { + id + } + } + } +` +export default associateCustomerWithCheckoutMutation diff --git a/framework/shopify/utils/mutations/checkout-create.ts b/framework/shopify/utils/mutations/checkout-create.ts new file mode 100644 index 000000000..912e1cbd2 --- /dev/null +++ b/framework/shopify/utils/mutations/checkout-create.ts @@ -0,0 +1,16 @@ +import { checkoutDetailsFragment } from '../queries/get-checkout-query' + +const checkoutCreateMutation = /* GraphQL */ ` + mutation { + checkoutCreate(input: {}) { + userErrors { + message + field + } + checkout { + ${checkoutDetailsFragment} + } + } + } +` +export default checkoutCreateMutation diff --git a/framework/shopify/utils/mutations/checkout-line-item-add.ts b/framework/shopify/utils/mutations/checkout-line-item-add.ts new file mode 100644 index 000000000..67b9cf250 --- /dev/null +++ b/framework/shopify/utils/mutations/checkout-line-item-add.ts @@ -0,0 +1,16 @@ +import { checkoutDetailsFragment } from '../queries/get-checkout-query' + +const checkoutLineItemAddMutation = /* GraphQL */ ` + mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemInput!]!) { + checkoutLineItemsAdd(checkoutId: $checkoutId, lineItems: $lineItems) { + userErrors { + message + field + } + checkout { + ${checkoutDetailsFragment} + } + } + } +` +export default checkoutLineItemAddMutation diff --git a/framework/shopify/utils/mutations/checkout-line-item-remove.ts b/framework/shopify/utils/mutations/checkout-line-item-remove.ts new file mode 100644 index 000000000..d967a5168 --- /dev/null +++ b/framework/shopify/utils/mutations/checkout-line-item-remove.ts @@ -0,0 +1,19 @@ +import { checkoutDetailsFragment } from '../queries/get-checkout-query' + +const checkoutLineItemRemoveMutation = /* GraphQL */ ` + mutation($checkoutId: ID!, $lineItemIds: [ID!]!) { + checkoutLineItemsRemove( + checkoutId: $checkoutId + lineItemIds: $lineItemIds + ) { + userErrors { + message + field + } + checkout { + ${checkoutDetailsFragment} + } + } + } +` +export default checkoutLineItemRemoveMutation diff --git a/framework/shopify/utils/mutations/checkout-line-item-update.ts b/framework/shopify/utils/mutations/checkout-line-item-update.ts new file mode 100644 index 000000000..8edf17587 --- /dev/null +++ b/framework/shopify/utils/mutations/checkout-line-item-update.ts @@ -0,0 +1,16 @@ +import { checkoutDetailsFragment } from '../queries/get-checkout-query' + +const checkoutLineItemUpdateMutation = /* GraphQL */ ` + mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemUpdateInput!]!) { + checkoutLineItemsUpdate(checkoutId: $checkoutId, lineItems: $lineItems) { + userErrors { + message + field + } + checkout { + ${checkoutDetailsFragment} + } + } + } +` +export default checkoutLineItemUpdateMutation diff --git a/framework/shopify/utils/mutations/customer-access-token-create.ts b/framework/shopify/utils/mutations/customer-access-token-create.ts new file mode 100644 index 000000000..7a45c3f49 --- /dev/null +++ b/framework/shopify/utils/mutations/customer-access-token-create.ts @@ -0,0 +1,16 @@ +const customerAccessTokenCreateMutation = /* GraphQL */ ` + mutation customerAccessTokenCreate($input: CustomerAccessTokenCreateInput!) { + customerAccessTokenCreate(input: $input) { + customerAccessToken { + accessToken + expiresAt + } + customerUserErrors { + code + field + message + } + } + } +` +export default customerAccessTokenCreateMutation diff --git a/framework/shopify/utils/mutations/customer-access-token-delete.ts b/framework/shopify/utils/mutations/customer-access-token-delete.ts new file mode 100644 index 000000000..c46eff1e5 --- /dev/null +++ b/framework/shopify/utils/mutations/customer-access-token-delete.ts @@ -0,0 +1,14 @@ +const customerAccessTokenDeleteMutation = /* GraphQL */ ` + mutation customerAccessTokenDelete($customerAccessToken: String!) { + customerAccessTokenDelete(customerAccessToken: $customerAccessToken) { + deletedAccessToken + deletedCustomerAccessTokenId + userErrors { + field + message + } + } + } +` + +export default customerAccessTokenDeleteMutation diff --git a/framework/shopify/utils/mutations/customer-create.ts b/framework/shopify/utils/mutations/customer-create.ts new file mode 100644 index 000000000..05c728a25 --- /dev/null +++ b/framework/shopify/utils/mutations/customer-create.ts @@ -0,0 +1,15 @@ +const customerCreateMutation = /* GraphQL */ ` + mutation customerCreate($input: CustomerCreateInput!) { + customerCreate(input: $input) { + customerUserErrors { + code + field + message + } + customer { + id + } + } + } +` +export default customerCreateMutation diff --git a/framework/shopify/utils/mutations/index.ts b/framework/shopify/utils/mutations/index.ts new file mode 100644 index 000000000..3a16d7cec --- /dev/null +++ b/framework/shopify/utils/mutations/index.ts @@ -0,0 +1,7 @@ +export { default as customerCreateMutation } from './customer-create' +export { default as checkoutCreateMutation } from './checkout-create' +export { default as checkoutLineItemAddMutation } from './checkout-line-item-add' +export { default as checkoutLineItemUpdateMutation } from './checkout-line-item-update' +export { default as checkoutLineItemRemoveMutation } from './checkout-line-item-remove' +export { default as customerAccessTokenCreateMutation } from './customer-access-token-create' +export { default as customerAccessTokenDeleteMutation } from './customer-access-token-delete' diff --git a/framework/shopify/utils/normalize.ts b/framework/shopify/utils/normalize.ts new file mode 100644 index 000000000..1eee9f336 --- /dev/null +++ b/framework/shopify/utils/normalize.ts @@ -0,0 +1,133 @@ +import { + Product as ShopifyProduct, + Checkout, + CheckoutLineItemEdge, + SelectedOption, + ImageConnection, + ProductVariantConnection, + ProductOption, + MoneyV2, +} from '@framework/schema' + +import type { Cart, LineItem } from '../types' + +const money = ({ amount, currencyCode }: MoneyV2) => { + return { + value: +amount, + currencyCode, + } +} + +const normalizeProductOption = ({ + name: displayName, + values, + ...rest +}: ProductOption) => { + return { + __typename: 'MultipleChoiceOption', + displayName, + values: values.map((value) => ({ + label: value, + hexColors: displayName === 'Color' ? [value] : null, + })), + ...rest, + } +} + +const normalizeProductImages = ({ edges }: ImageConnection) => + edges?.map(({ node: { originalSrc: url, ...rest } }) => ({ + url, + ...rest, + })) + +const normalizeProductVariants = ({ edges }: ProductVariantConnection) => { + return edges?.map(({ node: { id, selectedOptions } }) => ({ + id, + options: selectedOptions.map(({ name, value }: SelectedOption) => + normalizeProductOption({ + id, + name, + values: [value], + }) + ), + })) +} + +export function normalizeProduct(productNode: ShopifyProduct): any { + const { + id, + title: name, + vendor, + images, + variants, + description, + handle, + priceRange, + options, + ...rest + } = productNode + + const product = { + id, + name, + vendor, + description, + path: `/${handle}`, + slug: handle?.replace(/^\/+|\/+$/g, ''), + price: money(priceRange?.minVariantPrice), + images: normalizeProductImages(images), + variants: variants ? normalizeProductVariants(variants) : [], + options: options ? options.map((o) => normalizeProductOption(o)) : [], + ...rest, + } + + return product +} + +export function normalizeCart(checkout: Checkout): Cart { + return { + id: checkout.id, + customerId: '', + email: '', + createdAt: checkout.createdAt, + currency: { + code: checkout.totalPriceV2?.currencyCode, + }, + taxesIncluded: checkout.taxesIncluded, + lineItems: checkout.lineItems?.edges.map(normalizeLineItem), + lineItemsSubtotalPrice: checkout.subtotalPriceV2?.amount, + subtotalPrice: checkout.subtotalPriceV2?.amount, + totalPrice: checkout.totalPriceV2?.amount, + discounts: [], + } +} + +function normalizeLineItem({ + node: { id, title, variant, quantity }, +}: CheckoutLineItemEdge): LineItem { + return { + id, + variantId: String(variant?.id), + productId: String(variant?.id), + name: `${title}`, + quantity, + variant: { + id: String(variant?.id), + sku: variant?.sku ?? '', + name: variant?.title!, + image: { + url: variant?.image?.originalSrc, + }, + requiresShipping: variant?.requiresShipping ?? false, + price: variant?.priceV2?.amount, + listPrice: variant?.compareAtPriceV2?.amount, + }, + path: '', + discounts: [], + options: [ + { + value: variant?.title, + }, + ], + } +} diff --git a/framework/shopify/utils/queries/get-all-collections-query.ts b/framework/shopify/utils/queries/get-all-collections-query.ts new file mode 100644 index 000000000..2abf374d6 --- /dev/null +++ b/framework/shopify/utils/queries/get-all-collections-query.ts @@ -0,0 +1,14 @@ +const getSiteCollectionsQuery = /* GraphQL */ ` + query getSiteCollections($first: Int!) { + collections(first: $first) { + edges { + node { + id + title + handle + } + } + } + } +` +export default getSiteCollectionsQuery diff --git a/framework/shopify/utils/queries/get-all-pages-query.ts b/framework/shopify/utils/queries/get-all-pages-query.ts new file mode 100644 index 000000000..e3aee1f10 --- /dev/null +++ b/framework/shopify/utils/queries/get-all-pages-query.ts @@ -0,0 +1,14 @@ +export const getAllPagesQuery = /* GraphQL */ ` + query getAllPages($first: Int = 250) { + pages(first: $first) { + edges { + node { + id + title + handle + } + } + } + } +` +export default getAllPagesQuery diff --git a/framework/shopify/utils/queries/get-all-product-vendors-query.ts b/framework/shopify/utils/queries/get-all-product-vendors-query.ts new file mode 100644 index 000000000..be08b8ec6 --- /dev/null +++ b/framework/shopify/utils/queries/get-all-product-vendors-query.ts @@ -0,0 +1,17 @@ +const getAllProductVendors = /* GraphQL */ ` + query getAllProductVendors($first: Int = 250, $cursor: String) { + products(first: $first, after: $cursor) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + vendor + } + cursor + } + } + } +` +export default getAllProductVendors diff --git a/framework/shopify/utils/queries/get-all-products-paths-query.ts b/framework/shopify/utils/queries/get-all-products-paths-query.ts new file mode 100644 index 000000000..56298c204 --- /dev/null +++ b/framework/shopify/utils/queries/get-all-products-paths-query.ts @@ -0,0 +1,17 @@ +const getAllProductsPathsQuery = /* GraphQL */ ` + query getAllProductPaths($first: Int = 250, $cursor: String) { + products(first: $first, after: $cursor) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + handle + } + cursor + } + } + } +` +export default getAllProductsPathsQuery diff --git a/framework/shopify/utils/queries/get-all-products-query.ts b/framework/shopify/utils/queries/get-all-products-query.ts new file mode 100644 index 000000000..4a6c20b6e --- /dev/null +++ b/framework/shopify/utils/queries/get-all-products-query.ts @@ -0,0 +1,54 @@ +export const productsFragment = ` +products( + first: $first + sortKey: $sortKey + reverse: $reverse + query: $query +) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + id + title + vendor + handle + description + priceRange { + minVariantPrice { + amount + currencyCode + } + } + images(first: 1) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + originalSrc + altText + width + height + } + } + } + } + } +} +` + +const getAllProductsQuery = /* GraphQL */ ` + query getAllProducts( + $first: Int = 250 + $query: String = "" + $sortKey: ProductSortKeys = RELEVANCE + $reverse: Boolean = false + ) { + ${productsFragment} + } +` +export default getAllProductsQuery diff --git a/framework/shopify/utils/queries/get-checkout-query.ts b/framework/shopify/utils/queries/get-checkout-query.ts new file mode 100644 index 000000000..194e1619a --- /dev/null +++ b/framework/shopify/utils/queries/get-checkout-query.ts @@ -0,0 +1,62 @@ +export const checkoutDetailsFragment = ` + id + webUrl + subtotalPriceV2{ + amount + currencyCode + } + totalTaxV2 { + amount + currencyCode + } + totalPriceV2 { + amount + currencyCode + } + completedAt + createdAt + taxesIncluded + lineItems(first: 250) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + id + title + variant { + id + sku + title + image { + originalSrc + altText + width + height + } + priceV2{ + amount + currencyCode + } + compareAtPriceV2{ + amount + currencyCode + } + } + quantity + } + } + } +` + +const getCheckoutQuery = /* GraphQL */ ` + query($checkoutId: ID!) { + node(id: $checkoutId) { + ... on Checkout { + ${checkoutDetailsFragment} + } + } + } +` +export default getCheckoutQuery diff --git a/framework/shopify/utils/queries/get-collection-products-query.ts b/framework/shopify/utils/queries/get-collection-products-query.ts new file mode 100644 index 000000000..dd504b575 --- /dev/null +++ b/framework/shopify/utils/queries/get-collection-products-query.ts @@ -0,0 +1,17 @@ +import { productsFragment } from './get-all-products-query' + +const getCollectionProductsQuery = /* GraphQL */ ` + query getProductsFromCollection( + $categoryHandle: String! + $first: Int = 250 + $query: String = "" + $sortKey: ProductSortKeys = RELEVANCE + $reverse: Boolean = false + ) { + collectionByHandle(handle: $categoryHandle) + { + ${productsFragment} + } + } +` +export default getCollectionProductsQuery diff --git a/framework/shopify/utils/queries/get-customer-id-query.ts b/framework/shopify/utils/queries/get-customer-id-query.ts new file mode 100644 index 000000000..076ceb10b --- /dev/null +++ b/framework/shopify/utils/queries/get-customer-id-query.ts @@ -0,0 +1,8 @@ +export const getCustomerQuery = /* GraphQL */ ` + query getCustomerId($customerAccessToken: String!) { + customer(customerAccessToken: $customerAccessToken) { + id + } + } +` +export default getCustomerQuery diff --git a/framework/shopify/utils/queries/get-customer-query.ts b/framework/shopify/utils/queries/get-customer-query.ts new file mode 100644 index 000000000..87e37e68d --- /dev/null +++ b/framework/shopify/utils/queries/get-customer-query.ts @@ -0,0 +1,16 @@ +export const getCustomerQuery = /* GraphQL */ ` + query getCustomer($customerAccessToken: String!) { + customer(customerAccessToken: $customerAccessToken) { + id + firstName + lastName + displayName + email + phone + tags + acceptsMarketing + createdAt + } + } +` +export default getCustomerQuery diff --git a/framework/shopify/utils/queries/get-page-query.ts b/framework/shopify/utils/queries/get-page-query.ts new file mode 100644 index 000000000..dcafdc30d --- /dev/null +++ b/framework/shopify/utils/queries/get-page-query.ts @@ -0,0 +1,13 @@ +export const getPageQuery = /* GraphQL */ ` + query getPageBySlug($slug: String!) { + pageByHandle(handle: $slug) { + id + title + handle + body + bodySummary + url + } + } +` +export default getPageQuery diff --git a/framework/shopify/utils/queries/get-product-query.ts b/framework/shopify/utils/queries/get-product-query.ts new file mode 100644 index 000000000..d054c023d --- /dev/null +++ b/framework/shopify/utils/queries/get-product-query.ts @@ -0,0 +1,68 @@ +const getProductQuery = /* GraphQL */ ` + query getProductBySlug($slug: String!) { + productByHandle(handle: $slug) { + id + handle + title + productType + vendor + description + descriptionHtml + options { + id + name + values + } + priceRange { + maxVariantPrice { + amount + currencyCode + } + minVariantPrice { + amount + currencyCode + } + } + variants(first: 250) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + id + title + selectedOptions { + name + value + } + priceV2 { + amount + currencyCode + } + compareAtPriceV2 { + amount + currencyCode + } + } + } + } + images(first: 250) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + originalSrc + altText + width + height + } + } + } + } + } +` + +export default getProductQuery diff --git a/framework/shopify/utils/queries/index.ts b/framework/shopify/utils/queries/index.ts new file mode 100644 index 000000000..e19be9c8c --- /dev/null +++ b/framework/shopify/utils/queries/index.ts @@ -0,0 +1,10 @@ +export { default as getSiteCollectionsQuery } from './get-all-collections-query' +export { default as getProductQuery } from './get-product-query' +export { default as getAllProductsQuery } from './get-all-products-query' +export { default as getAllProductsPathtsQuery } from './get-all-products-paths-query' +export { default as getAllProductVendors } from './get-all-product-vendors-query' +export { default as getCollectionProductsQuery } from './get-collection-products-query' +export { default as getCheckoutQuery } from './get-checkout-query' +export { default as getAllPagesQuery } from './get-all-pages-query' +export { default as getPageQuery } from './get-page-query' +export { default as getCustomerQuery } from './get-customer-query' diff --git a/framework/shopify/wishlist/use-wishlist.tsx b/framework/shopify/wishlist/use-wishlist.tsx index 2aac16810..f8db4216f 100644 --- a/framework/shopify/wishlist/use-wishlist.tsx +++ b/framework/shopify/wishlist/use-wishlist.tsx @@ -1,7 +1,7 @@ import { HookFetcher } from '@commerce/utils/types' import { SwrOptions } from '@commerce/utils/use-data' import useCommerceWishlist from '@commerce/wishlist/use-wishlist' -import { Product } from '../types' +import { Product } from '../schema' import useCustomer from '../customer/use-customer' const defaultOpts = {} diff --git a/next.config.js b/next.config.js index 725024066..742a0690e 100644 --- a/next.config.js +++ b/next.config.js @@ -1,7 +1,8 @@ const withCommerceConfig = require('./framework/commerce/with-config') -const commerce = { provider: 'bigcommerce' } +const commerce = { provider: 'shopify' } const isBC = commerce.provider === 'bigcommerce' +const isShopify = commerce.provider === 'shopify' module.exports = withCommerceConfig({ commerce, @@ -11,7 +12,7 @@ module.exports = withCommerceConfig({ }, rewrites() { return [ - isBC && { + (isBC || isShopify) && { source: '/checkout', destination: '/api/bigcommerce/checkout', }, diff --git a/tsconfig.json b/tsconfig.json index 9e712fb18..e20f37099 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,8 +22,8 @@ "@components/*": ["components/*"], "@commerce": ["framework/commerce"], "@commerce/*": ["framework/commerce/*"], - "@framework": ["framework/bigcommerce"], - "@framework/*": ["framework/bigcommerce/*"] + "@framework": ["framework/shopify"], + "@framework/*": ["framework/shopify/*"] } }, "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], From 121ec4b61f9e778d7905de4b7059ecbf38febbad Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 25 Feb 2021 16:39:01 -0500 Subject: [PATCH 176/221] Updated the commerce config structure --- commerce.config.json | 6 ++++++ .../bigcommerce/{config.json => commerce.config.json} | 1 + framework/bigcommerce/next.config.js | 7 ++----- framework/shopify/{config.json => commerce.config.json} | 1 + framework/shopify/next.config.js | 7 ++----- next.config.js | 4 +++- tsconfig.json | 4 ++-- 7 files changed, 17 insertions(+), 13 deletions(-) create mode 100644 commerce.config.json rename framework/bigcommerce/{config.json => commerce.config.json} (60%) rename framework/shopify/{config.json => commerce.config.json} (64%) diff --git a/commerce.config.json b/commerce.config.json new file mode 100644 index 000000000..1c14a53f5 --- /dev/null +++ b/commerce.config.json @@ -0,0 +1,6 @@ +{ + "provider": "bigcommerce", + "features": { + "wishlist": false + } +} diff --git a/framework/bigcommerce/config.json b/framework/bigcommerce/commerce.config.json similarity index 60% rename from framework/bigcommerce/config.json rename to framework/bigcommerce/commerce.config.json index a0e7afc5d..3a8738f47 100644 --- a/framework/bigcommerce/config.json +++ b/framework/bigcommerce/commerce.config.json @@ -1,4 +1,5 @@ { + "provider": "bigcommerce", "features": { "wishlist": true } diff --git a/framework/bigcommerce/next.config.js b/framework/bigcommerce/next.config.js index 5703f2343..f33b16630 100644 --- a/framework/bigcommerce/next.config.js +++ b/framework/bigcommerce/next.config.js @@ -1,10 +1,7 @@ -const providerConfig = require('./config.json') +const commerce = require('./commerce.config.json') module.exports = { - commerce: { - provider: 'bigcommerce', - ...providerConfig, - }, + commerce, images: { domains: ['cdn11.bigcommerce.com'], }, diff --git a/framework/shopify/config.json b/framework/shopify/commerce.config.json similarity index 64% rename from framework/shopify/config.json rename to framework/shopify/commerce.config.json index 17ef37e25..b30ab39d9 100644 --- a/framework/shopify/config.json +++ b/framework/shopify/commerce.config.json @@ -1,4 +1,5 @@ { + "provider": "shopify", "features": { "wishlist": false } diff --git a/framework/shopify/next.config.js b/framework/shopify/next.config.js index 0f9bc31ff..e9d48c02c 100644 --- a/framework/shopify/next.config.js +++ b/framework/shopify/next.config.js @@ -1,10 +1,7 @@ -const providerConfig = require('./config.json') +const commerce = require('./commerce.config.json') module.exports = { - commerce: { - provider: 'shopify', - ...providerConfig, - }, + commerce, images: { domains: ['cdn.shopify.com'], }, diff --git a/next.config.js b/next.config.js index 742a0690e..046ba2aa1 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,6 @@ +const commerce = require('./commerce.config.json') const withCommerceConfig = require('./framework/commerce/with-config') -const commerce = { provider: 'shopify' } const isBC = commerce.provider === 'bigcommerce' const isShopify = commerce.provider === 'shopify' @@ -39,3 +39,5 @@ module.exports = withCommerceConfig({ ].filter((x) => x) }, }) + +console.log('configs', module.exports) diff --git a/tsconfig.json b/tsconfig.json index e20f37099..9e712fb18 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,8 +22,8 @@ "@components/*": ["components/*"], "@commerce": ["framework/commerce"], "@commerce/*": ["framework/commerce/*"], - "@framework": ["framework/shopify"], - "@framework/*": ["framework/shopify/*"] + "@framework": ["framework/bigcommerce"], + "@framework/*": ["framework/bigcommerce/*"] } }, "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], From 46ae76c67f3d1e0fb987cf85aca3f7afb54c2df0 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 25 Feb 2021 16:54:18 -0500 Subject: [PATCH 177/221] Removed @framework imports within framework providers --- framework/bigcommerce/product/get-product.ts | 2 +- framework/shopify/api/checkout/index.ts | 4 ++-- framework/shopify/api/index.ts | 2 +- framework/shopify/api/utils/fetch-all-products.ts | 2 +- framework/shopify/api/utils/fetch-graphql-api.ts | 2 +- framework/shopify/auth/use-login.tsx | 4 ++-- framework/shopify/auth/use-logout.tsx | 7 ++----- framework/shopify/auth/use-signup.tsx | 6 +++--- framework/shopify/cart/use-remove-item.tsx | 9 +++------ framework/shopify/cart/use-update-item.tsx | 5 +---- framework/shopify/cart/utils/checkout-create.ts | 4 ++-- framework/shopify/cart/utils/checkout-to-cart.ts | 4 ++-- framework/shopify/common/get-site-info.ts | 4 ++-- framework/shopify/customer/get-customer-id.ts | 4 ++-- framework/shopify/product/get-all-collections.ts | 2 +- framework/shopify/product/get-all-products.ts | 2 +- framework/shopify/product/use-search.tsx | 12 +++++++----- framework/shopify/utils/get-categories.ts | 4 ++-- framework/shopify/utils/get-search-variables.ts | 2 +- framework/shopify/utils/get-vendors.ts | 4 ++-- framework/shopify/utils/normalize.ts | 2 +- 21 files changed, 40 insertions(+), 47 deletions(-) diff --git a/framework/bigcommerce/product/get-product.ts b/framework/bigcommerce/product/get-product.ts index 7d77eb194..b52568b62 100644 --- a/framework/bigcommerce/product/get-product.ts +++ b/framework/bigcommerce/product/get-product.ts @@ -2,7 +2,7 @@ import type { GetProductQuery, GetProductQueryVariables } from '../schema' import setProductLocaleMeta from '../api/utils/set-product-locale-meta' import { productInfoFragment } from '../api/fragments/product' import { BigcommerceConfig, getConfig } from '../api' -import { normalizeProduct } from '@framework/lib/normalize' +import { normalizeProduct } from '../lib/normalize' import type { Product } from '@commerce/types' export const getProductQuery = /* GraphQL */ ` diff --git a/framework/shopify/api/checkout/index.ts b/framework/shopify/api/checkout/index.ts index d5d6d7f6e..244078466 100644 --- a/framework/shopify/api/checkout/index.ts +++ b/framework/shopify/api/checkout/index.ts @@ -7,10 +7,10 @@ import { SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CHECKOUT_URL_COOKIE, SHOPIFY_CUSTOMER_TOKEN_COOKIE, -} from '@framework/const' +} from '../../const' import { getConfig } from '..' -import associateCustomerWithCheckoutMutation from '@framework/utils/mutations/associate-customer-with-checkout' +import associateCustomerWithCheckoutMutation from '../../utils/mutations/associate-customer-with-checkout' const METHODS = ['GET'] diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts index 184d03748..0fe58f2df 100644 --- a/framework/shopify/api/index.ts +++ b/framework/shopify/api/index.ts @@ -5,7 +5,7 @@ import { API_TOKEN, SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CUSTOMER_TOKEN_COOKIE, -} from '@framework/const' +} from '../const' if (!API_URL) { console.log(process.env) diff --git a/framework/shopify/api/utils/fetch-all-products.ts b/framework/shopify/api/utils/fetch-all-products.ts index efeb809f1..9fa70a5ee 100644 --- a/framework/shopify/api/utils/fetch-all-products.ts +++ b/framework/shopify/api/utils/fetch-all-products.ts @@ -1,4 +1,4 @@ -import { ProductEdge } from '@framework/schema' +import { ProductEdge } from '../../schema' import { ShopifyConfig } from '..' const fetchAllProducts = async ({ diff --git a/framework/shopify/api/utils/fetch-graphql-api.ts b/framework/shopify/api/utils/fetch-graphql-api.ts index 92d4f2cf6..321cba2aa 100644 --- a/framework/shopify/api/utils/fetch-graphql-api.ts +++ b/framework/shopify/api/utils/fetch-graphql-api.ts @@ -2,7 +2,7 @@ import type { GraphQLFetcher } from '@commerce/api' import fetch from './fetch' import { API_URL, API_TOKEN } from '../../const' -import { getError } from '@framework/utils/handle-fetch-response' +import { getError } from '../../utils/handle-fetch-response' const fetchGraphqlApi: GraphQLFetcher = async ( query: string, diff --git a/framework/shopify/auth/use-login.tsx b/framework/shopify/auth/use-login.tsx index 32dd91920..188dd54a2 100644 --- a/framework/shopify/auth/use-login.tsx +++ b/framework/shopify/auth/use-login.tsx @@ -8,9 +8,9 @@ import { CustomerUserError, Mutation, MutationCheckoutCreateArgs, -} from '@framework/schema' +} from '../schema' import useLogin, { UseLogin } from '@commerce/auth/use-login' -import { setCustomerToken } from '@framework/utils' +import { setCustomerToken } from '../utils' export default useLogin as UseLogin<typeof handler> diff --git a/framework/shopify/auth/use-logout.tsx b/framework/shopify/auth/use-logout.tsx index ccbeb8166..81a3b8cdd 100644 --- a/framework/shopify/auth/use-logout.tsx +++ b/framework/shopify/auth/use-logout.tsx @@ -2,11 +2,8 @@ import { useCallback } from 'react' import type { MutationHook } from '@commerce/utils/types' import useLogout, { UseLogout } from '@commerce/auth/use-logout' import useCustomer from '../customer/use-customer' -import customerAccessTokenDeleteMutation from '@framework/utils/mutations/customer-access-token-delete' -import { - getCustomerToken, - setCustomerToken, -} from '@framework/utils/customer-token' +import customerAccessTokenDeleteMutation from '../utils/mutations/customer-access-token-delete' +import { getCustomerToken, setCustomerToken } from '../utils/customer-token' export default useLogout as UseLogout<typeof handler> diff --git a/framework/shopify/auth/use-signup.tsx b/framework/shopify/auth/use-signup.tsx index f072f1925..7f66448d3 100644 --- a/framework/shopify/auth/use-signup.tsx +++ b/framework/shopify/auth/use-signup.tsx @@ -3,13 +3,13 @@ import type { MutationHook } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' import useSignup, { UseSignup } from '@commerce/auth/use-signup' import useCustomer from '../customer/use-customer' -import { CustomerCreateInput } from '@framework/schema' +import { CustomerCreateInput } from '../schema' import { customerCreateMutation, customerAccessTokenCreateMutation, -} from '@framework/utils/mutations' -import handleLogin from '@framework/utils/handle-login' +} from '../utils/mutations' +import handleLogin from '../utils/handle-login' export default useSignup as UseSignup<typeof handler> diff --git a/framework/shopify/cart/use-remove-item.tsx b/framework/shopify/cart/use-remove-item.tsx index 1963176c8..e2aef13d8 100644 --- a/framework/shopify/cart/use-remove-item.tsx +++ b/framework/shopify/cart/use-remove-item.tsx @@ -13,13 +13,10 @@ import useRemoveItem, { } from '@commerce/cart/use-remove-item' import useCart from './use-cart' -import { checkoutLineItemRemoveMutation, getCheckoutId } from '@framework/utils' +import { checkoutLineItemRemoveMutation, getCheckoutId } from '../utils' import { checkoutToCart } from './utils' -import { Cart, LineItem } from '@framework/types' -import { - Mutation, - MutationCheckoutLineItemsRemoveArgs, -} from '@framework/schema' +import { Cart, LineItem } from '../types' +import { Mutation, MutationCheckoutLineItemsRemoveArgs } from '../schema' import { RemoveCartItemBody } from '@commerce/types' export type RemoveItemFn<T = any> = T extends LineItem diff --git a/framework/shopify/cart/use-update-item.tsx b/framework/shopify/cart/use-update-item.tsx index 9e89d0aca..666ce3d08 100644 --- a/framework/shopify/cart/use-update-item.tsx +++ b/framework/shopify/cart/use-update-item.tsx @@ -15,10 +15,7 @@ import { handler as removeItemHandler } from './use-remove-item' import type { Cart, LineItem, UpdateCartItemBody } from '../types' import { checkoutToCart } from './utils' import { getCheckoutId, checkoutLineItemUpdateMutation } from '../utils' -import { - Mutation, - MutationCheckoutLineItemsUpdateArgs, -} from '@framework/schema' +import { Mutation, MutationCheckoutLineItemsUpdateArgs } from '../schema' export type UpdateItemInput<T = any> = T extends LineItem ? Partial<UpdateItemInputBase<LineItem>> diff --git a/framework/shopify/cart/utils/checkout-create.ts b/framework/shopify/cart/utils/checkout-create.ts index 0e71be62f..6c4f81c21 100644 --- a/framework/shopify/cart/utils/checkout-create.ts +++ b/framework/shopify/cart/utils/checkout-create.ts @@ -1,9 +1,9 @@ import { SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CHECKOUT_URL_COOKIE, -} from '@framework/const' +} from '../../const' -import checkoutCreateMutation from '@framework/utils/mutations/checkout-create' +import checkoutCreateMutation from '../../utils/mutations/checkout-create' import Cookies from 'js-cookie' export const checkoutCreate = async (fetch: any) => { diff --git a/framework/shopify/cart/utils/checkout-to-cart.ts b/framework/shopify/cart/utils/checkout-to-cart.ts index 662db1c45..fa8b988f9 100644 --- a/framework/shopify/cart/utils/checkout-to-cart.ts +++ b/framework/shopify/cart/utils/checkout-to-cart.ts @@ -6,8 +6,8 @@ import { CheckoutLineItemsRemovePayload, CheckoutLineItemsUpdatePayload, Maybe, -} from '@framework/schema' -import { normalizeCart } from '@framework/utils' +} from '../../schema' +import { normalizeCart } from '../../utils' export type CheckoutPayload = | CheckoutLineItemsAddPayload diff --git a/framework/shopify/common/get-site-info.ts b/framework/shopify/common/get-site-info.ts index f6cdaad85..cbbacf5b6 100644 --- a/framework/shopify/common/get-site-info.ts +++ b/framework/shopify/common/get-site-info.ts @@ -1,5 +1,5 @@ -import getCategories, { Category } from '@framework/utils/get-categories' -import getVendors, { Brands } from '@framework/utils/get-vendors' +import getCategories, { Category } from '../utils/get-categories' +import getVendors, { Brands } from '../utils/get-vendors' import { getConfig, ShopifyConfig } from '../api' diff --git a/framework/shopify/customer/get-customer-id.ts b/framework/shopify/customer/get-customer-id.ts index 78309a8ec..ca096645a 100644 --- a/framework/shopify/customer/get-customer-id.ts +++ b/framework/shopify/customer/get-customer-id.ts @@ -1,5 +1,5 @@ -import { getConfig, ShopifyConfig } from '@framework/api' -import getCustomerIdQuery from '@framework/utils/queries/get-customer-id-query' +import { getConfig, ShopifyConfig } from '../api' +import getCustomerIdQuery from '../utils/queries/get-customer-id-query' import Cookies from 'js-cookie' async function getCustomerId({ diff --git a/framework/shopify/product/get-all-collections.ts b/framework/shopify/product/get-all-collections.ts index bf3fee392..15c4bc51a 100644 --- a/framework/shopify/product/get-all-collections.ts +++ b/framework/shopify/product/get-all-collections.ts @@ -1,4 +1,4 @@ -import { CollectionEdge } from '@framework/schema' +import { CollectionEdge } from '../schema' import { getConfig, ShopifyConfig } from '../api' import getAllCollectionsQuery from '../utils/queries/get-all-collections-query' diff --git a/framework/shopify/product/get-all-products.ts b/framework/shopify/product/get-all-products.ts index e1eb96aac..14e486563 100644 --- a/framework/shopify/product/get-all-products.ts +++ b/framework/shopify/product/get-all-products.ts @@ -2,7 +2,7 @@ import { GraphQLFetcherResult } from '@commerce/api' import { getConfig, ShopifyConfig } from '../api' import { ProductEdge } from '../schema' import { getAllProductsQuery } from '../utils/queries' -import { normalizeProduct } from '@framework/utils/normalize' +import { normalizeProduct } from '../utils/normalize' import { Product } from '@commerce/types' type Variables = { diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx index 7ca37916b..4b14249ca 100644 --- a/framework/shopify/product/use-search.tsx +++ b/framework/shopify/product/use-search.tsx @@ -1,17 +1,16 @@ import { SWRHook } from '@commerce/utils/types' import useSearch, { UseSearch } from '@commerce/product/use-search' -import { ProductEdge } from '@framework/schema' +import { ProductEdge } from '../schema' import { getAllProductsQuery, getSearchVariables, normalizeProduct, -} from '@framework/utils' -import type { ShopifyProvider } from '..' +} from '../utils' import { Product } from '@commerce/types' -export default useSearch as UseSearch<ShopifyProvider> +export default useSearch as UseSearch<typeof handler> export type SearchProductsInput = { search?: string @@ -40,7 +39,10 @@ export const handler: SWRHook< }) const edges = resp.products?.edges return { - products: edges?.map(({ node: p }: ProductEdge) => normalizeProduct(p)), + products: edges?.map(({ node: p }: ProductEdge) => + // TODO: Fix this product type + normalizeProduct(p as any) + ), found: !!edges?.length, } }, diff --git a/framework/shopify/utils/get-categories.ts b/framework/shopify/utils/get-categories.ts index 942ec9c62..e1176b068 100644 --- a/framework/shopify/utils/get-categories.ts +++ b/framework/shopify/utils/get-categories.ts @@ -1,5 +1,5 @@ -import { ShopifyConfig } from '@framework/api' -import { CollectionEdge } from '@framework/schema' +import { ShopifyConfig } from '../api' +import { CollectionEdge } from '../schema' import getSiteCollectionsQuery from './queries/get-all-collections-query' export type Category = { diff --git a/framework/shopify/utils/get-search-variables.ts b/framework/shopify/utils/get-search-variables.ts index 90d35ba50..6f5d08b1a 100644 --- a/framework/shopify/utils/get-search-variables.ts +++ b/framework/shopify/utils/get-search-variables.ts @@ -1,5 +1,5 @@ import getSortVariables from './get-sort-variables' -import type { SearchProductsInput } from '@framework/product/use-search' +import type { SearchProductsInput } from '../product/use-search' export const getSearchVariables = ({ categoryId, diff --git a/framework/shopify/utils/get-vendors.ts b/framework/shopify/utils/get-vendors.ts index d3ebce194..f04483bb1 100644 --- a/framework/shopify/utils/get-vendors.ts +++ b/framework/shopify/utils/get-vendors.ts @@ -1,5 +1,5 @@ -import { ShopifyConfig } from '@framework/api' -import fetchAllProducts from '@framework/api/utils/fetch-all-products' +import { ShopifyConfig } from '../api' +import fetchAllProducts from '../api/utils/fetch-all-products' import getAllProductVendors from './queries/get-all-product-vendors-query' export type BrandNode = { diff --git a/framework/shopify/utils/normalize.ts b/framework/shopify/utils/normalize.ts index 1eee9f336..67ab3a8a2 100644 --- a/framework/shopify/utils/normalize.ts +++ b/framework/shopify/utils/normalize.ts @@ -7,7 +7,7 @@ import { ProductVariantConnection, ProductOption, MoneyV2, -} from '@framework/schema' +} from '../schema' import type { Cart, LineItem } from '../types' From e90d9a2121e4b5f4e8967f9619548ad937956dd3 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 25 Feb 2021 17:18:50 -0500 Subject: [PATCH 178/221] Fixed types --- framework/shopify/api/operations/get-page.ts | 4 +--- framework/shopify/cart/use-add-item.tsx | 3 ++- framework/shopify/cart/use-cart.tsx | 7 +++---- framework/shopify/cart/utils/fetcher.ts | 3 ++- framework/shopify/customer/use-customer.tsx | 4 ++-- framework/shopify/types.ts | 3 --- .../shopify/utils/to-commerce-products.ts | 18 +++++++++++++----- framework/shopify/wishlist/use-wishlist.tsx | 7 +++++-- next.config.js | 2 -- 9 files changed, 28 insertions(+), 23 deletions(-) diff --git a/framework/shopify/api/operations/get-page.ts b/framework/shopify/api/operations/get-page.ts index 11651e335..32acb7c8f 100644 --- a/framework/shopify/api/operations/get-page.ts +++ b/framework/shopify/api/operations/get-page.ts @@ -1,7 +1,5 @@ +import { Page } from '../../schema' import { ShopifyConfig, getConfig } from '..' -import type { Page } from '../../types' - -export type { Page } export type GetPageResult<T extends { page?: any } = { page?: Page }> = T diff --git a/framework/shopify/cart/use-add-item.tsx b/framework/shopify/cart/use-add-item.tsx index 36f02847b..d0f891148 100644 --- a/framework/shopify/cart/use-add-item.tsx +++ b/framework/shopify/cart/use-add-item.tsx @@ -40,7 +40,8 @@ export const handler: MutationHook<Cart, {}, CartItemBody> = { }, }) - return checkoutToCart(checkoutLineItemsAdd) + // TODO: Fix this Cart type here + return checkoutToCart(checkoutLineItemsAdd) as any }, useHook: ({ fetch }) => () => { const { mutate } = useCart() diff --git a/framework/shopify/cart/use-cart.tsx b/framework/shopify/cart/use-cart.tsx index 2cf3a3e95..5f1f87299 100644 --- a/framework/shopify/cart/use-cart.tsx +++ b/framework/shopify/cart/use-cart.tsx @@ -1,6 +1,4 @@ import { useMemo } from 'react' -import type { ShopifyProvider } from '..' - import useCommerceCart, { FetchCartInput, UseCart, @@ -11,7 +9,7 @@ import { SWRHook } from '@commerce/utils/types' import { checkoutCreate, checkoutToCart } from './utils' import getCheckoutQuery from '../utils/queries/get-checkout-query' -export default useCommerceCart as UseCart<ShopifyProvider> +export default useCommerceCart as UseCart<typeof handler> export const handler: SWRHook< Cart | null, @@ -38,7 +36,8 @@ export const handler: SWRHook< checkout = await checkoutCreate(fetch) } - return checkoutToCart({ checkout }) + // TODO: Fix this type + return checkoutToCart({ checkout } as any) }, useHook: ({ useData }) => (input) => { const response = useData({ diff --git a/framework/shopify/cart/utils/fetcher.ts b/framework/shopify/cart/utils/fetcher.ts index a69492f0d..6afb55f18 100644 --- a/framework/shopify/cart/utils/fetcher.ts +++ b/framework/shopify/cart/utils/fetcher.ts @@ -24,7 +24,8 @@ const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ checkout = await checkoutCreate(fetch) } - return checkoutToCart({ checkout }) + // TODO: Fix this type + return checkoutToCart({ checkout } as any) } export default fetcher diff --git a/framework/shopify/customer/use-customer.tsx b/framework/shopify/customer/use-customer.tsx index 91b7281af..137f0da74 100644 --- a/framework/shopify/customer/use-customer.tsx +++ b/framework/shopify/customer/use-customer.tsx @@ -2,9 +2,9 @@ import useCustomer, { UseCustomer } from '@commerce/customer/use-customer' import { Customer } from '@commerce/types' import { SWRHook } from '@commerce/utils/types' import { getCustomerQuery, getCustomerToken } from '../utils' -import type { ShopifyProvider } from '..' -export default useCustomer as UseCustomer<ShopifyProvider> +export default useCustomer as UseCustomer<typeof handler> + export const handler: SWRHook<Customer | null> = { fetchOptions: { query: getCustomerQuery, diff --git a/framework/shopify/types.ts b/framework/shopify/types.ts index 9ad9fd016..c4e42b67d 100644 --- a/framework/shopify/types.ts +++ b/framework/shopify/types.ts @@ -30,9 +30,6 @@ export type CartItemBody = Core.CartItemBody & { optionSelections?: OptionSelections } -type X = Core.CartItemBody extends CartItemBody ? any : never -type Y = CartItemBody extends Core.CartItemBody ? any : never - export type GetCartHandlerBody = Core.GetCartHandlerBody export type AddCartItemBody = Core.AddCartItemBody<CartItemBody> diff --git a/framework/shopify/utils/to-commerce-products.ts b/framework/shopify/utils/to-commerce-products.ts index c0b411eb6..84925e001 100644 --- a/framework/shopify/utils/to-commerce-products.ts +++ b/framework/shopify/utils/to-commerce-products.ts @@ -1,4 +1,8 @@ -import { Product, Image } from '../types' +// TODO: Fix the types in this file +// import { Product, Image } from '../types' + +type Product = any +type Image = any export default function toCommerceProducts(products: Product[]) { return products.map((product: Product) => { @@ -20,10 +24,12 @@ export default function toCommerceProducts(products: Product[]) { url: image.src, } }), - variants: product.variants.map((variant) => { + // TODO: Fix the variant type + variants: product.variants.map((variant: any) => { return { id: variant.id, - options: variant.selectedOptions.map((selectedOption) => { + // TODO: Fix the selectedOption type + options: variant.selectedOptions.map((selectedOption: any) => { return { __typename: 'MultipleChoiceOption', displayName: selectedOption.name, @@ -39,11 +45,13 @@ export default function toCommerceProducts(products: Product[]) { }), } }), - productOptions: product.options.map((option) => { + // TODO: Fix the option type + productOptions: product.options.map((option: any) => { return { __typename: 'MultipleChoiceOption', displayName: option.name, - values: option.values.map((value) => { + // TODO: Fix the value type + values: option.values.map((value: any) => { return { node: { entityId: 1, diff --git a/framework/shopify/wishlist/use-wishlist.tsx b/framework/shopify/wishlist/use-wishlist.tsx index f8db4216f..13632bb95 100644 --- a/framework/shopify/wishlist/use-wishlist.tsx +++ b/framework/shopify/wishlist/use-wishlist.tsx @@ -1,5 +1,7 @@ +// TODO: replace this hook and other wishlist hooks with a handler, or remove them if +// Shopify doesn't have a wishlist + import { HookFetcher } from '@commerce/utils/types' -import { SwrOptions } from '@commerce/utils/use-data' import useCommerceWishlist from '@commerce/wishlist/use-wishlist' import { Product } from '../schema' import useCustomer from '../customer/use-customer' @@ -31,7 +33,8 @@ export const fetcher: HookFetcher<Wishlist | null, UseWishlistInput> = () => { export function extendHook( customFetcher: typeof fetcher, - swrOptions?: SwrOptions<Wishlist | null, UseWishlistInput> + // swrOptions?: SwrOptions<Wishlist | null, UseWishlistInput> + swrOptions?: any ) { const useWishlist = ({ includeProducts }: UseWishlistOptions = {}) => { return { data: null } diff --git a/next.config.js b/next.config.js index 046ba2aa1..7e86695a0 100644 --- a/next.config.js +++ b/next.config.js @@ -39,5 +39,3 @@ module.exports = withCommerceConfig({ ].filter((x) => x) }, }) - -console.log('configs', module.exports) From 75aec1441fd927006e53485d2ff73f2393e03084 Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Thu, 25 Feb 2021 19:34:54 -0300 Subject: [PATCH 179/221] Adding env templates to the providers --- framework/bigcommerce/.env.template | 6 ++++++ framework/shopify/.env.template | 2 ++ 2 files changed, 8 insertions(+) create mode 100644 framework/bigcommerce/.env.template create mode 100644 framework/shopify/.env.template diff --git a/framework/bigcommerce/.env.template b/framework/bigcommerce/.env.template new file mode 100644 index 000000000..83e7dd403 --- /dev/null +++ b/framework/bigcommerce/.env.template @@ -0,0 +1,6 @@ +BIGCOMMERCE_STOREFRONT_API_URL= +BIGCOMMERCE_STOREFRONT_API_TOKEN= +BIGCOMMERCE_STORE_API_URL= +BIGCOMMERCE_STORE_API_TOKEN= +BIGCOMMERCE_STORE_API_CLIENT_ID= +BIGCOMMERCE_CHANNEL_ID= \ No newline at end of file diff --git a/framework/shopify/.env.template b/framework/shopify/.env.template new file mode 100644 index 000000000..24521c2a1 --- /dev/null +++ b/framework/shopify/.env.template @@ -0,0 +1,2 @@ +SHOPIFY_STORE_DOMAIN= +SHOPIFY_STOREFRONT_ACCESS_TOKEN= From fc023de8445fc5fd23553e1dd895598c44f6f774 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Thu, 25 Feb 2021 18:10:59 -0500 Subject: [PATCH 180/221] Ignore some types --- components/wishlist/WishlistButton/WishlistButton.tsx | 2 ++ components/wishlist/WishlistCard/WishlistCard.tsx | 1 + framework/commerce/types.ts | 6 +++--- framework/shopify/cart/use-cart.tsx | 2 +- framework/shopify/cart/utils/checkout-to-cart.ts | 2 +- framework/shopify/index.tsx | 3 ++- framework/shopify/utils/get-categories.ts | 2 +- package.json | 1 + pages/[...pages].tsx | 3 ++- pages/search.tsx | 7 +++++-- pages/wishlist.tsx | 2 ++ scripts/commerce.js | 1 + 12 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 scripts/commerce.js diff --git a/components/wishlist/WishlistButton/WishlistButton.tsx b/components/wishlist/WishlistButton/WishlistButton.tsx index 57f769e3d..290f7f9ec 100644 --- a/components/wishlist/WishlistButton/WishlistButton.tsx +++ b/components/wishlist/WishlistButton/WishlistButton.tsx @@ -26,7 +26,9 @@ const WishlistButton: FC<Props> = ({ const { openModal, setModalView } = useUI() const [loading, setLoading] = useState(false) + // @ts-ignore Wishlist is not always enabled const itemInWishlist = data?.items?.find( + // @ts-ignore Wishlist is not always enabled (item) => item.product_id === productId && (item.variant_id as any) === variant.id ) diff --git a/components/wishlist/WishlistCard/WishlistCard.tsx b/components/wishlist/WishlistCard/WishlistCard.tsx index 5e4cce72a..1568d9e7e 100644 --- a/components/wishlist/WishlistCard/WishlistCard.tsx +++ b/components/wishlist/WishlistCard/WishlistCard.tsx @@ -22,6 +22,7 @@ const WishlistCard: FC<Props> = ({ product }) => { baseAmount: product.prices?.retailPrice?.value, currencyCode: product.prices?.price?.currencyCode!, }) + // @ts-ignore Wishlist is not always enabled const removeItem = useRemoveItem({ wishlist: { includeProducts: true } }) const [loading, setLoading] = useState(false) const [removing, setRemoving] = useState(false) diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index bf635c9dc..a398070ac 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -1,6 +1,6 @@ -import type { Wishlist as BCWishlist } from '@framework/api/wishlist' -import type { Customer as BCCustomer } from '@framework/api/customers' -import type { SearchProductsData as BCSearchProductsData } from '@framework/api/catalog/products' +import type { Wishlist as BCWishlist } from '../bigcommerce/api/wishlist' +import type { Customer as BCCustomer } from '../bigcommerce/api/customers' +import type { SearchProductsData as BCSearchProductsData } from '../bigcommerce/api/catalog/products' export type Discount = { // The value of the discount, can be an amount or percentage diff --git a/framework/shopify/cart/use-cart.tsx b/framework/shopify/cart/use-cart.tsx index 5f1f87299..d154bb837 100644 --- a/framework/shopify/cart/use-cart.tsx +++ b/framework/shopify/cart/use-cart.tsx @@ -4,7 +4,7 @@ import useCommerceCart, { UseCart, } from '@commerce/cart/use-cart' -import { Cart } from '@commerce/types' +import { Cart } from '../types' import { SWRHook } from '@commerce/utils/types' import { checkoutCreate, checkoutToCart } from './utils' import getCheckoutQuery from '../utils/queries/get-checkout-query' diff --git a/framework/shopify/cart/utils/checkout-to-cart.ts b/framework/shopify/cart/utils/checkout-to-cart.ts index fa8b988f9..03005f342 100644 --- a/framework/shopify/cart/utils/checkout-to-cart.ts +++ b/framework/shopify/cart/utils/checkout-to-cart.ts @@ -1,4 +1,4 @@ -import { Cart } from '@commerce/types' +import { Cart } from '../../types' import { CommerceError, ValidationError } from '@commerce/utils/errors' import { diff --git a/framework/shopify/index.tsx b/framework/shopify/index.tsx index 5b25d6b21..c26704771 100644 --- a/framework/shopify/index.tsx +++ b/framework/shopify/index.tsx @@ -28,7 +28,8 @@ export type ShopifyProps = { export function CommerceProvider({ children, ...config }: ShopifyProps) { return ( <CoreCommerceProvider - provider={shopifyProvider} + // TODO: Fix this type + provider={shopifyProvider as any} config={{ ...shopifyConfig, ...config }} > {children} diff --git a/framework/shopify/utils/get-categories.ts b/framework/shopify/utils/get-categories.ts index e1176b068..54048b896 100644 --- a/framework/shopify/utils/get-categories.ts +++ b/framework/shopify/utils/get-categories.ts @@ -3,7 +3,7 @@ import { CollectionEdge } from '../schema' import getSiteCollectionsQuery from './queries/get-all-collections-query' export type Category = { - endityId: string + entityId: string name: string path: string } diff --git a/package.json b/package.json index 906d950dc..491071e55 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "analyze": "BUNDLE_ANALYZE=both yarn build", "prettier-fix": "prettier --write .", "find:unused": "next-unused", + "commerce": "node scripts/commerce.js", "generate": "graphql-codegen", "generate:definitions": "node framework/bigcommerce/scripts/generate-definitions.js" }, diff --git a/pages/[...pages].tsx b/pages/[...pages].tsx index 3f39845b5..67adb6287 100644 --- a/pages/[...pages].tsx +++ b/pages/[...pages].tsx @@ -25,7 +25,8 @@ export async function getStaticProps({ const pageItem = pages.find((p) => (p.url ? getSlug(p.url) === slug : false)) const data = pageItem && - (await getPage({ variables: { id: pageItem.id! }, config, preview })) + // TODO: Shopify - Fix this type + (await getPage({ variables: { id: pageItem.id! } as any, config, preview })) const page = data?.page if (!page) { diff --git a/pages/search.tsx b/pages/search.tsx index a05203892..da2edccd8 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -75,8 +75,10 @@ export default function Search({ const { data } = useSearch({ search: typeof q === 'string' ? q : '', - categoryId: activeCategory?.entityId, - brandId: activeBrand?.entityId, + // TODO: Shopify - Fix this type + categoryId: activeCategory?.entityId as any, + // TODO: Shopify - Fix this type + brandId: (activeBrand as any)?.entityId, sort: typeof sort === 'string' ? sort : '', }) @@ -266,6 +268,7 @@ export default function Search({ className={cn( 'block text-sm leading-5 text-gray-700 hover:bg-gray-100 lg:hover:bg-transparent hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900', { + // @ts-ignore Shopify - Fix this types underline: activeBrand?.entityId === node.entityId, } )} diff --git a/pages/wishlist.tsx b/pages/wishlist.tsx index ce97532b0..9938698d4 100644 --- a/pages/wishlist.tsx +++ b/pages/wishlist.tsx @@ -35,6 +35,7 @@ export async function getStaticProps({ export default function Wishlist() { const { data: customer } = useCustomer() + // @ts-ignore Shopify - Fix this types const { data, isLoading, isEmpty } = useWishlist() const router = useRouter() @@ -57,6 +58,7 @@ export default function Wishlist() { </div> ) : ( data && + // @ts-ignore Shopify - Fix this types data.items?.map((item) => ( <WishlistCard key={item.id} product={item as any} /> )) diff --git a/scripts/commerce.js b/scripts/commerce.js new file mode 100644 index 000000000..e6eefa224 --- /dev/null +++ b/scripts/commerce.js @@ -0,0 +1 @@ +console.log('Hello') From 42be44c7ee62b3139e6eba8518c5dd03c85e8908 Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Fri, 26 Feb 2021 14:25:02 -0300 Subject: [PATCH 181/221] Adding link for Cart --- commerce.config.json | 3 ++- .../cart/CartSidebarView/CartSidebarView.tsx | 25 +++++++++++-------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/commerce.config.json b/commerce.config.json index 1c14a53f5..05dd2a043 100644 --- a/commerce.config.json +++ b/commerce.config.json @@ -1,6 +1,7 @@ { "provider": "bigcommerce", "features": { - "wishlist": false + "wishlist": true, + "customCheckout": true } } diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index cb932247f..326390327 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -1,14 +1,14 @@ import { FC } from 'react' import cn from 'classnames' -import { UserNav } from '@components/common' -import { Button } from '@components/ui' -import { Bag, Cross, Check } from '@components/icons' -import { useUI } from '@components/ui/context' -import useCart from '@framework/cart/use-cart' -import usePrice from '@framework/product/use-price' +import Link from 'next/link' import CartItem from '../CartItem' import s from './CartSidebarView.module.css' -import { LineItem } from '@commerce/types' +import { Button } from '@components/ui' +import { UserNav } from '@components/common' +import { useUI } from '@components/ui/context' +import { Bag, Cross, Check } from '@components/icons' +import useCart from '@framework/cart/use-cart' +import usePrice from '@framework/product/use-price' const CartSidebarView: FC = () => { const { closeSidebar } = useUI() @@ -88,9 +88,14 @@ const CartSidebarView: FC = () => { ) : ( <> <div className="px-4 sm:px-6 flex-1"> - <h2 className="pt-1 pb-4 text-2xl leading-7 font-bold text-base tracking-wide"> - My Cart - </h2> + <Link href="/cart"> + <h2 + className="pt-1 pb-4 text-2xl leading-7 font-bold text-base tracking-wide cursor-pointer inline-block" + onClick={handleClose} + > + My Cart + </h2> + </Link> <ul className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accents-3 border-t border-accents-3"> {data!.lineItems.map((item: any) => ( <CartItem From 3acca7cc17a78e2b31f2e8c775f6946e43dd36fb Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Fri, 26 Feb 2021 15:52:09 -0300 Subject: [PATCH 182/221] Adding customCheckout --- commerce.config.json | 2 +- components/icons/CreditCard.tsx | 20 ++++++++++++++++++++ components/icons/MapPin.tsx | 20 ++++++++++++++++++++ components/icons/index.ts | 2 ++ components/ui/context.tsx | 7 ++++++- pages/cart.tsx | 33 +++++++++++++++++++++++++++++++-- 6 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 components/icons/CreditCard.tsx create mode 100644 components/icons/MapPin.tsx diff --git a/commerce.config.json b/commerce.config.json index 05dd2a043..bef7db222 100644 --- a/commerce.config.json +++ b/commerce.config.json @@ -2,6 +2,6 @@ "provider": "bigcommerce", "features": { "wishlist": true, - "customCheckout": true + "customCheckout": false } } diff --git a/components/icons/CreditCard.tsx b/components/icons/CreditCard.tsx new file mode 100644 index 000000000..85504d8ba --- /dev/null +++ b/components/icons/CreditCard.tsx @@ -0,0 +1,20 @@ +const CreditCard = ({ ...props }) => { + return ( + <svg + viewBox="0 0 24 24" + width="24" + height="24" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + fill="none" + shapeRendering="geometricPrecision" + > + <rect x="1" y="4" width="22" height="16" rx="2" ry="2" /> + <path d="M1 10h22" /> + </svg> + ) +} + +export default CreditCard diff --git a/components/icons/MapPin.tsx b/components/icons/MapPin.tsx new file mode 100644 index 000000000..6323b9c1c --- /dev/null +++ b/components/icons/MapPin.tsx @@ -0,0 +1,20 @@ +const MapPin = ({ ...props }) => { + return ( + <svg + viewBox="0 0 24 24" + width="24" + height="24" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + fill="none" + shapeRendering="geometricPrecision" + > + <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z" /> + <circle cx="12" cy="10" r="3" /> + </svg> + ) +} + +export default MapPin diff --git a/components/icons/index.ts b/components/icons/index.ts index 6e57ab0e8..1f2089085 100644 --- a/components/icons/index.ts +++ b/components/icons/index.ts @@ -14,3 +14,5 @@ export { default as RightArrow } from './RightArrow' export { default as Info } from './Info' export { default as ChevronUp } from './ChevronUp' export { default as Vercel } from './Vercel' +export { default as MapPin } from './MapPin' +export { default as CreditCard } from './CreditCard' diff --git a/components/ui/context.tsx b/components/ui/context.tsx index 13992a736..f66adb9d7 100644 --- a/components/ui/context.tsx +++ b/components/ui/context.tsx @@ -59,7 +59,12 @@ type Action = value: string } -type MODAL_VIEWS = 'SIGNUP_VIEW' | 'LOGIN_VIEW' | 'FORGOT_VIEW' +type MODAL_VIEWS = + | 'SIGNUP_VIEW' + | 'LOGIN_VIEW' + | 'FORGOT_VIEW' + | 'NEW_SHIPPING_ADDRESS' + | 'NEW_PAYMENT_METHOD' type ToastText = string export const UIContext = React.createContext<State | any>(initialState) diff --git a/pages/cart.tsx b/pages/cart.tsx index 8b2dbb57b..cd5bedacc 100644 --- a/pages/cart.tsx +++ b/pages/cart.tsx @@ -5,7 +5,7 @@ import useCart from '@framework/cart/use-cart' import usePrice from '@framework/product/use-price' import { Layout } from '@components/common' import { Button, Text } from '@components/ui' -import { Bag, Cross, Check } from '@components/icons' +import { Bag, Cross, Check, MapPin, CreditCard } from '@components/icons' import { CartItem } from '@components/cart' export async function getStaticProps({ @@ -38,7 +38,7 @@ export default function Cart() { ) return ( - <div className="grid lg:grid-cols-12"> + <div className="grid lg:grid-cols-12 w-full max-w-7xl mx-auto"> <div className="lg:col-span-8"> {isLoading || isEmpty ? ( <div className="flex-1 px-12 py-24 flex flex-col justify-center items-center "> @@ -103,6 +103,35 @@ export default function Cart() { </div> <div className="lg:col-span-4"> <div className="flex-shrink-0 px-4 py-24 sm:px-6"> + {process.env.COMMERCE_CUSTOMCHECKOUT_ENABLED && ( + <> + {/* Shipping Address */} + {/* Only available with customCheckout set to true - Meaning that the provider does offer checkout functionality. */} + <div className="rounded-md border border-accents-2 px-6 py-6 mb-4 text-center flex items-center justify-center cursor-pointer hover:border-accents-4"> + <div className="mr-5"> + <MapPin /> + </div> + <div className="text-sm text-center font-medium"> + <span className="uppercase">+ Add Shipping Address</span> + {/* <span> + 1046 Kearny Street.<br/> + San Franssisco, California + </span> */} + </div> + </div> + {/* Payment Method */} + {/* Only available with customCheckout set to true - Meaning that the provider does offer checkout functionality. */} + <div className="rounded-md border border-accents-2 px-6 py-6 mb-4 text-center flex items-center justify-center cursor-pointer hover:border-accents-4"> + <div className="mr-5"> + <CreditCard /> + </div> + <div className="text-sm text-center font-medium"> + <span className="uppercase">+ Add Payment Method</span> + {/* <span>VISA #### #### #### 2345</span> */} + </div> + </div> + </> + )} <div className="border-t border-accents-2"> <ul className="py-3"> <li className="flex justify-between py-1"> From 751011767a93e670755f24cec490cfd86b5faaf1 Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Fri, 26 Feb 2021 21:35:09 -0500 Subject: [PATCH 183/221] multiple changes to fix the wishlist --- .../wishlist/WishlistButton/WishlistButton.tsx | 3 ++- framework/bigcommerce/api/utils/parse-item.ts | 13 ++++++++++--- .../bigcommerce/customer/get-customer-wishlist.ts | 5 +++-- framework/bigcommerce/wishlist/use-wishlist.tsx | 4 ++-- framework/shopify/api/index.ts | 1 - package.json | 1 - pages/wishlist.tsx | 8 ++------ scripts/commerce.js | 1 - 8 files changed, 19 insertions(+), 17 deletions(-) delete mode 100644 scripts/commerce.js diff --git a/components/wishlist/WishlistButton/WishlistButton.tsx b/components/wishlist/WishlistButton/WishlistButton.tsx index 290f7f9ec..6dc59b900 100644 --- a/components/wishlist/WishlistButton/WishlistButton.tsx +++ b/components/wishlist/WishlistButton/WishlistButton.tsx @@ -30,7 +30,8 @@ const WishlistButton: FC<Props> = ({ const itemInWishlist = data?.items?.find( // @ts-ignore Wishlist is not always enabled (item) => - item.product_id === productId && (item.variant_id as any) === variant.id + item.product_id === Number(productId) && + (item.variant_id as any) === Number(variant.id) ) const handleWishlistChange = async (e: any) => { diff --git a/framework/bigcommerce/api/utils/parse-item.ts b/framework/bigcommerce/api/utils/parse-item.ts index dcc716c23..7c8cd4728 100644 --- a/framework/bigcommerce/api/utils/parse-item.ts +++ b/framework/bigcommerce/api/utils/parse-item.ts @@ -1,6 +1,11 @@ import type { ItemBody as WishlistItemBody } from '../wishlist' import type { CartItemBody, OptionSelections } from '../../types' +type BCWishlistItemBody = { + product_id: number + variant_id: number +} + type BCCartItemBody = { product_id: number variant_id: number @@ -8,9 +13,11 @@ type BCCartItemBody = { option_selections?: OptionSelections } -export const parseWishlistItem = (item: WishlistItemBody) => ({ - product_id: item.productId, - variant_id: item.variantId, +export const parseWishlistItem = ( + item: WishlistItemBody +): BCWishlistItemBody => ({ + product_id: Number(item.productId), + variant_id: Number(item.variantId), }) export const parseCartItem = (item: CartItemBody): BCCartItemBody => ({ diff --git a/framework/bigcommerce/customer/get-customer-wishlist.ts b/framework/bigcommerce/customer/get-customer-wishlist.ts index e854ff933..97e5654a9 100644 --- a/framework/bigcommerce/customer/get-customer-wishlist.ts +++ b/framework/bigcommerce/customer/get-customer-wishlist.ts @@ -68,14 +68,15 @@ async function getCustomerWishlist({ const productsById = graphqlData.products.reduce<{ [k: number]: ProductEdge }>((prods, p) => { - prods[Number(p.node.entityId)] = p as any + prods[Number(p.id)] = p as any return prods }, {}) // Populate the wishlist items with the graphql products wishlist.items.forEach((item) => { const product = item && productsById[item.product_id!] if (item && product) { - item.product = product.node + // @ts-ignore Fix this type when the wishlist type is properly defined + item.product = product } }) } diff --git a/framework/bigcommerce/wishlist/use-wishlist.tsx b/framework/bigcommerce/wishlist/use-wishlist.tsx index 3efba7ffd..4850d1cd9 100644 --- a/framework/bigcommerce/wishlist/use-wishlist.tsx +++ b/framework/bigcommerce/wishlist/use-wishlist.tsx @@ -18,7 +18,7 @@ export const handler: SWRHook< url: '/api/bigcommerce/wishlist', method: 'GET', }, - fetcher({ input: { customerId, includeProducts }, options, fetch }) { + async fetcher({ input: { customerId, includeProducts }, options, fetch }) { if (!customerId) return null // Use a dummy base as we only care about the relative path @@ -35,7 +35,7 @@ export const handler: SWRHook< const { data: customer } = useCustomer() const response = useData({ input: [ - ['customerId', (customer as any)?.id], + ['customerId', customer?.entityId], ['includeProducts', input?.includeProducts], ], swrOptions: { diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts index 0fe58f2df..4e23ce99c 100644 --- a/framework/shopify/api/index.ts +++ b/framework/shopify/api/index.ts @@ -8,7 +8,6 @@ import { } from '../const' if (!API_URL) { - console.log(process.env) throw new Error( `The environment variable NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN is missing and it's required to access your store` ) diff --git a/package.json b/package.json index 491071e55..906d950dc 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,6 @@ "analyze": "BUNDLE_ANALYZE=both yarn build", "prettier-fix": "prettier --write .", "find:unused": "next-unused", - "commerce": "node scripts/commerce.js", "generate": "graphql-codegen", "generate:definitions": "node framework/bigcommerce/scripts/generate-definitions.js" }, diff --git a/pages/wishlist.tsx b/pages/wishlist.tsx index 9938698d4..0dddaf23d 100644 --- a/pages/wishlist.tsx +++ b/pages/wishlist.tsx @@ -1,7 +1,4 @@ -import { useEffect } from 'react' -import { useRouter } from 'next/router' import type { GetStaticPropsContext } from 'next' - import { Heart } from '@components/icons' import { Layout } from '@components/common' import { Text, Container } from '@components/ui' @@ -36,8 +33,7 @@ export async function getStaticProps({ export default function Wishlist() { const { data: customer } = useCustomer() // @ts-ignore Shopify - Fix this types - const { data, isLoading, isEmpty } = useWishlist() - const router = useRouter() + const { data, isLoading, isEmpty } = useWishlist({ includeProducts: true }) return ( <Container> @@ -60,7 +56,7 @@ export default function Wishlist() { data && // @ts-ignore Shopify - Fix this types data.items?.map((item) => ( - <WishlistCard key={item.id} product={item as any} /> + <WishlistCard key={item.id} product={item.product! as any} /> )) )} </div> diff --git a/scripts/commerce.js b/scripts/commerce.js deleted file mode 100644 index e6eefa224..000000000 --- a/scripts/commerce.js +++ /dev/null @@ -1 +0,0 @@ -console.log('Hello') From 641ce0aa64cc46fbdc492dc55df7942d1d2be3fa Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Mon, 1 Mar 2021 16:47:30 +0200 Subject: [PATCH 184/221] Shopify Provier Updates (#212) * changes * Adding shopify commit * Changed to query page by id * Fixed page query, Changed use-search GraphQl query * Update use-search.tsx * remove unused util * Changed cookie expiration * Update tsconfig.json Co-authored-by: okbel <curciobel@gmail.com> --- framework/bigcommerce/.env.template | 2 +- framework/shopify/api/index.ts | 4 +- .../shopify/cart/utils/checkout-create.ts | 8 ++- framework/shopify/common/get-all-pages.ts | 3 +- framework/shopify/common/get-page.ts | 15 ++-- framework/shopify/const.ts | 2 + framework/shopify/product/use-search.tsx | 34 ++++++--- framework/shopify/utils/customer-token.ts | 17 +++-- framework/shopify/utils/get-categories.ts | 4 +- .../shopify/utils/get-search-variables.ts | 11 ++- framework/shopify/utils/get-sort-variables.ts | 4 +- framework/shopify/utils/normalize.ts | 59 ++++++++++------ .../utils/queries/get-all-products-query.ts | 69 ++++++++++--------- .../queries/get-collection-products-query.ts | 21 ++++-- .../shopify/utils/queries/get-page-query.ts | 15 ++-- .../utils/queries/get-product-query.ts | 1 + .../shopify/utils/to-commerce-products.ts | 68 ------------------ framework/shopify/wishlist/use-wishlist.tsx | 2 - 18 files changed, 164 insertions(+), 175 deletions(-) delete mode 100644 framework/shopify/utils/to-commerce-products.ts diff --git a/framework/bigcommerce/.env.template b/framework/bigcommerce/.env.template index 83e7dd403..43e85c046 100644 --- a/framework/bigcommerce/.env.template +++ b/framework/bigcommerce/.env.template @@ -3,4 +3,4 @@ BIGCOMMERCE_STOREFRONT_API_TOKEN= BIGCOMMERCE_STORE_API_URL= BIGCOMMERCE_STORE_API_TOKEN= BIGCOMMERCE_STORE_API_CLIENT_ID= -BIGCOMMERCE_CHANNEL_ID= \ No newline at end of file +BIGCOMMERCE_CHANNEL_ID= diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts index 4e23ce99c..4f15cae15 100644 --- a/framework/shopify/api/index.ts +++ b/framework/shopify/api/index.ts @@ -5,6 +5,7 @@ import { API_TOKEN, SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CUSTOMER_TOKEN_COOKIE, + SHOPIFY_COOKIE_EXPIRE, } from '../const' if (!API_URL) { @@ -43,10 +44,11 @@ export class Config { } const config = new Config({ + locale: 'en-US', commerceUrl: API_URL, apiToken: API_TOKEN!, cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, - cartCookieMaxAge: 60 * 60 * 24 * 30, + cartCookieMaxAge: SHOPIFY_COOKIE_EXPIRE, fetch: fetchGraphqlApi, customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE, }) diff --git a/framework/shopify/cart/utils/checkout-create.ts b/framework/shopify/cart/utils/checkout-create.ts index 6c4f81c21..e950cc7e4 100644 --- a/framework/shopify/cart/utils/checkout-create.ts +++ b/framework/shopify/cart/utils/checkout-create.ts @@ -1,6 +1,7 @@ import { SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CHECKOUT_URL_COOKIE, + SHOPIFY_COOKIE_EXPIRE, } from '../../const' import checkoutCreateMutation from '../../utils/mutations/checkout-create' @@ -15,8 +16,11 @@ export const checkoutCreate = async (fetch: any) => { const checkoutId = checkout?.id if (checkoutId) { - Cookies.set(SHOPIFY_CHECKOUT_ID_COOKIE, checkoutId) - Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout?.webUrl) + const options = { + expires: SHOPIFY_COOKIE_EXPIRE, + } + Cookies.set(SHOPIFY_CHECKOUT_ID_COOKIE, checkoutId, options) + Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout?.webUrl, options) } return checkout diff --git a/framework/shopify/common/get-all-pages.ts b/framework/shopify/common/get-all-pages.ts index 6f06185e2..54231ed03 100644 --- a/framework/shopify/common/get-all-pages.ts +++ b/framework/shopify/common/get-all-pages.ts @@ -25,12 +25,13 @@ const getAllPages = async (options?: { }): Promise<ReturnType> => { let { config, variables = { first: 250 } } = options ?? {} config = getConfig(config) + const { locale } = config const { data } = await config.fetch(getAllPagesQuery, { variables }) const pages = data.pages?.edges?.map( ({ node: { title: name, handle, ...node } }: PageEdge) => ({ ...node, - url: `/${handle}`, + url: `/${locale}/${handle}`, name, }) ) diff --git a/framework/shopify/common/get-page.ts b/framework/shopify/common/get-page.ts index 6016c8c9a..be934aa42 100644 --- a/framework/shopify/common/get-page.ts +++ b/framework/shopify/common/get-page.ts @@ -3,33 +3,32 @@ import getPageQuery from '../utils/queries/get-page-query' import { Page } from './get-all-pages' type Variables = { - slug: string + id: string } -type ReturnType = { - page: Page -} +export type GetPageResult<T extends { page?: any } = { page?: Page }> = T const getPage = async (options: { variables: Variables config: ShopifyConfig preview?: boolean -}): Promise<ReturnType> => { +}): Promise<GetPageResult> => { let { config, variables } = options ?? {} + config = getConfig(config) + const { locale } = config const { data } = await config.fetch(getPageQuery, { variables, }) - - const { pageByHandle: page } = data + const page = data.node return { page: page ? { ...page, name: page.title, - url: page?.handle, + url: `/${locale}/${page.handle}`, } : null, } diff --git a/framework/shopify/const.ts b/framework/shopify/const.ts index a6e9e8d90..06fbe5054 100644 --- a/framework/shopify/const.ts +++ b/framework/shopify/const.ts @@ -6,6 +6,8 @@ export const SHOPIFY_CUSTOMER_TOKEN_COOKIE = 'shopify_customerToken' export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN +export const SHOPIFY_COOKIE_EXPIRE = 30 + export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json` export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx index 4b14249ca..425df9e83 100644 --- a/framework/shopify/product/use-search.tsx +++ b/framework/shopify/product/use-search.tsx @@ -4,6 +4,7 @@ import useSearch, { UseSearch } from '@commerce/product/use-search' import { ProductEdge } from '../schema' import { getAllProductsQuery, + getCollectionProductsQuery, getSearchVariables, normalizeProduct, } from '../utils' @@ -14,8 +15,8 @@ export default useSearch as UseSearch<typeof handler> export type SearchProductsInput = { search?: string - categoryId?: number - brandId?: number + categoryId?: string + brandId?: string sort?: string } @@ -23,6 +24,7 @@ export type SearchProductsData = { products: Product[] found: boolean } + export const handler: SWRHook< SearchProductsData, SearchProductsInput, @@ -32,18 +34,30 @@ export const handler: SWRHook< query: getAllProductsQuery, }, async fetcher({ input, options, fetch }) { - const resp = await fetch({ - query: options?.query, + const { categoryId, brandId } = input + + const data = await fetch({ + query: categoryId ? getCollectionProductsQuery : options.query, method: options?.method, variables: getSearchVariables(input), }) - const edges = resp.products?.edges + + let edges + + if (categoryId) { + edges = data.node?.products?.edges ?? [] + if (brandId) { + edges = edges.filter( + ({ node: { vendor } }: ProductEdge) => vendor === brandId + ) + } + } else { + edges = data.products?.edges ?? [] + } + return { - products: edges?.map(({ node: p }: ProductEdge) => - // TODO: Fix this product type - normalizeProduct(p as any) - ), - found: !!edges?.length, + products: edges.map(({ node }: ProductEdge) => normalizeProduct(node)), + found: !!edges.length, } }, useHook: ({ useData }) => (input = {}) => { diff --git a/framework/shopify/utils/customer-token.ts b/framework/shopify/utils/customer-token.ts index beae54765..85454cb83 100644 --- a/framework/shopify/utils/customer-token.ts +++ b/framework/shopify/utils/customer-token.ts @@ -1,12 +1,21 @@ -import Cookies from 'js-cookie' -import { SHOPIFY_CUSTOMER_TOKEN_COOKIE } from '../const' +import Cookies, { CookieAttributes } from 'js-cookie' +import { SHOPIFY_COOKIE_EXPIRE, SHOPIFY_CUSTOMER_TOKEN_COOKIE } from '../const' export const getCustomerToken = () => Cookies.get(SHOPIFY_CUSTOMER_TOKEN_COOKIE) -export const setCustomerToken = (token: string | null, options?: any) => { +export const setCustomerToken = ( + token: string | null, + options?: CookieAttributes +) => { if (!token) { Cookies.remove(SHOPIFY_CUSTOMER_TOKEN_COOKIE) } else { - Cookies.set(SHOPIFY_CUSTOMER_TOKEN_COOKIE, token, options) + Cookies.set( + SHOPIFY_CUSTOMER_TOKEN_COOKIE, + token, + options ?? { + expires: SHOPIFY_COOKIE_EXPIRE, + } + ) } } diff --git a/framework/shopify/utils/get-categories.ts b/framework/shopify/utils/get-categories.ts index 54048b896..cce4b2ad7 100644 --- a/framework/shopify/utils/get-categories.ts +++ b/framework/shopify/utils/get-categories.ts @@ -17,8 +17,8 @@ const getCategories = async (config: ShopifyConfig): Promise<Category[]> => { return ( data.collections?.edges?.map( - ({ node: { title: name, handle } }: CollectionEdge) => ({ - entityId: handle, + ({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({ + entityId, name, path: `/${handle}`, }) diff --git a/framework/shopify/utils/get-search-variables.ts b/framework/shopify/utils/get-search-variables.ts index 6f5d08b1a..c1b40ae5d 100644 --- a/framework/shopify/utils/get-search-variables.ts +++ b/framework/shopify/utils/get-search-variables.ts @@ -2,9 +2,9 @@ import getSortVariables from './get-sort-variables' import type { SearchProductsInput } from '../product/use-search' export const getSearchVariables = ({ - categoryId, brandId, search, + categoryId, sort, }: SearchProductsInput) => { let query = '' @@ -13,17 +13,14 @@ export const getSearchVariables = ({ query += `product_type:${search} OR title:${search} OR tag:${search}` } - if (categoryId) { - query += `tag:${categoryId}` - } - if (brandId) { - query += `${categoryId ? ' AND ' : ''}vendor:${brandId}` + query += `${search ? ' AND ' : ''}vendor:${brandId}` } return { + categoryId, query, - ...getSortVariables(sort), + ...getSortVariables(sort, !!categoryId), } } diff --git a/framework/shopify/utils/get-sort-variables.ts b/framework/shopify/utils/get-sort-variables.ts index 47650c0d7..b8cdeec51 100644 --- a/framework/shopify/utils/get-sort-variables.ts +++ b/framework/shopify/utils/get-sort-variables.ts @@ -1,4 +1,4 @@ -const getSortVariables = (sort?: string) => { +const getSortVariables = (sort?: string, isCategory = false) => { let output = {} switch (sort) { case 'price-asc': @@ -21,7 +21,7 @@ const getSortVariables = (sort?: string) => { break case 'latest-desc': output = { - sortKey: 'CREATED_AT', + sortKey: isCategory ? 'CREATED' : 'CREATED_AT', reverse: true, } break diff --git a/framework/shopify/utils/normalize.ts b/framework/shopify/utils/normalize.ts index 67ab3a8a2..c9b428b37 100644 --- a/framework/shopify/utils/normalize.ts +++ b/framework/shopify/utils/normalize.ts @@ -1,3 +1,5 @@ +import { Product } from '@commerce/types' + import { Product as ShopifyProduct, Checkout, @@ -5,8 +7,8 @@ import { SelectedOption, ImageConnection, ProductVariantConnection, - ProductOption, MoneyV2, + ProductOption, } from '../schema' import type { Cart, LineItem } from '../types' @@ -19,18 +21,26 @@ const money = ({ amount, currencyCode }: MoneyV2) => { } const normalizeProductOption = ({ + id, name: displayName, values, - ...rest }: ProductOption) => { return { __typename: 'MultipleChoiceOption', + id, displayName, - values: values.map((value) => ({ - label: value, - hexColors: displayName === 'Color' ? [value] : null, - })), - ...rest, + values: values.map((value) => { + let output: any = { + label: value, + } + if (displayName === 'Color') { + output = { + ...output, + hexColors: [value], + } + } + return output + }), } } @@ -41,19 +51,28 @@ const normalizeProductImages = ({ edges }: ImageConnection) => })) const normalizeProductVariants = ({ edges }: ProductVariantConnection) => { - return edges?.map(({ node: { id, selectedOptions } }) => ({ - id, - options: selectedOptions.map(({ name, value }: SelectedOption) => - normalizeProductOption({ - id, - name, - values: [value], - }) - ), - })) + return edges?.map( + ({ + node: { id, selectedOptions, sku, title, priceV2, compareAtPriceV2 }, + }) => ({ + id, + name: title, + sku: sku ?? id, + price: +priceV2.amount, + listPrice: +compareAtPriceV2?.amount, + requiresShipping: true, + options: selectedOptions.map(({ name, value }: SelectedOption) => + normalizeProductOption({ + id, + name, + values: [value], + }) + ), + }) + ) } -export function normalizeProduct(productNode: ShopifyProduct): any { +export function normalizeProduct(productNode: ShopifyProduct): Product { const { id, title: name, @@ -95,8 +114,8 @@ export function normalizeCart(checkout: Checkout): Cart { }, taxesIncluded: checkout.taxesIncluded, lineItems: checkout.lineItems?.edges.map(normalizeLineItem), - lineItemsSubtotalPrice: checkout.subtotalPriceV2?.amount, - subtotalPrice: checkout.subtotalPriceV2?.amount, + lineItemsSubtotalPrice: +checkout.subtotalPriceV2?.amount, + subtotalPrice: +checkout.subtotalPriceV2?.amount, totalPrice: checkout.totalPriceV2?.amount, discounts: [], } diff --git a/framework/shopify/utils/queries/get-all-products-query.ts b/framework/shopify/utils/queries/get-all-products-query.ts index 4a6c20b6e..5eb44c7a7 100644 --- a/framework/shopify/utils/queries/get-all-products-query.ts +++ b/framework/shopify/utils/queries/get-all-products-query.ts @@ -1,3 +1,38 @@ +export const productConnection = ` +pageInfo { + hasNextPage + hasPreviousPage +} +edges { + node { + id + title + vendor + handle + description + priceRange { + minVariantPrice { + amount + currencyCode + } + } + images(first: 1) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + originalSrc + altText + width + height + } + } + } + } +}` + export const productsFragment = ` products( first: $first @@ -5,39 +40,7 @@ products( reverse: $reverse query: $query ) { - pageInfo { - hasNextPage - hasPreviousPage - } - edges { - node { - id - title - vendor - handle - description - priceRange { - minVariantPrice { - amount - currencyCode - } - } - images(first: 1) { - pageInfo { - hasNextPage - hasPreviousPage - } - edges { - node { - originalSrc - altText - width - height - } - } - } - } - } + ${productConnection} } ` diff --git a/framework/shopify/utils/queries/get-collection-products-query.ts b/framework/shopify/utils/queries/get-collection-products-query.ts index dd504b575..04766caa4 100644 --- a/framework/shopify/utils/queries/get-collection-products-query.ts +++ b/framework/shopify/utils/queries/get-collection-products-query.ts @@ -1,16 +1,23 @@ -import { productsFragment } from './get-all-products-query' +import { productConnection } from './get-all-products-query' const getCollectionProductsQuery = /* GraphQL */ ` query getProductsFromCollection( - $categoryHandle: String! + $categoryId: ID! $first: Int = 250 - $query: String = "" - $sortKey: ProductSortKeys = RELEVANCE + $sortKey: ProductCollectionSortKeys = RELEVANCE $reverse: Boolean = false ) { - collectionByHandle(handle: $categoryHandle) - { - ${productsFragment} + node(id: $categoryId) { + id + ... on Collection { + products( + first: $first + sortKey: $sortKey + reverse: $reverse + ) { + ${productConnection} + } + } } } ` diff --git a/framework/shopify/utils/queries/get-page-query.ts b/framework/shopify/utils/queries/get-page-query.ts index dcafdc30d..2ca79abd4 100644 --- a/framework/shopify/utils/queries/get-page-query.ts +++ b/framework/shopify/utils/queries/get-page-query.ts @@ -1,12 +1,13 @@ export const getPageQuery = /* GraphQL */ ` - query getPageBySlug($slug: String!) { - pageByHandle(handle: $slug) { + query($id: ID!) { + node(id: $id) { id - title - handle - body - bodySummary - url + ... on Page { + title + handle + body + bodySummary + } } } ` diff --git a/framework/shopify/utils/queries/get-product-query.ts b/framework/shopify/utils/queries/get-product-query.ts index d054c023d..5c109901b 100644 --- a/framework/shopify/utils/queries/get-product-query.ts +++ b/framework/shopify/utils/queries/get-product-query.ts @@ -32,6 +32,7 @@ const getProductQuery = /* GraphQL */ ` node { id title + sku selectedOptions { name value diff --git a/framework/shopify/utils/to-commerce-products.ts b/framework/shopify/utils/to-commerce-products.ts deleted file mode 100644 index 84925e001..000000000 --- a/framework/shopify/utils/to-commerce-products.ts +++ /dev/null @@ -1,68 +0,0 @@ -// TODO: Fix the types in this file -// import { Product, Image } from '../types' - -type Product = any -type Image = any - -export default function toCommerceProducts(products: Product[]) { - return products.map((product: Product) => { - return { - id: product.id, - entityId: product.id, - name: product.title, - slug: product.handle, - title: product.title, - vendor: product.vendor, - description: product.descriptionHtml, - path: `/${product.handle}`, - price: { - value: +product.variants[0].price, - currencyCode: 'USD', // TODO - }, - images: product.images.map((image: Image) => { - return { - url: image.src, - } - }), - // TODO: Fix the variant type - variants: product.variants.map((variant: any) => { - return { - id: variant.id, - // TODO: Fix the selectedOption type - options: variant.selectedOptions.map((selectedOption: any) => { - return { - __typename: 'MultipleChoiceOption', - displayName: selectedOption.name, - values: [ - { - node: { - id: variant.id, - label: selectedOption.value, - }, - }, - ], - } - }), - } - }), - // TODO: Fix the option type - productOptions: product.options.map((option: any) => { - return { - __typename: 'MultipleChoiceOption', - displayName: option.name, - // TODO: Fix the value type - values: option.values.map((value: any) => { - return { - node: { - entityId: 1, - label: value.value, - hexColors: [value.value], - }, - } - }), - } - }), - options: [], - } - }) -} diff --git a/framework/shopify/wishlist/use-wishlist.tsx b/framework/shopify/wishlist/use-wishlist.tsx index 13632bb95..d2ce9db5b 100644 --- a/framework/shopify/wishlist/use-wishlist.tsx +++ b/framework/shopify/wishlist/use-wishlist.tsx @@ -2,9 +2,7 @@ // Shopify doesn't have a wishlist import { HookFetcher } from '@commerce/utils/types' -import useCommerceWishlist from '@commerce/wishlist/use-wishlist' import { Product } from '../schema' -import useCustomer from '../customer/use-customer' const defaultOpts = {} From 7780ec4818f730cad50eef2ffec306151f3b410b Mon Sep 17 00:00:00 2001 From: Bel Curcio <bel@vercel.com> Date: Mon, 1 Mar 2021 15:59:45 -0300 Subject: [PATCH 185/221] Bump and adding dependency --- components/product/Swatch/Swatch.module.css | 1 + package.json | 7 ++++--- yarn.lock | 23 ++++++++++++++++++--- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/components/product/Swatch/Swatch.module.css b/components/product/Swatch/Swatch.module.css index ae37771ad..051435afd 100644 --- a/components/product/Swatch/Swatch.module.css +++ b/components/product/Swatch/Swatch.module.css @@ -1,4 +1,5 @@ .root { + composes: root from 'components/ui/Button/Button.module.css'; @apply h-12 w-12 bg-primary text-primary rounded-full mr-3 inline-flex items-center justify-center cursor-pointer transition duration-150 ease-in-out p-0 shadow-none border-gray-200 border box-border; diff --git a/package.json b/package.json index 906d950dc..a3aac641b 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "sideEffects": false, "license": "MIT", "engines": { - "node": "12.x" + "node": "14.x" }, "prettier": { "semi": false, @@ -23,6 +23,7 @@ "dependencies": { "@reach/portal": "^0.11.2", "@vercel/fetch": "^6.1.0", + "autoprefixer": "^10.2.4", "body-scroll-lock": "^3.1.5", "bowser": "^2.11.0", "classnames": "^2.2.6", @@ -38,7 +39,7 @@ "next": "^10.0.7", "next-seo": "^4.11.0", "next-themes": "^0.0.4", - "postcss": "^8.2.4", + "postcss": "^8.2.6", "postcss-nesting": "^7.0.1", "react": "^17.0.1", "react-dom": "^17.0.1", @@ -47,7 +48,7 @@ "shopify-buy": "^2.11.0", "swr": "^0.4.0", "tabbable": "^5.1.5", - "tailwindcss": "^2.0.2" + "tailwindcss": "^2.0.3" }, "devDependencies": { "@graphql-codegen/cli": "^1.20.0", diff --git a/yarn.lock b/yarn.lock index 9238b1f03..2f23e6a57 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1609,6 +1609,18 @@ auto-bind@~4.0.0: resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" integrity sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ== +autoprefixer@^10.2.4: + version "10.2.4" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.2.4.tgz#c0e7cf24fcc6a1ae5d6250c623f0cb8beef2f7e1" + integrity sha512-DCCdUQiMD+P/as8m3XkeTUkUKuuRqLGcwD0nll7wevhqoJfMRpJlkFd1+MQh1pvupjiQuip42lc/VFvfUTMSKw== + dependencies: + browserslist "^4.16.1" + caniuse-lite "^1.0.30001181" + colorette "^1.2.1" + fraction.js "^4.0.13" + normalize-range "^0.1.2" + postcss-value-parser "^4.1.0" + autoprefixer@^9.6.1: version "9.8.6" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" @@ -1818,7 +1830,7 @@ browserslist@4.16.1: escalade "^3.1.1" node-releases "^1.1.69" -browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.6.4: +browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.1, browserslist@^4.6.4: version "4.16.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717" integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== @@ -3187,6 +3199,11 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +fraction.js@^4.0.13: + version "4.0.13" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.0.13.tgz#3c1c315fa16b35c85fffa95725a36fa729c69dfe" + integrity sha512-E1fz2Xs9ltlUp+qbiyx9wmt2n9dRzPsS11Jtdb8D2o+cC7wr9xkkKsVKJuBX0ST+LVS+LhLO+SbLJNtfWcJvXA== + fs-capacitor@^6.1.0: version "6.2.0" resolved "https://registry.yarnpkg.com/fs-capacitor/-/fs-capacitor-6.2.0.tgz#fa79ac6576629163cb84561995602d8999afb7f5" @@ -5586,7 +5603,7 @@ postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.26, postcss@^7.0. source-map "^0.6.1" supports-color "^6.1.0" -postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.4: +postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.6: version "8.2.6" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.6.tgz#5d69a974543b45f87e464bc4c3e392a97d6be9fe" integrity sha512-xpB8qYxgPuly166AGlpRjUdEYtmOWx2iCwGmrv4vqZL9YPVviDVPZPRXxnXr6xPZOdxQ9lp3ZBFCRgWJ7LE3Sg== @@ -6617,7 +6634,7 @@ tabbable@^5.1.5: resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-5.1.5.tgz#efec48ede268d511c261e3b81facbb4782a35147" integrity sha512-oVAPrWgLLqrbvQE8XqcU7CVBq6SQbaIbHkhOca3u7/jzuQvyZycrUKPCGr04qpEIUslmUlULbSeN+m3QrKEykA== -tailwindcss@^2.0.2: +tailwindcss@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.0.3.tgz#f8d07797d1f89dc4b171673c26237b58783c2c86" integrity sha512-s8NEqdLBiVbbdL0a5XwTb8jKmIonOuI4RMENEcKLR61jw6SdKvBss7NWZzwCaD+ZIjlgmesv8tmrjXEp7C0eAQ== From 6e8dbf1156d0223d570aded44174e78b2a426805 Mon Sep 17 00:00:00 2001 From: Bel Curcio <bel@vercel.com> Date: Mon, 1 Mar 2021 19:02:56 -0300 Subject: [PATCH 186/221] Adding color checks --- .prettierrc | 6 + .../ProductView/ProductView.module.css | 2 +- lib/colors.ts | 155 +++++++++++++++++- package.json | 6 +- yarn.lock | 2 +- 5 files changed, 162 insertions(+), 9 deletions(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..e1076edfa --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": false, + "singleQuote": true, + "tabWidth": 2, + "useTabs": false +} diff --git a/components/product/ProductView/ProductView.module.css b/components/product/ProductView/ProductView.module.css index dec06f665..6545d611b 100644 --- a/components/product/ProductView/ProductView.module.css +++ b/components/product/ProductView/ProductView.module.css @@ -7,7 +7,7 @@ } .productDisplay { - @apply relative flex px-0 pb-0 relative box-border col-span-1 bg-violet; + @apply relative flex px-0 pb-0 box-border col-span-1 bg-violet; min-height: 600px; @screen md { diff --git a/lib/colors.ts b/lib/colors.ts index 355201146..f9c0b5b0b 100644 --- a/lib/colors.ts +++ b/lib/colors.ts @@ -42,9 +42,160 @@ function hexToRgb(hex: string = '') { return [r, g, b] } -export function isDark(color = '') { +const colorMap: Record<string, string> = { + aliceblue: '#F0F8FF', + antiquewhite: '#FAEBD7', + aqua: '#00FFFF', + aquamarine: '#7FFFD4', + azure: '#F0FFFF', + beige: '#F5F5DC', + bisque: '#FFE4C4', + black: '#000000', + blanchedalmond: '#FFEBCD', + blue: '#0000FF', + blueviolet: '#8A2BE2', + brown: '#A52A2A', + burlywood: '#DEB887', + cadetblue: '#5F9EA0', + chartreuse: '#7FFF00', + chocolate: '#D2691E', + coral: '#FF7F50', + cornflowerblue: '#6495ED', + cornsilk: '#FFF8DC', + crimson: '#DC143C', + cyan: '#00FFFF', + darkblue: '#00008B', + darkcyan: '#008B8B', + darkgoldenrod: '#B8860B', + darkgray: '#A9A9A9', + darkgreen: '#006400', + darkgrey: '#A9A9A9', + darkkhaki: '#BDB76B', + darkmagenta: '#8B008B', + darkolivegreen: '#556B2F', + darkorange: '#FF8C00', + darkorchid: '#9932CC', + darkred: '#8B0000', + darksalmon: '#E9967A', + darkseagreen: '#8FBC8F', + darkslateblue: '#483D8B', + darkslategray: '#2F4F4F', + darkslategrey: '#2F4F4F', + darkturquoise: '#00CED1', + darkviolet: '#9400D3', + deeppink: '#FF1493', + deepskyblue: '#00BFFF', + dimgray: '#696969', + dimgrey: '#696969', + dodgerblue: '#1E90FF', + firebrick: '#B22222', + floralwhite: '#FFFAF0', + forestgreen: '#228B22', + fuchsia: '#FF00FF', + gainsboro: '#DCDCDC', + ghostwhite: '#F8F8FF', + gold: '#FFD700', + goldenrod: '#DAA520', + gray: '#808080', + green: '#008000', + greenyellow: '#ADFF2F', + grey: '#808080', + honeydew: '#F0FFF0', + hotpink: '#FF69B4', + indianred: '#CD5C5C', + indigo: '#4B0082', + ivory: '#FFFFF0', + khaki: '#F0E68C', + lavender: '#E6E6FA', + lavenderblush: '#FFF0F5', + lawngreen: '#7CFC00', + lemonchiffon: '#FFFACD', + lightblue: '#ADD8E6', + lightcoral: '#F08080', + lightcyan: '#E0FFFF', + lightgoldenrodyellow: '#FAFAD2', + lightgray: '#D3D3D3', + lightgreen: '#90EE90', + lightgrey: '#D3D3D3', + lightpink: '#FFB6C1', + lightsalmon: '#FFA07A', + lightseagreen: '#20B2AA', + lightskyblue: '#87CEFA', + lightslategray: '#778899', + lightslategrey: '#778899', + lightsteelblue: '#B0C4DE', + lightyellow: '#FFFFE0', + lime: '#00FF00', + limegreen: '#32CD32', + linen: '#FAF0E6', + magenta: '#FF00FF', + maroon: '#800000', + mediumaquamarine: '#66CDAA', + mediumblue: '#0000CD', + mediumorchid: '#BA55D3', + mediumpurple: '#9370DB', + mediumseagreen: '#3CB371', + mediumslateblue: '#7B68EE', + mediumspringgreen: '#00FA9A', + mediumturquoise: '#48D1CC', + mediumvioletred: '#C71585', + midnightblue: '#191970', + mintcream: '#F5FFFA', + mistyrose: '#FFE4E1', + moccasin: '#FFE4B5', + navajowhite: '#FFDEAD', + navy: '#000080', + oldlace: '#FDF5E6', + olive: '#808000', + olivedrab: '#6B8E23', + orange: '#FFA500', + orangered: '#FF4500', + orchid: '#DA70D6', + palegoldenrod: '#EEE8AA', + palegreen: '#98FB98', + paleturquoise: '#AFEEEE', + palevioletred: '#DB7093', + papayawhip: '#FFEFD5', + peachpuff: '#FFDAB9', + peru: '#CD853F', + pink: '#FFC0CB', + plum: '#DDA0DD', + powderblue: '#B0E0E6', + purple: '#800080', + rebeccapurple: '#663399', + red: '#FF0000', + rosybrown: '#BC8F8F', + royalblue: '#4169E1', + saddlebrown: '#8B4513', + salmon: '#FA8072', + sandybrown: '#F4A460', + seagreen: '#2E8B57', + seashell: '#FFF5EE', + sienna: '#A0522D', + silver: '#C0C0C0', + skyblue: '#87CEEB', + slateblue: '#6A5ACD', + slategray: '#708090', + slategrey: '#708090', + snow: '#FFFAFA', + springgreen: '#00FF7F', + steelblue: '#4682B4', + tan: '#D2B48C', + teal: '#008080', + thistle: '#D8BFD8', + tomato: '#FF6347', + turquoise: '#40E0D0', + violet: '#EE82EE', + wheat: '#F5DEB3', + white: '#FFFFFF', + whitesmoke: '#F5F5F5', + yellow: '#FFFF00', + yellowgreen: '#9ACD32', +} + +export function isDark(color: string = ''): boolean { // Equation from http://24ways.org/2010/calculating-color-contrast - const rgb = hexToRgb(color) + let rgb = colorMap[color] ? hexToRgb(colorMap[color]) : hexToRgb(color) const res = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000 return res < 128 } diff --git a/package.json b/package.json index a3aac641b..4e8fa8d30 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,6 @@ "engines": { "node": "14.x" }, - "prettier": { - "semi": false, - "singleQuote": true - }, "dependencies": { "@reach/portal": "^0.11.2", "@vercel/fetch": "^6.1.0", @@ -74,7 +70,7 @@ "next-unused": "^0.0.3", "postcss-flexbugs-fixes": "^4.2.1", "postcss-preset-env": "^6.7.0", - "prettier": "^2.1.2", + "prettier": "^2.2.1", "typescript": "^4.0.3" }, "resolutions": { diff --git a/yarn.lock b/yarn.lock index 2f23e6a57..7a1dce814 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5662,7 +5662,7 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= -prettier@^2.0.5, prettier@^2.1.2: +prettier@^2.0.5, prettier@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== From 394efd9e8191c2f3d44198388e03dc9a4fa4627a Mon Sep 17 00:00:00 2001 From: Luis Alvarez <luis@vercel.com> Date: Tue, 2 Mar 2021 18:13:30 -0500 Subject: [PATCH 187/221] Updated main readme --- README.md | 57 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index f254b1b07..cc181b411 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ We're using Github Projects to keep track of issues in progress and todo's. Here ## Integrations -Next.js Commerce integrates out-of-the-box with BigCommerce. We plan to support all major ecommerce backends. +Next.js Commerce integrates out-of-the-box with BigCommerce and Shopify. We plan to support all major ecommerce backends. ## Goals @@ -38,13 +38,13 @@ Next.js Commerce integrates out-of-the-box with BigCommerce. We plan to support There is a `framework` folder in the root folder that will contain multiple ecommerce providers. -Additionally, we need to ensure feature parity (not all providers have e.g. wishlist) we will also have to build a feature API to disable/enable features in the UI. +Additionally, we need to ensure feature parity (not all providers have e.g. wishlist) so we also have a feature API to disable/enable features in the UI. People actively working on this project: @okbel & @lfades. ## Framework -Framework is where the data comes from. It contains mostly hooks and functions. +Framework is where the data comes from. It contains mostly hook handlers and functions. ## Structure @@ -77,7 +77,7 @@ Main folder and its exposed functions - `config.json` - README.md -#### Example of correct usage of Commerce Framework +#### Example of correct usage of the Commerce Framework ```js import { useUI } from '@components/ui' @@ -85,22 +85,57 @@ import { useCustomer } from '@framework/customer' import { useWishlist, useAddItem, useRemoveItem } from '@framework/wishlist' ``` -## Config +## Configuration -### Features +### How to change providers -In order to make the UI entirely functional, we need to specify which features certain providers do not **provide**. +First, update the provider selected in `commerce.config.json`: -**Disabling wishlist:** - -``` +```json { + "provider": "bigcommerce", "features": { - "wishlist": false + "wishlist": true } } ``` +Then, change the paths defined in `tsconfig.json` and update the `@framework` paths to point to the right folder provider: + +```json +"@framework": ["framework/bigcommerce"], +"@framework/*": ["framework/bigcommerce/*"] +``` + +Make sure to add the environment variables required by the new provider. + +### Features + +Every provider defines the features that it supports under `framework/{provider}/commerce.config.json` + +#### How to turn Features on and off + +> NOTE: The selected provider should support the feature that you are toggling. (This means that you can't turn wishlist on if the provider doesn't support this functionality out the box) + +- Open `commerce.config.json` +- You'll see a config file like this: + ```json + { + "provider": "bigcommerce", + "features": { + "wishlist": false + } + } + ``` +- Turn wishlist on by setting wishlist to true. +- Run the app and the wishlist functionality should be back on. + +### How to create a new provider + +We'd recommend to duplicate a provider folder and push your providers SDK. + +If you succeeded building a provider, submit a PR so we can all enjoy it. + ## Contribute Our commitment to Open Source can be found [here](https://vercel.com/oss). From 753234dc51b90114abb0c8fa1d247d163511095f Mon Sep 17 00:00:00 2001 From: Dave Loneragan <dave@papertokyo.com> Date: Tue, 2 Mar 2021 21:05:13 -0600 Subject: [PATCH 188/221] Add swell provider folder --- framework/swell/.env.template | 2 + framework/swell/README.md | 260 + framework/swell/api/cart/index.ts | 1 + framework/swell/api/catalog/index.ts | 1 + framework/swell/api/catalog/products.ts | 1 + framework/swell/api/checkout/index.ts | 46 + framework/swell/api/customer.ts | 1 + framework/swell/api/customers/index.ts | 1 + framework/swell/api/customers/login.ts | 1 + framework/swell/api/customers/logout.ts | 1 + framework/swell/api/customers/signup.ts | 1 + framework/swell/api/index.ts | 62 + .../api/operations/get-all-collections.ts | 21 + framework/swell/api/operations/get-page.ts | 25 + .../swell/api/utils/create-api-handler.ts | 58 + .../swell/api/utils/fetch-all-products.ts | 41 + .../swell/api/utils/fetch-graphql-api.ts | 34 + framework/swell/api/utils/fetch.ts | 2 + .../swell/api/utils/is-allowed-method.ts | 28 + framework/swell/api/wishlist/index.tsx | 2 + framework/swell/auth/use-login.tsx | 76 + framework/swell/auth/use-logout.tsx | 36 + framework/swell/auth/use-signup.tsx | 74 + framework/swell/cart/index.ts | 3 + framework/swell/cart/use-add-item.tsx | 58 + framework/swell/cart/use-cart.tsx | 59 + framework/swell/cart/use-remove-item.tsx | 72 + framework/swell/cart/use-update-item.tsx | 107 + framework/swell/cart/utils/checkout-create.ts | 29 + .../swell/cart/utils/checkout-to-cart.ts | 42 + framework/swell/cart/utils/fetcher.ts | 31 + framework/swell/cart/utils/index.ts | 2 + framework/swell/commerce.config.json | 6 + framework/swell/common/get-all-pages.ts | 42 + framework/swell/common/get-page.ts | 37 + framework/swell/common/get-site-info.ts | 31 + framework/swell/const.ts | 13 + framework/swell/customer/get-customer-id.ts | 24 + framework/swell/customer/index.ts | 1 + framework/swell/customer/use-customer.tsx | 27 + framework/swell/fetcher.ts | 18 + framework/swell/index.tsx | 40 + framework/swell/next.config.js | 8 + .../swell/product/get-all-collections.ts | 29 + .../swell/product/get-all-product-paths.ts | 42 + framework/swell/product/get-all-products.ts | 40 + framework/swell/product/get-product.ts | 32 + framework/swell/product/use-price.tsx | 2 + framework/swell/product/use-search.tsx | 77 + framework/swell/provider.ts | 31 + framework/swell/schema.d.ts | 4985 +++++++++ framework/swell/schema.graphql | 9631 +++++++++++++++++ framework/swell/types.ts | 45 + framework/swell/utils/customer-token.ts | 21 + framework/swell/utils/get-categories.ts | 29 + framework/swell/utils/get-checkout-id.ts | 8 + framework/swell/utils/get-search-variables.ts | 27 + framework/swell/utils/get-sort-variables.ts | 32 + framework/swell/utils/get-vendors.ts | 36 + .../swell/utils/handle-fetch-response.ts | 27 + framework/swell/utils/handle-login.ts | 39 + framework/swell/utils/index.ts | 10 + .../associate-customer-with-checkout.ts | 18 + .../swell/utils/mutations/checkout-create.ts | 16 + .../utils/mutations/checkout-line-item-add.ts | 16 + .../mutations/checkout-line-item-remove.ts | 19 + .../mutations/checkout-line-item-update.ts | 16 + .../mutations/customer-access-token-create.ts | 16 + .../mutations/customer-access-token-delete.ts | 14 + .../swell/utils/mutations/customer-create.ts | 15 + framework/swell/utils/mutations/index.ts | 7 + framework/swell/utils/normalize.ts | 152 + .../queries/get-all-collections-query.ts | 14 + .../utils/queries/get-all-pages-query.ts | 14 + .../queries/get-all-product-vendors-query.ts | 17 + .../queries/get-all-products-paths-query.ts | 17 + .../utils/queries/get-all-products-query.ts | 57 + .../swell/utils/queries/get-checkout-query.ts | 62 + .../queries/get-collection-products-query.ts | 24 + .../utils/queries/get-customer-id-query.ts | 8 + .../swell/utils/queries/get-customer-query.ts | 16 + .../swell/utils/queries/get-page-query.ts | 14 + .../swell/utils/queries/get-product-query.ts | 69 + framework/swell/utils/queries/index.ts | 10 + framework/swell/utils/storage.ts | 13 + framework/swell/wishlist/use-add-item.tsx | 13 + framework/swell/wishlist/use-remove-item.tsx | 17 + framework/swell/wishlist/use-wishlist.tsx | 46 + 88 files changed, 17268 insertions(+) create mode 100644 framework/swell/.env.template create mode 100644 framework/swell/README.md create mode 100644 framework/swell/api/cart/index.ts create mode 100644 framework/swell/api/catalog/index.ts create mode 100644 framework/swell/api/catalog/products.ts create mode 100644 framework/swell/api/checkout/index.ts create mode 100644 framework/swell/api/customer.ts create mode 100644 framework/swell/api/customers/index.ts create mode 100644 framework/swell/api/customers/login.ts create mode 100644 framework/swell/api/customers/logout.ts create mode 100644 framework/swell/api/customers/signup.ts create mode 100644 framework/swell/api/index.ts create mode 100644 framework/swell/api/operations/get-all-collections.ts create mode 100644 framework/swell/api/operations/get-page.ts create mode 100644 framework/swell/api/utils/create-api-handler.ts create mode 100644 framework/swell/api/utils/fetch-all-products.ts create mode 100644 framework/swell/api/utils/fetch-graphql-api.ts create mode 100644 framework/swell/api/utils/fetch.ts create mode 100644 framework/swell/api/utils/is-allowed-method.ts create mode 100644 framework/swell/api/wishlist/index.tsx create mode 100644 framework/swell/auth/use-login.tsx create mode 100644 framework/swell/auth/use-logout.tsx create mode 100644 framework/swell/auth/use-signup.tsx create mode 100644 framework/swell/cart/index.ts create mode 100644 framework/swell/cart/use-add-item.tsx create mode 100644 framework/swell/cart/use-cart.tsx create mode 100644 framework/swell/cart/use-remove-item.tsx create mode 100644 framework/swell/cart/use-update-item.tsx create mode 100644 framework/swell/cart/utils/checkout-create.ts create mode 100644 framework/swell/cart/utils/checkout-to-cart.ts create mode 100644 framework/swell/cart/utils/fetcher.ts create mode 100644 framework/swell/cart/utils/index.ts create mode 100644 framework/swell/commerce.config.json create mode 100644 framework/swell/common/get-all-pages.ts create mode 100644 framework/swell/common/get-page.ts create mode 100644 framework/swell/common/get-site-info.ts create mode 100644 framework/swell/const.ts create mode 100644 framework/swell/customer/get-customer-id.ts create mode 100644 framework/swell/customer/index.ts create mode 100644 framework/swell/customer/use-customer.tsx create mode 100644 framework/swell/fetcher.ts create mode 100644 framework/swell/index.tsx create mode 100644 framework/swell/next.config.js create mode 100644 framework/swell/product/get-all-collections.ts create mode 100644 framework/swell/product/get-all-product-paths.ts create mode 100644 framework/swell/product/get-all-products.ts create mode 100644 framework/swell/product/get-product.ts create mode 100644 framework/swell/product/use-price.tsx create mode 100644 framework/swell/product/use-search.tsx create mode 100644 framework/swell/provider.ts create mode 100644 framework/swell/schema.d.ts create mode 100644 framework/swell/schema.graphql create mode 100644 framework/swell/types.ts create mode 100644 framework/swell/utils/customer-token.ts create mode 100644 framework/swell/utils/get-categories.ts create mode 100644 framework/swell/utils/get-checkout-id.ts create mode 100644 framework/swell/utils/get-search-variables.ts create mode 100644 framework/swell/utils/get-sort-variables.ts create mode 100644 framework/swell/utils/get-vendors.ts create mode 100644 framework/swell/utils/handle-fetch-response.ts create mode 100644 framework/swell/utils/handle-login.ts create mode 100644 framework/swell/utils/index.ts create mode 100644 framework/swell/utils/mutations/associate-customer-with-checkout.ts create mode 100644 framework/swell/utils/mutations/checkout-create.ts create mode 100644 framework/swell/utils/mutations/checkout-line-item-add.ts create mode 100644 framework/swell/utils/mutations/checkout-line-item-remove.ts create mode 100644 framework/swell/utils/mutations/checkout-line-item-update.ts create mode 100644 framework/swell/utils/mutations/customer-access-token-create.ts create mode 100644 framework/swell/utils/mutations/customer-access-token-delete.ts create mode 100644 framework/swell/utils/mutations/customer-create.ts create mode 100644 framework/swell/utils/mutations/index.ts create mode 100644 framework/swell/utils/normalize.ts create mode 100644 framework/swell/utils/queries/get-all-collections-query.ts create mode 100644 framework/swell/utils/queries/get-all-pages-query.ts create mode 100644 framework/swell/utils/queries/get-all-product-vendors-query.ts create mode 100644 framework/swell/utils/queries/get-all-products-paths-query.ts create mode 100644 framework/swell/utils/queries/get-all-products-query.ts create mode 100644 framework/swell/utils/queries/get-checkout-query.ts create mode 100644 framework/swell/utils/queries/get-collection-products-query.ts create mode 100644 framework/swell/utils/queries/get-customer-id-query.ts create mode 100644 framework/swell/utils/queries/get-customer-query.ts create mode 100644 framework/swell/utils/queries/get-page-query.ts create mode 100644 framework/swell/utils/queries/get-product-query.ts create mode 100644 framework/swell/utils/queries/index.ts create mode 100644 framework/swell/utils/storage.ts create mode 100644 framework/swell/wishlist/use-add-item.tsx create mode 100644 framework/swell/wishlist/use-remove-item.tsx create mode 100644 framework/swell/wishlist/use-wishlist.tsx diff --git a/framework/swell/.env.template b/framework/swell/.env.template new file mode 100644 index 000000000..24521c2a1 --- /dev/null +++ b/framework/swell/.env.template @@ -0,0 +1,2 @@ +SHOPIFY_STORE_DOMAIN= +SHOPIFY_STOREFRONT_ACCESS_TOKEN= diff --git a/framework/swell/README.md b/framework/swell/README.md new file mode 100644 index 000000000..fc6a70ce3 --- /dev/null +++ b/framework/swell/README.md @@ -0,0 +1,260 @@ +## Table of Contents + +- [Getting Started](#getting-started) + - [Modifications](#modifications) + - [Adding item to Cart](#adding-item-to-cart) + - [Proceed to Checkout](#proceed-to-checkout) +- [General Usage](#general-usage) + - [CommerceProvider](#commerceprovider) + - [useCommerce](#usecommerce) +- [Hooks](#hooks) + - [usePrice](#useprice) + - [useAddItem](#useadditem) + - [useRemoveItem](#useremoveitem) + - [useUpdateItem](#useupdateitem) +- [APIs](#apis) + - [getProduct](#getproduct) + - [getAllProducts](#getallproducts) + - [getAllCollections](#getallcollections) + - [getAllPages](#getallpages) + +# Shopify Storefront Data Hooks + +Collection of hooks and data fetching functions to integrate Shopify in a React application. Designed to work with [Next.js Commerce](https://demo.vercel.store/). + +## Getting Started + +1. Install dependencies: + +``` +yarn install shopify-buy +yarn install -D @types/shopify-buy +``` + +3. Environment variables need to be set: + +``` +SHOPIFY_STORE_DOMAIN= +SHOPIFY_STOREFRONT_ACCESS_TOKEN= +NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN= +NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN= +``` + +4. Point the framework to `shopify` by updating `tsconfig.json`: + +``` +"@framework/*": ["framework/shopify/*"], +"@framework": ["framework/shopify"] +``` + +### Modifications + +These modifications are temporarily until contributions are made to remove them. + +#### Adding item to Cart + +```js +// components/product/ProductView/ProductView.tsx +const ProductView: FC<Props> = ({ product }) => { + const addToCart = async () => { + setLoading(true) + try { + await addItem({ + productId: product.id, + variantId: variant ? variant.id : product.variants[0].id, + }) + openSidebar() + setLoading(false) + } catch (err) { + setLoading(false) + } + } +} +``` + +#### Proceed to Checkout + +```js +// components/cart/CartSidebarView/CartSidebarView.tsx +import { useCommerce } from '@framework' + +const CartSidebarView: FC = () => { + const { checkout } = useCommerce() + return ( + <Button href={checkout.webUrl} Component="a" width="100%"> + Proceed to Checkout + </Button> + ) +} +``` + +## General Usage + +### CommerceProvider + +Provider component that creates the commerce context for children. + +```js +import { CommerceProvider } from '@framework' + +const App = ({ children }) => { + return <CommerceProvider locale={locale}>{children}</CommerceProvider> +} + +export default App +``` + +### useCommerce + +Returns the configs that are defined in the nearest `CommerceProvider`. Also provides access to Shopify's `checkout` and `shop`. + +```js +import { useCommerce } from 'nextjs-commerce-shopify' + +const { checkout, shop } = useCommerce() +``` + +- `checkout`: The information required to checkout items and pay ([Documentation](https://shopify.dev/docs/storefront-api/reference/checkouts/checkout)). +- `shop`: Represents a collection of the general settings and information about the shop ([Documentation](https://shopify.dev/docs/storefront-api/reference/online-store/shop/index)). + +## Hooks + +### usePrice + +Display the product variant price according to currency and locale. + +```js +import usePrice from '@framework/product/use-price' + +const { price } = usePrice({ + amount, +}) +``` + +Takes in either `amount` or `variant`: + +- `amount`: A price value for a particular item if the amount is known. +- `variant`: A shopify product variant. Price will be extracted from the variant. + +### useAddItem + +```js +import { useAddItem } from '@framework/cart' + +const AddToCartButton = ({ variantId, quantity }) => { + const addItem = useAddItem() + + const addToCart = async () => { + await addItem({ + variantId, + }) + } + + return <button onClick={addToCart}>Add To Cart</button> +} +``` + +### useRemoveItem + +```js +import { useRemoveItem } from '@framework/cart' + +const RemoveButton = ({ item }) => { + const removeItem = useRemoveItem() + + const handleRemove = async () => { + await removeItem({ id: item.id }) + } + + return <button onClick={handleRemove}>Remove</button> +} +``` + +### useUpdateItem + +```js +import { useUpdateItem } from '@framework/cart' + +const CartItem = ({ item }) => { + const [quantity, setQuantity] = useState(item.quantity) + const updateItem = useUpdateItem(item) + + const updateQuantity = async (e) => { + const val = e.target.value + await updateItem({ quantity: val }) + } + + return ( + <input + type="number" + max={99} + min={0} + value={quantity} + onChange={updateQuantity} + /> + ) +} +``` + +## APIs + +Collections of APIs to fetch data from a Shopify store. + +The data is fetched using the [Shopify JavaScript Buy SDK](https://github.com/Shopify/js-buy-sdk#readme). Read the [Shopify Storefront API reference](https://shopify.dev/docs/storefront-api/reference) for more information. + +### getProduct + +Get a single product by its `handle`. + +```js +import getProduct from '@framework/product/get-product' +import { getConfig } from '@framework/api' + +const config = getConfig() + +const product = await getProduct({ + variables: { slug }, + config, +}) +``` + +### getAllProducts + +```js +import getAllProducts from '@framework/product/get-all-products' +import { getConfig } from '@framework/api' + +const config = getConfig() + +const { products } = await getAllProducts({ + variables: { first: 12 }, + config, +}) +``` + +### getAllCollections + +```js +import getAllCollections from '@framework/product/get-all-collections' +import { getConfig } from '@framework/api' + +const config = getConfig() + +const collections = await getAllCollections({ + config, +}) +``` + +### getAllPages + +```js +import getAllPages from '@framework/common/get-all-pages' +import { getConfig } from '@framework/api' + +const config = getConfig() + +const pages = await getAllPages({ + variables: { first: 12 }, + config, +}) +``` diff --git a/framework/swell/api/cart/index.ts b/framework/swell/api/cart/index.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/swell/api/cart/index.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/swell/api/catalog/index.ts b/framework/swell/api/catalog/index.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/swell/api/catalog/index.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/swell/api/catalog/products.ts b/framework/swell/api/catalog/products.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/swell/api/catalog/products.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/swell/api/checkout/index.ts b/framework/swell/api/checkout/index.ts new file mode 100644 index 000000000..244078466 --- /dev/null +++ b/framework/swell/api/checkout/index.ts @@ -0,0 +1,46 @@ +import isAllowedMethod from '../utils/is-allowed-method' +import createApiHandler, { + ShopifyApiHandler, +} from '../utils/create-api-handler' + +import { + SHOPIFY_CHECKOUT_ID_COOKIE, + SHOPIFY_CHECKOUT_URL_COOKIE, + SHOPIFY_CUSTOMER_TOKEN_COOKIE, +} from '../../const' + +import { getConfig } from '..' +import associateCustomerWithCheckoutMutation from '../../utils/mutations/associate-customer-with-checkout' + +const METHODS = ['GET'] + +const checkoutApi: ShopifyApiHandler<any> = async (req, res, config) => { + if (!isAllowedMethod(req, res, METHODS)) return + + config = getConfig() + + const { cookies } = req + const checkoutUrl = cookies[SHOPIFY_CHECKOUT_URL_COOKIE] + const customerCookie = cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE] + + if (customerCookie) { + try { + await config.fetch(associateCustomerWithCheckoutMutation, { + variables: { + checkoutId: cookies[SHOPIFY_CHECKOUT_ID_COOKIE], + customerAccessToken: cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE], + }, + }) + } catch (error) { + console.error(error) + } + } + + if (checkoutUrl) { + res.redirect(checkoutUrl) + } else { + res.redirect('/cart') + } +} + +export default createApiHandler(checkoutApi, {}, {}) diff --git a/framework/swell/api/customer.ts b/framework/swell/api/customer.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/swell/api/customer.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/swell/api/customers/index.ts b/framework/swell/api/customers/index.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/swell/api/customers/index.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/swell/api/customers/login.ts b/framework/swell/api/customers/login.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/swell/api/customers/login.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/swell/api/customers/logout.ts b/framework/swell/api/customers/logout.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/swell/api/customers/logout.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/swell/api/customers/signup.ts b/framework/swell/api/customers/signup.ts new file mode 100644 index 000000000..ea9b101e1 --- /dev/null +++ b/framework/swell/api/customers/signup.ts @@ -0,0 +1 @@ +export default function () {} diff --git a/framework/swell/api/index.ts b/framework/swell/api/index.ts new file mode 100644 index 000000000..4f15cae15 --- /dev/null +++ b/framework/swell/api/index.ts @@ -0,0 +1,62 @@ +import type { CommerceAPIConfig } from '@commerce/api' + +import { + API_URL, + API_TOKEN, + SHOPIFY_CHECKOUT_ID_COOKIE, + SHOPIFY_CUSTOMER_TOKEN_COOKIE, + SHOPIFY_COOKIE_EXPIRE, +} from '../const' + +if (!API_URL) { + throw new Error( + `The environment variable NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN is missing and it's required to access your store` + ) +} + +if (!API_TOKEN) { + throw new Error( + `The environment variable NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store` + ) +} + +import fetchGraphqlApi from './utils/fetch-graphql-api' + +export interface ShopifyConfig extends CommerceAPIConfig {} + +export class Config { + private config: ShopifyConfig + + constructor(config: ShopifyConfig) { + this.config = config + } + + getConfig(userConfig: Partial<ShopifyConfig> = {}) { + return Object.entries(userConfig).reduce<ShopifyConfig>( + (cfg, [key, value]) => Object.assign(cfg, { [key]: value }), + { ...this.config } + ) + } + + setConfig(newConfig: Partial<ShopifyConfig>) { + Object.assign(this.config, newConfig) + } +} + +const config = new Config({ + locale: 'en-US', + commerceUrl: API_URL, + apiToken: API_TOKEN!, + cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, + cartCookieMaxAge: SHOPIFY_COOKIE_EXPIRE, + fetch: fetchGraphqlApi, + customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE, +}) + +export function getConfig(userConfig?: Partial<ShopifyConfig>) { + return config.getConfig(userConfig) +} + +export function setConfig(newConfig: Partial<ShopifyConfig>) { + return config.setConfig(newConfig) +} diff --git a/framework/swell/api/operations/get-all-collections.ts b/framework/swell/api/operations/get-all-collections.ts new file mode 100644 index 000000000..9cf216a91 --- /dev/null +++ b/framework/swell/api/operations/get-all-collections.ts @@ -0,0 +1,21 @@ +import Client from 'shopify-buy' +import { ShopifyConfig } from '../index' + +type Options = { + config: ShopifyConfig +} + +const getAllCollections = async (options: Options) => { + const { config } = options + + const client = Client.buildClient({ + storefrontAccessToken: config.apiToken, + domain: config.commerceUrl, + }) + + const res = await client.collection.fetchAllWithProducts() + + return JSON.parse(JSON.stringify(res)) +} + +export default getAllCollections diff --git a/framework/swell/api/operations/get-page.ts b/framework/swell/api/operations/get-page.ts new file mode 100644 index 000000000..32acb7c8f --- /dev/null +++ b/framework/swell/api/operations/get-page.ts @@ -0,0 +1,25 @@ +import { Page } from '../../schema' +import { ShopifyConfig, getConfig } from '..' + +export type GetPageResult<T extends { page?: any } = { page?: Page }> = T + +export type PageVariables = { + id: string +} + +async function getPage({ + url, + variables, + config, + preview, +}: { + url?: string + variables: PageVariables + config?: ShopifyConfig + preview?: boolean +}): Promise<GetPageResult> { + config = getConfig(config) + return {} +} + +export default getPage diff --git a/framework/swell/api/utils/create-api-handler.ts b/framework/swell/api/utils/create-api-handler.ts new file mode 100644 index 000000000..8820aeabc --- /dev/null +++ b/framework/swell/api/utils/create-api-handler.ts @@ -0,0 +1,58 @@ +import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next' +import { ShopifyConfig, getConfig } from '..' + +export type ShopifyApiHandler< + T = any, + H extends ShopifyHandlers = {}, + Options extends {} = {} +> = ( + req: NextApiRequest, + res: NextApiResponse<ShopifyApiResponse<T>>, + config: ShopifyConfig, + handlers: H, + // Custom configs that may be used by a particular handler + options: Options +) => void | Promise<void> + +export type ShopifyHandler<T = any, Body = null> = (options: { + req: NextApiRequest + res: NextApiResponse<ShopifyApiResponse<T>> + config: ShopifyConfig + body: Body +}) => void | Promise<void> + +export type ShopifyHandlers<T = any> = { + [k: string]: ShopifyHandler<T, any> +} + +export type ShopifyApiResponse<T> = { + data: T | null + errors?: { message: string; code?: string }[] +} + +export default function createApiHandler< + T = any, + H extends ShopifyHandlers = {}, + Options extends {} = {} +>( + handler: ShopifyApiHandler<T, H, Options>, + handlers: H, + defaultOptions: Options +) { + return function getApiHandler({ + config, + operations, + options, + }: { + config?: ShopifyConfig + operations?: Partial<H> + options?: Options extends {} ? Partial<Options> : never + } = {}): NextApiHandler { + const ops = { ...operations, ...handlers } + const opts = { ...defaultOptions, ...options } + + return function apiHandler(req, res) { + return handler(req, res, getConfig(config), ops, opts) + } + } +} diff --git a/framework/swell/api/utils/fetch-all-products.ts b/framework/swell/api/utils/fetch-all-products.ts new file mode 100644 index 000000000..9fa70a5ee --- /dev/null +++ b/framework/swell/api/utils/fetch-all-products.ts @@ -0,0 +1,41 @@ +import { ProductEdge } from '../../schema' +import { ShopifyConfig } from '..' + +const fetchAllProducts = async ({ + config, + query, + variables, + acc = [], + cursor, +}: { + config: ShopifyConfig + query: string + acc?: ProductEdge[] + variables?: any + cursor?: string +}): Promise<ProductEdge[]> => { + const { data } = await config.fetch(query, { + variables: { ...variables, cursor }, + }) + + const edges: ProductEdge[] = data.products?.edges ?? [] + const hasNextPage = data.products?.pageInfo?.hasNextPage + acc = acc.concat(edges) + + if (hasNextPage) { + const cursor = edges.pop()?.cursor + if (cursor) { + return fetchAllProducts({ + config, + query, + variables, + acc, + cursor, + }) + } + } + + return acc +} + +export default fetchAllProducts diff --git a/framework/swell/api/utils/fetch-graphql-api.ts b/framework/swell/api/utils/fetch-graphql-api.ts new file mode 100644 index 000000000..321cba2aa --- /dev/null +++ b/framework/swell/api/utils/fetch-graphql-api.ts @@ -0,0 +1,34 @@ +import type { GraphQLFetcher } from '@commerce/api' +import fetch from './fetch' + +import { API_URL, API_TOKEN } from '../../const' +import { getError } from '../../utils/handle-fetch-response' + +const fetchGraphqlApi: GraphQLFetcher = async ( + query: string, + { variables } = {}, + fetchOptions +) => { + const res = await fetch(API_URL, { + ...fetchOptions, + method: 'POST', + headers: { + 'X-Shopify-Storefront-Access-Token': API_TOKEN!, + ...fetchOptions?.headers, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query, + variables, + }), + }) + + const { data, errors, status } = await res.json() + + if (errors) { + throw getError(errors, status) + } + + return { data, res } +} +export default fetchGraphqlApi diff --git a/framework/swell/api/utils/fetch.ts b/framework/swell/api/utils/fetch.ts new file mode 100644 index 000000000..0b8367102 --- /dev/null +++ b/framework/swell/api/utils/fetch.ts @@ -0,0 +1,2 @@ +import zeitFetch from '@vercel/fetch' +export default zeitFetch() diff --git a/framework/swell/api/utils/is-allowed-method.ts b/framework/swell/api/utils/is-allowed-method.ts new file mode 100644 index 000000000..78bbba568 --- /dev/null +++ b/framework/swell/api/utils/is-allowed-method.ts @@ -0,0 +1,28 @@ +import type { NextApiRequest, NextApiResponse } from 'next' + +export default function isAllowedMethod( + req: NextApiRequest, + res: NextApiResponse, + allowedMethods: string[] +) { + const methods = allowedMethods.includes('OPTIONS') + ? allowedMethods + : [...allowedMethods, 'OPTIONS'] + + if (!req.method || !methods.includes(req.method)) { + res.status(405) + res.setHeader('Allow', methods.join(', ')) + res.end() + return false + } + + if (req.method === 'OPTIONS') { + res.status(200) + res.setHeader('Allow', methods.join(', ')) + res.setHeader('Content-Length', '0') + res.end() + return false + } + + return true +} diff --git a/framework/swell/api/wishlist/index.tsx b/framework/swell/api/wishlist/index.tsx new file mode 100644 index 000000000..a72856673 --- /dev/null +++ b/framework/swell/api/wishlist/index.tsx @@ -0,0 +1,2 @@ +export type WishlistItem = { product: any; id: number } +export default function () {} diff --git a/framework/swell/auth/use-login.tsx b/framework/swell/auth/use-login.tsx new file mode 100644 index 000000000..188dd54a2 --- /dev/null +++ b/framework/swell/auth/use-login.tsx @@ -0,0 +1,76 @@ +import { useCallback } from 'react' +import type { MutationHook } from '@commerce/utils/types' +import { CommerceError, ValidationError } from '@commerce/utils/errors' +import useCustomer from '../customer/use-customer' +import createCustomerAccessTokenMutation from '../utils/mutations/customer-access-token-create' +import { + CustomerAccessTokenCreateInput, + CustomerUserError, + Mutation, + MutationCheckoutCreateArgs, +} from '../schema' +import useLogin, { UseLogin } from '@commerce/auth/use-login' +import { setCustomerToken } from '../utils' + +export default useLogin as UseLogin<typeof handler> + +const getErrorMessage = ({ code, message }: CustomerUserError) => { + switch (code) { + case 'UNIDENTIFIED_CUSTOMER': + message = 'Cannot find an account that matches the provided credentials' + break + } + return message +} + +export const handler: MutationHook<null, {}, CustomerAccessTokenCreateInput> = { + fetchOptions: { + query: createCustomerAccessTokenMutation, + }, + async fetcher({ input: { email, password }, options, fetch }) { + if (!(email && password)) { + throw new CommerceError({ + message: + 'A first name, last name, email and password are required to login', + }) + } + + const { customerAccessTokenCreate } = await fetch< + Mutation, + MutationCheckoutCreateArgs + >({ + ...options, + variables: { + input: { email, password }, + }, + }) + + const errors = customerAccessTokenCreate?.customerUserErrors + + if (errors && errors.length) { + throw new ValidationError({ + message: getErrorMessage(errors[0]), + }) + } + const customerAccessToken = customerAccessTokenCreate?.customerAccessToken + const accessToken = customerAccessToken?.accessToken + + if (accessToken) { + setCustomerToken(accessToken) + } + + return null + }, + useHook: ({ fetch }) => () => { + const { revalidate } = useCustomer() + + return useCallback( + async function login(input) { + const data = await fetch({ input }) + await revalidate() + return data + }, + [fetch, revalidate] + ) + }, +} diff --git a/framework/swell/auth/use-logout.tsx b/framework/swell/auth/use-logout.tsx new file mode 100644 index 000000000..81a3b8cdd --- /dev/null +++ b/framework/swell/auth/use-logout.tsx @@ -0,0 +1,36 @@ +import { useCallback } from 'react' +import type { MutationHook } from '@commerce/utils/types' +import useLogout, { UseLogout } from '@commerce/auth/use-logout' +import useCustomer from '../customer/use-customer' +import customerAccessTokenDeleteMutation from '../utils/mutations/customer-access-token-delete' +import { getCustomerToken, setCustomerToken } from '../utils/customer-token' + +export default useLogout as UseLogout<typeof handler> + +export const handler: MutationHook<null> = { + fetchOptions: { + query: customerAccessTokenDeleteMutation, + }, + async fetcher({ options, fetch }) { + await fetch({ + ...options, + variables: { + customerAccessToken: getCustomerToken(), + }, + }) + setCustomerToken(null) + return null + }, + useHook: ({ fetch }) => () => { + const { mutate } = useCustomer() + + return useCallback( + async function logout() { + const data = await fetch() + await mutate(null, false) + return data + }, + [fetch, mutate] + ) + }, +} diff --git a/framework/swell/auth/use-signup.tsx b/framework/swell/auth/use-signup.tsx new file mode 100644 index 000000000..7f66448d3 --- /dev/null +++ b/framework/swell/auth/use-signup.tsx @@ -0,0 +1,74 @@ +import { useCallback } from 'react' +import type { MutationHook } from '@commerce/utils/types' +import { CommerceError } from '@commerce/utils/errors' +import useSignup, { UseSignup } from '@commerce/auth/use-signup' +import useCustomer from '../customer/use-customer' +import { CustomerCreateInput } from '../schema' + +import { + customerCreateMutation, + customerAccessTokenCreateMutation, +} from '../utils/mutations' +import handleLogin from '../utils/handle-login' + +export default useSignup as UseSignup<typeof handler> + +export const handler: MutationHook< + null, + {}, + CustomerCreateInput, + CustomerCreateInput +> = { + fetchOptions: { + query: customerCreateMutation, + }, + async fetcher({ + input: { firstName, lastName, email, password }, + options, + fetch, + }) { + if (!(firstName && lastName && email && password)) { + throw new CommerceError({ + message: + 'A first name, last name, email and password are required to signup', + }) + } + const data = await fetch({ + ...options, + variables: { + input: { + firstName, + lastName, + email, + password, + }, + }, + }) + + try { + const loginData = await fetch({ + query: customerAccessTokenCreateMutation, + variables: { + input: { + email, + password, + }, + }, + }) + handleLogin(loginData) + } catch (error) {} + return data + }, + useHook: ({ fetch }) => () => { + const { revalidate } = useCustomer() + + return useCallback( + async function signup(input) { + const data = await fetch({ input }) + await revalidate() + return data + }, + [fetch, revalidate] + ) + }, +} diff --git a/framework/swell/cart/index.ts b/framework/swell/cart/index.ts new file mode 100644 index 000000000..3d288b1df --- /dev/null +++ b/framework/swell/cart/index.ts @@ -0,0 +1,3 @@ +export { default as useCart } from './use-cart' +export { default as useAddItem } from './use-add-item' +export { default as useRemoveItem } from './use-remove-item' diff --git a/framework/swell/cart/use-add-item.tsx b/framework/swell/cart/use-add-item.tsx new file mode 100644 index 000000000..d0f891148 --- /dev/null +++ b/framework/swell/cart/use-add-item.tsx @@ -0,0 +1,58 @@ +import type { MutationHook } from '@commerce/utils/types' +import { CommerceError } from '@commerce/utils/errors' +import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item' +import useCart from './use-cart' +import { Cart, CartItemBody } from '../types' +import { checkoutLineItemAddMutation, getCheckoutId } from '../utils' +import { checkoutToCart } from './utils' +import { Mutation, MutationCheckoutLineItemsAddArgs } from '../schema' +import { useCallback } from 'react' + +export default useAddItem as UseAddItem<typeof handler> + +export const handler: MutationHook<Cart, {}, CartItemBody> = { + fetchOptions: { + query: checkoutLineItemAddMutation, + }, + async fetcher({ input: item, options, fetch }) { + if ( + item.quantity && + (!Number.isInteger(item.quantity) || item.quantity! < 1) + ) { + throw new CommerceError({ + message: 'The item quantity has to be a valid integer greater than 0', + }) + } + + const { checkoutLineItemsAdd } = await fetch< + Mutation, + MutationCheckoutLineItemsAddArgs + >({ + ...options, + variables: { + checkoutId: getCheckoutId(), + lineItems: [ + { + variantId: item.variantId, + quantity: item.quantity ?? 1, + }, + ], + }, + }) + + // TODO: Fix this Cart type here + return checkoutToCart(checkoutLineItemsAdd) as any + }, + useHook: ({ fetch }) => () => { + const { mutate } = useCart() + + return useCallback( + async function addItem(input) { + const data = await fetch({ input }) + await mutate(data, false) + return data + }, + [fetch, mutate] + ) + }, +} diff --git a/framework/swell/cart/use-cart.tsx b/framework/swell/cart/use-cart.tsx new file mode 100644 index 000000000..d154bb837 --- /dev/null +++ b/framework/swell/cart/use-cart.tsx @@ -0,0 +1,59 @@ +import { useMemo } from 'react' +import useCommerceCart, { + FetchCartInput, + UseCart, +} from '@commerce/cart/use-cart' + +import { Cart } from '../types' +import { SWRHook } from '@commerce/utils/types' +import { checkoutCreate, checkoutToCart } from './utils' +import getCheckoutQuery from '../utils/queries/get-checkout-query' + +export default useCommerceCart as UseCart<typeof handler> + +export const handler: SWRHook< + Cart | null, + {}, + FetchCartInput, + { isEmpty?: boolean } +> = { + fetchOptions: { + query: getCheckoutQuery, + }, + async fetcher({ input: { cartId: checkoutId }, options, fetch }) { + let checkout + if (checkoutId) { + const data = await fetch({ + ...options, + variables: { + checkoutId, + }, + }) + checkout = data.node + } + + if (checkout?.completedAt || !checkoutId) { + checkout = await checkoutCreate(fetch) + } + + // TODO: Fix this type + return checkoutToCart({ checkout } as any) + }, + useHook: ({ useData }) => (input) => { + const response = useData({ + swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, + }) + return useMemo( + () => + Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, + }, + }), + [response] + ) + }, +} diff --git a/framework/swell/cart/use-remove-item.tsx b/framework/swell/cart/use-remove-item.tsx new file mode 100644 index 000000000..e2aef13d8 --- /dev/null +++ b/framework/swell/cart/use-remove-item.tsx @@ -0,0 +1,72 @@ +import { useCallback } from 'react' + +import type { + MutationHookContext, + HookFetcherContext, +} from '@commerce/utils/types' + +import { ValidationError } from '@commerce/utils/errors' + +import useRemoveItem, { + RemoveItemInput as RemoveItemInputBase, + UseRemoveItem, +} from '@commerce/cart/use-remove-item' + +import useCart from './use-cart' +import { checkoutLineItemRemoveMutation, getCheckoutId } from '../utils' +import { checkoutToCart } from './utils' +import { Cart, LineItem } from '../types' +import { Mutation, MutationCheckoutLineItemsRemoveArgs } from '../schema' +import { RemoveCartItemBody } from '@commerce/types' + +export type RemoveItemFn<T = any> = T extends LineItem + ? (input?: RemoveItemInput<T>) => Promise<Cart | null> + : (input: RemoveItemInput<T>) => Promise<Cart | null> + +export type RemoveItemInput<T = any> = T extends LineItem + ? Partial<RemoveItemInputBase> + : RemoveItemInputBase + +export default useRemoveItem as UseRemoveItem<typeof handler> + +export const handler = { + fetchOptions: { + query: checkoutLineItemRemoveMutation, + }, + async fetcher({ + input: { itemId }, + options, + fetch, + }: HookFetcherContext<RemoveCartItemBody>) { + const data = await fetch<Mutation, MutationCheckoutLineItemsRemoveArgs>({ + ...options, + variables: { checkoutId: getCheckoutId(), lineItemIds: [itemId] }, + }) + return checkoutToCart(data.checkoutLineItemsRemove) + }, + useHook: ({ + fetch, + }: MutationHookContext<Cart | null, RemoveCartItemBody>) => < + T extends LineItem | undefined = undefined + >( + ctx: { item?: T } = {} + ) => { + const { item } = ctx + const { mutate } = useCart() + const removeItem: RemoveItemFn<LineItem> = async (input) => { + const itemId = input?.id ?? item?.id + + if (!itemId) { + throw new ValidationError({ + message: 'Invalid input used for this operation', + }) + } + + const data = await fetch({ input: { itemId } }) + await mutate(data, false) + return data + } + + return useCallback(removeItem as RemoveItemFn<T>, [fetch, mutate]) + }, +} diff --git a/framework/swell/cart/use-update-item.tsx b/framework/swell/cart/use-update-item.tsx new file mode 100644 index 000000000..666ce3d08 --- /dev/null +++ b/framework/swell/cart/use-update-item.tsx @@ -0,0 +1,107 @@ +import { useCallback } from 'react' +import debounce from 'lodash.debounce' +import type { + HookFetcherContext, + MutationHookContext, +} from '@commerce/utils/types' +import { ValidationError } from '@commerce/utils/errors' +import useUpdateItem, { + UpdateItemInput as UpdateItemInputBase, + UseUpdateItem, +} from '@commerce/cart/use-update-item' + +import useCart from './use-cart' +import { handler as removeItemHandler } from './use-remove-item' +import type { Cart, LineItem, UpdateCartItemBody } from '../types' +import { checkoutToCart } from './utils' +import { getCheckoutId, checkoutLineItemUpdateMutation } from '../utils' +import { Mutation, MutationCheckoutLineItemsUpdateArgs } from '../schema' + +export type UpdateItemInput<T = any> = T extends LineItem + ? Partial<UpdateItemInputBase<LineItem>> + : UpdateItemInputBase<LineItem> + +export default useUpdateItem as UseUpdateItem<typeof handler> + +export const handler = { + fetchOptions: { + query: checkoutLineItemUpdateMutation, + }, + async fetcher({ + input: { itemId, item }, + options, + fetch, + }: HookFetcherContext<UpdateCartItemBody>) { + if (Number.isInteger(item.quantity)) { + // Also allow the update hook to remove an item if the quantity is lower than 1 + if (item.quantity! < 1) { + return removeItemHandler.fetcher({ + options: removeItemHandler.fetchOptions, + input: { itemId }, + fetch, + }) + } + } else if (item.quantity) { + throw new ValidationError({ + message: 'The item quantity has to be a valid integer', + }) + } + const { checkoutLineItemsUpdate } = await fetch< + Mutation, + MutationCheckoutLineItemsUpdateArgs + >({ + ...options, + variables: { + checkoutId: getCheckoutId(), + lineItems: [ + { + id: itemId, + quantity: item.quantity, + }, + ], + }, + }) + + return checkoutToCart(checkoutLineItemsUpdate) + }, + useHook: ({ + fetch, + }: MutationHookContext<Cart | null, UpdateCartItemBody>) => < + T extends LineItem | undefined = undefined + >( + ctx: { + item?: T + wait?: number + } = {} + ) => { + const { item } = ctx + const { mutate } = useCart() as any + + return useCallback( + debounce(async (input: UpdateItemInput<T>) => { + const itemId = input.id ?? item?.id + const productId = input.productId ?? item?.productId + const variantId = input.productId ?? item?.variantId + if (!itemId || !productId || !variantId) { + throw new ValidationError({ + message: 'Invalid input used for this operation', + }) + } + + const data = await fetch({ + input: { + item: { + productId, + variantId, + quantity: input.quantity, + }, + itemId, + }, + }) + await mutate(data, false) + return data + }, ctx.wait ?? 500), + [fetch, mutate] + ) + }, +} diff --git a/framework/swell/cart/utils/checkout-create.ts b/framework/swell/cart/utils/checkout-create.ts new file mode 100644 index 000000000..e950cc7e4 --- /dev/null +++ b/framework/swell/cart/utils/checkout-create.ts @@ -0,0 +1,29 @@ +import { + SHOPIFY_CHECKOUT_ID_COOKIE, + SHOPIFY_CHECKOUT_URL_COOKIE, + SHOPIFY_COOKIE_EXPIRE, +} from '../../const' + +import checkoutCreateMutation from '../../utils/mutations/checkout-create' +import Cookies from 'js-cookie' + +export const checkoutCreate = async (fetch: any) => { + const data = await fetch({ + query: checkoutCreateMutation, + }) + + const checkout = data.checkoutCreate?.checkout + const checkoutId = checkout?.id + + if (checkoutId) { + const options = { + expires: SHOPIFY_COOKIE_EXPIRE, + } + Cookies.set(SHOPIFY_CHECKOUT_ID_COOKIE, checkoutId, options) + Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout?.webUrl, options) + } + + return checkout +} + +export default checkoutCreate diff --git a/framework/swell/cart/utils/checkout-to-cart.ts b/framework/swell/cart/utils/checkout-to-cart.ts new file mode 100644 index 000000000..03005f342 --- /dev/null +++ b/framework/swell/cart/utils/checkout-to-cart.ts @@ -0,0 +1,42 @@ +import { Cart } from '../../types' +import { CommerceError, ValidationError } from '@commerce/utils/errors' + +import { + CheckoutLineItemsAddPayload, + CheckoutLineItemsRemovePayload, + CheckoutLineItemsUpdatePayload, + Maybe, +} from '../../schema' +import { normalizeCart } from '../../utils' + +export type CheckoutPayload = + | CheckoutLineItemsAddPayload + | CheckoutLineItemsUpdatePayload + | CheckoutLineItemsRemovePayload + +const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => { + if (!checkoutPayload) { + throw new CommerceError({ + message: 'Invalid response from Shopify', + }) + } + + const checkout = checkoutPayload?.checkout + const userErrors = checkoutPayload?.userErrors + + if (userErrors && userErrors.length) { + throw new ValidationError({ + message: userErrors[0].message, + }) + } + + if (!checkout) { + throw new CommerceError({ + message: 'Invalid response from Shopify', + }) + } + + return normalizeCart(checkout) +} + +export default checkoutToCart diff --git a/framework/swell/cart/utils/fetcher.ts b/framework/swell/cart/utils/fetcher.ts new file mode 100644 index 000000000..6afb55f18 --- /dev/null +++ b/framework/swell/cart/utils/fetcher.ts @@ -0,0 +1,31 @@ +import { HookFetcherFn } from '@commerce/utils/types' +import { Cart } from '@commerce/types' +import { checkoutCreate, checkoutToCart } from '.' +import { FetchCartInput } from '@commerce/cart/use-cart' + +const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ + options, + input: { cartId: checkoutId }, + fetch, +}) => { + let checkout + + if (checkoutId) { + const data = await fetch({ + ...options, + variables: { + checkoutId, + }, + }) + checkout = data.node + } + + if (checkout?.completedAt || !checkoutId) { + checkout = await checkoutCreate(fetch) + } + + // TODO: Fix this type + return checkoutToCart({ checkout } as any) +} + +export default fetcher diff --git a/framework/swell/cart/utils/index.ts b/framework/swell/cart/utils/index.ts new file mode 100644 index 000000000..20d04955d --- /dev/null +++ b/framework/swell/cart/utils/index.ts @@ -0,0 +1,2 @@ +export { default as checkoutToCart } from './checkout-to-cart' +export { default as checkoutCreate } from './checkout-create' diff --git a/framework/swell/commerce.config.json b/framework/swell/commerce.config.json new file mode 100644 index 000000000..b30ab39d9 --- /dev/null +++ b/framework/swell/commerce.config.json @@ -0,0 +1,6 @@ +{ + "provider": "shopify", + "features": { + "wishlist": false + } +} diff --git a/framework/swell/common/get-all-pages.ts b/framework/swell/common/get-all-pages.ts new file mode 100644 index 000000000..54231ed03 --- /dev/null +++ b/framework/swell/common/get-all-pages.ts @@ -0,0 +1,42 @@ +import { getConfig, ShopifyConfig } from '../api' +import { PageEdge } from '../schema' +import { getAllPagesQuery } from '../utils/queries' + +type Variables = { + first?: number +} + +type ReturnType = { + pages: Page[] +} + +export type Page = { + id: string + name: string + url: string + sort_order?: number + body: string +} + +const getAllPages = async (options?: { + variables?: Variables + config: ShopifyConfig + preview?: boolean +}): Promise<ReturnType> => { + let { config, variables = { first: 250 } } = options ?? {} + config = getConfig(config) + const { locale } = config + const { data } = await config.fetch(getAllPagesQuery, { variables }) + + const pages = data.pages?.edges?.map( + ({ node: { title: name, handle, ...node } }: PageEdge) => ({ + ...node, + url: `/${locale}/${handle}`, + name, + }) + ) + + return { pages } +} + +export default getAllPages diff --git a/framework/swell/common/get-page.ts b/framework/swell/common/get-page.ts new file mode 100644 index 000000000..be934aa42 --- /dev/null +++ b/framework/swell/common/get-page.ts @@ -0,0 +1,37 @@ +import { getConfig, ShopifyConfig } from '../api' +import getPageQuery from '../utils/queries/get-page-query' +import { Page } from './get-all-pages' + +type Variables = { + id: string +} + +export type GetPageResult<T extends { page?: any } = { page?: Page }> = T + +const getPage = async (options: { + variables: Variables + config: ShopifyConfig + preview?: boolean +}): Promise<GetPageResult> => { + let { config, variables } = options ?? {} + + config = getConfig(config) + const { locale } = config + + const { data } = await config.fetch(getPageQuery, { + variables, + }) + const page = data.node + + return { + page: page + ? { + ...page, + name: page.title, + url: `/${locale}/${page.handle}`, + } + : null, + } +} + +export default getPage diff --git a/framework/swell/common/get-site-info.ts b/framework/swell/common/get-site-info.ts new file mode 100644 index 000000000..cbbacf5b6 --- /dev/null +++ b/framework/swell/common/get-site-info.ts @@ -0,0 +1,31 @@ +import getCategories, { Category } from '../utils/get-categories' +import getVendors, { Brands } from '../utils/get-vendors' + +import { getConfig, ShopifyConfig } from '../api' + +export type GetSiteInfoResult< + T extends { categories: any[]; brands: any[] } = { + categories: Category[] + brands: Brands + } +> = T + +const getSiteInfo = async (options?: { + variables?: any + config: ShopifyConfig + preview?: boolean +}): Promise<GetSiteInfoResult> => { + let { config } = options ?? {} + + config = getConfig(config) + + const categories = await getCategories(config) + const brands = await getVendors(config) + + return { + categories, + brands, + } +} + +export default getSiteInfo diff --git a/framework/swell/const.ts b/framework/swell/const.ts new file mode 100644 index 000000000..06fbe5054 --- /dev/null +++ b/framework/swell/const.ts @@ -0,0 +1,13 @@ +export const SHOPIFY_CHECKOUT_ID_COOKIE = 'shopify_checkoutId' + +export const SHOPIFY_CHECKOUT_URL_COOKIE = 'shopify_checkoutUrl' + +export const SHOPIFY_CUSTOMER_TOKEN_COOKIE = 'shopify_customerToken' + +export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN + +export const SHOPIFY_COOKIE_EXPIRE = 30 + +export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json` + +export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN diff --git a/framework/swell/customer/get-customer-id.ts b/framework/swell/customer/get-customer-id.ts new file mode 100644 index 000000000..ca096645a --- /dev/null +++ b/framework/swell/customer/get-customer-id.ts @@ -0,0 +1,24 @@ +import { getConfig, ShopifyConfig } from '../api' +import getCustomerIdQuery from '../utils/queries/get-customer-id-query' +import Cookies from 'js-cookie' + +async function getCustomerId({ + customerToken: customerAccesToken, + config, +}: { + customerToken: string + config?: ShopifyConfig +}): Promise<number | undefined> { + config = getConfig(config) + + const { data } = await config.fetch(getCustomerIdQuery, { + variables: { + customerAccesToken: + customerAccesToken || Cookies.get(config.customerCookie), + }, + }) + + return data.customer?.id +} + +export default getCustomerId diff --git a/framework/swell/customer/index.ts b/framework/swell/customer/index.ts new file mode 100644 index 000000000..6c903ecc5 --- /dev/null +++ b/framework/swell/customer/index.ts @@ -0,0 +1 @@ +export { default as useCustomer } from './use-customer' diff --git a/framework/swell/customer/use-customer.tsx b/framework/swell/customer/use-customer.tsx new file mode 100644 index 000000000..137f0da74 --- /dev/null +++ b/framework/swell/customer/use-customer.tsx @@ -0,0 +1,27 @@ +import useCustomer, { UseCustomer } from '@commerce/customer/use-customer' +import { Customer } from '@commerce/types' +import { SWRHook } from '@commerce/utils/types' +import { getCustomerQuery, getCustomerToken } from '../utils' + +export default useCustomer as UseCustomer<typeof handler> + +export const handler: SWRHook<Customer | null> = { + fetchOptions: { + query: getCustomerQuery, + }, + async fetcher({ options, fetch }) { + const data = await fetch<any | null>({ + ...options, + variables: { customerAccessToken: getCustomerToken() }, + }) + return data.customer ?? null + }, + useHook: ({ useData }) => (input) => { + return useData({ + swrOptions: { + revalidateOnFocus: false, + ...input?.swrOptions, + }, + }) + }, +} diff --git a/framework/swell/fetcher.ts b/framework/swell/fetcher.ts new file mode 100644 index 000000000..9c4fe9a9e --- /dev/null +++ b/framework/swell/fetcher.ts @@ -0,0 +1,18 @@ +import { Fetcher } from '@commerce/utils/types' +import { API_TOKEN, API_URL } from './const' +import { handleFetchResponse } from './utils' + +const fetcher: Fetcher = async ({ method = 'POST', variables, query }) => { + return handleFetchResponse( + await fetch(API_URL, { + method, + body: JSON.stringify({ query, variables }), + headers: { + 'X-Shopify-Storefront-Access-Token': API_TOKEN!, + 'Content-Type': 'application/json', + }, + }) + ) +} + +export default fetcher diff --git a/framework/swell/index.tsx b/framework/swell/index.tsx new file mode 100644 index 000000000..c26704771 --- /dev/null +++ b/framework/swell/index.tsx @@ -0,0 +1,40 @@ +import * as React from 'react' +import { ReactNode } from 'react' + +import { + CommerceConfig, + CommerceProvider as CoreCommerceProvider, + useCommerce as useCoreCommerce, +} from '@commerce' + +import { shopifyProvider, ShopifyProvider } from './provider' +import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const' + +export { shopifyProvider } +export type { ShopifyProvider } + +export const shopifyConfig: CommerceConfig = { + locale: 'en-us', + cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, +} + +export type ShopifyConfig = Partial<CommerceConfig> + +export type ShopifyProps = { + children?: ReactNode + locale: string +} & ShopifyConfig + +export function CommerceProvider({ children, ...config }: ShopifyProps) { + return ( + <CoreCommerceProvider + // TODO: Fix this type + provider={shopifyProvider as any} + config={{ ...shopifyConfig, ...config }} + > + {children} + </CoreCommerceProvider> + ) +} + +export const useCommerce = () => useCoreCommerce() diff --git a/framework/swell/next.config.js b/framework/swell/next.config.js new file mode 100644 index 000000000..e9d48c02c --- /dev/null +++ b/framework/swell/next.config.js @@ -0,0 +1,8 @@ +const commerce = require('./commerce.config.json') + +module.exports = { + commerce, + images: { + domains: ['cdn.shopify.com'], + }, +} diff --git a/framework/swell/product/get-all-collections.ts b/framework/swell/product/get-all-collections.ts new file mode 100644 index 000000000..15c4bc51a --- /dev/null +++ b/framework/swell/product/get-all-collections.ts @@ -0,0 +1,29 @@ +import { CollectionEdge } from '../schema' +import { getConfig, ShopifyConfig } from '../api' +import getAllCollectionsQuery from '../utils/queries/get-all-collections-query' + +const getAllCollections = async (options?: { + variables?: any + config: ShopifyConfig + preview?: boolean +}) => { + let { config, variables = { first: 250 } } = options ?? {} + config = getConfig(config) + + const { data } = await config.fetch(getAllCollectionsQuery, { variables }) + const edges = data.collections?.edges ?? [] + + const categories = edges.map( + ({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({ + entityId, + name, + path: `/${handle}`, + }) + ) + + return { + categories, + } +} + +export default getAllCollections diff --git a/framework/swell/product/get-all-product-paths.ts b/framework/swell/product/get-all-product-paths.ts new file mode 100644 index 000000000..4431d1e53 --- /dev/null +++ b/framework/swell/product/get-all-product-paths.ts @@ -0,0 +1,42 @@ +import { Product } from '@commerce/types' +import { getConfig, ShopifyConfig } from '../api' +import fetchAllProducts from '../api/utils/fetch-all-products' +import { ProductEdge } from '../schema' +import getAllProductsPathsQuery from '../utils/queries/get-all-products-paths-query' + +type ProductPath = { + path: string +} + +export type ProductPathNode = { + node: ProductPath +} + +type ReturnType = { + products: ProductPathNode[] +} + +const getAllProductPaths = async (options?: { + variables?: any + config?: ShopifyConfig + preview?: boolean +}): Promise<ReturnType> => { + let { config, variables = { first: 250 } } = options ?? {} + config = getConfig(config) + + const products = await fetchAllProducts({ + config, + query: getAllProductsPathsQuery, + variables, + }) + + return { + products: products?.map(({ node: { handle } }: ProductEdge) => ({ + node: { + path: `/${handle}`, + }, + })), + } +} + +export default getAllProductPaths diff --git a/framework/swell/product/get-all-products.ts b/framework/swell/product/get-all-products.ts new file mode 100644 index 000000000..14e486563 --- /dev/null +++ b/framework/swell/product/get-all-products.ts @@ -0,0 +1,40 @@ +import { GraphQLFetcherResult } from '@commerce/api' +import { getConfig, ShopifyConfig } from '../api' +import { ProductEdge } from '../schema' +import { getAllProductsQuery } from '../utils/queries' +import { normalizeProduct } from '../utils/normalize' +import { Product } from '@commerce/types' + +type Variables = { + first?: number + field?: string +} + +type ReturnType = { + products: Product[] +} + +const getAllProducts = async (options: { + variables?: Variables + config?: ShopifyConfig + preview?: boolean +}): Promise<ReturnType> => { + let { config, variables = { first: 250 } } = options ?? {} + config = getConfig(config) + + const { data }: GraphQLFetcherResult = await config.fetch( + getAllProductsQuery, + { variables } + ) + + const products = + data.products?.edges?.map(({ node: p }: ProductEdge) => + normalizeProduct(p) + ) ?? [] + + return { + products, + } +} + +export default getAllProducts diff --git a/framework/swell/product/get-product.ts b/framework/swell/product/get-product.ts new file mode 100644 index 000000000..1f00288c7 --- /dev/null +++ b/framework/swell/product/get-product.ts @@ -0,0 +1,32 @@ +import { GraphQLFetcherResult } from '@commerce/api' +import { getConfig, ShopifyConfig } from '../api' +import { normalizeProduct, getProductQuery } from '../utils' + +type Variables = { + slug: string +} + +type ReturnType = { + product: any +} + +const getProduct = async (options: { + variables: Variables + config: ShopifyConfig + preview?: boolean +}): Promise<ReturnType> => { + let { config, variables } = options ?? {} + config = getConfig(config) + + const { data }: GraphQLFetcherResult = await config.fetch(getProductQuery, { + variables, + }) + + const { productByHandle: product } = data + + return { + product: product ? normalizeProduct(product) : null, + } +} + +export default getProduct diff --git a/framework/swell/product/use-price.tsx b/framework/swell/product/use-price.tsx new file mode 100644 index 000000000..0174faf5e --- /dev/null +++ b/framework/swell/product/use-price.tsx @@ -0,0 +1,2 @@ +export * from '@commerce/product/use-price' +export { default } from '@commerce/product/use-price' diff --git a/framework/swell/product/use-search.tsx b/framework/swell/product/use-search.tsx new file mode 100644 index 000000000..425df9e83 --- /dev/null +++ b/framework/swell/product/use-search.tsx @@ -0,0 +1,77 @@ +import { SWRHook } from '@commerce/utils/types' +import useSearch, { UseSearch } from '@commerce/product/use-search' + +import { ProductEdge } from '../schema' +import { + getAllProductsQuery, + getCollectionProductsQuery, + getSearchVariables, + normalizeProduct, +} from '../utils' + +import { Product } from '@commerce/types' + +export default useSearch as UseSearch<typeof handler> + +export type SearchProductsInput = { + search?: string + categoryId?: string + brandId?: string + sort?: string +} + +export type SearchProductsData = { + products: Product[] + found: boolean +} + +export const handler: SWRHook< + SearchProductsData, + SearchProductsInput, + SearchProductsInput +> = { + fetchOptions: { + query: getAllProductsQuery, + }, + async fetcher({ input, options, fetch }) { + const { categoryId, brandId } = input + + const data = await fetch({ + query: categoryId ? getCollectionProductsQuery : options.query, + method: options?.method, + variables: getSearchVariables(input), + }) + + let edges + + if (categoryId) { + edges = data.node?.products?.edges ?? [] + if (brandId) { + edges = edges.filter( + ({ node: { vendor } }: ProductEdge) => vendor === brandId + ) + } + } else { + edges = data.products?.edges ?? [] + } + + return { + products: edges.map(({ node }: ProductEdge) => normalizeProduct(node)), + found: !!edges.length, + } + }, + useHook: ({ useData }) => (input = {}) => { + return useData({ + input: [ + ['search', input.search], + ['categoryId', input.categoryId], + ['brandId', input.brandId], + ['sort', input.sort], + ], + swrOptions: { + revalidateOnFocus: false, + ...input.swrOptions, + }, + }) + }, +} diff --git a/framework/swell/provider.ts b/framework/swell/provider.ts new file mode 100644 index 000000000..383822baa --- /dev/null +++ b/framework/swell/provider.ts @@ -0,0 +1,31 @@ +import { SHOPIFY_CHECKOUT_ID_COOKIE, STORE_DOMAIN } from './const' + +import { handler as useCart } from './cart/use-cart' +import { handler as useAddItem } from './cart/use-add-item' +import { handler as useUpdateItem } from './cart/use-update-item' +import { handler as useRemoveItem } from './cart/use-remove-item' + +import { handler as useCustomer } from './customer/use-customer' +import { handler as useSearch } from './product/use-search' + +import { handler as useLogin } from './auth/use-login' +import { handler as useLogout } from './auth/use-logout' +import { handler as useSignup } from './auth/use-signup' + +import fetcher from './fetcher' + +export const shopifyProvider = { + locale: 'en-us', + cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, + storeDomain: STORE_DOMAIN, + fetcher, + cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, + customer: { useCustomer }, + products: { useSearch }, + auth: { useLogin, useLogout, useSignup }, + features: { + wishlist: false, + }, +} + +export type ShopifyProvider = typeof shopifyProvider diff --git a/framework/swell/schema.d.ts b/framework/swell/schema.d.ts new file mode 100644 index 000000000..b1b23a3e5 --- /dev/null +++ b/framework/swell/schema.d.ts @@ -0,0 +1,4985 @@ +export type Maybe<T> = T | null +export type Exact<T extends { [key: string]: unknown }> = { + [K in keyof T]: T[K] +} +export type MakeOptional<T, K extends keyof T> = Omit<T, K> & + { [SubKey in K]?: Maybe<T[SubKey]> } +export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & + { [SubKey in K]: Maybe<T[SubKey]> } +/** All built-in and custom scalars, mapped to their actual values */ +export type Scalars = { + ID: string + String: string + Boolean: boolean + Int: number + Float: number + /** An ISO-8601 encoded UTC date time string. Example value: `"2019-07-03T20:47:55Z"`. */ + DateTime: any + /** A signed decimal number, which supports arbitrary precision and is serialized as a string. Example value: `"29.99"`. */ + Decimal: any + /** A string containing HTML code. Example value: `"<p>Grey cotton knit sweater.</p>"`. */ + HTML: any + /** A monetary value string. Example value: `"100.57"`. */ + Money: any + /** + * An RFC 3986 and RFC 3987 compliant URI string. + * + * Example value: `"https://johns-apparel.myshopify.com"`. + * + */ + URL: any +} + +/** A version of the API. */ +export type ApiVersion = { + __typename?: 'ApiVersion' + /** The human-readable name of the version. */ + displayName: Scalars['String'] + /** The unique identifier of an ApiVersion. All supported API versions have a date-based (YYYY-MM) or `unstable` handle. */ + handle: Scalars['String'] + /** Whether the version is supported by Shopify. */ + supported: Scalars['Boolean'] +} + +/** Details about the gift card used on the checkout. */ +export type AppliedGiftCard = Node & { + __typename?: 'AppliedGiftCard' + /** + * The amount that was taken from the gift card by applying it. + * @deprecated Use `amountUsedV2` instead + */ + amountUsed: Scalars['Money'] + /** The amount that was taken from the gift card by applying it. */ + amountUsedV2: MoneyV2 + /** + * The amount left on the gift card. + * @deprecated Use `balanceV2` instead + */ + balance: Scalars['Money'] + /** The amount left on the gift card. */ + balanceV2: MoneyV2 + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The last characters of the gift card. */ + lastCharacters: Scalars['String'] + /** The amount that was applied to the checkout in its currency. */ + presentmentAmountUsed: MoneyV2 +} + +/** An article in an online store blog. */ +export type Article = Node & { + __typename?: 'Article' + /** + * The article's author. + * @deprecated Use `authorV2` instead + */ + author: ArticleAuthor + /** The article's author. */ + authorV2?: Maybe<ArticleAuthor> + /** The blog that the article belongs to. */ + blog: Blog + /** List of comments posted on the article. */ + comments: CommentConnection + /** Stripped content of the article, single line with HTML tags removed. */ + content: Scalars['String'] + /** The content of the article, complete with HTML formatting. */ + contentHtml: Scalars['HTML'] + /** Stripped excerpt of the article, single line with HTML tags removed. */ + excerpt?: Maybe<Scalars['String']> + /** The excerpt of the article, complete with HTML formatting. */ + excerptHtml?: Maybe<Scalars['HTML']> + /** A human-friendly unique string for the Article automatically generated from its title. */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The image associated with the article. */ + image?: Maybe<Image> + /** The date and time when the article was published. */ + publishedAt: Scalars['DateTime'] + /** The article’s SEO information. */ + seo?: Maybe<Seo> + /** A categorization that a article can be tagged with. */ + tags: Array<Scalars['String']> + /** The article’s name. */ + title: Scalars['String'] + /** The url pointing to the article accessible from the web. */ + url: Scalars['URL'] +} + +/** An article in an online store blog. */ +export type ArticleCommentsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** An article in an online store blog. */ +export type ArticleContentArgs = { + truncateAt?: Maybe<Scalars['Int']> +} + +/** An article in an online store blog. */ +export type ArticleExcerptArgs = { + truncateAt?: Maybe<Scalars['Int']> +} + +/** An article in an online store blog. */ +export type ArticleImageArgs = { + maxWidth?: Maybe<Scalars['Int']> + maxHeight?: Maybe<Scalars['Int']> + crop?: Maybe<CropRegion> + scale?: Maybe<Scalars['Int']> +} + +/** The author of an article. */ +export type ArticleAuthor = { + __typename?: 'ArticleAuthor' + /** The author's bio. */ + bio?: Maybe<Scalars['String']> + /** The author’s email. */ + email: Scalars['String'] + /** The author's first name. */ + firstName: Scalars['String'] + /** The author's last name. */ + lastName: Scalars['String'] + /** The author's full name. */ + name: Scalars['String'] +} + +/** An auto-generated type for paginating through multiple Articles. */ +export type ArticleConnection = { + __typename?: 'ArticleConnection' + /** A list of edges. */ + edges: Array<ArticleEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Article and a cursor during pagination. */ +export type ArticleEdge = { + __typename?: 'ArticleEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ArticleEdge. */ + node: Article +} + +/** The set of valid sort keys for the Article query. */ +export enum ArticleSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `blog_title` value. */ + BlogTitle = 'BLOG_TITLE', + /** Sort by the `author` value. */ + Author = 'AUTHOR', + /** Sort by the `updated_at` value. */ + UpdatedAt = 'UPDATED_AT', + /** Sort by the `published_at` value. */ + PublishedAt = 'PUBLISHED_AT', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** Represents a generic custom attribute. */ +export type Attribute = { + __typename?: 'Attribute' + /** Key or name of the attribute. */ + key: Scalars['String'] + /** Value of the attribute. */ + value?: Maybe<Scalars['String']> +} + +/** Specifies the input fields required for an attribute. */ +export type AttributeInput = { + /** Key or name of the attribute. */ + key: Scalars['String'] + /** Value of the attribute. */ + value: Scalars['String'] +} + +/** Automatic discount applications capture the intentions of a discount that was automatically applied. */ +export type AutomaticDiscountApplication = DiscountApplication & { + __typename?: 'AutomaticDiscountApplication' + /** The method by which the discount's value is allocated to its entitled items. */ + allocationMethod: DiscountApplicationAllocationMethod + /** Which lines of targetType that the discount is allocated over. */ + targetSelection: DiscountApplicationTargetSelection + /** The type of line that the discount is applicable towards. */ + targetType: DiscountApplicationTargetType + /** The title of the application. */ + title: Scalars['String'] + /** The value of the discount application. */ + value: PricingValue +} + +/** A collection of available shipping rates for a checkout. */ +export type AvailableShippingRates = { + __typename?: 'AvailableShippingRates' + /** + * Whether or not the shipping rates are ready. + * The `shippingRates` field is `null` when this value is `false`. + * This field should be polled until its value becomes `true`. + */ + ready: Scalars['Boolean'] + /** The fetched shipping rates. `null` until the `ready` field is `true`. */ + shippingRates?: Maybe<Array<ShippingRate>> +} + +/** An online store blog. */ +export type Blog = Node & { + __typename?: 'Blog' + /** Find an article by its handle. */ + articleByHandle?: Maybe<Article> + /** List of the blog's articles. */ + articles: ArticleConnection + /** The authors who have contributed to the blog. */ + authors: Array<ArticleAuthor> + /** A human-friendly unique string for the Blog automatically generated from its title. */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The blog's SEO information. */ + seo?: Maybe<Seo> + /** The blogs’s title. */ + title: Scalars['String'] + /** The url pointing to the blog accessible from the web. */ + url: Scalars['URL'] +} + +/** An online store blog. */ +export type BlogArticleByHandleArgs = { + handle: Scalars['String'] +} + +/** An online store blog. */ +export type BlogArticlesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ArticleSortKeys> + query?: Maybe<Scalars['String']> +} + +/** An auto-generated type for paginating through multiple Blogs. */ +export type BlogConnection = { + __typename?: 'BlogConnection' + /** A list of edges. */ + edges: Array<BlogEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Blog and a cursor during pagination. */ +export type BlogEdge = { + __typename?: 'BlogEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of BlogEdge. */ + node: Blog +} + +/** The set of valid sort keys for the Blog query. */ +export enum BlogSortKeys { + /** Sort by the `handle` value. */ + Handle = 'HANDLE', + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** Card brand, such as Visa or Mastercard, which can be used for payments. */ +export enum CardBrand { + /** Visa */ + Visa = 'VISA', + /** Mastercard */ + Mastercard = 'MASTERCARD', + /** Discover */ + Discover = 'DISCOVER', + /** American Express */ + AmericanExpress = 'AMERICAN_EXPRESS', + /** Diners Club */ + DinersClub = 'DINERS_CLUB', + /** JCB */ + Jcb = 'JCB', +} + +/** A container for all the information required to checkout items and pay. */ +export type Checkout = Node & { + __typename?: 'Checkout' + /** The gift cards used on the checkout. */ + appliedGiftCards: Array<AppliedGiftCard> + /** + * The available shipping rates for this Checkout. + * Should only be used when checkout `requiresShipping` is `true` and + * the shipping address is valid. + */ + availableShippingRates?: Maybe<AvailableShippingRates> + /** The date and time when the checkout was completed. */ + completedAt?: Maybe<Scalars['DateTime']> + /** The date and time when the checkout was created. */ + createdAt: Scalars['DateTime'] + /** The currency code for the Checkout. */ + currencyCode: CurrencyCode + /** A list of extra information that is added to the checkout. */ + customAttributes: Array<Attribute> + /** + * The customer associated with the checkout. + * @deprecated This field will always return null. If you have an authentication token for the customer, you can use the `customer` field on the query root to retrieve it. + */ + customer?: Maybe<Customer> + /** Discounts that have been applied on the checkout. */ + discountApplications: DiscountApplicationConnection + /** The email attached to this checkout. */ + email?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** A list of line item objects, each one containing information about an item in the checkout. */ + lineItems: CheckoutLineItemConnection + /** The sum of all the prices of all the items in the checkout. Duties, taxes, shipping and discounts excluded. */ + lineItemsSubtotalPrice: MoneyV2 + /** The note associated with the checkout. */ + note?: Maybe<Scalars['String']> + /** The resulting order from a paid checkout. */ + order?: Maybe<Order> + /** The Order Status Page for this Checkout, null when checkout is not completed. */ + orderStatusUrl?: Maybe<Scalars['URL']> + /** + * The amount left to be paid. This is equal to the cost of the line items, taxes and shipping minus discounts and gift cards. + * @deprecated Use `paymentDueV2` instead + */ + paymentDue: Scalars['Money'] + /** The amount left to be paid. This is equal to the cost of the line items, duties, taxes and shipping minus discounts and gift cards. */ + paymentDueV2: MoneyV2 + /** + * Whether or not the Checkout is ready and can be completed. Checkouts may + * have asynchronous operations that can take time to finish. If you want + * to complete a checkout or ensure all the fields are populated and up to + * date, polling is required until the value is true. + */ + ready: Scalars['Boolean'] + /** States whether or not the fulfillment requires shipping. */ + requiresShipping: Scalars['Boolean'] + /** The shipping address to where the line items will be shipped. */ + shippingAddress?: Maybe<MailingAddress> + /** The discounts that have been allocated onto the shipping line by discount applications. */ + shippingDiscountAllocations: Array<DiscountAllocation> + /** Once a shipping rate is selected by the customer it is transitioned to a `shipping_line` object. */ + shippingLine?: Maybe<ShippingRate> + /** + * Price of the checkout before shipping and taxes. + * @deprecated Use `subtotalPriceV2` instead + */ + subtotalPrice: Scalars['Money'] + /** Price of the checkout before duties, shipping and taxes. */ + subtotalPriceV2: MoneyV2 + /** Specifies if the Checkout is tax exempt. */ + taxExempt: Scalars['Boolean'] + /** Specifies if taxes are included in the line item and shipping line prices. */ + taxesIncluded: Scalars['Boolean'] + /** + * The sum of all the prices of all the items in the checkout, taxes and discounts included. + * @deprecated Use `totalPriceV2` instead + */ + totalPrice: Scalars['Money'] + /** The sum of all the prices of all the items in the checkout, duties, taxes and discounts included. */ + totalPriceV2: MoneyV2 + /** + * The sum of all the taxes applied to the line items and shipping lines in the checkout. + * @deprecated Use `totalTaxV2` instead + */ + totalTax: Scalars['Money'] + /** The sum of all the taxes applied to the line items and shipping lines in the checkout. */ + totalTaxV2: MoneyV2 + /** The date and time when the checkout was last updated. */ + updatedAt: Scalars['DateTime'] + /** The url pointing to the checkout accessible from the web. */ + webUrl: Scalars['URL'] +} + +/** A container for all the information required to checkout items and pay. */ +export type CheckoutDiscountApplicationsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** A container for all the information required to checkout items and pay. */ +export type CheckoutLineItemsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** Specifies the fields required to update a checkout's attributes. */ +export type CheckoutAttributesUpdateInput = { + /** The text of an optional note that a shop owner can attach to the checkout. */ + note?: Maybe<Scalars['String']> + /** A list of extra information that is added to the checkout. */ + customAttributes?: Maybe<Array<AttributeInput>> + /** + * Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + * The required attributes are city, province, and country. + * Full validation of the addresses is still done at complete time. + */ + allowPartialAddresses?: Maybe<Scalars['Boolean']> +} + +/** Return type for `checkoutAttributesUpdate` mutation. */ +export type CheckoutAttributesUpdatePayload = { + __typename?: 'CheckoutAttributesUpdatePayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Specifies the fields required to update a checkout's attributes. */ +export type CheckoutAttributesUpdateV2Input = { + /** The text of an optional note that a shop owner can attach to the checkout. */ + note?: Maybe<Scalars['String']> + /** A list of extra information that is added to the checkout. */ + customAttributes?: Maybe<Array<AttributeInput>> + /** + * Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + * The required attributes are city, province, and country. + * Full validation of the addresses is still done at complete time. + */ + allowPartialAddresses?: Maybe<Scalars['Boolean']> +} + +/** Return type for `checkoutAttributesUpdateV2` mutation. */ +export type CheckoutAttributesUpdateV2Payload = { + __typename?: 'CheckoutAttributesUpdateV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteFree` mutation. */ +export type CheckoutCompleteFreePayload = { + __typename?: 'CheckoutCompleteFreePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteWithCreditCard` mutation. */ +export type CheckoutCompleteWithCreditCardPayload = { + __typename?: 'CheckoutCompleteWithCreditCardPayload' + /** The checkout on which the payment was applied. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** A representation of the attempted payment. */ + payment?: Maybe<Payment> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteWithCreditCardV2` mutation. */ +export type CheckoutCompleteWithCreditCardV2Payload = { + __typename?: 'CheckoutCompleteWithCreditCardV2Payload' + /** The checkout on which the payment was applied. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** A representation of the attempted payment. */ + payment?: Maybe<Payment> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteWithTokenizedPayment` mutation. */ +export type CheckoutCompleteWithTokenizedPaymentPayload = { + __typename?: 'CheckoutCompleteWithTokenizedPaymentPayload' + /** The checkout on which the payment was applied. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** A representation of the attempted payment. */ + payment?: Maybe<Payment> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteWithTokenizedPaymentV2` mutation. */ +export type CheckoutCompleteWithTokenizedPaymentV2Payload = { + __typename?: 'CheckoutCompleteWithTokenizedPaymentV2Payload' + /** The checkout on which the payment was applied. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** A representation of the attempted payment. */ + payment?: Maybe<Payment> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCompleteWithTokenizedPaymentV3` mutation. */ +export type CheckoutCompleteWithTokenizedPaymentV3Payload = { + __typename?: 'CheckoutCompleteWithTokenizedPaymentV3Payload' + /** The checkout on which the payment was applied. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** A representation of the attempted payment. */ + payment?: Maybe<Payment> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Specifies the fields required to create a checkout. */ +export type CheckoutCreateInput = { + /** The email with which the customer wants to checkout. */ + email?: Maybe<Scalars['String']> + /** A list of line item objects, each one containing information about an item in the checkout. */ + lineItems?: Maybe<Array<CheckoutLineItemInput>> + /** The shipping address to where the line items will be shipped. */ + shippingAddress?: Maybe<MailingAddressInput> + /** The text of an optional note that a shop owner can attach to the checkout. */ + note?: Maybe<Scalars['String']> + /** A list of extra information that is added to the checkout. */ + customAttributes?: Maybe<Array<AttributeInput>> + /** + * Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + * The required attributes are city, province, and country. + * Full validation of addresses is still done at complete time. + */ + allowPartialAddresses?: Maybe<Scalars['Boolean']> + /** + * The three-letter currency code of one of the shop's enabled presentment currencies. + * Including this field creates a checkout in the specified currency. By default, new + * checkouts are created in the shop's primary currency. + */ + presentmentCurrencyCode?: Maybe<CurrencyCode> +} + +/** Return type for `checkoutCreate` mutation. */ +export type CheckoutCreatePayload = { + __typename?: 'CheckoutCreatePayload' + /** The new checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCustomerAssociate` mutation. */ +export type CheckoutCustomerAssociatePayload = { + __typename?: 'CheckoutCustomerAssociatePayload' + /** The updated checkout object. */ + checkout: Checkout + /** The associated customer object. */ + customer?: Maybe<Customer> + /** List of errors that occurred executing the mutation. */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCustomerAssociateV2` mutation. */ +export type CheckoutCustomerAssociateV2Payload = { + __typename?: 'CheckoutCustomerAssociateV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** The associated customer object. */ + customer?: Maybe<Customer> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCustomerDisassociate` mutation. */ +export type CheckoutCustomerDisassociatePayload = { + __typename?: 'CheckoutCustomerDisassociatePayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutCustomerDisassociateV2` mutation. */ +export type CheckoutCustomerDisassociateV2Payload = { + __typename?: 'CheckoutCustomerDisassociateV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutDiscountCodeApply` mutation. */ +export type CheckoutDiscountCodeApplyPayload = { + __typename?: 'CheckoutDiscountCodeApplyPayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutDiscountCodeApplyV2` mutation. */ +export type CheckoutDiscountCodeApplyV2Payload = { + __typename?: 'CheckoutDiscountCodeApplyV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutDiscountCodeRemove` mutation. */ +export type CheckoutDiscountCodeRemovePayload = { + __typename?: 'CheckoutDiscountCodeRemovePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutEmailUpdate` mutation. */ +export type CheckoutEmailUpdatePayload = { + __typename?: 'CheckoutEmailUpdatePayload' + /** The checkout object with the updated email. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutEmailUpdateV2` mutation. */ +export type CheckoutEmailUpdateV2Payload = { + __typename?: 'CheckoutEmailUpdateV2Payload' + /** The checkout object with the updated email. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Possible error codes that could be returned by CheckoutUserError. */ +export enum CheckoutErrorCode { + /** Input value is blank. */ + Blank = 'BLANK', + /** Input value is invalid. */ + Invalid = 'INVALID', + /** Input value is too long. */ + TooLong = 'TOO_LONG', + /** Input value is not present. */ + Present = 'PRESENT', + /** Input value should be less than maximum allowed value. */ + LessThan = 'LESS_THAN', + /** Input value should be greater than or equal to minimum allowed value. */ + GreaterThanOrEqualTo = 'GREATER_THAN_OR_EQUAL_TO', + /** Input value should be less or equal to maximum allowed value. */ + LessThanOrEqualTo = 'LESS_THAN_OR_EQUAL_TO', + /** Checkout is already completed. */ + AlreadyCompleted = 'ALREADY_COMPLETED', + /** Checkout is locked. */ + Locked = 'LOCKED', + /** Input value is not supported. */ + NotSupported = 'NOT_SUPPORTED', + /** Input email contains an invalid domain name. */ + BadDomain = 'BAD_DOMAIN', + /** Input Zip is invalid for country provided. */ + InvalidForCountry = 'INVALID_FOR_COUNTRY', + /** Input Zip is invalid for country and province provided. */ + InvalidForCountryAndProvince = 'INVALID_FOR_COUNTRY_AND_PROVINCE', + /** Invalid state in country. */ + InvalidStateInCountry = 'INVALID_STATE_IN_COUNTRY', + /** Invalid province in country. */ + InvalidProvinceInCountry = 'INVALID_PROVINCE_IN_COUNTRY', + /** Invalid region in country. */ + InvalidRegionInCountry = 'INVALID_REGION_IN_COUNTRY', + /** Shipping rate expired. */ + ShippingRateExpired = 'SHIPPING_RATE_EXPIRED', + /** Gift card cannot be applied to a checkout that contains a gift card. */ + GiftCardUnusable = 'GIFT_CARD_UNUSABLE', + /** Gift card is disabled. */ + GiftCardDisabled = 'GIFT_CARD_DISABLED', + /** Gift card code is invalid. */ + GiftCardCodeInvalid = 'GIFT_CARD_CODE_INVALID', + /** Gift card has already been applied. */ + GiftCardAlreadyApplied = 'GIFT_CARD_ALREADY_APPLIED', + /** Gift card currency does not match checkout currency. */ + GiftCardCurrencyMismatch = 'GIFT_CARD_CURRENCY_MISMATCH', + /** Gift card is expired. */ + GiftCardExpired = 'GIFT_CARD_EXPIRED', + /** Gift card has no funds left. */ + GiftCardDepleted = 'GIFT_CARD_DEPLETED', + /** Gift card was not found. */ + GiftCardNotFound = 'GIFT_CARD_NOT_FOUND', + /** Cart does not meet discount requirements notice. */ + CartDoesNotMeetDiscountRequirementsNotice = 'CART_DOES_NOT_MEET_DISCOUNT_REQUIREMENTS_NOTICE', + /** Discount expired. */ + DiscountExpired = 'DISCOUNT_EXPIRED', + /** Discount disabled. */ + DiscountDisabled = 'DISCOUNT_DISABLED', + /** Discount limit reached. */ + DiscountLimitReached = 'DISCOUNT_LIMIT_REACHED', + /** Discount not found. */ + DiscountNotFound = 'DISCOUNT_NOT_FOUND', + /** Customer already used once per customer discount notice. */ + CustomerAlreadyUsedOncePerCustomerDiscountNotice = 'CUSTOMER_ALREADY_USED_ONCE_PER_CUSTOMER_DISCOUNT_NOTICE', + /** Checkout is already completed. */ + Empty = 'EMPTY', + /** Not enough in stock. */ + NotEnoughInStock = 'NOT_ENOUGH_IN_STOCK', + /** Missing payment input. */ + MissingPaymentInput = 'MISSING_PAYMENT_INPUT', + /** The amount of the payment does not match the value to be paid. */ + TotalPriceMismatch = 'TOTAL_PRICE_MISMATCH', + /** Line item was not found in checkout. */ + LineItemNotFound = 'LINE_ITEM_NOT_FOUND', + /** Unable to apply discount. */ + UnableToApply = 'UNABLE_TO_APPLY', + /** Discount already applied. */ + DiscountAlreadyApplied = 'DISCOUNT_ALREADY_APPLIED', +} + +/** Return type for `checkoutGiftCardApply` mutation. */ +export type CheckoutGiftCardApplyPayload = { + __typename?: 'CheckoutGiftCardApplyPayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutGiftCardRemove` mutation. */ +export type CheckoutGiftCardRemovePayload = { + __typename?: 'CheckoutGiftCardRemovePayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutGiftCardRemoveV2` mutation. */ +export type CheckoutGiftCardRemoveV2Payload = { + __typename?: 'CheckoutGiftCardRemoveV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutGiftCardsAppend` mutation. */ +export type CheckoutGiftCardsAppendPayload = { + __typename?: 'CheckoutGiftCardsAppendPayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** A single line item in the checkout, grouped by variant and attributes. */ +export type CheckoutLineItem = Node & { + __typename?: 'CheckoutLineItem' + /** Extra information in the form of an array of Key-Value pairs about the line item. */ + customAttributes: Array<Attribute> + /** The discounts that have been allocated onto the checkout line item by discount applications. */ + discountAllocations: Array<DiscountAllocation> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The quantity of the line item. */ + quantity: Scalars['Int'] + /** Title of the line item. Defaults to the product's title. */ + title: Scalars['String'] + /** Unit price of the line item. */ + unitPrice?: Maybe<MoneyV2> + /** Product variant of the line item. */ + variant?: Maybe<ProductVariant> +} + +/** An auto-generated type for paginating through multiple CheckoutLineItems. */ +export type CheckoutLineItemConnection = { + __typename?: 'CheckoutLineItemConnection' + /** A list of edges. */ + edges: Array<CheckoutLineItemEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one CheckoutLineItem and a cursor during pagination. */ +export type CheckoutLineItemEdge = { + __typename?: 'CheckoutLineItemEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of CheckoutLineItemEdge. */ + node: CheckoutLineItem +} + +/** Specifies the input fields to create a line item on a checkout. */ +export type CheckoutLineItemInput = { + /** Extra information in the form of an array of Key-Value pairs about the line item. */ + customAttributes?: Maybe<Array<AttributeInput>> + /** The quantity of the line item. */ + quantity: Scalars['Int'] + /** The identifier of the product variant for the line item. */ + variantId: Scalars['ID'] +} + +/** Specifies the input fields to update a line item on the checkout. */ +export type CheckoutLineItemUpdateInput = { + /** The identifier of the line item. */ + id?: Maybe<Scalars['ID']> + /** The variant identifier of the line item. */ + variantId?: Maybe<Scalars['ID']> + /** The quantity of the line item. */ + quantity?: Maybe<Scalars['Int']> + /** Extra information in the form of an array of Key-Value pairs about the line item. */ + customAttributes?: Maybe<Array<AttributeInput>> +} + +/** Return type for `checkoutLineItemsAdd` mutation. */ +export type CheckoutLineItemsAddPayload = { + __typename?: 'CheckoutLineItemsAddPayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutLineItemsRemove` mutation. */ +export type CheckoutLineItemsRemovePayload = { + __typename?: 'CheckoutLineItemsRemovePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutLineItemsReplace` mutation. */ +export type CheckoutLineItemsReplacePayload = { + __typename?: 'CheckoutLineItemsReplacePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + userErrors: Array<CheckoutUserError> +} + +/** Return type for `checkoutLineItemsUpdate` mutation. */ +export type CheckoutLineItemsUpdatePayload = { + __typename?: 'CheckoutLineItemsUpdatePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutShippingAddressUpdate` mutation. */ +export type CheckoutShippingAddressUpdatePayload = { + __typename?: 'CheckoutShippingAddressUpdatePayload' + /** The updated checkout object. */ + checkout: Checkout + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutShippingAddressUpdateV2` mutation. */ +export type CheckoutShippingAddressUpdateV2Payload = { + __typename?: 'CheckoutShippingAddressUpdateV2Payload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `checkoutShippingLineUpdate` mutation. */ +export type CheckoutShippingLineUpdatePayload = { + __typename?: 'CheckoutShippingLineUpdatePayload' + /** The updated checkout object. */ + checkout?: Maybe<Checkout> + /** List of errors that occurred executing the mutation. */ + checkoutUserErrors: Array<CheckoutUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `checkoutUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Represents an error that happens during execution of a checkout mutation. */ +export type CheckoutUserError = DisplayableError & { + __typename?: 'CheckoutUserError' + /** Error code to uniquely identify the error. */ + code?: Maybe<CheckoutErrorCode> + /** Path to the input field which caused the error. */ + field?: Maybe<Array<Scalars['String']>> + /** The error message. */ + message: Scalars['String'] +} + +/** A collection represents a grouping of products that a shop owner can create to organize them or make their shops easier to browse. */ +export type Collection = Node & { + __typename?: 'Collection' + /** Stripped description of the collection, single line with HTML tags removed. */ + description: Scalars['String'] + /** The description of the collection, complete with HTML formatting. */ + descriptionHtml: Scalars['HTML'] + /** + * A human-friendly unique string for the collection automatically generated from its title. + * Limit of 255 characters. + */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** Image associated with the collection. */ + image?: Maybe<Image> + /** List of products in the collection. */ + products: ProductConnection + /** The collection’s name. Limit of 255 characters. */ + title: Scalars['String'] + /** The date and time when the collection was last modified. */ + updatedAt: Scalars['DateTime'] +} + +/** A collection represents a grouping of products that a shop owner can create to organize them or make their shops easier to browse. */ +export type CollectionDescriptionArgs = { + truncateAt?: Maybe<Scalars['Int']> +} + +/** A collection represents a grouping of products that a shop owner can create to organize them or make their shops easier to browse. */ +export type CollectionImageArgs = { + maxWidth?: Maybe<Scalars['Int']> + maxHeight?: Maybe<Scalars['Int']> + crop?: Maybe<CropRegion> + scale?: Maybe<Scalars['Int']> +} + +/** A collection represents a grouping of products that a shop owner can create to organize them or make their shops easier to browse. */ +export type CollectionProductsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductCollectionSortKeys> +} + +/** An auto-generated type for paginating through multiple Collections. */ +export type CollectionConnection = { + __typename?: 'CollectionConnection' + /** A list of edges. */ + edges: Array<CollectionEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Collection and a cursor during pagination. */ +export type CollectionEdge = { + __typename?: 'CollectionEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of CollectionEdge. */ + node: Collection +} + +/** The set of valid sort keys for the Collection query. */ +export enum CollectionSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `updated_at` value. */ + UpdatedAt = 'UPDATED_AT', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** A comment on an article. */ +export type Comment = Node & { + __typename?: 'Comment' + /** The comment’s author. */ + author: CommentAuthor + /** Stripped content of the comment, single line with HTML tags removed. */ + content: Scalars['String'] + /** The content of the comment, complete with HTML formatting. */ + contentHtml: Scalars['HTML'] + /** Globally unique identifier. */ + id: Scalars['ID'] +} + +/** A comment on an article. */ +export type CommentContentArgs = { + truncateAt?: Maybe<Scalars['Int']> +} + +/** The author of a comment. */ +export type CommentAuthor = { + __typename?: 'CommentAuthor' + /** The author's email. */ + email: Scalars['String'] + /** The author’s name. */ + name: Scalars['String'] +} + +/** An auto-generated type for paginating through multiple Comments. */ +export type CommentConnection = { + __typename?: 'CommentConnection' + /** A list of edges. */ + edges: Array<CommentEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Comment and a cursor during pagination. */ +export type CommentEdge = { + __typename?: 'CommentEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of CommentEdge. */ + node: Comment +} + +/** ISO 3166-1 alpha-2 country codes with some differences. */ +export enum CountryCode { + /** Afghanistan. */ + Af = 'AF', + /** Åland Islands. */ + Ax = 'AX', + /** Albania. */ + Al = 'AL', + /** Algeria. */ + Dz = 'DZ', + /** Andorra. */ + Ad = 'AD', + /** Angola. */ + Ao = 'AO', + /** Anguilla. */ + Ai = 'AI', + /** Antigua & Barbuda. */ + Ag = 'AG', + /** Argentina. */ + Ar = 'AR', + /** Armenia. */ + Am = 'AM', + /** Aruba. */ + Aw = 'AW', + /** Australia. */ + Au = 'AU', + /** Austria. */ + At = 'AT', + /** Azerbaijan. */ + Az = 'AZ', + /** Bahamas. */ + Bs = 'BS', + /** Bahrain. */ + Bh = 'BH', + /** Bangladesh. */ + Bd = 'BD', + /** Barbados. */ + Bb = 'BB', + /** Belarus. */ + By = 'BY', + /** Belgium. */ + Be = 'BE', + /** Belize. */ + Bz = 'BZ', + /** Benin. */ + Bj = 'BJ', + /** Bermuda. */ + Bm = 'BM', + /** Bhutan. */ + Bt = 'BT', + /** Bolivia. */ + Bo = 'BO', + /** Bosnia & Herzegovina. */ + Ba = 'BA', + /** Botswana. */ + Bw = 'BW', + /** Bouvet Island. */ + Bv = 'BV', + /** Brazil. */ + Br = 'BR', + /** British Indian Ocean Territory. */ + Io = 'IO', + /** Brunei. */ + Bn = 'BN', + /** Bulgaria. */ + Bg = 'BG', + /** Burkina Faso. */ + Bf = 'BF', + /** Burundi. */ + Bi = 'BI', + /** Cambodia. */ + Kh = 'KH', + /** Canada. */ + Ca = 'CA', + /** Cape Verde. */ + Cv = 'CV', + /** Caribbean Netherlands. */ + Bq = 'BQ', + /** Cayman Islands. */ + Ky = 'KY', + /** Central African Republic. */ + Cf = 'CF', + /** Chad. */ + Td = 'TD', + /** Chile. */ + Cl = 'CL', + /** China. */ + Cn = 'CN', + /** Christmas Island. */ + Cx = 'CX', + /** Cocos (Keeling) Islands. */ + Cc = 'CC', + /** Colombia. */ + Co = 'CO', + /** Comoros. */ + Km = 'KM', + /** Congo - Brazzaville. */ + Cg = 'CG', + /** Congo - Kinshasa. */ + Cd = 'CD', + /** Cook Islands. */ + Ck = 'CK', + /** Costa Rica. */ + Cr = 'CR', + /** Croatia. */ + Hr = 'HR', + /** Cuba. */ + Cu = 'CU', + /** Curaçao. */ + Cw = 'CW', + /** Cyprus. */ + Cy = 'CY', + /** Czechia. */ + Cz = 'CZ', + /** Côte d’Ivoire. */ + Ci = 'CI', + /** Denmark. */ + Dk = 'DK', + /** Djibouti. */ + Dj = 'DJ', + /** Dominica. */ + Dm = 'DM', + /** Dominican Republic. */ + Do = 'DO', + /** Ecuador. */ + Ec = 'EC', + /** Egypt. */ + Eg = 'EG', + /** El Salvador. */ + Sv = 'SV', + /** Equatorial Guinea. */ + Gq = 'GQ', + /** Eritrea. */ + Er = 'ER', + /** Estonia. */ + Ee = 'EE', + /** Eswatini. */ + Sz = 'SZ', + /** Ethiopia. */ + Et = 'ET', + /** Falkland Islands. */ + Fk = 'FK', + /** Faroe Islands. */ + Fo = 'FO', + /** Fiji. */ + Fj = 'FJ', + /** Finland. */ + Fi = 'FI', + /** France. */ + Fr = 'FR', + /** French Guiana. */ + Gf = 'GF', + /** French Polynesia. */ + Pf = 'PF', + /** French Southern Territories. */ + Tf = 'TF', + /** Gabon. */ + Ga = 'GA', + /** Gambia. */ + Gm = 'GM', + /** Georgia. */ + Ge = 'GE', + /** Germany. */ + De = 'DE', + /** Ghana. */ + Gh = 'GH', + /** Gibraltar. */ + Gi = 'GI', + /** Greece. */ + Gr = 'GR', + /** Greenland. */ + Gl = 'GL', + /** Grenada. */ + Gd = 'GD', + /** Guadeloupe. */ + Gp = 'GP', + /** Guatemala. */ + Gt = 'GT', + /** Guernsey. */ + Gg = 'GG', + /** Guinea. */ + Gn = 'GN', + /** Guinea-Bissau. */ + Gw = 'GW', + /** Guyana. */ + Gy = 'GY', + /** Haiti. */ + Ht = 'HT', + /** Heard & McDonald Islands. */ + Hm = 'HM', + /** Vatican City. */ + Va = 'VA', + /** Honduras. */ + Hn = 'HN', + /** Hong Kong SAR. */ + Hk = 'HK', + /** Hungary. */ + Hu = 'HU', + /** Iceland. */ + Is = 'IS', + /** India. */ + In = 'IN', + /** Indonesia. */ + Id = 'ID', + /** Iran. */ + Ir = 'IR', + /** Iraq. */ + Iq = 'IQ', + /** Ireland. */ + Ie = 'IE', + /** Isle of Man. */ + Im = 'IM', + /** Israel. */ + Il = 'IL', + /** Italy. */ + It = 'IT', + /** Jamaica. */ + Jm = 'JM', + /** Japan. */ + Jp = 'JP', + /** Jersey. */ + Je = 'JE', + /** Jordan. */ + Jo = 'JO', + /** Kazakhstan. */ + Kz = 'KZ', + /** Kenya. */ + Ke = 'KE', + /** Kiribati. */ + Ki = 'KI', + /** North Korea. */ + Kp = 'KP', + /** Kosovo. */ + Xk = 'XK', + /** Kuwait. */ + Kw = 'KW', + /** Kyrgyzstan. */ + Kg = 'KG', + /** Laos. */ + La = 'LA', + /** Latvia. */ + Lv = 'LV', + /** Lebanon. */ + Lb = 'LB', + /** Lesotho. */ + Ls = 'LS', + /** Liberia. */ + Lr = 'LR', + /** Libya. */ + Ly = 'LY', + /** Liechtenstein. */ + Li = 'LI', + /** Lithuania. */ + Lt = 'LT', + /** Luxembourg. */ + Lu = 'LU', + /** Macao SAR. */ + Mo = 'MO', + /** Madagascar. */ + Mg = 'MG', + /** Malawi. */ + Mw = 'MW', + /** Malaysia. */ + My = 'MY', + /** Maldives. */ + Mv = 'MV', + /** Mali. */ + Ml = 'ML', + /** Malta. */ + Mt = 'MT', + /** Martinique. */ + Mq = 'MQ', + /** Mauritania. */ + Mr = 'MR', + /** Mauritius. */ + Mu = 'MU', + /** Mayotte. */ + Yt = 'YT', + /** Mexico. */ + Mx = 'MX', + /** Moldova. */ + Md = 'MD', + /** Monaco. */ + Mc = 'MC', + /** Mongolia. */ + Mn = 'MN', + /** Montenegro. */ + Me = 'ME', + /** Montserrat. */ + Ms = 'MS', + /** Morocco. */ + Ma = 'MA', + /** Mozambique. */ + Mz = 'MZ', + /** Myanmar (Burma). */ + Mm = 'MM', + /** Namibia. */ + Na = 'NA', + /** Nauru. */ + Nr = 'NR', + /** Nepal. */ + Np = 'NP', + /** Netherlands. */ + Nl = 'NL', + /** Netherlands Antilles. */ + An = 'AN', + /** New Caledonia. */ + Nc = 'NC', + /** New Zealand. */ + Nz = 'NZ', + /** Nicaragua. */ + Ni = 'NI', + /** Niger. */ + Ne = 'NE', + /** Nigeria. */ + Ng = 'NG', + /** Niue. */ + Nu = 'NU', + /** Norfolk Island. */ + Nf = 'NF', + /** North Macedonia. */ + Mk = 'MK', + /** Norway. */ + No = 'NO', + /** Oman. */ + Om = 'OM', + /** Pakistan. */ + Pk = 'PK', + /** Palestinian Territories. */ + Ps = 'PS', + /** Panama. */ + Pa = 'PA', + /** Papua New Guinea. */ + Pg = 'PG', + /** Paraguay. */ + Py = 'PY', + /** Peru. */ + Pe = 'PE', + /** Philippines. */ + Ph = 'PH', + /** Pitcairn Islands. */ + Pn = 'PN', + /** Poland. */ + Pl = 'PL', + /** Portugal. */ + Pt = 'PT', + /** Qatar. */ + Qa = 'QA', + /** Cameroon. */ + Cm = 'CM', + /** Réunion. */ + Re = 'RE', + /** Romania. */ + Ro = 'RO', + /** Russia. */ + Ru = 'RU', + /** Rwanda. */ + Rw = 'RW', + /** St. Barthélemy. */ + Bl = 'BL', + /** St. Helena. */ + Sh = 'SH', + /** St. Kitts & Nevis. */ + Kn = 'KN', + /** St. Lucia. */ + Lc = 'LC', + /** St. Martin. */ + Mf = 'MF', + /** St. Pierre & Miquelon. */ + Pm = 'PM', + /** Samoa. */ + Ws = 'WS', + /** San Marino. */ + Sm = 'SM', + /** São Tomé & Príncipe. */ + St = 'ST', + /** Saudi Arabia. */ + Sa = 'SA', + /** Senegal. */ + Sn = 'SN', + /** Serbia. */ + Rs = 'RS', + /** Seychelles. */ + Sc = 'SC', + /** Sierra Leone. */ + Sl = 'SL', + /** Singapore. */ + Sg = 'SG', + /** Sint Maarten. */ + Sx = 'SX', + /** Slovakia. */ + Sk = 'SK', + /** Slovenia. */ + Si = 'SI', + /** Solomon Islands. */ + Sb = 'SB', + /** Somalia. */ + So = 'SO', + /** South Africa. */ + Za = 'ZA', + /** South Georgia & South Sandwich Islands. */ + Gs = 'GS', + /** South Korea. */ + Kr = 'KR', + /** South Sudan. */ + Ss = 'SS', + /** Spain. */ + Es = 'ES', + /** Sri Lanka. */ + Lk = 'LK', + /** St. Vincent & Grenadines. */ + Vc = 'VC', + /** Sudan. */ + Sd = 'SD', + /** Suriname. */ + Sr = 'SR', + /** Svalbard & Jan Mayen. */ + Sj = 'SJ', + /** Sweden. */ + Se = 'SE', + /** Switzerland. */ + Ch = 'CH', + /** Syria. */ + Sy = 'SY', + /** Taiwan. */ + Tw = 'TW', + /** Tajikistan. */ + Tj = 'TJ', + /** Tanzania. */ + Tz = 'TZ', + /** Thailand. */ + Th = 'TH', + /** Timor-Leste. */ + Tl = 'TL', + /** Togo. */ + Tg = 'TG', + /** Tokelau. */ + Tk = 'TK', + /** Tonga. */ + To = 'TO', + /** Trinidad & Tobago. */ + Tt = 'TT', + /** Tunisia. */ + Tn = 'TN', + /** Turkey. */ + Tr = 'TR', + /** Turkmenistan. */ + Tm = 'TM', + /** Turks & Caicos Islands. */ + Tc = 'TC', + /** Tuvalu. */ + Tv = 'TV', + /** Uganda. */ + Ug = 'UG', + /** Ukraine. */ + Ua = 'UA', + /** United Arab Emirates. */ + Ae = 'AE', + /** United Kingdom. */ + Gb = 'GB', + /** United States. */ + Us = 'US', + /** U.S. Outlying Islands. */ + Um = 'UM', + /** Uruguay. */ + Uy = 'UY', + /** Uzbekistan. */ + Uz = 'UZ', + /** Vanuatu. */ + Vu = 'VU', + /** Venezuela. */ + Ve = 'VE', + /** Vietnam. */ + Vn = 'VN', + /** British Virgin Islands. */ + Vg = 'VG', + /** Wallis & Futuna. */ + Wf = 'WF', + /** Western Sahara. */ + Eh = 'EH', + /** Yemen. */ + Ye = 'YE', + /** Zambia. */ + Zm = 'ZM', + /** Zimbabwe. */ + Zw = 'ZW', +} + +/** Credit card information used for a payment. */ +export type CreditCard = { + __typename?: 'CreditCard' + /** The brand of the credit card. */ + brand?: Maybe<Scalars['String']> + /** The expiry month of the credit card. */ + expiryMonth?: Maybe<Scalars['Int']> + /** The expiry year of the credit card. */ + expiryYear?: Maybe<Scalars['Int']> + /** The credit card's BIN number. */ + firstDigits?: Maybe<Scalars['String']> + /** The first name of the card holder. */ + firstName?: Maybe<Scalars['String']> + /** The last 4 digits of the credit card. */ + lastDigits?: Maybe<Scalars['String']> + /** The last name of the card holder. */ + lastName?: Maybe<Scalars['String']> + /** The masked credit card number with only the last 4 digits displayed. */ + maskedNumber?: Maybe<Scalars['String']> +} + +/** + * Specifies the fields required to complete a checkout with + * a Shopify vaulted credit card payment. + */ +export type CreditCardPaymentInput = { + /** The amount of the payment. */ + amount: Scalars['Money'] + /** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */ + idempotencyKey: Scalars['String'] + /** The billing address for the payment. */ + billingAddress: MailingAddressInput + /** The ID returned by Shopify's Card Vault. */ + vaultId: Scalars['String'] + /** Executes the payment in test mode if possible. Defaults to `false`. */ + test?: Maybe<Scalars['Boolean']> +} + +/** + * Specifies the fields required to complete a checkout with + * a Shopify vaulted credit card payment. + */ +export type CreditCardPaymentInputV2 = { + /** The amount and currency of the payment. */ + paymentAmount: MoneyInput + /** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */ + idempotencyKey: Scalars['String'] + /** The billing address for the payment. */ + billingAddress: MailingAddressInput + /** The ID returned by Shopify's Card Vault. */ + vaultId: Scalars['String'] + /** Executes the payment in test mode if possible. Defaults to `false`. */ + test?: Maybe<Scalars['Boolean']> +} + +/** The part of the image that should remain after cropping. */ +export enum CropRegion { + /** Keep the center of the image. */ + Center = 'CENTER', + /** Keep the top of the image. */ + Top = 'TOP', + /** Keep the bottom of the image. */ + Bottom = 'BOTTOM', + /** Keep the left of the image. */ + Left = 'LEFT', + /** Keep the right of the image. */ + Right = 'RIGHT', +} + +/** Currency codes. */ +export enum CurrencyCode { + /** United States Dollars (USD). */ + Usd = 'USD', + /** Euro (EUR). */ + Eur = 'EUR', + /** United Kingdom Pounds (GBP). */ + Gbp = 'GBP', + /** Canadian Dollars (CAD). */ + Cad = 'CAD', + /** Afghan Afghani (AFN). */ + Afn = 'AFN', + /** Albanian Lek (ALL). */ + All = 'ALL', + /** Algerian Dinar (DZD). */ + Dzd = 'DZD', + /** Angolan Kwanza (AOA). */ + Aoa = 'AOA', + /** Argentine Pesos (ARS). */ + Ars = 'ARS', + /** Armenian Dram (AMD). */ + Amd = 'AMD', + /** Aruban Florin (AWG). */ + Awg = 'AWG', + /** Australian Dollars (AUD). */ + Aud = 'AUD', + /** Barbadian Dollar (BBD). */ + Bbd = 'BBD', + /** Azerbaijani Manat (AZN). */ + Azn = 'AZN', + /** Bangladesh Taka (BDT). */ + Bdt = 'BDT', + /** Bahamian Dollar (BSD). */ + Bsd = 'BSD', + /** Bahraini Dinar (BHD). */ + Bhd = 'BHD', + /** Burundian Franc (BIF). */ + Bif = 'BIF', + /** Belarusian Ruble (BYN). */ + Byn = 'BYN', + /** Belarusian Ruble (BYR). */ + Byr = 'BYR', + /** Belize Dollar (BZD). */ + Bzd = 'BZD', + /** Bermudian Dollar (BMD). */ + Bmd = 'BMD', + /** Bhutanese Ngultrum (BTN). */ + Btn = 'BTN', + /** Bosnia and Herzegovina Convertible Mark (BAM). */ + Bam = 'BAM', + /** Brazilian Real (BRL). */ + Brl = 'BRL', + /** Bolivian Boliviano (BOB). */ + Bob = 'BOB', + /** Botswana Pula (BWP). */ + Bwp = 'BWP', + /** Brunei Dollar (BND). */ + Bnd = 'BND', + /** Bulgarian Lev (BGN). */ + Bgn = 'BGN', + /** Burmese Kyat (MMK). */ + Mmk = 'MMK', + /** Cambodian Riel. */ + Khr = 'KHR', + /** Cape Verdean escudo (CVE). */ + Cve = 'CVE', + /** Cayman Dollars (KYD). */ + Kyd = 'KYD', + /** Central African CFA Franc (XAF). */ + Xaf = 'XAF', + /** Chilean Peso (CLP). */ + Clp = 'CLP', + /** Chinese Yuan Renminbi (CNY). */ + Cny = 'CNY', + /** Colombian Peso (COP). */ + Cop = 'COP', + /** Comorian Franc (KMF). */ + Kmf = 'KMF', + /** Congolese franc (CDF). */ + Cdf = 'CDF', + /** Costa Rican Colones (CRC). */ + Crc = 'CRC', + /** Croatian Kuna (HRK). */ + Hrk = 'HRK', + /** Czech Koruny (CZK). */ + Czk = 'CZK', + /** Danish Kroner (DKK). */ + Dkk = 'DKK', + /** Djiboutian Franc (DJF). */ + Djf = 'DJF', + /** Dominican Peso (DOP). */ + Dop = 'DOP', + /** East Caribbean Dollar (XCD). */ + Xcd = 'XCD', + /** Egyptian Pound (EGP). */ + Egp = 'EGP', + /** Eritrean Nakfa (ERN). */ + Ern = 'ERN', + /** Ethiopian Birr (ETB). */ + Etb = 'ETB', + /** Falkland Islands Pounds (FKP). */ + Fkp = 'FKP', + /** CFP Franc (XPF). */ + Xpf = 'XPF', + /** Fijian Dollars (FJD). */ + Fjd = 'FJD', + /** Gibraltar Pounds (GIP). */ + Gip = 'GIP', + /** Gambian Dalasi (GMD). */ + Gmd = 'GMD', + /** Ghanaian Cedi (GHS). */ + Ghs = 'GHS', + /** Guatemalan Quetzal (GTQ). */ + Gtq = 'GTQ', + /** Guyanese Dollar (GYD). */ + Gyd = 'GYD', + /** Georgian Lari (GEL). */ + Gel = 'GEL', + /** Guinean Franc (GNF). */ + Gnf = 'GNF', + /** Haitian Gourde (HTG). */ + Htg = 'HTG', + /** Honduran Lempira (HNL). */ + Hnl = 'HNL', + /** Hong Kong Dollars (HKD). */ + Hkd = 'HKD', + /** Hungarian Forint (HUF). */ + Huf = 'HUF', + /** Icelandic Kronur (ISK). */ + Isk = 'ISK', + /** Indian Rupees (INR). */ + Inr = 'INR', + /** Indonesian Rupiah (IDR). */ + Idr = 'IDR', + /** Israeli New Shekel (NIS). */ + Ils = 'ILS', + /** Iranian Rial (IRR). */ + Irr = 'IRR', + /** Iraqi Dinar (IQD). */ + Iqd = 'IQD', + /** Jamaican Dollars (JMD). */ + Jmd = 'JMD', + /** Japanese Yen (JPY). */ + Jpy = 'JPY', + /** Jersey Pound. */ + Jep = 'JEP', + /** Jordanian Dinar (JOD). */ + Jod = 'JOD', + /** Kazakhstani Tenge (KZT). */ + Kzt = 'KZT', + /** Kenyan Shilling (KES). */ + Kes = 'KES', + /** Kiribati Dollar (KID). */ + Kid = 'KID', + /** Kuwaiti Dinar (KWD). */ + Kwd = 'KWD', + /** Kyrgyzstani Som (KGS). */ + Kgs = 'KGS', + /** Laotian Kip (LAK). */ + Lak = 'LAK', + /** Latvian Lati (LVL). */ + Lvl = 'LVL', + /** Lebanese Pounds (LBP). */ + Lbp = 'LBP', + /** Lesotho Loti (LSL). */ + Lsl = 'LSL', + /** Liberian Dollar (LRD). */ + Lrd = 'LRD', + /** Libyan Dinar (LYD). */ + Lyd = 'LYD', + /** Lithuanian Litai (LTL). */ + Ltl = 'LTL', + /** Malagasy Ariary (MGA). */ + Mga = 'MGA', + /** Macedonia Denar (MKD). */ + Mkd = 'MKD', + /** Macanese Pataca (MOP). */ + Mop = 'MOP', + /** Malawian Kwacha (MWK). */ + Mwk = 'MWK', + /** Maldivian Rufiyaa (MVR). */ + Mvr = 'MVR', + /** Mauritanian Ouguiya (MRU). */ + Mru = 'MRU', + /** Mexican Pesos (MXN). */ + Mxn = 'MXN', + /** Malaysian Ringgits (MYR). */ + Myr = 'MYR', + /** Mauritian Rupee (MUR). */ + Mur = 'MUR', + /** Moldovan Leu (MDL). */ + Mdl = 'MDL', + /** Moroccan Dirham. */ + Mad = 'MAD', + /** Mongolian Tugrik. */ + Mnt = 'MNT', + /** Mozambican Metical. */ + Mzn = 'MZN', + /** Namibian Dollar. */ + Nad = 'NAD', + /** Nepalese Rupee (NPR). */ + Npr = 'NPR', + /** Netherlands Antillean Guilder. */ + Ang = 'ANG', + /** New Zealand Dollars (NZD). */ + Nzd = 'NZD', + /** Nicaraguan Córdoba (NIO). */ + Nio = 'NIO', + /** Nigerian Naira (NGN). */ + Ngn = 'NGN', + /** Norwegian Kroner (NOK). */ + Nok = 'NOK', + /** Omani Rial (OMR). */ + Omr = 'OMR', + /** Panamian Balboa (PAB). */ + Pab = 'PAB', + /** Pakistani Rupee (PKR). */ + Pkr = 'PKR', + /** Papua New Guinean Kina (PGK). */ + Pgk = 'PGK', + /** Paraguayan Guarani (PYG). */ + Pyg = 'PYG', + /** Peruvian Nuevo Sol (PEN). */ + Pen = 'PEN', + /** Philippine Peso (PHP). */ + Php = 'PHP', + /** Polish Zlotych (PLN). */ + Pln = 'PLN', + /** Qatari Rial (QAR). */ + Qar = 'QAR', + /** Romanian Lei (RON). */ + Ron = 'RON', + /** Russian Rubles (RUB). */ + Rub = 'RUB', + /** Rwandan Franc (RWF). */ + Rwf = 'RWF', + /** Samoan Tala (WST). */ + Wst = 'WST', + /** Saint Helena Pounds (SHP). */ + Shp = 'SHP', + /** Saudi Riyal (SAR). */ + Sar = 'SAR', + /** Sao Tome And Principe Dobra (STD). */ + Std = 'STD', + /** Serbian dinar (RSD). */ + Rsd = 'RSD', + /** Seychellois Rupee (SCR). */ + Scr = 'SCR', + /** Sierra Leonean Leone (SLL). */ + Sll = 'SLL', + /** Singapore Dollars (SGD). */ + Sgd = 'SGD', + /** Sudanese Pound (SDG). */ + Sdg = 'SDG', + /** Somali Shilling (SOS). */ + Sos = 'SOS', + /** Syrian Pound (SYP). */ + Syp = 'SYP', + /** South African Rand (ZAR). */ + Zar = 'ZAR', + /** South Korean Won (KRW). */ + Krw = 'KRW', + /** South Sudanese Pound (SSP). */ + Ssp = 'SSP', + /** Solomon Islands Dollar (SBD). */ + Sbd = 'SBD', + /** Sri Lankan Rupees (LKR). */ + Lkr = 'LKR', + /** Surinamese Dollar (SRD). */ + Srd = 'SRD', + /** Swazi Lilangeni (SZL). */ + Szl = 'SZL', + /** Swedish Kronor (SEK). */ + Sek = 'SEK', + /** Swiss Francs (CHF). */ + Chf = 'CHF', + /** Taiwan Dollars (TWD). */ + Twd = 'TWD', + /** Thai baht (THB). */ + Thb = 'THB', + /** Tajikistani Somoni (TJS). */ + Tjs = 'TJS', + /** Tanzanian Shilling (TZS). */ + Tzs = 'TZS', + /** Tongan Pa'anga (TOP). */ + Top = 'TOP', + /** Trinidad and Tobago Dollars (TTD). */ + Ttd = 'TTD', + /** Tunisian Dinar (TND). */ + Tnd = 'TND', + /** Turkish Lira (TRY). */ + Try = 'TRY', + /** Turkmenistani Manat (TMT). */ + Tmt = 'TMT', + /** Ugandan Shilling (UGX). */ + Ugx = 'UGX', + /** Ukrainian Hryvnia (UAH). */ + Uah = 'UAH', + /** United Arab Emirates Dirham (AED). */ + Aed = 'AED', + /** Uruguayan Pesos (UYU). */ + Uyu = 'UYU', + /** Uzbekistan som (UZS). */ + Uzs = 'UZS', + /** Vanuatu Vatu (VUV). */ + Vuv = 'VUV', + /** Venezuelan Bolivares (VEF). */ + Vef = 'VEF', + /** Venezuelan Bolivares (VES). */ + Ves = 'VES', + /** Vietnamese đồng (VND). */ + Vnd = 'VND', + /** West African CFA franc (XOF). */ + Xof = 'XOF', + /** Yemeni Rial (YER). */ + Yer = 'YER', + /** Zambian Kwacha (ZMW). */ + Zmw = 'ZMW', +} + +/** A customer represents a customer account with the shop. Customer accounts store contact information for the customer, saving logged-in customers the trouble of having to provide it at every checkout. */ +export type Customer = { + __typename?: 'Customer' + /** Indicates whether the customer has consented to be sent marketing material via email. */ + acceptsMarketing: Scalars['Boolean'] + /** A list of addresses for the customer. */ + addresses: MailingAddressConnection + /** The date and time when the customer was created. */ + createdAt: Scalars['DateTime'] + /** The customer’s default address. */ + defaultAddress?: Maybe<MailingAddress> + /** The customer’s name, email or phone number. */ + displayName: Scalars['String'] + /** The customer’s email address. */ + email?: Maybe<Scalars['String']> + /** The customer’s first name. */ + firstName?: Maybe<Scalars['String']> + /** A unique identifier for the customer. */ + id: Scalars['ID'] + /** The customer's most recently updated, incomplete checkout. */ + lastIncompleteCheckout?: Maybe<Checkout> + /** The customer’s last name. */ + lastName?: Maybe<Scalars['String']> + /** The orders associated with the customer. */ + orders: OrderConnection + /** The customer’s phone number. */ + phone?: Maybe<Scalars['String']> + /** + * A comma separated list of tags that have been added to the customer. + * Additional access scope required: unauthenticated_read_customer_tags. + */ + tags: Array<Scalars['String']> + /** The date and time when the customer information was updated. */ + updatedAt: Scalars['DateTime'] +} + +/** A customer represents a customer account with the shop. Customer accounts store contact information for the customer, saving logged-in customers the trouble of having to provide it at every checkout. */ +export type CustomerAddressesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** A customer represents a customer account with the shop. Customer accounts store contact information for the customer, saving logged-in customers the trouble of having to provide it at every checkout. */ +export type CustomerOrdersArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<OrderSortKeys> + query?: Maybe<Scalars['String']> +} + +/** A CustomerAccessToken represents the unique token required to make modifications to the customer object. */ +export type CustomerAccessToken = { + __typename?: 'CustomerAccessToken' + /** The customer’s access token. */ + accessToken: Scalars['String'] + /** The date and time when the customer access token expires. */ + expiresAt: Scalars['DateTime'] +} + +/** Specifies the input fields required to create a customer access token. */ +export type CustomerAccessTokenCreateInput = { + /** The email associated to the customer. */ + email: Scalars['String'] + /** The login password to be used by the customer. */ + password: Scalars['String'] +} + +/** Return type for `customerAccessTokenCreate` mutation. */ +export type CustomerAccessTokenCreatePayload = { + __typename?: 'CustomerAccessTokenCreatePayload' + /** The newly created customer access token object. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerAccessTokenCreateWithMultipass` mutation. */ +export type CustomerAccessTokenCreateWithMultipassPayload = { + __typename?: 'CustomerAccessTokenCreateWithMultipassPayload' + /** An access token object associated with the customer. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> +} + +/** Return type for `customerAccessTokenDelete` mutation. */ +export type CustomerAccessTokenDeletePayload = { + __typename?: 'CustomerAccessTokenDeletePayload' + /** The destroyed access token. */ + deletedAccessToken?: Maybe<Scalars['String']> + /** ID of the destroyed customer access token. */ + deletedCustomerAccessTokenId?: Maybe<Scalars['String']> + /** List of errors that occurred executing the mutation. */ + userErrors: Array<UserError> +} + +/** Return type for `customerAccessTokenRenew` mutation. */ +export type CustomerAccessTokenRenewPayload = { + __typename?: 'CustomerAccessTokenRenewPayload' + /** The renewed customer access token object. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + userErrors: Array<UserError> +} + +/** Return type for `customerActivateByUrl` mutation. */ +export type CustomerActivateByUrlPayload = { + __typename?: 'CustomerActivateByUrlPayload' + /** The customer that was activated. */ + customer?: Maybe<Customer> + /** A new customer access token for the customer. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> +} + +/** Specifies the input fields required to activate a customer. */ +export type CustomerActivateInput = { + /** The activation token required to activate the customer. */ + activationToken: Scalars['String'] + /** New password that will be set during activation. */ + password: Scalars['String'] +} + +/** Return type for `customerActivate` mutation. */ +export type CustomerActivatePayload = { + __typename?: 'CustomerActivatePayload' + /** The customer object. */ + customer?: Maybe<Customer> + /** A newly created customer access token object for the customer. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerAddressCreate` mutation. */ +export type CustomerAddressCreatePayload = { + __typename?: 'CustomerAddressCreatePayload' + /** The new customer address object. */ + customerAddress?: Maybe<MailingAddress> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerAddressDelete` mutation. */ +export type CustomerAddressDeletePayload = { + __typename?: 'CustomerAddressDeletePayload' + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** ID of the deleted customer address. */ + deletedCustomerAddressId?: Maybe<Scalars['String']> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerAddressUpdate` mutation. */ +export type CustomerAddressUpdatePayload = { + __typename?: 'CustomerAddressUpdatePayload' + /** The customer’s updated mailing address. */ + customerAddress?: Maybe<MailingAddress> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Specifies the fields required to create a new customer. */ +export type CustomerCreateInput = { + /** The customer’s first name. */ + firstName?: Maybe<Scalars['String']> + /** The customer’s last name. */ + lastName?: Maybe<Scalars['String']> + /** The customer’s email. */ + email: Scalars['String'] + /** + * A unique phone number for the customer. + * + * Formatted using E.164 standard. For example, _+16135551111_. + */ + phone?: Maybe<Scalars['String']> + /** The login password used by the customer. */ + password: Scalars['String'] + /** Indicates whether the customer has consented to be sent marketing material via email. */ + acceptsMarketing?: Maybe<Scalars['Boolean']> +} + +/** Return type for `customerCreate` mutation. */ +export type CustomerCreatePayload = { + __typename?: 'CustomerCreatePayload' + /** The created customer object. */ + customer?: Maybe<Customer> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerDefaultAddressUpdate` mutation. */ +export type CustomerDefaultAddressUpdatePayload = { + __typename?: 'CustomerDefaultAddressUpdatePayload' + /** The updated customer object. */ + customer?: Maybe<Customer> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Possible error codes that could be returned by CustomerUserError. */ +export enum CustomerErrorCode { + /** Input value is blank. */ + Blank = 'BLANK', + /** Input value is invalid. */ + Invalid = 'INVALID', + /** Input value is already taken. */ + Taken = 'TAKEN', + /** Input value is too long. */ + TooLong = 'TOO_LONG', + /** Input value is too short. */ + TooShort = 'TOO_SHORT', + /** Unidentified customer. */ + UnidentifiedCustomer = 'UNIDENTIFIED_CUSTOMER', + /** Customer is disabled. */ + CustomerDisabled = 'CUSTOMER_DISABLED', + /** Input password starts or ends with whitespace. */ + PasswordStartsOrEndsWithWhitespace = 'PASSWORD_STARTS_OR_ENDS_WITH_WHITESPACE', + /** Input contains HTML tags. */ + ContainsHtmlTags = 'CONTAINS_HTML_TAGS', + /** Input contains URL. */ + ContainsUrl = 'CONTAINS_URL', + /** Invalid activation token. */ + TokenInvalid = 'TOKEN_INVALID', + /** Customer already enabled. */ + AlreadyEnabled = 'ALREADY_ENABLED', + /** Address does not exist. */ + NotFound = 'NOT_FOUND', + /** Input email contains an invalid domain name. */ + BadDomain = 'BAD_DOMAIN', + /** Multipass token is not valid. */ + InvalidMultipassRequest = 'INVALID_MULTIPASS_REQUEST', +} + +/** Return type for `customerRecover` mutation. */ +export type CustomerRecoverPayload = { + __typename?: 'CustomerRecoverPayload' + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Return type for `customerResetByUrl` mutation. */ +export type CustomerResetByUrlPayload = { + __typename?: 'CustomerResetByUrlPayload' + /** The customer object which was reset. */ + customer?: Maybe<Customer> + /** A newly created customer access token object for the customer. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Specifies the fields required to reset a customer’s password. */ +export type CustomerResetInput = { + /** The reset token required to reset the customer’s password. */ + resetToken: Scalars['String'] + /** New password that will be set as part of the reset password process. */ + password: Scalars['String'] +} + +/** Return type for `customerReset` mutation. */ +export type CustomerResetPayload = { + __typename?: 'CustomerResetPayload' + /** The customer object which was reset. */ + customer?: Maybe<Customer> + /** A newly created customer access token object for the customer. */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Specifies the fields required to update the Customer information. */ +export type CustomerUpdateInput = { + /** The customer’s first name. */ + firstName?: Maybe<Scalars['String']> + /** The customer’s last name. */ + lastName?: Maybe<Scalars['String']> + /** The customer’s email. */ + email?: Maybe<Scalars['String']> + /** + * A unique phone number for the customer. + * + * Formatted using E.164 standard. For example, _+16135551111_. To remove the phone number, specify `null`. + */ + phone?: Maybe<Scalars['String']> + /** The login password used by the customer. */ + password?: Maybe<Scalars['String']> + /** Indicates whether the customer has consented to be sent marketing material via email. */ + acceptsMarketing?: Maybe<Scalars['Boolean']> +} + +/** Return type for `customerUpdate` mutation. */ +export type CustomerUpdatePayload = { + __typename?: 'CustomerUpdatePayload' + /** The updated customer object. */ + customer?: Maybe<Customer> + /** + * The newly created customer access token. If the customer's password is updated, all previous access tokens + * (including the one used to perform this mutation) become invalid, and a new token is generated. + */ + customerAccessToken?: Maybe<CustomerAccessToken> + /** List of errors that occurred executing the mutation. */ + customerUserErrors: Array<CustomerUserError> + /** + * List of errors that occurred executing the mutation. + * @deprecated Use `customerUserErrors` instead + */ + userErrors: Array<UserError> +} + +/** Represents an error that happens during execution of a customer mutation. */ +export type CustomerUserError = DisplayableError & { + __typename?: 'CustomerUserError' + /** Error code to uniquely identify the error. */ + code?: Maybe<CustomerErrorCode> + /** Path to the input field which caused the error. */ + field?: Maybe<Array<Scalars['String']>> + /** The error message. */ + message: Scalars['String'] +} + +/** Digital wallet, such as Apple Pay, which can be used for accelerated checkouts. */ +export enum DigitalWallet { + /** Apple Pay. */ + ApplePay = 'APPLE_PAY', + /** Android Pay. */ + AndroidPay = 'ANDROID_PAY', + /** Google Pay. */ + GooglePay = 'GOOGLE_PAY', + /** Shopify Pay. */ + ShopifyPay = 'SHOPIFY_PAY', +} + +/** An amount discounting the line that has been allocated by a discount. */ +export type DiscountAllocation = { + __typename?: 'DiscountAllocation' + /** Amount of discount allocated. */ + allocatedAmount: MoneyV2 + /** The discount this allocated amount originated from. */ + discountApplication: DiscountApplication +} + +/** + * Discount applications capture the intentions of a discount source at + * the time of application. + */ +export type DiscountApplication = { + /** The method by which the discount's value is allocated to its entitled items. */ + allocationMethod: DiscountApplicationAllocationMethod + /** Which lines of targetType that the discount is allocated over. */ + targetSelection: DiscountApplicationTargetSelection + /** The type of line that the discount is applicable towards. */ + targetType: DiscountApplicationTargetType + /** The value of the discount application. */ + value: PricingValue +} + +/** The method by which the discount's value is allocated onto its entitled lines. */ +export enum DiscountApplicationAllocationMethod { + /** The value is spread across all entitled lines. */ + Across = 'ACROSS', + /** The value is applied onto every entitled line. */ + Each = 'EACH', + /** The value is specifically applied onto a particular line. */ + One = 'ONE', +} + +/** An auto-generated type for paginating through multiple DiscountApplications. */ +export type DiscountApplicationConnection = { + __typename?: 'DiscountApplicationConnection' + /** A list of edges. */ + edges: Array<DiscountApplicationEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one DiscountApplication and a cursor during pagination. */ +export type DiscountApplicationEdge = { + __typename?: 'DiscountApplicationEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of DiscountApplicationEdge. */ + node: DiscountApplication +} + +/** + * Which lines on the order that the discount is allocated over, of the type + * defined by the Discount Application's target_type. + */ +export enum DiscountApplicationTargetSelection { + /** The discount is allocated onto all the lines. */ + All = 'ALL', + /** The discount is allocated onto only the lines it is entitled for. */ + Entitled = 'ENTITLED', + /** The discount is allocated onto explicitly chosen lines. */ + Explicit = 'EXPLICIT', +} + +/** The type of line (i.e. line item or shipping line) on an order that the discount is applicable towards. */ +export enum DiscountApplicationTargetType { + /** The discount applies onto line items. */ + LineItem = 'LINE_ITEM', + /** The discount applies onto shipping lines. */ + ShippingLine = 'SHIPPING_LINE', +} + +/** + * Discount code applications capture the intentions of a discount code at + * the time that it is applied. + */ +export type DiscountCodeApplication = DiscountApplication & { + __typename?: 'DiscountCodeApplication' + /** The method by which the discount's value is allocated to its entitled items. */ + allocationMethod: DiscountApplicationAllocationMethod + /** Specifies whether the discount code was applied successfully. */ + applicable: Scalars['Boolean'] + /** The string identifying the discount code that was used at the time of application. */ + code: Scalars['String'] + /** Which lines of targetType that the discount is allocated over. */ + targetSelection: DiscountApplicationTargetSelection + /** The type of line that the discount is applicable towards. */ + targetType: DiscountApplicationTargetType + /** The value of the discount application. */ + value: PricingValue +} + +/** Represents an error in the input of a mutation. */ +export type DisplayableError = { + /** Path to the input field which caused the error. */ + field?: Maybe<Array<Scalars['String']>> + /** The error message. */ + message: Scalars['String'] +} + +/** Represents a web address. */ +export type Domain = { + __typename?: 'Domain' + /** The host name of the domain (eg: `example.com`). */ + host: Scalars['String'] + /** Whether SSL is enabled or not. */ + sslEnabled: Scalars['Boolean'] + /** The URL of the domain (eg: `https://example.com`). */ + url: Scalars['URL'] +} + +/** Represents a video hosted outside of Shopify. */ +export type ExternalVideo = Node & + Media & { + __typename?: 'ExternalVideo' + /** A word or phrase to share the nature or contents of a media. */ + alt?: Maybe<Scalars['String']> + /** The URL. */ + embeddedUrl: Scalars['URL'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The media content type. */ + mediaContentType: MediaContentType + /** The preview image for the media. */ + previewImage?: Maybe<Image> + } + +/** Represents a single fulfillment in an order. */ +export type Fulfillment = { + __typename?: 'Fulfillment' + /** List of the fulfillment's line items. */ + fulfillmentLineItems: FulfillmentLineItemConnection + /** The name of the tracking company. */ + trackingCompany?: Maybe<Scalars['String']> + /** + * Tracking information associated with the fulfillment, + * such as the tracking number and tracking URL. + */ + trackingInfo: Array<FulfillmentTrackingInfo> +} + +/** Represents a single fulfillment in an order. */ +export type FulfillmentFulfillmentLineItemsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** Represents a single fulfillment in an order. */ +export type FulfillmentTrackingInfoArgs = { + first?: Maybe<Scalars['Int']> +} + +/** Represents a single line item in a fulfillment. There is at most one fulfillment line item for each order line item. */ +export type FulfillmentLineItem = { + __typename?: 'FulfillmentLineItem' + /** The associated order's line item. */ + lineItem: OrderLineItem + /** The amount fulfilled in this fulfillment. */ + quantity: Scalars['Int'] +} + +/** An auto-generated type for paginating through multiple FulfillmentLineItems. */ +export type FulfillmentLineItemConnection = { + __typename?: 'FulfillmentLineItemConnection' + /** A list of edges. */ + edges: Array<FulfillmentLineItemEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one FulfillmentLineItem and a cursor during pagination. */ +export type FulfillmentLineItemEdge = { + __typename?: 'FulfillmentLineItemEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of FulfillmentLineItemEdge. */ + node: FulfillmentLineItem +} + +/** Tracking information associated with the fulfillment. */ +export type FulfillmentTrackingInfo = { + __typename?: 'FulfillmentTrackingInfo' + /** The tracking number of the fulfillment. */ + number?: Maybe<Scalars['String']> + /** The URL to track the fulfillment. */ + url?: Maybe<Scalars['URL']> +} + +/** Represents information about the metafields associated to the specified resource. */ +export type HasMetafields = { + /** The metafield associated with the resource. */ + metafield?: Maybe<Metafield> + /** A paginated list of metafields associated with the resource. */ + metafields: MetafieldConnection +} + +/** Represents information about the metafields associated to the specified resource. */ +export type HasMetafieldsMetafieldArgs = { + namespace: Scalars['String'] + key: Scalars['String'] +} + +/** Represents information about the metafields associated to the specified resource. */ +export type HasMetafieldsMetafieldsArgs = { + namespace?: Maybe<Scalars['String']> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** Represents an image resource. */ +export type Image = { + __typename?: 'Image' + /** A word or phrase to share the nature or contents of an image. */ + altText?: Maybe<Scalars['String']> + /** The original height of the image in pixels. Returns `null` if the image is not hosted by Shopify. */ + height?: Maybe<Scalars['Int']> + /** A unique identifier for the image. */ + id?: Maybe<Scalars['ID']> + /** + * The location of the original image as a URL. + * + * If there are any existing transformations in the original source URL, they will remain and not be stripped. + */ + originalSrc: Scalars['URL'] + /** + * The location of the image as a URL. + * @deprecated Previously an image had a single `src` field. This could either return the original image + * location or a URL that contained transformations such as sizing or scale. + * + * These transformations were specified by arguments on the parent field. + * + * Now an image has two distinct URL fields: `originalSrc` and `transformedSrc`. + * + * * `originalSrc` - the original unmodified image URL + * * `transformedSrc` - the image URL with the specified transformations included + * + * To migrate to the new fields, image transformations should be moved from the parent field to `transformedSrc`. + * + * Before: + * ```graphql + * { + * shop { + * productImages(maxWidth: 200, scale: 2) { + * edges { + * node { + * src + * } + * } + * } + * } + * } + * ``` + * + * After: + * ```graphql + * { + * shop { + * productImages { + * edges { + * node { + * transformedSrc(maxWidth: 200, scale: 2) + * } + * } + * } + * } + * } + * ``` + * + */ + src: Scalars['URL'] + /** + * The location of the transformed image as a URL. + * + * All transformation arguments are considered "best-effort". If they can be applied to an image, they will be. + * Otherwise any transformations which an image type does not support will be ignored. + */ + transformedSrc: Scalars['URL'] + /** The original width of the image in pixels. Returns `null` if the image is not hosted by Shopify. */ + width?: Maybe<Scalars['Int']> +} + +/** Represents an image resource. */ +export type ImageTransformedSrcArgs = { + maxWidth?: Maybe<Scalars['Int']> + maxHeight?: Maybe<Scalars['Int']> + crop?: Maybe<CropRegion> + scale?: Maybe<Scalars['Int']> + preferredContentType?: Maybe<ImageContentType> +} + +/** An auto-generated type for paginating through multiple Images. */ +export type ImageConnection = { + __typename?: 'ImageConnection' + /** A list of edges. */ + edges: Array<ImageEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** List of supported image content types. */ +export enum ImageContentType { + /** A PNG image. */ + Png = 'PNG', + /** A JPG image. */ + Jpg = 'JPG', + /** A WEBP image. */ + Webp = 'WEBP', +} + +/** An auto-generated type which holds one Image and a cursor during pagination. */ +export type ImageEdge = { + __typename?: 'ImageEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ImageEdge. */ + node: Image +} + +/** Represents a mailing address for customers and shipping. */ +export type MailingAddress = Node & { + __typename?: 'MailingAddress' + /** The first line of the address. Typically the street address or PO Box number. */ + address1?: Maybe<Scalars['String']> + /** The second line of the address. Typically the number of the apartment, suite, or unit. */ + address2?: Maybe<Scalars['String']> + /** The name of the city, district, village, or town. */ + city?: Maybe<Scalars['String']> + /** The name of the customer's company or organization. */ + company?: Maybe<Scalars['String']> + /** The name of the country. */ + country?: Maybe<Scalars['String']> + /** + * The two-letter code for the country of the address. + * + * For example, US. + * @deprecated Use `countryCodeV2` instead + */ + countryCode?: Maybe<Scalars['String']> + /** + * The two-letter code for the country of the address. + * + * For example, US. + */ + countryCodeV2?: Maybe<CountryCode> + /** The first name of the customer. */ + firstName?: Maybe<Scalars['String']> + /** A formatted version of the address, customized by the provided arguments. */ + formatted: Array<Scalars['String']> + /** A comma-separated list of the values for city, province, and country. */ + formattedArea?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The last name of the customer. */ + lastName?: Maybe<Scalars['String']> + /** The latitude coordinate of the customer address. */ + latitude?: Maybe<Scalars['Float']> + /** The longitude coordinate of the customer address. */ + longitude?: Maybe<Scalars['Float']> + /** The full name of the customer, based on firstName and lastName. */ + name?: Maybe<Scalars['String']> + /** + * A unique phone number for the customer. + * + * Formatted using E.164 standard. For example, _+16135551111_. + */ + phone?: Maybe<Scalars['String']> + /** The region of the address, such as the province, state, or district. */ + province?: Maybe<Scalars['String']> + /** + * The two-letter code for the region. + * + * For example, ON. + */ + provinceCode?: Maybe<Scalars['String']> + /** The zip or postal code of the address. */ + zip?: Maybe<Scalars['String']> +} + +/** Represents a mailing address for customers and shipping. */ +export type MailingAddressFormattedArgs = { + withName?: Maybe<Scalars['Boolean']> + withCompany?: Maybe<Scalars['Boolean']> +} + +/** An auto-generated type for paginating through multiple MailingAddresses. */ +export type MailingAddressConnection = { + __typename?: 'MailingAddressConnection' + /** A list of edges. */ + edges: Array<MailingAddressEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one MailingAddress and a cursor during pagination. */ +export type MailingAddressEdge = { + __typename?: 'MailingAddressEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of MailingAddressEdge. */ + node: MailingAddress +} + +/** Specifies the fields accepted to create or update a mailing address. */ +export type MailingAddressInput = { + /** The first line of the address. Typically the street address or PO Box number. */ + address1?: Maybe<Scalars['String']> + /** The second line of the address. Typically the number of the apartment, suite, or unit. */ + address2?: Maybe<Scalars['String']> + /** The name of the city, district, village, or town. */ + city?: Maybe<Scalars['String']> + /** The name of the customer's company or organization. */ + company?: Maybe<Scalars['String']> + /** The name of the country. */ + country?: Maybe<Scalars['String']> + /** The first name of the customer. */ + firstName?: Maybe<Scalars['String']> + /** The last name of the customer. */ + lastName?: Maybe<Scalars['String']> + /** + * A unique phone number for the customer. + * + * Formatted using E.164 standard. For example, _+16135551111_. + */ + phone?: Maybe<Scalars['String']> + /** The region of the address, such as the province, state, or district. */ + province?: Maybe<Scalars['String']> + /** The zip or postal code of the address. */ + zip?: Maybe<Scalars['String']> +} + +/** Manual discount applications capture the intentions of a discount that was manually created. */ +export type ManualDiscountApplication = DiscountApplication & { + __typename?: 'ManualDiscountApplication' + /** The method by which the discount's value is allocated to its entitled items. */ + allocationMethod: DiscountApplicationAllocationMethod + /** The description of the application. */ + description?: Maybe<Scalars['String']> + /** Which lines of targetType that the discount is allocated over. */ + targetSelection: DiscountApplicationTargetSelection + /** The type of line that the discount is applicable towards. */ + targetType: DiscountApplicationTargetType + /** The title of the application. */ + title: Scalars['String'] + /** The value of the discount application. */ + value: PricingValue +} + +/** Represents a media interface. */ +export type Media = { + /** A word or phrase to share the nature or contents of a media. */ + alt?: Maybe<Scalars['String']> + /** The media content type. */ + mediaContentType: MediaContentType + /** The preview image for the media. */ + previewImage?: Maybe<Image> +} + +/** An auto-generated type for paginating through multiple Media. */ +export type MediaConnection = { + __typename?: 'MediaConnection' + /** A list of edges. */ + edges: Array<MediaEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** The possible content types for a media object. */ +export enum MediaContentType { + /** An externally hosted video. */ + ExternalVideo = 'EXTERNAL_VIDEO', + /** A Shopify hosted image. */ + Image = 'IMAGE', + /** A 3d model. */ + Model_3D = 'MODEL_3D', + /** A Shopify hosted video. */ + Video = 'VIDEO', +} + +/** An auto-generated type which holds one Media and a cursor during pagination. */ +export type MediaEdge = { + __typename?: 'MediaEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of MediaEdge. */ + node: Media +} + +/** Represents a Shopify hosted image. */ +export type MediaImage = Node & + Media & { + __typename?: 'MediaImage' + /** A word or phrase to share the nature or contents of a media. */ + alt?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The image for the media. */ + image?: Maybe<Image> + /** The media content type. */ + mediaContentType: MediaContentType + /** The preview image for the media. */ + previewImage?: Maybe<Image> + } + +/** + * Metafields represent custom metadata attached to a resource. Metafields can be sorted into namespaces and are + * comprised of keys, values, and value types. + */ +export type Metafield = Node & { + __typename?: 'Metafield' + /** The date and time when the storefront metafield was created. */ + createdAt: Scalars['DateTime'] + /** The description of a metafield. */ + description?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The key name for a metafield. */ + key: Scalars['String'] + /** The namespace for a metafield. */ + namespace: Scalars['String'] + /** The parent object that the metafield belongs to. */ + parentResource: MetafieldParentResource + /** The date and time when the storefront metafield was updated. */ + updatedAt: Scalars['DateTime'] + /** The value of a metafield. */ + value: Scalars['String'] + /** Represents the metafield value type. */ + valueType: MetafieldValueType +} + +/** An auto-generated type for paginating through multiple Metafields. */ +export type MetafieldConnection = { + __typename?: 'MetafieldConnection' + /** A list of edges. */ + edges: Array<MetafieldEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Metafield and a cursor during pagination. */ +export type MetafieldEdge = { + __typename?: 'MetafieldEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of MetafieldEdge. */ + node: Metafield +} + +/** A resource that the metafield belongs to. */ +export type MetafieldParentResource = Product | ProductVariant + +/** Metafield value types. */ +export enum MetafieldValueType { + /** A string metafield. */ + String = 'STRING', + /** An integer metafield. */ + Integer = 'INTEGER', + /** A json string metafield. */ + JsonString = 'JSON_STRING', +} + +/** Represents a Shopify hosted 3D model. */ +export type Model3d = Node & + Media & { + __typename?: 'Model3d' + /** A word or phrase to share the nature or contents of a media. */ + alt?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The media content type. */ + mediaContentType: MediaContentType + /** The preview image for the media. */ + previewImage?: Maybe<Image> + /** The sources for a 3d model. */ + sources: Array<Model3dSource> + } + +/** Represents a source for a Shopify hosted 3d model. */ +export type Model3dSource = { + __typename?: 'Model3dSource' + /** The filesize of the 3d model. */ + filesize: Scalars['Int'] + /** The format of the 3d model. */ + format: Scalars['String'] + /** The MIME type of the 3d model. */ + mimeType: Scalars['String'] + /** The URL of the 3d model. */ + url: Scalars['String'] +} + +/** Specifies the fields for a monetary value with currency. */ +export type MoneyInput = { + /** Decimal money amount. */ + amount: Scalars['Decimal'] + /** Currency of the money. */ + currencyCode: CurrencyCode +} + +/** + * A monetary value with currency. + * + * To format currencies, combine this type's amount and currencyCode fields with your client's locale. + * + * For example, in JavaScript you could use Intl.NumberFormat: + * + * ```js + * new Intl.NumberFormat(locale, { + * style: 'currency', + * currency: currencyCode + * }).format(amount); + * ``` + * + * Other formatting libraries include: + * + * * iOS - [NumberFormatter](https://developer.apple.com/documentation/foundation/numberformatter) + * * Android - [NumberFormat](https://developer.android.com/reference/java/text/NumberFormat.html) + * * PHP - [NumberFormatter](http://php.net/manual/en/class.numberformatter.php) + * + * For a more general solution, the [Unicode CLDR number formatting database] is available with many implementations + * (such as [TwitterCldr](https://github.com/twitter/twitter-cldr-rb)). + */ +export type MoneyV2 = { + __typename?: 'MoneyV2' + /** Decimal money amount. */ + amount: Scalars['Decimal'] + /** Currency of the money. */ + currencyCode: CurrencyCode +} + +/** An auto-generated type for paginating through multiple MoneyV2s. */ +export type MoneyV2Connection = { + __typename?: 'MoneyV2Connection' + /** A list of edges. */ + edges: Array<MoneyV2Edge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one MoneyV2 and a cursor during pagination. */ +export type MoneyV2Edge = { + __typename?: 'MoneyV2Edge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of MoneyV2Edge. */ + node: MoneyV2 +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type Mutation = { + __typename?: 'Mutation' + /** + * Updates the attributes of a checkout. + * @deprecated Use `checkoutAttributesUpdateV2` instead + */ + checkoutAttributesUpdate?: Maybe<CheckoutAttributesUpdatePayload> + /** Updates the attributes of a checkout. */ + checkoutAttributesUpdateV2?: Maybe<CheckoutAttributesUpdateV2Payload> + /** Completes a checkout without providing payment information. You can use this mutation for free items or items whose purchase price is covered by a gift card. */ + checkoutCompleteFree?: Maybe<CheckoutCompleteFreePayload> + /** + * Completes a checkout using a credit card token from Shopify's Vault. + * @deprecated Use `checkoutCompleteWithCreditCardV2` instead + */ + checkoutCompleteWithCreditCard?: Maybe<CheckoutCompleteWithCreditCardPayload> + /** Completes a checkout using a credit card token from Shopify's card vault. Before you can complete checkouts using CheckoutCompleteWithCreditCardV2, you need to [_request payment processing_](https://help.shopify.com/api/guides/sales-channel-sdk/getting-started#request-payment-processing). */ + checkoutCompleteWithCreditCardV2?: Maybe<CheckoutCompleteWithCreditCardV2Payload> + /** + * Completes a checkout with a tokenized payment. + * @deprecated Use `checkoutCompleteWithTokenizedPaymentV2` instead + */ + checkoutCompleteWithTokenizedPayment?: Maybe<CheckoutCompleteWithTokenizedPaymentPayload> + /** + * Completes a checkout with a tokenized payment. + * @deprecated Use `checkoutCompleteWithTokenizedPaymentV3` instead + */ + checkoutCompleteWithTokenizedPaymentV2?: Maybe<CheckoutCompleteWithTokenizedPaymentV2Payload> + /** Completes a checkout with a tokenized payment. */ + checkoutCompleteWithTokenizedPaymentV3?: Maybe<CheckoutCompleteWithTokenizedPaymentV3Payload> + /** Creates a new checkout. */ + checkoutCreate?: Maybe<CheckoutCreatePayload> + /** + * Associates a customer to the checkout. + * @deprecated Use `checkoutCustomerAssociateV2` instead + */ + checkoutCustomerAssociate?: Maybe<CheckoutCustomerAssociatePayload> + /** Associates a customer to the checkout. */ + checkoutCustomerAssociateV2?: Maybe<CheckoutCustomerAssociateV2Payload> + /** + * Disassociates the current checkout customer from the checkout. + * @deprecated Use `checkoutCustomerDisassociateV2` instead + */ + checkoutCustomerDisassociate?: Maybe<CheckoutCustomerDisassociatePayload> + /** Disassociates the current checkout customer from the checkout. */ + checkoutCustomerDisassociateV2?: Maybe<CheckoutCustomerDisassociateV2Payload> + /** + * Applies a discount to an existing checkout using a discount code. + * @deprecated Use `checkoutDiscountCodeApplyV2` instead + */ + checkoutDiscountCodeApply?: Maybe<CheckoutDiscountCodeApplyPayload> + /** Applies a discount to an existing checkout using a discount code. */ + checkoutDiscountCodeApplyV2?: Maybe<CheckoutDiscountCodeApplyV2Payload> + /** Removes the applied discount from an existing checkout. */ + checkoutDiscountCodeRemove?: Maybe<CheckoutDiscountCodeRemovePayload> + /** + * Updates the email on an existing checkout. + * @deprecated Use `checkoutEmailUpdateV2` instead + */ + checkoutEmailUpdate?: Maybe<CheckoutEmailUpdatePayload> + /** Updates the email on an existing checkout. */ + checkoutEmailUpdateV2?: Maybe<CheckoutEmailUpdateV2Payload> + /** + * Applies a gift card to an existing checkout using a gift card code. This will replace all currently applied gift cards. + * @deprecated Use `checkoutGiftCardsAppend` instead + */ + checkoutGiftCardApply?: Maybe<CheckoutGiftCardApplyPayload> + /** + * Removes an applied gift card from the checkout. + * @deprecated Use `checkoutGiftCardRemoveV2` instead + */ + checkoutGiftCardRemove?: Maybe<CheckoutGiftCardRemovePayload> + /** Removes an applied gift card from the checkout. */ + checkoutGiftCardRemoveV2?: Maybe<CheckoutGiftCardRemoveV2Payload> + /** Appends gift cards to an existing checkout. */ + checkoutGiftCardsAppend?: Maybe<CheckoutGiftCardsAppendPayload> + /** Adds a list of line items to a checkout. */ + checkoutLineItemsAdd?: Maybe<CheckoutLineItemsAddPayload> + /** Removes line items from an existing checkout. */ + checkoutLineItemsRemove?: Maybe<CheckoutLineItemsRemovePayload> + /** Sets a list of line items to a checkout. */ + checkoutLineItemsReplace?: Maybe<CheckoutLineItemsReplacePayload> + /** Updates line items on a checkout. */ + checkoutLineItemsUpdate?: Maybe<CheckoutLineItemsUpdatePayload> + /** + * Updates the shipping address of an existing checkout. + * @deprecated Use `checkoutShippingAddressUpdateV2` instead + */ + checkoutShippingAddressUpdate?: Maybe<CheckoutShippingAddressUpdatePayload> + /** Updates the shipping address of an existing checkout. */ + checkoutShippingAddressUpdateV2?: Maybe<CheckoutShippingAddressUpdateV2Payload> + /** Updates the shipping lines on an existing checkout. */ + checkoutShippingLineUpdate?: Maybe<CheckoutShippingLineUpdatePayload> + /** + * Creates a customer access token. + * The customer access token is required to modify the customer object in any way. + */ + customerAccessTokenCreate?: Maybe<CustomerAccessTokenCreatePayload> + /** + * Creates a customer access token using a multipass token instead of email and password. + * A customer record is created if customer does not exist. If a customer record already + * exists but the record is disabled, then it's enabled. + */ + customerAccessTokenCreateWithMultipass?: Maybe<CustomerAccessTokenCreateWithMultipassPayload> + /** Permanently destroys a customer access token. */ + customerAccessTokenDelete?: Maybe<CustomerAccessTokenDeletePayload> + /** + * Renews a customer access token. + * + * Access token renewal must happen *before* a token expires. + * If a token has already expired, a new one should be created instead via `customerAccessTokenCreate`. + */ + customerAccessTokenRenew?: Maybe<CustomerAccessTokenRenewPayload> + /** Activates a customer. */ + customerActivate?: Maybe<CustomerActivatePayload> + /** Activates a customer with the activation url received from `customerCreate`. */ + customerActivateByUrl?: Maybe<CustomerActivateByUrlPayload> + /** Creates a new address for a customer. */ + customerAddressCreate?: Maybe<CustomerAddressCreatePayload> + /** Permanently deletes the address of an existing customer. */ + customerAddressDelete?: Maybe<CustomerAddressDeletePayload> + /** Updates the address of an existing customer. */ + customerAddressUpdate?: Maybe<CustomerAddressUpdatePayload> + /** Creates a new customer. */ + customerCreate?: Maybe<CustomerCreatePayload> + /** Updates the default address of an existing customer. */ + customerDefaultAddressUpdate?: Maybe<CustomerDefaultAddressUpdatePayload> + /** Sends a reset password email to the customer, as the first step in the reset password process. */ + customerRecover?: Maybe<CustomerRecoverPayload> + /** Resets a customer’s password with a token received from `CustomerRecover`. */ + customerReset?: Maybe<CustomerResetPayload> + /** Resets a customer’s password with the reset password url received from `CustomerRecover`. */ + customerResetByUrl?: Maybe<CustomerResetByUrlPayload> + /** Updates an existing customer. */ + customerUpdate?: Maybe<CustomerUpdatePayload> +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutAttributesUpdateArgs = { + checkoutId: Scalars['ID'] + input: CheckoutAttributesUpdateInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutAttributesUpdateV2Args = { + checkoutId: Scalars['ID'] + input: CheckoutAttributesUpdateV2Input +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteFreeArgs = { + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteWithCreditCardArgs = { + checkoutId: Scalars['ID'] + payment: CreditCardPaymentInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteWithCreditCardV2Args = { + checkoutId: Scalars['ID'] + payment: CreditCardPaymentInputV2 +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteWithTokenizedPaymentArgs = { + checkoutId: Scalars['ID'] + payment: TokenizedPaymentInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteWithTokenizedPaymentV2Args = { + checkoutId: Scalars['ID'] + payment: TokenizedPaymentInputV2 +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCompleteWithTokenizedPaymentV3Args = { + checkoutId: Scalars['ID'] + payment: TokenizedPaymentInputV3 +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCreateArgs = { + input: CheckoutCreateInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCustomerAssociateArgs = { + checkoutId: Scalars['ID'] + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCustomerAssociateV2Args = { + checkoutId: Scalars['ID'] + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCustomerDisassociateArgs = { + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutCustomerDisassociateV2Args = { + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutDiscountCodeApplyArgs = { + discountCode: Scalars['String'] + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutDiscountCodeApplyV2Args = { + discountCode: Scalars['String'] + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutDiscountCodeRemoveArgs = { + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutEmailUpdateArgs = { + checkoutId: Scalars['ID'] + email: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutEmailUpdateV2Args = { + checkoutId: Scalars['ID'] + email: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutGiftCardApplyArgs = { + giftCardCode: Scalars['String'] + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutGiftCardRemoveArgs = { + appliedGiftCardId: Scalars['ID'] + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutGiftCardRemoveV2Args = { + appliedGiftCardId: Scalars['ID'] + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutGiftCardsAppendArgs = { + giftCardCodes: Array<Scalars['String']> + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutLineItemsAddArgs = { + lineItems: Array<CheckoutLineItemInput> + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutLineItemsRemoveArgs = { + checkoutId: Scalars['ID'] + lineItemIds: Array<Scalars['ID']> +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutLineItemsReplaceArgs = { + lineItems: Array<CheckoutLineItemInput> + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutLineItemsUpdateArgs = { + checkoutId: Scalars['ID'] + lineItems: Array<CheckoutLineItemUpdateInput> +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutShippingAddressUpdateArgs = { + shippingAddress: MailingAddressInput + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutShippingAddressUpdateV2Args = { + shippingAddress: MailingAddressInput + checkoutId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCheckoutShippingLineUpdateArgs = { + checkoutId: Scalars['ID'] + shippingRateHandle: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAccessTokenCreateArgs = { + input: CustomerAccessTokenCreateInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAccessTokenCreateWithMultipassArgs = { + multipassToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAccessTokenDeleteArgs = { + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAccessTokenRenewArgs = { + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerActivateArgs = { + id: Scalars['ID'] + input: CustomerActivateInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerActivateByUrlArgs = { + activationUrl: Scalars['URL'] + password: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAddressCreateArgs = { + customerAccessToken: Scalars['String'] + address: MailingAddressInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAddressDeleteArgs = { + id: Scalars['ID'] + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerAddressUpdateArgs = { + customerAccessToken: Scalars['String'] + id: Scalars['ID'] + address: MailingAddressInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerCreateArgs = { + input: CustomerCreateInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerDefaultAddressUpdateArgs = { + customerAccessToken: Scalars['String'] + addressId: Scalars['ID'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerRecoverArgs = { + email: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerResetArgs = { + id: Scalars['ID'] + input: CustomerResetInput +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerResetByUrlArgs = { + resetUrl: Scalars['URL'] + password: Scalars['String'] +} + +/** The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. */ +export type MutationCustomerUpdateArgs = { + customerAccessToken: Scalars['String'] + customer: CustomerUpdateInput +} + +/** An object with an ID to support global identification. */ +export type Node = { + /** Globally unique identifier. */ + id: Scalars['ID'] +} + +/** An order is a customer’s completed request to purchase one or more products from a shop. An order is created when a customer completes the checkout process, during which time they provides an email address, billing address and payment information. */ +export type Order = Node & { + __typename?: 'Order' + /** The reason for the order's cancellation. Returns `null` if the order wasn't canceled. */ + cancelReason?: Maybe<OrderCancelReason> + /** The date and time when the order was canceled. Returns null if the order wasn't canceled. */ + canceledAt?: Maybe<Scalars['DateTime']> + /** The code of the currency used for the payment. */ + currencyCode: CurrencyCode + /** The subtotal of line items and their discounts, excluding line items that have been removed. Does not contain order-level discounts, duties, shipping costs, or shipping discounts. Taxes are not included unless the order is a taxes-included order. */ + currentSubtotalPrice: MoneyV2 + /** The total amount of the order, including duties, taxes and discounts, minus amounts for line items that have been removed. */ + currentTotalPrice: MoneyV2 + /** The total of all taxes applied to the order, excluding taxes for returned line items. */ + currentTotalTax: MoneyV2 + /** The locale code in which this specific order happened. */ + customerLocale?: Maybe<Scalars['String']> + /** The unique URL that the customer can use to access the order. */ + customerUrl?: Maybe<Scalars['URL']> + /** Discounts that have been applied on the order. */ + discountApplications: DiscountApplicationConnection + /** Whether the order has had any edits applied or not. */ + edited: Scalars['Boolean'] + /** The customer's email address. */ + email?: Maybe<Scalars['String']> + /** The financial status of the order. */ + financialStatus?: Maybe<OrderFinancialStatus> + /** The fulfillment status for the order. */ + fulfillmentStatus: OrderFulfillmentStatus + /** Globally unique identifier. */ + id: Scalars['ID'] + /** List of the order’s line items. */ + lineItems: OrderLineItemConnection + /** + * Unique identifier for the order that appears on the order. + * For example, _#1000_ or _Store1001. + */ + name: Scalars['String'] + /** A unique numeric identifier for the order for use by shop owner and customer. */ + orderNumber: Scalars['Int'] + /** The total price of the order before any applied edits. */ + originalTotalPrice: MoneyV2 + /** The customer's phone number for receiving SMS notifications. */ + phone?: Maybe<Scalars['String']> + /** + * The date and time when the order was imported. + * This value can be set to dates in the past when importing from other systems. + * If no value is provided, it will be auto-generated based on current date and time. + */ + processedAt: Scalars['DateTime'] + /** The address to where the order will be shipped. */ + shippingAddress?: Maybe<MailingAddress> + /** The discounts that have been allocated onto the shipping line by discount applications. */ + shippingDiscountAllocations: Array<DiscountAllocation> + /** The unique URL for the order's status page. */ + statusUrl: Scalars['URL'] + /** + * Price of the order before shipping and taxes. + * @deprecated Use `subtotalPriceV2` instead + */ + subtotalPrice?: Maybe<Scalars['Money']> + /** Price of the order before duties, shipping and taxes. */ + subtotalPriceV2?: Maybe<MoneyV2> + /** List of the order’s successful fulfillments. */ + successfulFulfillments?: Maybe<Array<Fulfillment>> + /** + * The sum of all the prices of all the items in the order, taxes and discounts included (must be positive). + * @deprecated Use `totalPriceV2` instead + */ + totalPrice: Scalars['Money'] + /** The sum of all the prices of all the items in the order, duties, taxes and discounts included (must be positive). */ + totalPriceV2: MoneyV2 + /** + * The total amount that has been refunded. + * @deprecated Use `totalRefundedV2` instead + */ + totalRefunded: Scalars['Money'] + /** The total amount that has been refunded. */ + totalRefundedV2: MoneyV2 + /** + * The total cost of shipping. + * @deprecated Use `totalShippingPriceV2` instead + */ + totalShippingPrice: Scalars['Money'] + /** The total cost of shipping. */ + totalShippingPriceV2: MoneyV2 + /** + * The total cost of taxes. + * @deprecated Use `totalTaxV2` instead + */ + totalTax?: Maybe<Scalars['Money']> + /** The total cost of taxes. */ + totalTaxV2?: Maybe<MoneyV2> +} + +/** An order is a customer’s completed request to purchase one or more products from a shop. An order is created when a customer completes the checkout process, during which time they provides an email address, billing address and payment information. */ +export type OrderDiscountApplicationsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** An order is a customer’s completed request to purchase one or more products from a shop. An order is created when a customer completes the checkout process, during which time they provides an email address, billing address and payment information. */ +export type OrderLineItemsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** An order is a customer’s completed request to purchase one or more products from a shop. An order is created when a customer completes the checkout process, during which time they provides an email address, billing address and payment information. */ +export type OrderSuccessfulFulfillmentsArgs = { + first?: Maybe<Scalars['Int']> +} + +/** Represents the reason for the order's cancellation. */ +export enum OrderCancelReason { + /** The customer wanted to cancel the order. */ + Customer = 'CUSTOMER', + /** The order was fraudulent. */ + Fraud = 'FRAUD', + /** There was insufficient inventory. */ + Inventory = 'INVENTORY', + /** Payment was declined. */ + Declined = 'DECLINED', + /** The order was canceled for an unlisted reason. */ + Other = 'OTHER', +} + +/** An auto-generated type for paginating through multiple Orders. */ +export type OrderConnection = { + __typename?: 'OrderConnection' + /** A list of edges. */ + edges: Array<OrderEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Order and a cursor during pagination. */ +export type OrderEdge = { + __typename?: 'OrderEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of OrderEdge. */ + node: Order +} + +/** Represents the order's current financial status. */ +export enum OrderFinancialStatus { + /** Displayed as **Pending**. */ + Pending = 'PENDING', + /** Displayed as **Authorized**. */ + Authorized = 'AUTHORIZED', + /** Displayed as **Partially paid**. */ + PartiallyPaid = 'PARTIALLY_PAID', + /** Displayed as **Partially refunded**. */ + PartiallyRefunded = 'PARTIALLY_REFUNDED', + /** Displayed as **Voided**. */ + Voided = 'VOIDED', + /** Displayed as **Paid**. */ + Paid = 'PAID', + /** Displayed as **Refunded**. */ + Refunded = 'REFUNDED', +} + +/** Represents the order's current fulfillment status. */ +export enum OrderFulfillmentStatus { + /** Displayed as **Unfulfilled**. */ + Unfulfilled = 'UNFULFILLED', + /** Displayed as **Partially fulfilled**. */ + PartiallyFulfilled = 'PARTIALLY_FULFILLED', + /** Displayed as **Fulfilled**. */ + Fulfilled = 'FULFILLED', + /** Displayed as **Restocked**. */ + Restocked = 'RESTOCKED', + /** Displayed as **Pending fulfillment**. */ + PendingFulfillment = 'PENDING_FULFILLMENT', + /** Displayed as **Open**. */ + Open = 'OPEN', + /** Displayed as **In progress**. */ + InProgress = 'IN_PROGRESS', + /** Displayed as **Scheduled**. */ + Scheduled = 'SCHEDULED', +} + +/** Represents a single line in an order. There is one line item for each distinct product variant. */ +export type OrderLineItem = { + __typename?: 'OrderLineItem' + /** The number of entries associated to the line item minus the items that have been removed. */ + currentQuantity: Scalars['Int'] + /** List of custom attributes associated to the line item. */ + customAttributes: Array<Attribute> + /** The discounts that have been allocated onto the order line item by discount applications. */ + discountAllocations: Array<DiscountAllocation> + /** The total price of the line item, including discounts, and displayed in the presentment currency. */ + discountedTotalPrice: MoneyV2 + /** The total price of the line item, not including any discounts. The total price is calculated using the original unit price multiplied by the quantity, and it is displayed in the presentment currency. */ + originalTotalPrice: MoneyV2 + /** The number of products variants associated to the line item. */ + quantity: Scalars['Int'] + /** The title of the product combined with title of the variant. */ + title: Scalars['String'] + /** The product variant object associated to the line item. */ + variant?: Maybe<ProductVariant> +} + +/** An auto-generated type for paginating through multiple OrderLineItems. */ +export type OrderLineItemConnection = { + __typename?: 'OrderLineItemConnection' + /** A list of edges. */ + edges: Array<OrderLineItemEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one OrderLineItem and a cursor during pagination. */ +export type OrderLineItemEdge = { + __typename?: 'OrderLineItemEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of OrderLineItemEdge. */ + node: OrderLineItem +} + +/** The set of valid sort keys for the Order query. */ +export enum OrderSortKeys { + /** Sort by the `processed_at` value. */ + ProcessedAt = 'PROCESSED_AT', + /** Sort by the `total_price` value. */ + TotalPrice = 'TOTAL_PRICE', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** Shopify merchants can create pages to hold static HTML content. Each Page object represents a custom page on the online store. */ +export type Page = Node & { + __typename?: 'Page' + /** The description of the page, complete with HTML formatting. */ + body: Scalars['HTML'] + /** Summary of the page body. */ + bodySummary: Scalars['String'] + /** The timestamp of the page creation. */ + createdAt: Scalars['DateTime'] + /** A human-friendly unique string for the page automatically generated from its title. */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The page's SEO information. */ + seo?: Maybe<Seo> + /** The title of the page. */ + title: Scalars['String'] + /** The timestamp of the latest page update. */ + updatedAt: Scalars['DateTime'] + /** The url pointing to the page accessible from the web. */ + url: Scalars['URL'] +} + +/** An auto-generated type for paginating through multiple Pages. */ +export type PageConnection = { + __typename?: 'PageConnection' + /** A list of edges. */ + edges: Array<PageEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Page and a cursor during pagination. */ +export type PageEdge = { + __typename?: 'PageEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of PageEdge. */ + node: Page +} + +/** Information about pagination in a connection. */ +export type PageInfo = { + __typename?: 'PageInfo' + /** Indicates if there are more pages to fetch. */ + hasNextPage: Scalars['Boolean'] + /** Indicates if there are any pages prior to the current page. */ + hasPreviousPage: Scalars['Boolean'] +} + +/** The set of valid sort keys for the Page query. */ +export enum PageSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `updated_at` value. */ + UpdatedAt = 'UPDATED_AT', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** A payment applied to a checkout. */ +export type Payment = Node & { + __typename?: 'Payment' + /** + * The amount of the payment. + * @deprecated Use `amountV2` instead + */ + amount: Scalars['Money'] + /** The amount of the payment. */ + amountV2: MoneyV2 + /** The billing address for the payment. */ + billingAddress?: Maybe<MailingAddress> + /** The checkout to which the payment belongs. */ + checkout: Checkout + /** The credit card used for the payment in the case of direct payments. */ + creditCard?: Maybe<CreditCard> + /** A message describing a processing error during asynchronous processing. */ + errorMessage?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** A client-side generated token to identify a payment and perform idempotent operations. */ + idempotencyKey?: Maybe<Scalars['String']> + /** The URL where the customer needs to be redirected so they can complete the 3D Secure payment flow. */ + nextActionUrl?: Maybe<Scalars['URL']> + /** Whether or not the payment is still processing asynchronously. */ + ready: Scalars['Boolean'] + /** A flag to indicate if the payment is to be done in test mode for gateways that support it. */ + test: Scalars['Boolean'] + /** The actual transaction recorded by Shopify after having processed the payment with the gateway. */ + transaction?: Maybe<Transaction> +} + +/** Settings related to payments. */ +export type PaymentSettings = { + __typename?: 'PaymentSettings' + /** List of the card brands which the shop accepts. */ + acceptedCardBrands: Array<CardBrand> + /** The url pointing to the endpoint to vault credit cards. */ + cardVaultUrl: Scalars['URL'] + /** The country where the shop is located. */ + countryCode: CountryCode + /** The three-letter code for the shop's primary currency. */ + currencyCode: CurrencyCode + /** A list of enabled currencies (ISO 4217 format) that the shop accepts. Merchants can enable currencies from their Shopify Payments settings in the Shopify admin. */ + enabledPresentmentCurrencies: Array<CurrencyCode> + /** The shop’s Shopify Payments account id. */ + shopifyPaymentsAccountId?: Maybe<Scalars['String']> + /** List of the digital wallets which the shop supports. */ + supportedDigitalWallets: Array<DigitalWallet> +} + +/** The valid values for the types of payment token. */ +export enum PaymentTokenType { + /** Apple Pay token type. */ + ApplePay = 'APPLE_PAY', + /** Vault payment token type. */ + Vault = 'VAULT', + /** Shopify Pay token type. */ + ShopifyPay = 'SHOPIFY_PAY', + /** Google Pay token type. */ + GooglePay = 'GOOGLE_PAY', +} + +/** The value of the percentage pricing object. */ +export type PricingPercentageValue = { + __typename?: 'PricingPercentageValue' + /** The percentage value of the object. */ + percentage: Scalars['Float'] +} + +/** The price value (fixed or percentage) for a discount application. */ +export type PricingValue = MoneyV2 | PricingPercentageValue + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type Product = Node & + HasMetafields & { + __typename?: 'Product' + /** Indicates if at least one product variant is available for sale. */ + availableForSale: Scalars['Boolean'] + /** List of collections a product belongs to. */ + collections: CollectionConnection + /** The compare at price of the product across all variants. */ + compareAtPriceRange: ProductPriceRange + /** The date and time when the product was created. */ + createdAt: Scalars['DateTime'] + /** Stripped description of the product, single line with HTML tags removed. */ + description: Scalars['String'] + /** The description of the product, complete with HTML formatting. */ + descriptionHtml: Scalars['HTML'] + /** + * A human-friendly unique string for the Product automatically generated from its title. + * They are used by the Liquid templating language to refer to objects. + */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** List of images associated with the product. */ + images: ImageConnection + /** The media associated with the product. */ + media: MediaConnection + /** The metafield associated with the resource. */ + metafield?: Maybe<Metafield> + /** A paginated list of metafields associated with the resource. */ + metafields: MetafieldConnection + /** + * The online store URL for the product. + * A value of `null` indicates that the product is not published to the Online Store sales channel. + */ + onlineStoreUrl?: Maybe<Scalars['URL']> + /** List of product options. */ + options: Array<ProductOption> + /** List of price ranges in the presentment currencies for this shop. */ + presentmentPriceRanges: ProductPriceRangeConnection + /** The price range. */ + priceRange: ProductPriceRange + /** A categorization that a product can be tagged with, commonly used for filtering and searching. */ + productType: Scalars['String'] + /** The date and time when the product was published to the channel. */ + publishedAt: Scalars['DateTime'] + /** The product's SEO information. */ + seo: Seo + /** + * A comma separated list of tags that have been added to the product. + * Additional access scope required for private apps: unauthenticated_read_product_tags. + */ + tags: Array<Scalars['String']> + /** The product’s title. */ + title: Scalars['String'] + /** The total quantity of inventory in stock for this Product. */ + totalInventory?: Maybe<Scalars['Int']> + /** + * The date and time when the product was last modified. + * A product's `updatedAt` value can change for different reasons. For example, if an order + * is placed for a product that has inventory tracking set up, then the inventory adjustment + * is counted as an update. + */ + updatedAt: Scalars['DateTime'] + /** + * Find a product’s variant based on its selected options. + * This is useful for converting a user’s selection of product options into a single matching variant. + * If there is not a variant for the selected options, `null` will be returned. + */ + variantBySelectedOptions?: Maybe<ProductVariant> + /** List of the product’s variants. */ + variants: ProductVariantConnection + /** The product’s vendor name. */ + vendor: Scalars['String'] + } + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductCollectionsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductDescriptionArgs = { + truncateAt?: Maybe<Scalars['Int']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductImagesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductImageSortKeys> + maxWidth?: Maybe<Scalars['Int']> + maxHeight?: Maybe<Scalars['Int']> + crop?: Maybe<CropRegion> + scale?: Maybe<Scalars['Int']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductMediaArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductMediaSortKeys> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductMetafieldArgs = { + namespace: Scalars['String'] + key: Scalars['String'] +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductMetafieldsArgs = { + namespace?: Maybe<Scalars['String']> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductOptionsArgs = { + first?: Maybe<Scalars['Int']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductPresentmentPriceRangesArgs = { + presentmentCurrencies?: Maybe<Array<CurrencyCode>> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductVariantBySelectedOptionsArgs = { + selectedOptions: Array<SelectedOptionInput> +} + +/** + * A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. + * For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). + */ +export type ProductVariantsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductVariantSortKeys> +} + +/** The set of valid sort keys for the ProductCollection query. */ +export enum ProductCollectionSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `price` value. */ + Price = 'PRICE', + /** Sort by the `best-selling` value. */ + BestSelling = 'BEST_SELLING', + /** Sort by the `created` value. */ + Created = 'CREATED', + /** Sort by the `id` value. */ + Id = 'ID', + /** Sort by the `manual` value. */ + Manual = 'MANUAL', + /** Sort by the `collection-default` value. */ + CollectionDefault = 'COLLECTION_DEFAULT', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** An auto-generated type for paginating through multiple Products. */ +export type ProductConnection = { + __typename?: 'ProductConnection' + /** A list of edges. */ + edges: Array<ProductEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one Product and a cursor during pagination. */ +export type ProductEdge = { + __typename?: 'ProductEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ProductEdge. */ + node: Product +} + +/** The set of valid sort keys for the ProductImage query. */ +export enum ProductImageSortKeys { + /** Sort by the `created_at` value. */ + CreatedAt = 'CREATED_AT', + /** Sort by the `position` value. */ + Position = 'POSITION', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** The set of valid sort keys for the ProductMedia query. */ +export enum ProductMediaSortKeys { + /** Sort by the `position` value. */ + Position = 'POSITION', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** + * Product property names like "Size", "Color", and "Material" that the customers can select. + * Variants are selected based on permutations of these options. + * 255 characters limit each. + */ +export type ProductOption = Node & { + __typename?: 'ProductOption' + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The product option’s name. */ + name: Scalars['String'] + /** The corresponding value to the product option name. */ + values: Array<Scalars['String']> +} + +/** The price range of the product. */ +export type ProductPriceRange = { + __typename?: 'ProductPriceRange' + /** The highest variant's price. */ + maxVariantPrice: MoneyV2 + /** The lowest variant's price. */ + minVariantPrice: MoneyV2 +} + +/** An auto-generated type for paginating through multiple ProductPriceRanges. */ +export type ProductPriceRangeConnection = { + __typename?: 'ProductPriceRangeConnection' + /** A list of edges. */ + edges: Array<ProductPriceRangeEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one ProductPriceRange and a cursor during pagination. */ +export type ProductPriceRangeEdge = { + __typename?: 'ProductPriceRangeEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ProductPriceRangeEdge. */ + node: ProductPriceRange +} + +/** The set of valid sort keys for the Product query. */ +export enum ProductSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `product_type` value. */ + ProductType = 'PRODUCT_TYPE', + /** Sort by the `vendor` value. */ + Vendor = 'VENDOR', + /** Sort by the `updated_at` value. */ + UpdatedAt = 'UPDATED_AT', + /** Sort by the `created_at` value. */ + CreatedAt = 'CREATED_AT', + /** Sort by the `best_selling` value. */ + BestSelling = 'BEST_SELLING', + /** Sort by the `price` value. */ + Price = 'PRICE', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariant = Node & + HasMetafields & { + __typename?: 'ProductVariant' + /** + * Indicates if the product variant is in stock. + * @deprecated Use `availableForSale` instead + */ + available?: Maybe<Scalars['Boolean']> + /** Indicates if the product variant is available for sale. */ + availableForSale: Scalars['Boolean'] + /** + * The compare at price of the variant. This can be used to mark a variant as on sale, when `compareAtPrice` is higher than `price`. + * @deprecated Use `compareAtPriceV2` instead + */ + compareAtPrice?: Maybe<Scalars['Money']> + /** The compare at price of the variant. This can be used to mark a variant as on sale, when `compareAtPriceV2` is higher than `priceV2`. */ + compareAtPriceV2?: Maybe<MoneyV2> + /** Whether a product is out of stock but still available for purchase (used for backorders). */ + currentlyNotInStock: Scalars['Boolean'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** Image associated with the product variant. This field falls back to the product image if no image is available. */ + image?: Maybe<Image> + /** The metafield associated with the resource. */ + metafield?: Maybe<Metafield> + /** A paginated list of metafields associated with the resource. */ + metafields: MetafieldConnection + /** List of prices and compare-at prices in the presentment currencies for this shop. */ + presentmentPrices: ProductVariantPricePairConnection + /** List of unit prices in the presentment currencies for this shop. */ + presentmentUnitPrices: MoneyV2Connection + /** + * The product variant’s price. + * @deprecated Use `priceV2` instead + */ + price: Scalars['Money'] + /** The product variant’s price. */ + priceV2: MoneyV2 + /** The product object that the product variant belongs to. */ + product: Product + /** The total sellable quantity of the variant for online sales channels. */ + quantityAvailable?: Maybe<Scalars['Int']> + /** Whether a customer needs to provide a shipping address when placing an order for the product variant. */ + requiresShipping: Scalars['Boolean'] + /** List of product options applied to the variant. */ + selectedOptions: Array<SelectedOption> + /** The SKU (stock keeping unit) associated with the variant. */ + sku?: Maybe<Scalars['String']> + /** The product variant’s title. */ + title: Scalars['String'] + /** The unit price value for the variant based on the variant's measurement. */ + unitPrice?: Maybe<MoneyV2> + /** The unit price measurement for the variant. */ + unitPriceMeasurement?: Maybe<UnitPriceMeasurement> + /** The weight of the product variant in the unit system specified with `weight_unit`. */ + weight?: Maybe<Scalars['Float']> + /** Unit of measurement for weight. */ + weightUnit: WeightUnit + } + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariantImageArgs = { + maxWidth?: Maybe<Scalars['Int']> + maxHeight?: Maybe<Scalars['Int']> + crop?: Maybe<CropRegion> + scale?: Maybe<Scalars['Int']> +} + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariantMetafieldArgs = { + namespace: Scalars['String'] + key: Scalars['String'] +} + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariantMetafieldsArgs = { + namespace?: Maybe<Scalars['String']> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariantPresentmentPricesArgs = { + presentmentCurrencies?: Maybe<Array<CurrencyCode>> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** A product variant represents a different version of a product, such as differing sizes or differing colors. */ +export type ProductVariantPresentmentUnitPricesArgs = { + presentmentCurrencies?: Maybe<Array<CurrencyCode>> + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> +} + +/** An auto-generated type for paginating through multiple ProductVariants. */ +export type ProductVariantConnection = { + __typename?: 'ProductVariantConnection' + /** A list of edges. */ + edges: Array<ProductVariantEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one ProductVariant and a cursor during pagination. */ +export type ProductVariantEdge = { + __typename?: 'ProductVariantEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ProductVariantEdge. */ + node: ProductVariant +} + +/** The compare-at price and price of a variant sharing a currency. */ +export type ProductVariantPricePair = { + __typename?: 'ProductVariantPricePair' + /** The compare-at price of the variant with associated currency. */ + compareAtPrice?: Maybe<MoneyV2> + /** The price of the variant with associated currency. */ + price: MoneyV2 +} + +/** An auto-generated type for paginating through multiple ProductVariantPricePairs. */ +export type ProductVariantPricePairConnection = { + __typename?: 'ProductVariantPricePairConnection' + /** A list of edges. */ + edges: Array<ProductVariantPricePairEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one ProductVariantPricePair and a cursor during pagination. */ +export type ProductVariantPricePairEdge = { + __typename?: 'ProductVariantPricePairEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of ProductVariantPricePairEdge. */ + node: ProductVariantPricePair +} + +/** The set of valid sort keys for the ProductVariant query. */ +export enum ProductVariantSortKeys { + /** Sort by the `title` value. */ + Title = 'TITLE', + /** Sort by the `sku` value. */ + Sku = 'SKU', + /** Sort by the `position` value. */ + Position = 'POSITION', + /** Sort by the `id` value. */ + Id = 'ID', + /** + * During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + * results by relevance to the search term(s). When no search query is specified, this sort key is not + * deterministic and should not be used. + */ + Relevance = 'RELEVANCE', +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRoot = { + __typename?: 'QueryRoot' + /** List of the shop's articles. */ + articles: ArticleConnection + /** Find a blog by its handle. */ + blogByHandle?: Maybe<Blog> + /** List of the shop's blogs. */ + blogs: BlogConnection + /** Find a collection by its handle. */ + collectionByHandle?: Maybe<Collection> + /** List of the shop’s collections. */ + collections: CollectionConnection + /** Find a customer by its access token. */ + customer?: Maybe<Customer> + node?: Maybe<Node> + nodes: Array<Maybe<Node>> + /** Find a page by its handle. */ + pageByHandle?: Maybe<Page> + /** List of the shop's pages. */ + pages: PageConnection + /** Find a product by its handle. */ + productByHandle?: Maybe<Product> + /** + * Find recommended products related to a given `product_id`. + * To learn more about how recommendations are generated, see + * [*Showing product recommendations on product pages*](https://help.shopify.com/themes/development/recommended-products). + */ + productRecommendations?: Maybe<Array<Product>> + /** + * Tags added to products. + * Additional access scope required: unauthenticated_read_product_tags. + */ + productTags: StringConnection + /** List of product types for the shop's products that are published to your app. */ + productTypes: StringConnection + /** List of the shop’s products. */ + products: ProductConnection + /** The list of public Storefront API versions, including supported, release candidate and unstable versions. */ + publicApiVersions: Array<ApiVersion> + /** The shop associated with the storefront access token. */ + shop: Shop +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootArticlesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ArticleSortKeys> + query?: Maybe<Scalars['String']> +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootBlogByHandleArgs = { + handle: Scalars['String'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootBlogsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<BlogSortKeys> + query?: Maybe<Scalars['String']> +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootCollectionByHandleArgs = { + handle: Scalars['String'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootCollectionsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<CollectionSortKeys> + query?: Maybe<Scalars['String']> +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootCustomerArgs = { + customerAccessToken: Scalars['String'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootNodeArgs = { + id: Scalars['ID'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootNodesArgs = { + ids: Array<Scalars['ID']> +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootPageByHandleArgs = { + handle: Scalars['String'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootPagesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<PageSortKeys> + query?: Maybe<Scalars['String']> +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootProductByHandleArgs = { + handle: Scalars['String'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootProductRecommendationsArgs = { + productId: Scalars['ID'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootProductTagsArgs = { + first: Scalars['Int'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootProductTypesArgs = { + first: Scalars['Int'] +} + +/** The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. */ +export type QueryRootProductsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductSortKeys> + query?: Maybe<Scalars['String']> +} + +/** SEO information. */ +export type Seo = { + __typename?: 'SEO' + /** The meta description. */ + description?: Maybe<Scalars['String']> + /** The SEO title. */ + title?: Maybe<Scalars['String']> +} + +/** + * Script discount applications capture the intentions of a discount that + * was created by a Shopify Script. + */ +export type ScriptDiscountApplication = DiscountApplication & { + __typename?: 'ScriptDiscountApplication' + /** The method by which the discount's value is allocated to its entitled items. */ + allocationMethod: DiscountApplicationAllocationMethod + /** + * The description of the application as defined by the Script. + * @deprecated Use `title` instead + */ + description: Scalars['String'] + /** Which lines of targetType that the discount is allocated over. */ + targetSelection: DiscountApplicationTargetSelection + /** The type of line that the discount is applicable towards. */ + targetType: DiscountApplicationTargetType + /** The title of the application as defined by the Script. */ + title: Scalars['String'] + /** The value of the discount application. */ + value: PricingValue +} + +/** + * Properties used by customers to select a product variant. + * Products can have multiple options, like different sizes or colors. + */ +export type SelectedOption = { + __typename?: 'SelectedOption' + /** The product option’s name. */ + name: Scalars['String'] + /** The product option’s value. */ + value: Scalars['String'] +} + +/** Specifies the input fields required for a selected option. */ +export type SelectedOptionInput = { + /** The product option’s name. */ + name: Scalars['String'] + /** The product option’s value. */ + value: Scalars['String'] +} + +/** A shipping rate to be applied to a checkout. */ +export type ShippingRate = { + __typename?: 'ShippingRate' + /** Human-readable unique identifier for this shipping rate. */ + handle: Scalars['String'] + /** + * Price of this shipping rate. + * @deprecated Use `priceV2` instead + */ + price: Scalars['Money'] + /** Price of this shipping rate. */ + priceV2: MoneyV2 + /** Title of this shipping rate. */ + title: Scalars['String'] +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type Shop = { + __typename?: 'Shop' + /** + * List of the shop' articles. + * @deprecated Use `QueryRoot.articles` instead. + */ + articles: ArticleConnection + /** + * List of the shop' blogs. + * @deprecated Use `QueryRoot.blogs` instead. + */ + blogs: BlogConnection + /** + * Find a collection by its handle. + * @deprecated Use `QueryRoot.collectionByHandle` instead. + */ + collectionByHandle?: Maybe<Collection> + /** + * List of the shop’s collections. + * @deprecated Use `QueryRoot.collections` instead. + */ + collections: CollectionConnection + /** + * The three-letter code for the currency that the shop accepts. + * @deprecated Use `paymentSettings` instead + */ + currencyCode: CurrencyCode + /** A description of the shop. */ + description?: Maybe<Scalars['String']> + /** A string representing the way currency is formatted when the currency isn’t specified. */ + moneyFormat: Scalars['String'] + /** The shop’s name. */ + name: Scalars['String'] + /** Settings related to payments. */ + paymentSettings: PaymentSettings + /** The shop’s primary domain. */ + primaryDomain: Domain + /** The shop’s privacy policy. */ + privacyPolicy?: Maybe<ShopPolicy> + /** + * Find a product by its handle. + * @deprecated Use `QueryRoot.productByHandle` instead. + */ + productByHandle?: Maybe<Product> + /** + * A list of tags that have been added to products. + * Additional access scope required: unauthenticated_read_product_tags. + * @deprecated Use `QueryRoot.productTags` instead. + */ + productTags: StringConnection + /** + * List of the shop’s product types. + * @deprecated Use `QueryRoot.productTypes` instead. + */ + productTypes: StringConnection + /** + * List of the shop’s products. + * @deprecated Use `QueryRoot.products` instead. + */ + products: ProductConnection + /** The shop’s refund policy. */ + refundPolicy?: Maybe<ShopPolicy> + /** The shop’s shipping policy. */ + shippingPolicy?: Maybe<ShopPolicy> + /** Countries that the shop ships to. */ + shipsToCountries: Array<CountryCode> + /** + * The shop’s Shopify Payments account id. + * @deprecated Use `paymentSettings` instead + */ + shopifyPaymentsAccountId?: Maybe<Scalars['String']> + /** The shop’s terms of service. */ + termsOfService?: Maybe<ShopPolicy> +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopArticlesArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ArticleSortKeys> + query?: Maybe<Scalars['String']> +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopBlogsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<BlogSortKeys> + query?: Maybe<Scalars['String']> +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopCollectionByHandleArgs = { + handle: Scalars['String'] +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopCollectionsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<CollectionSortKeys> + query?: Maybe<Scalars['String']> +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopProductByHandleArgs = { + handle: Scalars['String'] +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopProductTagsArgs = { + first: Scalars['Int'] +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopProductTypesArgs = { + first: Scalars['Int'] +} + +/** Shop represents a collection of the general settings and information about the shop. */ +export type ShopProductsArgs = { + first?: Maybe<Scalars['Int']> + after?: Maybe<Scalars['String']> + last?: Maybe<Scalars['Int']> + before?: Maybe<Scalars['String']> + reverse?: Maybe<Scalars['Boolean']> + sortKey?: Maybe<ProductSortKeys> + query?: Maybe<Scalars['String']> +} + +/** Policy that a merchant has configured for their store, such as their refund or privacy policy. */ +export type ShopPolicy = Node & { + __typename?: 'ShopPolicy' + /** Policy text, maximum size of 64kb. */ + body: Scalars['String'] + /** Policy’s handle. */ + handle: Scalars['String'] + /** Globally unique identifier. */ + id: Scalars['ID'] + /** Policy’s title. */ + title: Scalars['String'] + /** Public URL to the policy. */ + url: Scalars['URL'] +} + +/** An auto-generated type for paginating through multiple Strings. */ +export type StringConnection = { + __typename?: 'StringConnection' + /** A list of edges. */ + edges: Array<StringEdge> + /** Information to aid in pagination. */ + pageInfo: PageInfo +} + +/** An auto-generated type which holds one String and a cursor during pagination. */ +export type StringEdge = { + __typename?: 'StringEdge' + /** A cursor for use in pagination. */ + cursor: Scalars['String'] + /** The item at the end of StringEdge. */ + node: Scalars['String'] +} + +/** + * Specifies the fields required to complete a checkout with + * a tokenized payment. + */ +export type TokenizedPaymentInput = { + /** The amount of the payment. */ + amount: Scalars['Money'] + /** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */ + idempotencyKey: Scalars['String'] + /** The billing address for the payment. */ + billingAddress: MailingAddressInput + /** The type of payment token. */ + type: Scalars['String'] + /** A simple string or JSON containing the required payment data for the tokenized payment. */ + paymentData: Scalars['String'] + /** Executes the payment in test mode if possible. Defaults to `false`. */ + test?: Maybe<Scalars['Boolean']> + /** Public Hash Key used for AndroidPay payments only. */ + identifier?: Maybe<Scalars['String']> +} + +/** + * Specifies the fields required to complete a checkout with + * a tokenized payment. + */ +export type TokenizedPaymentInputV2 = { + /** The amount and currency of the payment. */ + paymentAmount: MoneyInput + /** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */ + idempotencyKey: Scalars['String'] + /** The billing address for the payment. */ + billingAddress: MailingAddressInput + /** A simple string or JSON containing the required payment data for the tokenized payment. */ + paymentData: Scalars['String'] + /** Whether to execute the payment in test mode, if possible. Test mode is not supported in production stores. Defaults to `false`. */ + test?: Maybe<Scalars['Boolean']> + /** Public Hash Key used for AndroidPay payments only. */ + identifier?: Maybe<Scalars['String']> + /** The type of payment token. */ + type: Scalars['String'] +} + +/** + * Specifies the fields required to complete a checkout with + * a tokenized payment. + */ +export type TokenizedPaymentInputV3 = { + /** The amount and currency of the payment. */ + paymentAmount: MoneyInput + /** A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. */ + idempotencyKey: Scalars['String'] + /** The billing address for the payment. */ + billingAddress: MailingAddressInput + /** A simple string or JSON containing the required payment data for the tokenized payment. */ + paymentData: Scalars['String'] + /** Whether to execute the payment in test mode, if possible. Test mode is not supported in production stores. Defaults to `false`. */ + test?: Maybe<Scalars['Boolean']> + /** Public Hash Key used for AndroidPay payments only. */ + identifier?: Maybe<Scalars['String']> + /** The type of payment token. */ + type: PaymentTokenType +} + +/** An object representing exchange of money for a product or service. */ +export type Transaction = { + __typename?: 'Transaction' + /** + * The amount of money that the transaction was for. + * @deprecated Use `amountV2` instead + */ + amount: Scalars['Money'] + /** The amount of money that the transaction was for. */ + amountV2: MoneyV2 + /** The kind of the transaction. */ + kind: TransactionKind + /** + * The status of the transaction. + * @deprecated Use `statusV2` instead + */ + status: TransactionStatus + /** The status of the transaction. */ + statusV2?: Maybe<TransactionStatus> + /** Whether the transaction was done in test mode or not. */ + test: Scalars['Boolean'] +} + +export enum TransactionKind { + Sale = 'SALE', + Capture = 'CAPTURE', + Authorization = 'AUTHORIZATION', + EmvAuthorization = 'EMV_AUTHORIZATION', + Change = 'CHANGE', +} + +export enum TransactionStatus { + Pending = 'PENDING', + Success = 'SUCCESS', + Failure = 'FAILURE', + Error = 'ERROR', +} + +/** The measurement used to calculate a unit price for a product variant (e.g. $9.99 / 100ml). */ +export type UnitPriceMeasurement = { + __typename?: 'UnitPriceMeasurement' + /** The type of unit of measurement for the unit price measurement. */ + measuredType?: Maybe<UnitPriceMeasurementMeasuredType> + /** The quantity unit for the unit price measurement. */ + quantityUnit?: Maybe<UnitPriceMeasurementMeasuredUnit> + /** The quantity value for the unit price measurement. */ + quantityValue: Scalars['Float'] + /** The reference unit for the unit price measurement. */ + referenceUnit?: Maybe<UnitPriceMeasurementMeasuredUnit> + /** The reference value for the unit price measurement. */ + referenceValue: Scalars['Int'] +} + +/** The accepted types of unit of measurement. */ +export enum UnitPriceMeasurementMeasuredType { + /** Unit of measurements representing volumes. */ + Volume = 'VOLUME', + /** Unit of measurements representing weights. */ + Weight = 'WEIGHT', + /** Unit of measurements representing lengths. */ + Length = 'LENGTH', + /** Unit of measurements representing areas. */ + Area = 'AREA', +} + +/** The valid units of measurement for a unit price measurement. */ +export enum UnitPriceMeasurementMeasuredUnit { + /** 1000 milliliters equals 1 liter. */ + Ml = 'ML', + /** 100 centiliters equals 1 liter. */ + Cl = 'CL', + /** Metric system unit of volume. */ + L = 'L', + /** 1 cubic meter equals 1000 liters. */ + M3 = 'M3', + /** 1000 milligrams equals 1 gram. */ + Mg = 'MG', + /** Metric system unit of weight. */ + G = 'G', + /** 1 kilogram equals 1000 grams. */ + Kg = 'KG', + /** 1000 millimeters equals 1 meter. */ + Mm = 'MM', + /** 100 centimeters equals 1 meter. */ + Cm = 'CM', + /** Metric system unit of length. */ + M = 'M', + /** Metric system unit of area. */ + M2 = 'M2', +} + +/** Represents an error in the input of a mutation. */ +export type UserError = DisplayableError & { + __typename?: 'UserError' + /** Path to the input field which caused the error. */ + field?: Maybe<Array<Scalars['String']>> + /** The error message. */ + message: Scalars['String'] +} + +/** Represents a Shopify hosted video. */ +export type Video = Node & + Media & { + __typename?: 'Video' + /** A word or phrase to share the nature or contents of a media. */ + alt?: Maybe<Scalars['String']> + /** Globally unique identifier. */ + id: Scalars['ID'] + /** The media content type. */ + mediaContentType: MediaContentType + /** The preview image for the media. */ + previewImage?: Maybe<Image> + /** The sources for a video. */ + sources: Array<VideoSource> + } + +/** Represents a source for a Shopify hosted video. */ +export type VideoSource = { + __typename?: 'VideoSource' + /** The format of the video source. */ + format: Scalars['String'] + /** The height of the video. */ + height: Scalars['Int'] + /** The video MIME type. */ + mimeType: Scalars['String'] + /** The URL of the video. */ + url: Scalars['String'] + /** The width of the video. */ + width: Scalars['Int'] +} + +/** Units of measurement for weight. */ +export enum WeightUnit { + /** 1 kilogram equals 1000 grams. */ + Kilograms = 'KILOGRAMS', + /** Metric system unit of mass. */ + Grams = 'GRAMS', + /** 1 pound equals 16 ounces. */ + Pounds = 'POUNDS', + /** Imperial system unit of mass. */ + Ounces = 'OUNCES', +} + +export type Unnamed_1_QueryVariables = Exact<{ + first: Scalars['Int'] +}> + +export type Unnamed_1_Query = { __typename?: 'QueryRoot' } & { + pages: { __typename?: 'PageConnection' } & { + edges: Array< + { __typename?: 'PageEdge' } & { + node: { __typename?: 'Page' } & Pick< + Page, + 'id' | 'title' | 'handle' | 'body' | 'bodySummary' | 'url' + > + } + > + } +} diff --git a/framework/swell/schema.graphql b/framework/swell/schema.graphql new file mode 100644 index 000000000..822e6007e --- /dev/null +++ b/framework/swell/schema.graphql @@ -0,0 +1,9631 @@ +schema { + query: QueryRoot + mutation: Mutation +} + +""" +Marks an element of a GraphQL schema as having restricted access. +""" +directive @accessRestricted( + """ + Explains the reason around this restriction + """ + reason: String = null +) on FIELD_DEFINITION | OBJECT + +""" +A version of the API. +""" +type ApiVersion { + """ + The human-readable name of the version. + """ + displayName: String! + + """ + The unique identifier of an ApiVersion. All supported API versions have a date-based (YYYY-MM) or `unstable` handle. + """ + handle: String! + + """ + Whether the version is supported by Shopify. + """ + supported: Boolean! +} + +""" +Details about the gift card used on the checkout. +""" +type AppliedGiftCard implements Node { + """ + The amount that was taken from the gift card by applying it. + """ + amountUsed: Money! @deprecated(reason: "Use `amountUsedV2` instead") + + """ + The amount that was taken from the gift card by applying it. + """ + amountUsedV2: MoneyV2! + + """ + The amount left on the gift card. + """ + balance: Money! @deprecated(reason: "Use `balanceV2` instead") + + """ + The amount left on the gift card. + """ + balanceV2: MoneyV2! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The last characters of the gift card. + """ + lastCharacters: String! + + """ + The amount that was applied to the checkout in its currency. + """ + presentmentAmountUsed: MoneyV2! +} + +""" +An article in an online store blog. +""" +type Article implements Node { + """ + The article's author. + """ + author: ArticleAuthor! @deprecated(reason: "Use `authorV2` instead") + + """ + The article's author. + """ + authorV2: ArticleAuthor + + """ + The blog that the article belongs to. + """ + blog: Blog! + + """ + List of comments posted on the article. + """ + comments( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): CommentConnection! + + """ + Stripped content of the article, single line with HTML tags removed. + """ + content( + """ + Truncates string after the given length. + """ + truncateAt: Int + ): String! + + """ + The content of the article, complete with HTML formatting. + """ + contentHtml: HTML! + + """ + Stripped excerpt of the article, single line with HTML tags removed. + """ + excerpt( + """ + Truncates string after the given length. + """ + truncateAt: Int + ): String + + """ + The excerpt of the article, complete with HTML formatting. + """ + excerptHtml: HTML + + """ + A human-friendly unique string for the Article automatically generated from its title. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The image associated with the article. + """ + image( + """ + Image width in pixels between 1 and 2048. This argument is deprecated: Use `maxWidth` on `Image.transformedSrc` instead. + """ + maxWidth: Int + + """ + Image height in pixels between 1 and 2048. This argument is deprecated: Use `maxHeight` on `Image.transformedSrc` instead. + """ + maxHeight: Int + + """ + Crops the image according to the specified region. This argument is deprecated: Use `crop` on `Image.transformedSrc` instead. + """ + crop: CropRegion + + """ + Image size multiplier for high-resolution retina displays. Must be between 1 and 3. This argument is deprecated: Use `scale` on `Image.transformedSrc` instead. + """ + scale: Int = 1 + ): Image + + """ + The date and time when the article was published. + """ + publishedAt: DateTime! + + """ + The article’s SEO information. + """ + seo: SEO + + """ + A categorization that a article can be tagged with. + """ + tags: [String!]! + + """ + The article’s name. + """ + title: String! + + """ + The url pointing to the article accessible from the web. + """ + url: URL! +} + +""" +The author of an article. +""" +type ArticleAuthor { + """ + The author's bio. + """ + bio: String + + """ + The author’s email. + """ + email: String! + + """ + The author's first name. + """ + firstName: String! + + """ + The author's last name. + """ + lastName: String! + + """ + The author's full name. + """ + name: String! +} + +""" +An auto-generated type for paginating through multiple Articles. +""" +type ArticleConnection { + """ + A list of edges. + """ + edges: [ArticleEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Article and a cursor during pagination. +""" +type ArticleEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ArticleEdge. + """ + node: Article! +} + +""" +The set of valid sort keys for the Article query. +""" +enum ArticleSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `blog_title` value. + """ + BLOG_TITLE + + """ + Sort by the `author` value. + """ + AUTHOR + + """ + Sort by the `updated_at` value. + """ + UPDATED_AT + + """ + Sort by the `published_at` value. + """ + PUBLISHED_AT + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +Represents a generic custom attribute. +""" +type Attribute { + """ + Key or name of the attribute. + """ + key: String! + + """ + Value of the attribute. + """ + value: String +} + +""" +Specifies the input fields required for an attribute. +""" +input AttributeInput { + """ + Key or name of the attribute. + """ + key: String! + + """ + Value of the attribute. + """ + value: String! +} + +""" +Automatic discount applications capture the intentions of a discount that was automatically applied. +""" +type AutomaticDiscountApplication implements DiscountApplication { + """ + The method by which the discount's value is allocated to its entitled items. + """ + allocationMethod: DiscountApplicationAllocationMethod! + + """ + Which lines of targetType that the discount is allocated over. + """ + targetSelection: DiscountApplicationTargetSelection! + + """ + The type of line that the discount is applicable towards. + """ + targetType: DiscountApplicationTargetType! + + """ + The title of the application. + """ + title: String! + + """ + The value of the discount application. + """ + value: PricingValue! +} + +""" +A collection of available shipping rates for a checkout. +""" +type AvailableShippingRates { + """ + Whether or not the shipping rates are ready. + The `shippingRates` field is `null` when this value is `false`. + This field should be polled until its value becomes `true`. + """ + ready: Boolean! + + """ + The fetched shipping rates. `null` until the `ready` field is `true`. + """ + shippingRates: [ShippingRate!] +} + +""" +An online store blog. +""" +type Blog implements Node { + """ + Find an article by its handle. + """ + articleByHandle( + """ + The handle of the article. + """ + handle: String! + ): Article + + """ + List of the blog's articles. + """ + articles( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ArticleSortKeys = ID + + """ + Supported filter parameters: + - `author` + - `blog_title` + - `created_at` + - `tag` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): ArticleConnection! + + """ + The authors who have contributed to the blog. + """ + authors: [ArticleAuthor!]! + + """ + A human-friendly unique string for the Blog automatically generated from its title. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The blog's SEO information. + """ + seo: SEO + + """ + The blogs’s title. + """ + title: String! + + """ + The url pointing to the blog accessible from the web. + """ + url: URL! +} + +""" +An auto-generated type for paginating through multiple Blogs. +""" +type BlogConnection { + """ + A list of edges. + """ + edges: [BlogEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Blog and a cursor during pagination. +""" +type BlogEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of BlogEdge. + """ + node: Blog! +} + +""" +The set of valid sort keys for the Blog query. +""" +enum BlogSortKeys { + """ + Sort by the `handle` value. + """ + HANDLE + + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +Card brand, such as Visa or Mastercard, which can be used for payments. +""" +enum CardBrand { + """ + Visa + """ + VISA + + """ + Mastercard + """ + MASTERCARD + + """ + Discover + """ + DISCOVER + + """ + American Express + """ + AMERICAN_EXPRESS + + """ + Diners Club + """ + DINERS_CLUB + + """ + JCB + """ + JCB +} + +""" +A container for all the information required to checkout items and pay. +""" +type Checkout implements Node { + """ + The gift cards used on the checkout. + """ + appliedGiftCards: [AppliedGiftCard!]! + + """ + The available shipping rates for this Checkout. + Should only be used when checkout `requiresShipping` is `true` and + the shipping address is valid. + """ + availableShippingRates: AvailableShippingRates + + """ + The date and time when the checkout was completed. + """ + completedAt: DateTime + + """ + The date and time when the checkout was created. + """ + createdAt: DateTime! + + """ + The currency code for the Checkout. + """ + currencyCode: CurrencyCode! + + """ + A list of extra information that is added to the checkout. + """ + customAttributes: [Attribute!]! + + """ + The customer associated with the checkout. + """ + customer: Customer + @deprecated( + reason: "This field will always return null. If you have an authentication token for the customer, you can use the `customer` field on the query root to retrieve it." + ) + + """ + Discounts that have been applied on the checkout. + """ + discountApplications( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): DiscountApplicationConnection! + + """ + The email attached to this checkout. + """ + email: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + A list of line item objects, each one containing information about an item in the checkout. + """ + lineItems( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): CheckoutLineItemConnection! + + """ + The sum of all the prices of all the items in the checkout. Duties, taxes, shipping and discounts excluded. + """ + lineItemsSubtotalPrice: MoneyV2! + + """ + The note associated with the checkout. + """ + note: String + + """ + The resulting order from a paid checkout. + """ + order: Order + + """ + The Order Status Page for this Checkout, null when checkout is not completed. + """ + orderStatusUrl: URL + + """ + The amount left to be paid. This is equal to the cost of the line items, taxes and shipping minus discounts and gift cards. + """ + paymentDue: Money! @deprecated(reason: "Use `paymentDueV2` instead") + + """ + The amount left to be paid. This is equal to the cost of the line items, duties, taxes and shipping minus discounts and gift cards. + """ + paymentDueV2: MoneyV2! + + """ + Whether or not the Checkout is ready and can be completed. Checkouts may + have asynchronous operations that can take time to finish. If you want + to complete a checkout or ensure all the fields are populated and up to + date, polling is required until the value is true. + """ + ready: Boolean! + + """ + States whether or not the fulfillment requires shipping. + """ + requiresShipping: Boolean! + + """ + The shipping address to where the line items will be shipped. + """ + shippingAddress: MailingAddress + + """ + The discounts that have been allocated onto the shipping line by discount applications. + """ + shippingDiscountAllocations: [DiscountAllocation!]! + + """ + Once a shipping rate is selected by the customer it is transitioned to a `shipping_line` object. + """ + shippingLine: ShippingRate + + """ + Price of the checkout before shipping and taxes. + """ + subtotalPrice: Money! @deprecated(reason: "Use `subtotalPriceV2` instead") + + """ + Price of the checkout before duties, shipping and taxes. + """ + subtotalPriceV2: MoneyV2! + + """ + Specifies if the Checkout is tax exempt. + """ + taxExempt: Boolean! + + """ + Specifies if taxes are included in the line item and shipping line prices. + """ + taxesIncluded: Boolean! + + """ + The sum of all the prices of all the items in the checkout, taxes and discounts included. + """ + totalPrice: Money! @deprecated(reason: "Use `totalPriceV2` instead") + + """ + The sum of all the prices of all the items in the checkout, duties, taxes and discounts included. + """ + totalPriceV2: MoneyV2! + + """ + The sum of all the taxes applied to the line items and shipping lines in the checkout. + """ + totalTax: Money! @deprecated(reason: "Use `totalTaxV2` instead") + + """ + The sum of all the taxes applied to the line items and shipping lines in the checkout. + """ + totalTaxV2: MoneyV2! + + """ + The date and time when the checkout was last updated. + """ + updatedAt: DateTime! + + """ + The url pointing to the checkout accessible from the web. + """ + webUrl: URL! +} + +""" +Specifies the fields required to update a checkout's attributes. +""" +input CheckoutAttributesUpdateInput { + """ + The text of an optional note that a shop owner can attach to the checkout. + """ + note: String + + """ + A list of extra information that is added to the checkout. + """ + customAttributes: [AttributeInput!] + + """ + Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + The required attributes are city, province, and country. + Full validation of the addresses is still done at complete time. + """ + allowPartialAddresses: Boolean +} + +""" +Return type for `checkoutAttributesUpdate` mutation. +""" +type CheckoutAttributesUpdatePayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Specifies the fields required to update a checkout's attributes. +""" +input CheckoutAttributesUpdateV2Input { + """ + The text of an optional note that a shop owner can attach to the checkout. + """ + note: String + + """ + A list of extra information that is added to the checkout. + """ + customAttributes: [AttributeInput!] + + """ + Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + The required attributes are city, province, and country. + Full validation of the addresses is still done at complete time. + """ + allowPartialAddresses: Boolean +} + +""" +Return type for `checkoutAttributesUpdateV2` mutation. +""" +type CheckoutAttributesUpdateV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteFree` mutation. +""" +type CheckoutCompleteFreePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteWithCreditCard` mutation. +""" +type CheckoutCompleteWithCreditCardPayload { + """ + The checkout on which the payment was applied. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + A representation of the attempted payment. + """ + payment: Payment + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteWithCreditCardV2` mutation. +""" +type CheckoutCompleteWithCreditCardV2Payload { + """ + The checkout on which the payment was applied. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + A representation of the attempted payment. + """ + payment: Payment + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteWithTokenizedPayment` mutation. +""" +type CheckoutCompleteWithTokenizedPaymentPayload { + """ + The checkout on which the payment was applied. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + A representation of the attempted payment. + """ + payment: Payment + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteWithTokenizedPaymentV2` mutation. +""" +type CheckoutCompleteWithTokenizedPaymentV2Payload { + """ + The checkout on which the payment was applied. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + A representation of the attempted payment. + """ + payment: Payment + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCompleteWithTokenizedPaymentV3` mutation. +""" +type CheckoutCompleteWithTokenizedPaymentV3Payload { + """ + The checkout on which the payment was applied. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + A representation of the attempted payment. + """ + payment: Payment + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Specifies the fields required to create a checkout. +""" +input CheckoutCreateInput { + """ + The email with which the customer wants to checkout. + """ + email: String + + """ + A list of line item objects, each one containing information about an item in the checkout. + """ + lineItems: [CheckoutLineItemInput!] + + """ + The shipping address to where the line items will be shipped. + """ + shippingAddress: MailingAddressInput + + """ + The text of an optional note that a shop owner can attach to the checkout. + """ + note: String + + """ + A list of extra information that is added to the checkout. + """ + customAttributes: [AttributeInput!] + + """ + Allows setting partial addresses on a Checkout, skipping the full validation of attributes. + The required attributes are city, province, and country. + Full validation of addresses is still done at complete time. + """ + allowPartialAddresses: Boolean + + """ + The three-letter currency code of one of the shop's enabled presentment currencies. + Including this field creates a checkout in the specified currency. By default, new + checkouts are created in the shop's primary currency. + """ + presentmentCurrencyCode: CurrencyCode +} + +""" +Return type for `checkoutCreate` mutation. +""" +type CheckoutCreatePayload { + """ + The new checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCustomerAssociate` mutation. +""" +type CheckoutCustomerAssociatePayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + The associated customer object. + """ + customer: Customer + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! +} + +""" +Return type for `checkoutCustomerAssociateV2` mutation. +""" +type CheckoutCustomerAssociateV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + The associated customer object. + """ + customer: Customer + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCustomerDisassociate` mutation. +""" +type CheckoutCustomerDisassociatePayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutCustomerDisassociateV2` mutation. +""" +type CheckoutCustomerDisassociateV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutDiscountCodeApply` mutation. +""" +type CheckoutDiscountCodeApplyPayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutDiscountCodeApplyV2` mutation. +""" +type CheckoutDiscountCodeApplyV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutDiscountCodeRemove` mutation. +""" +type CheckoutDiscountCodeRemovePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutEmailUpdate` mutation. +""" +type CheckoutEmailUpdatePayload { + """ + The checkout object with the updated email. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutEmailUpdateV2` mutation. +""" +type CheckoutEmailUpdateV2Payload { + """ + The checkout object with the updated email. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Possible error codes that could be returned by CheckoutUserError. +""" +enum CheckoutErrorCode { + """ + Input value is blank. + """ + BLANK + + """ + Input value is invalid. + """ + INVALID + + """ + Input value is too long. + """ + TOO_LONG + + """ + Input value is not present. + """ + PRESENT + + """ + Input value should be less than maximum allowed value. + """ + LESS_THAN + + """ + Input value should be greater than or equal to minimum allowed value. + """ + GREATER_THAN_OR_EQUAL_TO + + """ + Input value should be less or equal to maximum allowed value. + """ + LESS_THAN_OR_EQUAL_TO + + """ + Checkout is already completed. + """ + ALREADY_COMPLETED + + """ + Checkout is locked. + """ + LOCKED + + """ + Input value is not supported. + """ + NOT_SUPPORTED + + """ + Input email contains an invalid domain name. + """ + BAD_DOMAIN + + """ + Input Zip is invalid for country provided. + """ + INVALID_FOR_COUNTRY + + """ + Input Zip is invalid for country and province provided. + """ + INVALID_FOR_COUNTRY_AND_PROVINCE + + """ + Invalid state in country. + """ + INVALID_STATE_IN_COUNTRY + + """ + Invalid province in country. + """ + INVALID_PROVINCE_IN_COUNTRY + + """ + Invalid region in country. + """ + INVALID_REGION_IN_COUNTRY + + """ + Shipping rate expired. + """ + SHIPPING_RATE_EXPIRED + + """ + Gift card cannot be applied to a checkout that contains a gift card. + """ + GIFT_CARD_UNUSABLE + + """ + Gift card is disabled. + """ + GIFT_CARD_DISABLED + + """ + Gift card code is invalid. + """ + GIFT_CARD_CODE_INVALID + + """ + Gift card has already been applied. + """ + GIFT_CARD_ALREADY_APPLIED + + """ + Gift card currency does not match checkout currency. + """ + GIFT_CARD_CURRENCY_MISMATCH + + """ + Gift card is expired. + """ + GIFT_CARD_EXPIRED + + """ + Gift card has no funds left. + """ + GIFT_CARD_DEPLETED + + """ + Gift card was not found. + """ + GIFT_CARD_NOT_FOUND + + """ + Cart does not meet discount requirements notice. + """ + CART_DOES_NOT_MEET_DISCOUNT_REQUIREMENTS_NOTICE + + """ + Discount expired. + """ + DISCOUNT_EXPIRED + + """ + Discount disabled. + """ + DISCOUNT_DISABLED + + """ + Discount limit reached. + """ + DISCOUNT_LIMIT_REACHED + + """ + Discount not found. + """ + DISCOUNT_NOT_FOUND + + """ + Customer already used once per customer discount notice. + """ + CUSTOMER_ALREADY_USED_ONCE_PER_CUSTOMER_DISCOUNT_NOTICE + + """ + Checkout is already completed. + """ + EMPTY + + """ + Not enough in stock. + """ + NOT_ENOUGH_IN_STOCK + + """ + Missing payment input. + """ + MISSING_PAYMENT_INPUT + + """ + The amount of the payment does not match the value to be paid. + """ + TOTAL_PRICE_MISMATCH + + """ + Line item was not found in checkout. + """ + LINE_ITEM_NOT_FOUND + + """ + Unable to apply discount. + """ + UNABLE_TO_APPLY + + """ + Discount already applied. + """ + DISCOUNT_ALREADY_APPLIED +} + +""" +Return type for `checkoutGiftCardApply` mutation. +""" +type CheckoutGiftCardApplyPayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutGiftCardRemove` mutation. +""" +type CheckoutGiftCardRemovePayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutGiftCardRemoveV2` mutation. +""" +type CheckoutGiftCardRemoveV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutGiftCardsAppend` mutation. +""" +type CheckoutGiftCardsAppendPayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +A single line item in the checkout, grouped by variant and attributes. +""" +type CheckoutLineItem implements Node { + """ + Extra information in the form of an array of Key-Value pairs about the line item. + """ + customAttributes: [Attribute!]! + + """ + The discounts that have been allocated onto the checkout line item by discount applications. + """ + discountAllocations: [DiscountAllocation!]! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The quantity of the line item. + """ + quantity: Int! + + """ + Title of the line item. Defaults to the product's title. + """ + title: String! + + """ + Unit price of the line item. + """ + unitPrice: MoneyV2 + + """ + Product variant of the line item. + """ + variant: ProductVariant +} + +""" +An auto-generated type for paginating through multiple CheckoutLineItems. +""" +type CheckoutLineItemConnection { + """ + A list of edges. + """ + edges: [CheckoutLineItemEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one CheckoutLineItem and a cursor during pagination. +""" +type CheckoutLineItemEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of CheckoutLineItemEdge. + """ + node: CheckoutLineItem! +} + +""" +Specifies the input fields to create a line item on a checkout. +""" +input CheckoutLineItemInput { + """ + Extra information in the form of an array of Key-Value pairs about the line item. + """ + customAttributes: [AttributeInput!] + + """ + The quantity of the line item. + """ + quantity: Int! + + """ + The identifier of the product variant for the line item. + """ + variantId: ID! +} + +""" +Specifies the input fields to update a line item on the checkout. +""" +input CheckoutLineItemUpdateInput { + """ + The identifier of the line item. + """ + id: ID + + """ + The variant identifier of the line item. + """ + variantId: ID + + """ + The quantity of the line item. + """ + quantity: Int + + """ + Extra information in the form of an array of Key-Value pairs about the line item. + """ + customAttributes: [AttributeInput!] +} + +""" +Return type for `checkoutLineItemsAdd` mutation. +""" +type CheckoutLineItemsAddPayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutLineItemsRemove` mutation. +""" +type CheckoutLineItemsRemovePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutLineItemsReplace` mutation. +""" +type CheckoutLineItemsReplacePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [CheckoutUserError!]! +} + +""" +Return type for `checkoutLineItemsUpdate` mutation. +""" +type CheckoutLineItemsUpdatePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutShippingAddressUpdate` mutation. +""" +type CheckoutShippingAddressUpdatePayload { + """ + The updated checkout object. + """ + checkout: Checkout! + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutShippingAddressUpdateV2` mutation. +""" +type CheckoutShippingAddressUpdateV2Payload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Return type for `checkoutShippingLineUpdate` mutation. +""" +type CheckoutShippingLineUpdatePayload { + """ + The updated checkout object. + """ + checkout: Checkout + + """ + List of errors that occurred executing the mutation. + """ + checkoutUserErrors: [CheckoutUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `checkoutUserErrors` instead") +} + +""" +Represents an error that happens during execution of a checkout mutation. +""" +type CheckoutUserError implements DisplayableError { + """ + Error code to uniquely identify the error. + """ + code: CheckoutErrorCode + + """ + Path to the input field which caused the error. + """ + field: [String!] + + """ + The error message. + """ + message: String! +} + +""" +A collection represents a grouping of products that a shop owner can create to organize them or make their shops easier to browse. +""" +type Collection implements Node { + """ + Stripped description of the collection, single line with HTML tags removed. + """ + description( + """ + Truncates string after the given length. + """ + truncateAt: Int + ): String! + + """ + The description of the collection, complete with HTML formatting. + """ + descriptionHtml: HTML! + + """ + A human-friendly unique string for the collection automatically generated from its title. + Limit of 255 characters. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + Image associated with the collection. + """ + image( + """ + Image width in pixels between 1 and 2048. This argument is deprecated: Use `maxWidth` on `Image.transformedSrc` instead. + """ + maxWidth: Int + + """ + Image height in pixels between 1 and 2048. This argument is deprecated: Use `maxHeight` on `Image.transformedSrc` instead. + """ + maxHeight: Int + + """ + Crops the image according to the specified region. This argument is deprecated: Use `crop` on `Image.transformedSrc` instead. + """ + crop: CropRegion + + """ + Image size multiplier for high-resolution retina displays. Must be between 1 and 3. This argument is deprecated: Use `scale` on `Image.transformedSrc` instead. + """ + scale: Int = 1 + ): Image + + """ + List of products in the collection. + """ + products( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductCollectionSortKeys = COLLECTION_DEFAULT + ): ProductConnection! + + """ + The collection’s name. Limit of 255 characters. + """ + title: String! + + """ + The date and time when the collection was last modified. + """ + updatedAt: DateTime! +} + +""" +An auto-generated type for paginating through multiple Collections. +""" +type CollectionConnection { + """ + A list of edges. + """ + edges: [CollectionEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Collection and a cursor during pagination. +""" +type CollectionEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of CollectionEdge. + """ + node: Collection! +} + +""" +The set of valid sort keys for the Collection query. +""" +enum CollectionSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `updated_at` value. + """ + UPDATED_AT + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +A comment on an article. +""" +type Comment implements Node { + """ + The comment’s author. + """ + author: CommentAuthor! + + """ + Stripped content of the comment, single line with HTML tags removed. + """ + content( + """ + Truncates string after the given length. + """ + truncateAt: Int + ): String! + + """ + The content of the comment, complete with HTML formatting. + """ + contentHtml: HTML! + + """ + Globally unique identifier. + """ + id: ID! +} + +""" +The author of a comment. +""" +type CommentAuthor { + """ + The author's email. + """ + email: String! + + """ + The author’s name. + """ + name: String! +} + +""" +An auto-generated type for paginating through multiple Comments. +""" +type CommentConnection { + """ + A list of edges. + """ + edges: [CommentEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Comment and a cursor during pagination. +""" +type CommentEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of CommentEdge. + """ + node: Comment! +} + +""" +ISO 3166-1 alpha-2 country codes with some differences. +""" +enum CountryCode { + """ + Afghanistan. + """ + AF + + """ + Åland Islands. + """ + AX + + """ + Albania. + """ + AL + + """ + Algeria. + """ + DZ + + """ + Andorra. + """ + AD + + """ + Angola. + """ + AO + + """ + Anguilla. + """ + AI + + """ + Antigua & Barbuda. + """ + AG + + """ + Argentina. + """ + AR + + """ + Armenia. + """ + AM + + """ + Aruba. + """ + AW + + """ + Australia. + """ + AU + + """ + Austria. + """ + AT + + """ + Azerbaijan. + """ + AZ + + """ + Bahamas. + """ + BS + + """ + Bahrain. + """ + BH + + """ + Bangladesh. + """ + BD + + """ + Barbados. + """ + BB + + """ + Belarus. + """ + BY + + """ + Belgium. + """ + BE + + """ + Belize. + """ + BZ + + """ + Benin. + """ + BJ + + """ + Bermuda. + """ + BM + + """ + Bhutan. + """ + BT + + """ + Bolivia. + """ + BO + + """ + Bosnia & Herzegovina. + """ + BA + + """ + Botswana. + """ + BW + + """ + Bouvet Island. + """ + BV + + """ + Brazil. + """ + BR + + """ + British Indian Ocean Territory. + """ + IO + + """ + Brunei. + """ + BN + + """ + Bulgaria. + """ + BG + + """ + Burkina Faso. + """ + BF + + """ + Burundi. + """ + BI + + """ + Cambodia. + """ + KH + + """ + Canada. + """ + CA + + """ + Cape Verde. + """ + CV + + """ + Caribbean Netherlands. + """ + BQ + + """ + Cayman Islands. + """ + KY + + """ + Central African Republic. + """ + CF + + """ + Chad. + """ + TD + + """ + Chile. + """ + CL + + """ + China. + """ + CN + + """ + Christmas Island. + """ + CX + + """ + Cocos (Keeling) Islands. + """ + CC + + """ + Colombia. + """ + CO + + """ + Comoros. + """ + KM + + """ + Congo - Brazzaville. + """ + CG + + """ + Congo - Kinshasa. + """ + CD + + """ + Cook Islands. + """ + CK + + """ + Costa Rica. + """ + CR + + """ + Croatia. + """ + HR + + """ + Cuba. + """ + CU + + """ + Curaçao. + """ + CW + + """ + Cyprus. + """ + CY + + """ + Czechia. + """ + CZ + + """ + Côte d’Ivoire. + """ + CI + + """ + Denmark. + """ + DK + + """ + Djibouti. + """ + DJ + + """ + Dominica. + """ + DM + + """ + Dominican Republic. + """ + DO + + """ + Ecuador. + """ + EC + + """ + Egypt. + """ + EG + + """ + El Salvador. + """ + SV + + """ + Equatorial Guinea. + """ + GQ + + """ + Eritrea. + """ + ER + + """ + Estonia. + """ + EE + + """ + Eswatini. + """ + SZ + + """ + Ethiopia. + """ + ET + + """ + Falkland Islands. + """ + FK + + """ + Faroe Islands. + """ + FO + + """ + Fiji. + """ + FJ + + """ + Finland. + """ + FI + + """ + France. + """ + FR + + """ + French Guiana. + """ + GF + + """ + French Polynesia. + """ + PF + + """ + French Southern Territories. + """ + TF + + """ + Gabon. + """ + GA + + """ + Gambia. + """ + GM + + """ + Georgia. + """ + GE + + """ + Germany. + """ + DE + + """ + Ghana. + """ + GH + + """ + Gibraltar. + """ + GI + + """ + Greece. + """ + GR + + """ + Greenland. + """ + GL + + """ + Grenada. + """ + GD + + """ + Guadeloupe. + """ + GP + + """ + Guatemala. + """ + GT + + """ + Guernsey. + """ + GG + + """ + Guinea. + """ + GN + + """ + Guinea-Bissau. + """ + GW + + """ + Guyana. + """ + GY + + """ + Haiti. + """ + HT + + """ + Heard & McDonald Islands. + """ + HM + + """ + Vatican City. + """ + VA + + """ + Honduras. + """ + HN + + """ + Hong Kong SAR. + """ + HK + + """ + Hungary. + """ + HU + + """ + Iceland. + """ + IS + + """ + India. + """ + IN + + """ + Indonesia. + """ + ID + + """ + Iran. + """ + IR + + """ + Iraq. + """ + IQ + + """ + Ireland. + """ + IE + + """ + Isle of Man. + """ + IM + + """ + Israel. + """ + IL + + """ + Italy. + """ + IT + + """ + Jamaica. + """ + JM + + """ + Japan. + """ + JP + + """ + Jersey. + """ + JE + + """ + Jordan. + """ + JO + + """ + Kazakhstan. + """ + KZ + + """ + Kenya. + """ + KE + + """ + Kiribati. + """ + KI + + """ + North Korea. + """ + KP + + """ + Kosovo. + """ + XK + + """ + Kuwait. + """ + KW + + """ + Kyrgyzstan. + """ + KG + + """ + Laos. + """ + LA + + """ + Latvia. + """ + LV + + """ + Lebanon. + """ + LB + + """ + Lesotho. + """ + LS + + """ + Liberia. + """ + LR + + """ + Libya. + """ + LY + + """ + Liechtenstein. + """ + LI + + """ + Lithuania. + """ + LT + + """ + Luxembourg. + """ + LU + + """ + Macao SAR. + """ + MO + + """ + Madagascar. + """ + MG + + """ + Malawi. + """ + MW + + """ + Malaysia. + """ + MY + + """ + Maldives. + """ + MV + + """ + Mali. + """ + ML + + """ + Malta. + """ + MT + + """ + Martinique. + """ + MQ + + """ + Mauritania. + """ + MR + + """ + Mauritius. + """ + MU + + """ + Mayotte. + """ + YT + + """ + Mexico. + """ + MX + + """ + Moldova. + """ + MD + + """ + Monaco. + """ + MC + + """ + Mongolia. + """ + MN + + """ + Montenegro. + """ + ME + + """ + Montserrat. + """ + MS + + """ + Morocco. + """ + MA + + """ + Mozambique. + """ + MZ + + """ + Myanmar (Burma). + """ + MM + + """ + Namibia. + """ + NA + + """ + Nauru. + """ + NR + + """ + Nepal. + """ + NP + + """ + Netherlands. + """ + NL + + """ + Netherlands Antilles. + """ + AN + + """ + New Caledonia. + """ + NC + + """ + New Zealand. + """ + NZ + + """ + Nicaragua. + """ + NI + + """ + Niger. + """ + NE + + """ + Nigeria. + """ + NG + + """ + Niue. + """ + NU + + """ + Norfolk Island. + """ + NF + + """ + North Macedonia. + """ + MK + + """ + Norway. + """ + NO + + """ + Oman. + """ + OM + + """ + Pakistan. + """ + PK + + """ + Palestinian Territories. + """ + PS + + """ + Panama. + """ + PA + + """ + Papua New Guinea. + """ + PG + + """ + Paraguay. + """ + PY + + """ + Peru. + """ + PE + + """ + Philippines. + """ + PH + + """ + Pitcairn Islands. + """ + PN + + """ + Poland. + """ + PL + + """ + Portugal. + """ + PT + + """ + Qatar. + """ + QA + + """ + Cameroon. + """ + CM + + """ + Réunion. + """ + RE + + """ + Romania. + """ + RO + + """ + Russia. + """ + RU + + """ + Rwanda. + """ + RW + + """ + St. Barthélemy. + """ + BL + + """ + St. Helena. + """ + SH + + """ + St. Kitts & Nevis. + """ + KN + + """ + St. Lucia. + """ + LC + + """ + St. Martin. + """ + MF + + """ + St. Pierre & Miquelon. + """ + PM + + """ + Samoa. + """ + WS + + """ + San Marino. + """ + SM + + """ + São Tomé & Príncipe. + """ + ST + + """ + Saudi Arabia. + """ + SA + + """ + Senegal. + """ + SN + + """ + Serbia. + """ + RS + + """ + Seychelles. + """ + SC + + """ + Sierra Leone. + """ + SL + + """ + Singapore. + """ + SG + + """ + Sint Maarten. + """ + SX + + """ + Slovakia. + """ + SK + + """ + Slovenia. + """ + SI + + """ + Solomon Islands. + """ + SB + + """ + Somalia. + """ + SO + + """ + South Africa. + """ + ZA + + """ + South Georgia & South Sandwich Islands. + """ + GS + + """ + South Korea. + """ + KR + + """ + South Sudan. + """ + SS + + """ + Spain. + """ + ES + + """ + Sri Lanka. + """ + LK + + """ + St. Vincent & Grenadines. + """ + VC + + """ + Sudan. + """ + SD + + """ + Suriname. + """ + SR + + """ + Svalbard & Jan Mayen. + """ + SJ + + """ + Sweden. + """ + SE + + """ + Switzerland. + """ + CH + + """ + Syria. + """ + SY + + """ + Taiwan. + """ + TW + + """ + Tajikistan. + """ + TJ + + """ + Tanzania. + """ + TZ + + """ + Thailand. + """ + TH + + """ + Timor-Leste. + """ + TL + + """ + Togo. + """ + TG + + """ + Tokelau. + """ + TK + + """ + Tonga. + """ + TO + + """ + Trinidad & Tobago. + """ + TT + + """ + Tunisia. + """ + TN + + """ + Turkey. + """ + TR + + """ + Turkmenistan. + """ + TM + + """ + Turks & Caicos Islands. + """ + TC + + """ + Tuvalu. + """ + TV + + """ + Uganda. + """ + UG + + """ + Ukraine. + """ + UA + + """ + United Arab Emirates. + """ + AE + + """ + United Kingdom. + """ + GB + + """ + United States. + """ + US + + """ + U.S. Outlying Islands. + """ + UM + + """ + Uruguay. + """ + UY + + """ + Uzbekistan. + """ + UZ + + """ + Vanuatu. + """ + VU + + """ + Venezuela. + """ + VE + + """ + Vietnam. + """ + VN + + """ + British Virgin Islands. + """ + VG + + """ + Wallis & Futuna. + """ + WF + + """ + Western Sahara. + """ + EH + + """ + Yemen. + """ + YE + + """ + Zambia. + """ + ZM + + """ + Zimbabwe. + """ + ZW +} + +""" +Credit card information used for a payment. +""" +type CreditCard { + """ + The brand of the credit card. + """ + brand: String + + """ + The expiry month of the credit card. + """ + expiryMonth: Int + + """ + The expiry year of the credit card. + """ + expiryYear: Int + + """ + The credit card's BIN number. + """ + firstDigits: String + + """ + The first name of the card holder. + """ + firstName: String + + """ + The last 4 digits of the credit card. + """ + lastDigits: String + + """ + The last name of the card holder. + """ + lastName: String + + """ + The masked credit card number with only the last 4 digits displayed. + """ + maskedNumber: String +} + +""" +Specifies the fields required to complete a checkout with +a Shopify vaulted credit card payment. +""" +input CreditCardPaymentInput { + """ + The amount of the payment. + """ + amount: Money! + + """ + A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. + """ + idempotencyKey: String! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddressInput! + + """ + The ID returned by Shopify's Card Vault. + """ + vaultId: String! + + """ + Executes the payment in test mode if possible. Defaults to `false`. + """ + test: Boolean +} + +""" +Specifies the fields required to complete a checkout with +a Shopify vaulted credit card payment. +""" +input CreditCardPaymentInputV2 { + """ + The amount and currency of the payment. + """ + paymentAmount: MoneyInput! + + """ + A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. + """ + idempotencyKey: String! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddressInput! + + """ + The ID returned by Shopify's Card Vault. + """ + vaultId: String! + + """ + Executes the payment in test mode if possible. Defaults to `false`. + """ + test: Boolean +} + +""" +The part of the image that should remain after cropping. +""" +enum CropRegion { + """ + Keep the center of the image. + """ + CENTER + + """ + Keep the top of the image. + """ + TOP + + """ + Keep the bottom of the image. + """ + BOTTOM + + """ + Keep the left of the image. + """ + LEFT + + """ + Keep the right of the image. + """ + RIGHT +} + +""" +Currency codes. +""" +enum CurrencyCode { + """ + United States Dollars (USD). + """ + USD + + """ + Euro (EUR). + """ + EUR + + """ + United Kingdom Pounds (GBP). + """ + GBP + + """ + Canadian Dollars (CAD). + """ + CAD + + """ + Afghan Afghani (AFN). + """ + AFN + + """ + Albanian Lek (ALL). + """ + ALL + + """ + Algerian Dinar (DZD). + """ + DZD + + """ + Angolan Kwanza (AOA). + """ + AOA + + """ + Argentine Pesos (ARS). + """ + ARS + + """ + Armenian Dram (AMD). + """ + AMD + + """ + Aruban Florin (AWG). + """ + AWG + + """ + Australian Dollars (AUD). + """ + AUD + + """ + Barbadian Dollar (BBD). + """ + BBD + + """ + Azerbaijani Manat (AZN). + """ + AZN + + """ + Bangladesh Taka (BDT). + """ + BDT + + """ + Bahamian Dollar (BSD). + """ + BSD + + """ + Bahraini Dinar (BHD). + """ + BHD + + """ + Burundian Franc (BIF). + """ + BIF + + """ + Belarusian Ruble (BYN). + """ + BYN + + """ + Belarusian Ruble (BYR). + """ + BYR + + """ + Belize Dollar (BZD). + """ + BZD + + """ + Bermudian Dollar (BMD). + """ + BMD + + """ + Bhutanese Ngultrum (BTN). + """ + BTN + + """ + Bosnia and Herzegovina Convertible Mark (BAM). + """ + BAM + + """ + Brazilian Real (BRL). + """ + BRL + + """ + Bolivian Boliviano (BOB). + """ + BOB + + """ + Botswana Pula (BWP). + """ + BWP + + """ + Brunei Dollar (BND). + """ + BND + + """ + Bulgarian Lev (BGN). + """ + BGN + + """ + Burmese Kyat (MMK). + """ + MMK + + """ + Cambodian Riel. + """ + KHR + + """ + Cape Verdean escudo (CVE). + """ + CVE + + """ + Cayman Dollars (KYD). + """ + KYD + + """ + Central African CFA Franc (XAF). + """ + XAF + + """ + Chilean Peso (CLP). + """ + CLP + + """ + Chinese Yuan Renminbi (CNY). + """ + CNY + + """ + Colombian Peso (COP). + """ + COP + + """ + Comorian Franc (KMF). + """ + KMF + + """ + Congolese franc (CDF). + """ + CDF + + """ + Costa Rican Colones (CRC). + """ + CRC + + """ + Croatian Kuna (HRK). + """ + HRK + + """ + Czech Koruny (CZK). + """ + CZK + + """ + Danish Kroner (DKK). + """ + DKK + + """ + Djiboutian Franc (DJF). + """ + DJF + + """ + Dominican Peso (DOP). + """ + DOP + + """ + East Caribbean Dollar (XCD). + """ + XCD + + """ + Egyptian Pound (EGP). + """ + EGP + + """ + Eritrean Nakfa (ERN). + """ + ERN + + """ + Ethiopian Birr (ETB). + """ + ETB + + """ + Falkland Islands Pounds (FKP). + """ + FKP + + """ + CFP Franc (XPF). + """ + XPF + + """ + Fijian Dollars (FJD). + """ + FJD + + """ + Gibraltar Pounds (GIP). + """ + GIP + + """ + Gambian Dalasi (GMD). + """ + GMD + + """ + Ghanaian Cedi (GHS). + """ + GHS + + """ + Guatemalan Quetzal (GTQ). + """ + GTQ + + """ + Guyanese Dollar (GYD). + """ + GYD + + """ + Georgian Lari (GEL). + """ + GEL + + """ + Guinean Franc (GNF). + """ + GNF + + """ + Haitian Gourde (HTG). + """ + HTG + + """ + Honduran Lempira (HNL). + """ + HNL + + """ + Hong Kong Dollars (HKD). + """ + HKD + + """ + Hungarian Forint (HUF). + """ + HUF + + """ + Icelandic Kronur (ISK). + """ + ISK + + """ + Indian Rupees (INR). + """ + INR + + """ + Indonesian Rupiah (IDR). + """ + IDR + + """ + Israeli New Shekel (NIS). + """ + ILS + + """ + Iranian Rial (IRR). + """ + IRR + + """ + Iraqi Dinar (IQD). + """ + IQD + + """ + Jamaican Dollars (JMD). + """ + JMD + + """ + Japanese Yen (JPY). + """ + JPY + + """ + Jersey Pound. + """ + JEP + + """ + Jordanian Dinar (JOD). + """ + JOD + + """ + Kazakhstani Tenge (KZT). + """ + KZT + + """ + Kenyan Shilling (KES). + """ + KES + + """ + Kiribati Dollar (KID). + """ + KID + + """ + Kuwaiti Dinar (KWD). + """ + KWD + + """ + Kyrgyzstani Som (KGS). + """ + KGS + + """ + Laotian Kip (LAK). + """ + LAK + + """ + Latvian Lati (LVL). + """ + LVL + + """ + Lebanese Pounds (LBP). + """ + LBP + + """ + Lesotho Loti (LSL). + """ + LSL + + """ + Liberian Dollar (LRD). + """ + LRD + + """ + Libyan Dinar (LYD). + """ + LYD + + """ + Lithuanian Litai (LTL). + """ + LTL + + """ + Malagasy Ariary (MGA). + """ + MGA + + """ + Macedonia Denar (MKD). + """ + MKD + + """ + Macanese Pataca (MOP). + """ + MOP + + """ + Malawian Kwacha (MWK). + """ + MWK + + """ + Maldivian Rufiyaa (MVR). + """ + MVR + + """ + Mauritanian Ouguiya (MRU). + """ + MRU + + """ + Mexican Pesos (MXN). + """ + MXN + + """ + Malaysian Ringgits (MYR). + """ + MYR + + """ + Mauritian Rupee (MUR). + """ + MUR + + """ + Moldovan Leu (MDL). + """ + MDL + + """ + Moroccan Dirham. + """ + MAD + + """ + Mongolian Tugrik. + """ + MNT + + """ + Mozambican Metical. + """ + MZN + + """ + Namibian Dollar. + """ + NAD + + """ + Nepalese Rupee (NPR). + """ + NPR + + """ + Netherlands Antillean Guilder. + """ + ANG + + """ + New Zealand Dollars (NZD). + """ + NZD + + """ + Nicaraguan Córdoba (NIO). + """ + NIO + + """ + Nigerian Naira (NGN). + """ + NGN + + """ + Norwegian Kroner (NOK). + """ + NOK + + """ + Omani Rial (OMR). + """ + OMR + + """ + Panamian Balboa (PAB). + """ + PAB + + """ + Pakistani Rupee (PKR). + """ + PKR + + """ + Papua New Guinean Kina (PGK). + """ + PGK + + """ + Paraguayan Guarani (PYG). + """ + PYG + + """ + Peruvian Nuevo Sol (PEN). + """ + PEN + + """ + Philippine Peso (PHP). + """ + PHP + + """ + Polish Zlotych (PLN). + """ + PLN + + """ + Qatari Rial (QAR). + """ + QAR + + """ + Romanian Lei (RON). + """ + RON + + """ + Russian Rubles (RUB). + """ + RUB + + """ + Rwandan Franc (RWF). + """ + RWF + + """ + Samoan Tala (WST). + """ + WST + + """ + Saint Helena Pounds (SHP). + """ + SHP + + """ + Saudi Riyal (SAR). + """ + SAR + + """ + Sao Tome And Principe Dobra (STD). + """ + STD + + """ + Serbian dinar (RSD). + """ + RSD + + """ + Seychellois Rupee (SCR). + """ + SCR + + """ + Sierra Leonean Leone (SLL). + """ + SLL + + """ + Singapore Dollars (SGD). + """ + SGD + + """ + Sudanese Pound (SDG). + """ + SDG + + """ + Somali Shilling (SOS). + """ + SOS + + """ + Syrian Pound (SYP). + """ + SYP + + """ + South African Rand (ZAR). + """ + ZAR + + """ + South Korean Won (KRW). + """ + KRW + + """ + South Sudanese Pound (SSP). + """ + SSP + + """ + Solomon Islands Dollar (SBD). + """ + SBD + + """ + Sri Lankan Rupees (LKR). + """ + LKR + + """ + Surinamese Dollar (SRD). + """ + SRD + + """ + Swazi Lilangeni (SZL). + """ + SZL + + """ + Swedish Kronor (SEK). + """ + SEK + + """ + Swiss Francs (CHF). + """ + CHF + + """ + Taiwan Dollars (TWD). + """ + TWD + + """ + Thai baht (THB). + """ + THB + + """ + Tajikistani Somoni (TJS). + """ + TJS + + """ + Tanzanian Shilling (TZS). + """ + TZS + + """ + Tongan Pa'anga (TOP). + """ + TOP + + """ + Trinidad and Tobago Dollars (TTD). + """ + TTD + + """ + Tunisian Dinar (TND). + """ + TND + + """ + Turkish Lira (TRY). + """ + TRY + + """ + Turkmenistani Manat (TMT). + """ + TMT + + """ + Ugandan Shilling (UGX). + """ + UGX + + """ + Ukrainian Hryvnia (UAH). + """ + UAH + + """ + United Arab Emirates Dirham (AED). + """ + AED + + """ + Uruguayan Pesos (UYU). + """ + UYU + + """ + Uzbekistan som (UZS). + """ + UZS + + """ + Vanuatu Vatu (VUV). + """ + VUV + + """ + Venezuelan Bolivares (VEF). + """ + VEF + + """ + Venezuelan Bolivares (VES). + """ + VES + + """ + Vietnamese đồng (VND). + """ + VND + + """ + West African CFA franc (XOF). + """ + XOF + + """ + Yemeni Rial (YER). + """ + YER + + """ + Zambian Kwacha (ZMW). + """ + ZMW +} + +""" +A customer represents a customer account with the shop. Customer accounts store contact information for the customer, saving logged-in customers the trouble of having to provide it at every checkout. +""" +type Customer { + """ + Indicates whether the customer has consented to be sent marketing material via email. + """ + acceptsMarketing: Boolean! + + """ + A list of addresses for the customer. + """ + addresses( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): MailingAddressConnection! + + """ + The date and time when the customer was created. + """ + createdAt: DateTime! + + """ + The customer’s default address. + """ + defaultAddress: MailingAddress + + """ + The customer’s name, email or phone number. + """ + displayName: String! + + """ + The customer’s email address. + """ + email: String + + """ + The customer’s first name. + """ + firstName: String + + """ + A unique identifier for the customer. + """ + id: ID! + + """ + The customer's most recently updated, incomplete checkout. + """ + lastIncompleteCheckout: Checkout + + """ + The customer’s last name. + """ + lastName: String + + """ + The orders associated with the customer. + """ + orders( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: OrderSortKeys = ID + + """ + Supported filter parameters: + - `processed_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): OrderConnection! + + """ + The customer’s phone number. + """ + phone: String + + """ + A comma separated list of tags that have been added to the customer. + Additional access scope required: unauthenticated_read_customer_tags. + """ + tags: [String!]! + + """ + The date and time when the customer information was updated. + """ + updatedAt: DateTime! +} + +""" +A CustomerAccessToken represents the unique token required to make modifications to the customer object. +""" +type CustomerAccessToken { + """ + The customer’s access token. + """ + accessToken: String! + + """ + The date and time when the customer access token expires. + """ + expiresAt: DateTime! +} + +""" +Specifies the input fields required to create a customer access token. +""" +input CustomerAccessTokenCreateInput { + """ + The email associated to the customer. + """ + email: String! + + """ + The login password to be used by the customer. + """ + password: String! +} + +""" +Return type for `customerAccessTokenCreate` mutation. +""" +type CustomerAccessTokenCreatePayload { + """ + The newly created customer access token object. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerAccessTokenCreateWithMultipass` mutation. +""" +type CustomerAccessTokenCreateWithMultipassPayload { + """ + An access token object associated with the customer. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! +} + +""" +Return type for `customerAccessTokenDelete` mutation. +""" +type CustomerAccessTokenDeletePayload { + """ + The destroyed access token. + """ + deletedAccessToken: String + + """ + ID of the destroyed customer access token. + """ + deletedCustomerAccessTokenId: String + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! +} + +""" +Return type for `customerAccessTokenRenew` mutation. +""" +type CustomerAccessTokenRenewPayload { + """ + The renewed customer access token object. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! +} + +""" +Return type for `customerActivateByUrl` mutation. +""" +type CustomerActivateByUrlPayload { + """ + The customer that was activated. + """ + customer: Customer + + """ + A new customer access token for the customer. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! +} + +""" +Specifies the input fields required to activate a customer. +""" +input CustomerActivateInput { + """ + The activation token required to activate the customer. + """ + activationToken: String! + + """ + New password that will be set during activation. + """ + password: String! +} + +""" +Return type for `customerActivate` mutation. +""" +type CustomerActivatePayload { + """ + The customer object. + """ + customer: Customer + + """ + A newly created customer access token object for the customer. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerAddressCreate` mutation. +""" +type CustomerAddressCreatePayload { + """ + The new customer address object. + """ + customerAddress: MailingAddress + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerAddressDelete` mutation. +""" +type CustomerAddressDeletePayload { + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + ID of the deleted customer address. + """ + deletedCustomerAddressId: String + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerAddressUpdate` mutation. +""" +type CustomerAddressUpdatePayload { + """ + The customer’s updated mailing address. + """ + customerAddress: MailingAddress + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Specifies the fields required to create a new customer. +""" +input CustomerCreateInput { + """ + The customer’s first name. + """ + firstName: String + + """ + The customer’s last name. + """ + lastName: String + + """ + The customer’s email. + """ + email: String! + + """ + A unique phone number for the customer. + + Formatted using E.164 standard. For example, _+16135551111_. + """ + phone: String + + """ + The login password used by the customer. + """ + password: String! + + """ + Indicates whether the customer has consented to be sent marketing material via email. + """ + acceptsMarketing: Boolean +} + +""" +Return type for `customerCreate` mutation. +""" +type CustomerCreatePayload { + """ + The created customer object. + """ + customer: Customer + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerDefaultAddressUpdate` mutation. +""" +type CustomerDefaultAddressUpdatePayload { + """ + The updated customer object. + """ + customer: Customer + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Possible error codes that could be returned by CustomerUserError. +""" +enum CustomerErrorCode { + """ + Input value is blank. + """ + BLANK + + """ + Input value is invalid. + """ + INVALID + + """ + Input value is already taken. + """ + TAKEN + + """ + Input value is too long. + """ + TOO_LONG + + """ + Input value is too short. + """ + TOO_SHORT + + """ + Unidentified customer. + """ + UNIDENTIFIED_CUSTOMER + + """ + Customer is disabled. + """ + CUSTOMER_DISABLED + + """ + Input password starts or ends with whitespace. + """ + PASSWORD_STARTS_OR_ENDS_WITH_WHITESPACE + + """ + Input contains HTML tags. + """ + CONTAINS_HTML_TAGS + + """ + Input contains URL. + """ + CONTAINS_URL + + """ + Invalid activation token. + """ + TOKEN_INVALID + + """ + Customer already enabled. + """ + ALREADY_ENABLED + + """ + Address does not exist. + """ + NOT_FOUND + + """ + Input email contains an invalid domain name. + """ + BAD_DOMAIN + + """ + Multipass token is not valid. + """ + INVALID_MULTIPASS_REQUEST +} + +""" +Return type for `customerRecover` mutation. +""" +type CustomerRecoverPayload { + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Return type for `customerResetByUrl` mutation. +""" +type CustomerResetByUrlPayload { + """ + The customer object which was reset. + """ + customer: Customer + + """ + A newly created customer access token object for the customer. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Specifies the fields required to reset a customer’s password. +""" +input CustomerResetInput { + """ + The reset token required to reset the customer’s password. + """ + resetToken: String! + + """ + New password that will be set as part of the reset password process. + """ + password: String! +} + +""" +Return type for `customerReset` mutation. +""" +type CustomerResetPayload { + """ + The customer object which was reset. + """ + customer: Customer + + """ + A newly created customer access token object for the customer. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Specifies the fields required to update the Customer information. +""" +input CustomerUpdateInput { + """ + The customer’s first name. + """ + firstName: String + + """ + The customer’s last name. + """ + lastName: String + + """ + The customer’s email. + """ + email: String + + """ + A unique phone number for the customer. + + Formatted using E.164 standard. For example, _+16135551111_. To remove the phone number, specify `null`. + """ + phone: String + + """ + The login password used by the customer. + """ + password: String + + """ + Indicates whether the customer has consented to be sent marketing material via email. + """ + acceptsMarketing: Boolean +} + +""" +Return type for `customerUpdate` mutation. +""" +type CustomerUpdatePayload { + """ + The updated customer object. + """ + customer: Customer + + """ + The newly created customer access token. If the customer's password is updated, all previous access tokens + (including the one used to perform this mutation) become invalid, and a new token is generated. + """ + customerAccessToken: CustomerAccessToken + + """ + List of errors that occurred executing the mutation. + """ + customerUserErrors: [CustomerUserError!]! + + """ + List of errors that occurred executing the mutation. + """ + userErrors: [UserError!]! + @deprecated(reason: "Use `customerUserErrors` instead") +} + +""" +Represents an error that happens during execution of a customer mutation. +""" +type CustomerUserError implements DisplayableError { + """ + Error code to uniquely identify the error. + """ + code: CustomerErrorCode + + """ + Path to the input field which caused the error. + """ + field: [String!] + + """ + The error message. + """ + message: String! +} + +""" +An ISO-8601 encoded UTC date time string. Example value: `"2019-07-03T20:47:55Z"`. +""" +scalar DateTime + +""" +A signed decimal number, which supports arbitrary precision and is serialized as a string. Example value: `"29.99"`. +""" +scalar Decimal + +""" +Digital wallet, such as Apple Pay, which can be used for accelerated checkouts. +""" +enum DigitalWallet { + """ + Apple Pay. + """ + APPLE_PAY + + """ + Android Pay. + """ + ANDROID_PAY + + """ + Google Pay. + """ + GOOGLE_PAY + + """ + Shopify Pay. + """ + SHOPIFY_PAY +} + +""" +An amount discounting the line that has been allocated by a discount. +""" +type DiscountAllocation { + """ + Amount of discount allocated. + """ + allocatedAmount: MoneyV2! + + """ + The discount this allocated amount originated from. + """ + discountApplication: DiscountApplication! +} + +""" +Discount applications capture the intentions of a discount source at +the time of application. +""" +interface DiscountApplication { + """ + The method by which the discount's value is allocated to its entitled items. + """ + allocationMethod: DiscountApplicationAllocationMethod! + + """ + Which lines of targetType that the discount is allocated over. + """ + targetSelection: DiscountApplicationTargetSelection! + + """ + The type of line that the discount is applicable towards. + """ + targetType: DiscountApplicationTargetType! + + """ + The value of the discount application. + """ + value: PricingValue! +} + +""" +The method by which the discount's value is allocated onto its entitled lines. +""" +enum DiscountApplicationAllocationMethod { + """ + The value is spread across all entitled lines. + """ + ACROSS + + """ + The value is applied onto every entitled line. + """ + EACH + + """ + The value is specifically applied onto a particular line. + """ + ONE +} + +""" +An auto-generated type for paginating through multiple DiscountApplications. +""" +type DiscountApplicationConnection { + """ + A list of edges. + """ + edges: [DiscountApplicationEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one DiscountApplication and a cursor during pagination. +""" +type DiscountApplicationEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of DiscountApplicationEdge. + """ + node: DiscountApplication! +} + +""" +Which lines on the order that the discount is allocated over, of the type +defined by the Discount Application's target_type. +""" +enum DiscountApplicationTargetSelection { + """ + The discount is allocated onto all the lines. + """ + ALL + + """ + The discount is allocated onto only the lines it is entitled for. + """ + ENTITLED + + """ + The discount is allocated onto explicitly chosen lines. + """ + EXPLICIT +} + +""" +The type of line (i.e. line item or shipping line) on an order that the discount is applicable towards. +""" +enum DiscountApplicationTargetType { + """ + The discount applies onto line items. + """ + LINE_ITEM + + """ + The discount applies onto shipping lines. + """ + SHIPPING_LINE +} + +""" +Discount code applications capture the intentions of a discount code at +the time that it is applied. +""" +type DiscountCodeApplication implements DiscountApplication { + """ + The method by which the discount's value is allocated to its entitled items. + """ + allocationMethod: DiscountApplicationAllocationMethod! + + """ + Specifies whether the discount code was applied successfully. + """ + applicable: Boolean! + + """ + The string identifying the discount code that was used at the time of application. + """ + code: String! + + """ + Which lines of targetType that the discount is allocated over. + """ + targetSelection: DiscountApplicationTargetSelection! + + """ + The type of line that the discount is applicable towards. + """ + targetType: DiscountApplicationTargetType! + + """ + The value of the discount application. + """ + value: PricingValue! +} + +""" +Represents an error in the input of a mutation. +""" +interface DisplayableError { + """ + Path to the input field which caused the error. + """ + field: [String!] + + """ + The error message. + """ + message: String! +} + +""" +Represents a web address. +""" +type Domain { + """ + The host name of the domain (eg: `example.com`). + """ + host: String! + + """ + Whether SSL is enabled or not. + """ + sslEnabled: Boolean! + + """ + The URL of the domain (eg: `https://example.com`). + """ + url: URL! +} + +""" +Represents a video hosted outside of Shopify. +""" +type ExternalVideo implements Node & Media { + """ + A word or phrase to share the nature or contents of a media. + """ + alt: String + + """ + The URL. + """ + embeddedUrl: URL! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The media content type. + """ + mediaContentType: MediaContentType! + + """ + The preview image for the media. + """ + previewImage: Image +} + +""" +Represents a single fulfillment in an order. +""" +type Fulfillment { + """ + List of the fulfillment's line items. + """ + fulfillmentLineItems( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): FulfillmentLineItemConnection! + + """ + The name of the tracking company. + """ + trackingCompany: String + + """ + Tracking information associated with the fulfillment, + such as the tracking number and tracking URL. + """ + trackingInfo( + """ + Truncate the array result to this size. + """ + first: Int + ): [FulfillmentTrackingInfo!]! +} + +""" +Represents a single line item in a fulfillment. There is at most one fulfillment line item for each order line item. +""" +type FulfillmentLineItem { + """ + The associated order's line item. + """ + lineItem: OrderLineItem! + + """ + The amount fulfilled in this fulfillment. + """ + quantity: Int! +} + +""" +An auto-generated type for paginating through multiple FulfillmentLineItems. +""" +type FulfillmentLineItemConnection { + """ + A list of edges. + """ + edges: [FulfillmentLineItemEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one FulfillmentLineItem and a cursor during pagination. +""" +type FulfillmentLineItemEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of FulfillmentLineItemEdge. + """ + node: FulfillmentLineItem! +} + +""" +Tracking information associated with the fulfillment. +""" +type FulfillmentTrackingInfo { + """ + The tracking number of the fulfillment. + """ + number: String + + """ + The URL to track the fulfillment. + """ + url: URL +} + +""" +A string containing HTML code. Example value: `"<p>Grey cotton knit sweater.</p>"`. +""" +scalar HTML + +""" +Represents information about the metafields associated to the specified resource. +""" +interface HasMetafields { + """ + The metafield associated with the resource. + """ + metafield( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String! + + """ + Identifier for the metafield (maximum of 30 characters). + """ + key: String! + ): Metafield + + """ + A paginated list of metafields associated with the resource. + """ + metafields( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): MetafieldConnection! +} + +""" +Represents an image resource. +""" +type Image { + """ + A word or phrase to share the nature or contents of an image. + """ + altText: String + + """ + The original height of the image in pixels. Returns `null` if the image is not hosted by Shopify. + """ + height: Int + + """ + A unique identifier for the image. + """ + id: ID + + """ + The location of the original image as a URL. + + If there are any existing transformations in the original source URL, they will remain and not be stripped. + """ + originalSrc: URL! + + """ + The location of the image as a URL. + """ + src: URL! + @deprecated( + reason: "Previously an image had a single `src` field. This could either return the original image\nlocation or a URL that contained transformations such as sizing or scale.\n\nThese transformations were specified by arguments on the parent field.\n\nNow an image has two distinct URL fields: `originalSrc` and `transformedSrc`.\n\n* `originalSrc` - the original unmodified image URL\n* `transformedSrc` - the image URL with the specified transformations included\n\nTo migrate to the new fields, image transformations should be moved from the parent field to `transformedSrc`.\n\nBefore:\n```graphql\n{\n shop {\n productImages(maxWidth: 200, scale: 2) {\n edges {\n node {\n src\n }\n }\n }\n }\n}\n```\n\nAfter:\n```graphql\n{\n shop {\n productImages {\n edges {\n node {\n transformedSrc(maxWidth: 200, scale: 2)\n }\n }\n }\n }\n}\n```\n" + ) + + """ + The location of the transformed image as a URL. + + All transformation arguments are considered "best-effort". If they can be applied to an image, they will be. + Otherwise any transformations which an image type does not support will be ignored. + """ + transformedSrc( + """ + Image width in pixels between 1 and 5760. + """ + maxWidth: Int + + """ + Image height in pixels between 1 and 5760. + """ + maxHeight: Int + + """ + Crops the image according to the specified region. + """ + crop: CropRegion + + """ + Image size multiplier for high-resolution retina displays. Must be between 1 and 3. + """ + scale: Int = 1 + + """ + Best effort conversion of image into content type (SVG -> PNG, Anything -> JGP, Anything -> WEBP are supported). + """ + preferredContentType: ImageContentType + ): URL! + + """ + The original width of the image in pixels. Returns `null` if the image is not hosted by Shopify. + """ + width: Int +} + +""" +An auto-generated type for paginating through multiple Images. +""" +type ImageConnection { + """ + A list of edges. + """ + edges: [ImageEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +List of supported image content types. +""" +enum ImageContentType { + """ + A PNG image. + """ + PNG + + """ + A JPG image. + """ + JPG + + """ + A WEBP image. + """ + WEBP +} + +""" +An auto-generated type which holds one Image and a cursor during pagination. +""" +type ImageEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ImageEdge. + """ + node: Image! +} + +""" +Represents a mailing address for customers and shipping. +""" +type MailingAddress implements Node { + """ + The first line of the address. Typically the street address or PO Box number. + """ + address1: String + + """ + The second line of the address. Typically the number of the apartment, suite, or unit. + """ + address2: String + + """ + The name of the city, district, village, or town. + """ + city: String + + """ + The name of the customer's company or organization. + """ + company: String + + """ + The name of the country. + """ + country: String + + """ + The two-letter code for the country of the address. + + For example, US. + """ + countryCode: String @deprecated(reason: "Use `countryCodeV2` instead") + + """ + The two-letter code for the country of the address. + + For example, US. + """ + countryCodeV2: CountryCode + + """ + The first name of the customer. + """ + firstName: String + + """ + A formatted version of the address, customized by the provided arguments. + """ + formatted( + """ + Whether to include the customer's name in the formatted address. + """ + withName: Boolean = false + + """ + Whether to include the customer's company in the formatted address. + """ + withCompany: Boolean = true + ): [String!]! + + """ + A comma-separated list of the values for city, province, and country. + """ + formattedArea: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + The last name of the customer. + """ + lastName: String + + """ + The latitude coordinate of the customer address. + """ + latitude: Float + + """ + The longitude coordinate of the customer address. + """ + longitude: Float + + """ + The full name of the customer, based on firstName and lastName. + """ + name: String + + """ + A unique phone number for the customer. + + Formatted using E.164 standard. For example, _+16135551111_. + """ + phone: String + + """ + The region of the address, such as the province, state, or district. + """ + province: String + + """ + The two-letter code for the region. + + For example, ON. + """ + provinceCode: String + + """ + The zip or postal code of the address. + """ + zip: String +} + +""" +An auto-generated type for paginating through multiple MailingAddresses. +""" +type MailingAddressConnection { + """ + A list of edges. + """ + edges: [MailingAddressEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one MailingAddress and a cursor during pagination. +""" +type MailingAddressEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of MailingAddressEdge. + """ + node: MailingAddress! +} + +""" +Specifies the fields accepted to create or update a mailing address. +""" +input MailingAddressInput { + """ + The first line of the address. Typically the street address or PO Box number. + """ + address1: String + + """ + The second line of the address. Typically the number of the apartment, suite, or unit. + """ + address2: String + + """ + The name of the city, district, village, or town. + """ + city: String + + """ + The name of the customer's company or organization. + """ + company: String + + """ + The name of the country. + """ + country: String + + """ + The first name of the customer. + """ + firstName: String + + """ + The last name of the customer. + """ + lastName: String + + """ + A unique phone number for the customer. + + Formatted using E.164 standard. For example, _+16135551111_. + """ + phone: String + + """ + The region of the address, such as the province, state, or district. + """ + province: String + + """ + The zip or postal code of the address. + """ + zip: String +} + +""" +Manual discount applications capture the intentions of a discount that was manually created. +""" +type ManualDiscountApplication implements DiscountApplication { + """ + The method by which the discount's value is allocated to its entitled items. + """ + allocationMethod: DiscountApplicationAllocationMethod! + + """ + The description of the application. + """ + description: String + + """ + Which lines of targetType that the discount is allocated over. + """ + targetSelection: DiscountApplicationTargetSelection! + + """ + The type of line that the discount is applicable towards. + """ + targetType: DiscountApplicationTargetType! + + """ + The title of the application. + """ + title: String! + + """ + The value of the discount application. + """ + value: PricingValue! +} + +""" +Represents a media interface. +""" +interface Media { + """ + A word or phrase to share the nature or contents of a media. + """ + alt: String + + """ + The media content type. + """ + mediaContentType: MediaContentType! + + """ + The preview image for the media. + """ + previewImage: Image +} + +""" +An auto-generated type for paginating through multiple Media. +""" +type MediaConnection { + """ + A list of edges. + """ + edges: [MediaEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +The possible content types for a media object. +""" +enum MediaContentType { + """ + An externally hosted video. + """ + EXTERNAL_VIDEO + + """ + A Shopify hosted image. + """ + IMAGE + + """ + A 3d model. + """ + MODEL_3D + + """ + A Shopify hosted video. + """ + VIDEO +} + +""" +An auto-generated type which holds one Media and a cursor during pagination. +""" +type MediaEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of MediaEdge. + """ + node: Media! +} + +""" +Represents a Shopify hosted image. +""" +type MediaImage implements Node & Media { + """ + A word or phrase to share the nature or contents of a media. + """ + alt: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + The image for the media. + """ + image: Image + + """ + The media content type. + """ + mediaContentType: MediaContentType! + + """ + The preview image for the media. + """ + previewImage: Image +} + +""" +Metafields represent custom metadata attached to a resource. Metafields can be sorted into namespaces and are +comprised of keys, values, and value types. +""" +type Metafield implements Node { + """ + The date and time when the storefront metafield was created. + """ + createdAt: DateTime! + + """ + The description of a metafield. + """ + description: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + The key name for a metafield. + """ + key: String! + + """ + The namespace for a metafield. + """ + namespace: String! + + """ + The parent object that the metafield belongs to. + """ + parentResource: MetafieldParentResource! + + """ + The date and time when the storefront metafield was updated. + """ + updatedAt: DateTime! + + """ + The value of a metafield. + """ + value: String! + + """ + Represents the metafield value type. + """ + valueType: MetafieldValueType! +} + +""" +An auto-generated type for paginating through multiple Metafields. +""" +type MetafieldConnection { + """ + A list of edges. + """ + edges: [MetafieldEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Metafield and a cursor during pagination. +""" +type MetafieldEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of MetafieldEdge. + """ + node: Metafield! +} + +""" +A resource that the metafield belongs to. +""" +union MetafieldParentResource = Product | ProductVariant + +""" +Metafield value types. +""" +enum MetafieldValueType { + """ + A string metafield. + """ + STRING + + """ + An integer metafield. + """ + INTEGER + + """ + A json string metafield. + """ + JSON_STRING +} + +""" +Represents a Shopify hosted 3D model. +""" +type Model3d implements Node & Media { + """ + A word or phrase to share the nature or contents of a media. + """ + alt: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + The media content type. + """ + mediaContentType: MediaContentType! + + """ + The preview image for the media. + """ + previewImage: Image + + """ + The sources for a 3d model. + """ + sources: [Model3dSource!]! +} + +""" +Represents a source for a Shopify hosted 3d model. +""" +type Model3dSource { + """ + The filesize of the 3d model. + """ + filesize: Int! + + """ + The format of the 3d model. + """ + format: String! + + """ + The MIME type of the 3d model. + """ + mimeType: String! + + """ + The URL of the 3d model. + """ + url: String! +} + +""" +A monetary value string. Example value: `"100.57"`. +""" +scalar Money + +""" +Specifies the fields for a monetary value with currency. +""" +input MoneyInput { + """ + Decimal money amount. + """ + amount: Decimal! + + """ + Currency of the money. + """ + currencyCode: CurrencyCode! +} + +""" +A monetary value with currency. + +To format currencies, combine this type's amount and currencyCode fields with your client's locale. + +For example, in JavaScript you could use Intl.NumberFormat: + +```js +new Intl.NumberFormat(locale, { + style: 'currency', + currency: currencyCode +}).format(amount); +``` + +Other formatting libraries include: + +* iOS - [NumberFormatter](https://developer.apple.com/documentation/foundation/numberformatter) +* Android - [NumberFormat](https://developer.android.com/reference/java/text/NumberFormat.html) +* PHP - [NumberFormatter](http://php.net/manual/en/class.numberformatter.php) + +For a more general solution, the [Unicode CLDR number formatting database] is available with many implementations +(such as [TwitterCldr](https://github.com/twitter/twitter-cldr-rb)). +""" +type MoneyV2 { + """ + Decimal money amount. + """ + amount: Decimal! + + """ + Currency of the money. + """ + currencyCode: CurrencyCode! +} + +""" +An auto-generated type for paginating through multiple MoneyV2s. +""" +type MoneyV2Connection { + """ + A list of edges. + """ + edges: [MoneyV2Edge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one MoneyV2 and a cursor during pagination. +""" +type MoneyV2Edge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of MoneyV2Edge. + """ + node: MoneyV2! +} + +""" +The schema’s entry-point for mutations. This acts as the public, top-level API from which all mutation queries must start. +""" +type Mutation { + """ + Updates the attributes of a checkout. + """ + checkoutAttributesUpdate( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The fields used to update a checkout's attributes. + """ + input: CheckoutAttributesUpdateInput! + ): CheckoutAttributesUpdatePayload + @deprecated(reason: "Use `checkoutAttributesUpdateV2` instead") + + """ + Updates the attributes of a checkout. + """ + checkoutAttributesUpdateV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The checkout attributes to update. + """ + input: CheckoutAttributesUpdateV2Input! + ): CheckoutAttributesUpdateV2Payload + + """ + Completes a checkout without providing payment information. You can use this mutation for free items or items whose purchase price is covered by a gift card. + """ + checkoutCompleteFree( + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutCompleteFreePayload + + """ + Completes a checkout using a credit card token from Shopify's Vault. + """ + checkoutCompleteWithCreditCard( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The credit card info to apply as a payment. + """ + payment: CreditCardPaymentInput! + ): CheckoutCompleteWithCreditCardPayload + @deprecated(reason: "Use `checkoutCompleteWithCreditCardV2` instead") + + """ + Completes a checkout using a credit card token from Shopify's card vault. Before you can complete checkouts using CheckoutCompleteWithCreditCardV2, you need to [_request payment processing_](https://help.shopify.com/api/guides/sales-channel-sdk/getting-started#request-payment-processing). + """ + checkoutCompleteWithCreditCardV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The credit card info to apply as a payment. + """ + payment: CreditCardPaymentInputV2! + ): CheckoutCompleteWithCreditCardV2Payload + + """ + Completes a checkout with a tokenized payment. + """ + checkoutCompleteWithTokenizedPayment( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The info to apply as a tokenized payment. + """ + payment: TokenizedPaymentInput! + ): CheckoutCompleteWithTokenizedPaymentPayload + @deprecated(reason: "Use `checkoutCompleteWithTokenizedPaymentV2` instead") + + """ + Completes a checkout with a tokenized payment. + """ + checkoutCompleteWithTokenizedPaymentV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The info to apply as a tokenized payment. + """ + payment: TokenizedPaymentInputV2! + ): CheckoutCompleteWithTokenizedPaymentV2Payload + @deprecated(reason: "Use `checkoutCompleteWithTokenizedPaymentV3` instead") + + """ + Completes a checkout with a tokenized payment. + """ + checkoutCompleteWithTokenizedPaymentV3( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The info to apply as a tokenized payment. + """ + payment: TokenizedPaymentInputV3! + ): CheckoutCompleteWithTokenizedPaymentV3Payload + + """ + Creates a new checkout. + """ + checkoutCreate( + """ + The fields used to create a checkout. + """ + input: CheckoutCreateInput! + ): CheckoutCreatePayload + + """ + Associates a customer to the checkout. + """ + checkoutCustomerAssociate( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The customer access token of the customer to associate. + """ + customerAccessToken: String! + ): CheckoutCustomerAssociatePayload + @deprecated(reason: "Use `checkoutCustomerAssociateV2` instead") + + """ + Associates a customer to the checkout. + """ + checkoutCustomerAssociateV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The customer access token of the customer to associate. + """ + customerAccessToken: String! + ): CheckoutCustomerAssociateV2Payload + + """ + Disassociates the current checkout customer from the checkout. + """ + checkoutCustomerDisassociate( + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutCustomerDisassociatePayload + @deprecated(reason: "Use `checkoutCustomerDisassociateV2` instead") + + """ + Disassociates the current checkout customer from the checkout. + """ + checkoutCustomerDisassociateV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutCustomerDisassociateV2Payload + + """ + Applies a discount to an existing checkout using a discount code. + """ + checkoutDiscountCodeApply( + """ + The discount code to apply to the checkout. + """ + discountCode: String! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutDiscountCodeApplyPayload + @deprecated(reason: "Use `checkoutDiscountCodeApplyV2` instead") + + """ + Applies a discount to an existing checkout using a discount code. + """ + checkoutDiscountCodeApplyV2( + """ + The discount code to apply to the checkout. + """ + discountCode: String! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutDiscountCodeApplyV2Payload + + """ + Removes the applied discount from an existing checkout. + """ + checkoutDiscountCodeRemove( + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutDiscountCodeRemovePayload + + """ + Updates the email on an existing checkout. + """ + checkoutEmailUpdate( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The email to update the checkout with. + """ + email: String! + ): CheckoutEmailUpdatePayload + @deprecated(reason: "Use `checkoutEmailUpdateV2` instead") + + """ + Updates the email on an existing checkout. + """ + checkoutEmailUpdateV2( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + The email to update the checkout with. + """ + email: String! + ): CheckoutEmailUpdateV2Payload + + """ + Applies a gift card to an existing checkout using a gift card code. This will replace all currently applied gift cards. + """ + checkoutGiftCardApply( + """ + The code of the gift card to apply on the checkout. + """ + giftCardCode: String! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutGiftCardApplyPayload + @deprecated(reason: "Use `checkoutGiftCardsAppend` instead") + + """ + Removes an applied gift card from the checkout. + """ + checkoutGiftCardRemove( + """ + The ID of the Applied Gift Card to remove from the Checkout. + """ + appliedGiftCardId: ID! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutGiftCardRemovePayload + @deprecated(reason: "Use `checkoutGiftCardRemoveV2` instead") + + """ + Removes an applied gift card from the checkout. + """ + checkoutGiftCardRemoveV2( + """ + The ID of the Applied Gift Card to remove from the Checkout. + """ + appliedGiftCardId: ID! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutGiftCardRemoveV2Payload + + """ + Appends gift cards to an existing checkout. + """ + checkoutGiftCardsAppend( + """ + A list of gift card codes to append to the checkout. + """ + giftCardCodes: [String!]! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutGiftCardsAppendPayload + + """ + Adds a list of line items to a checkout. + """ + checkoutLineItemsAdd( + """ + A list of line item objects to add to the checkout. + """ + lineItems: [CheckoutLineItemInput!]! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutLineItemsAddPayload + + """ + Removes line items from an existing checkout. + """ + checkoutLineItemsRemove( + """ + The checkout on which to remove line items. + """ + checkoutId: ID! + + """ + Line item ids to remove. + """ + lineItemIds: [ID!]! + ): CheckoutLineItemsRemovePayload + + """ + Sets a list of line items to a checkout. + """ + checkoutLineItemsReplace( + """ + A list of line item objects to set on the checkout. + """ + lineItems: [CheckoutLineItemInput!]! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutLineItemsReplacePayload + + """ + Updates line items on a checkout. + """ + checkoutLineItemsUpdate( + """ + The checkout on which to update line items. + """ + checkoutId: ID! + + """ + Line items to update. + """ + lineItems: [CheckoutLineItemUpdateInput!]! + ): CheckoutLineItemsUpdatePayload + + """ + Updates the shipping address of an existing checkout. + """ + checkoutShippingAddressUpdate( + """ + The shipping address to where the line items will be shipped. + """ + shippingAddress: MailingAddressInput! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutShippingAddressUpdatePayload + @deprecated(reason: "Use `checkoutShippingAddressUpdateV2` instead") + + """ + Updates the shipping address of an existing checkout. + """ + checkoutShippingAddressUpdateV2( + """ + The shipping address to where the line items will be shipped. + """ + shippingAddress: MailingAddressInput! + + """ + The ID of the checkout. + """ + checkoutId: ID! + ): CheckoutShippingAddressUpdateV2Payload + + """ + Updates the shipping lines on an existing checkout. + """ + checkoutShippingLineUpdate( + """ + The ID of the checkout. + """ + checkoutId: ID! + + """ + A unique identifier to a Checkout’s shipping provider, price, and title combination, enabling the customer to select the availableShippingRates. + """ + shippingRateHandle: String! + ): CheckoutShippingLineUpdatePayload + + """ + Creates a customer access token. + The customer access token is required to modify the customer object in any way. + """ + customerAccessTokenCreate( + """ + The fields used to create a customer access token. + """ + input: CustomerAccessTokenCreateInput! + ): CustomerAccessTokenCreatePayload + + """ + Creates a customer access token using a multipass token instead of email and password. + A customer record is created if customer does not exist. If a customer record already + exists but the record is disabled, then it's enabled. + """ + customerAccessTokenCreateWithMultipass( + """ + A valid multipass token to be authenticated. + """ + multipassToken: String! + ): CustomerAccessTokenCreateWithMultipassPayload + + """ + Permanently destroys a customer access token. + """ + customerAccessTokenDelete( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + ): CustomerAccessTokenDeletePayload + + """ + Renews a customer access token. + + Access token renewal must happen *before* a token expires. + If a token has already expired, a new one should be created instead via `customerAccessTokenCreate`. + """ + customerAccessTokenRenew( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + ): CustomerAccessTokenRenewPayload + + """ + Activates a customer. + """ + customerActivate( + """ + Specifies the customer to activate. + """ + id: ID! + + """ + The fields used to activate a customer. + """ + input: CustomerActivateInput! + ): CustomerActivatePayload + + """ + Activates a customer with the activation url received from `customerCreate`. + """ + customerActivateByUrl( + """ + The customer activation URL. + """ + activationUrl: URL! + + """ + A new password set during activation. + """ + password: String! + ): CustomerActivateByUrlPayload + + """ + Creates a new address for a customer. + """ + customerAddressCreate( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + + """ + The customer mailing address to create. + """ + address: MailingAddressInput! + ): CustomerAddressCreatePayload + + """ + Permanently deletes the address of an existing customer. + """ + customerAddressDelete( + """ + Specifies the address to delete. + """ + id: ID! + + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + ): CustomerAddressDeletePayload + + """ + Updates the address of an existing customer. + """ + customerAddressUpdate( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + + """ + Specifies the customer address to update. + """ + id: ID! + + """ + The customer’s mailing address. + """ + address: MailingAddressInput! + ): CustomerAddressUpdatePayload + + """ + Creates a new customer. + """ + customerCreate( + """ + The fields used to create a new customer. + """ + input: CustomerCreateInput! + ): CustomerCreatePayload + + """ + Updates the default address of an existing customer. + """ + customerDefaultAddressUpdate( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + + """ + ID of the address to set as the new default for the customer. + """ + addressId: ID! + ): CustomerDefaultAddressUpdatePayload + + """ + Sends a reset password email to the customer, as the first step in the reset password process. + """ + customerRecover( + """ + The email address of the customer to recover. + """ + email: String! + ): CustomerRecoverPayload + + """ + Resets a customer’s password with a token received from `CustomerRecover`. + """ + customerReset( + """ + Specifies the customer to reset. + """ + id: ID! + + """ + The fields used to reset a customer’s password. + """ + input: CustomerResetInput! + ): CustomerResetPayload + + """ + Resets a customer’s password with the reset password url received from `CustomerRecover`. + """ + customerResetByUrl( + """ + The customer's reset password url. + """ + resetUrl: URL! + + """ + New password that will be set as part of the reset password process. + """ + password: String! + ): CustomerResetByUrlPayload + + """ + Updates an existing customer. + """ + customerUpdate( + """ + The access token used to identify the customer. + """ + customerAccessToken: String! + + """ + The customer object input. + """ + customer: CustomerUpdateInput! + ): CustomerUpdatePayload +} + +""" +An object with an ID to support global identification. +""" +interface Node { + """ + Globally unique identifier. + """ + id: ID! +} + +""" +An order is a customer’s completed request to purchase one or more products from a shop. An order is created when a customer completes the checkout process, during which time they provides an email address, billing address and payment information. +""" +type Order implements Node { + """ + The reason for the order's cancellation. Returns `null` if the order wasn't canceled. + """ + cancelReason: OrderCancelReason + + """ + The date and time when the order was canceled. Returns null if the order wasn't canceled. + """ + canceledAt: DateTime + + """ + The code of the currency used for the payment. + """ + currencyCode: CurrencyCode! + + """ + The subtotal of line items and their discounts, excluding line items that have been removed. Does not contain order-level discounts, duties, shipping costs, or shipping discounts. Taxes are not included unless the order is a taxes-included order. + """ + currentSubtotalPrice: MoneyV2! + + """ + The total amount of the order, including duties, taxes and discounts, minus amounts for line items that have been removed. + """ + currentTotalPrice: MoneyV2! + + """ + The total of all taxes applied to the order, excluding taxes for returned line items. + """ + currentTotalTax: MoneyV2! + + """ + The locale code in which this specific order happened. + """ + customerLocale: String + + """ + The unique URL that the customer can use to access the order. + """ + customerUrl: URL + + """ + Discounts that have been applied on the order. + """ + discountApplications( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): DiscountApplicationConnection! + + """ + Whether the order has had any edits applied or not. + """ + edited: Boolean! + + """ + The customer's email address. + """ + email: String + + """ + The financial status of the order. + """ + financialStatus: OrderFinancialStatus + + """ + The fulfillment status for the order. + """ + fulfillmentStatus: OrderFulfillmentStatus! + + """ + Globally unique identifier. + """ + id: ID! + + """ + List of the order’s line items. + """ + lineItems( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): OrderLineItemConnection! + + """ + Unique identifier for the order that appears on the order. + For example, _#1000_ or _Store1001. + """ + name: String! + + """ + A unique numeric identifier for the order for use by shop owner and customer. + """ + orderNumber: Int! + + """ + The total price of the order before any applied edits. + """ + originalTotalPrice: MoneyV2! + + """ + The customer's phone number for receiving SMS notifications. + """ + phone: String + + """ + The date and time when the order was imported. + This value can be set to dates in the past when importing from other systems. + If no value is provided, it will be auto-generated based on current date and time. + """ + processedAt: DateTime! + + """ + The address to where the order will be shipped. + """ + shippingAddress: MailingAddress + + """ + The discounts that have been allocated onto the shipping line by discount applications. + """ + shippingDiscountAllocations: [DiscountAllocation!]! + + """ + The unique URL for the order's status page. + """ + statusUrl: URL! + + """ + Price of the order before shipping and taxes. + """ + subtotalPrice: Money @deprecated(reason: "Use `subtotalPriceV2` instead") + + """ + Price of the order before duties, shipping and taxes. + """ + subtotalPriceV2: MoneyV2 + + """ + List of the order’s successful fulfillments. + """ + successfulFulfillments( + """ + Truncate the array result to this size. + """ + first: Int + ): [Fulfillment!] + + """ + The sum of all the prices of all the items in the order, taxes and discounts included (must be positive). + """ + totalPrice: Money! @deprecated(reason: "Use `totalPriceV2` instead") + + """ + The sum of all the prices of all the items in the order, duties, taxes and discounts included (must be positive). + """ + totalPriceV2: MoneyV2! + + """ + The total amount that has been refunded. + """ + totalRefunded: Money! @deprecated(reason: "Use `totalRefundedV2` instead") + + """ + The total amount that has been refunded. + """ + totalRefundedV2: MoneyV2! + + """ + The total cost of shipping. + """ + totalShippingPrice: Money! + @deprecated(reason: "Use `totalShippingPriceV2` instead") + + """ + The total cost of shipping. + """ + totalShippingPriceV2: MoneyV2! + + """ + The total cost of taxes. + """ + totalTax: Money @deprecated(reason: "Use `totalTaxV2` instead") + + """ + The total cost of taxes. + """ + totalTaxV2: MoneyV2 +} + +""" +Represents the reason for the order's cancellation. +""" +enum OrderCancelReason { + """ + The customer wanted to cancel the order. + """ + CUSTOMER + + """ + The order was fraudulent. + """ + FRAUD + + """ + There was insufficient inventory. + """ + INVENTORY + + """ + Payment was declined. + """ + DECLINED + + """ + The order was canceled for an unlisted reason. + """ + OTHER +} + +""" +An auto-generated type for paginating through multiple Orders. +""" +type OrderConnection { + """ + A list of edges. + """ + edges: [OrderEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Order and a cursor during pagination. +""" +type OrderEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of OrderEdge. + """ + node: Order! +} + +""" +Represents the order's current financial status. +""" +enum OrderFinancialStatus { + """ + Displayed as **Pending**. + """ + PENDING + + """ + Displayed as **Authorized**. + """ + AUTHORIZED + + """ + Displayed as **Partially paid**. + """ + PARTIALLY_PAID + + """ + Displayed as **Partially refunded**. + """ + PARTIALLY_REFUNDED + + """ + Displayed as **Voided**. + """ + VOIDED + + """ + Displayed as **Paid**. + """ + PAID + + """ + Displayed as **Refunded**. + """ + REFUNDED +} + +""" +Represents the order's current fulfillment status. +""" +enum OrderFulfillmentStatus { + """ + Displayed as **Unfulfilled**. + """ + UNFULFILLED + + """ + Displayed as **Partially fulfilled**. + """ + PARTIALLY_FULFILLED + + """ + Displayed as **Fulfilled**. + """ + FULFILLED + + """ + Displayed as **Restocked**. + """ + RESTOCKED + + """ + Displayed as **Pending fulfillment**. + """ + PENDING_FULFILLMENT + + """ + Displayed as **Open**. + """ + OPEN + + """ + Displayed as **In progress**. + """ + IN_PROGRESS + + """ + Displayed as **Scheduled**. + """ + SCHEDULED +} + +""" +Represents a single line in an order. There is one line item for each distinct product variant. +""" +type OrderLineItem { + """ + The number of entries associated to the line item minus the items that have been removed. + """ + currentQuantity: Int! + + """ + List of custom attributes associated to the line item. + """ + customAttributes: [Attribute!]! + + """ + The discounts that have been allocated onto the order line item by discount applications. + """ + discountAllocations: [DiscountAllocation!]! + + """ + The total price of the line item, including discounts, and displayed in the presentment currency. + """ + discountedTotalPrice: MoneyV2! + + """ + The total price of the line item, not including any discounts. The total price is calculated using the original unit price multiplied by the quantity, and it is displayed in the presentment currency. + """ + originalTotalPrice: MoneyV2! + + """ + The number of products variants associated to the line item. + """ + quantity: Int! + + """ + The title of the product combined with title of the variant. + """ + title: String! + + """ + The product variant object associated to the line item. + """ + variant: ProductVariant +} + +""" +An auto-generated type for paginating through multiple OrderLineItems. +""" +type OrderLineItemConnection { + """ + A list of edges. + """ + edges: [OrderLineItemEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one OrderLineItem and a cursor during pagination. +""" +type OrderLineItemEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of OrderLineItemEdge. + """ + node: OrderLineItem! +} + +""" +The set of valid sort keys for the Order query. +""" +enum OrderSortKeys { + """ + Sort by the `processed_at` value. + """ + PROCESSED_AT + + """ + Sort by the `total_price` value. + """ + TOTAL_PRICE + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +Shopify merchants can create pages to hold static HTML content. Each Page object represents a custom page on the online store. +""" +type Page implements Node { + """ + The description of the page, complete with HTML formatting. + """ + body: HTML! + + """ + Summary of the page body. + """ + bodySummary: String! + + """ + The timestamp of the page creation. + """ + createdAt: DateTime! + + """ + A human-friendly unique string for the page automatically generated from its title. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + The page's SEO information. + """ + seo: SEO + + """ + The title of the page. + """ + title: String! + + """ + The timestamp of the latest page update. + """ + updatedAt: DateTime! + + """ + The url pointing to the page accessible from the web. + """ + url: URL! +} + +""" +An auto-generated type for paginating through multiple Pages. +""" +type PageConnection { + """ + A list of edges. + """ + edges: [PageEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Page and a cursor during pagination. +""" +type PageEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of PageEdge. + """ + node: Page! +} + +""" +Information about pagination in a connection. +""" +type PageInfo { + """ + Indicates if there are more pages to fetch. + """ + hasNextPage: Boolean! + + """ + Indicates if there are any pages prior to the current page. + """ + hasPreviousPage: Boolean! +} + +""" +The set of valid sort keys for the Page query. +""" +enum PageSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `updated_at` value. + """ + UPDATED_AT + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +A payment applied to a checkout. +""" +type Payment implements Node { + """ + The amount of the payment. + """ + amount: Money! @deprecated(reason: "Use `amountV2` instead") + + """ + The amount of the payment. + """ + amountV2: MoneyV2! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddress + + """ + The checkout to which the payment belongs. + """ + checkout: Checkout! + + """ + The credit card used for the payment in the case of direct payments. + """ + creditCard: CreditCard + + """ + A message describing a processing error during asynchronous processing. + """ + errorMessage: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + A client-side generated token to identify a payment and perform idempotent operations. + """ + idempotencyKey: String + + """ + The URL where the customer needs to be redirected so they can complete the 3D Secure payment flow. + """ + nextActionUrl: URL + + """ + Whether or not the payment is still processing asynchronously. + """ + ready: Boolean! + + """ + A flag to indicate if the payment is to be done in test mode for gateways that support it. + """ + test: Boolean! + + """ + The actual transaction recorded by Shopify after having processed the payment with the gateway. + """ + transaction: Transaction +} + +""" +Settings related to payments. +""" +type PaymentSettings { + """ + List of the card brands which the shop accepts. + """ + acceptedCardBrands: [CardBrand!]! + + """ + The url pointing to the endpoint to vault credit cards. + """ + cardVaultUrl: URL! + + """ + The country where the shop is located. + """ + countryCode: CountryCode! + + """ + The three-letter code for the shop's primary currency. + """ + currencyCode: CurrencyCode! + + """ + A list of enabled currencies (ISO 4217 format) that the shop accepts. Merchants can enable currencies from their Shopify Payments settings in the Shopify admin. + """ + enabledPresentmentCurrencies: [CurrencyCode!]! + + """ + The shop’s Shopify Payments account id. + """ + shopifyPaymentsAccountId: String + + """ + List of the digital wallets which the shop supports. + """ + supportedDigitalWallets: [DigitalWallet!]! +} + +""" +The valid values for the types of payment token. +""" +enum PaymentTokenType { + """ + Apple Pay token type. + """ + APPLE_PAY + + """ + Vault payment token type. + """ + VAULT + + """ + Shopify Pay token type. + """ + SHOPIFY_PAY + + """ + Google Pay token type. + """ + GOOGLE_PAY +} + +""" +The value of the percentage pricing object. +""" +type PricingPercentageValue { + """ + The percentage value of the object. + """ + percentage: Float! +} + +""" +The price value (fixed or percentage) for a discount application. +""" +union PricingValue = MoneyV2 | PricingPercentageValue + +""" +A product represents an individual item for sale in a Shopify store. Products are often physical, but they don't have to be. +For example, a digital download (such as a movie, music or ebook file) also qualifies as a product, as do services (such as equipment rental, work for hire, customization of another product or an extended warranty). +""" +type Product implements Node & HasMetafields { + """ + Indicates if at least one product variant is available for sale. + """ + availableForSale: Boolean! + + """ + List of collections a product belongs to. + """ + collections( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): CollectionConnection! + + """ + The compare at price of the product across all variants. + """ + compareAtPriceRange: ProductPriceRange! + + """ + The date and time when the product was created. + """ + createdAt: DateTime! + + """ + Stripped description of the product, single line with HTML tags removed. + """ + description( + """ + Truncates string after the given length. + """ + truncateAt: Int + ): String! + + """ + The description of the product, complete with HTML formatting. + """ + descriptionHtml: HTML! + + """ + A human-friendly unique string for the Product automatically generated from its title. + They are used by the Liquid templating language to refer to objects. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + List of images associated with the product. + """ + images( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductImageSortKeys = POSITION + + """ + Image width in pixels between 1 and 2048. This argument is deprecated: Use `maxWidth` on `Image.transformedSrc` instead. + """ + maxWidth: Int + + """ + Image height in pixels between 1 and 2048. This argument is deprecated: Use `maxHeight` on `Image.transformedSrc` instead. + """ + maxHeight: Int + + """ + Crops the image according to the specified region. This argument is deprecated: Use `crop` on `Image.transformedSrc` instead. + """ + crop: CropRegion + + """ + Image size multiplier for high-resolution retina displays. Must be between 1 and 3. This argument is deprecated: Use `scale` on `Image.transformedSrc` instead. + """ + scale: Int = 1 + ): ImageConnection! + + """ + The media associated with the product. + """ + media( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductMediaSortKeys = POSITION + ): MediaConnection! + + """ + The metafield associated with the resource. + """ + metafield( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String! + + """ + Identifier for the metafield (maximum of 30 characters). + """ + key: String! + ): Metafield + + """ + A paginated list of metafields associated with the resource. + """ + metafields( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): MetafieldConnection! + + """ + The online store URL for the product. + A value of `null` indicates that the product is not published to the Online Store sales channel. + """ + onlineStoreUrl: URL + + """ + List of product options. + """ + options( + """ + Truncate the array result to this size. + """ + first: Int + ): [ProductOption!]! + + """ + List of price ranges in the presentment currencies for this shop. + """ + presentmentPriceRanges( + """ + Specifies the presentment currencies to return a price range in. + """ + presentmentCurrencies: [CurrencyCode!] + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): ProductPriceRangeConnection! + + """ + The price range. + """ + priceRange: ProductPriceRange! + + """ + A categorization that a product can be tagged with, commonly used for filtering and searching. + """ + productType: String! + + """ + The date and time when the product was published to the channel. + """ + publishedAt: DateTime! + + """ + The product's SEO information. + """ + seo: SEO! + + """ + A comma separated list of tags that have been added to the product. + Additional access scope required for private apps: unauthenticated_read_product_tags. + """ + tags: [String!]! + + """ + The product’s title. + """ + title: String! + + """ + The total quantity of inventory in stock for this Product. + """ + totalInventory: Int + + """ + The date and time when the product was last modified. + A product's `updatedAt` value can change for different reasons. For example, if an order + is placed for a product that has inventory tracking set up, then the inventory adjustment + is counted as an update. + """ + updatedAt: DateTime! + + """ + Find a product’s variant based on its selected options. + This is useful for converting a user’s selection of product options into a single matching variant. + If there is not a variant for the selected options, `null` will be returned. + """ + variantBySelectedOptions( + """ + The input fields used for a selected option. + """ + selectedOptions: [SelectedOptionInput!]! + ): ProductVariant + + """ + List of the product’s variants. + """ + variants( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductVariantSortKeys = POSITION + ): ProductVariantConnection! + + """ + The product’s vendor name. + """ + vendor: String! +} + +""" +The set of valid sort keys for the ProductCollection query. +""" +enum ProductCollectionSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `price` value. + """ + PRICE + + """ + Sort by the `best-selling` value. + """ + BEST_SELLING + + """ + Sort by the `created` value. + """ + CREATED + + """ + Sort by the `id` value. + """ + ID + + """ + Sort by the `manual` value. + """ + MANUAL + + """ + Sort by the `collection-default` value. + """ + COLLECTION_DEFAULT + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +An auto-generated type for paginating through multiple Products. +""" +type ProductConnection { + """ + A list of edges. + """ + edges: [ProductEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one Product and a cursor during pagination. +""" +type ProductEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ProductEdge. + """ + node: Product! +} + +""" +The set of valid sort keys for the ProductImage query. +""" +enum ProductImageSortKeys { + """ + Sort by the `created_at` value. + """ + CREATED_AT + + """ + Sort by the `position` value. + """ + POSITION + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +The set of valid sort keys for the ProductMedia query. +""" +enum ProductMediaSortKeys { + """ + Sort by the `position` value. + """ + POSITION + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +Product property names like "Size", "Color", and "Material" that the customers can select. +Variants are selected based on permutations of these options. +255 characters limit each. +""" +type ProductOption implements Node { + """ + Globally unique identifier. + """ + id: ID! + + """ + The product option’s name. + """ + name: String! + + """ + The corresponding value to the product option name. + """ + values: [String!]! +} + +""" +The price range of the product. +""" +type ProductPriceRange { + """ + The highest variant's price. + """ + maxVariantPrice: MoneyV2! + + """ + The lowest variant's price. + """ + minVariantPrice: MoneyV2! +} + +""" +An auto-generated type for paginating through multiple ProductPriceRanges. +""" +type ProductPriceRangeConnection { + """ + A list of edges. + """ + edges: [ProductPriceRangeEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one ProductPriceRange and a cursor during pagination. +""" +type ProductPriceRangeEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ProductPriceRangeEdge. + """ + node: ProductPriceRange! +} + +""" +The set of valid sort keys for the Product query. +""" +enum ProductSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `product_type` value. + """ + PRODUCT_TYPE + + """ + Sort by the `vendor` value. + """ + VENDOR + + """ + Sort by the `updated_at` value. + """ + UPDATED_AT + + """ + Sort by the `created_at` value. + """ + CREATED_AT + + """ + Sort by the `best_selling` value. + """ + BEST_SELLING + + """ + Sort by the `price` value. + """ + PRICE + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +A product variant represents a different version of a product, such as differing sizes or differing colors. +""" +type ProductVariant implements Node & HasMetafields { + """ + Indicates if the product variant is in stock. + """ + available: Boolean @deprecated(reason: "Use `availableForSale` instead") + + """ + Indicates if the product variant is available for sale. + """ + availableForSale: Boolean! + + """ + The compare at price of the variant. This can be used to mark a variant as on sale, when `compareAtPrice` is higher than `price`. + """ + compareAtPrice: Money @deprecated(reason: "Use `compareAtPriceV2` instead") + + """ + The compare at price of the variant. This can be used to mark a variant as on sale, when `compareAtPriceV2` is higher than `priceV2`. + """ + compareAtPriceV2: MoneyV2 + + """ + Whether a product is out of stock but still available for purchase (used for backorders). + """ + currentlyNotInStock: Boolean! + + """ + Globally unique identifier. + """ + id: ID! + + """ + Image associated with the product variant. This field falls back to the product image if no image is available. + """ + image( + """ + Image width in pixels between 1 and 2048. This argument is deprecated: Use `maxWidth` on `Image.transformedSrc` instead. + """ + maxWidth: Int + + """ + Image height in pixels between 1 and 2048. This argument is deprecated: Use `maxHeight` on `Image.transformedSrc` instead. + """ + maxHeight: Int + + """ + Crops the image according to the specified region. This argument is deprecated: Use `crop` on `Image.transformedSrc` instead. + """ + crop: CropRegion + + """ + Image size multiplier for high-resolution retina displays. Must be between 1 and 3. This argument is deprecated: Use `scale` on `Image.transformedSrc` instead. + """ + scale: Int = 1 + ): Image + + """ + The metafield associated with the resource. + """ + metafield( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String! + + """ + Identifier for the metafield (maximum of 30 characters). + """ + key: String! + ): Metafield + + """ + A paginated list of metafields associated with the resource. + """ + metafields( + """ + Container for a set of metafields (maximum of 20 characters). + """ + namespace: String + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): MetafieldConnection! + + """ + List of prices and compare-at prices in the presentment currencies for this shop. + """ + presentmentPrices( + """ + The presentment currencies prices should return in. + """ + presentmentCurrencies: [CurrencyCode!] + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): ProductVariantPricePairConnection! + + """ + List of unit prices in the presentment currencies for this shop. + """ + presentmentUnitPrices( + """ + Specify the currencies in which to return presentment unit prices. + """ + presentmentCurrencies: [CurrencyCode!] + + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + ): MoneyV2Connection! + + """ + The product variant’s price. + """ + price: Money! @deprecated(reason: "Use `priceV2` instead") + + """ + The product variant’s price. + """ + priceV2: MoneyV2! + + """ + The product object that the product variant belongs to. + """ + product: Product! + + """ + The total sellable quantity of the variant for online sales channels. + """ + quantityAvailable: Int + + """ + Whether a customer needs to provide a shipping address when placing an order for the product variant. + """ + requiresShipping: Boolean! + + """ + List of product options applied to the variant. + """ + selectedOptions: [SelectedOption!]! + + """ + The SKU (stock keeping unit) associated with the variant. + """ + sku: String + + """ + The product variant’s title. + """ + title: String! + + """ + The unit price value for the variant based on the variant's measurement. + """ + unitPrice: MoneyV2 + + """ + The unit price measurement for the variant. + """ + unitPriceMeasurement: UnitPriceMeasurement + + """ + The weight of the product variant in the unit system specified with `weight_unit`. + """ + weight: Float + + """ + Unit of measurement for weight. + """ + weightUnit: WeightUnit! +} + +""" +An auto-generated type for paginating through multiple ProductVariants. +""" +type ProductVariantConnection { + """ + A list of edges. + """ + edges: [ProductVariantEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one ProductVariant and a cursor during pagination. +""" +type ProductVariantEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ProductVariantEdge. + """ + node: ProductVariant! +} + +""" +The compare-at price and price of a variant sharing a currency. +""" +type ProductVariantPricePair { + """ + The compare-at price of the variant with associated currency. + """ + compareAtPrice: MoneyV2 + + """ + The price of the variant with associated currency. + """ + price: MoneyV2! +} + +""" +An auto-generated type for paginating through multiple ProductVariantPricePairs. +""" +type ProductVariantPricePairConnection { + """ + A list of edges. + """ + edges: [ProductVariantPricePairEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one ProductVariantPricePair and a cursor during pagination. +""" +type ProductVariantPricePairEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of ProductVariantPricePairEdge. + """ + node: ProductVariantPricePair! +} + +""" +The set of valid sort keys for the ProductVariant query. +""" +enum ProductVariantSortKeys { + """ + Sort by the `title` value. + """ + TITLE + + """ + Sort by the `sku` value. + """ + SKU + + """ + Sort by the `position` value. + """ + POSITION + + """ + Sort by the `id` value. + """ + ID + + """ + During a search (i.e. when the `query` parameter has been specified on the connection) this sorts the + results by relevance to the search term(s). When no search query is specified, this sort key is not + deterministic and should not be used. + """ + RELEVANCE +} + +""" +The schema’s entry-point for queries. This acts as the public, top-level API from which all queries must start. +""" +type QueryRoot { + """ + List of the shop's articles. + """ + articles( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ArticleSortKeys = ID + + """ + Supported filter parameters: + - `author` + - `blog_title` + - `created_at` + - `tag` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): ArticleConnection! + + """ + Find a blog by its handle. + """ + blogByHandle( + """ + The handle of the blog. + """ + handle: String! + ): Blog + + """ + List of the shop's blogs. + """ + blogs( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: BlogSortKeys = ID + + """ + Supported filter parameters: + - `created_at` + - `handle` + - `title` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): BlogConnection! + + """ + Find a collection by its handle. + """ + collectionByHandle( + """ + The handle of the collection. + """ + handle: String! + ): Collection + + """ + List of the shop’s collections. + """ + collections( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: CollectionSortKeys = ID + + """ + Supported filter parameters: + - `collection_type` + - `title` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): CollectionConnection! + + """ + Find a customer by its access token. + """ + customer( + """ + The customer access token. + """ + customerAccessToken: String! + ): Customer + node( + """ + The ID of the Node to return. + """ + id: ID! + ): Node + nodes( + """ + The IDs of the Nodes to return. + """ + ids: [ID!]! + ): [Node]! + + """ + Find a page by its handle. + """ + pageByHandle( + """ + The handle of the page. + """ + handle: String! + ): Page + + """ + List of the shop's pages. + """ + pages( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: PageSortKeys = ID + + """ + Supported filter parameters: + - `created_at` + - `handle` + - `title` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): PageConnection! + + """ + Find a product by its handle. + """ + productByHandle( + """ + The handle of the product. + """ + handle: String! + ): Product + + """ + Find recommended products related to a given `product_id`. + To learn more about how recommendations are generated, see + [*Showing product recommendations on product pages*](https://help.shopify.com/themes/development/recommended-products). + """ + productRecommendations( + """ + The id of the product. + """ + productId: ID! + ): [Product!] + + """ + Tags added to products. + Additional access scope required: unauthenticated_read_product_tags. + """ + productTags( + """ + Returns up to the first `n` elements from the list. + """ + first: Int! + ): StringConnection! + + """ + List of product types for the shop's products that are published to your app. + """ + productTypes( + """ + Returns up to the first `n` elements from the list. + """ + first: Int! + ): StringConnection! + + """ + List of the shop’s products. + """ + products( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductSortKeys = ID + + """ + Supported filter parameters: + - `available_for_sale` + - `created_at` + - `product_type` + - `tag` + - `title` + - `updated_at` + - `variants.price` + - `vendor` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): ProductConnection! + + """ + The list of public Storefront API versions, including supported, release candidate and unstable versions. + """ + publicApiVersions: [ApiVersion!]! + + """ + The shop associated with the storefront access token. + """ + shop: Shop! +} + +""" +SEO information. +""" +type SEO { + """ + The meta description. + """ + description: String + + """ + The SEO title. + """ + title: String +} + +""" +Script discount applications capture the intentions of a discount that +was created by a Shopify Script. +""" +type ScriptDiscountApplication implements DiscountApplication { + """ + The method by which the discount's value is allocated to its entitled items. + """ + allocationMethod: DiscountApplicationAllocationMethod! + + """ + The description of the application as defined by the Script. + """ + description: String! @deprecated(reason: "Use `title` instead") + + """ + Which lines of targetType that the discount is allocated over. + """ + targetSelection: DiscountApplicationTargetSelection! + + """ + The type of line that the discount is applicable towards. + """ + targetType: DiscountApplicationTargetType! + + """ + The title of the application as defined by the Script. + """ + title: String! + + """ + The value of the discount application. + """ + value: PricingValue! +} + +""" +Properties used by customers to select a product variant. +Products can have multiple options, like different sizes or colors. +""" +type SelectedOption { + """ + The product option’s name. + """ + name: String! + + """ + The product option’s value. + """ + value: String! +} + +""" +Specifies the input fields required for a selected option. +""" +input SelectedOptionInput { + """ + The product option’s name. + """ + name: String! + + """ + The product option’s value. + """ + value: String! +} + +""" +A shipping rate to be applied to a checkout. +""" +type ShippingRate { + """ + Human-readable unique identifier for this shipping rate. + """ + handle: String! + + """ + Price of this shipping rate. + """ + price: Money! @deprecated(reason: "Use `priceV2` instead") + + """ + Price of this shipping rate. + """ + priceV2: MoneyV2! + + """ + Title of this shipping rate. + """ + title: String! +} + +""" +Shop represents a collection of the general settings and information about the shop. +""" +type Shop { + """ + List of the shop' articles. + """ + articles( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ArticleSortKeys = ID + + """ + Supported filter parameters: + - `author` + - `blog_title` + - `created_at` + - `tag` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): ArticleConnection! @deprecated(reason: "Use `QueryRoot.articles` instead.") + + """ + List of the shop' blogs. + """ + blogs( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: BlogSortKeys = ID + + """ + Supported filter parameters: + - `created_at` + - `handle` + - `title` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): BlogConnection! @deprecated(reason: "Use `QueryRoot.blogs` instead.") + + """ + Find a collection by its handle. + """ + collectionByHandle( + """ + The handle of the collection. + """ + handle: String! + ): Collection + @deprecated(reason: "Use `QueryRoot.collectionByHandle` instead.") + + """ + List of the shop’s collections. + """ + collections( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: CollectionSortKeys = ID + + """ + Supported filter parameters: + - `collection_type` + - `title` + - `updated_at` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): CollectionConnection! + @deprecated(reason: "Use `QueryRoot.collections` instead.") + + """ + The three-letter code for the currency that the shop accepts. + """ + currencyCode: CurrencyCode! + @deprecated(reason: "Use `paymentSettings` instead") + + """ + A description of the shop. + """ + description: String + + """ + A string representing the way currency is formatted when the currency isn’t specified. + """ + moneyFormat: String! + + """ + The shop’s name. + """ + name: String! + + """ + Settings related to payments. + """ + paymentSettings: PaymentSettings! + + """ + The shop’s primary domain. + """ + primaryDomain: Domain! + + """ + The shop’s privacy policy. + """ + privacyPolicy: ShopPolicy + + """ + Find a product by its handle. + """ + productByHandle( + """ + The handle of the product. + """ + handle: String! + ): Product @deprecated(reason: "Use `QueryRoot.productByHandle` instead.") + + """ + A list of tags that have been added to products. + Additional access scope required: unauthenticated_read_product_tags. + """ + productTags( + """ + Returns up to the first `n` elements from the list. + """ + first: Int! + ): StringConnection! + @deprecated(reason: "Use `QueryRoot.productTags` instead.") + + """ + List of the shop’s product types. + """ + productTypes( + """ + Returns up to the first `n` elements from the list. + """ + first: Int! + ): StringConnection! + @deprecated(reason: "Use `QueryRoot.productTypes` instead.") + + """ + List of the shop’s products. + """ + products( + """ + Returns up to the first `n` elements from the list. + """ + first: Int + + """ + Returns the elements that come after the specified cursor. + """ + after: String + + """ + Returns up to the last `n` elements from the list. + """ + last: Int + + """ + Returns the elements that come before the specified cursor. + """ + before: String + + """ + Reverse the order of the underlying list. + """ + reverse: Boolean = false + + """ + Sort the underlying list by the given key. + """ + sortKey: ProductSortKeys = ID + + """ + Supported filter parameters: + - `available_for_sale` + - `created_at` + - `product_type` + - `tag` + - `title` + - `updated_at` + - `variants.price` + - `vendor` + + See the detailed [search syntax](https://help.shopify.com/api/getting-started/search-syntax) + for more information about using filters. + """ + query: String + ): ProductConnection! @deprecated(reason: "Use `QueryRoot.products` instead.") + + """ + The shop’s refund policy. + """ + refundPolicy: ShopPolicy + + """ + The shop’s shipping policy. + """ + shippingPolicy: ShopPolicy + + """ + Countries that the shop ships to. + """ + shipsToCountries: [CountryCode!]! + + """ + The shop’s Shopify Payments account id. + """ + shopifyPaymentsAccountId: String + @deprecated(reason: "Use `paymentSettings` instead") + + """ + The shop’s terms of service. + """ + termsOfService: ShopPolicy +} + +""" +Policy that a merchant has configured for their store, such as their refund or privacy policy. +""" +type ShopPolicy implements Node { + """ + Policy text, maximum size of 64kb. + """ + body: String! + + """ + Policy’s handle. + """ + handle: String! + + """ + Globally unique identifier. + """ + id: ID! + + """ + Policy’s title. + """ + title: String! + + """ + Public URL to the policy. + """ + url: URL! +} + +""" +An auto-generated type for paginating through multiple Strings. +""" +type StringConnection { + """ + A list of edges. + """ + edges: [StringEdge!]! + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An auto-generated type which holds one String and a cursor during pagination. +""" +type StringEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of StringEdge. + """ + node: String! +} + +""" +Specifies the fields required to complete a checkout with +a tokenized payment. +""" +input TokenizedPaymentInput { + """ + The amount of the payment. + """ + amount: Money! + + """ + A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. + """ + idempotencyKey: String! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddressInput! + + """ + The type of payment token. + """ + type: String! + + """ + A simple string or JSON containing the required payment data for the tokenized payment. + """ + paymentData: String! + + """ + Executes the payment in test mode if possible. Defaults to `false`. + """ + test: Boolean + + """ + Public Hash Key used for AndroidPay payments only. + """ + identifier: String +} + +""" +Specifies the fields required to complete a checkout with +a tokenized payment. +""" +input TokenizedPaymentInputV2 { + """ + The amount and currency of the payment. + """ + paymentAmount: MoneyInput! + + """ + A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. + """ + idempotencyKey: String! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddressInput! + + """ + A simple string or JSON containing the required payment data for the tokenized payment. + """ + paymentData: String! + + """ + Whether to execute the payment in test mode, if possible. Test mode is not supported in production stores. Defaults to `false`. + """ + test: Boolean + + """ + Public Hash Key used for AndroidPay payments only. + """ + identifier: String + + """ + The type of payment token. + """ + type: String! +} + +""" +Specifies the fields required to complete a checkout with +a tokenized payment. +""" +input TokenizedPaymentInputV3 { + """ + The amount and currency of the payment. + """ + paymentAmount: MoneyInput! + + """ + A unique client generated key used to avoid duplicate charges. When a duplicate payment is found, the original is returned instead of creating a new one. + """ + idempotencyKey: String! + + """ + The billing address for the payment. + """ + billingAddress: MailingAddressInput! + + """ + A simple string or JSON containing the required payment data for the tokenized payment. + """ + paymentData: String! + + """ + Whether to execute the payment in test mode, if possible. Test mode is not supported in production stores. Defaults to `false`. + """ + test: Boolean + + """ + Public Hash Key used for AndroidPay payments only. + """ + identifier: String + + """ + The type of payment token. + """ + type: PaymentTokenType! +} + +""" +An object representing exchange of money for a product or service. +""" +type Transaction { + """ + The amount of money that the transaction was for. + """ + amount: Money! @deprecated(reason: "Use `amountV2` instead") + + """ + The amount of money that the transaction was for. + """ + amountV2: MoneyV2! + + """ + The kind of the transaction. + """ + kind: TransactionKind! + + """ + The status of the transaction. + """ + status: TransactionStatus! @deprecated(reason: "Use `statusV2` instead") + + """ + The status of the transaction. + """ + statusV2: TransactionStatus + + """ + Whether the transaction was done in test mode or not. + """ + test: Boolean! +} + +enum TransactionKind { + SALE + CAPTURE + AUTHORIZATION + EMV_AUTHORIZATION + CHANGE +} + +enum TransactionStatus { + PENDING + SUCCESS + FAILURE + ERROR +} + +""" +An RFC 3986 and RFC 3987 compliant URI string. + +Example value: `"https://johns-apparel.myshopify.com"`. +""" +scalar URL + +""" +The measurement used to calculate a unit price for a product variant (e.g. $9.99 / 100ml). +""" +type UnitPriceMeasurement { + """ + The type of unit of measurement for the unit price measurement. + """ + measuredType: UnitPriceMeasurementMeasuredType + + """ + The quantity unit for the unit price measurement. + """ + quantityUnit: UnitPriceMeasurementMeasuredUnit + + """ + The quantity value for the unit price measurement. + """ + quantityValue: Float! + + """ + The reference unit for the unit price measurement. + """ + referenceUnit: UnitPriceMeasurementMeasuredUnit + + """ + The reference value for the unit price measurement. + """ + referenceValue: Int! +} + +""" +The accepted types of unit of measurement. +""" +enum UnitPriceMeasurementMeasuredType { + """ + Unit of measurements representing volumes. + """ + VOLUME + + """ + Unit of measurements representing weights. + """ + WEIGHT + + """ + Unit of measurements representing lengths. + """ + LENGTH + + """ + Unit of measurements representing areas. + """ + AREA +} + +""" +The valid units of measurement for a unit price measurement. +""" +enum UnitPriceMeasurementMeasuredUnit { + """ + 1000 milliliters equals 1 liter. + """ + ML + + """ + 100 centiliters equals 1 liter. + """ + CL + + """ + Metric system unit of volume. + """ + L + + """ + 1 cubic meter equals 1000 liters. + """ + M3 + + """ + 1000 milligrams equals 1 gram. + """ + MG + + """ + Metric system unit of weight. + """ + G + + """ + 1 kilogram equals 1000 grams. + """ + KG + + """ + 1000 millimeters equals 1 meter. + """ + MM + + """ + 100 centimeters equals 1 meter. + """ + CM + + """ + Metric system unit of length. + """ + M + + """ + Metric system unit of area. + """ + M2 +} + +""" +Represents an error in the input of a mutation. +""" +type UserError implements DisplayableError { + """ + Path to the input field which caused the error. + """ + field: [String!] + + """ + The error message. + """ + message: String! +} + +""" +Represents a Shopify hosted video. +""" +type Video implements Node & Media { + """ + A word or phrase to share the nature or contents of a media. + """ + alt: String + + """ + Globally unique identifier. + """ + id: ID! + + """ + The media content type. + """ + mediaContentType: MediaContentType! + + """ + The preview image for the media. + """ + previewImage: Image + + """ + The sources for a video. + """ + sources: [VideoSource!]! +} + +""" +Represents a source for a Shopify hosted video. +""" +type VideoSource { + """ + The format of the video source. + """ + format: String! + + """ + The height of the video. + """ + height: Int! + + """ + The video MIME type. + """ + mimeType: String! + + """ + The URL of the video. + """ + url: String! + + """ + The width of the video. + """ + width: Int! +} + +""" +Units of measurement for weight. +""" +enum WeightUnit { + """ + 1 kilogram equals 1000 grams. + """ + KILOGRAMS + + """ + Metric system unit of mass. + """ + GRAMS + + """ + 1 pound equals 16 ounces. + """ + POUNDS + + """ + Imperial system unit of mass. + """ + OUNCES +} diff --git a/framework/swell/types.ts b/framework/swell/types.ts new file mode 100644 index 000000000..c4e42b67d --- /dev/null +++ b/framework/swell/types.ts @@ -0,0 +1,45 @@ +import * as Core from '@commerce/types' +import { CheckoutLineItem } from './schema' + +export type ShopifyCheckout = { + id: string + webUrl: string + lineItems: CheckoutLineItem[] +} + +export interface Cart extends Core.Cart { + id: string + lineItems: LineItem[] +} + +export interface LineItem extends Core.LineItem { + options: any[] +} + +/** + * Cart mutations + */ + +export type OptionSelections = { + option_id: number + option_value: number | string +} + +export type CartItemBody = Core.CartItemBody & { + productId: string // The product id is always required for BC + optionSelections?: OptionSelections +} + +export type GetCartHandlerBody = Core.GetCartHandlerBody + +export type AddCartItemBody = Core.AddCartItemBody<CartItemBody> + +export type AddCartItemHandlerBody = Core.AddCartItemHandlerBody<CartItemBody> + +export type UpdateCartItemBody = Core.UpdateCartItemBody<CartItemBody> + +export type UpdateCartItemHandlerBody = Core.UpdateCartItemHandlerBody<CartItemBody> + +export type RemoveCartItemBody = Core.RemoveCartItemBody + +export type RemoveCartItemHandlerBody = Core.RemoveCartItemHandlerBody diff --git a/framework/swell/utils/customer-token.ts b/framework/swell/utils/customer-token.ts new file mode 100644 index 000000000..85454cb83 --- /dev/null +++ b/framework/swell/utils/customer-token.ts @@ -0,0 +1,21 @@ +import Cookies, { CookieAttributes } from 'js-cookie' +import { SHOPIFY_COOKIE_EXPIRE, SHOPIFY_CUSTOMER_TOKEN_COOKIE } from '../const' + +export const getCustomerToken = () => Cookies.get(SHOPIFY_CUSTOMER_TOKEN_COOKIE) + +export const setCustomerToken = ( + token: string | null, + options?: CookieAttributes +) => { + if (!token) { + Cookies.remove(SHOPIFY_CUSTOMER_TOKEN_COOKIE) + } else { + Cookies.set( + SHOPIFY_CUSTOMER_TOKEN_COOKIE, + token, + options ?? { + expires: SHOPIFY_COOKIE_EXPIRE, + } + ) + } +} diff --git a/framework/swell/utils/get-categories.ts b/framework/swell/utils/get-categories.ts new file mode 100644 index 000000000..cce4b2ad7 --- /dev/null +++ b/framework/swell/utils/get-categories.ts @@ -0,0 +1,29 @@ +import { ShopifyConfig } from '../api' +import { CollectionEdge } from '../schema' +import getSiteCollectionsQuery from './queries/get-all-collections-query' + +export type Category = { + entityId: string + name: string + path: string +} + +const getCategories = async (config: ShopifyConfig): Promise<Category[]> => { + const { data } = await config.fetch(getSiteCollectionsQuery, { + variables: { + first: 250, + }, + }) + + return ( + data.collections?.edges?.map( + ({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({ + entityId, + name, + path: `/${handle}`, + }) + ) ?? [] + ) +} + +export default getCategories diff --git a/framework/swell/utils/get-checkout-id.ts b/framework/swell/utils/get-checkout-id.ts new file mode 100644 index 000000000..11e3802d9 --- /dev/null +++ b/framework/swell/utils/get-checkout-id.ts @@ -0,0 +1,8 @@ +import Cookies from 'js-cookie' +import { SHOPIFY_CHECKOUT_ID_COOKIE } from '../const' + +const getCheckoutId = (id?: string) => { + return id ?? Cookies.get(SHOPIFY_CHECKOUT_ID_COOKIE) +} + +export default getCheckoutId diff --git a/framework/swell/utils/get-search-variables.ts b/framework/swell/utils/get-search-variables.ts new file mode 100644 index 000000000..c1b40ae5d --- /dev/null +++ b/framework/swell/utils/get-search-variables.ts @@ -0,0 +1,27 @@ +import getSortVariables from './get-sort-variables' +import type { SearchProductsInput } from '../product/use-search' + +export const getSearchVariables = ({ + brandId, + search, + categoryId, + sort, +}: SearchProductsInput) => { + let query = '' + + if (search) { + query += `product_type:${search} OR title:${search} OR tag:${search}` + } + + if (brandId) { + query += `${search ? ' AND ' : ''}vendor:${brandId}` + } + + return { + categoryId, + query, + ...getSortVariables(sort, !!categoryId), + } +} + +export default getSearchVariables diff --git a/framework/swell/utils/get-sort-variables.ts b/framework/swell/utils/get-sort-variables.ts new file mode 100644 index 000000000..b8cdeec51 --- /dev/null +++ b/framework/swell/utils/get-sort-variables.ts @@ -0,0 +1,32 @@ +const getSortVariables = (sort?: string, isCategory = false) => { + let output = {} + switch (sort) { + case 'price-asc': + output = { + sortKey: 'PRICE', + reverse: false, + } + break + case 'price-desc': + output = { + sortKey: 'PRICE', + reverse: true, + } + break + case 'trending-desc': + output = { + sortKey: 'BEST_SELLING', + reverse: false, + } + break + case 'latest-desc': + output = { + sortKey: isCategory ? 'CREATED' : 'CREATED_AT', + reverse: true, + } + break + } + return output +} + +export default getSortVariables diff --git a/framework/swell/utils/get-vendors.ts b/framework/swell/utils/get-vendors.ts new file mode 100644 index 000000000..f04483bb1 --- /dev/null +++ b/framework/swell/utils/get-vendors.ts @@ -0,0 +1,36 @@ +import { ShopifyConfig } from '../api' +import fetchAllProducts from '../api/utils/fetch-all-products' +import getAllProductVendors from './queries/get-all-product-vendors-query' + +export type BrandNode = { + name: string + path: string +} + +export type BrandEdge = { + node: BrandNode +} + +export type Brands = BrandEdge[] + +const getVendors = async (config: ShopifyConfig): Promise<BrandEdge[]> => { + const vendors = await fetchAllProducts({ + config, + query: getAllProductVendors, + variables: { + first: 250, + }, + }) + + let vendorsStrings = vendors.map(({ node: { vendor } }) => vendor) + + return [...new Set(vendorsStrings)].map((v) => ({ + node: { + entityId: v, + name: v, + path: `brands/${v}`, + }, + })) +} + +export default getVendors diff --git a/framework/swell/utils/handle-fetch-response.ts b/framework/swell/utils/handle-fetch-response.ts new file mode 100644 index 000000000..8d7427d91 --- /dev/null +++ b/framework/swell/utils/handle-fetch-response.ts @@ -0,0 +1,27 @@ +import { FetcherError } from '@commerce/utils/errors' + +export function getError(errors: any[], status: number) { + errors = errors ?? [{ message: 'Failed to fetch Shopify API' }] + return new FetcherError({ errors, status }) +} + +export async function getAsyncError(res: Response) { + const data = await res.json() + return getError(data.errors, res.status) +} + +const handleFetchResponse = async (res: Response) => { + if (res.ok) { + const { data, errors } = await res.json() + + if (errors && errors.length) { + throw getError(errors, res.status) + } + + return data + } + + throw await getAsyncError(res) +} + +export default handleFetchResponse diff --git a/framework/swell/utils/handle-login.ts b/framework/swell/utils/handle-login.ts new file mode 100644 index 000000000..77b6873e3 --- /dev/null +++ b/framework/swell/utils/handle-login.ts @@ -0,0 +1,39 @@ +import { ValidationError } from '@commerce/utils/errors' +import { setCustomerToken } from './customer-token' + +const getErrorMessage = ({ + code, + message, +}: { + code: string + message: string +}) => { + switch (code) { + case 'UNIDENTIFIED_CUSTOMER': + message = 'Cannot find an account that matches the provided credentials' + break + } + return message +} + +const handleLogin = (data: any) => { + const response = data.customerAccessTokenCreate + const errors = response?.customerUserErrors + + if (errors && errors.length) { + throw new ValidationError({ + message: getErrorMessage(errors[0]), + }) + } + + const customerAccessToken = response?.customerAccessToken + const accessToken = customerAccessToken?.accessToken + + if (accessToken) { + setCustomerToken(accessToken) + } + + return customerAccessToken +} + +export default handleLogin diff --git a/framework/swell/utils/index.ts b/framework/swell/utils/index.ts new file mode 100644 index 000000000..2d59aa506 --- /dev/null +++ b/framework/swell/utils/index.ts @@ -0,0 +1,10 @@ +export { default as handleFetchResponse } from './handle-fetch-response' +export { default as getSearchVariables } from './get-search-variables' +export { default as getSortVariables } from './get-sort-variables' +export { default as getVendors } from './get-vendors' +export { default as getCategories } from './get-categories' +export { default as getCheckoutId } from './get-checkout-id' +export * from './queries' +export * from './mutations' +export * from './normalize' +export * from './customer-token' diff --git a/framework/swell/utils/mutations/associate-customer-with-checkout.ts b/framework/swell/utils/mutations/associate-customer-with-checkout.ts new file mode 100644 index 000000000..6b1350e05 --- /dev/null +++ b/framework/swell/utils/mutations/associate-customer-with-checkout.ts @@ -0,0 +1,18 @@ +const associateCustomerWithCheckoutMutation = /* GraphQl */ ` +mutation associateCustomerWithCheckout($checkoutId: ID!, $customerAccessToken: String!) { + checkoutCustomerAssociateV2(checkoutId: $checkoutId, customerAccessToken: $customerAccessToken) { + checkout { + id + } + checkoutUserErrors { + code + field + message + } + customer { + id + } + } + } +` +export default associateCustomerWithCheckoutMutation diff --git a/framework/swell/utils/mutations/checkout-create.ts b/framework/swell/utils/mutations/checkout-create.ts new file mode 100644 index 000000000..912e1cbd2 --- /dev/null +++ b/framework/swell/utils/mutations/checkout-create.ts @@ -0,0 +1,16 @@ +import { checkoutDetailsFragment } from '../queries/get-checkout-query' + +const checkoutCreateMutation = /* GraphQL */ ` + mutation { + checkoutCreate(input: {}) { + userErrors { + message + field + } + checkout { + ${checkoutDetailsFragment} + } + } + } +` +export default checkoutCreateMutation diff --git a/framework/swell/utils/mutations/checkout-line-item-add.ts b/framework/swell/utils/mutations/checkout-line-item-add.ts new file mode 100644 index 000000000..67b9cf250 --- /dev/null +++ b/framework/swell/utils/mutations/checkout-line-item-add.ts @@ -0,0 +1,16 @@ +import { checkoutDetailsFragment } from '../queries/get-checkout-query' + +const checkoutLineItemAddMutation = /* GraphQL */ ` + mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemInput!]!) { + checkoutLineItemsAdd(checkoutId: $checkoutId, lineItems: $lineItems) { + userErrors { + message + field + } + checkout { + ${checkoutDetailsFragment} + } + } + } +` +export default checkoutLineItemAddMutation diff --git a/framework/swell/utils/mutations/checkout-line-item-remove.ts b/framework/swell/utils/mutations/checkout-line-item-remove.ts new file mode 100644 index 000000000..d967a5168 --- /dev/null +++ b/framework/swell/utils/mutations/checkout-line-item-remove.ts @@ -0,0 +1,19 @@ +import { checkoutDetailsFragment } from '../queries/get-checkout-query' + +const checkoutLineItemRemoveMutation = /* GraphQL */ ` + mutation($checkoutId: ID!, $lineItemIds: [ID!]!) { + checkoutLineItemsRemove( + checkoutId: $checkoutId + lineItemIds: $lineItemIds + ) { + userErrors { + message + field + } + checkout { + ${checkoutDetailsFragment} + } + } + } +` +export default checkoutLineItemRemoveMutation diff --git a/framework/swell/utils/mutations/checkout-line-item-update.ts b/framework/swell/utils/mutations/checkout-line-item-update.ts new file mode 100644 index 000000000..8edf17587 --- /dev/null +++ b/framework/swell/utils/mutations/checkout-line-item-update.ts @@ -0,0 +1,16 @@ +import { checkoutDetailsFragment } from '../queries/get-checkout-query' + +const checkoutLineItemUpdateMutation = /* GraphQL */ ` + mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemUpdateInput!]!) { + checkoutLineItemsUpdate(checkoutId: $checkoutId, lineItems: $lineItems) { + userErrors { + message + field + } + checkout { + ${checkoutDetailsFragment} + } + } + } +` +export default checkoutLineItemUpdateMutation diff --git a/framework/swell/utils/mutations/customer-access-token-create.ts b/framework/swell/utils/mutations/customer-access-token-create.ts new file mode 100644 index 000000000..7a45c3f49 --- /dev/null +++ b/framework/swell/utils/mutations/customer-access-token-create.ts @@ -0,0 +1,16 @@ +const customerAccessTokenCreateMutation = /* GraphQL */ ` + mutation customerAccessTokenCreate($input: CustomerAccessTokenCreateInput!) { + customerAccessTokenCreate(input: $input) { + customerAccessToken { + accessToken + expiresAt + } + customerUserErrors { + code + field + message + } + } + } +` +export default customerAccessTokenCreateMutation diff --git a/framework/swell/utils/mutations/customer-access-token-delete.ts b/framework/swell/utils/mutations/customer-access-token-delete.ts new file mode 100644 index 000000000..c46eff1e5 --- /dev/null +++ b/framework/swell/utils/mutations/customer-access-token-delete.ts @@ -0,0 +1,14 @@ +const customerAccessTokenDeleteMutation = /* GraphQL */ ` + mutation customerAccessTokenDelete($customerAccessToken: String!) { + customerAccessTokenDelete(customerAccessToken: $customerAccessToken) { + deletedAccessToken + deletedCustomerAccessTokenId + userErrors { + field + message + } + } + } +` + +export default customerAccessTokenDeleteMutation diff --git a/framework/swell/utils/mutations/customer-create.ts b/framework/swell/utils/mutations/customer-create.ts new file mode 100644 index 000000000..05c728a25 --- /dev/null +++ b/framework/swell/utils/mutations/customer-create.ts @@ -0,0 +1,15 @@ +const customerCreateMutation = /* GraphQL */ ` + mutation customerCreate($input: CustomerCreateInput!) { + customerCreate(input: $input) { + customerUserErrors { + code + field + message + } + customer { + id + } + } + } +` +export default customerCreateMutation diff --git a/framework/swell/utils/mutations/index.ts b/framework/swell/utils/mutations/index.ts new file mode 100644 index 000000000..3a16d7cec --- /dev/null +++ b/framework/swell/utils/mutations/index.ts @@ -0,0 +1,7 @@ +export { default as customerCreateMutation } from './customer-create' +export { default as checkoutCreateMutation } from './checkout-create' +export { default as checkoutLineItemAddMutation } from './checkout-line-item-add' +export { default as checkoutLineItemUpdateMutation } from './checkout-line-item-update' +export { default as checkoutLineItemRemoveMutation } from './checkout-line-item-remove' +export { default as customerAccessTokenCreateMutation } from './customer-access-token-create' +export { default as customerAccessTokenDeleteMutation } from './customer-access-token-delete' diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts new file mode 100644 index 000000000..c9b428b37 --- /dev/null +++ b/framework/swell/utils/normalize.ts @@ -0,0 +1,152 @@ +import { Product } from '@commerce/types' + +import { + Product as ShopifyProduct, + Checkout, + CheckoutLineItemEdge, + SelectedOption, + ImageConnection, + ProductVariantConnection, + MoneyV2, + ProductOption, +} from '../schema' + +import type { Cart, LineItem } from '../types' + +const money = ({ amount, currencyCode }: MoneyV2) => { + return { + value: +amount, + currencyCode, + } +} + +const normalizeProductOption = ({ + id, + name: displayName, + values, +}: ProductOption) => { + return { + __typename: 'MultipleChoiceOption', + id, + displayName, + values: values.map((value) => { + let output: any = { + label: value, + } + if (displayName === 'Color') { + output = { + ...output, + hexColors: [value], + } + } + return output + }), + } +} + +const normalizeProductImages = ({ edges }: ImageConnection) => + edges?.map(({ node: { originalSrc: url, ...rest } }) => ({ + url, + ...rest, + })) + +const normalizeProductVariants = ({ edges }: ProductVariantConnection) => { + return edges?.map( + ({ + node: { id, selectedOptions, sku, title, priceV2, compareAtPriceV2 }, + }) => ({ + id, + name: title, + sku: sku ?? id, + price: +priceV2.amount, + listPrice: +compareAtPriceV2?.amount, + requiresShipping: true, + options: selectedOptions.map(({ name, value }: SelectedOption) => + normalizeProductOption({ + id, + name, + values: [value], + }) + ), + }) + ) +} + +export function normalizeProduct(productNode: ShopifyProduct): Product { + const { + id, + title: name, + vendor, + images, + variants, + description, + handle, + priceRange, + options, + ...rest + } = productNode + + const product = { + id, + name, + vendor, + description, + path: `/${handle}`, + slug: handle?.replace(/^\/+|\/+$/g, ''), + price: money(priceRange?.minVariantPrice), + images: normalizeProductImages(images), + variants: variants ? normalizeProductVariants(variants) : [], + options: options ? options.map((o) => normalizeProductOption(o)) : [], + ...rest, + } + + return product +} + +export function normalizeCart(checkout: Checkout): Cart { + return { + id: checkout.id, + customerId: '', + email: '', + createdAt: checkout.createdAt, + currency: { + code: checkout.totalPriceV2?.currencyCode, + }, + taxesIncluded: checkout.taxesIncluded, + lineItems: checkout.lineItems?.edges.map(normalizeLineItem), + lineItemsSubtotalPrice: +checkout.subtotalPriceV2?.amount, + subtotalPrice: +checkout.subtotalPriceV2?.amount, + totalPrice: checkout.totalPriceV2?.amount, + discounts: [], + } +} + +function normalizeLineItem({ + node: { id, title, variant, quantity }, +}: CheckoutLineItemEdge): LineItem { + return { + id, + variantId: String(variant?.id), + productId: String(variant?.id), + name: `${title}`, + quantity, + variant: { + id: String(variant?.id), + sku: variant?.sku ?? '', + name: variant?.title!, + image: { + url: variant?.image?.originalSrc, + }, + requiresShipping: variant?.requiresShipping ?? false, + price: variant?.priceV2?.amount, + listPrice: variant?.compareAtPriceV2?.amount, + }, + path: '', + discounts: [], + options: [ + { + value: variant?.title, + }, + ], + } +} diff --git a/framework/swell/utils/queries/get-all-collections-query.ts b/framework/swell/utils/queries/get-all-collections-query.ts new file mode 100644 index 000000000..2abf374d6 --- /dev/null +++ b/framework/swell/utils/queries/get-all-collections-query.ts @@ -0,0 +1,14 @@ +const getSiteCollectionsQuery = /* GraphQL */ ` + query getSiteCollections($first: Int!) { + collections(first: $first) { + edges { + node { + id + title + handle + } + } + } + } +` +export default getSiteCollectionsQuery diff --git a/framework/swell/utils/queries/get-all-pages-query.ts b/framework/swell/utils/queries/get-all-pages-query.ts new file mode 100644 index 000000000..e3aee1f10 --- /dev/null +++ b/framework/swell/utils/queries/get-all-pages-query.ts @@ -0,0 +1,14 @@ +export const getAllPagesQuery = /* GraphQL */ ` + query getAllPages($first: Int = 250) { + pages(first: $first) { + edges { + node { + id + title + handle + } + } + } + } +` +export default getAllPagesQuery diff --git a/framework/swell/utils/queries/get-all-product-vendors-query.ts b/framework/swell/utils/queries/get-all-product-vendors-query.ts new file mode 100644 index 000000000..be08b8ec6 --- /dev/null +++ b/framework/swell/utils/queries/get-all-product-vendors-query.ts @@ -0,0 +1,17 @@ +const getAllProductVendors = /* GraphQL */ ` + query getAllProductVendors($first: Int = 250, $cursor: String) { + products(first: $first, after: $cursor) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + vendor + } + cursor + } + } + } +` +export default getAllProductVendors diff --git a/framework/swell/utils/queries/get-all-products-paths-query.ts b/framework/swell/utils/queries/get-all-products-paths-query.ts new file mode 100644 index 000000000..56298c204 --- /dev/null +++ b/framework/swell/utils/queries/get-all-products-paths-query.ts @@ -0,0 +1,17 @@ +const getAllProductsPathsQuery = /* GraphQL */ ` + query getAllProductPaths($first: Int = 250, $cursor: String) { + products(first: $first, after: $cursor) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + handle + } + cursor + } + } + } +` +export default getAllProductsPathsQuery diff --git a/framework/swell/utils/queries/get-all-products-query.ts b/framework/swell/utils/queries/get-all-products-query.ts new file mode 100644 index 000000000..5eb44c7a7 --- /dev/null +++ b/framework/swell/utils/queries/get-all-products-query.ts @@ -0,0 +1,57 @@ +export const productConnection = ` +pageInfo { + hasNextPage + hasPreviousPage +} +edges { + node { + id + title + vendor + handle + description + priceRange { + minVariantPrice { + amount + currencyCode + } + } + images(first: 1) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + originalSrc + altText + width + height + } + } + } + } +}` + +export const productsFragment = ` +products( + first: $first + sortKey: $sortKey + reverse: $reverse + query: $query +) { + ${productConnection} +} +` + +const getAllProductsQuery = /* GraphQL */ ` + query getAllProducts( + $first: Int = 250 + $query: String = "" + $sortKey: ProductSortKeys = RELEVANCE + $reverse: Boolean = false + ) { + ${productsFragment} + } +` +export default getAllProductsQuery diff --git a/framework/swell/utils/queries/get-checkout-query.ts b/framework/swell/utils/queries/get-checkout-query.ts new file mode 100644 index 000000000..194e1619a --- /dev/null +++ b/framework/swell/utils/queries/get-checkout-query.ts @@ -0,0 +1,62 @@ +export const checkoutDetailsFragment = ` + id + webUrl + subtotalPriceV2{ + amount + currencyCode + } + totalTaxV2 { + amount + currencyCode + } + totalPriceV2 { + amount + currencyCode + } + completedAt + createdAt + taxesIncluded + lineItems(first: 250) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + id + title + variant { + id + sku + title + image { + originalSrc + altText + width + height + } + priceV2{ + amount + currencyCode + } + compareAtPriceV2{ + amount + currencyCode + } + } + quantity + } + } + } +` + +const getCheckoutQuery = /* GraphQL */ ` + query($checkoutId: ID!) { + node(id: $checkoutId) { + ... on Checkout { + ${checkoutDetailsFragment} + } + } + } +` +export default getCheckoutQuery diff --git a/framework/swell/utils/queries/get-collection-products-query.ts b/framework/swell/utils/queries/get-collection-products-query.ts new file mode 100644 index 000000000..04766caa4 --- /dev/null +++ b/framework/swell/utils/queries/get-collection-products-query.ts @@ -0,0 +1,24 @@ +import { productConnection } from './get-all-products-query' + +const getCollectionProductsQuery = /* GraphQL */ ` + query getProductsFromCollection( + $categoryId: ID! + $first: Int = 250 + $sortKey: ProductCollectionSortKeys = RELEVANCE + $reverse: Boolean = false + ) { + node(id: $categoryId) { + id + ... on Collection { + products( + first: $first + sortKey: $sortKey + reverse: $reverse + ) { + ${productConnection} + } + } + } + } +` +export default getCollectionProductsQuery diff --git a/framework/swell/utils/queries/get-customer-id-query.ts b/framework/swell/utils/queries/get-customer-id-query.ts new file mode 100644 index 000000000..076ceb10b --- /dev/null +++ b/framework/swell/utils/queries/get-customer-id-query.ts @@ -0,0 +1,8 @@ +export const getCustomerQuery = /* GraphQL */ ` + query getCustomerId($customerAccessToken: String!) { + customer(customerAccessToken: $customerAccessToken) { + id + } + } +` +export default getCustomerQuery diff --git a/framework/swell/utils/queries/get-customer-query.ts b/framework/swell/utils/queries/get-customer-query.ts new file mode 100644 index 000000000..87e37e68d --- /dev/null +++ b/framework/swell/utils/queries/get-customer-query.ts @@ -0,0 +1,16 @@ +export const getCustomerQuery = /* GraphQL */ ` + query getCustomer($customerAccessToken: String!) { + customer(customerAccessToken: $customerAccessToken) { + id + firstName + lastName + displayName + email + phone + tags + acceptsMarketing + createdAt + } + } +` +export default getCustomerQuery diff --git a/framework/swell/utils/queries/get-page-query.ts b/framework/swell/utils/queries/get-page-query.ts new file mode 100644 index 000000000..2ca79abd4 --- /dev/null +++ b/framework/swell/utils/queries/get-page-query.ts @@ -0,0 +1,14 @@ +export const getPageQuery = /* GraphQL */ ` + query($id: ID!) { + node(id: $id) { + id + ... on Page { + title + handle + body + bodySummary + } + } + } +` +export default getPageQuery diff --git a/framework/swell/utils/queries/get-product-query.ts b/framework/swell/utils/queries/get-product-query.ts new file mode 100644 index 000000000..5c109901b --- /dev/null +++ b/framework/swell/utils/queries/get-product-query.ts @@ -0,0 +1,69 @@ +const getProductQuery = /* GraphQL */ ` + query getProductBySlug($slug: String!) { + productByHandle(handle: $slug) { + id + handle + title + productType + vendor + description + descriptionHtml + options { + id + name + values + } + priceRange { + maxVariantPrice { + amount + currencyCode + } + minVariantPrice { + amount + currencyCode + } + } + variants(first: 250) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + id + title + sku + selectedOptions { + name + value + } + priceV2 { + amount + currencyCode + } + compareAtPriceV2 { + amount + currencyCode + } + } + } + } + images(first: 250) { + pageInfo { + hasNextPage + hasPreviousPage + } + edges { + node { + originalSrc + altText + width + height + } + } + } + } + } +` + +export default getProductQuery diff --git a/framework/swell/utils/queries/index.ts b/framework/swell/utils/queries/index.ts new file mode 100644 index 000000000..e19be9c8c --- /dev/null +++ b/framework/swell/utils/queries/index.ts @@ -0,0 +1,10 @@ +export { default as getSiteCollectionsQuery } from './get-all-collections-query' +export { default as getProductQuery } from './get-product-query' +export { default as getAllProductsQuery } from './get-all-products-query' +export { default as getAllProductsPathtsQuery } from './get-all-products-paths-query' +export { default as getAllProductVendors } from './get-all-product-vendors-query' +export { default as getCollectionProductsQuery } from './get-collection-products-query' +export { default as getCheckoutQuery } from './get-checkout-query' +export { default as getAllPagesQuery } from './get-all-pages-query' +export { default as getPageQuery } from './get-page-query' +export { default as getCustomerQuery } from './get-customer-query' diff --git a/framework/swell/utils/storage.ts b/framework/swell/utils/storage.ts new file mode 100644 index 000000000..d46dadb21 --- /dev/null +++ b/framework/swell/utils/storage.ts @@ -0,0 +1,13 @@ +export const getCheckoutIdFromStorage = (token: string) => { + if (window && window.sessionStorage) { + return window.sessionStorage.getItem(token) + } + + return null +} + +export const setCheckoutIdInStorage = (token: string, id: string | number) => { + if (window && window.sessionStorage) { + return window.sessionStorage.setItem(token, id + '') + } +} diff --git a/framework/swell/wishlist/use-add-item.tsx b/framework/swell/wishlist/use-add-item.tsx new file mode 100644 index 000000000..75f067c3a --- /dev/null +++ b/framework/swell/wishlist/use-add-item.tsx @@ -0,0 +1,13 @@ +import { useCallback } from 'react' + +export function emptyHook() { + const useEmptyHook = async (options = {}) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/swell/wishlist/use-remove-item.tsx b/framework/swell/wishlist/use-remove-item.tsx new file mode 100644 index 000000000..a2d3a8a05 --- /dev/null +++ b/framework/swell/wishlist/use-remove-item.tsx @@ -0,0 +1,17 @@ +import { useCallback } from 'react' + +type Options = { + includeProducts?: boolean +} + +export function emptyHook(options?: Options) { + const useEmptyHook = async ({ id }: { id: string | number }) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/swell/wishlist/use-wishlist.tsx b/framework/swell/wishlist/use-wishlist.tsx new file mode 100644 index 000000000..d2ce9db5b --- /dev/null +++ b/framework/swell/wishlist/use-wishlist.tsx @@ -0,0 +1,46 @@ +// TODO: replace this hook and other wishlist hooks with a handler, or remove them if +// Shopify doesn't have a wishlist + +import { HookFetcher } from '@commerce/utils/types' +import { Product } from '../schema' + +const defaultOpts = {} + +export type Wishlist = { + items: [ + { + product_id: number + variant_id: number + id: number + product: Product + } + ] +} + +export interface UseWishlistOptions { + includeProducts?: boolean +} + +export interface UseWishlistInput extends UseWishlistOptions { + customerId?: number +} + +export const fetcher: HookFetcher<Wishlist | null, UseWishlistInput> = () => { + return null +} + +export function extendHook( + customFetcher: typeof fetcher, + // swrOptions?: SwrOptions<Wishlist | null, UseWishlistInput> + swrOptions?: any +) { + const useWishlist = ({ includeProducts }: UseWishlistOptions = {}) => { + return { data: null } + } + + useWishlist.extend = extendHook + + return useWishlist +} + +export default extendHook(fetcher) From 4c0c7ebf1093b6b5abc41e584693340674f7a240 Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Fri, 19 Mar 2021 15:39:57 -0300 Subject: [PATCH 189/221] changes --- .../common/FeatureBar/FeatureBar.module.css | 4 +- .../common/I18nWidget/I18nWidget.module.css | 8 +- .../common/UserNav/DropdownMenu.module.css | 4 +- components/ui/Grid/Grid.module.css | 15 +- components/ui/Hero/Hero.module.css | 5 +- package.json | 5 +- postcss.config.js | 2 +- yarn.lock | 438 +++--------------- 8 files changed, 105 insertions(+), 376 deletions(-) diff --git a/components/common/FeatureBar/FeatureBar.module.css b/components/common/FeatureBar/FeatureBar.module.css index a3cb61cd2..419fd4b08 100644 --- a/components/common/FeatureBar/FeatureBar.module.css +++ b/components/common/FeatureBar/FeatureBar.module.css @@ -1,7 +1,9 @@ .root { @apply text-center p-6 bg-primary text-sm flex-row justify-center items-center font-medium fixed bottom-0 w-full z-30 transition-all duration-300 ease-out; +} - @screen md { +@screen md { + .root { @apply flex text-left; } } diff --git a/components/common/I18nWidget/I18nWidget.module.css b/components/common/I18nWidget/I18nWidget.module.css index b216f5706..f100fd475 100644 --- a/components/common/I18nWidget/I18nWidget.module.css +++ b/components/common/I18nWidget/I18nWidget.module.css @@ -16,14 +16,16 @@ .dropdownMenu { @apply fixed right-0 top-12 mt-2 origin-top-right outline-none bg-primary z-40 w-full h-full; +} - @screen lg { +@screen lg { + .dropdownMenu { @apply absolute border border-accents-1 shadow-lg w-56 h-auto; } } -.closeButton { - @screen md { +@screen md { + .closeButton { @apply hidden; } } diff --git a/components/common/UserNav/DropdownMenu.module.css b/components/common/UserNav/DropdownMenu.module.css index 404726bc5..b2756e9d5 100644 --- a/components/common/UserNav/DropdownMenu.module.css +++ b/components/common/UserNav/DropdownMenu.module.css @@ -1,7 +1,9 @@ .dropdownMenu { @apply fixed right-0 mt-2 origin-top-right outline-none bg-primary z-40 w-full h-full; +} - @screen lg { +@screen lg { + .dropdownMenu { @apply absolute top-10 border border-accents-1 shadow-lg w-56 h-auto; } } diff --git a/components/ui/Grid/Grid.module.css b/components/ui/Grid/Grid.module.css index d3302fc61..9b331be8f 100644 --- a/components/ui/Grid/Grid.module.css +++ b/components/ui/Grid/Grid.module.css @@ -3,10 +3,6 @@ @apply grid grid-cols-1 gap-0; min-height: var(--row-height); - @screen lg { - @apply grid-cols-3 grid-rows-2; - } - & > * { @apply row-span-1 bg-transparent box-border overflow-hidden; height: 500px; @@ -19,6 +15,17 @@ } } +@screen lg { + .root { + @apply grid-cols-3 grid-rows-2; + } + + .root & > * { + @apply col-span-1; + height: inherit; + } +} + .default { & > * { @apply bg-transparent; diff --git a/components/ui/Hero/Hero.module.css b/components/ui/Hero/Hero.module.css index 364ad6580..c2032c8ae 100644 --- a/components/ui/Hero/Hero.module.css +++ b/components/ui/Hero/Hero.module.css @@ -1,6 +1,9 @@ .root { @apply mx-auto grid grid-cols-1 py-32 gap-4; - @screen md { +} + +@screen md { + .root { @apply grid-cols-2; } } diff --git a/package.json b/package.json index 85810ca91..2a30ccc4b 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "next": "^10.0.9-canary.5", "next-seo": "^4.11.0", "next-themes": "^0.0.4", - "postcss": "^8.2.6", + "postcss": "^8.2.8", "postcss-nesting": "^7.0.1", "react": "^17.0.1", "react-dom": "^17.0.1", @@ -44,7 +44,7 @@ "shopify-buy": "^2.11.0", "swr": "^0.4.0", "tabbable": "^5.1.5", - "tailwindcss": "^2.0.3" + "tailwindcss": "^2.0.4" }, "devDependencies": { "@graphql-codegen/cli": "^1.20.0", @@ -53,6 +53,7 @@ "@graphql-codegen/typescript-operations": "^1.17.13", "@manifoldco/swagger-to-ts": "^2.1.0", "@next/bundle-analyzer": "^10.0.1", + "@tailwindcss/jit": "^0.1.3", "@types/body-scroll-lock": "^2.6.1", "@types/classnames": "^2.2.10", "@types/cookie": "^0.4.0", diff --git a/postcss.config.js b/postcss.config.js index 9e0f0b2ca..9bff627f2 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,6 +1,6 @@ module.exports = { plugins: [ - 'tailwindcss', + '@tailwindcss/jit', 'postcss-nesting', 'postcss-flexbugs-fixes', [ diff --git a/yarn.lock b/yarn.lock index 45181fd21..1ac2dd830 100644 --- a/yarn.lock +++ b/yarn.lock @@ -968,6 +968,19 @@ dependencies: defer-to-connect "^1.0.1" +"@tailwindcss/jit@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/jit/-/jit-0.1.3.tgz#50390d9ac95fee78ed23a7e2320ef20bd4a35354" + integrity sha512-7VAvHKNLJxbGWRKxo2Z+beiodag7vWPx8b/+Egd5fve4zFihsngeNt6RwQFnll+almjppRYefRC5Py5v5K+6vg== + dependencies: + chokidar "^3.5.1" + dlv "^1.1.3" + fast-glob "^3.2.5" + lodash.topath "^4.5.2" + object-hash "^2.1.1" + postcss-selector-parser "^6.0.4" + quick-lru "^5.1.1" + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -993,32 +1006,6 @@ resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.0.tgz#14f854c0f93d326e39da6e3b6f34f7d37513d108" integrity sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg== -"@types/eslint-scope@^3.7.0": - version "3.7.0" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.0.tgz#4792816e31119ebd506902a482caec4951fabd86" - integrity sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw== - dependencies: - "@types/eslint" "*" - "@types/estree" "*" - -"@types/eslint@*": - version "7.2.6" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.6.tgz#5e9aff555a975596c03a98b59ecd103decc70c3c" - integrity sha512-I+1sYH+NPQ3/tVqCeUSBwTE/0heyvtXqpIopUUArlBm0Kpocb8FbMa3AZ/ASKIFpN3rnEx932TTXDbt9OXsNDw== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - -"@types/estree@*": - version "0.0.46" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe" - integrity sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg== - -"@types/estree@^0.0.45": - version "0.0.45" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884" - integrity sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g== - "@types/http-proxy-agent@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@types/http-proxy-agent/-/http-proxy-agent-2.0.2.tgz#942c1f35c7e1f0edd1b6ffae5d0f9051cfb32be1" @@ -1036,11 +1023,6 @@ resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.6.tgz#7f10c926aa41e189a2755c4c7fcf8e4573bd7ac1" integrity sha512-cK4XqrLvP17X6c0C8n4iTbT59EixqyXL3Fk8/Rsk4dF3oX4dg70gYUXrXVUUHpnsGMPNlTQMqf+TVmNPX6FmSQ== -"@types/json-schema@*", "@types/json-schema@^7.0.6": - version "7.0.7" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" - integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== - "@types/json-stable-stringify@^1.0.32": version "1.0.32" resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.32.tgz#121f6917c4389db3923640b2e68de5fa64dda88e" @@ -1186,161 +1168,6 @@ agentkeepalive "3.4.1" debug "3.1.0" -"@webassemblyjs/ast@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.1.tgz#76c6937716d68bf1484c15139f5ed30b9abc8bb4" - integrity sha512-uMu1nCWn2Wxyy126LlGqRVlhdTOsO/bsBRI4dNq3+6SiSuRKRQX6ejjKgh82LoGAPSq72lDUiQ4FWVaf0PecYw== - dependencies: - "@webassemblyjs/helper-module-context" "1.9.1" - "@webassemblyjs/helper-wasm-bytecode" "1.9.1" - "@webassemblyjs/wast-parser" "1.9.1" - -"@webassemblyjs/floating-point-hex-parser@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.1.tgz#9eb0ff90a1cdeef51f36ba533ed9f06b5cdadd09" - integrity sha512-5VEKu024RySmLKTTBl9q1eO/2K5jk9ZS+2HXDBLA9s9p5IjkaXxWiDb/+b7wSQp6FRdLaH1IVGIfOex58Na2pg== - -"@webassemblyjs/helper-api-error@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.1.tgz#ad89015c4246cd7f5ed0556700237f8b9c2c752f" - integrity sha512-y1lGmfm38djrScwpeL37rRR9f1D6sM8RhMpvM7CYLzOlHVboouZokXK/G88BpzW0NQBSvCCOnW5BFhten4FPfA== - -"@webassemblyjs/helper-buffer@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.1.tgz#186e67ac25f9546ea7939759413987f157524133" - integrity sha512-uS6VSgieHbk/m4GSkMU5cqe/5TekdCzQso4revCIEQ3vpGZgqSSExi4jWpTWwDpAHOIAb1Jfrs0gUB9AA4n71w== - -"@webassemblyjs/helper-code-frame@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.1.tgz#aab177b7cc87a318a8f8664ad68e2c3828ebc42b" - integrity sha512-ZQ2ZT6Evk4DPIfD+92AraGYaFIqGm4U20e7FpXwl7WUo2Pn1mZ1v8VGH8i+Y++IQpxPbQo/UyG0Khs7eInskzA== - dependencies: - "@webassemblyjs/wast-printer" "1.9.1" - -"@webassemblyjs/helper-fsm@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.1.tgz#527e91628e84d13d3573884b3dc4c53a81dcb911" - integrity sha512-J32HGpveEqqcKFS0YbgicB0zAlpfIxJa5MjxDxhu3i5ltPcVfY5EPvKQ1suRguFPehxiUs+/hfkwPEXom/l0lw== - -"@webassemblyjs/helper-module-context@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.1.tgz#778670b3d471f7cf093d1e7c0dde431b54310e16" - integrity sha512-IEH2cMmEQKt7fqelLWB5e/cMdZXf2rST1JIrzWmf4XBt3QTxGdnnLvV4DYoN8pJjOx0VYXsWg+yF16MmJtolZg== - dependencies: - "@webassemblyjs/ast" "1.9.1" - -"@webassemblyjs/helper-wasm-bytecode@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.1.tgz#563f59bcf409ccf469edde168b9426961ffbf6df" - integrity sha512-i2rGTBqFUcSXxyjt2K4vm/3kkHwyzG6o427iCjcIKjOqpWH8SEem+xe82jUk1iydJO250/CvE5o7hzNAMZf0dQ== - -"@webassemblyjs/helper-wasm-section@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.1.tgz#f7988f94c12b01b99a16120cb01dc099b00e4798" - integrity sha512-FetqzjtXZr2d57IECK+aId3D0IcGweeM0CbAnJHkYJkcRTHP+YcMb7Wmc0j21h5UWBpwYGb9dSkK/93SRCTrGg== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-buffer" "1.9.1" - "@webassemblyjs/helper-wasm-bytecode" "1.9.1" - "@webassemblyjs/wasm-gen" "1.9.1" - -"@webassemblyjs/ieee754@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.1.tgz#3b715871ca7d75784717cf9ceca9d7b81374b8af" - integrity sha512-EvTG9M78zP1MmkBpUjGQHZc26DzPGZSLIPxYHCjQsBMo60Qy2W34qf8z0exRDtxBbRIoiKa5dFyWer/7r1aaSQ== - dependencies: - "@xtuc/ieee754" "^1.2.0" - -"@webassemblyjs/leb128@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.1.tgz#b2ecaa39f9e8277cc9c707c1ca8b2aa7b27d0b72" - integrity sha512-Oc04ub0vFfLnF+2/+ki3AE+anmW4sv9uNBqb+79fgTaPv6xJsOT0dhphNfL3FrME84CbX/D1T9XT8tjFo0IIiw== - dependencies: - "@xtuc/long" "4.2.2" - -"@webassemblyjs/utf8@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.1.tgz#d02d9daab85cda3211e43caf31dca74c260a73b0" - integrity sha512-llkYtppagjCodFjo0alWOUhAkfOiQPQDIc5oA6C9sFAXz7vC9QhZf/f8ijQIX+A9ToM3c9Pq85X0EX7nx9gVhg== - -"@webassemblyjs/wasm-edit@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.1.tgz#e27a6bdbf78e5c72fa812a2fc3cbaad7c3e37578" - integrity sha512-S2IaD6+x9B2Xi8BCT0eGsrXXd8UxAh2LVJpg1ZMtHXnrDcsTtIX2bDjHi40Hio6Lc62dWHmKdvksI+MClCYbbw== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-buffer" "1.9.1" - "@webassemblyjs/helper-wasm-bytecode" "1.9.1" - "@webassemblyjs/helper-wasm-section" "1.9.1" - "@webassemblyjs/wasm-gen" "1.9.1" - "@webassemblyjs/wasm-opt" "1.9.1" - "@webassemblyjs/wasm-parser" "1.9.1" - "@webassemblyjs/wast-printer" "1.9.1" - -"@webassemblyjs/wasm-gen@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.1.tgz#56a0787d1fa7994fdc7bea59004e5bec7189c5fc" - integrity sha512-bqWI0S4lBQsEN5FTZ35vYzfKUJvtjNnBobB1agCALH30xNk1LToZ7Z8eiaR/Z5iVECTlBndoRQV3F6mbEqE/fg== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-wasm-bytecode" "1.9.1" - "@webassemblyjs/ieee754" "1.9.1" - "@webassemblyjs/leb128" "1.9.1" - "@webassemblyjs/utf8" "1.9.1" - -"@webassemblyjs/wasm-opt@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.1.tgz#fbdf8943a825e6dcc4cd69c3e092289fa4aec96c" - integrity sha512-gSf7I7YWVXZ5c6XqTEqkZjVs8K1kc1k57vsB6KBQscSagDNbAdxt6MwuJoMjsE1yWY1tsuL+pga268A6u+Fdkg== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-buffer" "1.9.1" - "@webassemblyjs/wasm-gen" "1.9.1" - "@webassemblyjs/wasm-parser" "1.9.1" - -"@webassemblyjs/wasm-parser@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.1.tgz#5e8352a246d3f605312c8e414f7990de55aaedfa" - integrity sha512-ImM4N2T1MEIond0MyE3rXvStVxEmivQrDKf/ggfh5pP6EHu3lL/YTAoSrR7shrbKNPpeKpGesW1LIK/L4kqduw== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-api-error" "1.9.1" - "@webassemblyjs/helper-wasm-bytecode" "1.9.1" - "@webassemblyjs/ieee754" "1.9.1" - "@webassemblyjs/leb128" "1.9.1" - "@webassemblyjs/utf8" "1.9.1" - -"@webassemblyjs/wast-parser@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.1.tgz#e25ef13585c060073c1db0d6bd94340fdeee7596" - integrity sha512-2xVxejXSvj3ls/o2TR/zI6p28qsGupjHhnHL6URULQRcXmryn3w7G83jQMcT7PHqUfyle65fZtWLukfdLdE7qw== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/floating-point-hex-parser" "1.9.1" - "@webassemblyjs/helper-api-error" "1.9.1" - "@webassemblyjs/helper-code-frame" "1.9.1" - "@webassemblyjs/helper-fsm" "1.9.1" - "@xtuc/long" "4.2.2" - -"@webassemblyjs/wast-printer@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.1.tgz#b9f38e93652037d4f3f9c91584635af4191ed7c1" - integrity sha512-tDV8V15wm7mmbAH6XvQRU1X+oPGmeOzYsd6h7hlRLz6QpV4Ec/KKxM8OpLtFmQPLCreGxTp+HuxtH4pRIZyL9w== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/wast-parser" "1.9.1" - "@xtuc/long" "4.2.2" - -"@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== - -"@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== - "@zeit/dns-cached-resolve@2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@zeit/dns-cached-resolve/-/dns-cached-resolve-2.1.0.tgz#78583010df1683fdb7b05949b75593c9a8641bc1" @@ -1403,12 +1230,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.12.5, ajv@^6.12.6: +ajv@^6.12.6: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -1812,7 +1634,7 @@ browserslist@4.16.1: escalade "^3.1.1" node-releases "^1.1.69" -browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.1, browserslist@^4.6.4: +browserslist@^4.12.0, browserslist@^4.16.1, browserslist@^4.6.4: version "4.16.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717" integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== @@ -1994,7 +1816,7 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chokidar@3.5.1, chokidar@^3.4.3: +chokidar@3.5.1, chokidar@^3.4.3, chokidar@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== @@ -2009,13 +1831,6 @@ chokidar@3.5.1, chokidar@^3.4.3: optionalDependencies: fsevents "~2.3.1" -chrome-trace-event@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" - integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== - dependencies: - tslib "^1.9.0" - ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -2159,6 +1974,11 @@ colorette@^1.2.1: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== +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== + combined-stream@^1.0.6, combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -2166,7 +1986,7 @@ combined-stream@^1.0.6, combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -commander@^2.13.0, commander@^2.16.0, commander@^2.20.0, commander@^2.20.3, commander@^2.8.1: +commander@^2.13.0, commander@^2.16.0, commander@^2.20.3, commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -2699,6 +2519,11 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" @@ -2801,14 +2626,6 @@ enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: memory-fs "^0.5.0" tapable "^1.0.0" -enhanced-resolve@^5.3.1: - version "5.7.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz#525c5d856680fbd5052de453ac83e32049958b5c" - integrity sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" - enquirer@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" @@ -2881,14 +2698,6 @@ escodegen@^1.8.0: optionalDependencies: source-map "~0.6.1" -eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - eslint-visitor-keys@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" @@ -2899,23 +2708,11 @@ esprima@4.0.1, esprima@^4.0.0, esprima@^4.0.1: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== - esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -2926,7 +2723,7 @@ etag@1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -events@^3.0.0, events@^3.2.0: +events@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== @@ -2980,7 +2777,7 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.1.1: +fast-glob@^3.1.1, fast-glob@^3.2.5: version "3.2.5" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== @@ -3302,7 +3099,7 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== @@ -3852,15 +3649,6 @@ jest-worker@24.9.0: merge-stream "^2.0.0" supports-color "^6.1.0" -jest-worker@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" - integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^7.0.0" - js-cookie@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" @@ -3889,11 +3677,6 @@ json-buffer@3.0.0: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= -json-parse-better-errors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - json-parse-even-better-errors@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -4105,11 +3888,6 @@ listr@^0.14.3: p-map "^2.0.0" rxjs "^6.3.3" -loader-runner@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384" - integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== - loader-utils@1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" @@ -4227,11 +4005,21 @@ lodash.toarray@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= +lodash.topath@^4.5.2: + version "4.5.2" + resolved "https://registry.yarnpkg.com/lodash.topath/-/lodash.topath-4.5.2.tgz#3616351f3bba61994a0931989660bd03254fd009" + integrity sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak= + lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@~4.17.20: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + log-symbols@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" @@ -4438,7 +4226,7 @@ mime-db@1.45.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== -mime-types@^2.1.12, mime-types@^2.1.27: +mime-types@^2.1.12: version "2.1.28" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ== @@ -4563,11 +4351,6 @@ native-url@0.3.4: dependencies: querystring "^0.2.0" -neo-async@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - next-seo@^4.11.0: version "4.19.0" resolved "https://registry.yarnpkg.com/next-seo/-/next-seo-4.19.0.tgz#b3be79a544e420dbbf1e4fcff98dad9790f15e36" @@ -4877,7 +4660,7 @@ p-limit@3.0.2: dependencies: p-try "^2.0.0" -p-limit@3.1.0, p-limit@^3.0.2, p-limit@^3.1.0: +p-limit@3.1.0, p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -5298,10 +5081,10 @@ postcss-media-minmax@^4.0.0: dependencies: postcss "^7.0.2" -postcss-nested@^5.0.1: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-5.0.3.tgz#2f46d77a06fc98d9c22344fd097396f5431386db" - integrity sha512-R2LHPw+u5hFfDgJG748KpGbJyTv7Yr33/2tIMWxquYuHTd9EXu27PYnKi7BxMXLtzKC0a0WVsqHtd7qIluQu/g== +postcss-nested@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-5.0.5.tgz#f0a107d33a9fab11d7637205f5321e27223e3603" + integrity sha512-GSRXYz5bccobpTzLQZXOnSOfKl6TwVr5CyAQJUPub4nuRJSOECK5AqurxVgmtxP48p0Kc/ndY/YyS1yqldX0Ew== dependencies: postcss-selector-parser "^6.0.4" @@ -5483,7 +5266,7 @@ postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.26, postcss@^7.0. source-map "^0.6.1" supports-color "^6.1.0" -postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.6: +postcss@^8.1.6, postcss@^8.2.1: version "8.2.6" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.6.tgz#5d69a974543b45f87e464bc4c3e392a97d6be9fe" integrity sha512-xpB8qYxgPuly166AGlpRjUdEYtmOWx2iCwGmrv4vqZL9YPVviDVPZPRXxnXr6xPZOdxQ9lp3ZBFCRgWJ7LE3Sg== @@ -5492,6 +5275,15 @@ postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.6: nanoid "^3.1.20" source-map "^0.6.1" +postcss@^8.2.8: + version "8.2.8" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.8.tgz#0b90f9382efda424c4f0f69a2ead6f6830d08ece" + integrity sha512-1F0Xb2T21xET7oQV9eKuctbM9S7BC0fetoHCc4H13z0PT6haiRLP4T0ZY4XWh7iLP0usgqykT6p9B2RtOf4FPw== + dependencies: + colorette "^1.2.2" + nanoid "^3.1.20" + source-map "^0.6.1" + precinct@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/precinct/-/precinct-6.3.1.tgz#8ad735a8afdfc48b56ed39c9ad3bf999b6b928dc" @@ -5639,7 +5431,12 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== @@ -5888,7 +5685,7 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve@^1.10.0, resolve@^1.11.1, resolve@^1.19.0: +resolve@^1.10.0, resolve@^1.11.1, 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== @@ -5986,15 +5783,6 @@ scheduler@^0.20.1: loose-envify "^1.1.0" object-assign "^4.1.1" -schema-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" - integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== - dependencies: - "@types/json-schema" "^7.0.6" - ajv "^6.12.5" - ajv-keywords "^3.5.2" - scuid@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/scuid/-/scuid-1.1.0.tgz#d3f9f920956e737a60f72d0e4ad280bf324d5dab" @@ -6027,13 +5815,6 @@ semver@^7.3.2: dependencies: lru-cache "^6.0.0" -serialize-javascript@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" - integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== - dependencies: - randombytes "^2.1.0" - set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -6138,12 +5919,7 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -source-list-map@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" - integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== - -source-map-support@^0.5.17, source-map-support@~0.5.19: +source-map-support@^0.5.17: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -6151,7 +5927,7 @@ source-map-support@^0.5.17, source-map-support@~0.5.19: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@0.7.3, source-map@~0.7.2: +source-map@0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== @@ -6429,7 +6205,7 @@ supports-color@^6.1.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -6461,10 +6237,10 @@ tabbable@^5.1.5: resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-5.1.5.tgz#efec48ede268d511c261e3b81facbb4782a35147" integrity sha512-oVAPrWgLLqrbvQE8XqcU7CVBq6SQbaIbHkhOca3u7/jzuQvyZycrUKPCGr04qpEIUslmUlULbSeN+m3QrKEykA== -tailwindcss@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.0.3.tgz#f8d07797d1f89dc4b171673c26237b58783c2c86" - integrity sha512-s8NEqdLBiVbbdL0a5XwTb8jKmIonOuI4RMENEcKLR61jw6SdKvBss7NWZzwCaD+ZIjlgmesv8tmrjXEp7C0eAQ== +tailwindcss@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.0.4.tgz#cf13e62738c3a27065664e449d93b66ee2945506" + integrity sha512-WhgR0oiBxGOZ9jY0yVfaJCHnckR7U74Fs/BMsYxGdwGJQ5Hd/HlaKD26bEJFZOvYScJo0QcUj2ImldzedsG7Bw== dependencies: "@fullhuman/postcss-purgecss" "^3.1.3" bytes "^3.0.0" @@ -6474,55 +6250,29 @@ tailwindcss@^2.0.3: didyoumean "^1.2.1" fs-extra "^9.1.0" html-tags "^3.1.0" - lodash "^4.17.20" + lodash "^4.17.21" modern-normalize "^1.0.0" node-emoji "^1.8.1" object-hash "^2.1.1" postcss-functions "^3" postcss-js "^3.0.3" - postcss-nested "^5.0.1" + postcss-nested "^5.0.5" postcss-selector-parser "^6.0.4" postcss-value-parser "^4.1.0" pretty-hrtime "^1.0.3" reduce-css-calc "^2.1.8" - resolve "^1.19.0" + resolve "^1.20.0" tapable@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tapable@^2.1.1, tapable@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" - integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== - temp@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/temp/-/temp-0.4.0.tgz#671ad63d57be0fe9d7294664b3fc400636678a60" integrity sha1-ZxrWPVe+D+nXKUZks/xABjZnimA= -terser-webpack-plugin@^5.0.3: - version "5.1.1" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.1.tgz#7effadee06f7ecfa093dbbd3e9ab23f5f3ed8673" - integrity sha512-5XNNXZiR8YO6X6KhSGXfY0QrGrCRlSwAEjIIrlRQR4W8nP69TaJUlh3bkuac6zzgspiGPfKEHcY295MMVExl5Q== - dependencies: - jest-worker "^26.6.2" - p-limit "^3.1.0" - schema-utils "^3.0.0" - serialize-javascript "^5.0.1" - source-map "^0.6.1" - terser "^5.5.1" - -terser@^5.5.1: - version "5.6.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.6.0.tgz#138cdf21c5e3100b1b3ddfddf720962f88badcd2" - integrity sha512-vyqLMoqadC1uR0vywqOZzriDYzgEkNJFK4q9GeyOBHIbiECHiWLKcWfbQWAUaPfxkjDhapSlZB9f7fkMrvkVjA== - dependencies: - commander "^2.20.0" - source-map "~0.7.2" - source-map-support "~0.5.19" - through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -6825,7 +6575,7 @@ warning@^4.0.3: dependencies: loose-envify "^1.0.0" -watchpack@2.1.1, watchpack@^2.0.0: +watchpack@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.1.1.tgz#e99630550fca07df9f90a06056987baa40a689c7" integrity sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw== @@ -6860,44 +6610,6 @@ webpack-bundle-analyzer@4.3.0: sirv "^1.0.7" ws "^7.3.1" -webpack-sources@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac" - integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w== - dependencies: - source-list-map "^2.0.1" - source-map "^0.6.1" - -webpack@5.11.1: - version "5.11.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.11.1.tgz#39b2b9daeb5c6c620e03b7556ec674eaed4016b4" - integrity sha512-tNUIdAmYJv+nupRs/U/gqmADm6fgrf5xE+rSlSsf2PgsGO7j2WG7ccU6AWNlOJlHFl+HnmXlBmHIkiLf+XA9mQ== - dependencies: - "@types/eslint-scope" "^3.7.0" - "@types/estree" "^0.0.45" - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-module-context" "1.9.1" - "@webassemblyjs/wasm-edit" "1.9.1" - "@webassemblyjs/wasm-parser" "1.9.1" - acorn "^8.0.4" - browserslist "^4.14.5" - chrome-trace-event "^1.0.2" - enhanced-resolve "^5.3.1" - eslint-scope "^5.1.1" - events "^3.2.0" - glob-to-regexp "^0.4.1" - graceful-fs "^4.2.4" - json-parse-better-errors "^1.0.2" - loader-runner "^4.1.0" - mime-types "^2.1.27" - neo-async "^2.6.2" - pkg-dir "^5.0.0" - schema-utils "^3.0.0" - tapable "^2.1.1" - terser-webpack-plugin "^5.0.3" - watchpack "^2.0.0" - webpack-sources "^2.1.1" - whatwg-fetch@^3.4.1: version "3.5.0" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz#605a2cd0a7146e5db141e29d1c62ab84c0c4c868" From ee1d8ed461372ec9d87ceddd07e88a283a012e50 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Sat, 27 Mar 2021 15:54:32 -0600 Subject: [PATCH 190/221] setup custom fetcher and auth hooks --- .env.template | 3 + .vscode/launch.json | 15 +++ commerce.config.json | 4 +- framework/commerce/with-config.js | 2 +- framework/swell/.env.template | 3 + framework/swell/api/index.ts | 16 +-- .../api/operations/get-all-collections.ts | 4 +- framework/swell/api/operations/get-page.ts | 4 +- .../swell/api/utils/create-api-handler.ts | 8 +- .../swell/api/utils/fetch-all-products.ts | 4 +- framework/swell/auth/use-login.tsx | 7 +- framework/swell/auth/use-logout.tsx | 3 +- framework/swell/auth/use-signup.tsx | 22 ++-- framework/swell/cart/use-cart.tsx | 3 +- framework/swell/commerce.config.json | 2 +- framework/swell/common/get-all-pages.ts | 4 +- framework/swell/common/get-page.ts | 4 +- framework/swell/common/get-site-info.ts | 4 +- framework/swell/const.ts | 4 + framework/swell/customer/get-customer-id.ts | 4 +- framework/swell/customer/use-customer.tsx | 36 +++++- framework/swell/fetcher.ts | 31 +++-- framework/swell/index.tsx | 29 +++-- .../swell/product/get-all-collections.ts | 4 +- .../swell/product/get-all-product-paths.ts | 4 +- framework/swell/product/get-all-products.ts | 4 +- framework/swell/product/get-product.ts | 4 +- framework/swell/provider.ts | 4 +- framework/swell/swell-js.d.ts | 1 + framework/swell/types.ts | 5 + framework/swell/utils/get-categories.ts | 4 +- framework/swell/utils/get-vendors.ts | 4 +- .../swell/utils/handle-fetch-response.ts | 15 +-- framework/swell/utils/normalize.ts | 12 +- package.json | 1 + tsconfig.json | 6 +- yarn.lock | 118 +++++++++++++++++- 37 files changed, 299 insertions(+), 103 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 framework/swell/swell-js.d.ts diff --git a/.env.template b/.env.template index 9b45afe4b..221e7a74e 100644 --- a/.env.template +++ b/.env.template @@ -7,3 +7,6 @@ BIGCOMMERCE_CHANNEL_ID= SHOPIFY_STORE_DOMAIN= SHOPIFY_STOREFRONT_ACCESS_TOKEN= + +SWELL_STORE_ID= +SWELL_PUBLIC_KEY= \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..177bc7dfa --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "pwa-chrome", + "request": "launch", + "name": "Launch Chrome against localhost", + "url": "http://localhost:8080", + "webRoot": "${workspaceFolder}" + } + ] +} diff --git a/commerce.config.json b/commerce.config.json index bef7db222..ab9497fd3 100644 --- a/commerce.config.json +++ b/commerce.config.json @@ -1,7 +1,7 @@ { - "provider": "bigcommerce", + "provider": "swell", "features": { - "wishlist": true, + "wishlist": false, "customCheckout": false } } diff --git a/framework/commerce/with-config.js b/framework/commerce/with-config.js index da6705cef..cc244dac0 100644 --- a/framework/commerce/with-config.js +++ b/framework/commerce/with-config.js @@ -4,7 +4,7 @@ const merge = require('deepmerge') -const PROVIDERS = ['bigcommerce', 'shopify'] +const PROVIDERS = ['bigcommerce', 'shopify', 'swell'] function getProviderName() { return process.env.BIGCOMMERCE_STOREFRONT_API_URL ? 'bigcommerce' : null diff --git a/framework/swell/.env.template b/framework/swell/.env.template index 24521c2a1..b5f38e883 100644 --- a/framework/swell/.env.template +++ b/framework/swell/.env.template @@ -1,2 +1,5 @@ SHOPIFY_STORE_DOMAIN= SHOPIFY_STOREFRONT_ACCESS_TOKEN= + +NEXT_PUBLIC_SWELL_STORE_ID= +NEXT_PUBLIC_SWELL_PUBLIC_KEY= diff --git a/framework/swell/api/index.ts b/framework/swell/api/index.ts index 4f15cae15..fc48116a6 100644 --- a/framework/swell/api/index.ts +++ b/framework/swell/api/index.ts @@ -22,23 +22,23 @@ if (!API_TOKEN) { import fetchGraphqlApi from './utils/fetch-graphql-api' -export interface ShopifyConfig extends CommerceAPIConfig {} +export interface SwellConfig extends CommerceAPIConfig {} export class Config { - private config: ShopifyConfig + private config: SwellConfig - constructor(config: ShopifyConfig) { + constructor(config: SwellConfig) { this.config = config } - getConfig(userConfig: Partial<ShopifyConfig> = {}) { - return Object.entries(userConfig).reduce<ShopifyConfig>( + getConfig(userConfig: Partial<SwellConfig> = {}) { + return Object.entries(userConfig).reduce<SwellConfig>( (cfg, [key, value]) => Object.assign(cfg, { [key]: value }), { ...this.config } ) } - setConfig(newConfig: Partial<ShopifyConfig>) { + setConfig(newConfig: Partial<SwellConfig>) { Object.assign(this.config, newConfig) } } @@ -53,10 +53,10 @@ const config = new Config({ customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE, }) -export function getConfig(userConfig?: Partial<ShopifyConfig>) { +export function getConfig(userConfig?: Partial<SwellConfig>) { return config.getConfig(userConfig) } -export function setConfig(newConfig: Partial<ShopifyConfig>) { +export function setConfig(newConfig: Partial<SwellConfig>) { return config.setConfig(newConfig) } diff --git a/framework/swell/api/operations/get-all-collections.ts b/framework/swell/api/operations/get-all-collections.ts index 9cf216a91..ed906ffcc 100644 --- a/framework/swell/api/operations/get-all-collections.ts +++ b/framework/swell/api/operations/get-all-collections.ts @@ -1,8 +1,8 @@ import Client from 'shopify-buy' -import { ShopifyConfig } from '../index' +import { SwellConfig } from '../index' type Options = { - config: ShopifyConfig + config: SwellConfig } const getAllCollections = async (options: Options) => { diff --git a/framework/swell/api/operations/get-page.ts b/framework/swell/api/operations/get-page.ts index 32acb7c8f..b04002dda 100644 --- a/framework/swell/api/operations/get-page.ts +++ b/framework/swell/api/operations/get-page.ts @@ -1,5 +1,5 @@ import { Page } from '../../schema' -import { ShopifyConfig, getConfig } from '..' +import { SwellConfig, getConfig } from '..' export type GetPageResult<T extends { page?: any } = { page?: Page }> = T @@ -15,7 +15,7 @@ async function getPage({ }: { url?: string variables: PageVariables - config?: ShopifyConfig + config?: SwellConfig preview?: boolean }): Promise<GetPageResult> { config = getConfig(config) diff --git a/framework/swell/api/utils/create-api-handler.ts b/framework/swell/api/utils/create-api-handler.ts index 8820aeabc..7ceb7d423 100644 --- a/framework/swell/api/utils/create-api-handler.ts +++ b/framework/swell/api/utils/create-api-handler.ts @@ -1,5 +1,5 @@ import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next' -import { ShopifyConfig, getConfig } from '..' +import { SwellConfig, getConfig } from '..' export type ShopifyApiHandler< T = any, @@ -8,7 +8,7 @@ export type ShopifyApiHandler< > = ( req: NextApiRequest, res: NextApiResponse<ShopifyApiResponse<T>>, - config: ShopifyConfig, + config: SwellConfig, handlers: H, // Custom configs that may be used by a particular handler options: Options @@ -17,7 +17,7 @@ export type ShopifyApiHandler< export type ShopifyHandler<T = any, Body = null> = (options: { req: NextApiRequest res: NextApiResponse<ShopifyApiResponse<T>> - config: ShopifyConfig + config: SwellConfig body: Body }) => void | Promise<void> @@ -44,7 +44,7 @@ export default function createApiHandler< operations, options, }: { - config?: ShopifyConfig + config?: SwellConfig operations?: Partial<H> options?: Options extends {} ? Partial<Options> : never } = {}): NextApiHandler { diff --git a/framework/swell/api/utils/fetch-all-products.ts b/framework/swell/api/utils/fetch-all-products.ts index 9fa70a5ee..7ad308b04 100644 --- a/framework/swell/api/utils/fetch-all-products.ts +++ b/framework/swell/api/utils/fetch-all-products.ts @@ -1,5 +1,5 @@ import { ProductEdge } from '../../schema' -import { ShopifyConfig } from '..' +import { SwellConfig } from '..' const fetchAllProducts = async ({ config, @@ -8,7 +8,7 @@ const fetchAllProducts = async ({ acc = [], cursor, }: { - config: ShopifyConfig + config: SwellConfig query: string acc?: ProductEdge[] variables?: any diff --git a/framework/swell/auth/use-login.tsx b/framework/swell/auth/use-login.tsx index 188dd54a2..cdaf6151b 100644 --- a/framework/swell/auth/use-login.tsx +++ b/framework/swell/auth/use-login.tsx @@ -25,7 +25,8 @@ const getErrorMessage = ({ code, message }: CustomerUserError) => { export const handler: MutationHook<null, {}, CustomerAccessTokenCreateInput> = { fetchOptions: { - query: createCustomerAccessTokenMutation, + query: 'account', + method: 'login', }, async fetcher({ input: { email, password }, options, fetch }) { if (!(email && password)) { @@ -40,9 +41,7 @@ export const handler: MutationHook<null, {}, CustomerAccessTokenCreateInput> = { MutationCheckoutCreateArgs >({ ...options, - variables: { - input: { email, password }, - }, + variables: [email, password], }) const errors = customerAccessTokenCreate?.customerUserErrors diff --git a/framework/swell/auth/use-logout.tsx b/framework/swell/auth/use-logout.tsx index 81a3b8cdd..6eebd997f 100644 --- a/framework/swell/auth/use-logout.tsx +++ b/framework/swell/auth/use-logout.tsx @@ -9,7 +9,8 @@ export default useLogout as UseLogout<typeof handler> export const handler: MutationHook<null> = { fetchOptions: { - query: customerAccessTokenDeleteMutation, + query: 'account', + method: 'logout', }, async fetcher({ options, fetch }) { await fetch({ diff --git a/framework/swell/auth/use-signup.tsx b/framework/swell/auth/use-signup.tsx index 7f66448d3..a3dbf73a3 100644 --- a/framework/swell/auth/use-signup.tsx +++ b/framework/swell/auth/use-signup.tsx @@ -20,7 +20,8 @@ export const handler: MutationHook< CustomerCreateInput > = { fetchOptions: { - query: customerCreateMutation, + query: 'account', + method: 'create', }, async fetcher({ input: { firstName, lastName, email, password }, @@ -36,23 +37,20 @@ export const handler: MutationHook< const data = await fetch({ ...options, variables: { - input: { - firstName, - lastName, - email, - password, - }, + first_name: firstName, + last_name: lastName, + email, + password, }, }) try { const loginData = await fetch({ - query: customerAccessTokenCreateMutation, + query: 'account', + method: 'login', variables: { - input: { - email, - password, - }, + email, + password, }, }) handleLogin(loginData) diff --git a/framework/swell/cart/use-cart.tsx b/framework/swell/cart/use-cart.tsx index d154bb837..a1a303210 100644 --- a/framework/swell/cart/use-cart.tsx +++ b/framework/swell/cart/use-cart.tsx @@ -18,7 +18,8 @@ export const handler: SWRHook< { isEmpty?: boolean } > = { fetchOptions: { - query: getCheckoutQuery, + query: 'cart', + method: 'get', }, async fetcher({ input: { cartId: checkoutId }, options, fetch }) { let checkout diff --git a/framework/swell/commerce.config.json b/framework/swell/commerce.config.json index b30ab39d9..01c9bf919 100644 --- a/framework/swell/commerce.config.json +++ b/framework/swell/commerce.config.json @@ -1,5 +1,5 @@ { - "provider": "shopify", + "provider": "swell", "features": { "wishlist": false } diff --git a/framework/swell/common/get-all-pages.ts b/framework/swell/common/get-all-pages.ts index 54231ed03..a092823ca 100644 --- a/framework/swell/common/get-all-pages.ts +++ b/framework/swell/common/get-all-pages.ts @@ -1,4 +1,4 @@ -import { getConfig, ShopifyConfig } from '../api' +import { getConfig, SwellConfig } from '../api' import { PageEdge } from '../schema' import { getAllPagesQuery } from '../utils/queries' @@ -20,7 +20,7 @@ export type Page = { const getAllPages = async (options?: { variables?: Variables - config: ShopifyConfig + config: SwellConfig preview?: boolean }): Promise<ReturnType> => { let { config, variables = { first: 250 } } = options ?? {} diff --git a/framework/swell/common/get-page.ts b/framework/swell/common/get-page.ts index be934aa42..ae7c0370b 100644 --- a/framework/swell/common/get-page.ts +++ b/framework/swell/common/get-page.ts @@ -1,4 +1,4 @@ -import { getConfig, ShopifyConfig } from '../api' +import { getConfig, SwellConfig } from '../api' import getPageQuery from '../utils/queries/get-page-query' import { Page } from './get-all-pages' @@ -10,7 +10,7 @@ export type GetPageResult<T extends { page?: any } = { page?: Page }> = T const getPage = async (options: { variables: Variables - config: ShopifyConfig + config: SwellConfig preview?: boolean }): Promise<GetPageResult> => { let { config, variables } = options ?? {} diff --git a/framework/swell/common/get-site-info.ts b/framework/swell/common/get-site-info.ts index cbbacf5b6..e44d9cc06 100644 --- a/framework/swell/common/get-site-info.ts +++ b/framework/swell/common/get-site-info.ts @@ -1,7 +1,7 @@ import getCategories, { Category } from '../utils/get-categories' import getVendors, { Brands } from '../utils/get-vendors' -import { getConfig, ShopifyConfig } from '../api' +import { getConfig, SwellConfig } from '../api' export type GetSiteInfoResult< T extends { categories: any[]; brands: any[] } = { @@ -12,7 +12,7 @@ export type GetSiteInfoResult< const getSiteInfo = async (options?: { variables?: any - config: ShopifyConfig + config: SwellConfig preview?: boolean }): Promise<GetSiteInfoResult> => { let { config } = options ?? {} diff --git a/framework/swell/const.ts b/framework/swell/const.ts index 06fbe5054..f2b0d5396 100644 --- a/framework/swell/const.ts +++ b/framework/swell/const.ts @@ -11,3 +11,7 @@ export const SHOPIFY_COOKIE_EXPIRE = 30 export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json` export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN + +export const SWELL_STORE_ID = process.env.NEXT_PUBLIC_SWELL_STORE_ID + +export const SWELL_PUBLIC_KEY = process.env.NEXT_PUBLIC_SWELL_PUBLIC_KEY diff --git a/framework/swell/customer/get-customer-id.ts b/framework/swell/customer/get-customer-id.ts index ca096645a..c3a1b8d2f 100644 --- a/framework/swell/customer/get-customer-id.ts +++ b/framework/swell/customer/get-customer-id.ts @@ -1,4 +1,4 @@ -import { getConfig, ShopifyConfig } from '../api' +import { getConfig, SwellConfig } from '../api' import getCustomerIdQuery from '../utils/queries/get-customer-id-query' import Cookies from 'js-cookie' @@ -7,7 +7,7 @@ async function getCustomerId({ config, }: { customerToken: string - config?: ShopifyConfig + config?: SwellConfig }): Promise<number | undefined> { config = getConfig(config) diff --git a/framework/swell/customer/use-customer.tsx b/framework/swell/customer/use-customer.tsx index 137f0da74..ec3dd99fd 100644 --- a/framework/swell/customer/use-customer.tsx +++ b/framework/swell/customer/use-customer.tsx @@ -1,20 +1,25 @@ import useCustomer, { UseCustomer } from '@commerce/customer/use-customer' import { Customer } from '@commerce/types' import { SWRHook } from '@commerce/utils/types' -import { getCustomerQuery, getCustomerToken } from '../utils' +import { normalizeCustomer } from '../utils/normalize' +// import { getCustomerQuery, getCustomerToken } from '../utils' export default useCustomer as UseCustomer<typeof handler> export const handler: SWRHook<Customer | null> = { fetchOptions: { - query: getCustomerQuery, + query: 'account', + method: 'get', }, async fetcher({ options, fetch }) { + // console.log('STORE_ID', STORE_ID, 'PUBLIC_KEY', PUBLIC_KEY); + // const data = await swell.account.get() const data = await fetch<any | null>({ ...options, - variables: { customerAccessToken: getCustomerToken() }, + // variables: { customerAccessToken: getCustomerToken() }, }) - return data.customer ?? null + console.log(`Customer data ${data}`) + return data ? normalizeCustomer(data) : null }, useHook: ({ useData }) => (input) => { return useData({ @@ -25,3 +30,26 @@ export const handler: SWRHook<Customer | null> = { }) }, } + +// const handler = (): { data: Customer } => { +// const swell = getContext(); +// const response = swell.account.get(); +// const { firstName, lastName, email, company, customerGroupId, notes, phone, +// entityId, addressCount, attributeCount, storeCredit } = response; +// return { +// data: { +// firstName, +// lastName, +// email, +// company, +// customerGroupId, +// notes, +// phone, +// entityId, +// addressCount, +// attributeCount, +// storeCredit +// } +// } +// } +// export default handler; diff --git a/framework/swell/fetcher.ts b/framework/swell/fetcher.ts index 9c4fe9a9e..f10032957 100644 --- a/framework/swell/fetcher.ts +++ b/framework/swell/fetcher.ts @@ -1,18 +1,25 @@ import { Fetcher } from '@commerce/utils/types' -import { API_TOKEN, API_URL } from './const' import { handleFetchResponse } from './utils' +import { swellConfig } from './index' -const fetcher: Fetcher = async ({ method = 'POST', variables, query }) => { - return handleFetchResponse( - await fetch(API_URL, { - method, - body: JSON.stringify({ query, variables }), - headers: { - 'X-Shopify-Storefront-Access-Token': API_TOKEN!, - 'Content-Type': 'application/json', - }, - }) - ) +const fetcher: Fetcher = async ({ method = 'get', variables, query }) => { + const { swell } = swellConfig + async function callSwell() { + if (Array.isArray(variables)) { + const arg1 = variables[0] + const arg2 = variables[1] + const response = await swell[query][method](arg1, arg2) + console.log(response) + return handleFetchResponse(response) + } else { + const response = await swell[query][method](variables) + console.log(response) + return handleFetchResponse(response) + } + } + if (query) { + return await callSwell() + } } export default fetcher diff --git a/framework/swell/index.tsx b/framework/swell/index.tsx index c26704771..db8cd3e1f 100644 --- a/framework/swell/index.tsx +++ b/framework/swell/index.tsx @@ -1,4 +1,5 @@ import * as React from 'react' +import swell from 'swell-js' import { ReactNode } from 'react' import { @@ -7,30 +8,36 @@ import { useCommerce as useCoreCommerce, } from '@commerce' -import { shopifyProvider, ShopifyProvider } from './provider' -import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const' +import { swellProvider, SwellProvider } from './provider' +import { + SHOPIFY_CHECKOUT_ID_COOKIE, + SWELL_STORE_ID, + SWELL_PUBLIC_KEY, +} from './const' +swell.init(SWELL_STORE_ID, SWELL_PUBLIC_KEY) -export { shopifyProvider } -export type { ShopifyProvider } +export { swellProvider } +export type { SwellProvider } -export const shopifyConfig: CommerceConfig = { +export const swellConfig: any = { locale: 'en-us', cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, + swell, } -export type ShopifyConfig = Partial<CommerceConfig> +export type SwellConfig = Partial<CommerceConfig> -export type ShopifyProps = { +export type SwellProps = { children?: ReactNode locale: string -} & ShopifyConfig +} & SwellConfig -export function CommerceProvider({ children, ...config }: ShopifyProps) { +export function CommerceProvider({ children, ...config }: SwellProps) { return ( <CoreCommerceProvider // TODO: Fix this type - provider={shopifyProvider as any} - config={{ ...shopifyConfig, ...config }} + provider={swellProvider as any} + config={{ ...swellConfig, ...config }} > {children} </CoreCommerceProvider> diff --git a/framework/swell/product/get-all-collections.ts b/framework/swell/product/get-all-collections.ts index 15c4bc51a..b3832316c 100644 --- a/framework/swell/product/get-all-collections.ts +++ b/framework/swell/product/get-all-collections.ts @@ -1,10 +1,10 @@ import { CollectionEdge } from '../schema' -import { getConfig, ShopifyConfig } from '../api' +import { getConfig, SwellConfig } from '../api' import getAllCollectionsQuery from '../utils/queries/get-all-collections-query' const getAllCollections = async (options?: { variables?: any - config: ShopifyConfig + config: SwellConfig preview?: boolean }) => { let { config, variables = { first: 250 } } = options ?? {} diff --git a/framework/swell/product/get-all-product-paths.ts b/framework/swell/product/get-all-product-paths.ts index 4431d1e53..4f3dec29a 100644 --- a/framework/swell/product/get-all-product-paths.ts +++ b/framework/swell/product/get-all-product-paths.ts @@ -1,5 +1,5 @@ import { Product } from '@commerce/types' -import { getConfig, ShopifyConfig } from '../api' +import { getConfig, SwellConfig } from '../api' import fetchAllProducts from '../api/utils/fetch-all-products' import { ProductEdge } from '../schema' import getAllProductsPathsQuery from '../utils/queries/get-all-products-paths-query' @@ -18,7 +18,7 @@ type ReturnType = { const getAllProductPaths = async (options?: { variables?: any - config?: ShopifyConfig + config?: SwellConfig preview?: boolean }): Promise<ReturnType> => { let { config, variables = { first: 250 } } = options ?? {} diff --git a/framework/swell/product/get-all-products.ts b/framework/swell/product/get-all-products.ts index 14e486563..78e081238 100644 --- a/framework/swell/product/get-all-products.ts +++ b/framework/swell/product/get-all-products.ts @@ -1,5 +1,5 @@ import { GraphQLFetcherResult } from '@commerce/api' -import { getConfig, ShopifyConfig } from '../api' +import { getConfig, SwellConfig } from '../api' import { ProductEdge } from '../schema' import { getAllProductsQuery } from '../utils/queries' import { normalizeProduct } from '../utils/normalize' @@ -16,7 +16,7 @@ type ReturnType = { const getAllProducts = async (options: { variables?: Variables - config?: ShopifyConfig + config?: SwellConfig preview?: boolean }): Promise<ReturnType> => { let { config, variables = { first: 250 } } = options ?? {} diff --git a/framework/swell/product/get-product.ts b/framework/swell/product/get-product.ts index 1f00288c7..cfac8e984 100644 --- a/framework/swell/product/get-product.ts +++ b/framework/swell/product/get-product.ts @@ -1,5 +1,5 @@ import { GraphQLFetcherResult } from '@commerce/api' -import { getConfig, ShopifyConfig } from '../api' +import { getConfig, SwellConfig } from '../api' import { normalizeProduct, getProductQuery } from '../utils' type Variables = { @@ -12,7 +12,7 @@ type ReturnType = { const getProduct = async (options: { variables: Variables - config: ShopifyConfig + config: SwellConfig preview?: boolean }): Promise<ReturnType> => { let { config, variables } = options ?? {} diff --git a/framework/swell/provider.ts b/framework/swell/provider.ts index 383822baa..f8a4ac1d6 100644 --- a/framework/swell/provider.ts +++ b/framework/swell/provider.ts @@ -14,7 +14,7 @@ import { handler as useSignup } from './auth/use-signup' import fetcher from './fetcher' -export const shopifyProvider = { +export const swellProvider = { locale: 'en-us', cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, storeDomain: STORE_DOMAIN, @@ -28,4 +28,4 @@ export const shopifyProvider = { }, } -export type ShopifyProvider = typeof shopifyProvider +export type SwellProvider = typeof swellProvider diff --git a/framework/swell/swell-js.d.ts b/framework/swell/swell-js.d.ts new file mode 100644 index 000000000..64a94d89e --- /dev/null +++ b/framework/swell/swell-js.d.ts @@ -0,0 +1 @@ +declare module 'swell-js' diff --git a/framework/swell/types.ts b/framework/swell/types.ts index c4e42b67d..579402f97 100644 --- a/framework/swell/types.ts +++ b/framework/swell/types.ts @@ -1,6 +1,11 @@ import * as Core from '@commerce/types' import { CheckoutLineItem } from './schema' +export interface SwellCustomer extends Core.Customer { + first_name: string + last_name: string +} + export type ShopifyCheckout = { id: string webUrl: string diff --git a/framework/swell/utils/get-categories.ts b/framework/swell/utils/get-categories.ts index cce4b2ad7..0f8c3c9d8 100644 --- a/framework/swell/utils/get-categories.ts +++ b/framework/swell/utils/get-categories.ts @@ -1,4 +1,4 @@ -import { ShopifyConfig } from '../api' +import { SwellConfig } from '../api' import { CollectionEdge } from '../schema' import getSiteCollectionsQuery from './queries/get-all-collections-query' @@ -8,7 +8,7 @@ export type Category = { path: string } -const getCategories = async (config: ShopifyConfig): Promise<Category[]> => { +const getCategories = async (config: SwellConfig): Promise<Category[]> => { const { data } = await config.fetch(getSiteCollectionsQuery, { variables: { first: 250, diff --git a/framework/swell/utils/get-vendors.ts b/framework/swell/utils/get-vendors.ts index f04483bb1..632f5daaf 100644 --- a/framework/swell/utils/get-vendors.ts +++ b/framework/swell/utils/get-vendors.ts @@ -1,4 +1,4 @@ -import { ShopifyConfig } from '../api' +import { SwellConfig } from '../api' import fetchAllProducts from '../api/utils/fetch-all-products' import getAllProductVendors from './queries/get-all-product-vendors-query' @@ -13,7 +13,7 @@ export type BrandEdge = { export type Brands = BrandEdge[] -const getVendors = async (config: ShopifyConfig): Promise<BrandEdge[]> => { +const getVendors = async (config: SwellConfig): Promise<BrandEdge[]> => { const vendors = await fetchAllProducts({ config, query: getAllProductVendors, diff --git a/framework/swell/utils/handle-fetch-response.ts b/framework/swell/utils/handle-fetch-response.ts index 8d7427d91..79aba8bd3 100644 --- a/framework/swell/utils/handle-fetch-response.ts +++ b/framework/swell/utils/handle-fetch-response.ts @@ -11,15 +11,16 @@ export async function getAsyncError(res: Response) { } const handleFetchResponse = async (res: Response) => { - if (res.ok) { - const { data, errors } = await res.json() + // if (res.ok) { + // const { data, errors } = await res.json() - if (errors && errors.length) { - throw getError(errors, res.status) - } + // if (errors && errors.length) { + // throw getError(errors, res.status) + // } - return data - } + // return data + // } + if (res) return res throw await getAsyncError(res) } diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts index c9b428b37..17579c2d3 100644 --- a/framework/swell/utils/normalize.ts +++ b/framework/swell/utils/normalize.ts @@ -1,4 +1,5 @@ import { Product } from '@commerce/types' +import { Customer } from '@commerce/types' import { Product as ShopifyProduct, @@ -11,7 +12,7 @@ import { ProductOption, } from '../schema' -import type { Cart, LineItem } from '../types' +import type { Cart, LineItem, SwellCustomer } from '../types' const money = ({ amount, currencyCode }: MoneyV2) => { return { @@ -121,6 +122,15 @@ export function normalizeCart(checkout: Checkout): Cart { } } +export function normalizeCustomer(customer: SwellCustomer): Customer { + const { first_name: firstName, last_name: lastName } = customer + return { + ...customer, + firstName, + lastName, + } +} + function normalizeLineItem({ node: { id, title, variant, quantity }, }: CheckoutLineItemEdge): LineItem { diff --git a/package.json b/package.json index 4e8fa8d30..b054dec66 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "react-merge-refs": "^1.1.0", "react-ticker": "^1.2.2", "shopify-buy": "^2.11.0", + "swell-js": "^4.0.0-next.0", "swr": "^0.4.0", "tabbable": "^5.1.5", "tailwindcss": "^2.0.3" diff --git a/tsconfig.json b/tsconfig.json index 9e712fb18..3d1b7584d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,10 +22,10 @@ "@components/*": ["components/*"], "@commerce": ["framework/commerce"], "@commerce/*": ["framework/commerce/*"], - "@framework": ["framework/bigcommerce"], - "@framework/*": ["framework/bigcommerce/*"] + "@framework": ["framework/swell"], + "@framework/*": ["framework/swell/*"] } }, "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], - "exclude": ["node_modules"] + "exclude": ["node_modules", "swell-js"] } diff --git a/yarn.lock b/yarn.lock index 7a1dce814..a0846380f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -401,6 +401,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@7.4.5": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12" + integrity sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ== + dependencies: + regenerator-runtime "^0.13.2" + "@babel/runtime@^7.0.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.13.tgz#0a21452352b02542db0ffb928ac2d3ca7cb6d66d" @@ -408,6 +415,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.12.13", "@babel/runtime@^7.13.10": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d" + integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" @@ -1522,6 +1536,14 @@ array-flatten@^3.0.0: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-3.0.0.tgz#6428ca2ee52c7b823192ec600fa3ed2f157cd541" integrity sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA== +array-includes-with-glob@^3.0.6: + version "3.0.8" + resolved "https://registry.yarnpkg.com/array-includes-with-glob/-/array-includes-with-glob-3.0.8.tgz#522a982e7913a9e6397efd3d933aa3cc61776cb2" + integrity sha512-g1XH4sJ/LMdyUSDB/9pNEC/eEO62chSTIi9I5agGHi4NI90SASER1Qe4emrTgCeaNPCAcivfi3Ba0owq6Pwo4Q== + dependencies: + "@babel/runtime" "^7.13.10" + matcher "^4.0.0" + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -2542,7 +2564,7 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= -deepmerge@^4.2.2: +deepmerge@4.2.2, deepmerge@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== @@ -2914,6 +2936,11 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + escodegen@^1.8.0: version "1.14.3" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" @@ -3896,7 +3923,7 @@ isobject@^4.0.0: resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== -isomorphic-fetch@^3.0.0: +isomorphic-fetch@3.0.0, isomorphic-fetch@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA== @@ -4219,6 +4246,16 @@ lodash._reinterpolate@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -4239,6 +4276,11 @@ lodash.isboolean@^3.0.3: resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= +lodash.isdate@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isdate/-/lodash.isdate-4.0.1.tgz#35a543673b9d76110de4114b32cc577048a7f366" + integrity sha1-NaVDZzuddhEN5BFLMsxXcEin82Y= + lodash.isinteger@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" @@ -4269,6 +4311,11 @@ lodash.random@^3.2.0: resolved "https://registry.yarnpkg.com/lodash.random/-/lodash.random-3.2.0.tgz#96e24e763333199130d2c9e2fd57f91703cc262d" integrity sha1-luJOdjMzGZEw0sni/Vf5FwPMJi0= +lodash.snakecase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + integrity sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40= + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -4299,6 +4346,16 @@ lodash.toarray@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= + +lodash@4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@~4.17.20: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" @@ -4445,6 +4502,13 @@ map-obj@^4.0.0: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.1.0.tgz#b91221b542734b9f14256c0132c897c5d7256fd5" integrity sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g== +matcher@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/matcher/-/matcher-4.0.0.tgz#a42a05a09aaed92e2d241eb91fddac689461ea51" + integrity sha512-S6x5wmcDmsDRRU/c2dkccDwQPXoFczc5+HpQ2lON8pnvHlnvHAHj5WlLVvw6n6vNyHuVugYrFohYxbS+pvFpKQ== + dependencies: + escape-string-regexp "^4.0.0" + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -4891,11 +4955,33 @@ object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== +object-keys-normalizer@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-keys-normalizer/-/object-keys-normalizer-1.0.1.tgz#db178dbba5e4c7b18b40837c8ef83365ee9348e7" + integrity sha1-2xeNu6Xkx7GLQIN8jvgzZe6TSOc= + dependencies: + lodash.camelcase "^4.3.0" + lodash.snakecase "^4.1.1" + object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== +object-merge-advanced@12.0.3: + version "12.0.3" + resolved "https://registry.yarnpkg.com/object-merge-advanced/-/object-merge-advanced-12.0.3.tgz#e03c19aa33cf88da6b32187e4907b487668808d9" + integrity sha512-xQIf2Vup1rpKiHr2tQca5jyNYgT4O0kNxOfAp3ZNonm2hS+5yaJgI0Czdk/QMy52bcRwQKX3uc3H8XtAiiYfVA== + dependencies: + "@babel/runtime" "^7.12.13" + array-includes-with-glob "^3.0.6" + lodash.clonedeep "^4.5.0" + lodash.includes "^4.3.0" + lodash.isdate "^4.0.1" + lodash.isplainobject "^4.0.6" + lodash.uniq "^4.5.0" + util-nonempty "^3.0.6" + object-path@^0.11.4: version "0.11.5" resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.5.tgz#d4e3cf19601a5140a55a16ad712019a9c50b577a" @@ -5755,6 +5841,11 @@ purgecss@^3.1.3: postcss "^8.2.1" postcss-selector-parser "^6.0.2" +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -5916,7 +6007,7 @@ reduce-css-calc@^2.1.8: css-unit-converter "^1.1.1" postcss-value-parser "^3.3.0" -regenerator-runtime@^0.13.4: +regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: version "0.13.7" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== @@ -6609,6 +6700,19 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +swell-js@^4.0.0-next.0: + version "4.0.0-next.0" + resolved "https://registry.yarnpkg.com/swell-js/-/swell-js-4.0.0-next.0.tgz#870599372e3c9eafefeafc2c63863c4032d8be6b" + integrity sha512-OQ1FLft3ruKpQw5P0TiCzs/X2Ma95+Qz+I2Xzs4KC6v+zVaFVUGNs80dQdtjfInisWoFC7iFZF2AITgellVGAg== + dependencies: + "@babel/runtime" "7.4.5" + deepmerge "4.2.2" + isomorphic-fetch "3.0.0" + lodash "4.17.21" + object-keys-normalizer "1.0.1" + object-merge-advanced "12.0.3" + qs "6.7.0" + swr@^0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/swr/-/swr-0.4.2.tgz#4a9ed5e9948088af145c79d716d294cb99712a29" @@ -6982,6 +7086,14 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +util-nonempty@^3.0.6: + version "3.0.8" + resolved "https://registry.yarnpkg.com/util-nonempty/-/util-nonempty-3.0.8.tgz#0e53820a29e2c6cdcc3ecece52bc7fdd193c9b2b" + integrity sha512-eB6dfVQWEBMT7i9EgWigvJiHUlW/iaq/Wg6pcWviwKsPWFwgprPVilZHkTAhzmXgv9LnGOLjrszm/HvIHpbeQw== + dependencies: + "@babel/runtime" "^7.13.10" + lodash.isplainobject "^4.0.6" + util@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" From e4c1050880a0a8d8fd8e31fcdc74bc73c8678059 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Mon, 29 Mar 2021 17:05:20 -0600 Subject: [PATCH 191/221] update get categories hook --- framework/swell/api/index.ts | 6 ++++- framework/swell/api/utils/fetch-swell-api.ts | 9 +++++++ framework/swell/utils/get-categories.ts | 25 +++++++------------- 3 files changed, 22 insertions(+), 18 deletions(-) create mode 100644 framework/swell/api/utils/fetch-swell-api.ts diff --git a/framework/swell/api/index.ts b/framework/swell/api/index.ts index fc48116a6..9c41b18ae 100644 --- a/framework/swell/api/index.ts +++ b/framework/swell/api/index.ts @@ -21,8 +21,11 @@ if (!API_TOKEN) { } import fetchGraphqlApi from './utils/fetch-graphql-api' +import fetchSwellApi from './utils/fetch-swell-api' -export interface SwellConfig extends CommerceAPIConfig {} +export interface SwellConfig extends CommerceAPIConfig { + fetchSwell: any +} export class Config { private config: SwellConfig @@ -49,6 +52,7 @@ const config = new Config({ apiToken: API_TOKEN!, cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, cartCookieMaxAge: SHOPIFY_COOKIE_EXPIRE, + fetchSwell: fetchSwellApi, fetch: fetchGraphqlApi, customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE, }) diff --git a/framework/swell/api/utils/fetch-swell-api.ts b/framework/swell/api/utils/fetch-swell-api.ts new file mode 100644 index 000000000..c4c9472cf --- /dev/null +++ b/framework/swell/api/utils/fetch-swell-api.ts @@ -0,0 +1,9 @@ +import { swellConfig } from '../../index' + +const fetchSwellApi = async (query: string, method: string) => { + const { swell } = swellConfig + const res = await swell[query][method]() + + return res +} +export default fetchSwellApi diff --git a/framework/swell/utils/get-categories.ts b/framework/swell/utils/get-categories.ts index 0f8c3c9d8..5b9ba501e 100644 --- a/framework/swell/utils/get-categories.ts +++ b/framework/swell/utils/get-categories.ts @@ -1,28 +1,19 @@ import { SwellConfig } from '../api' -import { CollectionEdge } from '../schema' -import getSiteCollectionsQuery from './queries/get-all-collections-query' export type Category = { - entityId: string + id: string name: string - path: string + slug: string } const getCategories = async (config: SwellConfig): Promise<Category[]> => { - const { data } = await config.fetch(getSiteCollectionsQuery, { - variables: { - first: 250, - }, - }) - + const data = await config.fetchSwell('categories', 'get') return ( - data.collections?.edges?.map( - ({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({ - entityId, - name, - path: `/${handle}`, - }) - ) ?? [] + data.results.map(({ id: entityId, name, slug }: Category) => ({ + entityId, + name, + path: `/${slug}`, + })) ?? [] ) } From 1ebf458fb23f3f8907503634394ad9b2e2f8e0e1 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Mon, 29 Mar 2021 17:49:14 -0600 Subject: [PATCH 192/221] update product search hook. whitelist cdn --- framework/swell/customer/use-customer.tsx | 4 --- framework/swell/fetcher.ts | 4 +-- framework/swell/next.config.js | 2 +- framework/swell/product/get-all-products.ts | 12 ++------ framework/swell/product/use-search.tsx | 33 +++++---------------- framework/swell/schema.d.ts | 2 +- framework/swell/types.ts | 5 ++++ framework/swell/utils/normalize.ts | 25 ++++++++-------- 8 files changed, 31 insertions(+), 56 deletions(-) diff --git a/framework/swell/customer/use-customer.tsx b/framework/swell/customer/use-customer.tsx index ec3dd99fd..7b84795aa 100644 --- a/framework/swell/customer/use-customer.tsx +++ b/framework/swell/customer/use-customer.tsx @@ -12,13 +12,9 @@ export const handler: SWRHook<Customer | null> = { method: 'get', }, async fetcher({ options, fetch }) { - // console.log('STORE_ID', STORE_ID, 'PUBLIC_KEY', PUBLIC_KEY); - // const data = await swell.account.get() const data = await fetch<any | null>({ ...options, - // variables: { customerAccessToken: getCustomerToken() }, }) - console.log(`Customer data ${data}`) return data ? normalizeCustomer(data) : null }, useHook: ({ useData }) => (input) => { diff --git a/framework/swell/fetcher.ts b/framework/swell/fetcher.ts index f10032957..e52d8fd54 100644 --- a/framework/swell/fetcher.ts +++ b/framework/swell/fetcher.ts @@ -9,15 +9,13 @@ const fetcher: Fetcher = async ({ method = 'get', variables, query }) => { const arg1 = variables[0] const arg2 = variables[1] const response = await swell[query][method](arg1, arg2) - console.log(response) return handleFetchResponse(response) } else { const response = await swell[query][method](variables) - console.log(response) return handleFetchResponse(response) } } - if (query) { + if (query in swell) { return await callSwell() } } diff --git a/framework/swell/next.config.js b/framework/swell/next.config.js index e9d48c02c..f6ac38345 100644 --- a/framework/swell/next.config.js +++ b/framework/swell/next.config.js @@ -3,6 +3,6 @@ const commerce = require('./commerce.config.json') module.exports = { commerce, images: { - domains: ['cdn.shopify.com'], + domains: ['cdn.schema.io'], }, } diff --git a/framework/swell/product/get-all-products.ts b/framework/swell/product/get-all-products.ts index 78e081238..8ca85eecd 100644 --- a/framework/swell/product/get-all-products.ts +++ b/framework/swell/product/get-all-products.ts @@ -21,16 +21,8 @@ const getAllProducts = async (options: { }): Promise<ReturnType> => { let { config, variables = { first: 250 } } = options ?? {} config = getConfig(config) - - const { data }: GraphQLFetcherResult = await config.fetch( - getAllProductsQuery, - { variables } - ) - - const products = - data.products?.edges?.map(({ node: p }: ProductEdge) => - normalizeProduct(p) - ) ?? [] + const { results } = await config.fetchSwell('products', 'get') + const products = results.map((product) => normalizeProduct(product)) ?? [] return { products, diff --git a/framework/swell/product/use-search.tsx b/framework/swell/product/use-search.tsx index 425df9e83..a00e39dc9 100644 --- a/framework/swell/product/use-search.tsx +++ b/framework/swell/product/use-search.tsx @@ -1,13 +1,7 @@ import { SWRHook } from '@commerce/utils/types' import useSearch, { UseSearch } from '@commerce/product/use-search' -import { ProductEdge } from '../schema' -import { - getAllProductsQuery, - getCollectionProductsQuery, - getSearchVariables, - normalizeProduct, -} from '../utils' +import { getAllProductsQuery, normalizeProduct } from '../utils' import { Product } from '@commerce/types' @@ -36,28 +30,17 @@ export const handler: SWRHook< async fetcher({ input, options, fetch }) { const { categoryId, brandId } = input - const data = await fetch({ - query: categoryId ? getCollectionProductsQuery : options.query, - method: options?.method, - variables: getSearchVariables(input), + const { results, count: found } = await fetch({ + query: 'products', + method: 'get', + // variables: { categoryId }, }) - let edges - - if (categoryId) { - edges = data.node?.products?.edges ?? [] - if (brandId) { - edges = edges.filter( - ({ node: { vendor } }: ProductEdge) => vendor === brandId - ) - } - } else { - edges = data.products?.edges ?? [] - } + const products = results.map((product) => normalizeProduct(product)) return { - products: edges.map(({ node }: ProductEdge) => normalizeProduct(node)), - found: !!edges.length, + products, + found, } }, useHook: ({ useData }) => (input = {}) => { diff --git a/framework/swell/schema.d.ts b/framework/swell/schema.d.ts index b1b23a3e5..8ffa23f73 100644 --- a/framework/swell/schema.d.ts +++ b/framework/swell/schema.d.ts @@ -4147,7 +4147,7 @@ export type ProductOption = Node & { /** The product option’s name. */ name: Scalars['String'] /** The corresponding value to the product option name. */ - values: Array<Scalars['String']> + values: Array<Scalars['Object']> } /** The price range of the product. */ diff --git a/framework/swell/types.ts b/framework/swell/types.ts index 579402f97..2878295e1 100644 --- a/framework/swell/types.ts +++ b/framework/swell/types.ts @@ -1,6 +1,11 @@ import * as Core from '@commerce/types' import { CheckoutLineItem } from './schema' +export interface SwellProduct extends Core.Product { + name: string + slug: string +} + export interface SwellCustomer extends Core.Customer { first_name: string last_name: string diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts index 17579c2d3..6dbb6d708 100644 --- a/framework/swell/utils/normalize.ts +++ b/framework/swell/utils/normalize.ts @@ -1,5 +1,6 @@ import { Product } from '@commerce/types' import { Customer } from '@commerce/types' +import { fileURLToPath } from 'node:url' import { Product as ShopifyProduct, @@ -12,7 +13,7 @@ import { ProductOption, } from '../schema' -import type { Cart, LineItem, SwellCustomer } from '../types' +import type { Cart, LineItem, SwellCustomer, SwellProduct } from '../types' const money = ({ amount, currencyCode }: MoneyV2) => { return { @@ -32,7 +33,7 @@ const normalizeProductOption = ({ displayName, values: values.map((value) => { let output: any = { - label: value, + label: value.name, } if (displayName === 'Color') { output = { @@ -45,9 +46,9 @@ const normalizeProductOption = ({ } } -const normalizeProductImages = ({ edges }: ImageConnection) => - edges?.map(({ node: { originalSrc: url, ...rest } }) => ({ - url, +const normalizeProductImages = (images) => + images?.map(({ file, ...rest }) => ({ + url: file.url, ...rest, })) @@ -73,16 +74,16 @@ const normalizeProductVariants = ({ edges }: ProductVariantConnection) => { ) } -export function normalizeProduct(productNode: ShopifyProduct): Product { +export function normalizeProduct(productNode: SwellProduct): Product { const { id, - title: name, + name, vendor, images, variants, description, - handle, - priceRange, + slug, + price, options, ...rest } = productNode @@ -92,9 +93,9 @@ export function normalizeProduct(productNode: ShopifyProduct): Product { name, vendor, description, - path: `/${handle}`, - slug: handle?.replace(/^\/+|\/+$/g, ''), - price: money(priceRange?.minVariantPrice), + path: `/${slug}`, + slug, + price, images: normalizeProductImages(images), variants: variants ? normalizeProductVariants(variants) : [], options: options ? options.map((o) => normalizeProductOption(o)) : [], From 18936b7544f39eed8e86d2bd3fd9a3a345144c78 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Thu, 1 Apr 2021 16:32:12 -0600 Subject: [PATCH 193/221] update get product hook --- framework/swell/api/utils/fetch-swell-api.ts | 8 +- framework/swell/cart/use-cart.tsx | 64 ++++--------- framework/swell/cart/utils/checkout-create.ts | 27 +++--- framework/swell/cart/utils/fetcher.ts | 32 ++++--- framework/swell/fetcher.ts | 2 + framework/swell/product/get-product.ts | 6 +- framework/swell/product/use-search.tsx | 4 +- framework/swell/utils/normalize.ts | 91 +++++++++---------- 8 files changed, 103 insertions(+), 131 deletions(-) diff --git a/framework/swell/api/utils/fetch-swell-api.ts b/framework/swell/api/utils/fetch-swell-api.ts index c4c9472cf..805aa46e8 100644 --- a/framework/swell/api/utils/fetch-swell-api.ts +++ b/framework/swell/api/utils/fetch-swell-api.ts @@ -1,8 +1,12 @@ import { swellConfig } from '../../index' -const fetchSwellApi = async (query: string, method: string) => { +const fetchSwellApi = async ( + query: string, + method: string, + variables: object | string +) => { const { swell } = swellConfig - const res = await swell[query][method]() + const res = await swell[query][method](variables) return res } diff --git a/framework/swell/cart/use-cart.tsx b/framework/swell/cart/use-cart.tsx index a1a303210..c9589f484 100644 --- a/framework/swell/cart/use-cart.tsx +++ b/framework/swell/cart/use-cart.tsx @@ -1,60 +1,28 @@ -import { useMemo } from 'react' -import useCommerceCart, { - FetchCartInput, - UseCart, -} from '@commerce/cart/use-cart' - -import { Cart } from '../types' +import useCart, { UseCart } from '@commerce/cart/use-cart' +import { Customer } from '@commerce/types' import { SWRHook } from '@commerce/utils/types' -import { checkoutCreate, checkoutToCart } from './utils' -import getCheckoutQuery from '../utils/queries/get-checkout-query' +import { normalizeCart } from '../utils/normalize' +// import { getCustomerQuery, getCustomerToken } from '../utils' -export default useCommerceCart as UseCart<typeof handler> +export default useCart as UseCart<typeof handler> -export const handler: SWRHook< - Cart | null, - {}, - FetchCartInput, - { isEmpty?: boolean } -> = { +export const handler: SWRHook<Customer | null> = { fetchOptions: { query: 'cart', method: 'get', }, - async fetcher({ input: { cartId: checkoutId }, options, fetch }) { - let checkout - if (checkoutId) { - const data = await fetch({ - ...options, - variables: { - checkoutId, - }, - }) - checkout = data.node - } - - if (checkout?.completedAt || !checkoutId) { - checkout = await checkoutCreate(fetch) - } - - // TODO: Fix this type - return checkoutToCart({ checkout } as any) + async fetcher({ options, fetch }) { + const data = await fetch<any | null>({ + ...options, + }) + return data ? normalizeCart(data) : null }, useHook: ({ useData }) => (input) => { - const response = useData({ - swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, + return useData({ + swrOptions: { + revalidateOnFocus: false, + ...input?.swrOptions, + }, }) - return useMemo( - () => - Object.create(response, { - isEmpty: { - get() { - return (response.data?.lineItems.length ?? 0) <= 0 - }, - enumerable: true, - }, - }), - [response] - ) }, } diff --git a/framework/swell/cart/utils/checkout-create.ts b/framework/swell/cart/utils/checkout-create.ts index e950cc7e4..73109baad 100644 --- a/framework/swell/cart/utils/checkout-create.ts +++ b/framework/swell/cart/utils/checkout-create.ts @@ -4,26 +4,27 @@ import { SHOPIFY_COOKIE_EXPIRE, } from '../../const' -import checkoutCreateMutation from '../../utils/mutations/checkout-create' +// import checkoutCreateMutation from '../../utils/mutations/checkout-create' import Cookies from 'js-cookie' export const checkoutCreate = async (fetch: any) => { - const data = await fetch({ - query: checkoutCreateMutation, + const cart = await fetch({ + query: 'cart', + method: 'get', }) - const checkout = data.checkoutCreate?.checkout - const checkoutId = checkout?.id + // const checkout = data.checkoutCreate?.checkout + const checkoutId = cart?.id - if (checkoutId) { - const options = { - expires: SHOPIFY_COOKIE_EXPIRE, - } - Cookies.set(SHOPIFY_CHECKOUT_ID_COOKIE, checkoutId, options) - Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout?.webUrl, options) - } + // if (checkoutId) { + // const options = { + // expires: SHOPIFY_COOKIE_EXPIRE, + // } + // Cookies.set(SHOPIFY_CHECKOUT_ID_COOKIE, checkoutId, options) + // Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout?.webUrl, options) + // } - return checkout + return cart } export default checkoutCreate diff --git a/framework/swell/cart/utils/fetcher.ts b/framework/swell/cart/utils/fetcher.ts index 6afb55f18..2e7ae15d3 100644 --- a/framework/swell/cart/utils/fetcher.ts +++ b/framework/swell/cart/utils/fetcher.ts @@ -1,31 +1,33 @@ import { HookFetcherFn } from '@commerce/utils/types' import { Cart } from '@commerce/types' -import { checkoutCreate, checkoutToCart } from '.' +// import { checkoutCreate, checkoutToCart } from '.' import { FetchCartInput } from '@commerce/cart/use-cart' +import { data } from 'autoprefixer' +import { normalizeCart } from '../../utils' const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ options, - input: { cartId: checkoutId }, + // input: { cartId: checkoutId }, fetch, }) => { let checkout - if (checkoutId) { - const data = await fetch({ - ...options, - variables: { - checkoutId, - }, - }) - checkout = data.node - } + // if (checkoutId) { + const data = await fetch({ + query: 'cart', + method: 'get', + // variables: { category: categoryId }, + }) + // checkout = data.node + // } - if (checkout?.completedAt || !checkoutId) { - checkout = await checkoutCreate(fetch) - } + // if (checkout?.completedAt || !checkoutId) { + // checkout = await checkoutCreate(fetch) + // } // TODO: Fix this type - return checkoutToCart({ checkout } as any) + // return checkoutToCart({ checkout } as any) + return normalizeCart(data) } export default fetcher diff --git a/framework/swell/fetcher.ts b/framework/swell/fetcher.ts index e52d8fd54..4c65b91d2 100644 --- a/framework/swell/fetcher.ts +++ b/framework/swell/fetcher.ts @@ -8,9 +8,11 @@ const fetcher: Fetcher = async ({ method = 'get', variables, query }) => { if (Array.isArray(variables)) { const arg1 = variables[0] const arg2 = variables[1] + // console.log('fetcher', query, method, variables); const response = await swell[query][method](arg1, arg2) return handleFetchResponse(response) } else { + // console.log('fetcher', query, method, variables); const response = await swell[query][method](variables) return handleFetchResponse(response) } diff --git a/framework/swell/product/get-product.ts b/framework/swell/product/get-product.ts index cfac8e984..c06617221 100644 --- a/framework/swell/product/get-product.ts +++ b/framework/swell/product/get-product.ts @@ -18,11 +18,7 @@ const getProduct = async (options: { let { config, variables } = options ?? {} config = getConfig(config) - const { data }: GraphQLFetcherResult = await config.fetch(getProductQuery, { - variables, - }) - - const { productByHandle: product } = data + const product = await config.fetchSwell('products', 'get', variables.slug) return { product: product ? normalizeProduct(product) : null, diff --git a/framework/swell/product/use-search.tsx b/framework/swell/product/use-search.tsx index a00e39dc9..4e55da22a 100644 --- a/framework/swell/product/use-search.tsx +++ b/framework/swell/product/use-search.tsx @@ -32,8 +32,8 @@ export const handler: SWRHook< const { results, count: found } = await fetch({ query: 'products', - method: 'get', - // variables: { categoryId }, + method: 'list', + variables: { category: categoryId }, }) const products = results.map((product) => normalizeProduct(product)) diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts index 6dbb6d708..bbb974c5e 100644 --- a/framework/swell/utils/normalize.ts +++ b/framework/swell/utils/normalize.ts @@ -47,31 +47,28 @@ const normalizeProductOption = ({ } const normalizeProductImages = (images) => - images?.map(({ file, ...rest }) => ({ + images?.map(({ file, id }) => ({ url: file.url, - ...rest, + id, })) -const normalizeProductVariants = ({ edges }: ProductVariantConnection) => { - return edges?.map( - ({ - node: { id, selectedOptions, sku, title, priceV2, compareAtPriceV2 }, - }) => ({ - id, - name: title, - sku: sku ?? id, - price: +priceV2.amount, - listPrice: +compareAtPriceV2?.amount, - requiresShipping: true, - options: selectedOptions.map(({ name, value }: SelectedOption) => - normalizeProductOption({ - id, - name, - values: [value], - }) - ), - }) - ) +const normalizeProductVariants = (variants) => { + // console.log('variant', variants); + return variants?.map(({ id, name, values, price, stock_status }) => ({ + id, + name, + sku: sku ?? id, + price, + listPrice: price, + // requiresShipping: true, + options: values.map(({ name, value }: SelectedOption) => + normalizeProductOption({ + id, + name, + values: [value], + }) + ), + })) } export function normalizeProduct(productNode: SwellProduct): Product { @@ -91,13 +88,13 @@ export function normalizeProduct(productNode: SwellProduct): Product { const product = { id, name, - vendor, + vendor: 'our brands', description, path: `/${slug}`, slug, price, images: normalizeProductImages(images), - variants: variants ? normalizeProductVariants(variants) : [], + variants: [], //variants ? normalizeProductVariants(options) : [], options: options ? options.map((o) => normalizeProductOption(o)) : [], ...rest, } @@ -105,21 +102,19 @@ export function normalizeProduct(productNode: SwellProduct): Product { return product } -export function normalizeCart(checkout: Checkout): Cart { +export function normalizeCart(cart: Checkout): Cart { return { - id: checkout.id, - customerId: '', + id: cart.id, + customerId: cart.account_id, email: '', - createdAt: checkout.createdAt, - currency: { - code: checkout.totalPriceV2?.currencyCode, - }, - taxesIncluded: checkout.taxesIncluded, - lineItems: checkout.lineItems?.edges.map(normalizeLineItem), - lineItemsSubtotalPrice: +checkout.subtotalPriceV2?.amount, - subtotalPrice: +checkout.subtotalPriceV2?.amount, - totalPrice: checkout.totalPriceV2?.amount, - discounts: [], + createdAt: cart.date_created, + currency: cart.currency, + taxesIncluded: cart.tax_included_total, + lineItems: cart.items?.map(normalizeLineItem), + lineItemsSubtotalPrice: +cart.sub_total, + subtotalPrice: +cart.sub_total, + totalPrice: cart.grand_total, + discounts: cart.discounts, } } @@ -133,30 +128,34 @@ export function normalizeCustomer(customer: SwellCustomer): Customer { } function normalizeLineItem({ - node: { id, title, variant, quantity }, + id, + product, + price, + variant, + quantity, }: CheckoutLineItemEdge): LineItem { return { id, variantId: String(variant?.id), - productId: String(variant?.id), - name: `${title}`, + productId: String(product?.id), + name: product.name, quantity, variant: { id: String(variant?.id), sku: variant?.sku ?? '', - name: variant?.title!, + name: variant?.name!, image: { - url: variant?.image?.originalSrc, + url: product?.images[0].file.url, }, - requiresShipping: variant?.requiresShipping ?? false, - price: variant?.priceV2?.amount, - listPrice: variant?.compareAtPriceV2?.amount, + requiresShipping: false, + price: price, + listPrice: price, }, path: '', discounts: [], options: [ { - value: variant?.title, + value: variant?.name, }, ], } From d0a04a8fe957fbd29d8fde84f3d826c8d0d82a04 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Sun, 4 Apr 2021 16:16:27 -0600 Subject: [PATCH 194/221] enable add and remove items (update) from cart. Update Search --- framework/swell/cart/use-add-item.tsx | 6 +--- framework/swell/cart/use-cart.tsx | 4 +-- framework/swell/cart/use-update-item.tsx | 36 ++++++++----------- .../swell/cart/utils/checkout-to-cart.ts | 19 ++-------- framework/swell/product/use-search.tsx | 5 ++- framework/swell/types.ts | 20 +++++++++++ framework/swell/utils/normalize.ts | 1 - 7 files changed, 41 insertions(+), 50 deletions(-) diff --git a/framework/swell/cart/use-add-item.tsx b/framework/swell/cart/use-add-item.tsx index d0f891148..34bdb3df8 100644 --- a/framework/swell/cart/use-add-item.tsx +++ b/framework/swell/cart/use-add-item.tsx @@ -23,11 +23,7 @@ export const handler: MutationHook<Cart, {}, CartItemBody> = { message: 'The item quantity has to be a valid integer greater than 0', }) } - - const { checkoutLineItemsAdd } = await fetch< - Mutation, - MutationCheckoutLineItemsAddArgs - >({ + const response = await fetch<Mutation, MutationCheckoutLineItemsAddArgs>({ ...options, variables: { checkoutId: getCheckoutId(), diff --git a/framework/swell/cart/use-cart.tsx b/framework/swell/cart/use-cart.tsx index c9589f484..183a9ad6d 100644 --- a/framework/swell/cart/use-cart.tsx +++ b/framework/swell/cart/use-cart.tsx @@ -1,12 +1,12 @@ import useCart, { UseCart } from '@commerce/cart/use-cart' -import { Customer } from '@commerce/types' +import { Cart } from '@commerce/types' import { SWRHook } from '@commerce/utils/types' import { normalizeCart } from '../utils/normalize' // import { getCustomerQuery, getCustomerToken } from '../utils' export default useCart as UseCart<typeof handler> -export const handler: SWRHook<Customer | null> = { +export const handler: SWRHook<Cart | null> = { fetchOptions: { query: 'cart', method: 'get', diff --git a/framework/swell/cart/use-update-item.tsx b/framework/swell/cart/use-update-item.tsx index 666ce3d08..3155778c6 100644 --- a/framework/swell/cart/use-update-item.tsx +++ b/framework/swell/cart/use-update-item.tsx @@ -14,7 +14,6 @@ import useCart from './use-cart' import { handler as removeItemHandler } from './use-remove-item' import type { Cart, LineItem, UpdateCartItemBody } from '../types' import { checkoutToCart } from './utils' -import { getCheckoutId, checkoutLineItemUpdateMutation } from '../utils' import { Mutation, MutationCheckoutLineItemsUpdateArgs } from '../schema' export type UpdateItemInput<T = any> = T extends LineItem @@ -25,7 +24,8 @@ export default useUpdateItem as UseUpdateItem<typeof handler> export const handler = { fetchOptions: { - query: checkoutLineItemUpdateMutation, + query: 'cart', + method: 'updateItem', }, async fetcher({ input: { itemId, item }, @@ -46,23 +46,14 @@ export const handler = { message: 'The item quantity has to be a valid integer', }) } - const { checkoutLineItemsUpdate } = await fetch< - Mutation, - MutationCheckoutLineItemsUpdateArgs - >({ - ...options, - variables: { - checkoutId: getCheckoutId(), - lineItems: [ - { - id: itemId, - quantity: item.quantity, - }, - ], - }, - }) + const response = await fetch<Mutation, MutationCheckoutLineItemsUpdateArgs>( + { + ...options, + variables: [item.itemId, { quantity: item.quantity }], + } + ) - return checkoutToCart(checkoutLineItemsUpdate) + return checkoutToCart(response) }, useHook: ({ fetch, @@ -75,13 +66,13 @@ export const handler = { } = {} ) => { const { item } = ctx - const { mutate } = useCart() as any + const { mutate, data: cartData } = useCart() as any return useCallback( debounce(async (input: UpdateItemInput<T>) => { - const itemId = input.id ?? item?.id - const productId = input.productId ?? item?.productId - const variantId = input.productId ?? item?.variantId + const itemId = cartData.lineItems[0].id + const productId = cartData.lineItems[0].productId + const variantId = cartData.lineItems[0].variant.id if (!itemId || !productId || !variantId) { throw new ValidationError({ message: 'Invalid input used for this operation', @@ -91,6 +82,7 @@ export const handler = { const data = await fetch({ input: { item: { + itemId, productId, variantId, quantity: input.quantity, diff --git a/framework/swell/cart/utils/checkout-to-cart.ts b/framework/swell/cart/utils/checkout-to-cart.ts index 03005f342..19a8bfd0b 100644 --- a/framework/swell/cart/utils/checkout-to-cart.ts +++ b/framework/swell/cart/utils/checkout-to-cart.ts @@ -17,26 +17,11 @@ export type CheckoutPayload = const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => { if (!checkoutPayload) { throw new CommerceError({ - message: 'Invalid response from Shopify', + message: 'Invalid response from Swell', }) } - const checkout = checkoutPayload?.checkout - const userErrors = checkoutPayload?.userErrors - - if (userErrors && userErrors.length) { - throw new ValidationError({ - message: userErrors[0].message, - }) - } - - if (!checkout) { - throw new CommerceError({ - message: 'Invalid response from Shopify', - }) - } - - return normalizeCart(checkout) + return normalizeCart(checkoutPayload) } export default checkoutToCart diff --git a/framework/swell/product/use-search.tsx b/framework/swell/product/use-search.tsx index 4e55da22a..204c65352 100644 --- a/framework/swell/product/use-search.tsx +++ b/framework/swell/product/use-search.tsx @@ -28,12 +28,11 @@ export const handler: SWRHook< query: getAllProductsQuery, }, async fetcher({ input, options, fetch }) { - const { categoryId, brandId } = input - + const { categoryId, search } = input const { results, count: found } = await fetch({ query: 'products', method: 'list', - variables: { category: categoryId }, + variables: { category: categoryId, search }, }) const products = results.map((product) => normalizeProduct(product)) diff --git a/framework/swell/types.ts b/framework/swell/types.ts index 2878295e1..0d1e85657 100644 --- a/framework/swell/types.ts +++ b/framework/swell/types.ts @@ -1,6 +1,26 @@ import * as Core from '@commerce/types' import { CheckoutLineItem } from './schema' +export type SwellCart = { + id: string + account_id: number + currency: string + tax_included_total: number + sub_total: number + discount_total: number + quantity: number + items: { + id: string + product: object + price: number + variant: boolean + quantity: number + } + date_created: string + discounts?: { id: number; amount: number }[] + // TODO: add missing fields +} + export interface SwellProduct extends Core.Product { name: string slug: string diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts index bbb974c5e..2707489a6 100644 --- a/framework/swell/utils/normalize.ts +++ b/framework/swell/utils/normalize.ts @@ -53,7 +53,6 @@ const normalizeProductImages = (images) => })) const normalizeProductVariants = (variants) => { - // console.log('variant', variants); return variants?.map(({ id, name, values, price, stock_status }) => ({ id, name, From abd86329d5c862cc1467544d99b60fe1503a8dc4 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Sun, 4 Apr 2021 18:07:14 -0600 Subject: [PATCH 195/221] update remove item from cart, landing page fetch, image normalization --- framework/swell/cart/use-remove-item.tsx | 19 ++++++++++-------- framework/swell/product/get-all-products.ts | 5 +++-- framework/swell/utils/normalize.ts | 22 +++++++++++++++++---- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/framework/swell/cart/use-remove-item.tsx b/framework/swell/cart/use-remove-item.tsx index e2aef13d8..13d137da4 100644 --- a/framework/swell/cart/use-remove-item.tsx +++ b/framework/swell/cart/use-remove-item.tsx @@ -31,27 +31,30 @@ export default useRemoveItem as UseRemoveItem<typeof handler> export const handler = { fetchOptions: { - query: checkoutLineItemRemoveMutation, + query: 'cart', + method: 'removeItem', }, async fetcher({ input: { itemId }, options, fetch, }: HookFetcherContext<RemoveCartItemBody>) { - const data = await fetch<Mutation, MutationCheckoutLineItemsRemoveArgs>({ - ...options, - variables: { checkoutId: getCheckoutId(), lineItemIds: [itemId] }, - }) - return checkoutToCart(data.checkoutLineItemsRemove) + const response = await fetch<Mutation, MutationCheckoutLineItemsRemoveArgs>( + { + ...options, + variables: [itemId], + } + ) + return checkoutToCart(response) }, useHook: ({ fetch, }: MutationHookContext<Cart | null, RemoveCartItemBody>) => < T extends LineItem | undefined = undefined >( - ctx: { item?: T } = {} + item ) => { - const { item } = ctx + // const { item } = ctx const { mutate } = useCart() const removeItem: RemoveItemFn<LineItem> = async (input) => { const itemId = input?.id ?? item?.id diff --git a/framework/swell/product/get-all-products.ts b/framework/swell/product/get-all-products.ts index 8ca85eecd..5150af993 100644 --- a/framework/swell/product/get-all-products.ts +++ b/framework/swell/product/get-all-products.ts @@ -21,9 +21,10 @@ const getAllProducts = async (options: { }): Promise<ReturnType> => { let { config, variables = { first: 250 } } = options ?? {} config = getConfig(config) - const { results } = await config.fetchSwell('products', 'get') + const { results } = await config.fetchSwell('products', 'list', { + limit: variables.first, + }) const products = results.map((product) => normalizeProduct(product)) ?? [] - return { products, } diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts index 2707489a6..5a95d94dc 100644 --- a/framework/swell/utils/normalize.ts +++ b/framework/swell/utils/normalize.ts @@ -46,11 +46,25 @@ const normalizeProductOption = ({ } } -const normalizeProductImages = (images) => - images?.map(({ file, id }) => ({ - url: file.url, - id, +type SwellImage = { + file: { + url: String + height: Number + width: Number + } + id: string +} +const normalizeProductImages = (images) => { + if (!images) { + return [{ url: '/' }] + } + return images?.map(({ file, ...rest }: SwellImage) => ({ + url: file?.url, + height: file.height, + width: file.width, + ...rest, })) +} const normalizeProductVariants = (variants) => { return variants?.map(({ id, name, values, price, stock_status }) => ({ From 644f196c972cb7da4e732e32e3e53414c2feaeb1 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Fri, 9 Apr 2021 16:51:35 -0600 Subject: [PATCH 196/221] update get-page and get-all-pages content hooks --- framework/swell/api/utils/fetch-swell-api.ts | 5 ++--- framework/swell/common/get-all-pages.ts | 16 ++++++---------- framework/swell/common/get-page.ts | 11 ++++------- framework/swell/product/get-all-products.ts | 8 +++++--- framework/swell/product/get-product.ts | 2 +- 5 files changed, 18 insertions(+), 24 deletions(-) diff --git a/framework/swell/api/utils/fetch-swell-api.ts b/framework/swell/api/utils/fetch-swell-api.ts index 805aa46e8..c8707230d 100644 --- a/framework/swell/api/utils/fetch-swell-api.ts +++ b/framework/swell/api/utils/fetch-swell-api.ts @@ -3,11 +3,10 @@ import { swellConfig } from '../../index' const fetchSwellApi = async ( query: string, method: string, - variables: object | string + variables: [] = [] ) => { const { swell } = swellConfig - const res = await swell[query][method](variables) - return res + return await swell[query][method](...variables) } export default fetchSwellApi diff --git a/framework/swell/common/get-all-pages.ts b/framework/swell/common/get-all-pages.ts index a092823ca..1088a472c 100644 --- a/framework/swell/common/get-all-pages.ts +++ b/framework/swell/common/get-all-pages.ts @@ -25,16 +25,12 @@ const getAllPages = async (options?: { }): Promise<ReturnType> => { let { config, variables = { first: 250 } } = options ?? {} config = getConfig(config) - const { locale } = config - const { data } = await config.fetch(getAllPagesQuery, { variables }) - - const pages = data.pages?.edges?.map( - ({ node: { title: name, handle, ...node } }: PageEdge) => ({ - ...node, - url: `/${locale}/${handle}`, - name, - }) - ) + const { locale, fetchSwell } = config + const { results } = await fetchSwell('content', 'list', ['pages']) + const pages = results.map(({ slug, ...rest }) => ({ + url: `/${locale}/${slug}`, + ...rest, + })) return { pages } } diff --git a/framework/swell/common/get-page.ts b/framework/swell/common/get-page.ts index ae7c0370b..40e9f6982 100644 --- a/framework/swell/common/get-page.ts +++ b/framework/swell/common/get-page.ts @@ -17,18 +17,15 @@ const getPage = async (options: { config = getConfig(config) const { locale } = config - - const { data } = await config.fetch(getPageQuery, { - variables, - }) - const page = data.node + const { id } = variables + const result = await config.fetchSwell('content', 'get', ['pages', id]) + const page = result return { page: page ? { ...page, - name: page.title, - url: `/${locale}/${page.handle}`, + url: `/${locale}/${page.slug}`, } : null, } diff --git a/framework/swell/product/get-all-products.ts b/framework/swell/product/get-all-products.ts index 5150af993..e212a2494 100644 --- a/framework/swell/product/get-all-products.ts +++ b/framework/swell/product/get-all-products.ts @@ -21,9 +21,11 @@ const getAllProducts = async (options: { }): Promise<ReturnType> => { let { config, variables = { first: 250 } } = options ?? {} config = getConfig(config) - const { results } = await config.fetchSwell('products', 'list', { - limit: variables.first, - }) + const { results } = await config.fetchSwell('products', 'list', [ + { + limit: variables.first, + }, + ]) const products = results.map((product) => normalizeProduct(product)) ?? [] return { products, diff --git a/framework/swell/product/get-product.ts b/framework/swell/product/get-product.ts index c06617221..a95610a10 100644 --- a/framework/swell/product/get-product.ts +++ b/framework/swell/product/get-product.ts @@ -18,7 +18,7 @@ const getProduct = async (options: { let { config, variables } = options ?? {} config = getConfig(config) - const product = await config.fetchSwell('products', 'get', variables.slug) + const product = await config.fetchSwell('products', 'get', [variables.slug]) return { product: product ? normalizeProduct(product) : null, From 7e0d126531e843c5724ec8e9c639dbea95cf4730 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@38-108-32-106.metrompls.com> Date: Sun, 11 Apr 2021 16:53:57 -0500 Subject: [PATCH 197/221] update product normalization --- framework/swell/utils/normalize.ts | 72 +++++++++++++----------------- 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts index 5a95d94dc..d50cdbb91 100644 --- a/framework/swell/utils/normalize.ts +++ b/framework/swell/utils/normalize.ts @@ -27,22 +27,24 @@ const normalizeProductOption = ({ name: displayName, values, }: ProductOption) => { + let returnValues = values.map((value) => { + let output: any = { + label: value.name, + } + if (displayName === 'Color') { + output = { + ...output, + hexColors: [value.name], + } + } + return output + }) + return { __typename: 'MultipleChoiceOption', id, displayName, - values: values.map((value) => { - let output: any = { - label: value.name, - } - if (displayName === 'Color') { - output = { - ...output, - hexColors: [value], - } - } - return output - }), + values: returnValues, } } @@ -60,56 +62,46 @@ const normalizeProductImages = (images) => { } return images?.map(({ file, ...rest }: SwellImage) => ({ url: file?.url, - height: file.height, - width: file.width, + height: file?.height, + width: file?.width, ...rest, })) } const normalizeProductVariants = (variants) => { - return variants?.map(({ id, name, values, price, stock_status }) => ({ + return variants?.map(({ id, name, values, price, sku }) => ({ id, name, sku: sku ?? id, - price, - listPrice: price, + price: price ?? null, + listPrice: price ?? null, // requiresShipping: true, options: values.map(({ name, value }: SelectedOption) => normalizeProductOption({ id, name, - values: [value], + values: value ? [value] : [], }) ), })) } export function normalizeProduct(productNode: SwellProduct): Product { - const { - id, - name, - vendor, - images, - variants, - description, - slug, - price, - options, - ...rest - } = productNode + const { images, options, slug, price } = productNode + + const productOptions = options.map((o) => normalizeProductOption(o)) + const productVariants = normalizeProductVariants( + options.filter((option) => option.variant) + ) + const productImages = normalizeProductImages(images) const product = { - id, - name, + ...productNode, vendor: 'our brands', - description, path: `/${slug}`, - slug, - price, - images: normalizeProductImages(images), - variants: [], //variants ? normalizeProductVariants(options) : [], - options: options ? options.map((o) => normalizeProductOption(o)) : [], - ...rest, + images: productImages ?? [], + variants: productVariants, + options: productOptions, } return product @@ -158,7 +150,7 @@ function normalizeLineItem({ sku: variant?.sku ?? '', name: variant?.name!, image: { - url: product?.images[0].file.url, + url: product.images ? product?.images[0].file.url : '', }, requiresShipping: false, price: price, From d489f59171d6561dfe63de7384bc03092f8d84b0 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Sat, 17 Apr 2021 19:01:46 -0500 Subject: [PATCH 198/221] update add item to cart hook --- framework/swell/cart/use-add-item.tsx | 15 +++----- framework/swell/utils/normalize.ts | 52 ++++++++++++++++----------- 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/framework/swell/cart/use-add-item.tsx b/framework/swell/cart/use-add-item.tsx index 34bdb3df8..ff6c7143f 100644 --- a/framework/swell/cart/use-add-item.tsx +++ b/framework/swell/cart/use-add-item.tsx @@ -3,7 +3,6 @@ import { CommerceError } from '@commerce/utils/errors' import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item' import useCart from './use-cart' import { Cart, CartItemBody } from '../types' -import { checkoutLineItemAddMutation, getCheckoutId } from '../utils' import { checkoutToCart } from './utils' import { Mutation, MutationCheckoutLineItemsAddArgs } from '../schema' import { useCallback } from 'react' @@ -12,7 +11,8 @@ export default useAddItem as UseAddItem<typeof handler> export const handler: MutationHook<Cart, {}, CartItemBody> = { fetchOptions: { - query: checkoutLineItemAddMutation, + query: 'cart', + method: 'addItem', }, async fetcher({ input: item, options, fetch }) { if ( @@ -26,18 +26,13 @@ export const handler: MutationHook<Cart, {}, CartItemBody> = { const response = await fetch<Mutation, MutationCheckoutLineItemsAddArgs>({ ...options, variables: { - checkoutId: getCheckoutId(), - lineItems: [ - { - variantId: item.variantId, - quantity: item.quantity ?? 1, - }, - ], + product_id: item.productId, + quantity: item.quantity, }, }) // TODO: Fix this Cart type here - return checkoutToCart(checkoutLineItemsAdd) as any + return checkoutToCart(response) as any }, useHook: ({ fetch }) => () => { const { mutate } = useCart() diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts index d50cdbb91..f40a7d8ec 100644 --- a/framework/swell/utils/normalize.ts +++ b/framework/swell/utils/normalize.ts @@ -29,6 +29,7 @@ const normalizeProductOption = ({ }: ProductOption) => { let returnValues = values.map((value) => { let output: any = { + displayName, label: value.name, } if (displayName === 'Color') { @@ -39,7 +40,6 @@ const normalizeProductOption = ({ } return output }) - return { __typename: 'MultipleChoiceOption', id, @@ -69,38 +69,49 @@ const normalizeProductImages = (images) => { } const normalizeProductVariants = (variants) => { - return variants?.map(({ id, name, values, price, sku }) => ({ - id, - name, - sku: sku ?? id, - price: price ?? null, - listPrice: price ?? null, - // requiresShipping: true, - options: values.map(({ name, value }: SelectedOption) => - normalizeProductOption({ + return variants?.map(({ id, name, values, price, sku }) => { + const options = values.map((option: SelectedOption) => { + return normalizeProductOption({ id, name, - values: value ? [value] : [], + values: option ? [option] : [], }) - ), - })) + }) + + return { + id, + name, + sku: sku ?? id, + price: price ?? null, + listPrice: price ?? null, + // requiresShipping: true, + options, + } + }) } export function normalizeProduct(productNode: SwellProduct): Product { const { images, options, slug, price } = productNode - - const productOptions = options.map((o) => normalizeProductOption(o)) + const productOptions = options + ? options.map((o) => normalizeProductOption(o)) + : [] const productVariants = normalizeProductVariants( options.filter((option) => option.variant) ) + + // ProductView.tsx assumes the existence of at least one product variant + const emptyVariants = [{ options: [{ id: 123 }] }] const productImages = normalizeProductImages(images) const product = { ...productNode, vendor: 'our brands', path: `/${slug}`, - images: productImages ?? [], - variants: productVariants, + images: productImages, + variants: + productVariants && productVariants.length + ? productVariants + : emptyVariants, options: productOptions, } @@ -139,18 +150,18 @@ function normalizeLineItem({ variant, quantity, }: CheckoutLineItemEdge): LineItem { - return { + const item = { id, variantId: String(variant?.id), productId: String(product?.id), - name: product.name, + name: product?.name ?? '', quantity, variant: { id: String(variant?.id), sku: variant?.sku ?? '', name: variant?.name!, image: { - url: product.images ? product?.images[0].file.url : '', + url: product && product.images ? product?.images[0].file.url : '', }, requiresShipping: false, price: price, @@ -164,4 +175,5 @@ function normalizeLineItem({ }, ], } + return item } From 79ed72a7102f5bb93f3374a8281796f3e8003b96 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Sun, 18 Apr 2021 19:33:23 -0500 Subject: [PATCH 199/221] update product normalization --- framework/swell/product/get-all-products.ts | 7 +++- framework/swell/product/get-product.ts | 7 ++-- framework/swell/product/use-search.tsx | 7 +++- framework/swell/types.ts | 14 +++++++- framework/swell/utils/normalize.ts | 39 ++++++++++++++------- 5 files changed, 57 insertions(+), 17 deletions(-) diff --git a/framework/swell/product/get-all-products.ts b/framework/swell/product/get-all-products.ts index e212a2494..1f4f4152c 100644 --- a/framework/swell/product/get-all-products.ts +++ b/framework/swell/product/get-all-products.ts @@ -26,7 +26,12 @@ const getAllProducts = async (options: { limit: variables.first, }, ]) - const products = results.map((product) => normalizeProduct(product)) ?? [] + const products = results.map((product) => { + if (product.variants) { + product.variants = product.variants.results + } + return normalizeProduct(product) ?? [] + }) return { products, } diff --git a/framework/swell/product/get-product.ts b/framework/swell/product/get-product.ts index a95610a10..49bc5b67f 100644 --- a/framework/swell/product/get-product.ts +++ b/framework/swell/product/get-product.ts @@ -19,9 +19,12 @@ const getProduct = async (options: { config = getConfig(config) const product = await config.fetchSwell('products', 'get', [variables.slug]) - + if (product.variants) { + product.variants = product.variants?.results + } + // console.log('product', product) return { - product: product ? normalizeProduct(product) : null, + product: normalizeProduct(product), } } diff --git a/framework/swell/product/use-search.tsx b/framework/swell/product/use-search.tsx index 204c65352..14b966319 100644 --- a/framework/swell/product/use-search.tsx +++ b/framework/swell/product/use-search.tsx @@ -35,7 +35,12 @@ export const handler: SWRHook< variables: { category: categoryId, search }, }) - const products = results.map((product) => normalizeProduct(product)) + const products = results.map((product) => { + if (product.variants) { + product.variants = product.variants?.results + } + return normalizeProduct(product) + }) return { products, diff --git a/framework/swell/types.ts b/framework/swell/types.ts index 0d1e85657..456b3da05 100644 --- a/framework/swell/types.ts +++ b/framework/swell/types.ts @@ -21,9 +21,21 @@ export type SwellCart = { // TODO: add missing fields } -export interface SwellProduct extends Core.Product { +export type VariantResult = { + id: string + option_value_ids: string[] +} + +export interface SwellProduct { + id: string + description: string name: string slug: string + currency: string + price: number + images: any[] + options: any[] + variants: any[] } export interface SwellCustomer extends Core.Customer { diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts index f40a7d8ec..ea12acbae 100644 --- a/framework/swell/utils/normalize.ts +++ b/framework/swell/utils/normalize.ts @@ -69,12 +69,16 @@ const normalizeProductImages = (images) => { } const normalizeProductVariants = (variants) => { - return variants?.map(({ id, name, values, price, sku }) => { - const options = values.map((option: SelectedOption) => { + return variants?.map(({ id, name, price, sku }) => { + const values = name + .split(',') + .map((i) => ({ name: i.trim(), label: i.trim() })) + + const options = values.map((value) => { return normalizeProductOption({ id, name, - values: option ? [option] : [], + values: [value], }) }) @@ -90,22 +94,30 @@ const normalizeProductVariants = (variants) => { }) } -export function normalizeProduct(productNode: SwellProduct): Product { - const { images, options, slug, price } = productNode +export function normalizeProduct(swellProduct: SwellProduct): Product { + const { + id, + description, + images, + options, + slug, + variants, + price: value, + currency: currencyCode, + } = swellProduct const productOptions = options ? options.map((o) => normalizeProductOption(o)) : [] - const productVariants = normalizeProductVariants( - options.filter((option) => option.variant) - ) + const productVariants = variants ? normalizeProductVariants(variants) : [] // ProductView.tsx assumes the existence of at least one product variant const emptyVariants = [{ options: [{ id: 123 }] }] const productImages = normalizeProductImages(images) - const product = { - ...productNode, - vendor: 'our brands', + ...swellProduct, + description, + id, + vendor: '', path: `/${slug}`, images: productImages, variants: @@ -113,8 +125,11 @@ export function normalizeProduct(productNode: SwellProduct): Product { ? productVariants : emptyVariants, options: productOptions, + price: { + value, + currencyCode, + }, } - return product } From 8a8ef7dbba8a4aa53475379aaa01f30960ac0ca4 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Mon, 19 Apr 2021 20:37:33 -0500 Subject: [PATCH 200/221] setup swell checkout --- framework/swell/api/checkout/index.ts | 28 ++----------------- framework/swell/cart/use-cart.tsx | 10 +++---- framework/swell/cart/utils/checkout-create.ts | 20 ++++--------- framework/swell/const.ts | 2 +- framework/swell/provider.ts | 4 +-- next.config.js | 3 +- 6 files changed, 17 insertions(+), 50 deletions(-) diff --git a/framework/swell/api/checkout/index.ts b/framework/swell/api/checkout/index.ts index 244078466..03f166a1a 100644 --- a/framework/swell/api/checkout/index.ts +++ b/framework/swell/api/checkout/index.ts @@ -1,40 +1,16 @@ -import isAllowedMethod from '../utils/is-allowed-method' import createApiHandler, { ShopifyApiHandler, } from '../utils/create-api-handler' -import { - SHOPIFY_CHECKOUT_ID_COOKIE, - SHOPIFY_CHECKOUT_URL_COOKIE, - SHOPIFY_CUSTOMER_TOKEN_COOKIE, -} from '../../const' +import { SWELL_CHECKOUT_URL_COOKIE } from '../../const' import { getConfig } from '..' -import associateCustomerWithCheckoutMutation from '../../utils/mutations/associate-customer-with-checkout' - -const METHODS = ['GET'] const checkoutApi: ShopifyApiHandler<any> = async (req, res, config) => { - if (!isAllowedMethod(req, res, METHODS)) return - config = getConfig() const { cookies } = req - const checkoutUrl = cookies[SHOPIFY_CHECKOUT_URL_COOKIE] - const customerCookie = cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE] - - if (customerCookie) { - try { - await config.fetch(associateCustomerWithCheckoutMutation, { - variables: { - checkoutId: cookies[SHOPIFY_CHECKOUT_ID_COOKIE], - customerAccessToken: cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE], - }, - }) - } catch (error) { - console.error(error) - } - } + const checkoutUrl = cookies[SWELL_CHECKOUT_URL_COOKIE] if (checkoutUrl) { res.redirect(checkoutUrl) diff --git a/framework/swell/cart/use-cart.tsx b/framework/swell/cart/use-cart.tsx index 183a9ad6d..04d07914d 100644 --- a/framework/swell/cart/use-cart.tsx +++ b/framework/swell/cart/use-cart.tsx @@ -2,7 +2,7 @@ import useCart, { UseCart } from '@commerce/cart/use-cart' import { Cart } from '@commerce/types' import { SWRHook } from '@commerce/utils/types' import { normalizeCart } from '../utils/normalize' -// import { getCustomerQuery, getCustomerToken } from '../utils' +import { checkoutCreate, checkoutToCart } from './utils' export default useCart as UseCart<typeof handler> @@ -12,10 +12,10 @@ export const handler: SWRHook<Cart | null> = { method: 'get', }, async fetcher({ options, fetch }) { - const data = await fetch<any | null>({ - ...options, - }) - return data ? normalizeCart(data) : null + const cart = await checkoutCreate(fetch) + + return cart ? normalizeCart(cart) : null + // return checkoutToCart({ checkout } as any) }, useHook: ({ useData }) => (input) => { return useData({ diff --git a/framework/swell/cart/utils/checkout-create.ts b/framework/swell/cart/utils/checkout-create.ts index 73109baad..cc08007f4 100644 --- a/framework/swell/cart/utils/checkout-create.ts +++ b/framework/swell/cart/utils/checkout-create.ts @@ -1,10 +1,5 @@ -import { - SHOPIFY_CHECKOUT_ID_COOKIE, - SHOPIFY_CHECKOUT_URL_COOKIE, - SHOPIFY_COOKIE_EXPIRE, -} from '../../const' +import { SWELL_CHECKOUT_URL_COOKIE } from '../../const' -// import checkoutCreateMutation from '../../utils/mutations/checkout-create' import Cookies from 'js-cookie' export const checkoutCreate = async (fetch: any) => { @@ -13,16 +8,11 @@ export const checkoutCreate = async (fetch: any) => { method: 'get', }) - // const checkout = data.checkoutCreate?.checkout - const checkoutId = cart?.id + const checkoutUrl = cart?.checkout_url - // if (checkoutId) { - // const options = { - // expires: SHOPIFY_COOKIE_EXPIRE, - // } - // Cookies.set(SHOPIFY_CHECKOUT_ID_COOKIE, checkoutId, options) - // Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout?.webUrl, options) - // } + if (checkoutUrl) { + Cookies.set(SWELL_CHECKOUT_URL_COOKIE, checkoutUrl) + } return cart } diff --git a/framework/swell/const.ts b/framework/swell/const.ts index f2b0d5396..7226b19cb 100644 --- a/framework/swell/const.ts +++ b/framework/swell/const.ts @@ -1,6 +1,6 @@ export const SHOPIFY_CHECKOUT_ID_COOKIE = 'shopify_checkoutId' -export const SHOPIFY_CHECKOUT_URL_COOKIE = 'shopify_checkoutUrl' +export const SWELL_CHECKOUT_URL_COOKIE = 'swell_checkoutUrl' export const SHOPIFY_CUSTOMER_TOKEN_COOKIE = 'shopify_customerToken' diff --git a/framework/swell/provider.ts b/framework/swell/provider.ts index f8a4ac1d6..0bcbd7888 100644 --- a/framework/swell/provider.ts +++ b/framework/swell/provider.ts @@ -1,4 +1,4 @@ -import { SHOPIFY_CHECKOUT_ID_COOKIE, STORE_DOMAIN } from './const' +import { SWELL_CHECKOUT_URL_COOKIE, STORE_DOMAIN } from './const' import { handler as useCart } from './cart/use-cart' import { handler as useAddItem } from './cart/use-add-item' @@ -16,7 +16,7 @@ import fetcher from './fetcher' export const swellProvider = { locale: 'en-us', - cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, + cartCookie: SWELL_CHECKOUT_URL_COOKIE, storeDomain: STORE_DOMAIN, fetcher, cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, diff --git a/next.config.js b/next.config.js index 7e86695a0..3569eff26 100644 --- a/next.config.js +++ b/next.config.js @@ -3,6 +3,7 @@ const withCommerceConfig = require('./framework/commerce/with-config') const isBC = commerce.provider === 'bigcommerce' const isShopify = commerce.provider === 'shopify' +const isSwell = commerce.provider === 'swell' module.exports = withCommerceConfig({ commerce, @@ -12,7 +13,7 @@ module.exports = withCommerceConfig({ }, rewrites() { return [ - (isBC || isShopify) && { + (isBC || isShopify || isSwell) && { source: '/checkout', destination: '/api/bigcommerce/checkout', }, From dd40b8c60465a202605f370bddc6396c86906118 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Wed, 21 Apr 2021 19:56:00 -0500 Subject: [PATCH 201/221] Match product options with variants --- framework/swell/common/get-page.ts | 1 - framework/swell/schema.d.ts | 197 ++++++++++++++++------------- framework/swell/types.ts | 30 ++++- framework/swell/utils/normalize.ts | 111 ++++++++-------- 4 files changed, 196 insertions(+), 143 deletions(-) diff --git a/framework/swell/common/get-page.ts b/framework/swell/common/get-page.ts index 40e9f6982..e5ca005e0 100644 --- a/framework/swell/common/get-page.ts +++ b/framework/swell/common/get-page.ts @@ -1,5 +1,4 @@ import { getConfig, SwellConfig } from '../api' -import getPageQuery from '../utils/queries/get-page-query' import { Page } from './get-all-pages' type Variables = { diff --git a/framework/swell/schema.d.ts b/framework/swell/schema.d.ts index 8ffa23f73..c5309310d 100644 --- a/framework/swell/schema.d.ts +++ b/framework/swell/schema.d.ts @@ -321,96 +321,113 @@ export enum CardBrand { } /** A container for all the information required to checkout items and pay. */ -export type Checkout = Node & { - __typename?: 'Checkout' - /** The gift cards used on the checkout. */ - appliedGiftCards: Array<AppliedGiftCard> - /** - * The available shipping rates for this Checkout. - * Should only be used when checkout `requiresShipping` is `true` and - * the shipping address is valid. - */ - availableShippingRates?: Maybe<AvailableShippingRates> - /** The date and time when the checkout was completed. */ - completedAt?: Maybe<Scalars['DateTime']> - /** The date and time when the checkout was created. */ - createdAt: Scalars['DateTime'] - /** The currency code for the Checkout. */ - currencyCode: CurrencyCode - /** A list of extra information that is added to the checkout. */ - customAttributes: Array<Attribute> - /** - * The customer associated with the checkout. - * @deprecated This field will always return null. If you have an authentication token for the customer, you can use the `customer` field on the query root to retrieve it. - */ - customer?: Maybe<Customer> - /** Discounts that have been applied on the checkout. */ - discountApplications: DiscountApplicationConnection - /** The email attached to this checkout. */ - email?: Maybe<Scalars['String']> - /** Globally unique identifier. */ - id: Scalars['ID'] - /** A list of line item objects, each one containing information about an item in the checkout. */ - lineItems: CheckoutLineItemConnection - /** The sum of all the prices of all the items in the checkout. Duties, taxes, shipping and discounts excluded. */ - lineItemsSubtotalPrice: MoneyV2 - /** The note associated with the checkout. */ - note?: Maybe<Scalars['String']> - /** The resulting order from a paid checkout. */ - order?: Maybe<Order> - /** The Order Status Page for this Checkout, null when checkout is not completed. */ - orderStatusUrl?: Maybe<Scalars['URL']> - /** - * The amount left to be paid. This is equal to the cost of the line items, taxes and shipping minus discounts and gift cards. - * @deprecated Use `paymentDueV2` instead - */ - paymentDue: Scalars['Money'] - /** The amount left to be paid. This is equal to the cost of the line items, duties, taxes and shipping minus discounts and gift cards. */ - paymentDueV2: MoneyV2 - /** - * Whether or not the Checkout is ready and can be completed. Checkouts may - * have asynchronous operations that can take time to finish. If you want - * to complete a checkout or ensure all the fields are populated and up to - * date, polling is required until the value is true. - */ - ready: Scalars['Boolean'] - /** States whether or not the fulfillment requires shipping. */ - requiresShipping: Scalars['Boolean'] - /** The shipping address to where the line items will be shipped. */ - shippingAddress?: Maybe<MailingAddress> - /** The discounts that have been allocated onto the shipping line by discount applications. */ - shippingDiscountAllocations: Array<DiscountAllocation> - /** Once a shipping rate is selected by the customer it is transitioned to a `shipping_line` object. */ - shippingLine?: Maybe<ShippingRate> - /** - * Price of the checkout before shipping and taxes. - * @deprecated Use `subtotalPriceV2` instead - */ - subtotalPrice: Scalars['Money'] - /** Price of the checkout before duties, shipping and taxes. */ - subtotalPriceV2: MoneyV2 - /** Specifies if the Checkout is tax exempt. */ - taxExempt: Scalars['Boolean'] - /** Specifies if taxes are included in the line item and shipping line prices. */ - taxesIncluded: Scalars['Boolean'] - /** - * The sum of all the prices of all the items in the checkout, taxes and discounts included. - * @deprecated Use `totalPriceV2` instead - */ - totalPrice: Scalars['Money'] - /** The sum of all the prices of all the items in the checkout, duties, taxes and discounts included. */ - totalPriceV2: MoneyV2 - /** - * The sum of all the taxes applied to the line items and shipping lines in the checkout. - * @deprecated Use `totalTaxV2` instead - */ - totalTax: Scalars['Money'] - /** The sum of all the taxes applied to the line items and shipping lines in the checkout. */ - totalTaxV2: MoneyV2 - /** The date and time when the checkout was last updated. */ - updatedAt: Scalars['DateTime'] - /** The url pointing to the checkout accessible from the web. */ - webUrl: Scalars['URL'] +export type Checkout = { + name: string + currency: string + support_email: string + fields: any[] + scripts: any[] + accounts: string + email_optin: boolean + terms_policy?: string + refund_policy?: string + privacy_policy?: string + theme?: stirng + countries: any[] + currencies: any[] + payment_methods: any[] + coupons: boolean + giftcards: boolean + + // __typename?: 'Checkout' + // /** The gift cards used on the checkout. */ + // appliedGiftCards: Array<AppliedGiftCard> + // /** + // * The available shipping rates for this Checkout. + // * Should only be used when checkout `requiresShipping` is `true` and + // * the shipping address is valid. + // */ + // availableShippingRates?: Maybe<AvailableShippingRates> + // /** The date and time when the checkout was completed. */ + // completedAt?: Maybe<Scalars['DateTime']> + // /** The date and time when the checkout was created. */ + // createdAt: Scalars['DateTime'] + // /** The currency code for the Checkout. */ + // currencyCode: CurrencyCode + // /** A list of extra information that is added to the checkout. */ + // customAttributes: Array<Attribute> + // /** + // * The customer associated with the checkout. + // * @deprecated This field will always return null. If you have an authentication token for the customer, you can use the `customer` field on the query root to retrieve it. + // */ + // customer?: Maybe<Customer> + // /** Discounts that have been applied on the checkout. */ + // discountApplications: DiscountApplicationConnection + // /** The email attached to this checkout. */ + // email?: Maybe<Scalars['String']> + // /** Globally unique identifier. */ + // id: Scalars['ID'] + // /** A list of line item objects, each one containing information about an item in the checkout. */ + // lineItems: CheckoutLineItemConnection + // /** The sum of all the prices of all the items in the checkout. Duties, taxes, shipping and discounts excluded. */ + // lineItemsSubtotalPrice: MoneyV2 + // /** The note associated with the checkout. */ + // note?: Maybe<Scalars['String']> + // /** The resulting order from a paid checkout. */ + // order?: Maybe<Order> + // /** The Order Status Page for this Checkout, null when checkout is not completed. */ + // orderStatusUrl?: Maybe<Scalars['URL']> + // /** + // * The amount left to be paid. This is equal to the cost of the line items, taxes and shipping minus discounts and gift cards. + // * @deprecated Use `paymentDueV2` instead + // */ + // paymentDue: Scalars['Money'] + // /** The amount left to be paid. This is equal to the cost of the line items, duties, taxes and shipping minus discounts and gift cards. */ + // paymentDueV2: MoneyV2 + // /** + // * Whether or not the Checkout is ready and can be completed. Checkouts may + // * have asynchronous operations that can take time to finish. If you want + // * to complete a checkout or ensure all the fields are populated and up to + // * date, polling is required until the value is true. + // */ + // ready: Scalars['Boolean'] + // /** States whether or not the fulfillment requires shipping. */ + // requiresShipping: Scalars['Boolean'] + // /** The shipping address to where the line items will be shipped. */ + // shippingAddress?: Maybe<MailingAddress> + // /** The discounts that have been allocated onto the shipping line by discount applications. */ + // shippingDiscountAllocations: Array<DiscountAllocation> + // /** Once a shipping rate is selected by the customer it is transitioned to a `shipping_line` object. */ + // shippingLine?: Maybe<ShippingRate> + // /** + // * Price of the checkout before shipping and taxes. + // * @deprecated Use `subtotalPriceV2` instead + // */ + // subtotalPrice: Scalars['Money'] + // /** Price of the checkout before duties, shipping and taxes. */ + // subtotalPriceV2: MoneyV2 + // /** Specifies if the Checkout is tax exempt. */ + // taxExempt: Scalars['Boolean'] + // /** Specifies if taxes are included in the line item and shipping line prices. */ + // taxesIncluded: Scalars['Boolean'] + // /** + // * The sum of all the prices of all the items in the checkout, taxes and discounts included. + // * @deprecated Use `totalPriceV2` instead + // */ + // totalPrice: Scalars['Money'] + // /** The sum of all the prices of all the items in the checkout, duties, taxes and discounts included. */ + // totalPriceV2: MoneyV2 + // /** + // * The sum of all the taxes applied to the line items and shipping lines in the checkout. + // * @deprecated Use `totalTaxV2` instead + // */ + // totalTax: Scalars['Money'] + // /** The sum of all the taxes applied to the line items and shipping lines in the checkout. */ + // totalTaxV2: MoneyV2 + // /** The date and time when the checkout was last updated. */ + // updatedAt: Scalars['DateTime'] + // /** The url pointing to the checkout accessible from the web. */ + // webUrl: Scalars['URL'] } /** A container for all the information required to checkout items and pay. */ diff --git a/framework/swell/types.ts b/framework/swell/types.ts index 456b3da05..24362ea38 100644 --- a/framework/swell/types.ts +++ b/framework/swell/types.ts @@ -1,6 +1,15 @@ import * as Core from '@commerce/types' import { CheckoutLineItem } from './schema' +export type SwellImage = { + file: { + url: String + height: Number + width: Number + } + id: string +} + export type SwellCart = { id: string account_id: number @@ -21,9 +30,28 @@ export type SwellCart = { // TODO: add missing fields } -export type VariantResult = { +export type SwellVariant = { id: string option_value_ids: string[] + name: string + price?: number + stock_status?: string +} + +export interface ProductOptionValue { + label: string + hexColors?: string[] + id: string +} + +export type ProductOptions = { + id: string + name: string + variant: boolean + values: ProductOptionValue[] + required: boolean + active: boolean + attribute_id: string } export interface SwellProduct { diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts index ea12acbae..a4c8cb732 100644 --- a/framework/swell/utils/normalize.ts +++ b/framework/swell/utils/normalize.ts @@ -1,19 +1,22 @@ -import { Product } from '@commerce/types' -import { Customer } from '@commerce/types' +import { Product, Customer } from '@commerce/types' import { fileURLToPath } from 'node:url' import { - Product as ShopifyProduct, Checkout, CheckoutLineItemEdge, - SelectedOption, - ImageConnection, - ProductVariantConnection, MoneyV2, ProductOption, } from '../schema' -import type { Cart, LineItem, SwellCustomer, SwellProduct } from '../types' +import type { + Cart, + LineItem, + SwellCustomer, + SwellProduct, + SwellImage, + SwellVariant, + ProductOptionValue, +} from '../types' const money = ({ amount, currencyCode }: MoneyV2) => { return { @@ -22,15 +25,22 @@ const money = ({ amount, currencyCode }: MoneyV2) => { } } +type normalizedProductOption = { + __typename?: string + id: string + displayName: string + values: ProductOptionValue[] +} + const normalizeProductOption = ({ id, - name: displayName, + name: displayName = '', values, }: ProductOption) => { let returnValues = values.map((value) => { let output: any = { - displayName, label: value.name, + id: value?.id || id, } if (displayName === 'Color') { output = { @@ -48,50 +58,52 @@ const normalizeProductOption = ({ } } -type SwellImage = { - file: { - url: String - height: Number - width: Number - } - id: string -} -const normalizeProductImages = (images) => { +const normalizeProductImages = (images: SwellImage[]) => { if (!images) { return [{ url: '/' }] } return images?.map(({ file, ...rest }: SwellImage) => ({ - url: file?.url, - height: file?.height, - width: file?.width, + url: file?.url + '', + height: Number(file?.height), + width: Number(file?.width), ...rest, })) } -const normalizeProductVariants = (variants) => { - return variants?.map(({ id, name, price, sku }) => { - const values = name - .split(',') - .map((i) => ({ name: i.trim(), label: i.trim() })) +const normalizeProductVariants = ( + variants: SwellVariant[], + productOptions: normalizedProductOption[] +) => { + return variants?.map( + ({ id, name, price, option_value_ids: optionValueIds }) => { + const values = name + .split(',') + .map((i) => ({ name: i.trim(), label: i.trim() })) - const options = values.map((value) => { - return normalizeProductOption({ + const options = optionValueIds.map((id) => { + const matchingOption = productOptions.find((option) => { + return option.values.find( + (value: ProductOptionValue) => value.id == id + ) + }) + return normalizeProductOption({ + id, + name: matchingOption?.displayName ?? '', + values, + }) + }) + + return { id, name, - values: [value], - }) - }) - - return { - id, - name, - sku: sku ?? id, - price: price ?? null, - listPrice: price ?? null, - // requiresShipping: true, - options, + // sku: sku ?? id, + price: price ?? null, + listPrice: price ?? null, + // requiresShipping: true, + options, + } } - }) + ) } export function normalizeProduct(swellProduct: SwellProduct): Product { @@ -108,10 +120,10 @@ export function normalizeProduct(swellProduct: SwellProduct): Product { const productOptions = options ? options.map((o) => normalizeProductOption(o)) : [] - const productVariants = variants ? normalizeProductVariants(variants) : [] + const productVariants = variants + ? normalizeProductVariants(variants, productOptions) + : [] - // ProductView.tsx assumes the existence of at least one product variant - const emptyVariants = [{ options: [{ id: 123 }] }] const productImages = normalizeProductImages(images) const product = { ...swellProduct, @@ -120,10 +132,7 @@ export function normalizeProduct(swellProduct: SwellProduct): Product { vendor: '', path: `/${slug}`, images: productImages, - variants: - productVariants && productVariants.length - ? productVariants - : emptyVariants, + variants: productVariants, options: productOptions, price: { value, @@ -167,12 +176,12 @@ function normalizeLineItem({ }: CheckoutLineItemEdge): LineItem { const item = { id, - variantId: String(variant?.id), - productId: String(product?.id), + variantId: variant?.id ?? '', + productId: product?.id ?? '', name: product?.name ?? '', quantity, variant: { - id: String(variant?.id), + id: variant?.id ?? '', sku: variant?.sku ?? '', name: variant?.name!, image: { From 6a9c6c3bca8a920404a1ded78a910b66a4fbf6df Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Sat, 24 Apr 2021 20:00:13 -0500 Subject: [PATCH 202/221] normalize cart. list brands (attribute) --- framework/swell/cart/use-update-item.tsx | 2 +- framework/swell/types.ts | 23 +++++++----- framework/swell/utils/get-vendors.ts | 18 ++++------ framework/swell/utils/normalize.ts | 46 +++++++++++++++--------- 4 files changed, 51 insertions(+), 38 deletions(-) diff --git a/framework/swell/cart/use-update-item.tsx b/framework/swell/cart/use-update-item.tsx index 3155778c6..38ad65bf1 100644 --- a/framework/swell/cart/use-update-item.tsx +++ b/framework/swell/cart/use-update-item.tsx @@ -73,7 +73,7 @@ export const handler = { const itemId = cartData.lineItems[0].id const productId = cartData.lineItems[0].productId const variantId = cartData.lineItems[0].variant.id - if (!itemId || !productId || !variantId) { + if (!itemId || !productId) { throw new ValidationError({ message: 'Invalid input used for this operation', }) diff --git a/framework/swell/types.ts b/framework/swell/types.ts index 24362ea38..a4ce4afa8 100644 --- a/framework/swell/types.ts +++ b/framework/swell/types.ts @@ -10,23 +10,30 @@ export type SwellImage = { id: string } +export type CartLineItem = { + id: string + product: SwellProduct + price: number + variant: { + name: string | null + sku: string | null + id: string + } + quantity: number +} + export type SwellCart = { id: string account_id: number currency: string tax_included_total: number sub_total: number + grand_total: number discount_total: number quantity: number - items: { - id: string - product: object - price: number - variant: boolean - quantity: number - } + items: CartLineItem[] date_created: string - discounts?: { id: number; amount: number }[] + discounts?: { id: number; amount: number }[] | null // TODO: add missing fields } diff --git a/framework/swell/utils/get-vendors.ts b/framework/swell/utils/get-vendors.ts index 632f5daaf..d80854f6d 100644 --- a/framework/swell/utils/get-vendors.ts +++ b/framework/swell/utils/get-vendors.ts @@ -1,4 +1,5 @@ -import { SwellConfig } from '../api' +import { swellConfig } from '@framework' +import { getConfig, SwellConfig } from '../api' import fetchAllProducts from '../api/utils/fetch-all-products' import getAllProductVendors from './queries/get-all-product-vendors-query' @@ -13,18 +14,11 @@ export type BrandEdge = { export type Brands = BrandEdge[] -const getVendors = async (config: SwellConfig): Promise<BrandEdge[]> => { - const vendors = await fetchAllProducts({ - config, - query: getAllProductVendors, - variables: { - first: 250, - }, - }) +const getVendors = async (config: SwellConfig) => { + const vendors = + (await config.fetchSwell('attributes', 'get', ['brand']).values) ?? [] - let vendorsStrings = vendors.map(({ node: { vendor } }) => vendor) - - return [...new Set(vendorsStrings)].map((v) => ({ + return [...new Set(vendors)].map((v) => ({ node: { entityId: v, name: v, diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts index a4c8cb732..63b63feb0 100644 --- a/framework/swell/utils/normalize.ts +++ b/framework/swell/utils/normalize.ts @@ -1,5 +1,4 @@ import { Product, Customer } from '@commerce/types' -import { fileURLToPath } from 'node:url' import { Checkout, @@ -10,12 +9,14 @@ import { import type { Cart, - LineItem, + CartLineItem, SwellCustomer, SwellProduct, SwellImage, SwellVariant, ProductOptionValue, + SwellCart, + LineItem, } from '../types' const money = ({ amount, currencyCode }: MoneyV2) => { @@ -142,20 +143,31 @@ export function normalizeProduct(swellProduct: SwellProduct): Product { return product } -export function normalizeCart(cart: Checkout): Cart { - return { - id: cart.id, - customerId: cart.account_id, +export function normalizeCart({ + id, + account_id, + date_created, + currency, + tax_included_total, + items, + sub_total, + grand_total, + discounts, +}: SwellCart) { + const cart: Cart = { + id: id, + customerId: account_id + '', email: '', - createdAt: cart.date_created, - currency: cart.currency, - taxesIncluded: cart.tax_included_total, - lineItems: cart.items?.map(normalizeLineItem), - lineItemsSubtotalPrice: +cart.sub_total, - subtotalPrice: +cart.sub_total, - totalPrice: cart.grand_total, - discounts: cart.discounts, + createdAt: date_created, + currency: { code: currency }, + taxesIncluded: tax_included_total > 0, + lineItems: items?.map(normalizeLineItem), + lineItemsSubtotalPrice: +sub_total, + subtotalPrice: +sub_total, + totalPrice: grand_total, + discounts: discounts?.map((discount) => ({ value: discount.amount })), } + return cart } export function normalizeCustomer(customer: SwellCustomer): Customer { @@ -173,11 +185,11 @@ function normalizeLineItem({ price, variant, quantity, -}: CheckoutLineItemEdge): LineItem { +}: CartLineItem): LineItem { const item = { id, - variantId: variant?.id ?? '', - productId: product?.id ?? '', + variantId: variant?.id, + productId: product.id ?? '', name: product?.name ?? '', quantity, variant: { From a409c373c43eeed126b004cadf0a16db5e43ba87 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Sun, 25 Apr 2021 14:20:58 -0500 Subject: [PATCH 203/221] cleanup, add sorting --- framework/swell/.env.template | 4 +- framework/swell/README.md | 2 +- framework/swell/api/checkout/index.ts | 6 +- framework/swell/api/index.ts | 34 +++------ .../swell/api/utils/create-api-handler.ts | 20 +++--- .../swell/api/utils/fetch-all-products.ts | 12 ++-- .../swell/api/utils/fetch-graphql-api.ts | 34 --------- framework/swell/api/utils/fetch-swell-api.ts | 1 - framework/swell/auth/use-login.tsx | 1 - framework/swell/auth/use-logout.tsx | 1 - framework/swell/cart/use-add-item.tsx | 3 +- framework/swell/cart/use-cart.tsx | 1 - framework/swell/common/get-all-pages.ts | 2 - framework/swell/const.ts | 12 ++-- framework/swell/customer/get-customer-id.ts | 24 ------- framework/swell/customer/use-customer.tsx | 1 - framework/swell/fetcher.ts | 2 - framework/swell/index.tsx | 4 +- .../swell/product/get-all-collections.ts | 7 +- .../swell/product/get-all-product-paths.ts | 8 +-- framework/swell/product/get-all-products.ts | 3 - framework/swell/product/get-product.ts | 1 - framework/swell/product/use-search.tsx | 16 +++-- framework/swell/types.ts | 2 +- framework/swell/utils/customer-token.ts | 10 +-- framework/swell/utils/get-checkout-id.ts | 4 +- framework/swell/utils/get-vendors.ts | 9 +-- .../swell/utils/handle-fetch-response.ts | 2 +- framework/swell/utils/index.ts | 3 +- .../associate-customer-with-checkout.ts | 18 ----- .../swell/utils/mutations/checkout-create.ts | 16 ----- .../utils/mutations/checkout-line-item-add.ts | 16 ----- .../mutations/checkout-line-item-remove.ts | 19 ----- .../mutations/checkout-line-item-update.ts | 16 ----- .../mutations/customer-access-token-create.ts | 16 ----- .../mutations/customer-access-token-delete.ts | 14 ---- .../swell/utils/mutations/customer-create.ts | 15 ---- framework/swell/utils/mutations/index.ts | 7 -- framework/swell/utils/normalize.ts | 2 +- .../queries/get-all-collections-query.ts | 14 ---- .../utils/queries/get-all-pages-query.ts | 14 ---- .../queries/get-all-product-vendors-query.ts | 17 ----- .../queries/get-all-products-paths-query.ts | 17 ----- .../utils/queries/get-all-products-query.ts | 57 --------------- .../swell/utils/queries/get-checkout-query.ts | 62 ----------------- .../queries/get-collection-products-query.ts | 24 ------- .../utils/queries/get-customer-id-query.ts | 8 --- .../swell/utils/queries/get-customer-query.ts | 16 ----- .../swell/utils/queries/get-page-query.ts | 14 ---- .../swell/utils/queries/get-product-query.ts | 69 ------------------- framework/swell/utils/queries/index.ts | 10 --- framework/swell/wishlist/use-wishlist.tsx | 2 +- 52 files changed, 74 insertions(+), 618 deletions(-) delete mode 100644 framework/swell/api/utils/fetch-graphql-api.ts delete mode 100644 framework/swell/customer/get-customer-id.ts delete mode 100644 framework/swell/utils/mutations/associate-customer-with-checkout.ts delete mode 100644 framework/swell/utils/mutations/checkout-create.ts delete mode 100644 framework/swell/utils/mutations/checkout-line-item-add.ts delete mode 100644 framework/swell/utils/mutations/checkout-line-item-remove.ts delete mode 100644 framework/swell/utils/mutations/checkout-line-item-update.ts delete mode 100644 framework/swell/utils/mutations/customer-access-token-create.ts delete mode 100644 framework/swell/utils/mutations/customer-access-token-delete.ts delete mode 100644 framework/swell/utils/mutations/customer-create.ts delete mode 100644 framework/swell/utils/mutations/index.ts delete mode 100644 framework/swell/utils/queries/get-all-collections-query.ts delete mode 100644 framework/swell/utils/queries/get-all-pages-query.ts delete mode 100644 framework/swell/utils/queries/get-all-product-vendors-query.ts delete mode 100644 framework/swell/utils/queries/get-all-products-paths-query.ts delete mode 100644 framework/swell/utils/queries/get-all-products-query.ts delete mode 100644 framework/swell/utils/queries/get-checkout-query.ts delete mode 100644 framework/swell/utils/queries/get-collection-products-query.ts delete mode 100644 framework/swell/utils/queries/get-customer-id-query.ts delete mode 100644 framework/swell/utils/queries/get-customer-query.ts delete mode 100644 framework/swell/utils/queries/get-page-query.ts delete mode 100644 framework/swell/utils/queries/get-product-query.ts delete mode 100644 framework/swell/utils/queries/index.ts diff --git a/framework/swell/.env.template b/framework/swell/.env.template index b5f38e883..43c931f45 100644 --- a/framework/swell/.env.template +++ b/framework/swell/.env.template @@ -1,5 +1,5 @@ -SHOPIFY_STORE_DOMAIN= -SHOPIFY_STOREFRONT_ACCESS_TOKEN= +SWELL_STORE_DOMAIN= +SWELL_STOREFRONT_ACCESS_TOKEN= NEXT_PUBLIC_SWELL_STORE_ID= NEXT_PUBLIC_SWELL_PUBLIC_KEY= diff --git a/framework/swell/README.md b/framework/swell/README.md index fc6a70ce3..f47a7a028 100644 --- a/framework/swell/README.md +++ b/framework/swell/README.md @@ -36,7 +36,7 @@ yarn install -D @types/shopify-buy ``` SHOPIFY_STORE_DOMAIN= SHOPIFY_STOREFRONT_ACCESS_TOKEN= -NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN= +NEXT_PUBLIC_SWELL_STORE_DOMAIN= NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN= ``` diff --git a/framework/swell/api/checkout/index.ts b/framework/swell/api/checkout/index.ts index 03f166a1a..0759e2abc 100644 --- a/framework/swell/api/checkout/index.ts +++ b/framework/swell/api/checkout/index.ts @@ -1,12 +1,10 @@ -import createApiHandler, { - ShopifyApiHandler, -} from '../utils/create-api-handler' +import createApiHandler, { SwellApiHandler } from '../utils/create-api-handler' import { SWELL_CHECKOUT_URL_COOKIE } from '../../const' import { getConfig } from '..' -const checkoutApi: ShopifyApiHandler<any> = async (req, res, config) => { +const checkoutApi: SwellApiHandler<any> = async (req, res, config) => { config = getConfig() const { cookies } = req diff --git a/framework/swell/api/index.ts b/framework/swell/api/index.ts index 9c41b18ae..1e328062b 100644 --- a/framework/swell/api/index.ts +++ b/framework/swell/api/index.ts @@ -1,26 +1,12 @@ import type { CommerceAPIConfig } from '@commerce/api' import { - API_URL, - API_TOKEN, - SHOPIFY_CHECKOUT_ID_COOKIE, - SHOPIFY_CUSTOMER_TOKEN_COOKIE, - SHOPIFY_COOKIE_EXPIRE, + SWELL_CHECKOUT_ID_COOKIE, + SWELL_CUSTOMER_TOKEN_COOKIE, + SWELL_COOKIE_EXPIRE, } from '../const' -if (!API_URL) { - throw new Error( - `The environment variable NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN is missing and it's required to access your store` - ) -} - -if (!API_TOKEN) { - throw new Error( - `The environment variable NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store` - ) -} - -import fetchGraphqlApi from './utils/fetch-graphql-api' +import fetcher from '../fetcher' import fetchSwellApi from './utils/fetch-swell-api' export interface SwellConfig extends CommerceAPIConfig { @@ -48,13 +34,13 @@ export class Config { const config = new Config({ locale: 'en-US', - commerceUrl: API_URL, - apiToken: API_TOKEN!, - cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, - cartCookieMaxAge: SHOPIFY_COOKIE_EXPIRE, + commerceUrl: '', + apiToken: ''!, + cartCookie: SWELL_CHECKOUT_ID_COOKIE, + cartCookieMaxAge: SWELL_COOKIE_EXPIRE, fetchSwell: fetchSwellApi, - fetch: fetchGraphqlApi, - customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE, + fetch: fetcher, + customerCookie: SWELL_CUSTOMER_TOKEN_COOKIE, }) export function getConfig(userConfig?: Partial<SwellConfig>) { diff --git a/framework/swell/api/utils/create-api-handler.ts b/framework/swell/api/utils/create-api-handler.ts index 7ceb7d423..bd40a7ee4 100644 --- a/framework/swell/api/utils/create-api-handler.ts +++ b/framework/swell/api/utils/create-api-handler.ts @@ -1,41 +1,41 @@ import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next' import { SwellConfig, getConfig } from '..' -export type ShopifyApiHandler< +export type SwellApiHandler< T = any, - H extends ShopifyHandlers = {}, + H extends SwellHandlers = {}, Options extends {} = {} > = ( req: NextApiRequest, - res: NextApiResponse<ShopifyApiResponse<T>>, + res: NextApiResponse<SwellApiResponse<T>>, config: SwellConfig, handlers: H, // Custom configs that may be used by a particular handler options: Options ) => void | Promise<void> -export type ShopifyHandler<T = any, Body = null> = (options: { +export type SwellHandler<T = any, Body = null> = (options: { req: NextApiRequest - res: NextApiResponse<ShopifyApiResponse<T>> + res: NextApiResponse<SwellApiResponse<T>> config: SwellConfig body: Body }) => void | Promise<void> -export type ShopifyHandlers<T = any> = { - [k: string]: ShopifyHandler<T, any> +export type SwellHandlers<T = any> = { + [k: string]: SwellHandler<T, any> } -export type ShopifyApiResponse<T> = { +export type SwellApiResponse<T> = { data: T | null errors?: { message: string; code?: string }[] } export default function createApiHandler< T = any, - H extends ShopifyHandlers = {}, + H extends SwellHandlers = {}, Options extends {} = {} >( - handler: ShopifyApiHandler<T, H, Options>, + handler: SwellApiHandler<T, H, Options>, handlers: H, defaultOptions: Options ) { diff --git a/framework/swell/api/utils/fetch-all-products.ts b/framework/swell/api/utils/fetch-all-products.ts index 7ad308b04..859ec0212 100644 --- a/framework/swell/api/utils/fetch-all-products.ts +++ b/framework/swell/api/utils/fetch-all-products.ts @@ -4,6 +4,7 @@ import { SwellConfig } from '..' const fetchAllProducts = async ({ config, query, + method, variables, acc = [], cursor, @@ -14,12 +15,13 @@ const fetchAllProducts = async ({ variables?: any cursor?: string }): Promise<ProductEdge[]> => { - const { data } = await config.fetch(query, { - variables: { ...variables, cursor }, - }) + // const response = await config.fetch(query, { + // variables: { ...variables, cursor }, + // }) + const response = await config.fetchSwell('products', 'list', [{ limit: 100 }]) - const edges: ProductEdge[] = data.products?.edges ?? [] - const hasNextPage = data.products?.pageInfo?.hasNextPage + const edges: ProductEdge[] = response.results ?? [] + const hasNextPage = response.results.length < response.count acc = acc.concat(edges) if (hasNextPage) { diff --git a/framework/swell/api/utils/fetch-graphql-api.ts b/framework/swell/api/utils/fetch-graphql-api.ts deleted file mode 100644 index 321cba2aa..000000000 --- a/framework/swell/api/utils/fetch-graphql-api.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { GraphQLFetcher } from '@commerce/api' -import fetch from './fetch' - -import { API_URL, API_TOKEN } from '../../const' -import { getError } from '../../utils/handle-fetch-response' - -const fetchGraphqlApi: GraphQLFetcher = async ( - query: string, - { variables } = {}, - fetchOptions -) => { - const res = await fetch(API_URL, { - ...fetchOptions, - method: 'POST', - headers: { - 'X-Shopify-Storefront-Access-Token': API_TOKEN!, - ...fetchOptions?.headers, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query, - variables, - }), - }) - - const { data, errors, status } = await res.json() - - if (errors) { - throw getError(errors, status) - } - - return { data, res } -} -export default fetchGraphqlApi diff --git a/framework/swell/api/utils/fetch-swell-api.ts b/framework/swell/api/utils/fetch-swell-api.ts index c8707230d..e070483ae 100644 --- a/framework/swell/api/utils/fetch-swell-api.ts +++ b/framework/swell/api/utils/fetch-swell-api.ts @@ -6,7 +6,6 @@ const fetchSwellApi = async ( variables: [] = [] ) => { const { swell } = swellConfig - return await swell[query][method](...variables) } export default fetchSwellApi diff --git a/framework/swell/auth/use-login.tsx b/framework/swell/auth/use-login.tsx index cdaf6151b..57c3ce921 100644 --- a/framework/swell/auth/use-login.tsx +++ b/framework/swell/auth/use-login.tsx @@ -2,7 +2,6 @@ import { useCallback } from 'react' import type { MutationHook } from '@commerce/utils/types' import { CommerceError, ValidationError } from '@commerce/utils/errors' import useCustomer from '../customer/use-customer' -import createCustomerAccessTokenMutation from '../utils/mutations/customer-access-token-create' import { CustomerAccessTokenCreateInput, CustomerUserError, diff --git a/framework/swell/auth/use-logout.tsx b/framework/swell/auth/use-logout.tsx index 6eebd997f..ae17ba74e 100644 --- a/framework/swell/auth/use-logout.tsx +++ b/framework/swell/auth/use-logout.tsx @@ -2,7 +2,6 @@ import { useCallback } from 'react' import type { MutationHook } from '@commerce/utils/types' import useLogout, { UseLogout } from '@commerce/auth/use-logout' import useCustomer from '../customer/use-customer' -import customerAccessTokenDeleteMutation from '../utils/mutations/customer-access-token-delete' import { getCustomerToken, setCustomerToken } from '../utils/customer-token' export default useLogout as UseLogout<typeof handler> diff --git a/framework/swell/cart/use-add-item.tsx b/framework/swell/cart/use-add-item.tsx index ff6c7143f..346c6c955 100644 --- a/framework/swell/cart/use-add-item.tsx +++ b/framework/swell/cart/use-add-item.tsx @@ -4,6 +4,7 @@ import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item' import useCart from './use-cart' import { Cart, CartItemBody } from '../types' import { checkoutToCart } from './utils' +import { getCheckoutId } from '../utils' import { Mutation, MutationCheckoutLineItemsAddArgs } from '../schema' import { useCallback } from 'react' @@ -26,12 +27,12 @@ export const handler: MutationHook<Cart, {}, CartItemBody> = { const response = await fetch<Mutation, MutationCheckoutLineItemsAddArgs>({ ...options, variables: { + checkoutId: getCheckoutId(), product_id: item.productId, quantity: item.quantity, }, }) - // TODO: Fix this Cart type here return checkoutToCart(response) as any }, useHook: ({ fetch }) => () => { diff --git a/framework/swell/cart/use-cart.tsx b/framework/swell/cart/use-cart.tsx index 04d07914d..354b4f947 100644 --- a/framework/swell/cart/use-cart.tsx +++ b/framework/swell/cart/use-cart.tsx @@ -15,7 +15,6 @@ export const handler: SWRHook<Cart | null> = { const cart = await checkoutCreate(fetch) return cart ? normalizeCart(cart) : null - // return checkoutToCart({ checkout } as any) }, useHook: ({ useData }) => (input) => { return useData({ diff --git a/framework/swell/common/get-all-pages.ts b/framework/swell/common/get-all-pages.ts index 1088a472c..489af339c 100644 --- a/framework/swell/common/get-all-pages.ts +++ b/framework/swell/common/get-all-pages.ts @@ -1,6 +1,4 @@ import { getConfig, SwellConfig } from '../api' -import { PageEdge } from '../schema' -import { getAllPagesQuery } from '../utils/queries' type Variables = { first?: number diff --git a/framework/swell/const.ts b/framework/swell/const.ts index 7226b19cb..669194298 100644 --- a/framework/swell/const.ts +++ b/framework/swell/const.ts @@ -1,16 +1,12 @@ -export const SHOPIFY_CHECKOUT_ID_COOKIE = 'shopify_checkoutId' +export const SWELL_CHECKOUT_ID_COOKIE = 'SWELL_checkoutId' export const SWELL_CHECKOUT_URL_COOKIE = 'swell_checkoutUrl' -export const SHOPIFY_CUSTOMER_TOKEN_COOKIE = 'shopify_customerToken' +export const SWELL_CUSTOMER_TOKEN_COOKIE = 'swell_customerToken' -export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN +export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SWELL_STORE_DOMAIN -export const SHOPIFY_COOKIE_EXPIRE = 30 - -export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json` - -export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN +export const SWELL_COOKIE_EXPIRE = 30 export const SWELL_STORE_ID = process.env.NEXT_PUBLIC_SWELL_STORE_ID diff --git a/framework/swell/customer/get-customer-id.ts b/framework/swell/customer/get-customer-id.ts deleted file mode 100644 index c3a1b8d2f..000000000 --- a/framework/swell/customer/get-customer-id.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { getConfig, SwellConfig } from '../api' -import getCustomerIdQuery from '../utils/queries/get-customer-id-query' -import Cookies from 'js-cookie' - -async function getCustomerId({ - customerToken: customerAccesToken, - config, -}: { - customerToken: string - config?: SwellConfig -}): Promise<number | undefined> { - config = getConfig(config) - - const { data } = await config.fetch(getCustomerIdQuery, { - variables: { - customerAccesToken: - customerAccesToken || Cookies.get(config.customerCookie), - }, - }) - - return data.customer?.id -} - -export default getCustomerId diff --git a/framework/swell/customer/use-customer.tsx b/framework/swell/customer/use-customer.tsx index 7b84795aa..632065d5a 100644 --- a/framework/swell/customer/use-customer.tsx +++ b/framework/swell/customer/use-customer.tsx @@ -2,7 +2,6 @@ import useCustomer, { UseCustomer } from '@commerce/customer/use-customer' import { Customer } from '@commerce/types' import { SWRHook } from '@commerce/utils/types' import { normalizeCustomer } from '../utils/normalize' -// import { getCustomerQuery, getCustomerToken } from '../utils' export default useCustomer as UseCustomer<typeof handler> diff --git a/framework/swell/fetcher.ts b/framework/swell/fetcher.ts index 4c65b91d2..e52d8fd54 100644 --- a/framework/swell/fetcher.ts +++ b/framework/swell/fetcher.ts @@ -8,11 +8,9 @@ const fetcher: Fetcher = async ({ method = 'get', variables, query }) => { if (Array.isArray(variables)) { const arg1 = variables[0] const arg2 = variables[1] - // console.log('fetcher', query, method, variables); const response = await swell[query][method](arg1, arg2) return handleFetchResponse(response) } else { - // console.log('fetcher', query, method, variables); const response = await swell[query][method](variables) return handleFetchResponse(response) } diff --git a/framework/swell/index.tsx b/framework/swell/index.tsx index db8cd3e1f..28f60b394 100644 --- a/framework/swell/index.tsx +++ b/framework/swell/index.tsx @@ -10,7 +10,7 @@ import { import { swellProvider, SwellProvider } from './provider' import { - SHOPIFY_CHECKOUT_ID_COOKIE, + SWELL_CHECKOUT_ID_COOKIE, SWELL_STORE_ID, SWELL_PUBLIC_KEY, } from './const' @@ -21,7 +21,7 @@ export type { SwellProvider } export const swellConfig: any = { locale: 'en-us', - cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, + cartCookie: SWELL_CHECKOUT_ID_COOKIE, swell, } diff --git a/framework/swell/product/get-all-collections.ts b/framework/swell/product/get-all-collections.ts index b3832316c..e6da3ade4 100644 --- a/framework/swell/product/get-all-collections.ts +++ b/framework/swell/product/get-all-collections.ts @@ -1,17 +1,16 @@ import { CollectionEdge } from '../schema' import { getConfig, SwellConfig } from '../api' -import getAllCollectionsQuery from '../utils/queries/get-all-collections-query' const getAllCollections = async (options?: { variables?: any config: SwellConfig preview?: boolean }) => { - let { config, variables = { first: 250 } } = options ?? {} + let { config, variables = { limit: 25 } } = options ?? {} config = getConfig(config) - const { data } = await config.fetch(getAllCollectionsQuery, { variables }) - const edges = data.collections?.edges ?? [] + const response = await config.fetchSwell('categories', 'list', { variables }) + const edges = response.results ?? [] const categories = edges.map( ({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({ diff --git a/framework/swell/product/get-all-product-paths.ts b/framework/swell/product/get-all-product-paths.ts index 4f3dec29a..fad11f8c2 100644 --- a/framework/swell/product/get-all-product-paths.ts +++ b/framework/swell/product/get-all-product-paths.ts @@ -2,7 +2,6 @@ import { Product } from '@commerce/types' import { getConfig, SwellConfig } from '../api' import fetchAllProducts from '../api/utils/fetch-all-products' import { ProductEdge } from '../schema' -import getAllProductsPathsQuery from '../utils/queries/get-all-products-paths-query' type ProductPath = { path: string @@ -21,17 +20,18 @@ const getAllProductPaths = async (options?: { config?: SwellConfig preview?: boolean }): Promise<ReturnType> => { - let { config, variables = { first: 250 } } = options ?? {} + let { config, variables = { limit: 100 } } = options ?? {} config = getConfig(config) const products = await fetchAllProducts({ config, - query: getAllProductsPathsQuery, + query: 'products', + method: 'list', variables, }) return { - products: products?.map(({ node: { handle } }: ProductEdge) => ({ + products: products?.map(({ slug: handle }) => ({ node: { path: `/${handle}`, }, diff --git a/framework/swell/product/get-all-products.ts b/framework/swell/product/get-all-products.ts index 1f4f4152c..dff5bf067 100644 --- a/framework/swell/product/get-all-products.ts +++ b/framework/swell/product/get-all-products.ts @@ -1,7 +1,4 @@ -import { GraphQLFetcherResult } from '@commerce/api' import { getConfig, SwellConfig } from '../api' -import { ProductEdge } from '../schema' -import { getAllProductsQuery } from '../utils/queries' import { normalizeProduct } from '../utils/normalize' import { Product } from '@commerce/types' diff --git a/framework/swell/product/get-product.ts b/framework/swell/product/get-product.ts index 49bc5b67f..1923fc75a 100644 --- a/framework/swell/product/get-product.ts +++ b/framework/swell/product/get-product.ts @@ -22,7 +22,6 @@ const getProduct = async (options: { if (product.variants) { product.variants = product.variants?.results } - // console.log('product', product) return { product: normalizeProduct(product), } diff --git a/framework/swell/product/use-search.tsx b/framework/swell/product/use-search.tsx index 14b966319..b93a5598f 100644 --- a/framework/swell/product/use-search.tsx +++ b/framework/swell/product/use-search.tsx @@ -1,7 +1,7 @@ import { SWRHook } from '@commerce/utils/types' import useSearch, { UseSearch } from '@commerce/product/use-search' -import { getAllProductsQuery, normalizeProduct } from '../utils' +import { normalizeProduct } from '../utils' import { Product } from '@commerce/types' @@ -25,14 +25,22 @@ export const handler: SWRHook< SearchProductsInput > = { fetchOptions: { - query: getAllProductsQuery, + query: 'products', // String(Math.random()), + method: 'list', }, async fetcher({ input, options, fetch }) { - const { categoryId, search } = input + const sortMap = new Map([ + ['latest-desc', ''], + ['price-asc', 'price_asc'], + ['price-desc', 'price_desc'], + ['trending-desc', 'popularity'], + ]) + const { categoryId, search, sort = 'latest-desc' } = input + const mappedSort = sortMap.get(sort) const { results, count: found } = await fetch({ query: 'products', method: 'list', - variables: { category: categoryId, search }, + variables: { category: categoryId, search, sort: mappedSort }, }) const products = results.map((product) => { diff --git a/framework/swell/types.ts b/framework/swell/types.ts index a4ce4afa8..248ea3158 100644 --- a/framework/swell/types.ts +++ b/framework/swell/types.ts @@ -78,7 +78,7 @@ export interface SwellCustomer extends Core.Customer { last_name: string } -export type ShopifyCheckout = { +export type SwellCheckout = { id: string webUrl: string lineItems: CheckoutLineItem[] diff --git a/framework/swell/utils/customer-token.ts b/framework/swell/utils/customer-token.ts index 85454cb83..63bd51bc0 100644 --- a/framework/swell/utils/customer-token.ts +++ b/framework/swell/utils/customer-token.ts @@ -1,20 +1,20 @@ import Cookies, { CookieAttributes } from 'js-cookie' -import { SHOPIFY_COOKIE_EXPIRE, SHOPIFY_CUSTOMER_TOKEN_COOKIE } from '../const' +import { SWELL_COOKIE_EXPIRE, SWELL_CUSTOMER_TOKEN_COOKIE } from '../const' -export const getCustomerToken = () => Cookies.get(SHOPIFY_CUSTOMER_TOKEN_COOKIE) +export const getCustomerToken = () => Cookies.get(SWELL_CUSTOMER_TOKEN_COOKIE) export const setCustomerToken = ( token: string | null, options?: CookieAttributes ) => { if (!token) { - Cookies.remove(SHOPIFY_CUSTOMER_TOKEN_COOKIE) + Cookies.remove(SWELL_CUSTOMER_TOKEN_COOKIE) } else { Cookies.set( - SHOPIFY_CUSTOMER_TOKEN_COOKIE, + SWELL_CUSTOMER_TOKEN_COOKIE, token, options ?? { - expires: SHOPIFY_COOKIE_EXPIRE, + expires: SWELL_COOKIE_EXPIRE, } ) } diff --git a/framework/swell/utils/get-checkout-id.ts b/framework/swell/utils/get-checkout-id.ts index 11e3802d9..07643f475 100644 --- a/framework/swell/utils/get-checkout-id.ts +++ b/framework/swell/utils/get-checkout-id.ts @@ -1,8 +1,8 @@ import Cookies from 'js-cookie' -import { SHOPIFY_CHECKOUT_ID_COOKIE } from '../const' +import { SWELL_CHECKOUT_ID_COOKIE } from '../const' const getCheckoutId = (id?: string) => { - return id ?? Cookies.get(SHOPIFY_CHECKOUT_ID_COOKIE) + return id ?? Cookies.get(SWELL_CHECKOUT_ID_COOKIE) } export default getCheckoutId diff --git a/framework/swell/utils/get-vendors.ts b/framework/swell/utils/get-vendors.ts index d80854f6d..a96d9dccd 100644 --- a/framework/swell/utils/get-vendors.ts +++ b/framework/swell/utils/get-vendors.ts @@ -1,7 +1,4 @@ -import { swellConfig } from '@framework' -import { getConfig, SwellConfig } from '../api' -import fetchAllProducts from '../api/utils/fetch-all-products' -import getAllProductVendors from './queries/get-all-product-vendors-query' +import { SwellConfig } from '../api' export type BrandNode = { name: string @@ -15,8 +12,8 @@ export type BrandEdge = { export type Brands = BrandEdge[] const getVendors = async (config: SwellConfig) => { - const vendors = - (await config.fetchSwell('attributes', 'get', ['brand']).values) ?? [] + const vendors: [string] = + (await config.fetchSwell('attributes', 'get', ['brand'])).values ?? [] return [...new Set(vendors)].map((v) => ({ node: { diff --git a/framework/swell/utils/handle-fetch-response.ts b/framework/swell/utils/handle-fetch-response.ts index 79aba8bd3..96ceb2469 100644 --- a/framework/swell/utils/handle-fetch-response.ts +++ b/framework/swell/utils/handle-fetch-response.ts @@ -1,7 +1,7 @@ import { FetcherError } from '@commerce/utils/errors' export function getError(errors: any[], status: number) { - errors = errors ?? [{ message: 'Failed to fetch Shopify API' }] + errors = errors ?? [{ message: 'Failed to fetch Swell API' }] return new FetcherError({ errors, status }) } diff --git a/framework/swell/utils/index.ts b/framework/swell/utils/index.ts index 2d59aa506..9ec81bfb8 100644 --- a/framework/swell/utils/index.ts +++ b/framework/swell/utils/index.ts @@ -4,7 +4,6 @@ export { default as getSortVariables } from './get-sort-variables' export { default as getVendors } from './get-vendors' export { default as getCategories } from './get-categories' export { default as getCheckoutId } from './get-checkout-id' -export * from './queries' -export * from './mutations' + export * from './normalize' export * from './customer-token' diff --git a/framework/swell/utils/mutations/associate-customer-with-checkout.ts b/framework/swell/utils/mutations/associate-customer-with-checkout.ts deleted file mode 100644 index 6b1350e05..000000000 --- a/framework/swell/utils/mutations/associate-customer-with-checkout.ts +++ /dev/null @@ -1,18 +0,0 @@ -const associateCustomerWithCheckoutMutation = /* GraphQl */ ` -mutation associateCustomerWithCheckout($checkoutId: ID!, $customerAccessToken: String!) { - checkoutCustomerAssociateV2(checkoutId: $checkoutId, customerAccessToken: $customerAccessToken) { - checkout { - id - } - checkoutUserErrors { - code - field - message - } - customer { - id - } - } - } -` -export default associateCustomerWithCheckoutMutation diff --git a/framework/swell/utils/mutations/checkout-create.ts b/framework/swell/utils/mutations/checkout-create.ts deleted file mode 100644 index 912e1cbd2..000000000 --- a/framework/swell/utils/mutations/checkout-create.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { checkoutDetailsFragment } from '../queries/get-checkout-query' - -const checkoutCreateMutation = /* GraphQL */ ` - mutation { - checkoutCreate(input: {}) { - userErrors { - message - field - } - checkout { - ${checkoutDetailsFragment} - } - } - } -` -export default checkoutCreateMutation diff --git a/framework/swell/utils/mutations/checkout-line-item-add.ts b/framework/swell/utils/mutations/checkout-line-item-add.ts deleted file mode 100644 index 67b9cf250..000000000 --- a/framework/swell/utils/mutations/checkout-line-item-add.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { checkoutDetailsFragment } from '../queries/get-checkout-query' - -const checkoutLineItemAddMutation = /* GraphQL */ ` - mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemInput!]!) { - checkoutLineItemsAdd(checkoutId: $checkoutId, lineItems: $lineItems) { - userErrors { - message - field - } - checkout { - ${checkoutDetailsFragment} - } - } - } -` -export default checkoutLineItemAddMutation diff --git a/framework/swell/utils/mutations/checkout-line-item-remove.ts b/framework/swell/utils/mutations/checkout-line-item-remove.ts deleted file mode 100644 index d967a5168..000000000 --- a/framework/swell/utils/mutations/checkout-line-item-remove.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { checkoutDetailsFragment } from '../queries/get-checkout-query' - -const checkoutLineItemRemoveMutation = /* GraphQL */ ` - mutation($checkoutId: ID!, $lineItemIds: [ID!]!) { - checkoutLineItemsRemove( - checkoutId: $checkoutId - lineItemIds: $lineItemIds - ) { - userErrors { - message - field - } - checkout { - ${checkoutDetailsFragment} - } - } - } -` -export default checkoutLineItemRemoveMutation diff --git a/framework/swell/utils/mutations/checkout-line-item-update.ts b/framework/swell/utils/mutations/checkout-line-item-update.ts deleted file mode 100644 index 8edf17587..000000000 --- a/framework/swell/utils/mutations/checkout-line-item-update.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { checkoutDetailsFragment } from '../queries/get-checkout-query' - -const checkoutLineItemUpdateMutation = /* GraphQL */ ` - mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemUpdateInput!]!) { - checkoutLineItemsUpdate(checkoutId: $checkoutId, lineItems: $lineItems) { - userErrors { - message - field - } - checkout { - ${checkoutDetailsFragment} - } - } - } -` -export default checkoutLineItemUpdateMutation diff --git a/framework/swell/utils/mutations/customer-access-token-create.ts b/framework/swell/utils/mutations/customer-access-token-create.ts deleted file mode 100644 index 7a45c3f49..000000000 --- a/framework/swell/utils/mutations/customer-access-token-create.ts +++ /dev/null @@ -1,16 +0,0 @@ -const customerAccessTokenCreateMutation = /* GraphQL */ ` - mutation customerAccessTokenCreate($input: CustomerAccessTokenCreateInput!) { - customerAccessTokenCreate(input: $input) { - customerAccessToken { - accessToken - expiresAt - } - customerUserErrors { - code - field - message - } - } - } -` -export default customerAccessTokenCreateMutation diff --git a/framework/swell/utils/mutations/customer-access-token-delete.ts b/framework/swell/utils/mutations/customer-access-token-delete.ts deleted file mode 100644 index c46eff1e5..000000000 --- a/framework/swell/utils/mutations/customer-access-token-delete.ts +++ /dev/null @@ -1,14 +0,0 @@ -const customerAccessTokenDeleteMutation = /* GraphQL */ ` - mutation customerAccessTokenDelete($customerAccessToken: String!) { - customerAccessTokenDelete(customerAccessToken: $customerAccessToken) { - deletedAccessToken - deletedCustomerAccessTokenId - userErrors { - field - message - } - } - } -` - -export default customerAccessTokenDeleteMutation diff --git a/framework/swell/utils/mutations/customer-create.ts b/framework/swell/utils/mutations/customer-create.ts deleted file mode 100644 index 05c728a25..000000000 --- a/framework/swell/utils/mutations/customer-create.ts +++ /dev/null @@ -1,15 +0,0 @@ -const customerCreateMutation = /* GraphQL */ ` - mutation customerCreate($input: CustomerCreateInput!) { - customerCreate(input: $input) { - customerUserErrors { - code - field - message - } - customer { - id - } - } - } -` -export default customerCreateMutation diff --git a/framework/swell/utils/mutations/index.ts b/framework/swell/utils/mutations/index.ts deleted file mode 100644 index 3a16d7cec..000000000 --- a/framework/swell/utils/mutations/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export { default as customerCreateMutation } from './customer-create' -export { default as checkoutCreateMutation } from './checkout-create' -export { default as checkoutLineItemAddMutation } from './checkout-line-item-add' -export { default as checkoutLineItemUpdateMutation } from './checkout-line-item-update' -export { default as checkoutLineItemRemoveMutation } from './checkout-line-item-remove' -export { default as customerAccessTokenCreateMutation } from './customer-access-token-create' -export { default as customerAccessTokenDeleteMutation } from './customer-access-token-delete' diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts index 63b63feb0..9f69f702e 100644 --- a/framework/swell/utils/normalize.ts +++ b/framework/swell/utils/normalize.ts @@ -36,7 +36,7 @@ type normalizedProductOption = { const normalizeProductOption = ({ id, name: displayName = '', - values, + values = [], }: ProductOption) => { let returnValues = values.map((value) => { let output: any = { diff --git a/framework/swell/utils/queries/get-all-collections-query.ts b/framework/swell/utils/queries/get-all-collections-query.ts deleted file mode 100644 index 2abf374d6..000000000 --- a/framework/swell/utils/queries/get-all-collections-query.ts +++ /dev/null @@ -1,14 +0,0 @@ -const getSiteCollectionsQuery = /* GraphQL */ ` - query getSiteCollections($first: Int!) { - collections(first: $first) { - edges { - node { - id - title - handle - } - } - } - } -` -export default getSiteCollectionsQuery diff --git a/framework/swell/utils/queries/get-all-pages-query.ts b/framework/swell/utils/queries/get-all-pages-query.ts deleted file mode 100644 index e3aee1f10..000000000 --- a/framework/swell/utils/queries/get-all-pages-query.ts +++ /dev/null @@ -1,14 +0,0 @@ -export const getAllPagesQuery = /* GraphQL */ ` - query getAllPages($first: Int = 250) { - pages(first: $first) { - edges { - node { - id - title - handle - } - } - } - } -` -export default getAllPagesQuery diff --git a/framework/swell/utils/queries/get-all-product-vendors-query.ts b/framework/swell/utils/queries/get-all-product-vendors-query.ts deleted file mode 100644 index be08b8ec6..000000000 --- a/framework/swell/utils/queries/get-all-product-vendors-query.ts +++ /dev/null @@ -1,17 +0,0 @@ -const getAllProductVendors = /* GraphQL */ ` - query getAllProductVendors($first: Int = 250, $cursor: String) { - products(first: $first, after: $cursor) { - pageInfo { - hasNextPage - hasPreviousPage - } - edges { - node { - vendor - } - cursor - } - } - } -` -export default getAllProductVendors diff --git a/framework/swell/utils/queries/get-all-products-paths-query.ts b/framework/swell/utils/queries/get-all-products-paths-query.ts deleted file mode 100644 index 56298c204..000000000 --- a/framework/swell/utils/queries/get-all-products-paths-query.ts +++ /dev/null @@ -1,17 +0,0 @@ -const getAllProductsPathsQuery = /* GraphQL */ ` - query getAllProductPaths($first: Int = 250, $cursor: String) { - products(first: $first, after: $cursor) { - pageInfo { - hasNextPage - hasPreviousPage - } - edges { - node { - handle - } - cursor - } - } - } -` -export default getAllProductsPathsQuery diff --git a/framework/swell/utils/queries/get-all-products-query.ts b/framework/swell/utils/queries/get-all-products-query.ts deleted file mode 100644 index 5eb44c7a7..000000000 --- a/framework/swell/utils/queries/get-all-products-query.ts +++ /dev/null @@ -1,57 +0,0 @@ -export const productConnection = ` -pageInfo { - hasNextPage - hasPreviousPage -} -edges { - node { - id - title - vendor - handle - description - priceRange { - minVariantPrice { - amount - currencyCode - } - } - images(first: 1) { - pageInfo { - hasNextPage - hasPreviousPage - } - edges { - node { - originalSrc - altText - width - height - } - } - } - } -}` - -export const productsFragment = ` -products( - first: $first - sortKey: $sortKey - reverse: $reverse - query: $query -) { - ${productConnection} -} -` - -const getAllProductsQuery = /* GraphQL */ ` - query getAllProducts( - $first: Int = 250 - $query: String = "" - $sortKey: ProductSortKeys = RELEVANCE - $reverse: Boolean = false - ) { - ${productsFragment} - } -` -export default getAllProductsQuery diff --git a/framework/swell/utils/queries/get-checkout-query.ts b/framework/swell/utils/queries/get-checkout-query.ts deleted file mode 100644 index 194e1619a..000000000 --- a/framework/swell/utils/queries/get-checkout-query.ts +++ /dev/null @@ -1,62 +0,0 @@ -export const checkoutDetailsFragment = ` - id - webUrl - subtotalPriceV2{ - amount - currencyCode - } - totalTaxV2 { - amount - currencyCode - } - totalPriceV2 { - amount - currencyCode - } - completedAt - createdAt - taxesIncluded - lineItems(first: 250) { - pageInfo { - hasNextPage - hasPreviousPage - } - edges { - node { - id - title - variant { - id - sku - title - image { - originalSrc - altText - width - height - } - priceV2{ - amount - currencyCode - } - compareAtPriceV2{ - amount - currencyCode - } - } - quantity - } - } - } -` - -const getCheckoutQuery = /* GraphQL */ ` - query($checkoutId: ID!) { - node(id: $checkoutId) { - ... on Checkout { - ${checkoutDetailsFragment} - } - } - } -` -export default getCheckoutQuery diff --git a/framework/swell/utils/queries/get-collection-products-query.ts b/framework/swell/utils/queries/get-collection-products-query.ts deleted file mode 100644 index 04766caa4..000000000 --- a/framework/swell/utils/queries/get-collection-products-query.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { productConnection } from './get-all-products-query' - -const getCollectionProductsQuery = /* GraphQL */ ` - query getProductsFromCollection( - $categoryId: ID! - $first: Int = 250 - $sortKey: ProductCollectionSortKeys = RELEVANCE - $reverse: Boolean = false - ) { - node(id: $categoryId) { - id - ... on Collection { - products( - first: $first - sortKey: $sortKey - reverse: $reverse - ) { - ${productConnection} - } - } - } - } -` -export default getCollectionProductsQuery diff --git a/framework/swell/utils/queries/get-customer-id-query.ts b/framework/swell/utils/queries/get-customer-id-query.ts deleted file mode 100644 index 076ceb10b..000000000 --- a/framework/swell/utils/queries/get-customer-id-query.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const getCustomerQuery = /* GraphQL */ ` - query getCustomerId($customerAccessToken: String!) { - customer(customerAccessToken: $customerAccessToken) { - id - } - } -` -export default getCustomerQuery diff --git a/framework/swell/utils/queries/get-customer-query.ts b/framework/swell/utils/queries/get-customer-query.ts deleted file mode 100644 index 87e37e68d..000000000 --- a/framework/swell/utils/queries/get-customer-query.ts +++ /dev/null @@ -1,16 +0,0 @@ -export const getCustomerQuery = /* GraphQL */ ` - query getCustomer($customerAccessToken: String!) { - customer(customerAccessToken: $customerAccessToken) { - id - firstName - lastName - displayName - email - phone - tags - acceptsMarketing - createdAt - } - } -` -export default getCustomerQuery diff --git a/framework/swell/utils/queries/get-page-query.ts b/framework/swell/utils/queries/get-page-query.ts deleted file mode 100644 index 2ca79abd4..000000000 --- a/framework/swell/utils/queries/get-page-query.ts +++ /dev/null @@ -1,14 +0,0 @@ -export const getPageQuery = /* GraphQL */ ` - query($id: ID!) { - node(id: $id) { - id - ... on Page { - title - handle - body - bodySummary - } - } - } -` -export default getPageQuery diff --git a/framework/swell/utils/queries/get-product-query.ts b/framework/swell/utils/queries/get-product-query.ts deleted file mode 100644 index 5c109901b..000000000 --- a/framework/swell/utils/queries/get-product-query.ts +++ /dev/null @@ -1,69 +0,0 @@ -const getProductQuery = /* GraphQL */ ` - query getProductBySlug($slug: String!) { - productByHandle(handle: $slug) { - id - handle - title - productType - vendor - description - descriptionHtml - options { - id - name - values - } - priceRange { - maxVariantPrice { - amount - currencyCode - } - minVariantPrice { - amount - currencyCode - } - } - variants(first: 250) { - pageInfo { - hasNextPage - hasPreviousPage - } - edges { - node { - id - title - sku - selectedOptions { - name - value - } - priceV2 { - amount - currencyCode - } - compareAtPriceV2 { - amount - currencyCode - } - } - } - } - images(first: 250) { - pageInfo { - hasNextPage - hasPreviousPage - } - edges { - node { - originalSrc - altText - width - height - } - } - } - } - } -` - -export default getProductQuery diff --git a/framework/swell/utils/queries/index.ts b/framework/swell/utils/queries/index.ts deleted file mode 100644 index e19be9c8c..000000000 --- a/framework/swell/utils/queries/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { default as getSiteCollectionsQuery } from './get-all-collections-query' -export { default as getProductQuery } from './get-product-query' -export { default as getAllProductsQuery } from './get-all-products-query' -export { default as getAllProductsPathtsQuery } from './get-all-products-paths-query' -export { default as getAllProductVendors } from './get-all-product-vendors-query' -export { default as getCollectionProductsQuery } from './get-collection-products-query' -export { default as getCheckoutQuery } from './get-checkout-query' -export { default as getAllPagesQuery } from './get-all-pages-query' -export { default as getPageQuery } from './get-page-query' -export { default as getCustomerQuery } from './get-customer-query' diff --git a/framework/swell/wishlist/use-wishlist.tsx b/framework/swell/wishlist/use-wishlist.tsx index d2ce9db5b..cd1bfa0ad 100644 --- a/framework/swell/wishlist/use-wishlist.tsx +++ b/framework/swell/wishlist/use-wishlist.tsx @@ -1,5 +1,5 @@ // TODO: replace this hook and other wishlist hooks with a handler, or remove them if -// Shopify doesn't have a wishlist +// Swell doesn't have a wishlist import { HookFetcher } from '@commerce/utils/types' import { Product } from '../schema' From c6d06e60b6ba4b5745f98735fc302646f1617bb5 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Tue, 27 Apr 2021 17:54:42 -0500 Subject: [PATCH 204/221] ensure products have at least one variant --- framework/swell/cart/use-add-item.tsx | 20 +++++++--- framework/swell/product/get-all-products.ts | 11 +++--- framework/swell/product/get-product.ts | 2 +- framework/swell/product/use-search.tsx | 11 +++--- framework/swell/schema.d.ts | 42 ++++++++++----------- framework/swell/utils/normalize.ts | 8 +++- 6 files changed, 54 insertions(+), 40 deletions(-) diff --git a/framework/swell/cart/use-add-item.tsx b/framework/swell/cart/use-add-item.tsx index 346c6c955..a86d2ab28 100644 --- a/framework/swell/cart/use-add-item.tsx +++ b/framework/swell/cart/use-add-item.tsx @@ -24,13 +24,23 @@ export const handler: MutationHook<Cart, {}, CartItemBody> = { message: 'The item quantity has to be a valid integer greater than 0', }) } + const variables: { + product_id: string + variant_id?: string + checkoutId?: string + quantity?: number + } = { + checkoutId: getCheckoutId(), + product_id: item.productId, + quantity: item.quantity, + } + if (item.productId !== item.variantId) { + variables.variant_id = item.variantId + } + const response = await fetch<Mutation, MutationCheckoutLineItemsAddArgs>({ ...options, - variables: { - checkoutId: getCheckoutId(), - product_id: item.productId, - quantity: item.quantity, - }, + variables, }) return checkoutToCart(response) as any diff --git a/framework/swell/product/get-all-products.ts b/framework/swell/product/get-all-products.ts index dff5bf067..a129c18a2 100644 --- a/framework/swell/product/get-all-products.ts +++ b/framework/swell/product/get-all-products.ts @@ -1,6 +1,7 @@ import { getConfig, SwellConfig } from '../api' import { normalizeProduct } from '../utils/normalize' import { Product } from '@commerce/types' +import { SwellProduct } from '../types' type Variables = { first?: number @@ -23,12 +24,10 @@ const getAllProducts = async (options: { limit: variables.first, }, ]) - const products = results.map((product) => { - if (product.variants) { - product.variants = product.variants.results - } - return normalizeProduct(product) ?? [] - }) + const products = results.map((product: SwellProduct) => + normalizeProduct(product) + ) + return { products, } diff --git a/framework/swell/product/get-product.ts b/framework/swell/product/get-product.ts index 1923fc75a..8cd59f6e0 100644 --- a/framework/swell/product/get-product.ts +++ b/framework/swell/product/get-product.ts @@ -1,6 +1,6 @@ import { GraphQLFetcherResult } from '@commerce/api' import { getConfig, SwellConfig } from '../api' -import { normalizeProduct, getProductQuery } from '../utils' +import { normalizeProduct } from '../utils' type Variables = { slug: string diff --git a/framework/swell/product/use-search.tsx b/framework/swell/product/use-search.tsx index b93a5598f..ce116caa3 100644 --- a/framework/swell/product/use-search.tsx +++ b/framework/swell/product/use-search.tsx @@ -5,6 +5,8 @@ import { normalizeProduct } from '../utils' import { Product } from '@commerce/types' +import { SwellProduct } from '../types' + export default useSearch as UseSearch<typeof handler> export type SearchProductsInput = { @@ -43,12 +45,9 @@ export const handler: SWRHook< variables: { category: categoryId, search, sort: mappedSort }, }) - const products = results.map((product) => { - if (product.variants) { - product.variants = product.variants?.results - } - return normalizeProduct(product) - }) + const products = results.map((product: SwellProduct) => + normalizeProduct(product) + ) return { products, diff --git a/framework/swell/schema.d.ts b/framework/swell/schema.d.ts index c5309310d..e77d3c8d9 100644 --- a/framework/swell/schema.d.ts +++ b/framework/swell/schema.d.ts @@ -962,28 +962,28 @@ export type CheckoutLineItemUpdateInput = { export type CheckoutLineItemsAddPayload = { __typename?: 'CheckoutLineItemsAddPayload' /** The updated checkout object. */ - checkout?: Maybe<Checkout> + items?: Maybe<Checkout> /** List of errors that occurred executing the mutation. */ - checkoutUserErrors: Array<CheckoutUserError> - /** - * List of errors that occurred executing the mutation. - * @deprecated Use `checkoutUserErrors` instead - */ - userErrors: Array<UserError> + // checkoutUserErrors: Array<CheckoutUserError> + // /** + // * List of errors that occurred executing the mutation. + // * @deprecated Use `checkoutUserErrors` instead + // */ + // userErrors: Array<UserError> } /** Return type for `checkoutLineItemsRemove` mutation. */ export type CheckoutLineItemsRemovePayload = { __typename?: 'CheckoutLineItemsRemovePayload' /** The updated checkout object. */ - checkout?: Maybe<Checkout> + items?: Maybe<Checkout> /** List of errors that occurred executing the mutation. */ - checkoutUserErrors: Array<CheckoutUserError> - /** - * List of errors that occurred executing the mutation. - * @deprecated Use `checkoutUserErrors` instead - */ - userErrors: Array<UserError> + // checkoutUserErrors: Array<CheckoutUserError> + // /** + // * List of errors that occurred executing the mutation. + // * @deprecated Use `checkoutUserErrors` instead + // */ + // userErrors: Array<UserError> } /** Return type for `checkoutLineItemsReplace` mutation. */ @@ -999,14 +999,14 @@ export type CheckoutLineItemsReplacePayload = { export type CheckoutLineItemsUpdatePayload = { __typename?: 'CheckoutLineItemsUpdatePayload' /** The updated checkout object. */ - checkout?: Maybe<Checkout> + items?: Maybe<Checkout> /** List of errors that occurred executing the mutation. */ - checkoutUserErrors: Array<CheckoutUserError> - /** - * List of errors that occurred executing the mutation. - * @deprecated Use `checkoutUserErrors` instead - */ - userErrors: Array<UserError> + // checkoutUserErrors: Array<CheckoutUserError> + // /** + // * List of errors that occurred executing the mutation. + // * @deprecated Use `checkoutUserErrors` instead + // */ + // userErrors: Array<UserError> } /** Return type for `checkoutShippingAddressUpdate` mutation. */ diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts index 9f69f702e..57215d485 100644 --- a/framework/swell/utils/normalize.ts +++ b/framework/swell/utils/normalize.ts @@ -110,6 +110,7 @@ const normalizeProductVariants = ( export function normalizeProduct(swellProduct: SwellProduct): Product { const { id, + name, description, images, options, @@ -118,6 +119,8 @@ export function normalizeProduct(swellProduct: SwellProduct): Product { price: value, currency: currencyCode, } = swellProduct + // ProductView accesses variants for each product + const emptyVariants = [{ options: [], id, name }] const productOptions = options ? options.map((o) => normalizeProductOption(o)) : [] @@ -133,7 +136,10 @@ export function normalizeProduct(swellProduct: SwellProduct): Product { vendor: '', path: `/${slug}`, images: productImages, - variants: productVariants, + variants: + productVariants && productVariants.length + ? productVariants + : emptyVariants, options: productOptions, price: { value, From ffbcce2a9e1631de90fffec8a48c290b4fc05d93 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Fri, 30 Apr 2021 18:35:19 -0500 Subject: [PATCH 205/221] handle empty cart, variants, options, errors --- .../api/operations/get-all-collections.ts | 21 ----------- .../swell/api/utils/fetch-all-products.ts | 30 ++++------------ framework/swell/cart/utils/checkout-create.ts | 8 +++++ .../swell/product/get-all-product-paths.ts | 4 +-- framework/swell/product/get-product.ts | 4 +-- framework/swell/utils/get-vendors.ts | 2 +- .../swell/utils/handle-fetch-response.ts | 35 +++++++------------ framework/swell/utils/normalize.ts | 4 +-- 8 files changed, 33 insertions(+), 75 deletions(-) delete mode 100644 framework/swell/api/operations/get-all-collections.ts diff --git a/framework/swell/api/operations/get-all-collections.ts b/framework/swell/api/operations/get-all-collections.ts deleted file mode 100644 index ed906ffcc..000000000 --- a/framework/swell/api/operations/get-all-collections.ts +++ /dev/null @@ -1,21 +0,0 @@ -import Client from 'shopify-buy' -import { SwellConfig } from '../index' - -type Options = { - config: SwellConfig -} - -const getAllCollections = async (options: Options) => { - const { config } = options - - const client = Client.buildClient({ - storefrontAccessToken: config.apiToken, - domain: config.commerceUrl, - }) - - const res = await client.collection.fetchAllWithProducts() - - return JSON.parse(JSON.stringify(res)) -} - -export default getAllCollections diff --git a/framework/swell/api/utils/fetch-all-products.ts b/framework/swell/api/utils/fetch-all-products.ts index 859ec0212..38ba5f6e3 100644 --- a/framework/swell/api/utils/fetch-all-products.ts +++ b/framework/swell/api/utils/fetch-all-products.ts @@ -1,5 +1,5 @@ -import { ProductEdge } from '../../schema' import { SwellConfig } from '..' +import { SwellProduct } from '../../types' const fetchAllProducts = async ({ config, @@ -7,35 +7,17 @@ const fetchAllProducts = async ({ method, variables, acc = [], - cursor, }: { config: SwellConfig query: string - acc?: ProductEdge[] + method: string + acc?: SwellProduct[] variables?: any cursor?: string -}): Promise<ProductEdge[]> => { - // const response = await config.fetch(query, { - // variables: { ...variables, cursor }, - // }) - const response = await config.fetchSwell('products', 'list', [{ limit: 100 }]) +}): Promise<SwellProduct[]> => { + const response = await config.fetchSwell(query, method, variables) - const edges: ProductEdge[] = response.results ?? [] - const hasNextPage = response.results.length < response.count - acc = acc.concat(edges) - - if (hasNextPage) { - const cursor = edges.pop()?.cursor - if (cursor) { - return fetchAllProducts({ - config, - query, - variables, - acc, - cursor, - }) - } - } + acc = acc.concat(response.results) return acc } diff --git a/framework/swell/cart/utils/checkout-create.ts b/framework/swell/cart/utils/checkout-create.ts index cc08007f4..5ee3833c5 100644 --- a/framework/swell/cart/utils/checkout-create.ts +++ b/framework/swell/cart/utils/checkout-create.ts @@ -8,6 +8,14 @@ export const checkoutCreate = async (fetch: any) => { method: 'get', }) + if (!cart) { + const cart = await fetch({ + query: 'cart', + method: 'setItems', + variables: [[]], + }) + } + const checkoutUrl = cart?.checkout_url if (checkoutUrl) { diff --git a/framework/swell/product/get-all-product-paths.ts b/framework/swell/product/get-all-product-paths.ts index fad11f8c2..3b95798e4 100644 --- a/framework/swell/product/get-all-product-paths.ts +++ b/framework/swell/product/get-all-product-paths.ts @@ -1,7 +1,5 @@ -import { Product } from '@commerce/types' import { getConfig, SwellConfig } from '../api' import fetchAllProducts from '../api/utils/fetch-all-products' -import { ProductEdge } from '../schema' type ProductPath = { path: string @@ -20,7 +18,7 @@ const getAllProductPaths = async (options?: { config?: SwellConfig preview?: boolean }): Promise<ReturnType> => { - let { config, variables = { limit: 100 } } = options ?? {} + let { config, variables = [{ limit: 100 }] } = options ?? {} config = getConfig(config) const products = await fetchAllProducts({ diff --git a/framework/swell/product/get-product.ts b/framework/swell/product/get-product.ts index 8cd59f6e0..15fde1cfa 100644 --- a/framework/swell/product/get-product.ts +++ b/framework/swell/product/get-product.ts @@ -19,11 +19,11 @@ const getProduct = async (options: { config = getConfig(config) const product = await config.fetchSwell('products', 'get', [variables.slug]) - if (product.variants) { + if (product && product.variants) { product.variants = product.variants?.results } return { - product: normalizeProduct(product), + product: product ? normalizeProduct(product) : null, } } diff --git a/framework/swell/utils/get-vendors.ts b/framework/swell/utils/get-vendors.ts index a96d9dccd..9f2184fc3 100644 --- a/framework/swell/utils/get-vendors.ts +++ b/framework/swell/utils/get-vendors.ts @@ -13,7 +13,7 @@ export type Brands = BrandEdge[] const getVendors = async (config: SwellConfig) => { const vendors: [string] = - (await config.fetchSwell('attributes', 'get', ['brand'])).values ?? [] + (await config.fetchSwell('attributes', 'get', ['brand']))?.values ?? [] return [...new Set(vendors)].map((v) => ({ node: { diff --git a/framework/swell/utils/handle-fetch-response.ts b/framework/swell/utils/handle-fetch-response.ts index 96ceb2469..2688c9c70 100644 --- a/framework/swell/utils/handle-fetch-response.ts +++ b/framework/swell/utils/handle-fetch-response.ts @@ -1,28 +1,19 @@ -import { FetcherError } from '@commerce/utils/errors' +import { CommerceError } from '@commerce/utils/errors' -export function getError(errors: any[], status: number) { - errors = errors ?? [{ message: 'Failed to fetch Swell API' }] - return new FetcherError({ errors, status }) +type SwellFetchResponse = { + error: { + message: string + code?: string + } } -export async function getAsyncError(res: Response) { - const data = await res.json() - return getError(data.errors, res.status) -} - -const handleFetchResponse = async (res: Response) => { - // if (res.ok) { - // const { data, errors } = await res.json() - - // if (errors && errors.length) { - // throw getError(errors, res.status) - // } - - // return data - // } - if (res) return res - - throw await getAsyncError(res) +const handleFetchResponse = async (res: SwellFetchResponse) => { + if (res) { + if (res.error) { + throw new CommerceError(res.error) + } + return res + } } export default handleFetchResponse diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts index 57215d485..a7a7be1ce 100644 --- a/framework/swell/utils/normalize.ts +++ b/framework/swell/utils/normalize.ts @@ -76,7 +76,7 @@ const normalizeProductVariants = ( productOptions: normalizedProductOption[] ) => { return variants?.map( - ({ id, name, price, option_value_ids: optionValueIds }) => { + ({ id, name, price, option_value_ids: optionValueIds = [] }) => { const values = name .split(',') .map((i) => ({ name: i.trim(), label: i.trim() })) @@ -167,7 +167,7 @@ export function normalizeCart({ createdAt: date_created, currency: { code: currency }, taxesIncluded: tax_included_total > 0, - lineItems: items?.map(normalizeLineItem), + lineItems: items?.map(normalizeLineItem) ?? [], lineItemsSubtotalPrice: +sub_total, subtotalPrice: +sub_total, totalPrice: grand_total, From 7f438aa67d7ef9e6e8acdd90d35904474bef17ff Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Wed, 5 May 2021 12:42:47 -0500 Subject: [PATCH 206/221] remove provider --- .env.template | 2 +- commerce.config.json | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.env.template b/.env.template index 5ebc092d3..9d8a57355 100644 --- a/.env.template +++ b/.env.template @@ -1,5 +1,5 @@ # Available providers: bigcommerce, shopify, swell -COMMERCE_PROVIDER=swell +COMMERCE_PROVIDER= BIGCOMMERCE_STOREFRONT_API_URL= BIGCOMMERCE_STOREFRONT_API_TOKEN= diff --git a/commerce.config.json b/commerce.config.json index ab9497fd3..06b985504 100644 --- a/commerce.config.json +++ b/commerce.config.json @@ -1,5 +1,4 @@ { - "provider": "swell", "features": { "wishlist": false, "customCheckout": false From 7786d6445dbe29f902d08396532cffcac9f2e86e Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Wed, 5 May 2021 15:21:35 -0500 Subject: [PATCH 207/221] cleanup for PR --- .vscode/launch.json | 15 - README.md | 231 ---------------- commerce.config.json | 2 +- framework/commerce/with-config.js | 40 --- .../api/operations/get-all-collections.ts | 21 -- framework/shopify/api/operations/get-page.ts | 25 -- framework/shopify/cart/use-cart.tsx | 1 + .../shopify/cart/utils/checkout-create.ts | 29 -- .../shopify/cart/utils/checkout-to-cart.ts | 42 --- framework/shopify/cart/utils/fetcher.ts | 31 --- framework/shopify/cart/utils/index.ts | 2 - framework/shopify/utils/storage.ts | 13 - framework/swell/README.md | 260 ------------------ tsconfig.json | 4 +- 14 files changed, 4 insertions(+), 712 deletions(-) delete mode 100644 .vscode/launch.json delete mode 100644 README.md delete mode 100644 framework/commerce/with-config.js delete mode 100644 framework/shopify/api/operations/get-all-collections.ts delete mode 100644 framework/shopify/api/operations/get-page.ts delete mode 100644 framework/shopify/cart/utils/checkout-create.ts delete mode 100644 framework/shopify/cart/utils/checkout-to-cart.ts delete mode 100644 framework/shopify/cart/utils/fetcher.ts delete mode 100644 framework/shopify/cart/utils/index.ts delete mode 100644 framework/shopify/utils/storage.ts delete mode 100644 framework/swell/README.md diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 177bc7dfa..000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "pwa-chrome", - "request": "launch", - "name": "Launch Chrome against localhost", - "url": "http://localhost:8080", - "webRoot": "${workspaceFolder}" - } - ] -} diff --git a/README.md b/README.md deleted file mode 100644 index 2eabbe6c1..000000000 --- a/README.md +++ /dev/null @@ -1,231 +0,0 @@ -[](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fcommerce&project-name=commerce&repo-name=commerce&demo-title=Next.js%20Commerce&demo-description=An%20all-in-one%20starter%20kit%20for%20high-performance%20e-commerce%20sites.&demo-url=https%3A%2F%2Fdemo.vercel.store&demo-image=https%3A%2F%2Fbigcommerce-demo-asset-ksvtgfvnd.vercel.app%2Fbigcommerce.png&integration-ids=oac_MuWZiE4jtmQ2ejZQaQ7ncuDT) - -# Next.js Commerce - -The all-in-one starter kit for high-performance e-commerce sites. With a few clicks, Next.js developers can clone, deploy and fully customize their own store. -Start right now at [nextjs.org/commerce](https://nextjs.org/commerce) - -Demo live at: [demo.vercel.store](https://demo.vercel.store/) - -- Shopify Demo: https://shopify.demo.vercel.store/ -- BigCommerce Demo: https://bigcommerce.demo.vercel.store/ - -## Features - -- Performant by default -- SEO Ready -- Internationalization -- Responsive -- UI Components -- Theming -- Standardized Data Hooks -- Integrations - Integrate seamlessly with the most common ecommerce platforms. -- Dark Mode Support - -## Integrations - -Next.js Commerce integrates out-of-the-box with BigCommerce and Shopify. We plan to support all major ecommerce backends. - -## Considerations - -- `framework/commerce` contains all types, helpers and functions to be used as base to build a new **provider**. -- **Providers** live under `framework`'s root folder and they will extend Next.js Commerce types and functionality (`framework/commerce`). -- We have a **Features API** to ensure feature parity between the UI and the Provider. The UI should update accordingly and no extra code should be bundled. All extra configuration for features will live under `features` in `commerce.config.json` and if needed it can also be accessed programatically. -- Each **provider** should add its corresponding `next.config.js` and `commerce.config.json` adding specific data related to the provider. For example in case of BigCommerce, the images CDN and additional API routes. -- **Providers don't depend on anything that's specific to the application they're used in**. They only depend on `framework/commerce`, on their own framework folder and on some dependencies included in `package.json` - -## Configuration - -### How to change providers - -Open `.env.local` and change the value of `COMMERCE_PROVIDER` to the provider you would like to use, then set the environment variables for that provider (use `.env.template` as the base). - -### Features - -Every provider defines the features that it supports under `framework/{provider}/commerce.config.json` - -#### How to turn Features on and off - -> NOTE: The selected provider should support the feature that you are toggling. (This means that you can't turn wishlist on if the provider doesn't support this functionality out the box) - -- Open `commerce.config.json` -- You'll see a config file like this: - ```json - { - "features": { - "wishlist": false - } - } - ``` -- Turn wishlist on by setting wishlist to true. -- Run the app and the wishlist functionality should be back on. - -### How to create a new provider - -Follow our docs for [Adding a new Commerce Provider](framework/commerce/new-provider.md). - -If you succeeded building a provider, submit a PR with a valid demo and we'll review it asap. - -## Contribute - -Our commitment to Open Source can be found [here](https://vercel.com/oss). - -1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device. -2. Create a new branch `git checkout -b MY_BRANCH_NAME` -3. Install yarn: `npm install -g yarn` -4. Install the dependencies: `yarn` -5. Duplicate `.env.template` and rename it to `.env.local` -6. Add proper store values to `.env.local` -7. Run `yarn dev` to build and watch for code changes - -## Work in progress - -We're using Github Projects to keep track of issues in progress and todo's. Here is our [Board](https://github.com/vercel/commerce/projects/1) - -People actively working on this project: @okbel & @lfades. - -## Framework - -Framework is where the data comes from. It contains mostly hook handlers and functions. - -## Structure - -Main folder and its exposed functions - -- `product` - - usePrice - - useSearch - - getProduct - - getAllProducts -- `wishlist` - - useWishlist - - useAddItem - - useRemoveItem -- `auth` - - useLogin - - useLogout - - useSignup -- `customer` - - useCustomer - - getCustomerId - - getCustomerWistlist -- `cart` - - - useCart - - useAddItem - - useRemoveItem - - useUpdateItem - -- `config.json` -- README.md - -#### Example of correct usage of the Commerce Framework - -```js -import { useUI } from '@components/ui' -import { useCustomer } from '@framework/customer' -import { useWishlist, useAddItem, useRemoveItem } from '@framework/wishlist' -``` - -## Configuration - -### How to change providers - -First, update the provider selected in `commerce.config.json`: - -```json -{ - "provider": "bigcommerce", - "features": { - "wishlist": true - } -} -``` - -Then, change the paths defined in `tsconfig.json` and update the `@framework` paths to point to the right folder provider: - -```json -"@framework": ["framework/bigcommerce"], -"@framework/*": ["framework/bigcommerce/*"] -``` - -Make sure to add the environment variables required by the new provider. - -### Features - -Every provider defines the features that it supports under `framework/{provider}/commerce.config.json` - -#### How to turn Features on and off - -> NOTE: The selected provider should support the feature that you are toggling. (This means that you can't turn wishlist on if the provider doesn't support this functionality out the box) - -- Open `commerce.config.json` -- You'll see a config file like this: - ```json - { - "provider": "bigcommerce", - "features": { - "wishlist": false - } - } - ``` -- Turn wishlist on by setting wishlist to true. -- Run the app and the wishlist functionality should be back on. - -### How to create a new provider - -We'd recommend to duplicate a provider folder and push your providers SDK. - -If you succeeded building a provider, submit a PR so we can all enjoy it. - -## Contribute - -Our commitment to Open Source can be found [here](https://vercel.com/oss). - -1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device. -2. Create a new branch `git checkout -b MY_BRANCH_NAME` -3. Install yarn: `npm install -g yarn` -4. Install the dependencies: `yarn` -5. Duplicate `.env.template` and rename it to `.env.local`. -6. Add proper store values to `.env.local`. -7. Run `yarn dev` to build and watch for code changes -8. The development branch is `canary` (this is the branch pull requests should be made against). - On a release, `canary` branch is rebased into `master`. - -## Troubleshoot - -<details> -<summary>I already own a BigCommerce store. What should I do?</summary> -<br> -First thing you do is: <b>set your environment variables</b> -<br> -<br> -.env.local - -```sh -BIGCOMMERCE_STOREFRONT_API_URL=<> -BIGCOMMERCE_STOREFRONT_API_TOKEN=<> -BIGCOMMERCE_STORE_API_URL=<> -BIGCOMMERCE_STORE_API_TOKEN=<> -BIGCOMMERCE_STORE_API_CLIENT_ID=<> -BIGCOMMERCE_CHANNEL_ID=<> -``` - -If your project was started with a "Deploy with Vercel" button, you can use Vercel's CLI to retrieve these credentials. - -1. Install Vercel CLI: `npm i -g vercel` -2. Link local instance with Vercel and Github accounts (creates .vercel file): `vercel link` -3. Download your environment variables: `vercel env pull .env.local` - -Next, you're free to customize the starter. More updates coming soon. Stay tuned. - -</details> - -<details> -<summary>BigCommerce shows a Coming Soon page and requests a Preview Code</summary> -<br> -After Email confirmation, Checkout should be manually enabled through BigCommerce platform. Look for "Review & test your store" section through BigCommerce's dashboard. -<br> -<br> -BigCommerce team has been notified and they plan to add more detailed about this subject. -</details> diff --git a/commerce.config.json b/commerce.config.json index 06b985504..08ea78814 100644 --- a/commerce.config.json +++ b/commerce.config.json @@ -1,6 +1,6 @@ { "features": { - "wishlist": false, + "wishlist": true, "customCheckout": false } } diff --git a/framework/commerce/with-config.js b/framework/commerce/with-config.js deleted file mode 100644 index cc244dac0..000000000 --- a/framework/commerce/with-config.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * This file is expected to be used in next.config.js only - */ - -const merge = require('deepmerge') - -const PROVIDERS = ['bigcommerce', 'shopify', 'swell'] - -function getProviderName() { - return process.env.BIGCOMMERCE_STOREFRONT_API_URL ? 'bigcommerce' : null -} - -module.exports = (nextConfig = {}) => { - const commerce = nextConfig.commerce || {} - const name = commerce.provider || getProviderName() - - if (!name) { - throw new Error( - `The commerce provider is missing, please add a valid provider name or its environment variables` - ) - } - if (!PROVIDERS.includes(name)) { - throw new Error( - `The commerce provider "${name}" can't be found, please use one of "${PROVIDERS.join( - ', ' - )}"` - ) - } - - const commerceNextConfig = require(`../${name}/next.config`) - const config = merge(commerceNextConfig, nextConfig) - - config.env = config.env || {} - - Object.entries(config.commerce.features).forEach(([k, v]) => { - if (v) config.env[`COMMERCE_${k.toUpperCase()}_ENABLED`] = true - }) - - return config -} diff --git a/framework/shopify/api/operations/get-all-collections.ts b/framework/shopify/api/operations/get-all-collections.ts deleted file mode 100644 index 9cf216a91..000000000 --- a/framework/shopify/api/operations/get-all-collections.ts +++ /dev/null @@ -1,21 +0,0 @@ -import Client from 'shopify-buy' -import { ShopifyConfig } from '../index' - -type Options = { - config: ShopifyConfig -} - -const getAllCollections = async (options: Options) => { - const { config } = options - - const client = Client.buildClient({ - storefrontAccessToken: config.apiToken, - domain: config.commerceUrl, - }) - - const res = await client.collection.fetchAllWithProducts() - - return JSON.parse(JSON.stringify(res)) -} - -export default getAllCollections diff --git a/framework/shopify/api/operations/get-page.ts b/framework/shopify/api/operations/get-page.ts deleted file mode 100644 index 32acb7c8f..000000000 --- a/framework/shopify/api/operations/get-page.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Page } from '../../schema' -import { ShopifyConfig, getConfig } from '..' - -export type GetPageResult<T extends { page?: any } = { page?: Page }> = T - -export type PageVariables = { - id: string -} - -async function getPage({ - url, - variables, - config, - preview, -}: { - url?: string - variables: PageVariables - config?: ShopifyConfig - preview?: boolean -}): Promise<GetPageResult> { - config = getConfig(config) - return {} -} - -export default getPage diff --git a/framework/shopify/cart/use-cart.tsx b/framework/shopify/cart/use-cart.tsx index c2ea7ea49..5f77360bb 100644 --- a/framework/shopify/cart/use-cart.tsx +++ b/framework/shopify/cart/use-cart.tsx @@ -22,6 +22,7 @@ export const handler: SWRHook< }, async fetcher({ input: { cartId: checkoutId }, options, fetch }) { let checkout + if (checkoutId) { const data = await fetch({ ...options, diff --git a/framework/shopify/cart/utils/checkout-create.ts b/framework/shopify/cart/utils/checkout-create.ts deleted file mode 100644 index e950cc7e4..000000000 --- a/framework/shopify/cart/utils/checkout-create.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { - SHOPIFY_CHECKOUT_ID_COOKIE, - SHOPIFY_CHECKOUT_URL_COOKIE, - SHOPIFY_COOKIE_EXPIRE, -} from '../../const' - -import checkoutCreateMutation from '../../utils/mutations/checkout-create' -import Cookies from 'js-cookie' - -export const checkoutCreate = async (fetch: any) => { - const data = await fetch({ - query: checkoutCreateMutation, - }) - - const checkout = data.checkoutCreate?.checkout - const checkoutId = checkout?.id - - if (checkoutId) { - const options = { - expires: SHOPIFY_COOKIE_EXPIRE, - } - Cookies.set(SHOPIFY_CHECKOUT_ID_COOKIE, checkoutId, options) - Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout?.webUrl, options) - } - - return checkout -} - -export default checkoutCreate diff --git a/framework/shopify/cart/utils/checkout-to-cart.ts b/framework/shopify/cart/utils/checkout-to-cart.ts deleted file mode 100644 index 03005f342..000000000 --- a/framework/shopify/cart/utils/checkout-to-cart.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Cart } from '../../types' -import { CommerceError, ValidationError } from '@commerce/utils/errors' - -import { - CheckoutLineItemsAddPayload, - CheckoutLineItemsRemovePayload, - CheckoutLineItemsUpdatePayload, - Maybe, -} from '../../schema' -import { normalizeCart } from '../../utils' - -export type CheckoutPayload = - | CheckoutLineItemsAddPayload - | CheckoutLineItemsUpdatePayload - | CheckoutLineItemsRemovePayload - -const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => { - if (!checkoutPayload) { - throw new CommerceError({ - message: 'Invalid response from Shopify', - }) - } - - const checkout = checkoutPayload?.checkout - const userErrors = checkoutPayload?.userErrors - - if (userErrors && userErrors.length) { - throw new ValidationError({ - message: userErrors[0].message, - }) - } - - if (!checkout) { - throw new CommerceError({ - message: 'Invalid response from Shopify', - }) - } - - return normalizeCart(checkout) -} - -export default checkoutToCart diff --git a/framework/shopify/cart/utils/fetcher.ts b/framework/shopify/cart/utils/fetcher.ts deleted file mode 100644 index 6afb55f18..000000000 --- a/framework/shopify/cart/utils/fetcher.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { HookFetcherFn } from '@commerce/utils/types' -import { Cart } from '@commerce/types' -import { checkoutCreate, checkoutToCart } from '.' -import { FetchCartInput } from '@commerce/cart/use-cart' - -const fetcher: HookFetcherFn<Cart | null, FetchCartInput> = async ({ - options, - input: { cartId: checkoutId }, - fetch, -}) => { - let checkout - - if (checkoutId) { - const data = await fetch({ - ...options, - variables: { - checkoutId, - }, - }) - checkout = data.node - } - - if (checkout?.completedAt || !checkoutId) { - checkout = await checkoutCreate(fetch) - } - - // TODO: Fix this type - return checkoutToCart({ checkout } as any) -} - -export default fetcher diff --git a/framework/shopify/cart/utils/index.ts b/framework/shopify/cart/utils/index.ts deleted file mode 100644 index 20d04955d..000000000 --- a/framework/shopify/cart/utils/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as checkoutToCart } from './checkout-to-cart' -export { default as checkoutCreate } from './checkout-create' diff --git a/framework/shopify/utils/storage.ts b/framework/shopify/utils/storage.ts deleted file mode 100644 index d46dadb21..000000000 --- a/framework/shopify/utils/storage.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const getCheckoutIdFromStorage = (token: string) => { - if (window && window.sessionStorage) { - return window.sessionStorage.getItem(token) - } - - return null -} - -export const setCheckoutIdInStorage = (token: string, id: string | number) => { - if (window && window.sessionStorage) { - return window.sessionStorage.setItem(token, id + '') - } -} diff --git a/framework/swell/README.md b/framework/swell/README.md deleted file mode 100644 index f47a7a028..000000000 --- a/framework/swell/README.md +++ /dev/null @@ -1,260 +0,0 @@ -## Table of Contents - -- [Getting Started](#getting-started) - - [Modifications](#modifications) - - [Adding item to Cart](#adding-item-to-cart) - - [Proceed to Checkout](#proceed-to-checkout) -- [General Usage](#general-usage) - - [CommerceProvider](#commerceprovider) - - [useCommerce](#usecommerce) -- [Hooks](#hooks) - - [usePrice](#useprice) - - [useAddItem](#useadditem) - - [useRemoveItem](#useremoveitem) - - [useUpdateItem](#useupdateitem) -- [APIs](#apis) - - [getProduct](#getproduct) - - [getAllProducts](#getallproducts) - - [getAllCollections](#getallcollections) - - [getAllPages](#getallpages) - -# Shopify Storefront Data Hooks - -Collection of hooks and data fetching functions to integrate Shopify in a React application. Designed to work with [Next.js Commerce](https://demo.vercel.store/). - -## Getting Started - -1. Install dependencies: - -``` -yarn install shopify-buy -yarn install -D @types/shopify-buy -``` - -3. Environment variables need to be set: - -``` -SHOPIFY_STORE_DOMAIN= -SHOPIFY_STOREFRONT_ACCESS_TOKEN= -NEXT_PUBLIC_SWELL_STORE_DOMAIN= -NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN= -``` - -4. Point the framework to `shopify` by updating `tsconfig.json`: - -``` -"@framework/*": ["framework/shopify/*"], -"@framework": ["framework/shopify"] -``` - -### Modifications - -These modifications are temporarily until contributions are made to remove them. - -#### Adding item to Cart - -```js -// components/product/ProductView/ProductView.tsx -const ProductView: FC<Props> = ({ product }) => { - const addToCart = async () => { - setLoading(true) - try { - await addItem({ - productId: product.id, - variantId: variant ? variant.id : product.variants[0].id, - }) - openSidebar() - setLoading(false) - } catch (err) { - setLoading(false) - } - } -} -``` - -#### Proceed to Checkout - -```js -// components/cart/CartSidebarView/CartSidebarView.tsx -import { useCommerce } from '@framework' - -const CartSidebarView: FC = () => { - const { checkout } = useCommerce() - return ( - <Button href={checkout.webUrl} Component="a" width="100%"> - Proceed to Checkout - </Button> - ) -} -``` - -## General Usage - -### CommerceProvider - -Provider component that creates the commerce context for children. - -```js -import { CommerceProvider } from '@framework' - -const App = ({ children }) => { - return <CommerceProvider locale={locale}>{children}</CommerceProvider> -} - -export default App -``` - -### useCommerce - -Returns the configs that are defined in the nearest `CommerceProvider`. Also provides access to Shopify's `checkout` and `shop`. - -```js -import { useCommerce } from 'nextjs-commerce-shopify' - -const { checkout, shop } = useCommerce() -``` - -- `checkout`: The information required to checkout items and pay ([Documentation](https://shopify.dev/docs/storefront-api/reference/checkouts/checkout)). -- `shop`: Represents a collection of the general settings and information about the shop ([Documentation](https://shopify.dev/docs/storefront-api/reference/online-store/shop/index)). - -## Hooks - -### usePrice - -Display the product variant price according to currency and locale. - -```js -import usePrice from '@framework/product/use-price' - -const { price } = usePrice({ - amount, -}) -``` - -Takes in either `amount` or `variant`: - -- `amount`: A price value for a particular item if the amount is known. -- `variant`: A shopify product variant. Price will be extracted from the variant. - -### useAddItem - -```js -import { useAddItem } from '@framework/cart' - -const AddToCartButton = ({ variantId, quantity }) => { - const addItem = useAddItem() - - const addToCart = async () => { - await addItem({ - variantId, - }) - } - - return <button onClick={addToCart}>Add To Cart</button> -} -``` - -### useRemoveItem - -```js -import { useRemoveItem } from '@framework/cart' - -const RemoveButton = ({ item }) => { - const removeItem = useRemoveItem() - - const handleRemove = async () => { - await removeItem({ id: item.id }) - } - - return <button onClick={handleRemove}>Remove</button> -} -``` - -### useUpdateItem - -```js -import { useUpdateItem } from '@framework/cart' - -const CartItem = ({ item }) => { - const [quantity, setQuantity] = useState(item.quantity) - const updateItem = useUpdateItem(item) - - const updateQuantity = async (e) => { - const val = e.target.value - await updateItem({ quantity: val }) - } - - return ( - <input - type="number" - max={99} - min={0} - value={quantity} - onChange={updateQuantity} - /> - ) -} -``` - -## APIs - -Collections of APIs to fetch data from a Shopify store. - -The data is fetched using the [Shopify JavaScript Buy SDK](https://github.com/Shopify/js-buy-sdk#readme). Read the [Shopify Storefront API reference](https://shopify.dev/docs/storefront-api/reference) for more information. - -### getProduct - -Get a single product by its `handle`. - -```js -import getProduct from '@framework/product/get-product' -import { getConfig } from '@framework/api' - -const config = getConfig() - -const product = await getProduct({ - variables: { slug }, - config, -}) -``` - -### getAllProducts - -```js -import getAllProducts from '@framework/product/get-all-products' -import { getConfig } from '@framework/api' - -const config = getConfig() - -const { products } = await getAllProducts({ - variables: { first: 12 }, - config, -}) -``` - -### getAllCollections - -```js -import getAllCollections from '@framework/product/get-all-collections' -import { getConfig } from '@framework/api' - -const config = getConfig() - -const collections = await getAllCollections({ - config, -}) -``` - -### getAllPages - -```js -import getAllPages from '@framework/common/get-all-pages' -import { getConfig } from '@framework/api' - -const config = getConfig() - -const pages = await getAllPages({ - variables: { first: 12 }, - config, -}) -``` diff --git a/tsconfig.json b/tsconfig.json index ffb74e388..e20f37099 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,8 +22,8 @@ "@components/*": ["components/*"], "@commerce": ["framework/commerce"], "@commerce/*": ["framework/commerce/*"], - "@framework": ["framework/swell"], - "@framework/*": ["framework/swell/*"] + "@framework": ["framework/shopify"], + "@framework/*": ["framework/shopify/*"] } }, "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], From 9675be154643b7302bbbad4196436c66b4613eaf Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Tue, 11 May 2021 14:28:46 -0500 Subject: [PATCH 208/221] update swell consts, cleanup cart types --- .env.template | 4 ++-- framework/swell/api/utils/fetch-swell-api.ts | 2 +- framework/swell/cart/use-add-item.tsx | 3 +-- framework/swell/cart/use-remove-item.tsx | 16 ++++++---------- framework/swell/cart/use-update-item.tsx | 11 ++++------- framework/swell/common/get-all-pages.ts | 2 +- 6 files changed, 15 insertions(+), 23 deletions(-) diff --git a/.env.template b/.env.template index 9d8a57355..b68daaffe 100644 --- a/.env.template +++ b/.env.template @@ -11,5 +11,5 @@ BIGCOMMERCE_CHANNEL_ID= NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN= NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN= -SWELL_STORE_ID= -SWELL_PUBLIC_KEY= \ No newline at end of file +NEXT_PUBLIC_SWELL_STORE_ID= +NEXT_PUBLIC_SWELL_PUBLIC_KEY= diff --git a/framework/swell/api/utils/fetch-swell-api.ts b/framework/swell/api/utils/fetch-swell-api.ts index e070483ae..d70b71954 100644 --- a/framework/swell/api/utils/fetch-swell-api.ts +++ b/framework/swell/api/utils/fetch-swell-api.ts @@ -1,4 +1,4 @@ -import { swellConfig } from '../../index' +import { swellConfig } from '../..' const fetchSwellApi = async ( query: string, diff --git a/framework/swell/cart/use-add-item.tsx b/framework/swell/cart/use-add-item.tsx index a86d2ab28..990514d42 100644 --- a/framework/swell/cart/use-add-item.tsx +++ b/framework/swell/cart/use-add-item.tsx @@ -5,7 +5,6 @@ import useCart from './use-cart' import { Cart, CartItemBody } from '../types' import { checkoutToCart } from './utils' import { getCheckoutId } from '../utils' -import { Mutation, MutationCheckoutLineItemsAddArgs } from '../schema' import { useCallback } from 'react' export default useAddItem as UseAddItem<typeof handler> @@ -38,7 +37,7 @@ export const handler: MutationHook<Cart, {}, CartItemBody> = { variables.variant_id = item.variantId } - const response = await fetch<Mutation, MutationCheckoutLineItemsAddArgs>({ + const response = await fetch({ ...options, variables, }) diff --git a/framework/swell/cart/use-remove-item.tsx b/framework/swell/cart/use-remove-item.tsx index 13d137da4..8dc1ea713 100644 --- a/framework/swell/cart/use-remove-item.tsx +++ b/framework/swell/cart/use-remove-item.tsx @@ -13,10 +13,8 @@ import useRemoveItem, { } from '@commerce/cart/use-remove-item' import useCart from './use-cart' -import { checkoutLineItemRemoveMutation, getCheckoutId } from '../utils' import { checkoutToCart } from './utils' import { Cart, LineItem } from '../types' -import { Mutation, MutationCheckoutLineItemsRemoveArgs } from '../schema' import { RemoveCartItemBody } from '@commerce/types' export type RemoveItemFn<T = any> = T extends LineItem @@ -39,12 +37,10 @@ export const handler = { options, fetch, }: HookFetcherContext<RemoveCartItemBody>) { - const response = await fetch<Mutation, MutationCheckoutLineItemsRemoveArgs>( - { - ...options, - variables: [itemId], - } - ) + const response = await fetch({ + ...options, + variables: [itemId], + }) return checkoutToCart(response) }, useHook: ({ @@ -52,9 +48,9 @@ export const handler = { }: MutationHookContext<Cart | null, RemoveCartItemBody>) => < T extends LineItem | undefined = undefined >( - item + ctx: { item?: T } = {} ) => { - // const { item } = ctx + const { item } = ctx const { mutate } = useCart() const removeItem: RemoveItemFn<LineItem> = async (input) => { const itemId = input?.id ?? item?.id diff --git a/framework/swell/cart/use-update-item.tsx b/framework/swell/cart/use-update-item.tsx index 38ad65bf1..b74b65dc9 100644 --- a/framework/swell/cart/use-update-item.tsx +++ b/framework/swell/cart/use-update-item.tsx @@ -14,7 +14,6 @@ import useCart from './use-cart' import { handler as removeItemHandler } from './use-remove-item' import type { Cart, LineItem, UpdateCartItemBody } from '../types' import { checkoutToCart } from './utils' -import { Mutation, MutationCheckoutLineItemsUpdateArgs } from '../schema' export type UpdateItemInput<T = any> = T extends LineItem ? Partial<UpdateItemInputBase<LineItem>> @@ -46,12 +45,10 @@ export const handler = { message: 'The item quantity has to be a valid integer', }) } - const response = await fetch<Mutation, MutationCheckoutLineItemsUpdateArgs>( - { - ...options, - variables: [item.itemId, { quantity: item.quantity }], - } - ) + const response = await fetch({ + ...options, + variables: [item.itemId, { quantity: item.quantity }], + }) return checkoutToCart(response) }, diff --git a/framework/swell/common/get-all-pages.ts b/framework/swell/common/get-all-pages.ts index 489af339c..99e07f6e6 100644 --- a/framework/swell/common/get-all-pages.ts +++ b/framework/swell/common/get-all-pages.ts @@ -25,7 +25,7 @@ const getAllPages = async (options?: { config = getConfig(config) const { locale, fetchSwell } = config const { results } = await fetchSwell('content', 'list', ['pages']) - const pages = results.map(({ slug, ...rest }) => ({ + const pages = results.map(({ slug, ...rest }: { slug: string }) => ({ url: `/${locale}/${slug}`, ...rest, })) From e7d0f56e85428edc5f1d26075d5adf6ff1bddd24 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Wed, 12 May 2021 11:23:04 -0500 Subject: [PATCH 209/221] fix checkout url, product w/ no images error --- framework/swell/utils/normalize.ts | 2 +- next.config.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts index a7a7be1ce..cc8eafdb7 100644 --- a/framework/swell/utils/normalize.ts +++ b/framework/swell/utils/normalize.ts @@ -60,7 +60,7 @@ const normalizeProductOption = ({ } const normalizeProductImages = (images: SwellImage[]) => { - if (!images) { + if (!images || images.length < 1) { return [{ url: '/' }] } return images?.map(({ file, ...rest }: SwellImage) => ({ diff --git a/next.config.js b/next.config.js index 1e1b1a126..e17d3e5c4 100644 --- a/next.config.js +++ b/next.config.js @@ -7,7 +7,7 @@ const { const provider = commerce.provider || getProviderName() const isBC = provider === 'bigcommerce' const isShopify = provider === 'shopify' -const isSwell = commerce.provider === 'swell' +const isSwell = provider === 'swell' module.exports = withCommerceConfig({ commerce, From bff94e73aedc21c2e1a10562c61b8b256c4c7630 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 13 May 2021 16:10:09 +0300 Subject: [PATCH 210/221] Fix build errors --- framework/swell/api/index.ts | 8 +- .../swell/api/utils/fetch-all-products.ts | 25 -- framework/swell/api/utils/fetch-swell-api.ts | 10 +- framework/swell/auth/use-signup.tsx | 4 - framework/swell/cart/use-cart.tsx | 24 +- framework/swell/cart/use-update-item.tsx | 3 +- .../swell/cart/utils/checkout-to-cart.ts | 5 +- framework/swell/common/get-all-pages.ts | 13 +- framework/swell/common/get-page.ts | 2 +- framework/swell/fetcher.ts | 11 +- .../swell/product/get-all-collections.ts | 2 +- .../swell/product/get-all-product-paths.ts | 15 +- framework/swell/product/get-all-products.ts | 2 +- framework/swell/product/get-product.ts | 4 +- framework/swell/types.ts | 2 +- framework/swell/utils/get-categories.ts | 8 +- framework/swell/utils/get-vendors.ts | 2 +- framework/swell/utils/normalize.ts | 7 +- pages/[...pages].tsx | 3 +- pages/search.tsx | 3 +- tsconfig.json | 4 +- yarn.lock | 388 +----------------- 22 files changed, 87 insertions(+), 458 deletions(-) delete mode 100644 framework/swell/api/utils/fetch-all-products.ts diff --git a/framework/swell/api/index.ts b/framework/swell/api/index.ts index 1e328062b..1494af9c7 100644 --- a/framework/swell/api/index.ts +++ b/framework/swell/api/index.ts @@ -6,11 +6,10 @@ import { SWELL_COOKIE_EXPIRE, } from '../const' -import fetcher from '../fetcher' -import fetchSwellApi from './utils/fetch-swell-api' +import fetchApi from './utils/fetch-swell-api' export interface SwellConfig extends CommerceAPIConfig { - fetchSwell: any + fetch: any } export class Config { @@ -38,8 +37,7 @@ const config = new Config({ apiToken: ''!, cartCookie: SWELL_CHECKOUT_ID_COOKIE, cartCookieMaxAge: SWELL_COOKIE_EXPIRE, - fetchSwell: fetchSwellApi, - fetch: fetcher, + fetch: fetchApi, customerCookie: SWELL_CUSTOMER_TOKEN_COOKIE, }) diff --git a/framework/swell/api/utils/fetch-all-products.ts b/framework/swell/api/utils/fetch-all-products.ts deleted file mode 100644 index 38ba5f6e3..000000000 --- a/framework/swell/api/utils/fetch-all-products.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { SwellConfig } from '..' -import { SwellProduct } from '../../types' - -const fetchAllProducts = async ({ - config, - query, - method, - variables, - acc = [], -}: { - config: SwellConfig - query: string - method: string - acc?: SwellProduct[] - variables?: any - cursor?: string -}): Promise<SwellProduct[]> => { - const response = await config.fetchSwell(query, method, variables) - - acc = acc.concat(response.results) - - return acc -} - -export default fetchAllProducts diff --git a/framework/swell/api/utils/fetch-swell-api.ts b/framework/swell/api/utils/fetch-swell-api.ts index d70b71954..65caed763 100644 --- a/framework/swell/api/utils/fetch-swell-api.ts +++ b/framework/swell/api/utils/fetch-swell-api.ts @@ -1,11 +1,7 @@ import { swellConfig } from '../..' -const fetchSwellApi = async ( - query: string, - method: string, - variables: [] = [] -) => { +const fetchApi = async (query: string, method: string, variables: [] = []) => { const { swell } = swellConfig - return await swell[query][method](...variables) + return swell[query][method](...variables) } -export default fetchSwellApi +export default fetchApi diff --git a/framework/swell/auth/use-signup.tsx b/framework/swell/auth/use-signup.tsx index a3dbf73a3..f01d45044 100644 --- a/framework/swell/auth/use-signup.tsx +++ b/framework/swell/auth/use-signup.tsx @@ -5,10 +5,6 @@ import useSignup, { UseSignup } from '@commerce/auth/use-signup' import useCustomer from '../customer/use-customer' import { CustomerCreateInput } from '../schema' -import { - customerCreateMutation, - customerAccessTokenCreateMutation, -} from '../utils/mutations' import handleLogin from '../utils/handle-login' export default useSignup as UseSignup<typeof handler> diff --git a/framework/swell/cart/use-cart.tsx b/framework/swell/cart/use-cart.tsx index 354b4f947..531286ee6 100644 --- a/framework/swell/cart/use-cart.tsx +++ b/framework/swell/cart/use-cart.tsx @@ -1,27 +1,37 @@ import useCart, { UseCart } from '@commerce/cart/use-cart' import { Cart } from '@commerce/types' import { SWRHook } from '@commerce/utils/types' +import { useMemo } from 'react' import { normalizeCart } from '../utils/normalize' import { checkoutCreate, checkoutToCart } from './utils' export default useCart as UseCart<typeof handler> -export const handler: SWRHook<Cart | null> = { +export const handler: SWRHook<Cart | null, {}, any, { isEmpty?: boolean }> = { fetchOptions: { query: 'cart', method: 'get', }, - async fetcher({ options, fetch }) { + async fetcher({ fetch }) { const cart = await checkoutCreate(fetch) return cart ? normalizeCart(cart) : null }, useHook: ({ useData }) => (input) => { - return useData({ - swrOptions: { - revalidateOnFocus: false, - ...input?.swrOptions, - }, + const response = useData({ + swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, }) + return useMemo( + () => + Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, + }, + }), + [response] + ) }, } diff --git a/framework/swell/cart/use-update-item.tsx b/framework/swell/cart/use-update-item.tsx index b74b65dc9..e4261fc85 100644 --- a/framework/swell/cart/use-update-item.tsx +++ b/framework/swell/cart/use-update-item.tsx @@ -47,7 +47,7 @@ export const handler = { } const response = await fetch({ ...options, - variables: [item.itemId, { quantity: item.quantity }], + variables: [itemId, { quantity: item.quantity }], }) return checkoutToCart(response) @@ -79,7 +79,6 @@ export const handler = { const data = await fetch({ input: { item: { - itemId, productId, variantId, quantity: input.quantity, diff --git a/framework/swell/cart/utils/checkout-to-cart.ts b/framework/swell/cart/utils/checkout-to-cart.ts index 19a8bfd0b..d6dfda206 100644 --- a/framework/swell/cart/utils/checkout-to-cart.ts +++ b/framework/swell/cart/utils/checkout-to-cart.ts @@ -1,5 +1,5 @@ import { Cart } from '../../types' -import { CommerceError, ValidationError } from '@commerce/utils/errors' +import { CommerceError } from '@commerce/utils/errors' import { CheckoutLineItemsAddPayload, @@ -20,8 +20,7 @@ const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => { message: 'Invalid response from Swell', }) } - - return normalizeCart(checkoutPayload) + return normalizeCart(checkoutPayload as any) } export default checkoutToCart diff --git a/framework/swell/common/get-all-pages.ts b/framework/swell/common/get-all-pages.ts index 99e07f6e6..8d0b9cf36 100644 --- a/framework/swell/common/get-all-pages.ts +++ b/framework/swell/common/get-all-pages.ts @@ -23,12 +23,13 @@ const getAllPages = async (options?: { }): Promise<ReturnType> => { let { config, variables = { first: 250 } } = options ?? {} config = getConfig(config) - const { locale, fetchSwell } = config - const { results } = await fetchSwell('content', 'list', ['pages']) - const pages = results.map(({ slug, ...rest }: { slug: string }) => ({ - url: `/${locale}/${slug}`, - ...rest, - })) + const { locale, fetch } = config + const data = await fetch('content', 'list', ['pages']) + const pages = + data?.results?.map(({ slug, ...rest }: { slug: string }) => ({ + url: `/${locale}/${slug}`, + ...rest, + })) ?? [] return { pages } } diff --git a/framework/swell/common/get-page.ts b/framework/swell/common/get-page.ts index e5ca005e0..dca317a19 100644 --- a/framework/swell/common/get-page.ts +++ b/framework/swell/common/get-page.ts @@ -17,7 +17,7 @@ const getPage = async (options: { config = getConfig(config) const { locale } = config const { id } = variables - const result = await config.fetchSwell('content', 'get', ['pages', id]) + const result = await config.fetch('content', 'get', ['pages', id]) const page = result return { diff --git a/framework/swell/fetcher.ts b/framework/swell/fetcher.ts index e52d8fd54..f18dcf667 100644 --- a/framework/swell/fetcher.ts +++ b/framework/swell/fetcher.ts @@ -1,22 +1,27 @@ import { Fetcher } from '@commerce/utils/types' import { handleFetchResponse } from './utils' import { swellConfig } from './index' +import { CommerceError } from '@commerce/utils/errors' const fetcher: Fetcher = async ({ method = 'get', variables, query }) => { const { swell } = swellConfig + async function callSwell() { if (Array.isArray(variables)) { const arg1 = variables[0] const arg2 = variables[1] - const response = await swell[query][method](arg1, arg2) + const response = await swell[query!][method](arg1, arg2) return handleFetchResponse(response) } else { - const response = await swell[query][method](variables) + const response = await swell[query!][method](variables) return handleFetchResponse(response) } } - if (query in swell) { + + if (query && query in swell) { return await callSwell() + } else { + throw new CommerceError({ message: 'Invalid query argument!' }) } } diff --git a/framework/swell/product/get-all-collections.ts b/framework/swell/product/get-all-collections.ts index e6da3ade4..6b82ce449 100644 --- a/framework/swell/product/get-all-collections.ts +++ b/framework/swell/product/get-all-collections.ts @@ -9,7 +9,7 @@ const getAllCollections = async (options?: { let { config, variables = { limit: 25 } } = options ?? {} config = getConfig(config) - const response = await config.fetchSwell('categories', 'list', { variables }) + const response = await config.fetch('categories', 'list', { variables }) const edges = response.results ?? [] const categories = edges.map( diff --git a/framework/swell/product/get-all-product-paths.ts b/framework/swell/product/get-all-product-paths.ts index 3b95798e4..baa9ce8fe 100644 --- a/framework/swell/product/get-all-product-paths.ts +++ b/framework/swell/product/get-all-product-paths.ts @@ -1,5 +1,5 @@ +import { SwellProduct } from '@framework/types' import { getConfig, SwellConfig } from '../api' -import fetchAllProducts from '../api/utils/fetch-all-products' type ProductPath = { path: string @@ -21,15 +21,14 @@ const getAllProductPaths = async (options?: { let { config, variables = [{ limit: 100 }] } = options ?? {} config = getConfig(config) - const products = await fetchAllProducts({ - config, - query: 'products', - method: 'list', - variables, - }) + const { results } = await config.fetch('products', 'list', [ + { + limit: variables.first, + }, + ]) return { - products: products?.map(({ slug: handle }) => ({ + products: results?.map(({ slug: handle }: SwellProduct) => ({ node: { path: `/${handle}`, }, diff --git a/framework/swell/product/get-all-products.ts b/framework/swell/product/get-all-products.ts index a129c18a2..c0746efca 100644 --- a/framework/swell/product/get-all-products.ts +++ b/framework/swell/product/get-all-products.ts @@ -19,7 +19,7 @@ const getAllProducts = async (options: { }): Promise<ReturnType> => { let { config, variables = { first: 250 } } = options ?? {} config = getConfig(config) - const { results } = await config.fetchSwell('products', 'list', [ + const { results } = await config.fetch('products', 'list', [ { limit: variables.first, }, diff --git a/framework/swell/product/get-product.ts b/framework/swell/product/get-product.ts index 15fde1cfa..0d75a57cd 100644 --- a/framework/swell/product/get-product.ts +++ b/framework/swell/product/get-product.ts @@ -18,10 +18,12 @@ const getProduct = async (options: { let { config, variables } = options ?? {} config = getConfig(config) - const product = await config.fetchSwell('products', 'get', [variables.slug]) + const product = await config.fetch('products', 'get', [variables.slug]) + if (product && product.variants) { product.variants = product.variants?.results } + return { product: product ? normalizeProduct(product) : null, } diff --git a/framework/swell/types.ts b/framework/swell/types.ts index 248ea3158..71848d4a4 100644 --- a/framework/swell/types.ts +++ b/framework/swell/types.ts @@ -90,7 +90,7 @@ export interface Cart extends Core.Cart { } export interface LineItem extends Core.LineItem { - options: any[] + options?: any[] } /** diff --git a/framework/swell/utils/get-categories.ts b/framework/swell/utils/get-categories.ts index 5b9ba501e..4372dde4e 100644 --- a/framework/swell/utils/get-categories.ts +++ b/framework/swell/utils/get-categories.ts @@ -1,15 +1,15 @@ import { SwellConfig } from '../api' export type Category = { - id: string + entityId: string name: string - slug: string + path: string } const getCategories = async (config: SwellConfig): Promise<Category[]> => { - const data = await config.fetchSwell('categories', 'get') + const data = await config.fetch('categories', 'get') return ( - data.results.map(({ id: entityId, name, slug }: Category) => ({ + data.results.map(({ id: entityId, name, slug }: any) => ({ entityId, name, path: `/${slug}`, diff --git a/framework/swell/utils/get-vendors.ts b/framework/swell/utils/get-vendors.ts index 9f2184fc3..1ede68835 100644 --- a/framework/swell/utils/get-vendors.ts +++ b/framework/swell/utils/get-vendors.ts @@ -13,7 +13,7 @@ export type Brands = BrandEdge[] const getVendors = async (config: SwellConfig) => { const vendors: [string] = - (await config.fetchSwell('attributes', 'get', ['brand']))?.values ?? [] + (await config.fetch('attributes', 'get', ['brand']))?.values ?? [] return [...new Set(vendors)].map((v) => ({ node: { diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts index cc8eafdb7..5e54428ca 100644 --- a/framework/swell/utils/normalize.ts +++ b/framework/swell/utils/normalize.ts @@ -1,11 +1,6 @@ import { Product, Customer } from '@commerce/types' -import { - Checkout, - CheckoutLineItemEdge, - MoneyV2, - ProductOption, -} from '../schema' +import { MoneyV2, ProductOption } from '../schema' import type { Cart, diff --git a/pages/[...pages].tsx b/pages/[...pages].tsx index 67adb6287..3f39845b5 100644 --- a/pages/[...pages].tsx +++ b/pages/[...pages].tsx @@ -25,8 +25,7 @@ export async function getStaticProps({ const pageItem = pages.find((p) => (p.url ? getSlug(p.url) === slug : false)) const data = pageItem && - // TODO: Shopify - Fix this type - (await getPage({ variables: { id: pageItem.id! } as any, config, preview })) + (await getPage({ variables: { id: pageItem.id! }, config, preview })) const page = data?.page if (!page) { diff --git a/pages/search.tsx b/pages/search.tsx index da2edccd8..3b26e7a14 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -75,9 +75,8 @@ export default function Search({ const { data } = useSearch({ search: typeof q === 'string' ? q : '', - // TODO: Shopify - Fix this type + // @ts-ignore Swell - Fix this types categoryId: activeCategory?.entityId as any, - // TODO: Shopify - Fix this type brandId: (activeBrand as any)?.entityId, sort: typeof sort === 'string' ? sort : '', }) diff --git a/tsconfig.json b/tsconfig.json index e20f37099..ffb74e388 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,8 +22,8 @@ "@components/*": ["components/*"], "@commerce": ["framework/commerce"], "@commerce/*": ["framework/commerce/*"], - "@framework": ["framework/shopify"], - "@framework/*": ["framework/shopify/*"] + "@framework": ["framework/swell"], + "@framework/*": ["framework/swell/*"] } }, "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], diff --git a/yarn.lock b/yarn.lock index 8bc30e067..6a04c4ac7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1007,32 +1007,6 @@ resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.0.tgz#14f854c0f93d326e39da6e3b6f34f7d37513d108" integrity sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg== -"@types/eslint-scope@^3.7.0": - version "3.7.0" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.0.tgz#4792816e31119ebd506902a482caec4951fabd86" - integrity sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw== - dependencies: - "@types/eslint" "*" - "@types/estree" "*" - -"@types/eslint@*": - version "7.2.6" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.6.tgz#5e9aff555a975596c03a98b59ecd103decc70c3c" - integrity sha512-I+1sYH+NPQ3/tVqCeUSBwTE/0heyvtXqpIopUUArlBm0Kpocb8FbMa3AZ/ASKIFpN3rnEx932TTXDbt9OXsNDw== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - -"@types/estree@*": - version "0.0.46" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe" - integrity sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg== - -"@types/estree@^0.0.45": - version "0.0.45" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884" - integrity sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g== - "@types/http-proxy-agent@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@types/http-proxy-agent/-/http-proxy-agent-2.0.2.tgz#942c1f35c7e1f0edd1b6ffae5d0f9051cfb32be1" @@ -1050,11 +1024,6 @@ resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.6.tgz#7f10c926aa41e189a2755c4c7fcf8e4573bd7ac1" integrity sha512-cK4XqrLvP17X6c0C8n4iTbT59EixqyXL3Fk8/Rsk4dF3oX4dg70gYUXrXVUUHpnsGMPNlTQMqf+TVmNPX6FmSQ== -"@types/json-schema@*", "@types/json-schema@^7.0.6": - version "7.0.7" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" - integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== - "@types/json-stable-stringify@^1.0.32": version "1.0.32" resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.32.tgz#121f6917c4389db3923640b2e68de5fa64dda88e" @@ -1200,161 +1169,6 @@ agentkeepalive "3.4.1" debug "3.1.0" -"@webassemblyjs/ast@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.1.tgz#76c6937716d68bf1484c15139f5ed30b9abc8bb4" - integrity sha512-uMu1nCWn2Wxyy126LlGqRVlhdTOsO/bsBRI4dNq3+6SiSuRKRQX6ejjKgh82LoGAPSq72lDUiQ4FWVaf0PecYw== - dependencies: - "@webassemblyjs/helper-module-context" "1.9.1" - "@webassemblyjs/helper-wasm-bytecode" "1.9.1" - "@webassemblyjs/wast-parser" "1.9.1" - -"@webassemblyjs/floating-point-hex-parser@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.1.tgz#9eb0ff90a1cdeef51f36ba533ed9f06b5cdadd09" - integrity sha512-5VEKu024RySmLKTTBl9q1eO/2K5jk9ZS+2HXDBLA9s9p5IjkaXxWiDb/+b7wSQp6FRdLaH1IVGIfOex58Na2pg== - -"@webassemblyjs/helper-api-error@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.1.tgz#ad89015c4246cd7f5ed0556700237f8b9c2c752f" - integrity sha512-y1lGmfm38djrScwpeL37rRR9f1D6sM8RhMpvM7CYLzOlHVboouZokXK/G88BpzW0NQBSvCCOnW5BFhten4FPfA== - -"@webassemblyjs/helper-buffer@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.1.tgz#186e67ac25f9546ea7939759413987f157524133" - integrity sha512-uS6VSgieHbk/m4GSkMU5cqe/5TekdCzQso4revCIEQ3vpGZgqSSExi4jWpTWwDpAHOIAb1Jfrs0gUB9AA4n71w== - -"@webassemblyjs/helper-code-frame@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.1.tgz#aab177b7cc87a318a8f8664ad68e2c3828ebc42b" - integrity sha512-ZQ2ZT6Evk4DPIfD+92AraGYaFIqGm4U20e7FpXwl7WUo2Pn1mZ1v8VGH8i+Y++IQpxPbQo/UyG0Khs7eInskzA== - dependencies: - "@webassemblyjs/wast-printer" "1.9.1" - -"@webassemblyjs/helper-fsm@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.1.tgz#527e91628e84d13d3573884b3dc4c53a81dcb911" - integrity sha512-J32HGpveEqqcKFS0YbgicB0zAlpfIxJa5MjxDxhu3i5ltPcVfY5EPvKQ1suRguFPehxiUs+/hfkwPEXom/l0lw== - -"@webassemblyjs/helper-module-context@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.1.tgz#778670b3d471f7cf093d1e7c0dde431b54310e16" - integrity sha512-IEH2cMmEQKt7fqelLWB5e/cMdZXf2rST1JIrzWmf4XBt3QTxGdnnLvV4DYoN8pJjOx0VYXsWg+yF16MmJtolZg== - dependencies: - "@webassemblyjs/ast" "1.9.1" - -"@webassemblyjs/helper-wasm-bytecode@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.1.tgz#563f59bcf409ccf469edde168b9426961ffbf6df" - integrity sha512-i2rGTBqFUcSXxyjt2K4vm/3kkHwyzG6o427iCjcIKjOqpWH8SEem+xe82jUk1iydJO250/CvE5o7hzNAMZf0dQ== - -"@webassemblyjs/helper-wasm-section@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.1.tgz#f7988f94c12b01b99a16120cb01dc099b00e4798" - integrity sha512-FetqzjtXZr2d57IECK+aId3D0IcGweeM0CbAnJHkYJkcRTHP+YcMb7Wmc0j21h5UWBpwYGb9dSkK/93SRCTrGg== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-buffer" "1.9.1" - "@webassemblyjs/helper-wasm-bytecode" "1.9.1" - "@webassemblyjs/wasm-gen" "1.9.1" - -"@webassemblyjs/ieee754@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.1.tgz#3b715871ca7d75784717cf9ceca9d7b81374b8af" - integrity sha512-EvTG9M78zP1MmkBpUjGQHZc26DzPGZSLIPxYHCjQsBMo60Qy2W34qf8z0exRDtxBbRIoiKa5dFyWer/7r1aaSQ== - dependencies: - "@xtuc/ieee754" "^1.2.0" - -"@webassemblyjs/leb128@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.1.tgz#b2ecaa39f9e8277cc9c707c1ca8b2aa7b27d0b72" - integrity sha512-Oc04ub0vFfLnF+2/+ki3AE+anmW4sv9uNBqb+79fgTaPv6xJsOT0dhphNfL3FrME84CbX/D1T9XT8tjFo0IIiw== - dependencies: - "@xtuc/long" "4.2.2" - -"@webassemblyjs/utf8@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.1.tgz#d02d9daab85cda3211e43caf31dca74c260a73b0" - integrity sha512-llkYtppagjCodFjo0alWOUhAkfOiQPQDIc5oA6C9sFAXz7vC9QhZf/f8ijQIX+A9ToM3c9Pq85X0EX7nx9gVhg== - -"@webassemblyjs/wasm-edit@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.1.tgz#e27a6bdbf78e5c72fa812a2fc3cbaad7c3e37578" - integrity sha512-S2IaD6+x9B2Xi8BCT0eGsrXXd8UxAh2LVJpg1ZMtHXnrDcsTtIX2bDjHi40Hio6Lc62dWHmKdvksI+MClCYbbw== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-buffer" "1.9.1" - "@webassemblyjs/helper-wasm-bytecode" "1.9.1" - "@webassemblyjs/helper-wasm-section" "1.9.1" - "@webassemblyjs/wasm-gen" "1.9.1" - "@webassemblyjs/wasm-opt" "1.9.1" - "@webassemblyjs/wasm-parser" "1.9.1" - "@webassemblyjs/wast-printer" "1.9.1" - -"@webassemblyjs/wasm-gen@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.1.tgz#56a0787d1fa7994fdc7bea59004e5bec7189c5fc" - integrity sha512-bqWI0S4lBQsEN5FTZ35vYzfKUJvtjNnBobB1agCALH30xNk1LToZ7Z8eiaR/Z5iVECTlBndoRQV3F6mbEqE/fg== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-wasm-bytecode" "1.9.1" - "@webassemblyjs/ieee754" "1.9.1" - "@webassemblyjs/leb128" "1.9.1" - "@webassemblyjs/utf8" "1.9.1" - -"@webassemblyjs/wasm-opt@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.1.tgz#fbdf8943a825e6dcc4cd69c3e092289fa4aec96c" - integrity sha512-gSf7I7YWVXZ5c6XqTEqkZjVs8K1kc1k57vsB6KBQscSagDNbAdxt6MwuJoMjsE1yWY1tsuL+pga268A6u+Fdkg== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-buffer" "1.9.1" - "@webassemblyjs/wasm-gen" "1.9.1" - "@webassemblyjs/wasm-parser" "1.9.1" - -"@webassemblyjs/wasm-parser@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.1.tgz#5e8352a246d3f605312c8e414f7990de55aaedfa" - integrity sha512-ImM4N2T1MEIond0MyE3rXvStVxEmivQrDKf/ggfh5pP6EHu3lL/YTAoSrR7shrbKNPpeKpGesW1LIK/L4kqduw== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-api-error" "1.9.1" - "@webassemblyjs/helper-wasm-bytecode" "1.9.1" - "@webassemblyjs/ieee754" "1.9.1" - "@webassemblyjs/leb128" "1.9.1" - "@webassemblyjs/utf8" "1.9.1" - -"@webassemblyjs/wast-parser@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.1.tgz#e25ef13585c060073c1db0d6bd94340fdeee7596" - integrity sha512-2xVxejXSvj3ls/o2TR/zI6p28qsGupjHhnHL6URULQRcXmryn3w7G83jQMcT7PHqUfyle65fZtWLukfdLdE7qw== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/floating-point-hex-parser" "1.9.1" - "@webassemblyjs/helper-api-error" "1.9.1" - "@webassemblyjs/helper-code-frame" "1.9.1" - "@webassemblyjs/helper-fsm" "1.9.1" - "@xtuc/long" "4.2.2" - -"@webassemblyjs/wast-printer@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.1.tgz#b9f38e93652037d4f3f9c91584635af4191ed7c1" - integrity sha512-tDV8V15wm7mmbAH6XvQRU1X+oPGmeOzYsd6h7hlRLz6QpV4Ec/KKxM8OpLtFmQPLCreGxTp+HuxtH4pRIZyL9w== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/wast-parser" "1.9.1" - "@xtuc/long" "4.2.2" - -"@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== - -"@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== - "@zeit/dns-cached-resolve@2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@zeit/dns-cached-resolve/-/dns-cached-resolve-2.1.0.tgz#78583010df1683fdb7b05949b75593c9a8641bc1" @@ -1417,12 +1231,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.12.5, ajv@^6.12.6: +ajv@^6.12.6: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -1518,6 +1327,14 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +array-includes-with-glob@^3.0.6: + version "3.0.16" + resolved "https://registry.yarnpkg.com/array-includes-with-glob/-/array-includes-with-glob-3.0.16.tgz#fdf2bf1e914cb9b95bd2a866194ede0ab3225d7b" + integrity sha512-uFwmmz78W4WzmMsLKojbCA2q5EbqugWFMm5zDyO+SSR6eW1rqzoxramiTCJYq1o/NZijt1szOJMA29JHZuU34A== + dependencies: + "@babel/runtime" "^7.13.10" + matcher "^4.0.0" + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -1826,7 +1643,7 @@ browserslist@4.16.1: escalade "^3.1.1" node-releases "^1.1.69" -browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.1, browserslist@^4.6.4: +browserslist@^4.12.0, browserslist@^4.16.1, browserslist@^4.6.4: version "4.16.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717" integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== @@ -2023,13 +1840,6 @@ chokidar@3.5.1, chokidar@^3.4.3: optionalDependencies: fsevents "~2.3.1" -chrome-trace-event@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" - integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== - dependencies: - tslib "^1.9.0" - ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -2180,7 +1990,7 @@ combined-stream@^1.0.6, combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -commander@^2.13.0, commander@^2.16.0, commander@^2.20.0, commander@^2.20.3, commander@^2.8.1: +commander@^2.13.0, commander@^2.16.0, commander@^2.20.3, commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -2511,11 +2321,6 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= -dedent@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= - deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -2526,7 +2331,7 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= -deepmerge@^4.2.2: +deepmerge@4.2.2, deepmerge@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== @@ -2820,14 +2625,6 @@ enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: memory-fs "^0.5.0" tapable "^1.0.0" -enhanced-resolve@^5.3.1: - version "5.7.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz#525c5d856680fbd5052de453ac83e32049958b5c" - integrity sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" - enquirer@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" @@ -2905,14 +2702,6 @@ escodegen@^1.8.0: optionalDependencies: source-map "~0.6.1" -eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - eslint-visitor-keys@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" @@ -2923,23 +2712,11 @@ esprima@4.0.1, esprima@^4.0.0, esprima@^4.0.1: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== - esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -2950,7 +2727,7 @@ etag@1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -events@^3.0.0, events@^3.2.0: +events@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== @@ -3326,7 +3103,7 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== @@ -3876,15 +3653,6 @@ jest-worker@24.9.0: merge-stream "^2.0.0" supports-color "^6.1.0" -jest-worker@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" - integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^7.0.0" - js-cookie@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" @@ -3913,11 +3681,6 @@ json-buffer@3.0.0: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= -json-parse-better-errors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - json-parse-even-better-errors@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -4129,11 +3892,6 @@ listr@^0.14.3: p-map "^2.0.0" rxjs "^6.3.3" -loader-runner@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384" - integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== - loader-utils@1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" @@ -4499,7 +4257,7 @@ mime-db@1.45.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== -mime-types@^2.1.12, mime-types@^2.1.27: +mime-types@^2.1.12: version "2.1.28" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ== @@ -4624,11 +4382,6 @@ native-url@0.3.4: dependencies: querystring "^0.2.0" -neo-async@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - next-seo@^4.11.0: version "4.19.0" resolved "https://registry.yarnpkg.com/next-seo/-/next-seo-4.19.0.tgz#b3be79a544e420dbbf1e4fcff98dad9790f15e36" @@ -4960,7 +4713,7 @@ p-limit@3.0.2: dependencies: p-try "^2.0.0" -p-limit@3.1.0, p-limit@^3.0.2, p-limit@^3.1.0: +p-limit@3.1.0, p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -5727,7 +5480,7 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== @@ -6074,15 +5827,6 @@ scheduler@^0.20.1: loose-envify "^1.1.0" object-assign "^4.1.1" -schema-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" - integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== - dependencies: - "@types/json-schema" "^7.0.6" - ajv "^6.12.5" - ajv-keywords "^3.5.2" - scuid@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/scuid/-/scuid-1.1.0.tgz#d3f9f920956e737a60f72d0e4ad280bf324d5dab" @@ -6115,13 +5859,6 @@ semver@^7.3.2: dependencies: lru-cache "^6.0.0" -serialize-javascript@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" - integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== - dependencies: - randombytes "^2.1.0" - set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -6162,18 +5899,6 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "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: version "1.7.2" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" @@ -6238,12 +5963,7 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -source-list-map@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" - integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== - -source-map-support@^0.5.17, source-map-support@~0.5.19: +source-map-support@^0.5.17: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -6251,7 +5971,7 @@ source-map-support@^0.5.17, source-map-support@~0.5.19: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@0.7.3, source-map@~0.7.2: +source-map@0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== @@ -6529,7 +6249,7 @@ supports-color@^6.1.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -6605,37 +6325,11 @@ tapable@^1.0.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tapable@^2.1.1, tapable@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" - integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== - temp@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/temp/-/temp-0.4.0.tgz#671ad63d57be0fe9d7294664b3fc400636678a60" integrity sha1-ZxrWPVe+D+nXKUZks/xABjZnimA= -terser-webpack-plugin@^5.0.3: - version "5.1.1" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.1.tgz#7effadee06f7ecfa093dbbd3e9ab23f5f3ed8673" - integrity sha512-5XNNXZiR8YO6X6KhSGXfY0QrGrCRlSwAEjIIrlRQR4W8nP69TaJUlh3bkuac6zzgspiGPfKEHcY295MMVExl5Q== - dependencies: - jest-worker "^26.6.2" - p-limit "^3.1.0" - schema-utils "^3.0.0" - serialize-javascript "^5.0.1" - source-map "^0.6.1" - terser "^5.5.1" - -terser@^5.5.1: - version "5.6.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.6.0.tgz#138cdf21c5e3100b1b3ddfddf720962f88badcd2" - integrity sha512-vyqLMoqadC1uR0vywqOZzriDYzgEkNJFK4q9GeyOBHIbiECHiWLKcWfbQWAUaPfxkjDhapSlZB9f7fkMrvkVjA== - dependencies: - commander "^2.20.0" - source-map "~0.7.2" - source-map-support "~0.5.19" - through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -6946,7 +6640,7 @@ warning@^4.0.3: dependencies: loose-envify "^1.0.0" -watchpack@2.1.1, watchpack@^2.0.0: +watchpack@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.1.1.tgz#e99630550fca07df9f90a06056987baa40a689c7" integrity sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw== @@ -6981,44 +6675,6 @@ webpack-bundle-analyzer@4.3.0: sirv "^1.0.7" ws "^7.3.1" -webpack-sources@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac" - integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w== - dependencies: - source-list-map "^2.0.1" - source-map "^0.6.1" - -webpack@5.11.1: - version "5.11.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.11.1.tgz#39b2b9daeb5c6c620e03b7556ec674eaed4016b4" - integrity sha512-tNUIdAmYJv+nupRs/U/gqmADm6fgrf5xE+rSlSsf2PgsGO7j2WG7ccU6AWNlOJlHFl+HnmXlBmHIkiLf+XA9mQ== - dependencies: - "@types/eslint-scope" "^3.7.0" - "@types/estree" "^0.0.45" - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-module-context" "1.9.1" - "@webassemblyjs/wasm-edit" "1.9.1" - "@webassemblyjs/wasm-parser" "1.9.1" - acorn "^8.0.4" - browserslist "^4.14.5" - chrome-trace-event "^1.0.2" - enhanced-resolve "^5.3.1" - eslint-scope "^5.1.1" - events "^3.2.0" - glob-to-regexp "^0.4.1" - graceful-fs "^4.2.4" - json-parse-better-errors "^1.0.2" - loader-runner "^4.1.0" - mime-types "^2.1.27" - neo-async "^2.6.2" - pkg-dir "^5.0.0" - schema-utils "^3.0.0" - tapable "^2.1.1" - terser-webpack-plugin "^5.0.3" - watchpack "^2.0.0" - webpack-sources "^2.1.1" - whatwg-fetch@^3.4.1: version "3.5.0" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz#605a2cd0a7146e5db141e29d1c62ab84c0c4c868" From 926db9f4ad2b7dd1a0abbfb2b54a6d7af9eecfc8 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 13 May 2021 16:14:21 +0300 Subject: [PATCH 211/221] Create README.md --- README.md | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..941b1699b --- /dev/null +++ b/README.md @@ -0,0 +1,123 @@ +[](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fcommerce&project-name=commerce&repo-name=commerce&demo-title=Next.js%20Commerce&demo-description=An%20all-in-one%20starter%20kit%20for%20high-performance%20e-commerce%20sites.&demo-url=https%3A%2F%2Fdemo.vercel.store&demo-image=https%3A%2F%2Fbigcommerce-demo-asset-ksvtgfvnd.vercel.app%2Fbigcommerce.png&integration-ids=oac_MuWZiE4jtmQ2ejZQaQ7ncuDT) + +# Next.js Commerce + +The all-in-one starter kit for high-performance e-commerce sites. With a few clicks, Next.js developers can clone, deploy and fully customize their own store. +Start right now at [nextjs.org/commerce](https://nextjs.org/commerce) + +Demo live at: [demo.vercel.store](https://demo.vercel.store/) + +- Shopify Demo: https://shopify.demo.vercel.store/ +- BigCommerce Demo: https://bigcommerce.demo.vercel.store/ + +## Features + +- Performant by default +- SEO Ready +- Internationalization +- Responsive +- UI Components +- Theming +- Standardized Data Hooks +- Integrations - Integrate seamlessly with the most common ecommerce platforms. +- Dark Mode Support + +## Integrations + +Next.js Commerce integrates out-of-the-box with BigCommerce and Shopify. We plan to support all major ecommerce backends. + +## Considerations + +- `framework/commerce` contains all types, helpers and functions to be used as base to build a new **provider**. +- **Providers** live under `framework`'s root folder and they will extend Next.js Commerce types and functionality (`framework/commerce`). +- We have a **Features API** to ensure feature parity between the UI and the Provider. The UI should update accordingly and no extra code should be bundled. All extra configuration for features will live under `features` in `commerce.config.json` and if needed it can also be accessed programatically. +- Each **provider** should add its corresponding `next.config.js` and `commerce.config.json` adding specific data related to the provider. For example in case of BigCommerce, the images CDN and additional API routes. +- **Providers don't depend on anything that's specific to the application they're used in**. They only depend on `framework/commerce`, on their own framework folder and on some dependencies included in `package.json` + +## Configuration + +### How to change providers + +Open `.env.local` and change the value of `COMMERCE_PROVIDER` to the provider you would like to use, then set the environment variables for that provider (use `.env.template` as the base). + +### Features + +Every provider defines the features that it supports under `framework/{provider}/commerce.config.json` + +#### How to turn Features on and off + +> NOTE: The selected provider should support the feature that you are toggling. (This means that you can't turn wishlist on if the provider doesn't support this functionality out the box) + +- Open `commerce.config.json` +- You'll see a config file like this: + ```json + { + "features": { + "wishlist": false + } + } + ``` +- Turn wishlist on by setting wishlist to true. +- Run the app and the wishlist functionality should be back on. + +### How to create a new provider + +Follow our docs for [Adding a new Commerce Provider](framework/commerce/new-provider.md). + +If you succeeded building a provider, submit a PR with a valid demo and we'll review it asap. + +## Contribute + +Our commitment to Open Source can be found [here](https://vercel.com/oss). + +1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device. +2. Create a new branch `git checkout -b MY_BRANCH_NAME` +3. Install yarn: `npm install -g yarn` +4. Install the dependencies: `yarn` +5. Duplicate `.env.template` and rename it to `.env.local` +6. Add proper store values to `.env.local` +7. Run `yarn dev` to build and watch for code changes + +## Work in progress + +We're using Github Projects to keep track of issues in progress and todo's. Here is our [Board](https://github.com/vercel/commerce/projects/1) + +People actively working on this project: @okbel & @lfades. + +## Troubleshoot + +<details> +<summary>I already own a BigCommerce store. What should I do?</summary> +<br> +First thing you do is: <b>set your environment variables</b> +<br> +<br> +.env.local + +```sh +BIGCOMMERCE_STOREFRONT_API_URL=<> +BIGCOMMERCE_STOREFRONT_API_TOKEN=<> +BIGCOMMERCE_STORE_API_URL=<> +BIGCOMMERCE_STORE_API_TOKEN=<> +BIGCOMMERCE_STORE_API_CLIENT_ID=<> +BIGCOMMERCE_CHANNEL_ID=<> +``` + +If your project was started with a "Deploy with Vercel" button, you can use Vercel's CLI to retrieve these credentials. + +1. Install Vercel CLI: `npm i -g vercel` +2. Link local instance with Vercel and Github accounts (creates .vercel file): `vercel link` +3. Download your environment variables: `vercel env pull .env.local` + +Next, you're free to customize the starter. More updates coming soon. Stay tuned. + +</details> + +<details> +<summary>BigCommerce shows a Coming Soon page and requests a Preview Code</summary> +<br> +After Email confirmation, Checkout should be manually enabled through BigCommerce platform. Look for "Review & test your store" section through BigCommerce's dashboard. +<br> +<br> +BigCommerce team has been notified and they plan to add more detailed about this subject. +</details> From 48306052ecf2c58d34989dbccfed709e0a6812a2 Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 13 May 2021 16:29:24 +0300 Subject: [PATCH 212/221] Update tsconfig.json --- tsconfig.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index ffb74e388..9e712fb18 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,8 +22,8 @@ "@components/*": ["components/*"], "@commerce": ["framework/commerce"], "@commerce/*": ["framework/commerce/*"], - "@framework": ["framework/swell"], - "@framework/*": ["framework/swell/*"] + "@framework": ["framework/bigcommerce"], + "@framework/*": ["framework/bigcommerce/*"] } }, "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], From b852cd477902cd5284628e32d6d9a26ea7ed1ebe Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 13 May 2021 16:30:30 +0300 Subject: [PATCH 213/221] Update get-all-product-paths.ts --- framework/swell/product/get-all-product-paths.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/swell/product/get-all-product-paths.ts b/framework/swell/product/get-all-product-paths.ts index baa9ce8fe..933eb73bc 100644 --- a/framework/swell/product/get-all-product-paths.ts +++ b/framework/swell/product/get-all-product-paths.ts @@ -1,4 +1,4 @@ -import { SwellProduct } from '@framework/types' +import { SwellProduct } from '../types' import { getConfig, SwellConfig } from '../api' type ProductPath = { From d981475818fd1b7880440ea79b036dc19fb7178e Mon Sep 17 00:00:00 2001 From: cond0r <pinte_catalin@yahoo.com> Date: Thu, 13 May 2021 17:00:10 +0300 Subject: [PATCH 214/221] Default to Bigcommerce --- pages/search.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pages/search.tsx b/pages/search.tsx index 3b26e7a14..78f784572 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -75,8 +75,7 @@ export default function Search({ const { data } = useSearch({ search: typeof q === 'string' ? q : '', - // @ts-ignore Swell - Fix this types - categoryId: activeCategory?.entityId as any, + categoryId: activeCategory?.entityId, brandId: (activeBrand as any)?.entityId, sort: typeof sort === 'string' ? sort : '', }) From c8cf6e733c834d374e9f437e754f0c958162ce28 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Sun, 16 May 2021 13:31:57 -0700 Subject: [PATCH 215/221] fix error from missing product imgages --- framework/swell/utils/normalize.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts index 5e54428ca..8dc7d0e28 100644 --- a/framework/swell/utils/normalize.ts +++ b/framework/swell/utils/normalize.ts @@ -198,7 +198,10 @@ function normalizeLineItem({ sku: variant?.sku ?? '', name: variant?.name!, image: { - url: product && product.images ? product?.images[0].file.url : '', + url: + product?.images && product.images.length > 0 + ? product?.images[0].file.url + : '/', }, requiresShipping: false, price: price, From 7d62e7ce18337743f5e834c32cf6878e8fb4b617 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Sun, 16 May 2021 14:15:49 -0700 Subject: [PATCH 216/221] fix product option color check --- framework/swell/utils/normalize.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/swell/utils/normalize.ts b/framework/swell/utils/normalize.ts index 8dc7d0e28..b0cfb6193 100644 --- a/framework/swell/utils/normalize.ts +++ b/framework/swell/utils/normalize.ts @@ -38,7 +38,7 @@ const normalizeProductOption = ({ label: value.name, id: value?.id || id, } - if (displayName === 'Color') { + if (displayName.match(/colou?r/gi)) { output = { ...output, hexColors: [value.name], From 385dc1b672dc00fc895f403d0c54f0762f52ef09 Mon Sep 17 00:00:00 2001 From: Greg Hoskin <greghoskin@Gregs-MacBook-Pro.local> Date: Mon, 17 May 2021 09:34:53 -0700 Subject: [PATCH 217/221] fix signup-triggered login --- framework/swell/auth/use-signup.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/framework/swell/auth/use-signup.tsx b/framework/swell/auth/use-signup.tsx index f01d45044..d417122fc 100644 --- a/framework/swell/auth/use-signup.tsx +++ b/framework/swell/auth/use-signup.tsx @@ -44,10 +44,7 @@ export const handler: MutationHook< const loginData = await fetch({ query: 'account', method: 'login', - variables: { - email, - password, - }, + variables: [email, password], }) handleLogin(loginData) } catch (error) {} From 4f3674aafa2fa83495d4f17b30207fecd6b0e1cc Mon Sep 17 00:00:00 2001 From: B <curciobelen@gmail.com> Date: Wed, 26 May 2021 17:18:50 -0300 Subject: [PATCH 218/221] New provider and tiny fixes. (#326) * changes * Adding new provider * Adding new provider * Adding new provider * Adding new provider --- .../common/FeatureBar/FeatureBar.module.css | 4 +- .../common/I18nWidget/I18nWidget.module.css | 8 +- .../common/UserNav/DropdownMenu.module.css | 4 +- components/ui/Grid/Grid.module.css | 15 +- components/ui/Hero/Hero.module.css | 5 +- framework/commerce/config.js | 4 +- yarn.lock | 493 +++++------------- 7 files changed, 161 insertions(+), 372 deletions(-) diff --git a/components/common/FeatureBar/FeatureBar.module.css b/components/common/FeatureBar/FeatureBar.module.css index a3cb61cd2..419fd4b08 100644 --- a/components/common/FeatureBar/FeatureBar.module.css +++ b/components/common/FeatureBar/FeatureBar.module.css @@ -1,7 +1,9 @@ .root { @apply text-center p-6 bg-primary text-sm flex-row justify-center items-center font-medium fixed bottom-0 w-full z-30 transition-all duration-300 ease-out; +} - @screen md { +@screen md { + .root { @apply flex text-left; } } diff --git a/components/common/I18nWidget/I18nWidget.module.css b/components/common/I18nWidget/I18nWidget.module.css index b216f5706..f100fd475 100644 --- a/components/common/I18nWidget/I18nWidget.module.css +++ b/components/common/I18nWidget/I18nWidget.module.css @@ -16,14 +16,16 @@ .dropdownMenu { @apply fixed right-0 top-12 mt-2 origin-top-right outline-none bg-primary z-40 w-full h-full; +} - @screen lg { +@screen lg { + .dropdownMenu { @apply absolute border border-accents-1 shadow-lg w-56 h-auto; } } -.closeButton { - @screen md { +@screen md { + .closeButton { @apply hidden; } } diff --git a/components/common/UserNav/DropdownMenu.module.css b/components/common/UserNav/DropdownMenu.module.css index 404726bc5..b2756e9d5 100644 --- a/components/common/UserNav/DropdownMenu.module.css +++ b/components/common/UserNav/DropdownMenu.module.css @@ -1,7 +1,9 @@ .dropdownMenu { @apply fixed right-0 mt-2 origin-top-right outline-none bg-primary z-40 w-full h-full; +} - @screen lg { +@screen lg { + .dropdownMenu { @apply absolute top-10 border border-accents-1 shadow-lg w-56 h-auto; } } diff --git a/components/ui/Grid/Grid.module.css b/components/ui/Grid/Grid.module.css index d3302fc61..9b331be8f 100644 --- a/components/ui/Grid/Grid.module.css +++ b/components/ui/Grid/Grid.module.css @@ -3,10 +3,6 @@ @apply grid grid-cols-1 gap-0; min-height: var(--row-height); - @screen lg { - @apply grid-cols-3 grid-rows-2; - } - & > * { @apply row-span-1 bg-transparent box-border overflow-hidden; height: 500px; @@ -19,6 +15,17 @@ } } +@screen lg { + .root { + @apply grid-cols-3 grid-rows-2; + } + + .root & > * { + @apply col-span-1; + height: inherit; + } +} + .default { & > * { @apply bg-transparent; diff --git a/components/ui/Hero/Hero.module.css b/components/ui/Hero/Hero.module.css index 364ad6580..c2032c8ae 100644 --- a/components/ui/Hero/Hero.module.css +++ b/components/ui/Hero/Hero.module.css @@ -1,6 +1,9 @@ .root { @apply mx-auto grid grid-cols-1 py-32 gap-4; - @screen md { +} + +@screen md { + .root { @apply grid-cols-2; } } diff --git a/framework/commerce/config.js b/framework/commerce/config.js index 2dd3284bc..dc20b518e 100644 --- a/framework/commerce/config.js +++ b/framework/commerce/config.js @@ -7,7 +7,7 @@ const fs = require('fs') const merge = require('deepmerge') const prettier = require('prettier') -const PROVIDERS = ['bigcommerce', 'shopify'] +const PROVIDERS = ['bigcommerce', 'shopify', 'swell'] function getProviderName() { return ( @@ -16,6 +16,8 @@ function getProviderName() { ? 'bigcommerce' : process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN ? 'shopify' + : process.env.NEXT_PUBLIC_SWELL_STORE_ID + ? 'swell' : null) ) } diff --git a/yarn.lock b/yarn.lock index 45181fd21..7ea915b60 100644 --- a/yarn.lock +++ b/yarn.lock @@ -968,6 +968,19 @@ dependencies: defer-to-connect "^1.0.1" +"@tailwindcss/jit@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/jit/-/jit-0.1.3.tgz#50390d9ac95fee78ed23a7e2320ef20bd4a35354" + integrity sha512-7VAvHKNLJxbGWRKxo2Z+beiodag7vWPx8b/+Egd5fve4zFihsngeNt6RwQFnll+almjppRYefRC5Py5v5K+6vg== + dependencies: + chokidar "^3.5.1" + dlv "^1.1.3" + fast-glob "^3.2.5" + lodash.topath "^4.5.2" + object-hash "^2.1.1" + postcss-selector-parser "^6.0.4" + quick-lru "^5.1.1" + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -993,32 +1006,6 @@ resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.0.tgz#14f854c0f93d326e39da6e3b6f34f7d37513d108" integrity sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg== -"@types/eslint-scope@^3.7.0": - version "3.7.0" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.0.tgz#4792816e31119ebd506902a482caec4951fabd86" - integrity sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw== - dependencies: - "@types/eslint" "*" - "@types/estree" "*" - -"@types/eslint@*": - version "7.2.6" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.6.tgz#5e9aff555a975596c03a98b59ecd103decc70c3c" - integrity sha512-I+1sYH+NPQ3/tVqCeUSBwTE/0heyvtXqpIopUUArlBm0Kpocb8FbMa3AZ/ASKIFpN3rnEx932TTXDbt9OXsNDw== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - -"@types/estree@*": - version "0.0.46" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe" - integrity sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg== - -"@types/estree@^0.0.45": - version "0.0.45" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884" - integrity sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g== - "@types/http-proxy-agent@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@types/http-proxy-agent/-/http-proxy-agent-2.0.2.tgz#942c1f35c7e1f0edd1b6ffae5d0f9051cfb32be1" @@ -1036,11 +1023,6 @@ resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.6.tgz#7f10c926aa41e189a2755c4c7fcf8e4573bd7ac1" integrity sha512-cK4XqrLvP17X6c0C8n4iTbT59EixqyXL3Fk8/Rsk4dF3oX4dg70gYUXrXVUUHpnsGMPNlTQMqf+TVmNPX6FmSQ== -"@types/json-schema@*", "@types/json-schema@^7.0.6": - version "7.0.7" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" - integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== - "@types/json-stable-stringify@^1.0.32": version "1.0.32" resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.32.tgz#121f6917c4389db3923640b2e68de5fa64dda88e" @@ -1186,161 +1168,6 @@ agentkeepalive "3.4.1" debug "3.1.0" -"@webassemblyjs/ast@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.1.tgz#76c6937716d68bf1484c15139f5ed30b9abc8bb4" - integrity sha512-uMu1nCWn2Wxyy126LlGqRVlhdTOsO/bsBRI4dNq3+6SiSuRKRQX6ejjKgh82LoGAPSq72lDUiQ4FWVaf0PecYw== - dependencies: - "@webassemblyjs/helper-module-context" "1.9.1" - "@webassemblyjs/helper-wasm-bytecode" "1.9.1" - "@webassemblyjs/wast-parser" "1.9.1" - -"@webassemblyjs/floating-point-hex-parser@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.1.tgz#9eb0ff90a1cdeef51f36ba533ed9f06b5cdadd09" - integrity sha512-5VEKu024RySmLKTTBl9q1eO/2K5jk9ZS+2HXDBLA9s9p5IjkaXxWiDb/+b7wSQp6FRdLaH1IVGIfOex58Na2pg== - -"@webassemblyjs/helper-api-error@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.1.tgz#ad89015c4246cd7f5ed0556700237f8b9c2c752f" - integrity sha512-y1lGmfm38djrScwpeL37rRR9f1D6sM8RhMpvM7CYLzOlHVboouZokXK/G88BpzW0NQBSvCCOnW5BFhten4FPfA== - -"@webassemblyjs/helper-buffer@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.1.tgz#186e67ac25f9546ea7939759413987f157524133" - integrity sha512-uS6VSgieHbk/m4GSkMU5cqe/5TekdCzQso4revCIEQ3vpGZgqSSExi4jWpTWwDpAHOIAb1Jfrs0gUB9AA4n71w== - -"@webassemblyjs/helper-code-frame@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.1.tgz#aab177b7cc87a318a8f8664ad68e2c3828ebc42b" - integrity sha512-ZQ2ZT6Evk4DPIfD+92AraGYaFIqGm4U20e7FpXwl7WUo2Pn1mZ1v8VGH8i+Y++IQpxPbQo/UyG0Khs7eInskzA== - dependencies: - "@webassemblyjs/wast-printer" "1.9.1" - -"@webassemblyjs/helper-fsm@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.1.tgz#527e91628e84d13d3573884b3dc4c53a81dcb911" - integrity sha512-J32HGpveEqqcKFS0YbgicB0zAlpfIxJa5MjxDxhu3i5ltPcVfY5EPvKQ1suRguFPehxiUs+/hfkwPEXom/l0lw== - -"@webassemblyjs/helper-module-context@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.1.tgz#778670b3d471f7cf093d1e7c0dde431b54310e16" - integrity sha512-IEH2cMmEQKt7fqelLWB5e/cMdZXf2rST1JIrzWmf4XBt3QTxGdnnLvV4DYoN8pJjOx0VYXsWg+yF16MmJtolZg== - dependencies: - "@webassemblyjs/ast" "1.9.1" - -"@webassemblyjs/helper-wasm-bytecode@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.1.tgz#563f59bcf409ccf469edde168b9426961ffbf6df" - integrity sha512-i2rGTBqFUcSXxyjt2K4vm/3kkHwyzG6o427iCjcIKjOqpWH8SEem+xe82jUk1iydJO250/CvE5o7hzNAMZf0dQ== - -"@webassemblyjs/helper-wasm-section@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.1.tgz#f7988f94c12b01b99a16120cb01dc099b00e4798" - integrity sha512-FetqzjtXZr2d57IECK+aId3D0IcGweeM0CbAnJHkYJkcRTHP+YcMb7Wmc0j21h5UWBpwYGb9dSkK/93SRCTrGg== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-buffer" "1.9.1" - "@webassemblyjs/helper-wasm-bytecode" "1.9.1" - "@webassemblyjs/wasm-gen" "1.9.1" - -"@webassemblyjs/ieee754@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.1.tgz#3b715871ca7d75784717cf9ceca9d7b81374b8af" - integrity sha512-EvTG9M78zP1MmkBpUjGQHZc26DzPGZSLIPxYHCjQsBMo60Qy2W34qf8z0exRDtxBbRIoiKa5dFyWer/7r1aaSQ== - dependencies: - "@xtuc/ieee754" "^1.2.0" - -"@webassemblyjs/leb128@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.1.tgz#b2ecaa39f9e8277cc9c707c1ca8b2aa7b27d0b72" - integrity sha512-Oc04ub0vFfLnF+2/+ki3AE+anmW4sv9uNBqb+79fgTaPv6xJsOT0dhphNfL3FrME84CbX/D1T9XT8tjFo0IIiw== - dependencies: - "@xtuc/long" "4.2.2" - -"@webassemblyjs/utf8@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.1.tgz#d02d9daab85cda3211e43caf31dca74c260a73b0" - integrity sha512-llkYtppagjCodFjo0alWOUhAkfOiQPQDIc5oA6C9sFAXz7vC9QhZf/f8ijQIX+A9ToM3c9Pq85X0EX7nx9gVhg== - -"@webassemblyjs/wasm-edit@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.1.tgz#e27a6bdbf78e5c72fa812a2fc3cbaad7c3e37578" - integrity sha512-S2IaD6+x9B2Xi8BCT0eGsrXXd8UxAh2LVJpg1ZMtHXnrDcsTtIX2bDjHi40Hio6Lc62dWHmKdvksI+MClCYbbw== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-buffer" "1.9.1" - "@webassemblyjs/helper-wasm-bytecode" "1.9.1" - "@webassemblyjs/helper-wasm-section" "1.9.1" - "@webassemblyjs/wasm-gen" "1.9.1" - "@webassemblyjs/wasm-opt" "1.9.1" - "@webassemblyjs/wasm-parser" "1.9.1" - "@webassemblyjs/wast-printer" "1.9.1" - -"@webassemblyjs/wasm-gen@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.1.tgz#56a0787d1fa7994fdc7bea59004e5bec7189c5fc" - integrity sha512-bqWI0S4lBQsEN5FTZ35vYzfKUJvtjNnBobB1agCALH30xNk1LToZ7Z8eiaR/Z5iVECTlBndoRQV3F6mbEqE/fg== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-wasm-bytecode" "1.9.1" - "@webassemblyjs/ieee754" "1.9.1" - "@webassemblyjs/leb128" "1.9.1" - "@webassemblyjs/utf8" "1.9.1" - -"@webassemblyjs/wasm-opt@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.1.tgz#fbdf8943a825e6dcc4cd69c3e092289fa4aec96c" - integrity sha512-gSf7I7YWVXZ5c6XqTEqkZjVs8K1kc1k57vsB6KBQscSagDNbAdxt6MwuJoMjsE1yWY1tsuL+pga268A6u+Fdkg== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-buffer" "1.9.1" - "@webassemblyjs/wasm-gen" "1.9.1" - "@webassemblyjs/wasm-parser" "1.9.1" - -"@webassemblyjs/wasm-parser@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.1.tgz#5e8352a246d3f605312c8e414f7990de55aaedfa" - integrity sha512-ImM4N2T1MEIond0MyE3rXvStVxEmivQrDKf/ggfh5pP6EHu3lL/YTAoSrR7shrbKNPpeKpGesW1LIK/L4kqduw== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-api-error" "1.9.1" - "@webassemblyjs/helper-wasm-bytecode" "1.9.1" - "@webassemblyjs/ieee754" "1.9.1" - "@webassemblyjs/leb128" "1.9.1" - "@webassemblyjs/utf8" "1.9.1" - -"@webassemblyjs/wast-parser@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.1.tgz#e25ef13585c060073c1db0d6bd94340fdeee7596" - integrity sha512-2xVxejXSvj3ls/o2TR/zI6p28qsGupjHhnHL6URULQRcXmryn3w7G83jQMcT7PHqUfyle65fZtWLukfdLdE7qw== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/floating-point-hex-parser" "1.9.1" - "@webassemblyjs/helper-api-error" "1.9.1" - "@webassemblyjs/helper-code-frame" "1.9.1" - "@webassemblyjs/helper-fsm" "1.9.1" - "@xtuc/long" "4.2.2" - -"@webassemblyjs/wast-printer@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.1.tgz#b9f38e93652037d4f3f9c91584635af4191ed7c1" - integrity sha512-tDV8V15wm7mmbAH6XvQRU1X+oPGmeOzYsd6h7hlRLz6QpV4Ec/KKxM8OpLtFmQPLCreGxTp+HuxtH4pRIZyL9w== - dependencies: - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/wast-parser" "1.9.1" - "@xtuc/long" "4.2.2" - -"@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== - -"@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== - "@zeit/dns-cached-resolve@2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@zeit/dns-cached-resolve/-/dns-cached-resolve-2.1.0.tgz#78583010df1683fdb7b05949b75593c9a8641bc1" @@ -1403,12 +1230,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.12.5, ajv@^6.12.6: +ajv@^6.12.6: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -1812,7 +1634,7 @@ browserslist@4.16.1: escalade "^3.1.1" node-releases "^1.1.69" -browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.1, browserslist@^4.6.4: +browserslist@^4.12.0, browserslist@^4.16.1, browserslist@^4.6.4: version "4.16.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717" integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== @@ -1994,7 +1816,7 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chokidar@3.5.1, chokidar@^3.4.3: +chokidar@3.5.1, chokidar@^3.4.3, chokidar@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== @@ -2009,13 +1831,6 @@ chokidar@3.5.1, chokidar@^3.4.3: optionalDependencies: fsevents "~2.3.1" -chrome-trace-event@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" - integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== - dependencies: - tslib "^1.9.0" - ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -2159,6 +1974,11 @@ colorette@^1.2.1: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== +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== + combined-stream@^1.0.6, combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -2166,7 +1986,7 @@ combined-stream@^1.0.6, combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -commander@^2.13.0, commander@^2.16.0, commander@^2.20.0, commander@^2.20.3, commander@^2.8.1: +commander@^2.13.0, commander@^2.16.0, commander@^2.20.3, commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -2699,6 +2519,11 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" @@ -2801,14 +2626,6 @@ enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: memory-fs "^0.5.0" tapable "^1.0.0" -enhanced-resolve@^5.3.1: - version "5.7.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz#525c5d856680fbd5052de453ac83e32049958b5c" - integrity sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" - enquirer@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" @@ -2881,14 +2698,6 @@ escodegen@^1.8.0: optionalDependencies: source-map "~0.6.1" -eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - eslint-visitor-keys@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" @@ -2899,23 +2708,11 @@ esprima@4.0.1, esprima@^4.0.0, esprima@^4.0.1: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== - esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -2926,7 +2723,7 @@ etag@1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -events@^3.0.0, events@^3.2.0: +events@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== @@ -2980,7 +2777,7 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.1.1: +fast-glob@^3.1.1, fast-glob@^3.2.5: version "3.2.5" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== @@ -3237,6 +3034,21 @@ get-stream@^5.0.0, get-stream@^5.1.0: dependencies: pump "^3.0.0" +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= + dependencies: + is-glob "^2.0.0" + glob-parent@^5.1.0, glob-parent@~5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" @@ -3302,7 +3114,7 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== @@ -3672,6 +3484,16 @@ is-date-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -3701,6 +3523,13 @@ is-glob@4.0.1, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-glob@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= + dependencies: + is-extglob "^1.0.0" + is-interactive@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" @@ -3852,15 +3681,6 @@ jest-worker@24.9.0: merge-stream "^2.0.0" supports-color "^6.1.0" -jest-worker@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" - integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^7.0.0" - js-cookie@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" @@ -3889,11 +3709,6 @@ json-buffer@3.0.0: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= -json-parse-better-errors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - json-parse-even-better-errors@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -4105,11 +3920,6 @@ listr@^0.14.3: p-map "^2.0.0" rxjs "^6.3.3" -loader-runner@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384" - integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== - loader-utils@1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" @@ -4227,11 +4037,21 @@ lodash.toarray@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= +lodash.topath@^4.5.2: + version "4.5.2" + resolved "https://registry.yarnpkg.com/lodash.topath/-/lodash.topath-4.5.2.tgz#3616351f3bba61994a0931989660bd03254fd009" + integrity sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak= + lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@~4.17.20: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + log-symbols@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" @@ -4438,7 +4258,7 @@ mime-db@1.45.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== -mime-types@^2.1.12, mime-types@^2.1.27: +mime-types@^2.1.12: version "2.1.28" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ== @@ -4556,6 +4376,11 @@ nanoid@^3.1.16, nanoid@^3.1.20: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== +nanoid@^3.1.23: + version "3.1.23" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" + integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== + native-url@0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/native-url/-/native-url-0.3.4.tgz#29c943172aed86c63cee62c8c04db7f5756661f8" @@ -4563,11 +4388,6 @@ native-url@0.3.4: dependencies: querystring "^0.2.0" -neo-async@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - next-seo@^4.11.0: version "4.19.0" resolved "https://registry.yarnpkg.com/next-seo/-/next-seo-4.19.0.tgz#b3be79a544e420dbbf1e4fcff98dad9790f15e36" @@ -4877,7 +4697,7 @@ p-limit@3.0.2: dependencies: p-try "^2.0.0" -p-limit@3.1.0, p-limit@^3.0.2, p-limit@^3.1.0: +p-limit@3.1.0, p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -4972,6 +4792,16 @@ parse-filepath@^1.0.2: map-cache "^0.2.0" path-root "^0.1.1" +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + parse-json@^5.0.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" @@ -5298,10 +5128,10 @@ postcss-media-minmax@^4.0.0: dependencies: postcss "^7.0.2" -postcss-nested@^5.0.1: - version "5.0.3" - resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-5.0.3.tgz#2f46d77a06fc98d9c22344fd097396f5431386db" - integrity sha512-R2LHPw+u5hFfDgJG748KpGbJyTv7Yr33/2tIMWxquYuHTd9EXu27PYnKi7BxMXLtzKC0a0WVsqHtd7qIluQu/g== +postcss-nested@5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-5.0.5.tgz#f0a107d33a9fab11d7637205f5321e27223e3603" + integrity sha512-GSRXYz5bccobpTzLQZXOnSOfKl6TwVr5CyAQJUPub4nuRJSOECK5AqurxVgmtxP48p0Kc/ndY/YyS1yqldX0Ew== dependencies: postcss-selector-parser "^6.0.4" @@ -5483,7 +5313,7 @@ postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.26, postcss@^7.0. source-map "^0.6.1" supports-color "^6.1.0" -postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.6: +postcss@^8.1.6, postcss@^8.2.1: version "8.2.6" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.6.tgz#5d69a974543b45f87e464bc4c3e392a97d6be9fe" integrity sha512-xpB8qYxgPuly166AGlpRjUdEYtmOWx2iCwGmrv4vqZL9YPVviDVPZPRXxnXr6xPZOdxQ9lp3ZBFCRgWJ7LE3Sg== @@ -5492,6 +5322,15 @@ postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.6: nanoid "^3.1.20" source-map "^0.6.1" +postcss@^8.2.6: + version "8.3.0" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.0.tgz#b1a713f6172ca427e3f05ef1303de8b65683325f" + integrity sha512-+ogXpdAjWGa+fdYY5BQ96V/6tAo+TdSSIMP5huJBIygdWwKtVoB5JWZ7yUd4xZ8r+8Kvvx4nyg/PQ071H4UtcQ== + dependencies: + colorette "^1.2.2" + nanoid "^3.1.23" + source-map-js "^0.6.2" + precinct@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/precinct/-/precinct-6.3.1.tgz#8ad735a8afdfc48b56ed39c9ad3bf999b6b928dc" @@ -5639,7 +5478,12 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== @@ -5888,7 +5732,7 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve@^1.10.0, resolve@^1.11.1, resolve@^1.19.0: +resolve@^1.10.0, resolve@^1.11.1, 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== @@ -5986,15 +5830,6 @@ scheduler@^0.20.1: loose-envify "^1.1.0" object-assign "^4.1.1" -schema-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" - integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== - dependencies: - "@types/json-schema" "^7.0.6" - ajv "^6.12.5" - ajv-keywords "^3.5.2" - scuid@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/scuid/-/scuid-1.1.0.tgz#d3f9f920956e737a60f72d0e4ad280bf324d5dab" @@ -6027,13 +5862,6 @@ semver@^7.3.2: dependencies: lru-cache "^6.0.0" -serialize-javascript@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" - integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== - dependencies: - randombytes "^2.1.0" - set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -6138,12 +5966,12 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -source-list-map@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" - integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== +source-map-js@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" + integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== -source-map-support@^0.5.17, source-map-support@~0.5.19: +source-map-support@^0.5.17: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -6151,7 +5979,7 @@ source-map-support@^0.5.17, source-map-support@~0.5.19: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@0.7.3, source-map@~0.7.2: +source-map@0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== @@ -6429,7 +6257,7 @@ supports-color@^6.1.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -6462,67 +6290,48 @@ tabbable@^5.1.5: integrity sha512-oVAPrWgLLqrbvQE8XqcU7CVBq6SQbaIbHkhOca3u7/jzuQvyZycrUKPCGr04qpEIUslmUlULbSeN+m3QrKEykA== tailwindcss@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.0.3.tgz#f8d07797d1f89dc4b171673c26237b58783c2c86" - integrity sha512-s8NEqdLBiVbbdL0a5XwTb8jKmIonOuI4RMENEcKLR61jw6SdKvBss7NWZzwCaD+ZIjlgmesv8tmrjXEp7C0eAQ== + version "2.1.2" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.1.2.tgz#29402bf73a445faedd03df6d3b177e7b52b7c4a1" + integrity sha512-T5t+wwd+/hsOyRw2HJuFuv0LTUm3MUdHm2DJ94GPVgzqwPPFa9XxX0KlwLWupUuiOUj6uiKURCzYPHFcuPch/w== dependencies: "@fullhuman/postcss-purgecss" "^3.1.3" bytes "^3.0.0" chalk "^4.1.0" + chokidar "^3.5.1" color "^3.1.3" detective "^5.2.0" didyoumean "^1.2.1" + dlv "^1.1.3" + fast-glob "^3.2.5" fs-extra "^9.1.0" html-tags "^3.1.0" - lodash "^4.17.20" + lodash "^4.17.21" + lodash.topath "^4.5.2" modern-normalize "^1.0.0" node-emoji "^1.8.1" + normalize-path "^3.0.0" object-hash "^2.1.1" + parse-glob "^3.0.4" postcss-functions "^3" postcss-js "^3.0.3" - postcss-nested "^5.0.1" + postcss-nested "5.0.5" postcss-selector-parser "^6.0.4" postcss-value-parser "^4.1.0" pretty-hrtime "^1.0.3" + quick-lru "^5.1.1" reduce-css-calc "^2.1.8" - resolve "^1.19.0" + resolve "^1.20.0" tapable@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tapable@^2.1.1, tapable@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" - integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== - temp@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/temp/-/temp-0.4.0.tgz#671ad63d57be0fe9d7294664b3fc400636678a60" integrity sha1-ZxrWPVe+D+nXKUZks/xABjZnimA= -terser-webpack-plugin@^5.0.3: - version "5.1.1" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.1.tgz#7effadee06f7ecfa093dbbd3e9ab23f5f3ed8673" - integrity sha512-5XNNXZiR8YO6X6KhSGXfY0QrGrCRlSwAEjIIrlRQR4W8nP69TaJUlh3bkuac6zzgspiGPfKEHcY295MMVExl5Q== - dependencies: - jest-worker "^26.6.2" - p-limit "^3.1.0" - schema-utils "^3.0.0" - serialize-javascript "^5.0.1" - source-map "^0.6.1" - terser "^5.5.1" - -terser@^5.5.1: - version "5.6.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.6.0.tgz#138cdf21c5e3100b1b3ddfddf720962f88badcd2" - integrity sha512-vyqLMoqadC1uR0vywqOZzriDYzgEkNJFK4q9GeyOBHIbiECHiWLKcWfbQWAUaPfxkjDhapSlZB9f7fkMrvkVjA== - dependencies: - commander "^2.20.0" - source-map "~0.7.2" - source-map-support "~0.5.19" - through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -6825,7 +6634,7 @@ warning@^4.0.3: dependencies: loose-envify "^1.0.0" -watchpack@2.1.1, watchpack@^2.0.0: +watchpack@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.1.1.tgz#e99630550fca07df9f90a06056987baa40a689c7" integrity sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw== @@ -6860,44 +6669,6 @@ webpack-bundle-analyzer@4.3.0: sirv "^1.0.7" ws "^7.3.1" -webpack-sources@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac" - integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w== - dependencies: - source-list-map "^2.0.1" - source-map "^0.6.1" - -webpack@5.11.1: - version "5.11.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.11.1.tgz#39b2b9daeb5c6c620e03b7556ec674eaed4016b4" - integrity sha512-tNUIdAmYJv+nupRs/U/gqmADm6fgrf5xE+rSlSsf2PgsGO7j2WG7ccU6AWNlOJlHFl+HnmXlBmHIkiLf+XA9mQ== - dependencies: - "@types/eslint-scope" "^3.7.0" - "@types/estree" "^0.0.45" - "@webassemblyjs/ast" "1.9.1" - "@webassemblyjs/helper-module-context" "1.9.1" - "@webassemblyjs/wasm-edit" "1.9.1" - "@webassemblyjs/wasm-parser" "1.9.1" - acorn "^8.0.4" - browserslist "^4.14.5" - chrome-trace-event "^1.0.2" - enhanced-resolve "^5.3.1" - eslint-scope "^5.1.1" - events "^3.2.0" - glob-to-regexp "^0.4.1" - graceful-fs "^4.2.4" - json-parse-better-errors "^1.0.2" - loader-runner "^4.1.0" - mime-types "^2.1.27" - neo-async "^2.6.2" - pkg-dir "^5.0.0" - schema-utils "^3.0.0" - tapable "^2.1.1" - terser-webpack-plugin "^5.0.3" - watchpack "^2.0.0" - webpack-sources "^2.1.1" - whatwg-fetch@^3.4.1: version "3.5.0" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz#605a2cd0a7146e5db141e29d1c62ab84c0c4c868" From a84bc462d7a052ad9c17d27cdec895d12733d5bf Mon Sep 17 00:00:00 2001 From: okbel <curciobel@gmail.com> Date: Wed, 26 May 2021 18:32:15 -0300 Subject: [PATCH 219/221] Jit --- postcss.config.js | 2 +- yarn.lock | 57 ----------------------------------------------- 2 files changed, 1 insertion(+), 58 deletions(-) diff --git a/postcss.config.js b/postcss.config.js index 9bff627f2..9e0f0b2ca 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,6 +1,6 @@ module.exports = { plugins: [ - '@tailwindcss/jit', + 'tailwindcss', 'postcss-nesting', 'postcss-flexbugs-fixes', [ diff --git a/yarn.lock b/yarn.lock index e8110bef3..8c96bd2a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4089,7 +4089,6 @@ lodash.topath@^4.5.2: resolved "https://registry.yarnpkg.com/lodash.topath/-/lodash.topath-4.5.2.tgz#3616351f3bba61994a0931989660bd03254fd009" integrity sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak= -<<<<<<< HEAD lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -4100,18 +4099,11 @@ lodash@4.17.21, lodash@^4.17.21: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -======= ->>>>>>> 4f3674aafa2fa83495d4f17b30207fecd6b0e1cc lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@~4.17.20: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== -lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - log-symbols@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" @@ -4443,11 +4435,6 @@ nanoid@^3.1.16, nanoid@^3.1.20: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== -nanoid@^3.1.23: - version "3.1.23" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" - integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== - native-url@0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/native-url/-/native-url-0.3.4.tgz#29c943172aed86c63cee62c8c04db7f5756661f8" @@ -5217,11 +5204,7 @@ postcss-media-minmax@^4.0.0: dependencies: postcss "^7.0.2" -<<<<<<< HEAD postcss-nested@^5.0.5: -======= -postcss-nested@5.0.5: ->>>>>>> 4f3674aafa2fa83495d4f17b30207fecd6b0e1cc version "5.0.5" resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-5.0.5.tgz#f0a107d33a9fab11d7637205f5321e27223e3603" integrity sha512-GSRXYz5bccobpTzLQZXOnSOfKl6TwVr5CyAQJUPub4nuRJSOECK5AqurxVgmtxP48p0Kc/ndY/YyS1yqldX0Ew== @@ -5415,7 +5398,6 @@ postcss@^8.1.6, postcss@^8.2.1: nanoid "^3.1.20" source-map "^0.6.1" -<<<<<<< HEAD postcss@^8.2.8: version "8.2.8" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.8.tgz#0b90f9382efda424c4f0f69a2ead6f6830d08ece" @@ -5424,16 +5406,6 @@ postcss@^8.2.8: colorette "^1.2.2" nanoid "^3.1.20" source-map "^0.6.1" -======= -postcss@^8.2.6: - version "8.3.0" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.0.tgz#b1a713f6172ca427e3f05ef1303de8b65683325f" - integrity sha512-+ogXpdAjWGa+fdYY5BQ96V/6tAo+TdSSIMP5huJBIygdWwKtVoB5JWZ7yUd4xZ8r+8Kvvx4nyg/PQ071H4UtcQ== - dependencies: - colorette "^1.2.2" - nanoid "^3.1.23" - source-map-js "^0.6.2" ->>>>>>> 4f3674aafa2fa83495d4f17b30207fecd6b0e1cc precinct@^6.3.1: version "6.3.1" @@ -6075,14 +6047,6 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -<<<<<<< HEAD -======= -source-map-js@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" - integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== - ->>>>>>> 4f3674aafa2fa83495d4f17b30207fecd6b0e1cc source-map-support@^0.5.17: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" @@ -6414,50 +6378,29 @@ tabbable@^5.1.5: resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-5.1.5.tgz#efec48ede268d511c261e3b81facbb4782a35147" integrity sha512-oVAPrWgLLqrbvQE8XqcU7CVBq6SQbaIbHkhOca3u7/jzuQvyZycrUKPCGr04qpEIUslmUlULbSeN+m3QrKEykA== -<<<<<<< HEAD tailwindcss@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.0.4.tgz#cf13e62738c3a27065664e449d93b66ee2945506" integrity sha512-WhgR0oiBxGOZ9jY0yVfaJCHnckR7U74Fs/BMsYxGdwGJQ5Hd/HlaKD26bEJFZOvYScJo0QcUj2ImldzedsG7Bw== -======= -tailwindcss@^2.0.3: - version "2.1.2" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.1.2.tgz#29402bf73a445faedd03df6d3b177e7b52b7c4a1" - integrity sha512-T5t+wwd+/hsOyRw2HJuFuv0LTUm3MUdHm2DJ94GPVgzqwPPFa9XxX0KlwLWupUuiOUj6uiKURCzYPHFcuPch/w== ->>>>>>> 4f3674aafa2fa83495d4f17b30207fecd6b0e1cc dependencies: "@fullhuman/postcss-purgecss" "^3.1.3" bytes "^3.0.0" chalk "^4.1.0" - chokidar "^3.5.1" color "^3.1.3" detective "^5.2.0" didyoumean "^1.2.1" - dlv "^1.1.3" - fast-glob "^3.2.5" fs-extra "^9.1.0" html-tags "^3.1.0" lodash "^4.17.21" -<<<<<<< HEAD -======= - lodash.topath "^4.5.2" ->>>>>>> 4f3674aafa2fa83495d4f17b30207fecd6b0e1cc modern-normalize "^1.0.0" node-emoji "^1.8.1" - normalize-path "^3.0.0" object-hash "^2.1.1" - parse-glob "^3.0.4" postcss-functions "^3" postcss-js "^3.0.3" -<<<<<<< HEAD postcss-nested "^5.0.5" -======= - postcss-nested "5.0.5" ->>>>>>> 4f3674aafa2fa83495d4f17b30207fecd6b0e1cc postcss-selector-parser "^6.0.4" postcss-value-parser "^4.1.0" pretty-hrtime "^1.0.3" - quick-lru "^5.1.1" reduce-css-calc "^2.1.8" resolve "^1.20.0" From 50be2a7e6e6cc34301811b27cd3522e2353e91a0 Mon Sep 17 00:00:00 2001 From: B <curciobelen@gmail.com> Date: Wed, 26 May 2021 18:57:03 -0300 Subject: [PATCH 220/221] Removing Blog and changes in Footer (#328) --- components/common/Footer/Footer.tsx | 14 ----- components/ui/Hero/Hero.tsx | 2 +- pages/blog.tsx | 97 ----------------------------- 3 files changed, 1 insertion(+), 112 deletions(-) delete mode 100644 pages/blog.tsx diff --git a/components/common/Footer/Footer.tsx b/components/common/Footer/Footer.tsx index 75b2806ef..5fb9ede58 100644 --- a/components/common/Footer/Footer.tsx +++ b/components/common/Footer/Footer.tsx @@ -44,20 +44,6 @@ const Footer: FC<Props> = ({ className, pages }) => { </a> </Link> </li> - <li className="py-3 md:py-0 md:pb-4"> - <Link href="/"> - <a className="text-primary hover:text-accents-6 transition ease-in-out duration-150"> - Careers - </a> - </Link> - </li> - <li className="py-3 md:py-0 md:pb-4"> - <Link href="/blog"> - <a className="text-primary hover:text-accents-6 transition ease-in-out duration-150"> - Blog - </a> - </Link> - </li> {sitePages.map((page) => ( <li key={page.url} className="py-3 md:py-0 md:pb-4"> <Link href={page.url!}> diff --git a/components/ui/Hero/Hero.tsx b/components/ui/Hero/Hero.tsx index 2e1f124ae..1802f9ee8 100644 --- a/components/ui/Hero/Hero.tsx +++ b/components/ui/Hero/Hero.tsx @@ -21,7 +21,7 @@ const Hero: FC<Props> = ({ headline, description }) => { <p className="mt-5 text-xl leading-7 text-accent-2 text-white"> {description} </p> - <Link href="/blog"> + <Link href="/"> <a className="text-white pt-3 font-bold hover:underline flex flex-row cursor-pointer w-max-content"> Read it here <RightArrow width="20" heigh="20" className="ml-1" /> diff --git a/pages/blog.tsx b/pages/blog.tsx deleted file mode 100644 index eca3b2295..000000000 --- a/pages/blog.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import type { GetStaticPropsContext } from 'next' -import { getConfig } from '@framework/api' -import getAllPages from '@framework/common/get-all-pages' -import { Layout } from '@components/common' -import { Container } from '@components/ui' - -export async function getStaticProps({ - preview, - locale, -}: GetStaticPropsContext) { - const config = getConfig({ locale }) - const { pages } = await getAllPages({ config, preview }) - return { - props: { pages }, - } -} - -export default function Blog() { - return ( - <div className="pb-20"> - <div className="text-center pt-40 pb-56 bg-violet"> - <Container> - <h2 className="text-4xl tracking-tight leading-10 font-extrabold text-white sm:text-5xl sm:leading-none md:text-6xl"> - Welcome to Acme, the simplest way to start publishing with Next.js - </h2> - <p className="mt-3 max-w-md mx-auto text-gray-100 sm:text-lg md:mt-5 md:text-xl md:max-w-3xl"> - The Yeezy BOOST 350 V2 lineup continues to grow. We recently had the - ‘Carbon’ iteration, and now release details have been locked in for - this ‘Natural’ joint. Revealed by Yeezy Mafia earlier this year, the - shoe was originally called ‘Abez’, which translated to ‘Tin’ in - Hebrew. It’s now undergone a name change, and will be referred to as - ‘Natura` - </p> - <div className="mt-5 max-w-md mx-auto sm:flex sm:justify-center md:mt-12"> - <div className="flex"> - <div className="flex-shrink-0 inline-flex rounded-full border-2 border-white"> - <img - className="h-12 w-12 rounded-full" - src="https://vercel.com/api/www/avatar/61182a9f6bda512b4d9263c9c8a60aabe0402f4c?s=204" - alt="Avatar" - /> - </div> - <div className="ml-4"> - <div className="leading-6 font-medium text-white"> - José Rodriguez - </div> - <div className="leading-6 font-medium text-gray-200"> - CEO, Acme - </div> - </div> - </div> - </div> - </Container> - </div> - <Container> - <div className="-mt-96 mx-auto"> - <img src="/jacket.png" alt="Jacket" /> - </div> - {/** Replace by HTML Content */} - <div className="text-lg leading-7 font-medium py-6 text-justify max-w-6xl mx-auto"> - <p className="py-6"> - Biscuit oat cake wafer icing ice cream tiramisu pudding cupcake. - Candy canes bonbon dragée jujubes chocolate bar. Cotton candy gummi - bears toffee cake muffin caramels. Gummi bears danish liquorice ice - cream pie chocolate cake lemon drops tootsie roll tart. Biscuit - gingerbread fruitcake cake powder pudding cotton candy chocolate - bar. Sweet donut marshmallow powder gummies jelly tart powder. - Cheesecake bonbon caramels cupcake jujubes halvah donut dessert - chocolate bar. Jelly gummies liquorice lollipop chocolate bar - chocolate cake sugar plum. Lollipop toffee dragée chocolate bar - jelly beans biscuit. Halvah danish cheesecake. Tiramisu donut - lollipop pie donut caramels tiramisu. Jujubes candy canes pudding - danish fruitcake chupa chups jujubes carrot cake bonbon. Halvah - donut jelly halvah bonbon. - </p> - <p className="py-6"> - Biscuit sugar plum sweet chocolate cake sesame snaps soufflé - topping. Gummies topping bonbon chocolate pudding cookie. Wafer - icing cake pastry. Gummies candy dessert chupa chups lemon drops. - Soufflé marshmallow oat cake chocolate jelly-o caramels pie marzipan - jelly beans. Cheesecake liquorice donut jujubes halvah ice cream - cotton candy cupcake sugar plum. Ice cream ice cream sweet roll - fruitcake icing. Muffin candy canes bonbon croissant gummies lemon - drops pie danish. Oat cake chocolate toffee cake jelly tart - caramels. Sweet donut cheesecake pastry pie sweet. Bonbon lollipop - brownie. Soufflé pudding macaroon cotton candy gingerbread. Biscuit - macaroon gummi bears candy canes chocolate cake lemon drops - marshmallow. Chocolate cake cotton candy marshmallow cake sweet - tootsie roll bonbon carrot cake sugar plum. - </p> - </div> - </Container> - </div> - ) -} - -Blog.Layout = Layout From 8fb6c7b206472d0fa0a79f4097f783934c409ee3 Mon Sep 17 00:00:00 2001 From: B <curciobelen@gmail.com> Date: Wed, 26 May 2021 19:14:34 -0300 Subject: [PATCH 221/221] Update README.md --- README.md | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 941b1699b..4e7a1aa1e 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,9 @@ Start right now at [nextjs.org/commerce](https://nextjs.org/commerce) Demo live at: [demo.vercel.store](https://demo.vercel.store/) -- Shopify Demo: https://shopify.demo.vercel.store/ -- BigCommerce Demo: https://bigcommerce.demo.vercel.store/ +- Shopify Demo: https://shopify.vercel.store/ +- Swell Demo: https://swell.vercel.store/ +- BigCommerce Demo: https://bigcommerce.vercel.store/ ## Features @@ -40,6 +41,22 @@ Next.js Commerce integrates out-of-the-box with BigCommerce and Shopify. We plan Open `.env.local` and change the value of `COMMERCE_PROVIDER` to the provider you would like to use, then set the environment variables for that provider (use `.env.template` as the base). +The setup for Shopify would look like this for example: + +``` +COMMERCE_PROVIDER=shopify +NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxx +NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN=xxxxxxx.myshopify.com +``` + +And change the `tsconfig.json` to resolve to the chosen provider: +``` + "@framework": ["framework/shopify"], + "@framework/*": ["framework/shopify/*"] +``` + +That's it! + ### Features Every provider defines the features that it supports under `framework/{provider}/commerce.config.json`