({
+ handler: checkoutEndpoint,
+ handlers,
+})
+
+export default checkoutApi
diff --git a/framework/saleor/api/endpoints/customer.ts b/framework/saleor/api/endpoints/customer.ts
new file mode 100644
index 000000000..d09c976c3
--- /dev/null
+++ b/framework/saleor/api/endpoints/customer.ts
@@ -0,0 +1 @@
+export default function (_commerce: any) {}
diff --git a/framework/saleor/api/endpoints/login.ts b/framework/saleor/api/endpoints/login.ts
new file mode 100644
index 000000000..d09c976c3
--- /dev/null
+++ b/framework/saleor/api/endpoints/login.ts
@@ -0,0 +1 @@
+export default function (_commerce: any) {}
diff --git a/framework/saleor/api/endpoints/logout.ts b/framework/saleor/api/endpoints/logout.ts
new file mode 100644
index 000000000..d09c976c3
--- /dev/null
+++ b/framework/saleor/api/endpoints/logout.ts
@@ -0,0 +1 @@
+export default function (_commerce: any) {}
diff --git a/framework/saleor/api/endpoints/signup.ts b/framework/saleor/api/endpoints/signup.ts
new file mode 100644
index 000000000..d09c976c3
--- /dev/null
+++ b/framework/saleor/api/endpoints/signup.ts
@@ -0,0 +1 @@
+export default function (_commerce: any) {}
diff --git a/framework/saleor/api/endpoints/wishlist.ts b/framework/saleor/api/endpoints/wishlist.ts
new file mode 100644
index 000000000..d09c976c3
--- /dev/null
+++ b/framework/saleor/api/endpoints/wishlist.ts
@@ -0,0 +1 @@
+export default function (_commerce: any) {}
diff --git a/framework/saleor/api/index.ts b/framework/saleor/api/index.ts
new file mode 100644
index 000000000..5ae5f3a8a
--- /dev/null
+++ b/framework/saleor/api/index.ts
@@ -0,0 +1,49 @@
+import type { CommerceAPIConfig } from '@commerce/api'
+
+import * as Const from '../const'
+
+if (!Const.API_URL) {
+ throw new Error(`The environment variable NEXT_SALEOR_API_URL is missing and it's required to access your store`)
+}
+
+if (!Const.API_CHANNEL) {
+ throw new Error(`The environment variable NEXT_SALEOR_CHANNEL is missing and it's required to access your store`)
+}
+
+import fetchGraphqlApi from './utils/fetch-graphql-api'
+
+export interface SaleorConfig extends CommerceAPIConfig {
+ storeChannel: string
+}
+
+const config: SaleorConfig = {
+ locale: 'en-US',
+ commerceUrl: Const.API_URL,
+ apiToken: Const.SALEOR_TOKEN,
+ cartCookie: Const.CHECKOUT_ID_COOKIE,
+ cartCookieMaxAge: 60 * 60 * 24 * 30,
+ fetch: fetchGraphqlApi,
+ customerCookie: '',
+ storeChannel: Const.API_CHANNEL,
+}
+
+import {
+ CommerceAPI,
+ getCommerceApi as commerceApi,
+} from '@commerce/api'
+
+import * as operations from './operations'
+
+export interface ShopifyConfig extends CommerceAPIConfig {}
+
+export const provider = { config, operations }
+
+export type Provider = typeof provider
+
+export type SaleorAPI = CommerceAPI
+
+export function getCommerceApi
(
+ customProvider: P = provider as any
+): SaleorAPI
{
+ return commerceApi(customProvider)
+}
diff --git a/framework/saleor/api/operations/get-all-pages.ts b/framework/saleor/api/operations/get-all-pages.ts
new file mode 100644
index 000000000..f3ed54e27
--- /dev/null
+++ b/framework/saleor/api/operations/get-all-pages.ts
@@ -0,0 +1,50 @@
+import type { OperationContext } from '@commerce/api/operations'
+
+import { QueryPagesArgs, PageCountableEdge } from '../../schema'
+import type { SaleorConfig, Provider } from '..'
+import * as Query from '../../utils/queries'
+
+export type Page = any
+
+ export type GetAllPagesResult<
+ T extends { pages: any[] } = { pages: Page[] }
+ > = T
+
+export default function getAllPagesOperation({
+ commerce,
+}: OperationContext) {
+
+ async function getAllPages({
+ query = Query.PageMany,
+ config,
+ variables,
+ }: {
+ url?: string
+ config?: Partial
+ variables?: QueryPagesArgs
+ preview?: boolean
+ query?: string
+ } = {}): Promise {
+ const { fetch, locale, locales = ['en-US'] } = commerce.getConfig(config)
+
+ const { data } = await fetch(query, { variables },
+ {
+ ...(locale && {
+ headers: {
+ 'Accept-Language': locale,
+ },
+ }),
+ }
+ )
+
+ const pages = data.pages?.edges?.map(({ node: { title: name, slug, ...node } }: PageCountableEdge) => ({
+ ...node,
+ url: `/${locale}/${slug}`,
+ name,
+ }))
+
+ return { pages }
+ }
+
+ return getAllPages
+}
diff --git a/framework/saleor/api/operations/get-all-product-paths.ts b/framework/saleor/api/operations/get-all-product-paths.ts
new file mode 100644
index 000000000..43ce7de94
--- /dev/null
+++ b/framework/saleor/api/operations/get-all-product-paths.ts
@@ -0,0 +1,46 @@
+import type { OperationContext } from '@commerce/api/operations'
+import {
+ GetAllProductPathsQuery,
+ GetAllProductPathsQueryVariables,
+ ProductCountableEdge,
+} from '../../schema'
+import type { ShopifyConfig, Provider, SaleorConfig } from '..'
+
+import { getAllProductsPathsQuery } from '../../utils/queries'
+import fetchAllProducts from '../utils/fetch-all-products'
+
+export type GetAllProductPathsResult = {
+ products: Array<{ path: string }>
+}
+
+export default function getAllProductPathsOperation({
+ commerce,
+}: OperationContext) {
+
+ async function getAllProductPaths({
+ query,
+ config,
+ variables,
+ }: {
+ query?: string
+ config?: SaleorConfig
+ variables?: any
+ } = {}): Promise {
+ config = commerce.getConfig(config)
+
+ const products = await fetchAllProducts({
+ config,
+ query: getAllProductsPathsQuery,
+ variables,
+ })
+
+ return {
+ products: products?.map(({ node: { slug } }: ProductCountableEdge) => ({
+ path: `/${slug}`,
+ })),
+ }
+
+ }
+
+ return getAllProductPaths
+}
diff --git a/framework/saleor/api/operations/get-all-products.ts b/framework/saleor/api/operations/get-all-products.ts
new file mode 100644
index 000000000..a1a7ce0c9
--- /dev/null
+++ b/framework/saleor/api/operations/get-all-products.ts
@@ -0,0 +1,67 @@
+import type { OperationContext } from '@commerce/api/operations'
+import { Product } from '@commerce/types/product'
+
+import { ProductCountableEdge } from '../../schema'
+import type { Provider, SaleorConfig } from '..'
+import { normalizeProduct } from '../../utils'
+
+import * as Query from '../../utils/queries'
+import { GraphQLFetcherResult } from '@commerce/api'
+
+type ReturnType = {
+ products: Product[]
+}
+
+export default function getAllProductsOperation({
+ commerce,
+}: OperationContext) {
+ async function getAllProducts({
+ query = Query.ProductMany,
+ variables,
+ config,
+ featured,
+ }: {
+ query?: string
+ variables?: any
+ config?: Partial
+ preview?: boolean
+ featured?: boolean
+ } = {}): Promise {
+ const { fetch, locale } = commerce.getConfig(config)
+
+ if (featured) {
+ variables = { ...variables, categoryId: 'Q29sbGVjdGlvbjo0' };
+ query = Query.CollectionOne
+ }
+
+
+ const { data }: GraphQLFetcherResult = await fetch(
+ query,
+ { variables },
+ {
+ ...(locale && {
+ headers: {
+ 'Accept-Language': locale,
+ },
+ }),
+ }
+ )
+
+ if (featured) {
+ const products = data.collection.products?.edges?.map(({ node: p }: ProductCountableEdge) => normalizeProduct(p)) ?? []
+
+ return {
+ products,
+ }
+ } else {
+ const products = data.products?.edges?.map(({ node: p }: ProductCountableEdge) => normalizeProduct(p)) ?? []
+
+ return {
+ products,
+ }
+ }
+
+ }
+
+ return getAllProducts
+}
diff --git a/framework/saleor/api/operations/get-page.ts b/framework/saleor/api/operations/get-page.ts
new file mode 100644
index 000000000..af2d5b8e6
--- /dev/null
+++ b/framework/saleor/api/operations/get-page.ts
@@ -0,0 +1,51 @@
+import type { OperationContext } from '@commerce/api/operations'
+import type { Provider, SaleorConfig } from '..'
+import { QueryPageArgs } from '../../schema'
+
+import * as Query from '../../utils/queries'
+
+export type Page = any
+
+ export type GetPageResult = T
+
+export default function getPageOperation({
+ commerce,
+}: OperationContext) {
+
+ async function getPage({
+ query = Query.PageOne,
+ variables,
+ config,
+ }: {
+ query?: string
+ variables: QueryPageArgs,
+ config?: Partial
+ preview?: boolean
+ }): Promise {
+ const { fetch, locale = 'en-US' } = commerce.getConfig(config)
+
+ const {
+ data: { page },
+ } = await fetch(query, { variables },
+ {
+ ...(locale && {
+ headers: {
+ 'Accept-Language': locale,
+ },
+ }),
+ }
+ )
+
+ return {
+ page: page
+ ? {
+ ...page,
+ name: page.title,
+ url: `/${locale}/${page.slug}`,
+ }
+ : null,
+ }
+ }
+
+ return getPage
+}
diff --git a/framework/saleor/api/operations/get-product.ts b/framework/saleor/api/operations/get-product.ts
new file mode 100644
index 000000000..85fca934a
--- /dev/null
+++ b/framework/saleor/api/operations/get-product.ts
@@ -0,0 +1,46 @@
+import type { OperationContext } from '@commerce/api/operations'
+import { normalizeProduct, } from '../../utils'
+import type { Provider, SaleorConfig } from '..'
+
+import * as Query from '../../utils/queries'
+
+type Variables = {
+ slug: string
+}
+
+type ReturnType = {
+ product: any
+}
+
+export default function getProductOperation({
+ commerce,
+}: OperationContext) {
+ async function getProduct({
+ query = Query.ProductOneBySlug,
+ variables,
+ config: cfg,
+ }: {
+ query?: string
+ variables: Variables
+ config?: Partial
+ preview?: boolean
+ }): Promise {
+ const { fetch, locale } = commerce.getConfig(cfg)
+
+ const { data } = await fetch(query, { variables },
+ {
+ ...(locale && {
+ headers: {
+ 'Accept-Language': locale,
+ },
+ }),
+ }
+ )
+
+ return {
+ product: data?.product ? normalizeProduct(data.product) : null,
+ }
+ }
+
+ return getProduct
+}
diff --git a/framework/saleor/api/operations/get-site-info.ts b/framework/saleor/api/operations/get-site-info.ts
new file mode 100644
index 000000000..eca0f2246
--- /dev/null
+++ b/framework/saleor/api/operations/get-site-info.ts
@@ -0,0 +1,35 @@
+import type { OperationContext } from '@commerce/api/operations'
+import { Category } from '@commerce/types/site'
+import type { SaleorConfig, Provider } from '..'
+
+import { getCategories, getVendors } from '../../utils'
+
+interface GetSiteInfoResult {
+ categories: Category[]
+ brands: any[]
+}
+
+export default function getSiteInfoOperation({ commerce }: OperationContext) {
+ async function getSiteInfo({
+ query,
+ config,
+ variables,
+ }: {
+ query?: string
+ config?: Partial
+ preview?: boolean
+ variables?: any
+ } = {}): Promise {
+ const cfg = commerce.getConfig(config)
+
+ const categories = await getCategories(cfg)
+ const brands = await getVendors(cfg)
+
+ return {
+ categories,
+ brands,
+ }
+ }
+
+ return getSiteInfo
+}
diff --git a/framework/saleor/api/operations/index.ts b/framework/saleor/api/operations/index.ts
new file mode 100644
index 000000000..7872a20b6
--- /dev/null
+++ b/framework/saleor/api/operations/index.ts
@@ -0,0 +1,7 @@
+export { default as getAllPages } from './get-all-pages'
+export { default as getPage } from './get-page'
+export { default as getAllProducts } from './get-all-products'
+export { default as getAllProductPaths } from './get-all-product-paths'
+export { default as getProduct } from './get-product'
+export { default as getSiteInfo } from './get-site-info'
+export { default as login } from './login'
diff --git a/framework/saleor/api/operations/login.ts b/framework/saleor/api/operations/login.ts
new file mode 100644
index 000000000..ca680b82c
--- /dev/null
+++ b/framework/saleor/api/operations/login.ts
@@ -0,0 +1,42 @@
+import type { ServerResponse } from 'http'
+import type { OperationContext } from '@commerce/api/operations'
+import type { Provider, SaleorConfig } from '..'
+import {
+ throwUserErrors,
+} from '../../utils'
+
+import * as Mutation from '../../utils/mutations'
+
+export default function loginOperation({
+ commerce,
+}: OperationContext) {
+ async function login({
+ query = Mutation.SessionCreate,
+ variables,
+ config,
+ }: {
+ query?: string
+ variables: any
+ res: ServerResponse
+ config?: SaleorConfig
+ }): Promise {
+ config = commerce.getConfig(config)
+
+ const { data: { customerAccessTokenCreate } } = await config.fetch(query, { variables })
+
+ throwUserErrors(customerAccessTokenCreate?.customerUserErrors)
+
+ const customerAccessToken = customerAccessTokenCreate?.customerAccessToken
+ const accessToken = customerAccessToken?.accessToken
+
+ // if (accessToken) {
+ // setCustomerToken(accessToken)
+ // }
+
+ return {
+ result: customerAccessToken?.accessToken,
+ }
+ }
+
+ return login
+}
diff --git a/framework/saleor/api/utils/fetch-all-products.ts b/framework/saleor/api/utils/fetch-all-products.ts
new file mode 100644
index 000000000..1cfb3157c
--- /dev/null
+++ b/framework/saleor/api/utils/fetch-all-products.ts
@@ -0,0 +1,41 @@
+import { ProductCountableEdge } from '../../schema'
+import { SaleorConfig } from '..'
+
+const fetchAllProducts = async ({
+ config,
+ query,
+ variables,
+ acc = [],
+ cursor,
+}: {
+ config: SaleorConfig
+ query: string
+ acc?: ProductCountableEdge[]
+ variables?: any
+ cursor?: string
+}): Promise => {
+ const { data } = await config.fetch(query, {
+ variables: { ...variables, cursor },
+ })
+
+ const edges: ProductCountableEdge[] = 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/saleor/api/utils/fetch-graphql-api.ts b/framework/saleor/api/utils/fetch-graphql-api.ts
new file mode 100644
index 000000000..71199d661
--- /dev/null
+++ b/framework/saleor/api/utils/fetch-graphql-api.ts
@@ -0,0 +1,37 @@
+import type { GraphQLFetcher } from '@commerce/api'
+import fetch from './fetch'
+
+import { API_URL } from '../../const'
+import { getError } from '../../utils/handle-fetch-response'
+import { getCommerceApi } from '..'
+import { getToken } from '@framework/utils'
+
+const fetchGraphqlApi: GraphQLFetcher = async (query: string, { variables } = {}, fetchOptions) => {
+ const config = getCommerceApi().getConfig()
+ const token = getToken()
+
+ const res = await fetch(API_URL!, {
+ ...fetchOptions,
+ method: 'POST',
+ headers: {
+ ...(token && {
+ Authorization: `Bearer ${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/saleor/api/utils/fetch.ts b/framework/saleor/api/utils/fetch.ts
new file mode 100644
index 000000000..0b8367102
--- /dev/null
+++ b/framework/saleor/api/utils/fetch.ts
@@ -0,0 +1,2 @@
+import zeitFetch from '@vercel/fetch'
+export default zeitFetch()
diff --git a/framework/saleor/api/utils/is-allowed-method.ts b/framework/saleor/api/utils/is-allowed-method.ts
new file mode 100644
index 000000000..cbaab2251
--- /dev/null
+++ b/framework/saleor/api/utils/is-allowed-method.ts
@@ -0,0 +1,22 @@
+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/saleor/api/wishlist.ts b/framework/saleor/api/wishlist.ts
new file mode 100644
index 000000000..ea9b101e1
--- /dev/null
+++ b/framework/saleor/api/wishlist.ts
@@ -0,0 +1 @@
+export default function () {}
diff --git a/framework/saleor/auth/use-login.tsx b/framework/saleor/auth/use-login.tsx
new file mode 100644
index 000000000..2a31c932b
--- /dev/null
+++ b/framework/saleor/auth/use-login.tsx
@@ -0,0 +1,63 @@
+import { useCallback } from 'react'
+
+import type { MutationHook } from '@commerce/utils/types'
+import { CommerceError } from '@commerce/utils/errors'
+import useCustomer from '../customer/use-customer'
+import * as mutation from '../utils/mutations'
+import { Mutation, MutationTokenCreateArgs } from '../schema'
+import useLogin, { UseLogin } from '@commerce/auth/use-login'
+import { setCSRFToken, setToken, throwUserErrors, checkoutAttach, getCheckoutId } from '../utils'
+import { LoginHook } from '@commerce/types/login'
+
+export default useLogin as UseLogin
+
+export const handler: MutationHook = {
+ fetchOptions: {
+ query: mutation.SessionCreate,
+ },
+ 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 { tokenCreate } = await fetch({
+ ...options,
+ variables: { email, password },
+ })
+
+ throwUserErrors(tokenCreate?.errors)
+
+ const { token, csrfToken } = tokenCreate!
+
+ if (token && csrfToken) {
+ setToken(token)
+ setCSRFToken(csrfToken)
+
+ const { checkoutId } = getCheckoutId()
+ checkoutAttach(fetch, {
+ variables: { checkoutId },
+ headers: {
+ Authorization: `JWT ${token}`,
+ },
+ })
+ }
+
+ 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/saleor/auth/use-logout.tsx b/framework/saleor/auth/use-logout.tsx
new file mode 100644
index 000000000..fe75df84b
--- /dev/null
+++ b/framework/saleor/auth/use-logout.tsx
@@ -0,0 +1,41 @@
+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 * as mutation from '../utils/mutations'
+import { setCSRFToken, setToken, setCheckoutToken } from '../utils/customer-token'
+import { LogoutHook } from '@commerce/types/logout'
+
+export default useLogout as UseLogout
+
+export const handler: MutationHook = {
+ fetchOptions: {
+ query: mutation.SessionDestroy,
+ },
+ async fetcher({ options, fetch }) {
+ await fetch({
+ ...options,
+ variables: {},
+ })
+
+ setToken()
+ setCSRFToken()
+ setCheckoutToken()
+
+ 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/saleor/auth/use-signup.tsx b/framework/saleor/auth/use-signup.tsx
new file mode 100644
index 000000000..d9e91b468
--- /dev/null
+++ b/framework/saleor/auth/use-signup.tsx
@@ -0,0 +1,56 @@
+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 { AccountRegisterInput, Mutation, MutationAccountRegisterArgs } from '../schema'
+
+import * as mutation from '../utils/mutations'
+import { handleAutomaticLogin, throwUserErrors } from '../utils'
+import { SignupHook } from '@commerce/types/signup'
+
+export default useSignup as UseSignup
+
+export const handler: MutationHook = {
+ fetchOptions: {
+ query: mutation.AccountCreate,
+ },
+ 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 signup',
+ })
+ }
+
+ const { customerCreate } = await fetch({
+ ...options,
+ variables: {
+ input: {
+ email,
+ password,
+ redirectUrl: 'https://localhost.com',
+ channel: 'default-channel'
+ },
+ },
+ })
+
+ throwUserErrors(customerCreate?.errors)
+ await handleAutomaticLogin(fetch, { email, password })
+
+ return null
+ },
+ 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/saleor/cart/index.ts b/framework/saleor/cart/index.ts
new file mode 100644
index 000000000..f6d36b443
--- /dev/null
+++ b/framework/saleor/cart/index.ts
@@ -0,0 +1,4 @@
+export { default as useCart } from './use-cart'
+export { default as useAddItem } from './use-add-item'
+export { default as useUpdateItem } from './use-update-item'
+export { default as useRemoveItem } from './use-remove-item'
diff --git a/framework/saleor/cart/use-add-item.tsx b/framework/saleor/cart/use-add-item.tsx
new file mode 100644
index 000000000..3af368e70
--- /dev/null
+++ b/framework/saleor/cart/use-add-item.tsx
@@ -0,0 +1,54 @@
+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 useCart from './use-cart'
+
+import * as mutation from '../utils/mutations'
+
+import { getCheckoutId, checkoutToCart } from '../utils'
+
+import { Mutation, MutationCheckoutLinesAddArgs } from '../schema'
+import { AddItemHook } from '@commerce/types/cart'
+
+export default useAddItem as UseAddItem
+
+export const handler: MutationHook = {
+ fetchOptions: { query: mutation.CheckoutLineAdd },
+ 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 { checkoutLinesAdd } = await fetch({
+ ...options,
+ variables: {
+ checkoutId: getCheckoutId().checkoutId,
+ lineItems: [
+ {
+ variantId: item.variantId,
+ quantity: item.quantity ?? 1,
+ },
+ ],
+ },
+ })
+
+ return checkoutToCart(checkoutLinesAdd)
+ },
+ 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/saleor/cart/use-cart.tsx b/framework/saleor/cart/use-cart.tsx
new file mode 100644
index 000000000..ab80ea395
--- /dev/null
+++ b/framework/saleor/cart/use-cart.tsx
@@ -0,0 +1,53 @@
+import { useMemo } from 'react'
+import useCommerceCart, { UseCart } from '@commerce/cart/use-cart'
+
+import { SWRHook } from '@commerce/utils/types'
+import { checkoutCreate, checkoutToCart, getCheckoutId } from '../utils'
+import * as query from '../utils/queries'
+import { GetCartHook } from '@commerce/types/cart'
+
+export default useCommerceCart as UseCart
+
+export const handler: SWRHook = {
+ fetchOptions: {
+ query: query.CheckoutOne,
+ },
+ async fetcher({ input: { cartId: checkoutId }, options, fetch }) {
+ let checkout
+
+ if (checkoutId) {
+ const checkoutId = getCheckoutId().checkoutToken
+ const data = await fetch({
+ ...options,
+ variables: { checkoutId },
+ })
+
+ checkout = data
+ }
+
+ 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
+ },
+ enumerable: true,
+ },
+ }),
+ [response]
+ )
+ },
+}
diff --git a/framework/saleor/cart/use-remove-item.tsx b/framework/saleor/cart/use-remove-item.tsx
new file mode 100644
index 000000000..81f9c122f
--- /dev/null
+++ b/framework/saleor/cart/use-remove-item.tsx
@@ -0,0 +1,39 @@
+import { useCallback } from 'react'
+import type { MutationHookContext, HookFetcherContext, MutationHook } from '@commerce/utils/types'
+import useRemoveItem, { UseRemoveItem } from '@commerce/cart/use-remove-item'
+import useCart from './use-cart'
+import * as mutation from '../utils/mutations'
+import { getCheckoutId, checkoutToCart } from '../utils'
+import { Mutation, MutationCheckoutLineDeleteArgs } from '../schema'
+import { LineItem, RemoveItemHook } from '../types/cart'
+
+export default useRemoveItem as UseRemoveItem
+
+export const handler = {
+ fetchOptions: { query: mutation.CheckoutLineDelete },
+ async fetcher({ input: { itemId }, options, fetch }: HookFetcherContext) {
+ const data = await fetch({
+ ...options,
+ variables: {
+ checkoutId: getCheckoutId().checkoutId,
+ lineId: itemId,
+ },
+ })
+ return checkoutToCart(data.checkoutLineDelete)
+ },
+ useHook: ({ fetch }: MutationHookContext) => <
+ T extends LineItem | undefined = undefined
+ > () => {
+ const { mutate } = useCart()
+
+ return useCallback(
+ async function removeItem(input) {
+ const data = await fetch({ input: { itemId: input.id } })
+ await mutate(data, false)
+
+ return data
+ },
+ [fetch, mutate]
+ );
+ },
+}
diff --git a/framework/saleor/cart/use-update-item.tsx b/framework/saleor/cart/use-update-item.tsx
new file mode 100644
index 000000000..361ae5cdf
--- /dev/null
+++ b/framework/saleor/cart/use-update-item.tsx
@@ -0,0 +1,99 @@
+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, { UseUpdateItem } from '@commerce/cart/use-update-item'
+
+import useCart from './use-cart'
+import { handler as removeItemHandler } from './use-remove-item'
+import type { LineItem } from '../types'
+import { checkoutToCart } from '../utils'
+import { getCheckoutId } from '../utils'
+import { Mutation, MutationCheckoutLinesUpdateArgs } from '../schema'
+
+import * as mutation from '../utils/mutations'
+
+import type { UpdateItemHook } from '../types/cart'
+
+export type UpdateItemActionInput = T extends LineItem
+ ? Partial
+ : UpdateItemHook['actionInput']
+
+export default useUpdateItem as UseUpdateItem
+
+export const handler = {
+ fetchOptions: { query: mutation.CheckoutLineUpdate },
+ async fetcher({
+ input: { itemId, item },
+ options,
+ fetch
+ }: HookFetcherContext) {
+ 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 checkoutId = getCheckoutId().checkoutId
+ const { checkoutLinesUpdate } = await fetch({
+ ...options,
+ variables: {
+ checkoutId,
+ lineItems: [
+ {
+ variantId: item.variantId,
+ quantity: item.quantity,
+ },
+ ],
+ },
+ })
+
+ return checkoutToCart(checkoutLinesUpdate)
+ },
+ useHook: ({ fetch }: MutationHookContext) =>
+ (
+ ctx: {
+ item?: T
+ wait?: number
+ } = {}
+ ) => {
+ const { item } = ctx
+ const { mutate } = useCart() as any
+
+ return useCallback(
+ debounce(async (input: UpdateItemActionInput) => {
+ 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/saleor/commerce.config.json b/framework/saleor/commerce.config.json
new file mode 100644
index 000000000..d5a1ac35d
--- /dev/null
+++ b/framework/saleor/commerce.config.json
@@ -0,0 +1,7 @@
+{
+ "provider": "saleor",
+ "features": {
+ "wishlist": false,
+ "customCheckout": true
+ }
+}
diff --git a/framework/saleor/const.ts b/framework/saleor/const.ts
new file mode 100644
index 000000000..df348770d
--- /dev/null
+++ b/framework/saleor/const.ts
@@ -0,0 +1,5 @@
+export const API_URL = process.env.NEXT_PUBLIC_SALEOR_API_URL
+export const API_CHANNEL = process.env.NEXT_PUBLIC_SALEOR_CHANNEL
+export const CHECKOUT_ID_COOKIE = 'saleor.CheckoutID'
+export const SALEOR_TOKEN = 'saleor.Token'
+export const SALEOR_CRSF_TOKEN = 'saleor.CSRFToken'
diff --git a/framework/saleor/customer/index.ts b/framework/saleor/customer/index.ts
new file mode 100644
index 000000000..6c903ecc5
--- /dev/null
+++ b/framework/saleor/customer/index.ts
@@ -0,0 +1 @@
+export { default as useCustomer } from './use-customer'
diff --git a/framework/saleor/customer/use-customer.tsx b/framework/saleor/customer/use-customer.tsx
new file mode 100644
index 000000000..1e0e63d5a
--- /dev/null
+++ b/framework/saleor/customer/use-customer.tsx
@@ -0,0 +1,30 @@
+import useCustomer, { UseCustomer } from '@commerce/customer/use-customer'
+import { CustomerHook } from '@commerce/types/customer'
+import { SWRHook } from '@commerce/utils/types'
+
+import * as query from '../utils/queries'
+
+export default useCustomer as UseCustomer
+
+export const handler: SWRHook = {
+ fetchOptions: {
+ query: query.CustomerCurrent,
+ },
+ async fetcher({ options, fetch }) {
+ const data = await fetch({
+ ...options,
+ variables: {},
+ })
+ return data.me ?? null
+ },
+ useHook:
+ ({ useData }) =>
+ (input) => {
+ return useData({
+ swrOptions: {
+ revalidateOnFocus: false,
+ ...input?.swrOptions,
+ },
+ })
+ },
+}
diff --git a/framework/saleor/fetcher.ts b/framework/saleor/fetcher.ts
new file mode 100644
index 000000000..9d3c0bf89
--- /dev/null
+++ b/framework/saleor/fetcher.ts
@@ -0,0 +1,20 @@
+import { Fetcher } from '@commerce/utils/types'
+import { API_URL } from './const'
+import { getToken, handleFetchResponse } from './utils'
+
+const fetcher: Fetcher = async ({ url = API_URL, method = 'POST', variables, query }) => {
+ const token = getToken()
+
+ return handleFetchResponse(
+ await fetch(url!, {
+ method,
+ body: JSON.stringify({ query, variables }),
+ headers: {
+ Authorization: `JWT ${token}`,
+ 'Content-Type': 'application/json',
+ },
+ })
+ )
+}
+
+export default fetcher
diff --git a/framework/saleor/index.tsx b/framework/saleor/index.tsx
new file mode 100644
index 000000000..5c9e61ec8
--- /dev/null
+++ b/framework/saleor/index.tsx
@@ -0,0 +1,32 @@
+import * as React from 'react'
+import { ReactNode } from 'react'
+
+import { CommerceConfig, CommerceProvider as CoreCommerceProvider, useCommerce as useCoreCommerce } from '@commerce'
+
+import { saleorProvider, SaleorProvider } from './provider'
+import * as Const from './const'
+
+export { saleorProvider }
+export type { SaleorProvider }
+
+export const saleorConfig: CommerceConfig = {
+ locale: 'en-us',
+ cartCookie: Const.CHECKOUT_ID_COOKIE,
+}
+
+export type SaleorConfig = Partial
+
+export type SaleorProps = {
+ children?: ReactNode
+ locale: string
+} & SaleorConfig
+
+export function CommerceProvider({ children, ...config }: SaleorProps) {
+ return (
+
+ {children}
+
+ )
+}
+
+export const useCommerce = () => useCoreCommerce()
diff --git a/framework/saleor/next.config.js b/framework/saleor/next.config.js
new file mode 100644
index 000000000..397a37b2a
--- /dev/null
+++ b/framework/saleor/next.config.js
@@ -0,0 +1,8 @@
+const commerce = require('./commerce.config.json')
+
+module.exports = {
+ commerce,
+ images: {
+ domains: [process.env.COMMERCE_IMAGE_HOST],
+ },
+}
diff --git a/framework/saleor/product/use-price.tsx b/framework/saleor/product/use-price.tsx
new file mode 100644
index 000000000..0174faf5e
--- /dev/null
+++ b/framework/saleor/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/saleor/product/use-search.tsx b/framework/saleor/product/use-search.tsx
new file mode 100644
index 000000000..cc763c4b7
--- /dev/null
+++ b/framework/saleor/product/use-search.tsx
@@ -0,0 +1,74 @@
+import { SWRHook } from '@commerce/utils/types'
+import { Product } from '@commerce/types/product'
+import useSearch, { UseSearch } from '@commerce/product/use-search'
+
+import { ProductCountableEdge } from '../schema'
+import { getSearchVariables, normalizeProduct } from '../utils'
+
+import * as query from '../utils/queries'
+import { SearchProductsHook } from '@commerce/types/product'
+
+export default useSearch as UseSearch
+
+export type SearchProductsInput = {
+ search?: string
+ categoryId?: string | number
+ brandId?: string | number
+ sort?: string
+}
+
+export type SearchProductsData = {
+ products: Product[]
+ found: boolean
+}
+
+export const handler: SWRHook = {
+ fetchOptions: {
+ query: query.ProductMany,
+ },
+ async fetcher({ input, options, fetch }) {
+ const { categoryId, brandId } = input
+
+ const data = await fetch({
+ query: categoryId ? query.CollectionOne : options.query,
+ method: options?.method,
+ variables: getSearchVariables(input),
+ })
+
+ let edges
+
+ if (categoryId) {
+ edges = data.collection?.products?.edges ?? []
+ // FIXME @zaiste, no `vendor` in Saleor
+ // if (brandId) {
+ // edges = edges.filter(
+ // ({ node: { vendor } }: ProductCountableEdge) =>
+ // vendor.replace(/\s+/g, '-').toLowerCase() === brandId
+ // )
+ // }
+ } else {
+ edges = data.products?.edges ?? []
+ }
+
+ return {
+ products: edges.map(({ node }: ProductCountableEdge) => 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/saleor/provider.ts b/framework/saleor/provider.ts
new file mode 100644
index 000000000..2ca96475a
--- /dev/null
+++ b/framework/saleor/provider.ts
@@ -0,0 +1,26 @@
+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 saleorProvider = {
+ locale: 'en-us',
+ cartCookie: '',
+ cartCookieToken: '',
+ fetcher,
+ cart: { useCart, useAddItem, useUpdateItem, useRemoveItem },
+ customer: { useCustomer },
+ products: { useSearch },
+ auth: { useLogin, useLogout, useSignup },
+}
+
+export type SaleorProvider = typeof saleorProvider
diff --git a/framework/saleor/schema.d.ts b/framework/saleor/schema.d.ts
new file mode 100644
index 000000000..339e7269d
--- /dev/null
+++ b/framework/saleor/schema.d.ts
@@ -0,0 +1,11488 @@
+export type Maybe = T | null
+export type Exact = {
+ [K in keyof T]: T[K]
+}
+export type MakeOptional = Omit & { [SubKey in K]?: Maybe }
+export type MakeMaybe = Omit & { [SubKey in K]: Maybe }
+/** All built-in and custom scalars, mapped to their actual values */
+export type Scalars = {
+ ID: string
+ String: string
+ Boolean: boolean
+ Int: number
+ Float: number
+ /**
+ * The `Date` scalar type represents a Date
+ * value as specified by
+ * [iso8601](https://en.wikipedia.org/wiki/ISO_8601).
+ */
+ Date: any
+ /**
+ * The `DateTime` scalar type represents a DateTime
+ * value as specified by
+ * [iso8601](https://en.wikipedia.org/wiki/ISO_8601).
+ */
+ DateTime: any
+ /**
+ * The `GenericScalar` scalar type represents a generic
+ * GraphQL scalar value that could be:
+ * String, Boolean, Int, Float, List or Object.
+ */
+ GenericScalar: any
+ /**
+ * Allows use of a JSON String for input / output from the GraphQL schema.
+ *
+ * Use of this type is *not recommended* as you lose the benefits of having a defined, static
+ * schema (one of the key benefits of GraphQL).
+ */
+ JSONString: any
+ /**
+ * Positive Decimal scalar implementation.
+ *
+ * Should be used in places where value must be positive.
+ */
+ PositiveDecimal: any
+ UUID: any
+ /** Variables of this type must be set to null in mutations. They will be replaced with a filename from a following multipart part containing a binary file. See: https://github.com/jaydenseric/graphql-multipart-request-spec. */
+ Upload: any
+ WeightScalar: any
+ /** Anything */
+ _Any: any
+}
+
+/** Create a new address for the customer. */
+export type AccountAddressCreate = {
+ __typename?: 'AccountAddressCreate'
+ /** A user instance for which the address was created. */
+ user?: Maybe
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ accountErrors: Array
+ errors: Array
+ address?: Maybe
+}
+
+/** Delete an address of the logged-in user. */
+export type AccountAddressDelete = {
+ __typename?: 'AccountAddressDelete'
+ /** A user instance for which the address was deleted. */
+ user?: Maybe
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ accountErrors: Array
+ errors: Array
+ address?: Maybe
+}
+
+/** Updates an address of the logged-in user. */
+export type AccountAddressUpdate = {
+ __typename?: 'AccountAddressUpdate'
+ /** A user object for which the address was edited. */
+ user?: Maybe
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ accountErrors: Array
+ errors: Array
+ address?: Maybe
+}
+
+/** Remove user account. */
+export type AccountDelete = {
+ __typename?: 'AccountDelete'
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ accountErrors: Array
+ errors: Array
+ user?: Maybe
+}
+
+export type AccountError = {
+ __typename?: 'AccountError'
+ /** Name of a field that caused the error. A value of `null` indicates that the error isn't associated with a particular field. */
+ field?: Maybe
+ /** The error message. */
+ message?: Maybe
+ /** The error code. */
+ code: AccountErrorCode
+ /** A type of address that causes the error. */
+ addressType?: Maybe
+}
+
+/** An enumeration. */
+export enum AccountErrorCode {
+ ActivateOwnAccount = 'ACTIVATE_OWN_ACCOUNT',
+ ActivateSuperuserAccount = 'ACTIVATE_SUPERUSER_ACCOUNT',
+ DuplicatedInputItem = 'DUPLICATED_INPUT_ITEM',
+ DeactivateOwnAccount = 'DEACTIVATE_OWN_ACCOUNT',
+ DeactivateSuperuserAccount = 'DEACTIVATE_SUPERUSER_ACCOUNT',
+ DeleteNonStaffUser = 'DELETE_NON_STAFF_USER',
+ DeleteOwnAccount = 'DELETE_OWN_ACCOUNT',
+ DeleteStaffAccount = 'DELETE_STAFF_ACCOUNT',
+ DeleteSuperuserAccount = 'DELETE_SUPERUSER_ACCOUNT',
+ GraphqlError = 'GRAPHQL_ERROR',
+ Inactive = 'INACTIVE',
+ Invalid = 'INVALID',
+ InvalidPassword = 'INVALID_PASSWORD',
+ LeftNotManageablePermission = 'LEFT_NOT_MANAGEABLE_PERMISSION',
+ InvalidCredentials = 'INVALID_CREDENTIALS',
+ NotFound = 'NOT_FOUND',
+ OutOfScopeUser = 'OUT_OF_SCOPE_USER',
+ OutOfScopeGroup = 'OUT_OF_SCOPE_GROUP',
+ OutOfScopePermission = 'OUT_OF_SCOPE_PERMISSION',
+ PasswordEntirelyNumeric = 'PASSWORD_ENTIRELY_NUMERIC',
+ PasswordTooCommon = 'PASSWORD_TOO_COMMON',
+ PasswordTooShort = 'PASSWORD_TOO_SHORT',
+ PasswordTooSimilar = 'PASSWORD_TOO_SIMILAR',
+ Required = 'REQUIRED',
+ Unique = 'UNIQUE',
+ JwtSignatureExpired = 'JWT_SIGNATURE_EXPIRED',
+ JwtInvalidToken = 'JWT_INVALID_TOKEN',
+ JwtDecodeError = 'JWT_DECODE_ERROR',
+ JwtMissingToken = 'JWT_MISSING_TOKEN',
+ JwtInvalidCsrfToken = 'JWT_INVALID_CSRF_TOKEN',
+ ChannelInactive = 'CHANNEL_INACTIVE',
+ MissingChannelSlug = 'MISSING_CHANNEL_SLUG',
+}
+
+export type AccountInput = {
+ /** Given name. */
+ firstName?: Maybe
+ /** Family name. */
+ lastName?: Maybe
+ /** Billing address of the customer. */
+ defaultBillingAddress?: Maybe
+ /** Shipping address of the customer. */
+ defaultShippingAddress?: Maybe
+ /** User language code. */
+ languageCode?: Maybe
+}
+
+/** Register a new user. */
+export type AccountRegister = {
+ __typename?: 'AccountRegister'
+ /** Informs whether users need to confirm their email address. */
+ requiresConfirmation?: Maybe
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ accountErrors: Array
+ errors: Array
+ user?: Maybe
+}
+
+export type AccountRegisterInput = {
+ /** The email address of the user. */
+ email: Scalars['String']
+ /** Password. */
+ password: Scalars['String']
+ /** Base of frontend URL that will be needed to create confirmation URL. */
+ redirectUrl?: Maybe
+ /** User language code. */
+ languageCode?: Maybe
+ /** User public metadata. */
+ metadata?: Maybe>
+ /** Slug of a channel which will be used to notify users. Optional when only one channel exists. */
+ channel?: Maybe
+}
+
+/** Sends an email with the account removal link for the logged-in user. */
+export type AccountRequestDeletion = {
+ __typename?: 'AccountRequestDeletion'
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ accountErrors: Array
+ errors: Array
+}
+
+/** Sets a default address for the authenticated user. */
+export type AccountSetDefaultAddress = {
+ __typename?: 'AccountSetDefaultAddress'
+ /** An updated user instance. */
+ user?: Maybe
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ accountErrors: Array
+ errors: Array
+}
+
+/** Updates the account of the logged-in user. */
+export type AccountUpdate = {
+ __typename?: 'AccountUpdate'
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ accountErrors: Array
+ errors: Array
+ user?: Maybe
+}
+
+/** Represents user address data. */
+export type Address = Node & {
+ __typename?: 'Address'
+ /** The ID of the object. */
+ id: Scalars['ID']
+ firstName: Scalars['String']
+ lastName: Scalars['String']
+ companyName: Scalars['String']
+ streetAddress1: Scalars['String']
+ streetAddress2: Scalars['String']
+ city: Scalars['String']
+ cityArea: Scalars['String']
+ postalCode: Scalars['String']
+ /** Shop's default country. */
+ country: CountryDisplay
+ countryArea: Scalars['String']
+ phone?: Maybe
+ /** Address is user's default shipping address. */
+ isDefaultShippingAddress?: Maybe
+ /** Address is user's default billing address. */
+ isDefaultBillingAddress?: Maybe
+}
+
+/** Creates user address. */
+export type AddressCreate = {
+ __typename?: 'AddressCreate'
+ /** A user instance for which the address was created. */
+ user?: Maybe
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ accountErrors: Array
+ errors: Array
+ address?: Maybe
+}
+
+/** Deletes an address. */
+export type AddressDelete = {
+ __typename?: 'AddressDelete'
+ /** A user instance for which the address was deleted. */
+ user?: Maybe
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ accountErrors: Array
+ errors: Array
+ address?: Maybe
+}
+
+export type AddressInput = {
+ /** Given name. */
+ firstName?: Maybe
+ /** Family name. */
+ lastName?: Maybe
+ /** Company or organization. */
+ companyName?: Maybe
+ /** Address. */
+ streetAddress1?: Maybe
+ /** Address. */
+ streetAddress2?: Maybe
+ /** City. */
+ city?: Maybe
+ /** District. */
+ cityArea?: Maybe
+ /** Postal code. */
+ postalCode?: Maybe
+ /** Country. */
+ country?: Maybe
+ /** State or province. */
+ countryArea?: Maybe
+ /** Phone number. */
+ phone?: Maybe
+}
+
+/** Sets a default address for the given user. */
+export type AddressSetDefault = {
+ __typename?: 'AddressSetDefault'
+ /** An updated user instance. */
+ user?: Maybe
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ accountErrors: Array
+ errors: Array
+}
+
+/** An enumeration. */
+export enum AddressTypeEnum {
+ Billing = 'BILLING',
+ Shipping = 'SHIPPING',
+}
+
+/** Updates an address. */
+export type AddressUpdate = {
+ __typename?: 'AddressUpdate'
+ /** A user object for which the address was edited. */
+ user?: Maybe
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ accountErrors: Array
+ errors: Array
+ address?: Maybe
+}
+
+export type AddressValidationData = {
+ __typename?: 'AddressValidationData'
+ countryCode?: Maybe
+ countryName?: Maybe
+ addressFormat?: Maybe
+ addressLatinFormat?: Maybe
+ allowedFields?: Maybe>>
+ requiredFields?: Maybe>>
+ upperFields?: Maybe>>
+ countryAreaType?: Maybe
+ countryAreaChoices?: Maybe>>
+ cityType?: Maybe
+ cityChoices?: Maybe>>
+ cityAreaType?: Maybe
+ cityAreaChoices?: Maybe>>
+ postalCodeType?: Maybe
+ postalCodeMatchers?: Maybe>>
+ postalCodeExamples?: Maybe>>
+ postalCodePrefix?: Maybe
+}
+
+/** Represents allocation. */
+export type Allocation = Node & {
+ __typename?: 'Allocation'
+ /** The ID of the object. */
+ id: Scalars['ID']
+ /** Quantity allocated for orders. */
+ quantity: Scalars['Int']
+ /** The warehouse were items were allocated. */
+ warehouse: Warehouse
+}
+
+/** Represents app data. */
+export type App = Node &
+ ObjectWithMetadata & {
+ __typename?: 'App'
+ /** The ID of the object. */
+ id: Scalars['ID']
+ /** Name of the app. */
+ name?: Maybe
+ /** The date and time when the app was created. */
+ created?: Maybe
+ /** Determine if app will be set active or not. */
+ isActive?: Maybe
+ /** List of the app's permissions. */
+ permissions?: Maybe>>
+ /** Last 4 characters of the tokens. */
+ tokens?: Maybe>>
+ /** List of private metadata items.Requires proper staff permissions to access. */
+ privateMetadata: Array>
+ /** List of public metadata items. Can be accessed without permissions. */
+ metadata: Array>
+ /** Type of the app. */
+ type?: Maybe
+ /** List of webhooks assigned to this app. */
+ webhooks?: Maybe>>
+ /** Description of this app. */
+ aboutApp?: Maybe
+ /** Description of the data privacy defined for this app. */
+ dataPrivacy?: Maybe
+ /** Url to details about the privacy policy on the app owner page. */
+ dataPrivacyUrl?: Maybe
+ /** Homepage of the app. */
+ homepageUrl?: Maybe
+ /** Support page for the app. */
+ supportUrl?: Maybe
+ /** Url to iframe with the configuration for the app. */
+ configurationUrl?: Maybe
+ /** Url to iframe with the app. */
+ appUrl?: Maybe
+ /** Version number of the app. */
+ version?: Maybe
+ /** JWT token used to authenticate by thridparty app. */
+ accessToken?: Maybe
+ }
+
+/** Activate the app. */
+export type AppActivate = {
+ __typename?: 'AppActivate'
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ appErrors: Array
+ errors: Array
+ app?: Maybe
+}
+
+export type AppCountableConnection = {
+ __typename?: 'AppCountableConnection'
+ /** Pagination data for this connection. */
+ pageInfo: PageInfo
+ edges: Array
+ /** A total count of items in the collection. */
+ totalCount?: Maybe
+}
+
+export type AppCountableEdge = {
+ __typename?: 'AppCountableEdge'
+ /** The item at the end of the edge. */
+ node: App
+ /** A cursor for use in pagination. */
+ cursor: Scalars['String']
+}
+
+/** Creates a new app. */
+export type AppCreate = {
+ __typename?: 'AppCreate'
+ /** The newly created authentication token. */
+ authToken?: Maybe
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ appErrors: Array
+ errors: Array
+ app?: Maybe
+}
+
+/** Deactivate the app. */
+export type AppDeactivate = {
+ __typename?: 'AppDeactivate'
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ appErrors: Array
+ errors: Array
+ app?: Maybe
+}
+
+/** Deletes an app. */
+export type AppDelete = {
+ __typename?: 'AppDelete'
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ appErrors: Array
+ errors: Array
+ app?: Maybe
+}
+
+/** Delete failed installation. */
+export type AppDeleteFailedInstallation = {
+ __typename?: 'AppDeleteFailedInstallation'
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ appErrors: Array
+ errors: Array
+ appInstallation?: Maybe
+}
+
+export type AppError = {
+ __typename?: 'AppError'
+ /** Name of a field that caused the error. A value of `null` indicates that the error isn't associated with a particular field. */
+ field?: Maybe
+ /** The error message. */
+ message?: Maybe
+ /** The error code. */
+ code: AppErrorCode
+ /** List of permissions which causes the error. */
+ permissions?: Maybe>
+}
+
+/** An enumeration. */
+export enum AppErrorCode {
+ Forbidden = 'FORBIDDEN',
+ GraphqlError = 'GRAPHQL_ERROR',
+ Invalid = 'INVALID',
+ InvalidStatus = 'INVALID_STATUS',
+ InvalidPermission = 'INVALID_PERMISSION',
+ InvalidUrlFormat = 'INVALID_URL_FORMAT',
+ InvalidManifestFormat = 'INVALID_MANIFEST_FORMAT',
+ ManifestUrlCantConnect = 'MANIFEST_URL_CANT_CONNECT',
+ NotFound = 'NOT_FOUND',
+ Required = 'REQUIRED',
+ Unique = 'UNIQUE',
+ OutOfScopeApp = 'OUT_OF_SCOPE_APP',
+ OutOfScopePermission = 'OUT_OF_SCOPE_PERMISSION',
+}
+
+/** Fetch and validate manifest. */
+export type AppFetchManifest = {
+ __typename?: 'AppFetchManifest'
+ manifest?: Maybe
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ appErrors: Array
+ errors: Array
+}
+
+export type AppFilterInput = {
+ search?: Maybe
+ isActive?: Maybe
+ type?: Maybe
+}
+
+export type AppInput = {
+ /** Name of the app. */
+ name?: Maybe
+ /** List of permission code names to assign to this app. */
+ permissions?: Maybe>>
+}
+
+/** Install new app by using app manifest. */
+export type AppInstall = {
+ __typename?: 'AppInstall'
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ appErrors: Array
+ errors: Array
+ appInstallation?: Maybe
+}
+
+export type AppInstallInput = {
+ /** Name of the app to install. */
+ appName?: Maybe
+ /** Url to app's manifest in JSON format. */
+ manifestUrl?: Maybe
+ /** Determine if app will be set active or not. */
+ activateAfterInstallation?: Maybe
+ /** List of permission code names to assign to this app. */
+ permissions?: Maybe>>
+}
+
+/** Represents ongoing installation of app. */
+export type AppInstallation = Node &
+ Job & {
+ __typename?: 'AppInstallation'
+ appName: Scalars['String']
+ manifestUrl: Scalars['String']
+ /** The ID of the object. */
+ id: Scalars['ID']
+ /** Job status. */
+ status: JobStatusEnum
+ /** Created date time of job in ISO 8601 format. */
+ createdAt: Scalars['DateTime']
+ /** Date time of job last update in ISO 8601 format. */
+ updatedAt: Scalars['DateTime']
+ /** Job message. */
+ message?: Maybe
+ }
+
+/** Retry failed installation of new app. */
+export type AppRetryInstall = {
+ __typename?: 'AppRetryInstall'
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ appErrors: Array
+ errors: Array
+ appInstallation?: Maybe
+}
+
+export enum AppSortField {
+ /** Sort apps by name. */
+ Name = 'NAME',
+ /** Sort apps by creation date. */
+ CreationDate = 'CREATION_DATE',
+}
+
+export type AppSortingInput = {
+ /** Specifies the direction in which to sort products. */
+ direction: OrderDirection
+ /** Sort apps by the selected field. */
+ field: AppSortField
+}
+
+/** Represents token data. */
+export type AppToken = Node & {
+ __typename?: 'AppToken'
+ /** Name of the authenticated token. */
+ name?: Maybe
+ /** Last 4 characters of the token. */
+ authToken?: Maybe
+ /** The ID of the object. */
+ id: Scalars['ID']
+}
+
+/** Creates a new token. */
+export type AppTokenCreate = {
+ __typename?: 'AppTokenCreate'
+ /** The newly created authentication token. */
+ authToken?: Maybe
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ appErrors: Array
+ errors: Array
+ appToken?: Maybe
+}
+
+/** Deletes an authentication token assigned to app. */
+export type AppTokenDelete = {
+ __typename?: 'AppTokenDelete'
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ appErrors: Array
+ errors: Array
+ appToken?: Maybe
+}
+
+export type AppTokenInput = {
+ /** Name of the token. */
+ name?: Maybe
+ /** ID of app. */
+ app: Scalars['ID']
+}
+
+/** Verify provided app token. */
+export type AppTokenVerify = {
+ __typename?: 'AppTokenVerify'
+ /** Determine if token is valid or not. */
+ valid: Scalars['Boolean']
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ appErrors: Array
+ errors: Array
+}
+
+/** Enum determining type of your App. */
+export enum AppTypeEnum {
+ /** Local Saleor App. The app is fully manageable from dashboard. You can change assigned permissions, add webhooks, or authentication token */
+ Local = 'LOCAL',
+ /** Third party external App. Installation is fully automated. Saleor uses a defined App manifest to gather all required information. */
+ Thirdparty = 'THIRDPARTY',
+}
+
+/** Updates an existing app. */
+export type AppUpdate = {
+ __typename?: 'AppUpdate'
+ /** @deprecated Use errors field instead. This field will be removed in Saleor 4.0. */
+ appErrors: Array
+ errors: Array
+ app?: Maybe
+}
+
+/** An enumeration. */
+export enum AreaUnitsEnum {
+ SqCm = 'SQ_CM',
+ SqM = 'SQ_M',
+ SqKm = 'SQ_KM',
+ SqFt = 'SQ_FT',
+ SqYd = 'SQ_YD',
+ SqInch = 'SQ_INCH',
+}
+
+/** Assigns storefront's navigation menus. */
+export type AssignNavigation = {
+ __typename?: 'AssignNavigation'
+ /** Assigned navigation menu. */
+ menu?: Maybe