diff --git a/.gitignore b/.gitignore
index 640afa9c9..bcbf6047a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,6 +32,3 @@ yarn-error.log*
# vercel
.vercel
-
-# dev
-framework
diff --git a/framework/bigcommerce/README.md b/framework/bigcommerce/README.md
new file mode 100644
index 000000000..7beee6c14
--- /dev/null
+++ b/framework/bigcommerce/README.md
@@ -0,0 +1,383 @@
+
+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
+
+> This project is under active development, new features and updates will be continuously added over time
+
+UI hooks and data fetching methods built from the ground up for e-commerce applications written in React, that use BigCommerce as a headless e-commerce platform. The package provides:
+
+- Code splitted hooks for data fetching using [SWR](https://swr.vercel.app/), and to handle common user actions
+- Code splitted data fetching methods for initial data population and static generation of content
+- Helpers to create the API endpoints that connect to the hooks, very well suited for Next.js applications
+
+## Installation
+
+To install:
+
+```
+yarn add storefront-data-hooks
+```
+
+After install, the first thing you do is: set your environment variables in `.env.local`
+
+```sh
+BIGCOMMERCE_STOREFRONT_API_URL=<>
+BIGCOMMERCE_STOREFRONT_API_TOKEN=<>
+BIGCOMMERCE_STORE_API_URL=<>
+BIGCOMMERCE_STORE_API_TOKEN=<>
+BIGCOMMERCE_STORE_API_CLIENT_ID=<>
+```
+
+## General Usage
+
+### CommerceProvider
+
+This component is a provider pattern component that creates commerce context for it's children. It takes config values for the locale and an optional `fetcherRef` object for data fetching.
+
+```jsx
+...
+import { CommerceProvider } from '@bigcommerce/storefront-data-hooks'
+
+const App = ({ locale = 'en-US', children }) => {
+ return (
+
+ {children}
+
+ )
+}
+...
+```
+
+### useLogin hook
+
+Hook for bigcommerce user login functionality, returns `login` function to handle user login.
+
+```jsx
+...
+import useLogin from '@bigcommerce/storefront-data-hooks/use-login'
+
+const LoginView = () => {
+ const login = useLogin()
+
+ const handleLogin = async () => {
+ await login({
+ email,
+ password,
+ })
+ }
+
+ return (
+
+ )
+}
+```
+
+### useSignup
+
+Hook for bigcommerce user signup, returns `signup` function to handle user signups.
+
+```jsx
+...
+import useSignup from '@bigcommerce/storefront-data-hooks/use-login'
+
+const SignupView = () => {
+ const signup = useSignup()
+
+ const handleSignup = async () => {
+ await signup({
+ email,
+ firstName,
+ lastName,
+ password,
+ })
+ }
+
+ return (
+
+ )
+}
+...
+```
+
+### usePrice
+
+Helper hook to format price according to commerce locale, and return discount if available.
+
+```jsx
+import usePrice from '@bigcommerce/storefront-data-hooks/use-price'
+...
+ const { price, discount, basePrice } = usePrice(
+ data && {
+ amount: data.cart_amount,
+ currencyCode: data.currency.code,
+ }
+ )
+...
+```
+
+## Cart Hooks
+
+### useCart
+
+Returns the current cart data for use
+
+```jsx
+...
+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 CartNumber = () => {
+ const { data } = useCart()
+ const itemsCount = Object.values(data?.line_items ?? {}).reduce(countItems, 0)
+
+ return itemsCount > 0 ? {itemsCount} : null
+}
+```
+
+### useAddItem
+
+```jsx
+...
+import useAddItem from '@bigcommerce/storefront-data-hooks/cart/use-add-item'
+
+const AddToCartButton = ({ productId, variantId }) => {
+ const addItem = useAddItem()
+
+ const addToCart = async () => {
+ await addItem({
+ productId,
+ variantId,
+ })
+ }
+
+ return
+}
+...
+```
+
+### useUpdateItem
+
+```jsx
+...
+import useUpdateItem from '@bigcommerce/storefront-data-hooks/cart/use-update-item'
+
+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 (
+
+ )
+}
+...
+```
+
+### useRemoveItem
+
+Provided with a cartItemId, will remove an item from the cart:
+
+```jsx
+...
+import useRemoveItem from '@bigcommerce/storefront-data-hooks/cart/use-remove-item'
+
+const RemoveButton = ({ item }) => {
+ const removeItem = useRemoveItem()
+
+ const handleRemove = async () => {
+ await removeItem({ id: item.id })
+ }
+
+ return
+}
+...
+```
+
+## Wishlist Hooks
+
+Wishlist hooks are similar to cart hooks. See the below example for how to use `useWishlist`, `useAddItem`, and `useRemoveItem`.
+
+```jsx
+import useAddItem from '@bigcommerce/storefront-data-hooks/wishlist/use-add-item'
+import useRemoveItem from '@bigcommerce/storefront-data-hooks/wishlist/use-remove-item'
+import useWishlist from '@bigcommerce/storefront-data-hooks/wishlist/use-wishlist'
+
+const WishlistButton = ({ productId, variant }) => {
+ const addItem = useAddItem()
+ const removeItem = useRemoveItem()
+ const { data } = useWishlist()
+ const { data: customer } = useCustomer()
+ const itemInWishlist = data?.items?.find(
+ (item) =>
+ item.product_id === productId &&
+ item.variant_id === variant?.node.entityId
+ )
+
+ const handleWishlistChange = async (e) => {
+ e.preventDefault()
+
+ if (!customer) {
+ return
+ }
+
+ if (itemInWishlist) {
+ await removeItem({ id: itemInWishlist.id! })
+ } else {
+ await addItem({
+ productId,
+ variantId: variant?.node.entityId!,
+ })
+ }
+ }
+
+ return (
+
+ )
+}
+```
+
+## Product Hooks and API
+
+### useSearch
+
+`useSearch` handles searching the bigcommerce storefront product catalog by catalog, brand, and query string.
+
+```jsx
+...
+import useSearch from '@bigcommerce/storefront-data-hooks/products/use-search'
+
+const SearchPage = ({ searchString, category, brand, sortStr }) => {
+ const { data } = useSearch({
+ search: searchString || '',
+ categoryId: category?.entityId,
+ brandId: brand?.entityId,
+ sort: sortStr || '',
+ })
+
+ return (
+
+ {data.products.map(({ node }) => (
+
+ ))}
+
+ )
+}
+```
+
+### getAllProducts
+
+API function to retrieve a product list.
+
+```js
+import { getConfig } from '@bigcommerce/storefront-data-hooks/api'
+import getAllProducts from '@bigcommerce/storefront-data-hooks/api/operations/get-all-products'
+
+const { products } = await getAllProducts({
+ variables: { field: 'featuredProducts', first: 6 },
+ config,
+ preview,
+})
+```
+
+### getProduct
+
+API product to retrieve a single product when provided with the product
+slug string.
+
+```js
+import { getConfig } from '@bigcommerce/storefront-data-hooks/api'
+import getProduct from '@bigcommerce/storefront-data-hooks/api/operations/get-product'
+
+const { product } = await getProduct({
+ variables: { slug },
+ config,
+ preview,
+})
+```
+
+## More
+
+Feel free to read through the source for more usage, and check the commerce vercel demo and commerce repo for usage examples: ([demo.vercel.store](https://demo.vercel.store/)) ([repo](https://github.com/vercel/commerce))
diff --git a/framework/bigcommerce/api/cart/handlers/add-item.ts b/framework/bigcommerce/api/cart/handlers/add-item.ts
new file mode 100644
index 000000000..25ae3880e
--- /dev/null
+++ b/framework/bigcommerce/api/cart/handlers/add-item.ts
@@ -0,0 +1,40 @@
+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 },
+ config,
+}) => {
+ if (!item) {
+ return res.status(400).json({
+ data: null,
+ errors: [{ message: 'Missing item' }],
+ })
+ }
+ if (!item.quantity) item.quantity = 1
+
+ const options = {
+ method: 'POST',
+ body: JSON.stringify({
+ line_items: [parseCartItem(item)],
+ ...(!cartId && config.storeChannelId
+ ? { channel_id: config.storeChannelId }
+ : {}),
+ }),
+ }
+ const { data } = cartId
+ ? await config.storeApiFetch(`/v3/carts/${cartId}/items`, options)
+ : await config.storeApiFetch('/v3/carts', options)
+
+ // Create or update the cart cookie
+ res.setHeader(
+ 'Set-Cookie',
+ getCartCookie(config.cartCookie, data.id, config.cartCookieMaxAge)
+ )
+ res.status(200).json({ data })
+}
+
+export default addItem
diff --git a/framework/bigcommerce/api/cart/handlers/get-cart.ts b/framework/bigcommerce/api/cart/handlers/get-cart.ts
new file mode 100644
index 000000000..9fb42d730
--- /dev/null
+++ b/framework/bigcommerce/api/cart/handlers/get-cart.ts
@@ -0,0 +1,29 @@
+import { BigcommerceApiError } from '../../utils/errors'
+import getCartCookie from '../../utils/get-cart-cookie'
+import type { Cart, CartHandlers } from '..'
+
+// Return current cart info
+const getCart: CartHandlers['getCart'] = async ({
+ res,
+ body: { cartId },
+ config,
+}) => {
+ let result: { data?: Cart } = {}
+
+ if (cartId) {
+ try {
+ result = await config.storeApiFetch(`/v3/carts/${cartId}`)
+ } catch (error) {
+ if (error instanceof BigcommerceApiError && error.status === 404) {
+ // Remove the cookie if it exists but the cart wasn't found
+ res.setHeader('Set-Cookie', getCartCookie(config.cartCookie))
+ } else {
+ throw error
+ }
+ }
+ }
+
+ res.status(200).json({ data: result.data ?? null })
+}
+
+export default getCart
diff --git a/framework/bigcommerce/api/cart/handlers/remove-item.ts b/framework/bigcommerce/api/cart/handlers/remove-item.ts
new file mode 100644
index 000000000..2ee5dbe16
--- /dev/null
+++ b/framework/bigcommerce/api/cart/handlers/remove-item.ts
@@ -0,0 +1,34 @@
+import getCartCookie from '../../utils/get-cart-cookie'
+import type { CartHandlers } from '..'
+
+// Return current cart info
+const removeItem: CartHandlers['removeItem'] = async ({
+ res,
+ body: { cartId, itemId },
+ config,
+}) => {
+ if (!cartId || !itemId) {
+ return res.status(400).json({
+ data: null,
+ errors: [{ message: 'Invalid request' }],
+ })
+ }
+
+ const result = await config.storeApiFetch<{ data: any } | null>(
+ `/v3/carts/${cartId}/items/${itemId}`,
+ { method: 'DELETE' }
+ )
+ const data = result?.data ?? null
+
+ res.setHeader(
+ 'Set-Cookie',
+ data
+ ? // Update the cart cookie
+ getCartCookie(config.cartCookie, cartId, config.cartCookieMaxAge)
+ : // Remove the cart cookie if the cart was removed (empty items)
+ getCartCookie(config.cartCookie)
+ )
+ res.status(200).json({ data })
+}
+
+export default removeItem
diff --git a/framework/bigcommerce/api/cart/handlers/update-item.ts b/framework/bigcommerce/api/cart/handlers/update-item.ts
new file mode 100644
index 000000000..c64c111df
--- /dev/null
+++ b/framework/bigcommerce/api/cart/handlers/update-item.ts
@@ -0,0 +1,36 @@
+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 },
+ config,
+}) => {
+ if (!cartId || !itemId || !item) {
+ return res.status(400).json({
+ data: null,
+ errors: [{ message: 'Invalid request' }],
+ })
+ }
+
+ const { data } = await config.storeApiFetch(
+ `/v3/carts/${cartId}/items/${itemId}`,
+ {
+ method: 'PUT',
+ body: JSON.stringify({
+ line_item: parseCartItem(item),
+ }),
+ }
+ )
+
+ // Update the cart cookie
+ res.setHeader(
+ 'Set-Cookie',
+ getCartCookie(config.cartCookie, cartId, config.cartCookieMaxAge)
+ )
+ res.status(200).json({ data })
+}
+
+export default updateItem
diff --git a/framework/bigcommerce/api/cart/index.ts b/framework/bigcommerce/api/cart/index.ts
new file mode 100644
index 000000000..5ff2d975b
--- /dev/null
+++ b/framework/bigcommerce/api/cart/index.ts
@@ -0,0 +1,116 @@
+import isAllowedMethod from '../utils/is-allowed-method'
+import createApiHandler, {
+ BigcommerceApiHandler,
+ BigcommerceHandler,
+} from '../utils/create-api-handler'
+import { BigcommerceApiError } from '../utils/errors'
+import getCart from './handlers/get-cart'
+import addItem from './handlers/add-item'
+import updateItem from './handlers/update-item'
+import removeItem from './handlers/remove-item'
+
+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 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[]
+ }
+ // TODO: add missing fields
+}
+
+export type CartHandlers = {
+ getCart: BigcommerceHandler
+ addItem: BigcommerceHandler>
+ updateItem: BigcommerceHandler<
+ Cart,
+ { cartId?: string } & Partial
+ >
+ removeItem: BigcommerceHandler<
+ Cart,
+ { cartId?: string } & Partial
+ >
+}
+
+const METHODS = ['GET', 'POST', 'PUT', 'DELETE']
+
+// TODO: a complete implementation should have schema validation for `req.body`
+const cartApi: BigcommerceApiHandler = async (
+ req,
+ res,
+ config,
+ handlers
+) => {
+ if (!isAllowedMethod(req, res, METHODS)) return
+
+ const { cookies } = req
+ const cartId = cookies[config.cartCookie]
+
+ try {
+ // Return current cart info
+ if (req.method === 'GET') {
+ const body = { cartId }
+ return await handlers['getCart']({ req, res, config, body })
+ }
+
+ // Create or add an item to the cart
+ if (req.method === 'POST') {
+ const body = { ...req.body, cartId }
+ return await handlers['addItem']({ req, res, config, body })
+ }
+
+ // Update item in cart
+ if (req.method === 'PUT') {
+ const body = { ...req.body, cartId }
+ return await handlers['updateItem']({ req, res, config, body })
+ }
+
+ // Remove an item from the cart
+ if (req.method === 'DELETE') {
+ const body = { ...req.body, cartId }
+ return await handlers['removeItem']({ req, res, config, body })
+ }
+ } catch (error) {
+ console.error(error)
+
+ const message =
+ error instanceof BigcommerceApiError
+ ? 'An unexpected error ocurred with the Bigcommerce API'
+ : 'An unexpected error ocurred'
+
+ res.status(500).json({ data: null, errors: [{ message }] })
+ }
+}
+
+export const handlers = { getCart, addItem, updateItem, removeItem }
+
+export default createApiHandler(cartApi, handlers, {})
diff --git a/framework/bigcommerce/api/catalog/handlers/get-products.ts b/framework/bigcommerce/api/catalog/handlers/get-products.ts
new file mode 100644
index 000000000..b05548e40
--- /dev/null
+++ b/framework/bigcommerce/api/catalog/handlers/get-products.ts
@@ -0,0 +1,73 @@
+import getAllProducts, { ProductEdge } from '../../operations/get-all-products'
+import type { ProductsHandlers } from '../products'
+
+const SORT: { [key: string]: string | undefined } = {
+ latest: 'id',
+ trending: 'total_sold',
+ price: 'price',
+}
+const LIMIT = 12
+
+// Return current cart info
+const getProducts: ProductsHandlers['getProducts'] = async ({
+ res,
+ body: { search, category, brand, sort },
+ config,
+}) => {
+ // Use a dummy base as we only care about the relative path
+ const url = new URL('/v3/catalog/products', 'http://a')
+
+ url.searchParams.set('is_visible', 'true')
+ url.searchParams.set('limit', String(LIMIT))
+
+ if (search) url.searchParams.set('keyword', search)
+
+ if (category && Number.isInteger(Number(category)))
+ url.searchParams.set('categories:in', category)
+
+ if (brand && Number.isInteger(Number(brand)))
+ url.searchParams.set('brand_id', brand)
+
+ if (sort) {
+ const [_sort, direction] = sort.split('-')
+ const sortValue = SORT[_sort]
+
+ if (sortValue && direction) {
+ url.searchParams.set('sort', sortValue)
+ url.searchParams.set('direction', direction)
+ }
+ }
+
+ // We only want the id of each product
+ url.searchParams.set('include_fields', 'id')
+
+ 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
+ }>((prods, p) => {
+ prods[p.node.entityId] = p
+ return prods
+ }, {})
+ const products: ProductEdge[] = found ? [] : graphqlData.products
+
+ // Populate the products array with the graphql products, in the order
+ // assigned by the list of entity ids
+ entityIds.forEach((id) => {
+ const product = productsById[id]
+ if (product) products.push(product)
+ })
+
+ res.status(200).json({ data: { products, found } })
+}
+
+export default getProducts
diff --git a/framework/bigcommerce/api/catalog/products.ts b/framework/bigcommerce/api/catalog/products.ts
new file mode 100644
index 000000000..0e3690e55
--- /dev/null
+++ b/framework/bigcommerce/api/catalog/products.ts
@@ -0,0 +1,48 @@
+import isAllowedMethod from '../utils/is-allowed-method'
+import createApiHandler, {
+ BigcommerceApiHandler,
+ 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'
+
+export type SearchProductsData = {
+ products: ProductEdge[]
+ found: boolean
+}
+
+export type ProductsHandlers = {
+ getProducts: BigcommerceHandler<
+ SearchProductsData,
+ { search?: 'string'; category?: string; brand?: string; sort?: string }
+ >
+}
+
+const METHODS = ['GET']
+
+// TODO: a complete implementation should have schema validation for `req.body`
+const productsApi: BigcommerceApiHandler<
+ SearchProductsData,
+ ProductsHandlers
+> = async (req, res, config, handlers) => {
+ if (!isAllowedMethod(req, res, METHODS)) return
+
+ try {
+ const body = req.query
+ return await handlers['getProducts']({ req, res, config, body })
+ } catch (error) {
+ console.error(error)
+
+ const message =
+ error instanceof BigcommerceApiError
+ ? 'An unexpected error ocurred with the Bigcommerce API'
+ : 'An unexpected error ocurred'
+
+ res.status(500).json({ data: null, errors: [{ message }] })
+ }
+}
+
+export const handlers = { getProducts }
+
+export default createApiHandler(productsApi, handlers, {})
diff --git a/framework/bigcommerce/api/checkout.ts b/framework/bigcommerce/api/checkout.ts
new file mode 100644
index 000000000..530f4c40a
--- /dev/null
+++ b/framework/bigcommerce/api/checkout.ts
@@ -0,0 +1,77 @@
+import isAllowedMethod from './utils/is-allowed-method'
+import createApiHandler, {
+ BigcommerceApiHandler,
+} from './utils/create-api-handler'
+import { BigcommerceApiError } from './utils/errors'
+
+const METHODS = ['GET']
+const fullCheckout = true
+
+// TODO: a complete implementation should have schema validation for `req.body`
+const checkoutApi: BigcommerceApiHandler = async (req, res, config) => {
+ if (!isAllowedMethod(req, res, METHODS)) return
+
+ const { cookies } = req
+ const cartId = cookies[config.cartCookie]
+
+ try {
+ if (!cartId) {
+ res.redirect('/cart')
+ return
+ }
+
+ const { data } = await config.storeApiFetch(
+ `/v3/carts/${cartId}/redirect_urls`,
+ {
+ method: 'POST',
+ }
+ )
+
+ if (fullCheckout) {
+ res.redirect(data.checkout_url)
+ return
+ }
+
+ // TODO: make the embedded checkout work too!
+ const html = `
+
+
+
+
+
+ Checkout
+
+
+
+
+
+
+
+ `
+
+ res.status(200)
+ res.setHeader('Content-Type', 'text/html')
+ res.write(html)
+ res.end()
+ } catch (error) {
+ console.error(error)
+
+ const message =
+ error instanceof BigcommerceApiError
+ ? 'An unexpected error ocurred with the Bigcommerce API'
+ : 'An unexpected error ocurred'
+
+ res.status(500).json({ data: null, errors: [{ message }] })
+ }
+}
+
+export default createApiHandler(checkoutApi, {}, {})
diff --git a/framework/bigcommerce/api/customers/handlers/get-logged-in-customer.ts b/framework/bigcommerce/api/customers/handlers/get-logged-in-customer.ts
new file mode 100644
index 000000000..698235dda
--- /dev/null
+++ b/framework/bigcommerce/api/customers/handlers/get-logged-in-customer.ts
@@ -0,0 +1,59 @@
+import type { GetLoggedInCustomerQuery } from '../../../schema'
+import type { CustomersHandlers } from '..'
+
+export const getLoggedInCustomerQuery = /* GraphQL */ `
+ query getLoggedInCustomer {
+ customer {
+ entityId
+ firstName
+ lastName
+ email
+ company
+ customerGroupId
+ notes
+ phone
+ addressCount
+ attributeCount
+ storeCredit {
+ value
+ currencyCode
+ }
+ }
+ }
+`
+
+export type Customer = NonNullable
+
+const getLoggedInCustomer: CustomersHandlers['getLoggedInCustomer'] = async ({
+ req,
+ res,
+ config,
+}) => {
+ const token = req.cookies[config.customerCookie]
+
+ if (token) {
+ const { data } = await config.fetch(
+ getLoggedInCustomerQuery,
+ undefined,
+ {
+ headers: {
+ cookie: `${config.customerCookie}=${token}`,
+ },
+ }
+ )
+ const { customer } = data
+
+ if (!customer) {
+ return res.status(400).json({
+ data: null,
+ errors: [{ message: 'Customer not found', code: 'not_found' }],
+ })
+ }
+
+ return res.status(200).json({ data: { customer } })
+ }
+
+ res.status(200).json({ data: null })
+}
+
+export default getLoggedInCustomer
diff --git a/framework/bigcommerce/api/customers/handlers/login.ts b/framework/bigcommerce/api/customers/handlers/login.ts
new file mode 100644
index 000000000..c4062f6cc
--- /dev/null
+++ b/framework/bigcommerce/api/customers/handlers/login.ts
@@ -0,0 +1,49 @@
+import { FetcherError } from '@commerce/utils/errors'
+import login from '../../operations/login'
+import type { LoginHandlers } from '../login'
+
+const invalidCredentials = /invalid credentials/i
+
+const loginHandler: LoginHandlers['login'] = async ({
+ res,
+ body: { email, password },
+ config,
+}) => {
+ // TODO: Add proper validations with something like Ajv
+ if (!(email && password)) {
+ return res.status(400).json({
+ data: null,
+ errors: [{ message: 'Invalid request' }],
+ })
+ }
+ // TODO: validate the password and email
+ // Passwords must be at least 7 characters and contain both alphabetic
+ // and numeric characters.
+
+ try {
+ await login({ variables: { email, password }, config, res })
+ } catch (error) {
+ // Check if the email and password didn't match an existing account
+ if (
+ error instanceof FetcherError &&
+ invalidCredentials.test(error.message)
+ ) {
+ 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/bigcommerce/api/customers/handlers/logout.ts b/framework/bigcommerce/api/customers/handlers/logout.ts
new file mode 100644
index 000000000..937ce0954
--- /dev/null
+++ b/framework/bigcommerce/api/customers/handlers/logout.ts
@@ -0,0 +1,23 @@
+import { serialize } from 'cookie'
+import { LogoutHandlers } from '../logout'
+
+const logoutHandler: LogoutHandlers['logout'] = async ({
+ res,
+ body: { redirectTo },
+ config,
+}) => {
+ // Remove the cookie
+ res.setHeader(
+ 'Set-Cookie',
+ serialize(config.customerCookie, '', { maxAge: -1, path: '/' })
+ )
+
+ // Only allow redirects to a relative URL
+ if (redirectTo?.startsWith('/')) {
+ res.redirect(redirectTo)
+ } else {
+ res.status(200).json({ data: null })
+ }
+}
+
+export default logoutHandler
diff --git a/framework/bigcommerce/api/customers/handlers/signup.ts b/framework/bigcommerce/api/customers/handlers/signup.ts
new file mode 100644
index 000000000..ff45adadb
--- /dev/null
+++ b/framework/bigcommerce/api/customers/handlers/signup.ts
@@ -0,0 +1,62 @@
+import { BigcommerceApiError } from '../../utils/errors'
+import login from '../../operations/login'
+import { SignupHandlers } from '../signup'
+
+const signup: SignupHandlers['signup'] = async ({
+ res,
+ body: { firstName, lastName, email, password },
+ config,
+}) => {
+ // TODO: Add proper validations with something like Ajv
+ if (!(firstName && lastName && email && password)) {
+ return res.status(400).json({
+ data: null,
+ errors: [{ message: 'Invalid request' }],
+ })
+ }
+ // TODO: validate the password and email
+ // Passwords must be at least 7 characters and contain both alphabetic
+ // and numeric characters.
+
+ try {
+ await config.storeApiFetch('/v3/customers', {
+ method: 'POST',
+ body: JSON.stringify([
+ {
+ first_name: firstName,
+ last_name: lastName,
+ email,
+ authentication: {
+ new_password: password,
+ },
+ },
+ ]),
+ })
+ } catch (error) {
+ if (error instanceof BigcommerceApiError && error.status === 422) {
+ const hasEmailError = '0.email' in error.data?.errors
+
+ // If there's an error with the email, it most likely means it's duplicated
+ if (hasEmailError) {
+ return res.status(400).json({
+ data: null,
+ errors: [
+ {
+ message: 'The email is already in use',
+ code: 'duplicated_email',
+ },
+ ],
+ })
+ }
+ }
+
+ throw error
+ }
+
+ // Login the customer right after creating it
+ await login({ variables: { email, password }, res, config })
+
+ res.status(200).json({ data: null })
+}
+
+export default signup
diff --git a/framework/bigcommerce/api/customers/index.ts b/framework/bigcommerce/api/customers/index.ts
new file mode 100644
index 000000000..5af4d1d1d
--- /dev/null
+++ b/framework/bigcommerce/api/customers/index.ts
@@ -0,0 +1,46 @@
+import createApiHandler, {
+ BigcommerceApiHandler,
+ BigcommerceHandler,
+} from '../utils/create-api-handler'
+import isAllowedMethod from '../utils/is-allowed-method'
+import { BigcommerceApiError } from '../utils/errors'
+import getLoggedInCustomer, {
+ Customer,
+} from './handlers/get-logged-in-customer'
+
+export type { Customer }
+
+export type CustomerData = {
+ customer: Customer
+}
+
+export type CustomersHandlers = {
+ getLoggedInCustomer: BigcommerceHandler
+}
+
+const METHODS = ['GET']
+
+const customersApi: BigcommerceApiHandler<
+ CustomerData,
+ CustomersHandlers
+> = async (req, res, config, handlers) => {
+ if (!isAllowedMethod(req, res, METHODS)) return
+
+ try {
+ const body = null
+ return await handlers['getLoggedInCustomer']({ req, res, config, body })
+ } catch (error) {
+ console.error(error)
+
+ const message =
+ error instanceof BigcommerceApiError
+ ? 'An unexpected error ocurred with the Bigcommerce API'
+ : 'An unexpected error ocurred'
+
+ res.status(500).json({ data: null, errors: [{ message }] })
+ }
+}
+
+const handlers = { getLoggedInCustomer }
+
+export default createApiHandler(customersApi, handlers, {})
diff --git a/framework/bigcommerce/api/customers/login.ts b/framework/bigcommerce/api/customers/login.ts
new file mode 100644
index 000000000..e8f24a92d
--- /dev/null
+++ b/framework/bigcommerce/api/customers/login.ts
@@ -0,0 +1,45 @@
+import createApiHandler, {
+ BigcommerceApiHandler,
+ BigcommerceHandler,
+} from '../utils/create-api-handler'
+import isAllowedMethod from '../utils/is-allowed-method'
+import { BigcommerceApiError } from '../utils/errors'
+import login from './handlers/login'
+
+export type LoginBody = {
+ email: string
+ password: string
+}
+
+export type LoginHandlers = {
+ login: BigcommerceHandler>
+}
+
+const METHODS = ['POST']
+
+const loginApi: BigcommerceApiHandler = async (
+ req,
+ res,
+ config,
+ handlers
+) => {
+ if (!isAllowedMethod(req, res, METHODS)) return
+
+ try {
+ const body = req.body ?? {}
+ return await handlers['login']({ req, res, config, body })
+ } catch (error) {
+ console.error(error)
+
+ const message =
+ error instanceof BigcommerceApiError
+ ? 'An unexpected error ocurred with the Bigcommerce API'
+ : 'An unexpected error ocurred'
+
+ res.status(500).json({ data: null, errors: [{ message }] })
+ }
+}
+
+const handlers = { login }
+
+export default createApiHandler(loginApi, handlers, {})
diff --git a/framework/bigcommerce/api/customers/logout.ts b/framework/bigcommerce/api/customers/logout.ts
new file mode 100644
index 000000000..0a4965245
--- /dev/null
+++ b/framework/bigcommerce/api/customers/logout.ts
@@ -0,0 +1,42 @@
+import createApiHandler, {
+ BigcommerceApiHandler,
+ BigcommerceHandler,
+} from '../utils/create-api-handler'
+import isAllowedMethod from '../utils/is-allowed-method'
+import { BigcommerceApiError } from '../utils/errors'
+import logout from './handlers/logout'
+
+export type LogoutHandlers = {
+ logout: BigcommerceHandler
+}
+
+const METHODS = ['GET']
+
+const logoutApi: BigcommerceApiHandler = async (
+ req,
+ res,
+ config,
+ handlers
+) => {
+ if (!isAllowedMethod(req, res, METHODS)) return
+
+ try {
+ const redirectTo = req.query.redirect_to
+ const body = typeof redirectTo === 'string' ? { redirectTo } : {}
+
+ return await handlers['logout']({ req, res, config, body })
+ } catch (error) {
+ console.error(error)
+
+ const message =
+ error instanceof BigcommerceApiError
+ ? 'An unexpected error ocurred with the Bigcommerce API'
+ : 'An unexpected error ocurred'
+
+ res.status(500).json({ data: null, errors: [{ message }] })
+ }
+}
+
+const handlers = { logout }
+
+export default createApiHandler(logoutApi, handlers, {})
diff --git a/framework/bigcommerce/api/customers/signup.ts b/framework/bigcommerce/api/customers/signup.ts
new file mode 100644
index 000000000..aa26f78cf
--- /dev/null
+++ b/framework/bigcommerce/api/customers/signup.ts
@@ -0,0 +1,50 @@
+import createApiHandler, {
+ BigcommerceApiHandler,
+ BigcommerceHandler,
+} from '../utils/create-api-handler'
+import isAllowedMethod from '../utils/is-allowed-method'
+import { BigcommerceApiError } from '../utils/errors'
+import signup from './handlers/signup'
+
+export type SignupBody = {
+ firstName: string
+ lastName: string
+ email: string
+ password: string
+}
+
+export type SignupHandlers = {
+ signup: BigcommerceHandler>
+}
+
+const METHODS = ['POST']
+
+const signupApi: BigcommerceApiHandler = async (
+ req,
+ res,
+ config,
+ handlers
+) => {
+ if (!isAllowedMethod(req, res, METHODS)) return
+
+ const { cookies } = req
+ const cartId = cookies[config.cartCookie]
+
+ try {
+ const body = { ...req.body, cartId }
+ return await handlers['signup']({ req, res, config, body })
+ } catch (error) {
+ console.error(error)
+
+ const message =
+ error instanceof BigcommerceApiError
+ ? 'An unexpected error ocurred with the Bigcommerce API'
+ : 'An unexpected error ocurred'
+
+ res.status(500).json({ data: null, errors: [{ message }] })
+ }
+}
+
+const handlers = { signup }
+
+export default createApiHandler(signupApi, handlers, {})
diff --git a/framework/bigcommerce/api/definitions/catalog.ts b/framework/bigcommerce/api/definitions/catalog.ts
new file mode 100644
index 000000000..2c483f781
--- /dev/null
+++ b/framework/bigcommerce/api/definitions/catalog.ts
@@ -0,0 +1,2993 @@
+/**
+ * This file was auto-generated by swagger-to-ts.
+ * Do not make direct changes to the file.
+ */
+
+export interface definitions {
+ /**
+ * Common Modifier properties.
+ */
+ productModifier_Base: {
+ /**
+ * BigCommerce API, which determines how it will display on the storefront. Acceptable values: `date`, `checkbox`, `file`, `text`, `multi_line_text`, `numbers_only_text`, `radio_buttons`, `rectangles`, `dropdown`, `product_list`, `product_list_with_images`, `swatch`. Required in a /POST.
+ */
+ type:
+ | 'date'
+ | 'checkbox'
+ | 'file'
+ | 'text'
+ | 'multi_line_text'
+ | 'numbers_only_text'
+ | 'radio_buttons'
+ | 'rectangles'
+ | 'dropdown'
+ | 'product_list'
+ | 'product_list_with_images'
+ | 'swatch'
+ /**
+ * Whether or not this modifer is required or not at checkout. Required in a /POST.
+ */
+ required: boolean
+ /**
+ * The order the modifiers display on the product detail page.
+ */
+ sort_order?: number
+ config?: definitions['config_Full']
+ /**
+ * The name of the option shown on the storefront.
+ */
+ display_name?: string
+ }
+ /**
+ * Product Modifier
+ */
+ productModifier_Full: definitions['productModifier_Base'] & {
+ /**
+ * The unique numeric ID of the modifier; increments sequentially.
+ */
+ id?: number
+ /**
+ * The unique numeric ID of the product to which the option belongs.
+ */
+ product_id?: number
+ /**
+ * The unique option name. Auto-generated from the display name, a timestamp, and the product ID.
+ */
+ name?: string
+ option_values?: definitions['productModifierOptionValue_Full'][]
+ }
+ /**
+ * The model for a POST to create a modifier on a product.
+ */
+ productModifier_Post: {
+ /**
+ * BigCommerce API, which determines how it will display on the storefront. Acceptable values: `date`, `checkbox`, `file`, `text`, `multi_line_text`, `numbers_only_text`, `radio_buttons`, `rectangles`, `dropdown`, `product_list`, `product_list_with_images`, `swatch`. Required in a /POST.
+ */
+ type:
+ | 'date'
+ | 'checkbox'
+ | 'file'
+ | 'text'
+ | 'multi_line_text'
+ | 'numbers_only_text'
+ | 'radio_buttons'
+ | 'rectangles'
+ | 'dropdown'
+ | 'product_list'
+ | 'product_list_with_images'
+ | 'swatch'
+ /**
+ * Whether or not this modifer is required or not at checkout. Required in a /POST.
+ */
+ required: boolean
+ /**
+ * The order the modifiers display on the product detail page.
+ */
+ sort_order?: number
+ /**
+ * The values for option config can vary based on the Modifier created.
+ */
+ config?: {
+ /**
+ * (date, text, multi_line_text, numbers_only_text) The default value. Shown on a date option as an ISO-8601–formatted string, or on a text option as a string.
+ */
+ default_value?: string
+ /**
+ * (checkbox) Flag for setting the checkbox to be checked by default.
+ */
+ checked_by_default?: boolean
+ /**
+ * (checkbox) Label displayed for the checkbox option.
+ */
+ checkbox_label?: string
+ /**
+ * (date) Flag to limit the dates allowed to be entered on a date option.
+ */
+ date_limited?: boolean
+ /**
+ * (date) The type of limit that is allowed to be entered on a date option.
+ */
+ date_limit_mode?: 'earliest' | 'range' | 'latest'
+ /**
+ * (date) The earliest date allowed to be entered on the date option, as an ISO-8601 formatted string.
+ */
+ date_earliest_value?: string
+ /**
+ * (date) The latest date allowed to be entered on the date option, as an ISO-8601 formatted string.
+ */
+ date_latest_value?: string
+ /**
+ * (file) The kind of restriction on the file types that can be uploaded with a file upload option. Values: `specific` - restricts uploads to particular file types; `all` - allows all file types.
+ */
+ file_types_mode?: 'specific' | 'all'
+ /**
+ * (file) The type of files allowed to be uploaded if the `file_type_option` is set to `specific`. Values:
+ * `images` - Allows upload of image MIME types (`bmp`, `gif`, `jpg`, `jpeg`, `jpe`, `jif`, `jfif`, `jfi`, `png`, `wbmp`, `xbm`, `tiff`). `documents` - Allows upload of document MIME types (`txt`, `pdf`, `rtf`, `doc`, `docx`, `xls`, `xlsx`, `accdb`, `mdb`, `one`, `pps`, `ppsx`, `ppt`, `pptx`, `pub`, `odt`, `ods`, `odp`, `odg`, `odf`).
+ * `other` - Allows file types defined in the `file_types_other` array.
+ */
+ file_types_supported?: string[]
+ /**
+ * (file) A list of other file types allowed with the file upload option.
+ */
+ file_types_other?: string[]
+ /**
+ * (file) The maximum size for a file that can be used with the file upload option. This will still be limited by the server.
+ */
+ file_max_size?: number
+ /**
+ * (text, multi_line_text) Flag to validate the length of a text or multi-line text input.
+ */
+ text_characters_limited?: boolean
+ /**
+ * (text, multi_line_text) The minimum length allowed for a text or multi-line text option.
+ */
+ text_min_length?: number
+ /**
+ * (text, multi_line_text) The maximum length allowed for a text or multi line text option.
+ */
+ text_max_length?: number
+ /**
+ * (multi_line_text) Flag to validate the maximum number of lines allowed on a multi-line text input.
+ */
+ text_lines_limited?: boolean
+ /**
+ * (multi_line_text) The maximum number of lines allowed on a multi-line text input.
+ */
+ text_max_lines?: number
+ /**
+ * (numbers_only_text) Flag to limit the value of a number option.
+ */
+ number_limited?: boolean
+ /**
+ * (numbers_only_text) The type of limit on values entered for a number option.
+ */
+ number_limit_mode?: 'lowest' | 'highest' | 'range'
+ /**
+ * (numbers_only_text) The lowest allowed value for a number option if `number_limited` is true.
+ */
+ number_lowest_value?: number
+ /**
+ * (numbers_only_text) The highest allowed value for a number option if `number_limited` is true.
+ */
+ number_highest_value?: number
+ /**
+ * (numbers_only_text) Flag to limit the input on a number option to whole numbers only.
+ */
+ number_integers_only?: boolean
+ /**
+ * (product_list, product_list_with_images) Flag for automatically adjusting inventory on a product included in the list.
+ */
+ product_list_adjusts_inventory?: boolean
+ /**
+ * (product_list, product_list_with_images) Flag to add the optional product's price to the main product's price.
+ */
+ product_list_adjusts_pricing?: boolean
+ /**
+ * (product_list, product_list_with_images) How to factor the optional product's weight and package dimensions into the shipping quote. Values: `none` - don't adjust; `weight` - use shipping weight only; `package` - use weight and dimensions.
+ */
+ product_list_shipping_calc?: 'none' | 'weight' | 'package'
+ }
+ option_values?: (({
+ /**
+ * The flag for preselecting a value as the default on the storefront. This field is not supported for swatch options/modifiers.
+ */
+ is_default?: boolean
+ /**
+ * The text display identifying the value on the storefront. Required in a /POST.
+ */
+ label: string
+ /**
+ * The order in which the value will be displayed on the product page. Required in a /POST.
+ */
+ sort_order: number
+ /**
+ * Extra data describing the value, based on the type of option or modifier with which the value is associated. The `swatch` type option can accept an array of `colors`, with up to three hexidecimal color keys; or an `image_url`, which is a full image URL path including protocol. The `product list` type option requires a `product_id`. The `checkbox` type option requires a boolean flag, called `checked_value`, to determine which value is considered to be the checked state.
+ */
+ value_data?: { [key: string]: any }
+ } & {
+ adjusters?: {
+ /**
+ * Adjuster for Complex Rules.
+ */
+ price?: {
+ /**
+ * The type of adjuster for either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster?: 'relative' | 'percentage'
+ /**
+ * The numeric amount by which the adjuster will change either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster_value?: number
+ }
+ /**
+ * Adjuster for Complex Rules.
+ */
+ weight?: {
+ /**
+ * The type of adjuster for either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster?: 'relative' | 'percentage'
+ /**
+ * The numeric amount by which the adjuster will change either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster_value?: number
+ }
+ /**
+ * The URL for an image displayed on the storefront when the modifier value is selected.Limit of 8MB per file.
+ */
+ image_url?: string
+ purchasing_disabled?: {
+ /**
+ * Flag for whether the modifier value disables purchasing when selected on the storefront. This can be used for temporarily disabling a particular modifier value.
+ */
+ status?: boolean
+ /**
+ * The message displayed on the storefront when the purchasing disabled status is `true`.
+ */
+ message?: string
+ }
+ }
+ }) & {
+ /**
+ * The unique numeric ID of the value; increments sequentially.
+ */
+ id?: number
+ })[]
+ } & {
+ /**
+ * The name of the option shown on the storefront.
+ */
+ display_name: string
+ }
+ /**
+ * The model for a PUT to update a modifier on a product.
+ */
+ productModifier_Put: {
+ /**
+ * BigCommerce API, which determines how it will display on the storefront. Acceptable values: `date`, `checkbox`, `file`, `text`, `multi_line_text`, `numbers_only_text`, `radio_buttons`, `rectangles`, `dropdown`, `product_list`, `product_list_with_images`, `swatch`. Required in a /POST.
+ */
+ type:
+ | 'date'
+ | 'checkbox'
+ | 'file'
+ | 'text'
+ | 'multi_line_text'
+ | 'numbers_only_text'
+ | 'radio_buttons'
+ | 'rectangles'
+ | 'dropdown'
+ | 'product_list'
+ | 'product_list_with_images'
+ | 'swatch'
+ /**
+ * Whether or not this modifer is required or not at checkout. Required in a /POST.
+ */
+ required: boolean
+ /**
+ * The order the modifiers display on the product detail page.
+ */
+ sort_order?: number
+ /**
+ * The values for option config can vary based on the Modifier created.
+ */
+ config?: {
+ /**
+ * (date, text, multi_line_text, numbers_only_text) The default value. Shown on a date option as an ISO-8601–formatted string, or on a text option as a string.
+ */
+ default_value?: string
+ /**
+ * (checkbox) Flag for setting the checkbox to be checked by default.
+ */
+ checked_by_default?: boolean
+ /**
+ * (checkbox) Label displayed for the checkbox option.
+ */
+ checkbox_label?: string
+ /**
+ * (date) Flag to limit the dates allowed to be entered on a date option.
+ */
+ date_limited?: boolean
+ /**
+ * (date) The type of limit that is allowed to be entered on a date option.
+ */
+ date_limit_mode?: 'earliest' | 'range' | 'latest'
+ /**
+ * (date) The earliest date allowed to be entered on the date option, as an ISO-8601 formatted string.
+ */
+ date_earliest_value?: string
+ /**
+ * (date) The latest date allowed to be entered on the date option, as an ISO-8601 formatted string.
+ */
+ date_latest_value?: string
+ /**
+ * (file) The kind of restriction on the file types that can be uploaded with a file upload option. Values: `specific` - restricts uploads to particular file types; `all` - allows all file types.
+ */
+ file_types_mode?: 'specific' | 'all'
+ /**
+ * (file) The type of files allowed to be uploaded if the `file_type_option` is set to `specific`. Values:
+ * `images` - Allows upload of image MIME types (`bmp`, `gif`, `jpg`, `jpeg`, `jpe`, `jif`, `jfif`, `jfi`, `png`, `wbmp`, `xbm`, `tiff`). `documents` - Allows upload of document MIME types (`txt`, `pdf`, `rtf`, `doc`, `docx`, `xls`, `xlsx`, `accdb`, `mdb`, `one`, `pps`, `ppsx`, `ppt`, `pptx`, `pub`, `odt`, `ods`, `odp`, `odg`, `odf`).
+ * `other` - Allows file types defined in the `file_types_other` array.
+ */
+ file_types_supported?: string[]
+ /**
+ * (file) A list of other file types allowed with the file upload option.
+ */
+ file_types_other?: string[]
+ /**
+ * (file) The maximum size for a file that can be used with the file upload option. This will still be limited by the server.
+ */
+ file_max_size?: number
+ /**
+ * (text, multi_line_text) Flag to validate the length of a text or multi-line text input.
+ */
+ text_characters_limited?: boolean
+ /**
+ * (text, multi_line_text) The minimum length allowed for a text or multi-line text option.
+ */
+ text_min_length?: number
+ /**
+ * (text, multi_line_text) The maximum length allowed for a text or multi line text option.
+ */
+ text_max_length?: number
+ /**
+ * (multi_line_text) Flag to validate the maximum number of lines allowed on a multi-line text input.
+ */
+ text_lines_limited?: boolean
+ /**
+ * (multi_line_text) The maximum number of lines allowed on a multi-line text input.
+ */
+ text_max_lines?: number
+ /**
+ * (numbers_only_text) Flag to limit the value of a number option.
+ */
+ number_limited?: boolean
+ /**
+ * (numbers_only_text) The type of limit on values entered for a number option.
+ */
+ number_limit_mode?: 'lowest' | 'highest' | 'range'
+ /**
+ * (numbers_only_text) The lowest allowed value for a number option if `number_limited` is true.
+ */
+ number_lowest_value?: number
+ /**
+ * (numbers_only_text) The highest allowed value for a number option if `number_limited` is true.
+ */
+ number_highest_value?: number
+ /**
+ * (numbers_only_text) Flag to limit the input on a number option to whole numbers only.
+ */
+ number_integers_only?: boolean
+ /**
+ * (product_list, product_list_with_images) Flag for automatically adjusting inventory on a product included in the list.
+ */
+ product_list_adjusts_inventory?: boolean
+ /**
+ * (product_list, product_list_with_images) Flag to add the optional product's price to the main product's price.
+ */
+ product_list_adjusts_pricing?: boolean
+ /**
+ * (product_list, product_list_with_images) How to factor the optional product's weight and package dimensions into the shipping quote. Values: `none` - don't adjust; `weight` - use shipping weight only; `package` - use weight and dimensions.
+ */
+ product_list_shipping_calc?: 'none' | 'weight' | 'package'
+ }
+ option_values?: (({
+ /**
+ * The flag for preselecting a value as the default on the storefront. This field is not supported for swatch options/modifiers.
+ */
+ is_default?: boolean
+ /**
+ * The text display identifying the value on the storefront. Required in a /POST.
+ */
+ label: string
+ /**
+ * The order in which the value will be displayed on the product page. Required in a /POST.
+ */
+ sort_order: number
+ /**
+ * Extra data describing the value, based on the type of option or modifier with which the value is associated. The `swatch` type option can accept an array of `colors`, with up to three hexidecimal color keys; or an `image_url`, which is a full image URL path including protocol. The `product list` type option requires a `product_id`. The `checkbox` type option requires a boolean flag, called `checked_value`, to determine which value is considered to be the checked state.
+ */
+ value_data?: { [key: string]: any }
+ } & {
+ adjusters?: {
+ /**
+ * Adjuster for Complex Rules.
+ */
+ price?: {
+ /**
+ * The type of adjuster for either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster?: 'relative' | 'percentage'
+ /**
+ * The numeric amount by which the adjuster will change either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster_value?: number
+ }
+ /**
+ * Adjuster for Complex Rules.
+ */
+ weight?: {
+ /**
+ * The type of adjuster for either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster?: 'relative' | 'percentage'
+ /**
+ * The numeric amount by which the adjuster will change either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster_value?: number
+ }
+ /**
+ * The URL for an image displayed on the storefront when the modifier value is selected.Limit of 8MB per file.
+ */
+ image_url?: string
+ purchasing_disabled?: {
+ /**
+ * Flag for whether the modifier value disables purchasing when selected on the storefront. This can be used for temporarily disabling a particular modifier value.
+ */
+ status?: boolean
+ /**
+ * The message displayed on the storefront when the purchasing disabled status is `true`.
+ */
+ message?: string
+ }
+ }
+ }) & {
+ /**
+ * The unique numeric ID of the value; increments sequentially.
+ */
+ id?: number
+ })[]
+ }
+ /**
+ * Common Product Modifer `option_value` properties.
+ */
+ productModifierOptionValue_Base: {
+ /**
+ * The flag for preselecting a value as the default on the storefront. This field is not supported for swatch options/modifiers.
+ */
+ is_default?: boolean
+ /**
+ * The text display identifying the value on the storefront. Required in a /POST.
+ */
+ label: string
+ /**
+ * The order in which the value will be displayed on the product page. Required in a /POST.
+ */
+ sort_order: number
+ /**
+ * Extra data describing the value, based on the type of option or modifier with which the value is associated. The `swatch` type option can accept an array of `colors`, with up to three hexidecimal color keys; or an `image_url`, which is a full image URL path including protocol. The `product list` type option requires a `product_id`. The `checkbox` type option requires a boolean flag, called `checked_value`, to determine which value is considered to be the checked state. If no data is available, returns `null`.
+ */
+ value_data?: { [key: string]: any }
+ adjusters?: definitions['adjusters_Full']
+ }
+ /**
+ * Product Modifer `option_value`.
+ */
+ productModifierOptionValue_Full: definitions['productModifierOptionValue_Base'] & {
+ /**
+ * The unique numeric ID of the value; increments sequentially.
+ */
+ id?: number
+ option_id?: number
+ }
+ /**
+ * The model for a POST to create a modifier value on a product.
+ */
+ productModifierOptionValue_Post: {
+ /**
+ * The flag for preselecting a value as the default on the storefront. This field is not supported for swatch options/modifiers.
+ */
+ is_default?: boolean
+ /**
+ * The text display identifying the value on the storefront. Required in a /POST.
+ */
+ label: string
+ /**
+ * The order in which the value will be displayed on the product page. Required in a /POST.
+ */
+ sort_order: number
+ /**
+ * Extra data describing the value, based on the type of option or modifier with which the value is associated. The `swatch` type option can accept an array of `colors`, with up to three hexidecimal color keys; or an `image_url`, which is a full image URL path including protocol. The `product list` type option requires a `product_id`. The `checkbox` type option requires a boolean flag, called `checked_value`, to determine which value is considered to be the checked state.
+ */
+ value_data?: { [key: string]: any }
+ } & {
+ adjusters?: {
+ /**
+ * Adjuster for Complex Rules.
+ */
+ price?: {
+ /**
+ * The type of adjuster for either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster?: 'relative' | 'percentage'
+ /**
+ * The numeric amount by which the adjuster will change either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster_value?: number
+ }
+ /**
+ * Adjuster for Complex Rules.
+ */
+ weight?: {
+ /**
+ * The type of adjuster for either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster?: 'relative' | 'percentage'
+ /**
+ * The numeric amount by which the adjuster will change either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster_value?: number
+ }
+ /**
+ * The URL for an image displayed on the storefront when the modifier value is selected.Limit of 8MB per file.
+ */
+ image_url?: string
+ purchasing_disabled?: {
+ /**
+ * Flag for whether the modifier value disables purchasing when selected on the storefront. This can be used for temporarily disabling a particular modifier value.
+ */
+ status?: boolean
+ /**
+ * The message displayed on the storefront when the purchasing disabled status is `true`.
+ */
+ message?: string
+ }
+ }
+ }
+ /**
+ * The model for a PUT to update a modifier value on a product.
+ */
+ productModifierOptionValue_Put: ({
+ /**
+ * The flag for preselecting a value as the default on the storefront. This field is not supported for swatch options/modifiers.
+ */
+ is_default?: boolean
+ /**
+ * The text display identifying the value on the storefront. Required in a /POST.
+ */
+ label: string
+ /**
+ * The order in which the value will be displayed on the product page. Required in a /POST.
+ */
+ sort_order: number
+ /**
+ * Extra data describing the value, based on the type of option or modifier with which the value is associated. The `swatch` type option can accept an array of `colors`, with up to three hexidecimal color keys; or an `image_url`, which is a full image URL path including protocol. The `product list` type option requires a `product_id`. The `checkbox` type option requires a boolean flag, called `checked_value`, to determine which value is considered to be the checked state.
+ */
+ value_data?: { [key: string]: any }
+ } & {
+ adjusters?: {
+ /**
+ * Adjuster for Complex Rules.
+ */
+ price?: {
+ /**
+ * The type of adjuster for either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster?: 'relative' | 'percentage'
+ /**
+ * The numeric amount by which the adjuster will change either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster_value?: number
+ }
+ /**
+ * Adjuster for Complex Rules.
+ */
+ weight?: {
+ /**
+ * The type of adjuster for either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster?: 'relative' | 'percentage'
+ /**
+ * The numeric amount by which the adjuster will change either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster_value?: number
+ }
+ /**
+ * The URL for an image displayed on the storefront when the modifier value is selected.Limit of 8MB per file.
+ */
+ image_url?: string
+ purchasing_disabled?: {
+ /**
+ * Flag for whether the modifier value disables purchasing when selected on the storefront. This can be used for temporarily disabling a particular modifier value.
+ */
+ status?: boolean
+ /**
+ * The message displayed on the storefront when the purchasing disabled status is `true`.
+ */
+ message?: string
+ }
+ }
+ }) & {
+ /**
+ * The unique numeric ID of the value; increments sequentially.
+ */
+ id?: number
+ }
+ resp_productionOption: {
+ data?: definitions['productOption_Full']
+ /**
+ * Empty meta object; may be used later.
+ */
+ meta?: { ''?: string }
+ }
+ /**
+ * Common Option properties.
+ */
+ productOption_Base: {
+ /**
+ * The unique numerical ID of the option, increments sequentially.
+ */
+ id?: number
+ /**
+ * The unique numerical ID of the product to which the option belongs.
+ */
+ product_id?: number
+ /**
+ * The name of the option shown on the storefront.
+ */
+ display_name?: string
+ /**
+ * The type of option, which determines how it will display on the storefront. Acceptable values: `radio_buttons`, `rectangles`, `dropdown`, `product_list`, `product_list_with_images`, `swatch`. For reference, the former v2 API values are: RB = radio_buttons, RT = rectangles, S = dropdown, P = product_list, PI = product_list_with_images, CS = swatch.
+ */
+ type?:
+ | 'radio_buttons'
+ | 'rectangles'
+ | 'dropdown'
+ | 'product_list'
+ | 'product_list_with_images'
+ | 'swatch'
+ config?: definitions['productOptionConfig_Full']
+ /**
+ * Order in which the option is displayed on the storefront.
+ */
+ sort_order?: number
+ option_values?: definitions['productOptionOptionValue_Full']
+ }
+ productOption_Full: definitions['productOption_Base'] & {
+ /**
+ * The unique option name, auto-generated from the display name, a timestamp, and the product ID.
+ */
+ name?: string
+ }
+ /**
+ * The model for a POST to create options on a product.
+ */
+ productOption_Post: {
+ /**
+ * The unique numerical ID of the option, increments sequentially.
+ */
+ id?: number
+ /**
+ * The unique numerical ID of the product to which the option belongs.
+ */
+ product_id?: number
+ /**
+ * The name of the option shown on the storefront.
+ */
+ display_name?: string
+ /**
+ * The type of option, which determines how it will display on the storefront. Acceptable values: `radio_buttons`, `rectangles`, `dropdown`, `product_list`, `product_list_with_images`, `swatch`. For reference, the former v2 API values are: RB = radio_buttons, RT = rectangles, S = dropdown, P = product_list, PI = product_list_with_images, CS = swatch.
+ */
+ type?:
+ | 'radio_buttons'
+ | 'rectangles'
+ | 'dropdown'
+ | 'product_list'
+ | 'product_list_with_images'
+ | 'swatch'
+ /**
+ * The values for option config can vary based on the Modifier created.
+ */
+ config?: {
+ /**
+ * (date, text, multi_line_text, numbers_only_text) The default value. Shown on a date option as an ISO-8601–formatted string, or on a text option as a string.
+ */
+ default_value?: string
+ /**
+ * (checkbox) Flag for setting the checkbox to be checked by default.
+ */
+ checked_by_default?: boolean
+ /**
+ * (checkbox) Label displayed for the checkbox option.
+ */
+ checkbox_label?: string
+ /**
+ * (date) Flag to limit the dates allowed to be entered on a date option.
+ */
+ date_limited?: boolean
+ /**
+ * (date) The type of limit that is allowed to be entered on a date option.
+ */
+ date_limit_mode?: 'earliest' | 'range' | 'latest'
+ /**
+ * (date) The earliest date allowed to be entered on the date option, as an ISO-8601 formatted string.
+ */
+ date_earliest_value?: string
+ /**
+ * (date) The latest date allowed to be entered on the date option, as an ISO-8601 formatted string.
+ */
+ date_latest_value?: string
+ /**
+ * (file) The kind of restriction on the file types that can be uploaded with a file upload option. Values: `specific` - restricts uploads to particular file types; `all` - allows all file types.
+ */
+ file_types_mode?: 'specific' | 'all'
+ /**
+ * (file) The type of files allowed to be uploaded if the `file_type_option` is set to `specific`. Values:
+ * `images` - Allows upload of image MIME types (`bmp`, `gif`, `jpg`, `jpeg`, `jpe`, `jif`, `jfif`, `jfi`, `png`, `wbmp`, `xbm`, `tiff`). `documents` - Allows upload of document MIME types (`txt`, `pdf`, `rtf`, `doc`, `docx`, `xls`, `xlsx`, `accdb`, `mdb`, `one`, `pps`, `ppsx`, `ppt`, `pptx`, `pub`, `odt`, `ods`, `odp`, `odg`, `odf`).
+ * `other` - Allows file types defined in the `file_types_other` array.
+ */
+ file_types_supported?: string[]
+ /**
+ * (file) A list of other file types allowed with the file upload option.
+ */
+ file_types_other?: string[]
+ /**
+ * (file) The maximum size for a file that can be used with the file upload option. This will still be limited by the server.
+ */
+ file_max_size?: number
+ /**
+ * (text, multi_line_text) Flag to validate the length of a text or multi-line text input.
+ */
+ text_characters_limited?: boolean
+ /**
+ * (text, multi_line_text) The minimum length allowed for a text or multi-line text option.
+ */
+ text_min_length?: number
+ /**
+ * (text, multi_line_text) The maximum length allowed for a text or multi line text option.
+ */
+ text_max_length?: number
+ /**
+ * (multi_line_text) Flag to validate the maximum number of lines allowed on a multi-line text input.
+ */
+ text_lines_limited?: boolean
+ /**
+ * (multi_line_text) The maximum number of lines allowed on a multi-line text input.
+ */
+ text_max_lines?: number
+ /**
+ * (numbers_only_text) Flag to limit the value of a number option.
+ */
+ number_limited?: boolean
+ /**
+ * (numbers_only_text) The type of limit on values entered for a number option.
+ */
+ number_limit_mode?: 'lowest' | 'highest' | 'range'
+ /**
+ * (numbers_only_text) The lowest allowed value for a number option if `number_limited` is true.
+ */
+ number_lowest_value?: number
+ /**
+ * (numbers_only_text) The highest allowed value for a number option if `number_limited` is true.
+ */
+ number_highest_value?: number
+ /**
+ * (numbers_only_text) Flag to limit the input on a number option to whole numbers only.
+ */
+ number_integers_only?: boolean
+ /**
+ * (product_list, product_list_with_images) Flag for automatically adjusting inventory on a product included in the list.
+ */
+ product_list_adjusts_inventory?: boolean
+ /**
+ * (product_list, product_list_with_images) Flag to add the optional product's price to the main product's price.
+ */
+ product_list_adjusts_pricing?: boolean
+ /**
+ * (product_list, product_list_with_images) How to factor the optional product's weight and package dimensions into the shipping quote. Values: `none` - don't adjust; `weight` - use shipping weight only; `package` - use weight and dimensions.
+ */
+ product_list_shipping_calc?: 'none' | 'weight' | 'package'
+ }
+ /**
+ * Order in which the option is displayed on the storefront.
+ */
+ sort_order?: number
+ option_values?: ({
+ /**
+ * The flag for preselecting a value as the default on the storefront. This field is not supported for swatch options/modifiers.
+ */
+ is_default?: boolean
+ /**
+ * The text display identifying the value on the storefront. Required in a /POST.
+ */
+ label: string
+ /**
+ * The order in which the value will be displayed on the product page. Required in a /POST.
+ */
+ sort_order: number
+ /**
+ * Extra data describing the value, based on the type of option or modifier with which the value is associated. The `swatch` type option can accept an array of `colors`, with up to three hexidecimal color keys; or an `image_url`, which is a full image URL path including protocol. The `product list` type option requires a `product_id`. The `checkbox` type option requires a boolean flag, called `checked_value`, to determine which value is considered to be the checked state.
+ */
+ value_data?: { [key: string]: any }
+ } & {
+ /**
+ * The unique numeric ID of the value; increments sequentially.
+ */
+ id?: number
+ })[]
+ /**
+ * Publicly available image url
+ */
+ image_url?: string
+ }
+ /**
+ * The model for a PUT to update options on a product.
+ */
+ productOption_Put: {
+ /**
+ * The unique numerical ID of the option, increments sequentially.
+ */
+ id?: number
+ /**
+ * The unique numerical ID of the product to which the option belongs.
+ */
+ product_id?: number
+ /**
+ * The name of the option shown on the storefront.
+ */
+ display_name?: string
+ /**
+ * The type of option, which determines how it will display on the storefront. Acceptable values: `radio_buttons`, `rectangles`, `dropdown`, `product_list`, `product_list_with_images`, `swatch`. For reference, the former v2 API values are: RB = radio_buttons, RT = rectangles, S = dropdown, P = product_list, PI = product_list_with_images, CS = swatch.
+ */
+ type?:
+ | 'radio_buttons'
+ | 'rectangles'
+ | 'dropdown'
+ | 'product_list'
+ | 'product_list_with_images'
+ | 'swatch'
+ /**
+ * The values for option config can vary based on the Modifier created.
+ */
+ config?: {
+ /**
+ * (date, text, multi_line_text, numbers_only_text) The default value. Shown on a date option as an ISO-8601–formatted string, or on a text option as a string.
+ */
+ default_value?: string
+ /**
+ * (checkbox) Flag for setting the checkbox to be checked by default.
+ */
+ checked_by_default?: boolean
+ /**
+ * (checkbox) Label displayed for the checkbox option.
+ */
+ checkbox_label?: string
+ /**
+ * (date) Flag to limit the dates allowed to be entered on a date option.
+ */
+ date_limited?: boolean
+ /**
+ * (date) The type of limit that is allowed to be entered on a date option.
+ */
+ date_limit_mode?: 'earliest' | 'range' | 'latest'
+ /**
+ * (date) The earliest date allowed to be entered on the date option, as an ISO-8601 formatted string.
+ */
+ date_earliest_value?: string
+ /**
+ * (date) The latest date allowed to be entered on the date option, as an ISO-8601 formatted string.
+ */
+ date_latest_value?: string
+ /**
+ * (file) The kind of restriction on the file types that can be uploaded with a file upload option. Values: `specific` - restricts uploads to particular file types; `all` - allows all file types.
+ */
+ file_types_mode?: 'specific' | 'all'
+ /**
+ * (file) The type of files allowed to be uploaded if the `file_type_option` is set to `specific`. Values:
+ * `images` - Allows upload of image MIME types (`bmp`, `gif`, `jpg`, `jpeg`, `jpe`, `jif`, `jfif`, `jfi`, `png`, `wbmp`, `xbm`, `tiff`). `documents` - Allows upload of document MIME types (`txt`, `pdf`, `rtf`, `doc`, `docx`, `xls`, `xlsx`, `accdb`, `mdb`, `one`, `pps`, `ppsx`, `ppt`, `pptx`, `pub`, `odt`, `ods`, `odp`, `odg`, `odf`).
+ * `other` - Allows file types defined in the `file_types_other` array.
+ */
+ file_types_supported?: string[]
+ /**
+ * (file) A list of other file types allowed with the file upload option.
+ */
+ file_types_other?: string[]
+ /**
+ * (file) The maximum size for a file that can be used with the file upload option. This will still be limited by the server.
+ */
+ file_max_size?: number
+ /**
+ * (text, multi_line_text) Flag to validate the length of a text or multi-line text input.
+ */
+ text_characters_limited?: boolean
+ /**
+ * (text, multi_line_text) The minimum length allowed for a text or multi-line text option.
+ */
+ text_min_length?: number
+ /**
+ * (text, multi_line_text) The maximum length allowed for a text or multi line text option.
+ */
+ text_max_length?: number
+ /**
+ * (multi_line_text) Flag to validate the maximum number of lines allowed on a multi-line text input.
+ */
+ text_lines_limited?: boolean
+ /**
+ * (multi_line_text) The maximum number of lines allowed on a multi-line text input.
+ */
+ text_max_lines?: number
+ /**
+ * (numbers_only_text) Flag to limit the value of a number option.
+ */
+ number_limited?: boolean
+ /**
+ * (numbers_only_text) The type of limit on values entered for a number option.
+ */
+ number_limit_mode?: 'lowest' | 'highest' | 'range'
+ /**
+ * (numbers_only_text) The lowest allowed value for a number option if `number_limited` is true.
+ */
+ number_lowest_value?: number
+ /**
+ * (numbers_only_text) The highest allowed value for a number option if `number_limited` is true.
+ */
+ number_highest_value?: number
+ /**
+ * (numbers_only_text) Flag to limit the input on a number option to whole numbers only.
+ */
+ number_integers_only?: boolean
+ /**
+ * (product_list, product_list_with_images) Flag for automatically adjusting inventory on a product included in the list.
+ */
+ product_list_adjusts_inventory?: boolean
+ /**
+ * (product_list, product_list_with_images) Flag to add the optional product's price to the main product's price.
+ */
+ product_list_adjusts_pricing?: boolean
+ /**
+ * (product_list, product_list_with_images) How to factor the optional product's weight and package dimensions into the shipping quote. Values: `none` - don't adjust; `weight` - use shipping weight only; `package` - use weight and dimensions.
+ */
+ product_list_shipping_calc?: 'none' | 'weight' | 'package'
+ }
+ /**
+ * Order in which the option is displayed on the storefront.
+ */
+ sort_order?: number
+ option_values?: ({
+ /**
+ * The flag for preselecting a value as the default on the storefront. This field is not supported for swatch options/modifiers.
+ */
+ is_default?: boolean
+ /**
+ * The text display identifying the value on the storefront. Required in a /POST.
+ */
+ label: string
+ /**
+ * The order in which the value will be displayed on the product page. Required in a /POST.
+ */
+ sort_order: number
+ /**
+ * Extra data describing the value, based on the type of option or modifier with which the value is associated. The `swatch` type option can accept an array of `colors`, with up to three hexidecimal color keys; or an `image_url`, which is a full image URL path including protocol. The `product list` type option requires a `product_id`. The `checkbox` type option requires a boolean flag, called `checked_value`, to determine which value is considered to be the checked state.
+ */
+ value_data?: { [key: string]: any }
+ } & {
+ /**
+ * The unique numeric ID of the value; increments sequentially.
+ */
+ id?: number
+ })[]
+ /**
+ * Publicly available image url
+ */
+ image_url?: string
+ }
+ /**
+ * Returns the categories tree, a nested lineage of the categories with parent->child relationship. The Category objects returned are simplified versions of the category objects returned in the rest of this API.
+ */
+ categoriesTree_Resp: {
+ data?: definitions['categoriesTreeNode_Full'][]
+ meta?: definitions['metaEmpty_Full']
+ }
+ /**
+ * Used to reflect parent <> child category relationships. Used by Category Tree.
+ */
+ categoriesTreeNode_Full: {
+ /**
+ * The unique numeric ID of the category; increments sequentially.
+ */
+ id?: number
+ /**
+ * The unique numeric ID of the category's parent. This field controls where the category sits in the tree of categories that organize the catalog.
+ */
+ parent_id?: number
+ /**
+ * The name displayed for the category. Name is unique with respect to the category's siblings.
+ */
+ name?: string
+ /**
+ * Flag to determine whether the product should be displayed to customers browsing the store. If `true`, the category will be displayed. If `false`, the category will be hidden from view.
+ */
+ is_visible?: boolean
+ /**
+ * The custom URL for the category on the storefront.
+ */
+ url?: string
+ /**
+ * The list of children of the category.
+ */
+ children?: definitions['categoriesTreeNode_Full'][]
+ }
+ /**
+ * Common Category object properties.
+ */
+ category_Full: {
+ /**
+ * Unique ID of the *Category*. Increments sequentially.
+ * Read-Only.
+ */
+ id?: number
+ /**
+ * The unique numeric ID of the category's parent. This field controls where the category sits in the tree of categories that organize the catalog.
+ * Required in a POST if creating a child category.
+ */
+ parent_id: number
+ /**
+ * The name displayed for the category. Name is unique with respect to the category's siblings.
+ * Required in a POST.
+ */
+ name: string
+ /**
+ * The product description, which can include HTML formatting.
+ */
+ description?: string
+ /**
+ * Number of views the category has on the storefront.
+ */
+ views?: number
+ /**
+ * Priority this category will be given when included in the menu and category pages. The lower the number, the closer to the top of the results the category will be.
+ */
+ sort_order?: number
+ /**
+ * Custom title for the category page. If not defined, the category name will be used as the meta title.
+ */
+ page_title?: string
+ /**
+ * A comma-separated list of keywords that can be used to locate the category when searching the store.
+ */
+ search_keywords?: string
+ /**
+ * Custom meta keywords for the category page. If not defined, the store's default keywords will be used. Must post as an array like: ["awesome","sauce"].
+ */
+ meta_keywords?: string[]
+ /**
+ * Custom meta description for the category page. If not defined, the store's default meta description will be used.
+ */
+ meta_description?: string
+ /**
+ * A valid layout file. (Please refer to [this article](https://support.bigcommerce.com/articles/Public/Creating-Custom-Template-Files/) on creating category files.) This field is writable only for stores with a Blueprint theme applied.
+ */
+ layout_file?: string
+ /**
+ * Flag to determine whether the product should be displayed to customers browsing the store. If `true`, the category will be displayed. If `false`, the category will be hidden from view.
+ */
+ is_visible?: boolean
+ /**
+ * Determines how the products are sorted on category page load.
+ */
+ default_product_sort?:
+ | 'use_store_settings'
+ | 'featured'
+ | 'newest'
+ | 'best_selling'
+ | 'alpha_asc'
+ | 'alpha_desc'
+ | 'avg_customer_review'
+ | 'price_asc'
+ | 'price_desc'
+ /**
+ * Image URL used for this category on the storefront. Images can be uploaded via form file post to `/categories/{categoryId}/image`, or by providing a publicly accessible URL in this field.
+ */
+ image_url?: string
+ custom_url?: definitions['customUrl_Full']
+ }
+ /**
+ * Common Brand properties.
+ */
+ brand_Full: {
+ /**
+ * Unique ID of the *Brand*. Read-Only.
+ */
+ id?: number
+ /**
+ * The name of the brand. Must be unique.
+ * Required in POST.
+ */
+ name: string
+ /**
+ * The title shown in the browser while viewing the brand.
+ */
+ page_title?: string
+ /**
+ * Comma-separated list of meta keywords to include in the HTML.
+ */
+ meta_keywords?: string[]
+ /**
+ * A meta description to include.
+ */
+ meta_description?: string
+ /**
+ * A comma-separated list of keywords that can be used to locate this brand.
+ */
+ search_keywords?: string
+ /**
+ * Image URL used for this category on the storefront. Images can be uploaded via form file post to `/brands/{brandId}/image`, or by providing a publicly accessible URL in this field.
+ */
+ image_url?: string
+ custom_url?: definitions['customUrl_Full']
+ }
+ /**
+ * Common Variant properties.
+ */
+ productVariant_Base: {
+ /**
+ * The cost price of the variant. Not affected by Price List prices.
+ */
+ cost_price?: number
+ /**
+ * This variant's base price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is `null`, the product's default price (set in the Product resource's `price` field) will be used as the base price.
+ */
+ price?: number
+ /**
+ * This variant's sale price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is null, the product's sale price (set in the Product resource's `price` field) will be used as the sale price.
+ */
+ sale_price?: number
+ /**
+ * This variant's retail price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is null, the product's retail price (set in the Product resource's `price` field) will be used as the retail price.
+ */
+ retail_price?: number
+ /**
+ * This variant's base weight on the storefront. If this value is null, the product's default weight (set in the Product resource's weight field) will be used as the base weight.
+ */
+ weight?: number
+ /**
+ * Width of the variant, which can be used when calculating shipping costs. If this value is `null`, the product's default width (set in the Product resource's `width` field) will be used as the base width.
+ */
+ width?: number
+ /**
+ * Height of the variant, which can be used when calculating shipping costs. If this value is `null`, the product's default height (set in the Product resource's `height` field) will be used as the base height.
+ */
+ height?: number
+ /**
+ * Depth of the variant, which can be used when calculating shipping costs. If this value is `null`, the product's default depth (set in the Product resource's `depth` field) will be used as the base depth.
+ */
+ depth?: number
+ /**
+ * Flag used to indicate whether the variant has free shipping. If `true`, the shipping cost for the variant will be zero.
+ */
+ is_free_shipping?: boolean
+ /**
+ * A fixed shipping cost for the variant. If defined, this value will be used during checkout instead of normal shipping-cost calculation.
+ */
+ fixed_cost_shipping_price?: number
+ /**
+ * If `true`, this variant will not be purchasable on the storefront.
+ */
+ purchasing_disabled?: boolean
+ /**
+ * If `purchasing_disabled` is `true`, this message should show on the storefront when the variant is selected.
+ */
+ purchasing_disabled_message?: string
+ /**
+ * The UPC code used in feeds for shopping comparison sites and external channel integrations.
+ */
+ upc?: string
+ /**
+ * Inventory level for the variant, which is used when the product's inventory_tracking is set to `variant`.
+ */
+ inventory_level?: number
+ /**
+ * When the variant hits this inventory level, it is considered low stock.
+ */
+ inventory_warning_level?: number
+ /**
+ * Identifies where in a warehouse the variant is located.
+ */
+ bin_picking_number?: string
+ }
+ productVariant_Full: definitions['productVariant_Base'] & {
+ id?: number
+ product_id?: number
+ sku?: string
+ /**
+ * Read-only reference to v2 API's SKU ID. Null if it is a base variant.
+ */
+ sku_id?: number
+ /**
+ * Array of option and option values IDs that make up this variant. Will be empty if the variant is the product's base variant.
+ */
+ option_values?: definitions['productVariantOptionValue_Base'][]
+ /**
+ * The price of the variant as seen on the storefront. This price takes into account `sale_price` and any price adjustment rules that are applicable to this variant.
+ */
+ calculated_price?: number
+ }
+ /**
+ * The model for a POST to create variants on a product.
+ */
+ productVariant_Post: {
+ /**
+ * The cost price of the variant. Not affected by Price List prices.
+ */
+ cost_price?: number
+ /**
+ * This variant's base price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is `null`, the product's default price (set in the Product resource's `price` field) will be used as the base price.
+ */
+ price?: number
+ /**
+ * This variant's sale price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is null, the product's sale price (set in the Product resource's `price` field) will be used as the sale price.
+ */
+ sale_price?: number
+ /**
+ * This variant's retail price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is null, the product's retail price (set in the Product resource's `price` field) will be used as the retail price.
+ */
+ retail_price?: number
+ /**
+ * This variant's base weight on the storefront. If this value is null, the product's default weight (set in the Product resource's weight field) will be used as the base weight.
+ */
+ weight?: number
+ /**
+ * Width of the variant, which can be used when calculating shipping costs. If this value is `null`, the product's default width (set in the Product resource's `width` field) will be used as the base width.
+ */
+ width?: number
+ /**
+ * Height of the variant, which can be used when calculating shipping costs. If this value is `null`, the product's default height (set in the Product resource's `height` field) will be used as the base height.
+ */
+ height?: number
+ /**
+ * Depth of the variant, which can be used when calculating shipping costs. If this value is `null`, the product's default depth (set in the Product resource's `depth` field) will be used as the base depth.
+ */
+ depth?: number
+ /**
+ * Flag used to indicate whether the variant has free shipping. If `true`, the shipping cost for the variant will be zero.
+ */
+ is_free_shipping?: boolean
+ /**
+ * A fixed shipping cost for the variant. If defined, this value will be used during checkout instead of normal shipping-cost calculation.
+ */
+ fixed_cost_shipping_price?: number
+ /**
+ * If `true`, this variant will not be purchasable on the storefront.
+ */
+ purchasing_disabled?: boolean
+ /**
+ * If `purchasing_disabled` is `true`, this message should show on the storefront when the variant is selected.
+ */
+ purchasing_disabled_message?: string
+ /**
+ * The UPC code used in feeds for shopping comparison sites and external channel integrations.
+ */
+ upc?: string
+ /**
+ * Inventory level for the variant, which is used when the product's inventory_tracking is set to `variant`.
+ */
+ inventory_level?: number
+ /**
+ * When the variant hits this inventory level, it is considered low stock.
+ */
+ inventory_warning_level?: number
+ /**
+ * Identifies where in a warehouse the variant is located.
+ */
+ bin_picking_number?: string
+ } & {
+ product_id?: number
+ sku?: string
+ /**
+ * Array of option and option values IDs that make up this variant. Will be empty if the variant is the product's base variant.
+ */
+ option_values?: { id?: number; option_id?: number }[]
+ }
+ variantCollection_Put: definitions['productVariant_Full'][]
+ /**
+ * The model for a PUT to update variants on a product.
+ */
+ variant_Put: {
+ /**
+ * The cost price of the variant. Not affected by Price List prices.
+ */
+ cost_price?: number
+ /**
+ * This variant's base price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is `null`, the product's default price (set in the Product resource's `price` field) will be used as the base price.
+ */
+ price?: number
+ /**
+ * This variant's sale price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is null, the product's sale price (set in the Product resource's `price` field) will be used as the sale price.
+ */
+ sale_price?: number
+ /**
+ * This variant's retail price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is null, the product's retail price (set in the Product resource's `price` field) will be used as the retail price.
+ */
+ retail_price?: number
+ /**
+ * This variant's base weight on the storefront. If this value is null, the product's default weight (set in the Product resource's weight field) will be used as the base weight.
+ */
+ weight?: number
+ /**
+ * Width of the variant, which can be used when calculating shipping costs. If this value is `null`, the product's default width (set in the Product resource's `width` field) will be used as the base width.
+ */
+ width?: number
+ /**
+ * Height of the variant, which can be used when calculating shipping costs. If this value is `null`, the product's default height (set in the Product resource's `height` field) will be used as the base height.
+ */
+ height?: number
+ /**
+ * Depth of the variant, which can be used when calculating shipping costs. If this value is `null`, the product's default depth (set in the Product resource's `depth` field) will be used as the base depth.
+ */
+ depth?: number
+ /**
+ * Flag used to indicate whether the variant has free shipping. If `true`, the shipping cost for the variant will be zero.
+ */
+ is_free_shipping?: boolean
+ /**
+ * A fixed shipping cost for the variant. If defined, this value will be used during checkout instead of normal shipping-cost calculation.
+ */
+ fixed_cost_shipping_price?: number
+ /**
+ * If `true`, this variant will not be purchasable on the storefront.
+ */
+ purchasing_disabled?: boolean
+ /**
+ * If `purchasing_disabled` is `true`, this message should show on the storefront when the variant is selected.
+ */
+ purchasing_disabled_message?: string
+ /**
+ * The UPC code used in feeds for shopping comparison sites and external channel integrations.
+ */
+ upc?: string
+ /**
+ * Inventory level for the variant, which is used when the product's inventory_tracking is set to `variant`.
+ */
+ inventory_level?: number
+ /**
+ * When the variant hits this inventory level, it is considered low stock.
+ */
+ inventory_warning_level?: number
+ /**
+ * Identifies where in a warehouse the variant is located.
+ */
+ bin_picking_number?: string
+ } & { id?: number }
+ /**
+ * The model for a POST to create variants on a product.
+ */
+ productVariant_Post_Product: definitions['productVariant_Base'] & {
+ sku?: string
+ option_values?: {
+ /**
+ * The name of the option.
+ */
+ option_display_name?: string
+ /**
+ * The label of the option value.
+ */
+ label?: string
+ }[]
+ }
+ /**
+ * The model for a PUT to update variants on a product.
+ */
+ productVariant_Put_Product: {
+ /**
+ * The cost price of the variant. Not affected by Price List prices.
+ */
+ cost_price?: number
+ /**
+ * This variant's base price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is `null`, the product's default price (set in the Product resource's `price` field) will be used as the base price.
+ */
+ price?: number
+ /**
+ * This variant's sale price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is null, the product's sale price (set in the Product resource's `price` field) will be used as the sale price.
+ */
+ sale_price?: number
+ /**
+ * This variant's retail price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is null, the product's retail price (set in the Product resource's `price` field) will be used as the retail price.
+ */
+ retail_price?: number
+ /**
+ * This variant's base weight on the storefront. If this value is null, the product's default weight (set in the Product resource's weight field) will be used as the base weight.
+ */
+ weight?: number
+ /**
+ * Width of the variant, which can be used when calculating shipping costs. If this value is `null`, the product's default width (set in the Product resource's `width` field) will be used as the base width.
+ */
+ width?: number
+ /**
+ * Height of the variant, which can be used when calculating shipping costs. If this value is `null`, the product's default height (set in the Product resource's `height` field) will be used as the base height.
+ */
+ height?: number
+ /**
+ * Depth of the variant, which can be used when calculating shipping costs. If this value is `null`, the product's default depth (set in the Product resource's `depth` field) will be used as the base depth.
+ */
+ depth?: number
+ /**
+ * Flag used to indicate whether the variant has free shipping. If `true`, the shipping cost for the variant will be zero.
+ */
+ is_free_shipping?: boolean
+ /**
+ * A fixed shipping cost for the variant. If defined, this value will be used during checkout instead of normal shipping-cost calculation.
+ */
+ fixed_cost_shipping_price?: number
+ /**
+ * If `true`, this variant will not be purchasable on the storefront.
+ */
+ purchasing_disabled?: boolean
+ /**
+ * If `purchasing_disabled` is `true`, this message should show on the storefront when the variant is selected.
+ */
+ purchasing_disabled_message?: string
+ /**
+ * The UPC code used in feeds for shopping comparison sites and external channel integrations.
+ */
+ upc?: string
+ /**
+ * Inventory level for the variant, which is used when the product's inventory_tracking is set to `variant`.
+ */
+ inventory_level?: number
+ /**
+ * When the variant hits this inventory level, it is considered low stock.
+ */
+ inventory_warning_level?: number
+ /**
+ * Identifies where in a warehouse the variant is located.
+ */
+ bin_picking_number?: string
+ product_id?: number
+ sku?: string
+ }
+ productVariantOptionValue_Full: {
+ /**
+ * The name of the option.
+ */
+ option_display_name?: string
+ /**
+ * The label of the option value.
+ */
+ label?: string
+ } & definitions['productVariantOptionValue_Base']
+ /**
+ * The model for a POST to create option values on a product.
+ */
+ productOptionValue_Post_Product: {
+ /**
+ * The name of the option.
+ */
+ option_display_name?: string
+ /**
+ * The label of the option value.
+ */
+ label?: string
+ }
+ /**
+ * Common Product Variant Option properties.
+ */
+ productVariantOptionValue_Base: { id?: number; option_id?: number }
+ /**
+ * The model for a POST to create option values on a variant.
+ */
+ productVariantOptionValue_Post: { id?: number; option_id?: number }
+ resp_productOptionValue: {
+ data?: definitions['productOptionOptionValue_Full']
+ /**
+ * Empty meta object; may be used later.
+ */
+ meta?: { ''?: string }
+ }
+ /**
+ * Common Product Option `option_value` properties.
+ */
+ productOptionOptionValue_Base: {
+ /**
+ * The flag for preselecting a value as the default on the storefront. This field is not supported for swatch options/modifiers.
+ */
+ is_default?: boolean
+ /**
+ * The text display identifying the value on the storefront. Required in a /POST.
+ */
+ label: string
+ /**
+ * The order in which the value will be displayed on the product page. Required in a /POST.
+ */
+ sort_order: number
+ /**
+ * Extra data describing the value, based on the type of option or modifier with which the value is associated. The `swatch` type option can accept an array of `colors`, with up to three hexidecimal color keys; or an `image_url`, which is a full image URL path including protocol. The `product list` type option requires a `product_id`. The `checkbox` type option requires a boolean flag, called `checked_value`, to determine which value is considered to be the checked state. If no data is available, returns `null`.
+ */
+ value_data?: { [key: string]: any }
+ }
+ /**
+ * Product Option `option_value`.
+ */
+ productOptionOptionValue_Full: definitions['productOptionOptionValue_Base'] & {
+ /**
+ * The unique numeric ID of the value; increments sequentially.
+ */
+ id?: number
+ }
+ /**
+ * The model for a POST to create option values on a product.
+ */
+ productOptionValue_Post: {
+ /**
+ * The flag for preselecting a value as the default on the storefront. This field is not supported for swatch options/modifiers.
+ */
+ is_default?: boolean
+ /**
+ * The text display identifying the value on the storefront. Required in a /POST.
+ */
+ label: string
+ /**
+ * The order in which the value will be displayed on the product page. Required in a /POST.
+ */
+ sort_order: number
+ /**
+ * Extra data describing the value, based on the type of option or modifier with which the value is associated. The `swatch` type option can accept an array of `colors`, with up to three hexidecimal color keys; or an `image_url`, which is a full image URL path including protocol. The `product list` type option requires a `product_id`. The `checkbox` type option requires a boolean flag, called `checked_value`, to determine which value is considered to be the checked state.
+ */
+ value_data?: { [key: string]: any }
+ }
+ /**
+ * The model for a PUT to update option values on a product.
+ */
+ productOptionValue_Put: {
+ /**
+ * The flag for preselecting a value as the default on the storefront. This field is not supported for swatch options/modifiers.
+ */
+ is_default?: boolean
+ /**
+ * The text display identifying the value on the storefront. Required in a /POST.
+ */
+ label: string
+ /**
+ * The order in which the value will be displayed on the product page. Required in a /POST.
+ */
+ sort_order: number
+ /**
+ * Extra data describing the value, based on the type of option or modifier with which the value is associated. The `swatch` type option can accept an array of `colors`, with up to three hexidecimal color keys; or an `image_url`, which is a full image URL path including protocol. The `product list` type option requires a `product_id`. The `checkbox` type option requires a boolean flag, called `checked_value`, to determine which value is considered to be the checked state.
+ */
+ value_data?: { [key: string]: any }
+ } & {
+ /**
+ * The unique numeric ID of the value; increments sequentially.
+ */
+ id?: number
+ }
+ /**
+ * Common ProductImage properties.
+ */
+ productImage_Base: {
+ /**
+ * The local path to the original image file uploaded to BigCommerce.
+ */
+ image_file?: string
+ /**
+ * Flag for identifying whether the image is used as the product's thumbnail.
+ */
+ is_thumbnail?: boolean
+ /**
+ * The order in which the image will be displayed on the product page. Higher integers give the image a lower priority. When updating, if the image is given a lower priority, all images with a `sort_order` the same as or greater than the image's new `sort_order` value will have their `sort_order`s reordered.
+ */
+ sort_order?: number
+ /**
+ * The description for the image.
+ */
+ description?: string
+ /**
+ * Must be a fully qualified URL path, including protocol. Limit of 8MB per file.
+ */
+ image_url?: string
+ }
+ /**
+ * The model for a POST to create an image on a product.
+ */
+ productImage_Post: {
+ /**
+ * The unique numeric ID of the image; increments sequentially.
+ */
+ id?: number
+ /**
+ * The unique numeric identifier for the product with which the image is associated.
+ */
+ product_id?: number
+ /**
+ * The local path to the original image file uploaded to BigCommerce.
+ */
+ image_file?: string
+ /**
+ * The zoom URL for this image. By default, this is used as the zoom image on product pages when zoom images are enabled.
+ */
+ url_zoom?: string
+ /**
+ * The standard URL for this image. By default, this is used for product-page images.
+ */
+ url_standard?: string
+ /**
+ * The thumbnail URL for this image. By default, this is the image size used on the category page and in side panels.
+ */
+ url_thumbnail?: string
+ /**
+ * The tiny URL for this image. By default, this is the image size used for thumbnails beneath the product image on a product page.
+ */
+ url_tiny?: string
+ /**
+ * The date on which the product image was modified.
+ */
+ date_modified?: string
+ /**
+ * Flag for identifying whether the image is used as the product's thumbnail.
+ */
+ is_thumbnail?: boolean
+ /**
+ * The order in which the image will be displayed on the product page. Higher integers give the image a lower priority. When updating, if the image is given a lower priority, all images with a `sort_order` the same as or greater than the image's new `sort_order` value will have their `sort_order`s reordered.
+ */
+ sort_order?: number
+ /**
+ * The description for the image.
+ */
+ description?: string
+ } & {
+ /**
+ * Must be a fully qualified URL path, including protocol. Limit of 8MB per file.
+ */
+ image_url?: string
+ /**
+ * Must be sent as a multipart/form-data field in the request body.
+ */
+ image_file?: string
+ }
+ /**
+ * The model for a PUT to update applicable Product Image fields.
+ */
+ productImage_Put: {
+ /**
+ * The unique numeric ID of the image; increments sequentially.
+ */
+ id?: number
+ /**
+ * The unique numeric identifier for the product with which the image is associated.
+ */
+ product_id?: number
+ /**
+ * The local path to the original image file uploaded to BigCommerce.
+ */
+ image_file?: string
+ /**
+ * The zoom URL for this image. By default, this is used as the zoom image on product pages when zoom images are enabled.
+ */
+ url_zoom?: string
+ /**
+ * The standard URL for this image. By default, this is used for product-page images.
+ */
+ url_standard?: string
+ /**
+ * The thumbnail URL for this image. By default, this is the image size used on the category page and in side panels.
+ */
+ url_thumbnail?: string
+ /**
+ * The tiny URL for this image. By default, this is the image size used for thumbnails beneath the product image on a product page.
+ */
+ url_tiny?: string
+ /**
+ * The date on which the product image was modified.
+ */
+ date_modified?: string
+ /**
+ * Flag for identifying whether the image is used as the product's thumbnail.
+ */
+ is_thumbnail?: boolean
+ /**
+ * The order in which the image will be displayed on the product page. Higher integers give the image a lower priority. When updating, if the image is given a lower priority, all images with a `sort_order` the same as or greater than the image's new `sort_order` value will have their `sort_order`s reordered.
+ */
+ sort_order?: number
+ /**
+ * The description for the image.
+ */
+ description?: string
+ }
+ /**
+ * The model for a POST to create a video on a product.
+ */
+ productVideo_Base: {
+ /**
+ * The title for the video. If left blank, this will be filled in according to data on a host site.
+ */
+ title?: string
+ /**
+ * The description for the video. If left blank, this will be filled in according to data on a host site.
+ */
+ description?: string
+ /**
+ * The order in which the video will be displayed on the product page. Higher integers give the video a lower priority. When updating, if the video is given a lower priority, all videos with a `sort_order` the same as or greater than the video's new `sort_order` value will have their `sort_order`s reordered.
+ */
+ sort_order?: number
+ /**
+ * The video type (a short name of a host site).
+ */
+ type?: 'youtube'
+ /**
+ * The ID of the video on a host site.
+ */
+ video_id?: string
+ }
+ /**
+ * A product video model.
+ */
+ productVideo_Full: definitions['productVideo_Base'] & {
+ /**
+ * The unique numeric ID of the product video; increments sequentially.
+ */
+ id?: number
+ /**
+ * The unique numeric identifier for the product with which the image is associated.
+ */
+ product_id?: number
+ /**
+ * Length of the video. This will be filled in according to data on a host site.
+ */
+ length?: string
+ }
+ /**
+ * The model for a POST to create a video on a product.
+ */
+ productVideo_Post: definitions['productVideo_Base']
+ /**
+ * The model for a PUT to update a video on a product.
+ */
+ productVideo_Put: definitions['productVideo_Base'] & {
+ /**
+ * The unique numeric ID of the product video; increments sequentially.
+ */
+ id?: number
+ }
+ productReview_Base: {
+ /**
+ * The title for the product review.
+ * Required in /POST.
+ */
+ title: string
+ /**
+ * The text for the product review.
+ */
+ text?: string
+ /**
+ * The status of the product review. Must be one of `approved`, `disapproved` or `pending`.
+ */
+ status?: string
+ /**
+ * The rating of the product review. Must be one of 0, 1, 2, 3, 4, 5.
+ */
+ rating?: number
+ /**
+ * The email of the reviewer. Must be a valid email, or an empty string.
+ */
+ email?: string
+ /**
+ * The name of the reviewer.
+ */
+ name?: string
+ /**
+ * Date the product was reviewed. Required in /POST.
+ */
+ date_reviewed: string
+ }
+ /**
+ * A product review model.
+ */
+ productReview_Full: definitions['productReview_Base'] & {
+ /**
+ * The unique numeric ID of the product review; increments sequentially.
+ */
+ id?: number
+ /**
+ * The unique numeric identifier for the product with which the review is associated.
+ */
+ product_id?: number
+ /**
+ * Date the product review was created.
+ */
+ date_created?: string
+ /**
+ * Date the product review was modified.
+ */
+ date_modified?: string
+ }
+ /**
+ * The model for a POST to create a product review.
+ */
+ productReview_Post: {
+ /**
+ * The title for the product review.
+ * Required in /POST.
+ */
+ title: string
+ /**
+ * The text for the product review.
+ */
+ text?: string
+ /**
+ * The status of the product review. Must be one of `approved`, `disapproved` or `pending`.
+ */
+ status?: string
+ /**
+ * The rating of the product review. Must be one of 0, 1, 2, 3, 4, 5.
+ */
+ rating?: number
+ /**
+ * The email of the reviewer. Must be a valid email, or an empty string.
+ */
+ email?: string
+ /**
+ * The name of the reviewer.
+ */
+ name?: string
+ /**
+ * Date the product was reviewed. Required in /POST.
+ */
+ date_reviewed: string
+ }
+ /**
+ * The model for a PUT to update a product review.
+ */
+ productReview_Put: {
+ /**
+ * The title for the product review.
+ * Required in /POST.
+ */
+ title: string
+ /**
+ * The text for the product review.
+ */
+ text?: string
+ /**
+ * The status of the product review. Must be one of `approved`, `disapproved` or `pending`.
+ */
+ status?: string
+ /**
+ * The rating of the product review. Must be one of 0, 1, 2, 3, 4, 5.
+ */
+ rating?: number
+ /**
+ * The email of the reviewer. Must be a valid email, or an empty string.
+ */
+ email?: string
+ /**
+ * The name of the reviewer.
+ */
+ name?: string
+ /**
+ * Date the product was reviewed. Required in /POST.
+ */
+ date_reviewed: string
+ }
+ /**
+ * Image Response returns for:
+ * * Create Variant Image
+ * * Create Modifier Image
+ * * Create Category Image
+ * * Create Brand Image
+ */
+ resp_productImage: {
+ data?: definitions['productImage_Full']
+ /**
+ * Empty meta object; may be used later.
+ */
+ meta?: { [key: string]: any }
+ }
+ /**
+ * An object containing a publicly accessible image URL, or a form post that contains an image file.
+ */
+ resourceImage_Full: {
+ /**
+ * A public URL for a GIF, JPEG, or PNG image. Limit of 8MB per file.
+ */
+ image_url?: string
+ }
+ product_Post: definitions['product_Base'] & {
+ variants?: definitions['productVariant_Post_Product']
+ }
+ /**
+ * The model for a PUT to update a product.
+ */
+ product_Put: {
+ /**
+ * The unique numerical ID of the product; increments sequentially.
+ */
+ id?: number
+ } & definitions['product_Base'] & {
+ variants?: definitions['productVariant_Put_Product']
+ }
+ /**
+ * Catalog Summary object describes a lightweight summary of the catalog.
+ */
+ catalogSummary_Full: {
+ /**
+ * A count of all inventory items in the catalog.
+ */
+ inventory_count?: number
+ /**
+ * Total value of store's inventory.
+ */
+ inventory_value?: number
+ /**
+ * ID of the category containing the most products.
+ */
+ primary_category_id?: number
+ /**
+ * Name of the category containing the most products.
+ */
+ primary_category_name?: string
+ /**
+ * Total number of variants
+ */
+ variant_count?: number
+ /**
+ * Highest priced variant
+ */
+ highest_variant_price?: number
+ /**
+ * Average price of all variants
+ */
+ average_variant_price?: number
+ /**
+ * Lowest priced variant in the store
+ */
+ lowest_variant_price?: string
+ oldest_variant_date?: string
+ newest_variant_date?: string
+ }
+ /**
+ * Metafield for products, categories, variants, and brands. The max number of metafields allowed on each product, category, variant, or brand is fifty. For more information, see [Platform Limits](https://support.bigcommerce.com/s/article/Platform-Limits) in the Help Center.
+ */
+ metafield_Base: {
+ /**
+ * Unique ID of the *Metafield*. Read-Only.
+ */
+ id?: number
+ /**
+ * Determines the visibility and writeability of the field by other API consumers.
+ *
+ * |Value|Description
+ * |-|-|
+ * |`app_only`|Private to the app that owns the field|
+ * |`read`|Visible to other API consumers|
+ * |`write`|Open for reading and writing by other API consumers|
+ * |`read_and_sf_access`|Visible to other API consumers, including on storefront|
+ * |`write_and_sf_access`|Open for reading and writing by other API consumers, including on storefront|
+ */
+ permission_set:
+ | 'app_only'
+ | 'read'
+ | 'write'
+ | 'read_and_sf_access'
+ | 'write_and_sf_access'
+ /**
+ * Namespace for the metafield, for organizational purposes. This is set set by the developer. Required for POST.
+ */
+ namespace: string
+ /**
+ * The name of the field, for example: `location_id`, `color`. Required for POST.
+ */
+ key: string
+ /**
+ * The value of the field, for example: `1`, `blue`. Required for POST.
+ */
+ value: string
+ /**
+ * Description for the metafields.
+ */
+ description?: string
+ /**
+ * The type of resource with which the metafield is associated.
+ */
+ resource_type?: 'category' | 'brand' | 'product' | 'variant'
+ /**
+ * The ID for the resource with which the metafield is associated.
+ */
+ resource_id?: number
+ /**
+ * Date and time of the metafield's creation. Read-Only.
+ */
+ date_created?: string
+ /**
+ * Date and time when the metafield was last updated. Read-Only.
+ */
+ date_modified?: string
+ }
+ /**
+ * Common ComplexRule properties.
+ */
+ complexRule_Base: {
+ /**
+ * The unique numeric ID of the rule; increments sequentially.
+ * Read-Only
+ */
+ id?: number
+ /**
+ * The unique numeric ID of the product with which the rule is associated; increments sequentially.
+ */
+ product_id?: number
+ /**
+ * The priority to give this rule when making adjustments to the product properties.
+ */
+ sort_order?: number
+ /**
+ * Flag for determining whether the rule is to be used when adjusting a product's price, weight, image, or availabilty.
+ */
+ enabled?: boolean
+ /**
+ * Flag for determining whether other rules should not be applied after this rule has been applied.
+ */
+ stop?: boolean
+ /**
+ * Flag for determining whether the rule should disable purchasing of a product when the conditions are applied.
+ */
+ purchasing_disabled?: boolean
+ /**
+ * Message displayed on the storefront when a rule disables the purchasing of a product.
+ */
+ purchasing_disabled_message?: string
+ /**
+ * Flag for determining whether the rule should hide purchasing of a product when the conditions are applied.
+ */
+ purchasing_hidden?: boolean
+ /**
+ * The URL for an image displayed on the storefront when the conditions are applied. Limit of 8MB per file.
+ */
+ image_url?: string
+ price_adjuster?: definitions['adjuster_Full']
+ weight_adjuster?: definitions['adjuster_Full']
+ conditions?: definitions['complexRuleConditionBase'][]
+ }
+ /**
+ * Gets custom fields associated with a product. These allow you to specify additional information that will appear on the product's page, such as a book's ISBN or a DVD's release date.
+ */
+ productCustomField_Base: {
+ /**
+ * The unique numeric ID of the custom field; increments sequentially.
+ * Read-Only
+ */
+ id?: number
+ /**
+ * The name of the field, shown on the storefront, orders, etc. Required for /POST
+ */
+ name: string
+ /**
+ * The name of the field, shown on the storefront, orders, etc. Required for /POST
+ */
+ value: string
+ }
+ /**
+ * The model for a POST to create a custom field on a product.
+ */
+ productCustomField_Post: {
+ /**
+ * The unique numeric ID of the custom field; increments sequentially.
+ * Read-Only
+ */
+ id?: number
+ /**
+ * The name of the field, shown on the storefront, orders, etc. Required for /POST
+ */
+ name: string
+ /**
+ * The name of the field, shown on the storefront, orders, etc. Required for /POST
+ */
+ value: string
+ }
+ /**
+ * The model for a PUT to update a custom field on a product.
+ */
+ productCustomField_Put: {
+ /**
+ * The unique numeric ID of the custom field; increments sequentially.
+ * Read-Only
+ */
+ id?: number
+ /**
+ * The name of the field, shown on the storefront, orders, etc. Required for /POST
+ */
+ name: string
+ /**
+ * The name of the field, shown on the storefront, orders, etc. Required for /POST
+ */
+ value: string
+ }
+ /**
+ * Complex rules may return with conditions that apply to one or more variants, or with a single modifier value (if the rules were created using the v2 API or the control panel). Complex rules created or updated in the v3 API must have conditions that either reference multiple `modifier_value_id`'s, or else reference a `modifier_value_id` and a `variant_id`.
+ */
+ complexRuleConditionBase: {
+ /**
+ * The unique numeric ID of the rule condition; increments sequentially. Read-Only
+ */
+ id?: number
+ /**
+ * The unique numeric ID of the rule with which the condition is associated.
+ * Read-Only
+ */
+ rule_id?: number
+ /**
+ * The unique numeric ID of the modifier with which the rule condition is associated.
+ * Required in /POST.
+ */
+ modifier_id: number
+ /**
+ * The unique numeric ID of the modifier value with which the rule condition is associated.
+ * Required in /POST.
+ */
+ modifier_value_id: number
+ /**
+ * The unique numeric ID of the variant with which the rule condition is associated.
+ * Required in /POST.
+ */
+ variant_id: number
+ /**
+ * (READ-ONLY:) The unique numeric ID of the SKU (v2 API), or Combination, with which the rule condition is associated. This is to maintain cross-compatibility between v2 and v3.
+ */
+ combination_id?: number
+ }
+ /**
+ * The custom URL for the category on the storefront.
+ */
+ customUrl_Full: {
+ /**
+ * Category URL on the storefront.
+ */
+ url?: string
+ /**
+ * Returns `true` if the URL has been changed from its default state (the auto-assigned URL that BigCommerce provides).
+ */
+ is_customized?: boolean
+ }
+ /**
+ * Common Bulk Pricing Rule properties
+ */
+ bulkPricingRule_Full: {
+ /**
+ * Unique ID of the *Bulk Pricing Rule*. Read-Only.
+ */
+ id?: number
+ /**
+ * The minimum inclusive quantity of a product to satisfy this rule. Must be greater than or equal to zero.
+ * Required in /POST.
+ */
+ quantity_min: number
+ /**
+ * The maximum inclusive quantity of a product to satisfy this rule. Must be greater than the `quantity_min` value – unless this field has a value of 0 (zero), in which case there will be no maximum bound for this rule.
+ * Required in /POST.
+ */
+ quantity_max: number
+ /**
+ * The type of adjustment that is made. Values: `price` - the adjustment amount per product; `percent` - the adjustment as a percentage of the original price; `fixed` - the adjusted absolute price of the product.
+ * Required in /POST.
+ */
+ type: 'price' | 'percent' | 'fixed'
+ /**
+ * The discount can be a fixed dollar amount or a percentage. For a fixed dollar amount enter it as an integer and the response will return as an integer. For percentage enter the amount as the percentage divided by 100 using string format. For example 10% percent would be “.10”. The response will return as an integer.
+ * Required in /POST.
+ */
+ amount: number
+ }
+ /**
+ * The values for option config can vary based on the Modifier created.
+ */
+ productOptionConfig_Full: {
+ /**
+ * (date, text, multi_line_text, numbers_only_text) The default value. Shown on a date option as an ISO-8601–formatted string, or on a text option as a string.
+ */
+ default_value?: string
+ /**
+ * (checkbox) Flag for setting the checkbox to be checked by default.
+ */
+ checked_by_default?: boolean
+ /**
+ * (checkbox) Label displayed for the checkbox option.
+ */
+ checkbox_label?: string
+ /**
+ * (date) Flag to limit the dates allowed to be entered on a date option.
+ */
+ date_limited?: boolean
+ /**
+ * (date) The type of limit that is allowed to be entered on a date option.
+ */
+ date_limit_mode?: 'earliest' | 'range' | 'latest'
+ /**
+ * (date) The earliest date allowed to be entered on the date option, as an ISO-8601 formatted string.
+ */
+ date_earliest_value?: string
+ /**
+ * (date) The latest date allowed to be entered on the date option, as an ISO-8601 formatted string.
+ */
+ date_latest_value?: string
+ /**
+ * (file) The kind of restriction on the file types that can be uploaded with a file upload option. Values: `specific` - restricts uploads to particular file types; `all` - allows all file types.
+ */
+ file_types_mode?: 'specific' | 'all'
+ /**
+ * (file) The type of files allowed to be uploaded if the `file_type_option` is set to `specific`. Values:
+ * `images` - Allows upload of image MIME types (`bmp`, `gif`, `jpg`, `jpeg`, `jpe`, `jif`, `jfif`, `jfi`, `png`, `wbmp`, `xbm`, `tiff`). `documents` - Allows upload of document MIME types (`txt`, `pdf`, `rtf`, `doc`, `docx`, `xls`, `xlsx`, `accdb`, `mdb`, `one`, `pps`, `ppsx`, `ppt`, `pptx`, `pub`, `odt`, `ods`, `odp`, `odg`, `odf`).
+ * `other` - Allows file types defined in the `file_types_other` array.
+ */
+ file_types_supported?: string[]
+ /**
+ * (file) A list of other file types allowed with the file upload option.
+ */
+ file_types_other?: string[]
+ /**
+ * (file) The maximum size for a file that can be used with the file upload option. This will still be limited by the server.
+ */
+ file_max_size?: number
+ /**
+ * (text, multi_line_text) Flag to validate the length of a text or multi-line text input.
+ */
+ text_characters_limited?: boolean
+ /**
+ * (text, multi_line_text) The minimum length allowed for a text or multi-line text option.
+ */
+ text_min_length?: number
+ /**
+ * (text, multi_line_text) The maximum length allowed for a text or multi line text option.
+ */
+ text_max_length?: number
+ /**
+ * (multi_line_text) Flag to validate the maximum number of lines allowed on a multi-line text input.
+ */
+ text_lines_limited?: boolean
+ /**
+ * (multi_line_text) The maximum number of lines allowed on a multi-line text input.
+ */
+ text_max_lines?: number
+ /**
+ * (numbers_only_text) Flag to limit the value of a number option.
+ */
+ number_limited?: boolean
+ /**
+ * (numbers_only_text) The type of limit on values entered for a number option.
+ */
+ number_limit_mode?: 'lowest' | 'highest' | 'range'
+ /**
+ * (numbers_only_text) The lowest allowed value for a number option if `number_limited` is true.
+ */
+ number_lowest_value?: number
+ /**
+ * (numbers_only_text) The highest allowed value for a number option if `number_limited` is true.
+ */
+ number_highest_value?: number
+ /**
+ * (numbers_only_text) Flag to limit the input on a number option to whole numbers only.
+ */
+ number_integers_only?: boolean
+ /**
+ * (product_list, product_list_with_images) Flag for automatically adjusting inventory on a product included in the list.
+ */
+ product_list_adjusts_inventory?: boolean
+ /**
+ * (product_list, product_list_with_images) Flag to add the optional product's price to the main product's price.
+ */
+ product_list_adjusts_pricing?: boolean
+ /**
+ * (product_list, product_list_with_images) How to factor the optional product's weight and package dimensions into the shipping quote. Values: `none` - don't adjust; `weight` - use shipping weight only; `package` - use weight and dimensions.
+ */
+ product_list_shipping_calc?: 'none' | 'weight' | 'package'
+ }
+ /**
+ * Adjuster for Complex Rules.
+ */
+ adjuster_Full: {
+ /**
+ * The type of adjuster for either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster?: 'relative' | 'percentage'
+ /**
+ * The numeric amount by which the adjuster will change either the price or the weight of the variant, when the modifier value is selected on the storefront.
+ */
+ adjuster_value?: number
+ }
+ /**
+ * Errors during batch usage for the BigCommerce API.
+ */
+ resp_variantBatchError: {
+ batch_errors?: (definitions['error_Base'] & {
+ errors?: { additionalProperties?: string }
+ })[]
+ }
+ /**
+ * Data about the response, including pagination and collection totals.
+ */
+ metaCollection_Full: { pagination?: definitions['pagination_Full'] }
+ /**
+ * Data about the response, including pagination and collection totals.
+ */
+ pagination_Full: {
+ /**
+ * Total number of items in the result set.
+ */
+ total?: number
+ /**
+ * Total number of items in the collection response.
+ */
+ count?: number
+ /**
+ * The amount of items returned in the collection per page, controlled by the limit parameter.
+ */
+ per_page?: number
+ /**
+ * The page you are currently on within the collection.
+ */
+ current_page?: number
+ /**
+ * The total number of pages in the collection.
+ */
+ total_pages?: number
+ /**
+ * Pagination links for the previous and next parts of the whole collection.
+ */
+ links?: {
+ /**
+ * Link to the previous page returned in the response.
+ */
+ previous?: string
+ /**
+ * Link to the current page returned in the response.
+ */
+ current?: string
+ /**
+ * Link to the next page returned in the response.
+ */
+ next?: string
+ }
+ }
+ /**
+ * Empty meta object; may be used later.
+ */
+ metaEmpty_Full: { [key: string]: any }
+ errorResponse_Full: definitions['error_Base'] & {
+ errors?: definitions['detailedErrors']
+ }
+ /**
+ * Error payload for the BigCommerce API.
+ */
+ error_Base: {
+ /**
+ * The HTTP status code.
+ */
+ status?: number
+ /**
+ * The error title describing the particular error.
+ */
+ title?: string
+ type?: string
+ instance?: string
+ }
+ /**
+ * Error payload for the BigCommerce API.
+ */
+ errorNotFound: {
+ /**
+ * 404 HTTP status code.
+ */
+ status?: number
+ /**
+ * The error title describing the particular error.
+ */
+ title?: string
+ type?: string
+ instance?: string
+ }
+ /**
+ * A gift-certificate model.
+ */
+ giftCertificate_Full: {
+ /**
+ * The gift-certificate code.
+ */
+ code?: string
+ /**
+ * The balance on a gift certificate when it was purchased.
+ */
+ original_balance?: number
+ /**
+ * The balance on a gift certificate at the time of this purchase.
+ */
+ starting_balance?: number
+ /**
+ * The remaining balance on a gift certificate.
+ */
+ remaining_balance?: number
+ /**
+ * The status of a gift certificate: `active` - gift certificate is active; `pending` - gift certificate purchase is pending; `disabled` - gift certificate is disabled; `expired` - gift certificate is expired.
+ */
+ status?: 'active' | 'pending' | 'disabled' | 'expired'
+ }
+ /**
+ * No-content response for the BigCommerce API.
+ */
+ errorNoContent: {
+ /**
+ * 204 HTTP status code.
+ */
+ status?: number
+ /**
+ * The error title describing the situation.
+ */
+ title?: string
+ type?: string
+ instance?: string
+ }
+ detailedErrors: { additionalProperties?: string }
+ product_Full: definitions['product_Base'] & {
+ /**
+ * The date on which the product was created.
+ */
+ date_created?: string
+ /**
+ * The date on which the product was modified.
+ */
+ date_modified?: string
+ /**
+ * ID of the product. Read Only.
+ */
+ id?: number
+ /**
+ * The unique identifier of the base variant associated with a simple product. This value is `null` for complex products.
+ */
+ base_variant_id?: number
+ /**
+ * The price of the product as seen on the storefront. It will be equal to the `sale_price`, if set, and the `price` if there is not a `sale_price`.
+ */
+ calculated_price?: number
+ options?: definitions['productOption_Base'][]
+ modifiers?: definitions['productModifier_Full'][]
+ /**
+ * Minimum Advertised Price.
+ */
+ map_price?: number
+ /**
+ * Indicates that the product is in an Option Set (legacy V2 concept).
+ */
+ option_set_id?: number
+ /**
+ * Legacy template setting which controls if the option set shows up to the side of or below the product image and description.
+ */
+ option_set_display?: string
+ } & { variants?: definitions['productVariant_Full'] }
+ /**
+ * Common ProductImage properties.
+ */
+ productImage_Full: definitions['productImage_Base'] & {
+ /**
+ * The unique numeric ID of the image; increments sequentially.
+ */
+ id?: number
+ /**
+ * The unique numeric identifier for the product with which the image is associated.
+ */
+ product_id?: number
+ /**
+ * The zoom URL for this image. By default, this is used as the zoom image on product pages when zoom images are enabled.
+ */
+ url_zoom?: string
+ /**
+ * The standard URL for this image. By default, this is used for product-page images.
+ */
+ url_standard?: string
+ /**
+ * The thumbnail URL for this image. By default, this is the image size used on the category page and in side panels.
+ */
+ url_thumbnail?: string
+ /**
+ * The tiny URL for this image. By default, this is the image size used for thumbnails beneath the product image on a product page.
+ */
+ url_tiny?: string
+ /**
+ * The date on which the product image was modified.
+ */
+ date_modified?: string
+ }
+ metafield_Post: definitions['metafield_Base']
+ /**
+ * The model for batch updating products.
+ */
+ product_Put_Collection: ({
+ /**
+ * The unique numerical ID of the product; increments sequentially. Required on batch product `PUT` requests.
+ */
+ id: number
+ } & definitions['product_Base'])[]
+ /**
+ * The values for option config can vary based on the Modifier created.
+ */
+ config_Full: {
+ /**
+ * (date, text, multi_line_text, numbers_only_text) The default value. Shown on a date option as an ISO-8601–formatted string, or on a text option as a string.
+ */
+ default_value?: string
+ /**
+ * (checkbox) Flag for setting the checkbox to be checked by default.
+ */
+ checked_by_default?: boolean
+ /**
+ * (checkbox) Label displayed for the checkbox option.
+ */
+ checkbox_label?: string
+ /**
+ * (date) Flag to limit the dates allowed to be entered on a date option.
+ */
+ date_limited?: boolean
+ /**
+ * (date) The type of limit that is allowed to be entered on a date option.
+ */
+ date_limit_mode?: 'earliest' | 'range' | 'latest'
+ /**
+ * (date) The earliest date allowed to be entered on the date option, as an ISO-8601 formatted string.
+ */
+ date_earliest_value?: string
+ /**
+ * (date) The latest date allowed to be entered on the date option, as an ISO-8601 formatted string.
+ */
+ date_latest_value?: string
+ /**
+ * (file) The kind of restriction on the file types that can be uploaded with a file upload option. Values: `specific` - restricts uploads to particular file types; `all` - allows all file types.
+ */
+ file_types_mode?: 'specific' | 'all'
+ /**
+ * (file) The type of files allowed to be uploaded if the `file_type_option` is set to `specific`. Values:
+ * `images` - Allows upload of image MIME types (`bmp`, `gif`, `jpg`, `jpeg`, `jpe`, `jif`, `jfif`, `jfi`, `png`, `wbmp`, `xbm`, `tiff`). `documents` - Allows upload of document MIME types (`txt`, `pdf`, `rtf`, `doc`, `docx`, `xls`, `xlsx`, `accdb`, `mdb`, `one`, `pps`, `ppsx`, `ppt`, `pptx`, `pub`, `odt`, `ods`, `odp`, `odg`, `odf`).
+ * `other` - Allows file types defined in the `file_types_other` array.
+ */
+ file_types_supported?: string[]
+ /**
+ * (file) A list of other file types allowed with the file upload option.
+ */
+ file_types_other?: string[]
+ /**
+ * (file) The maximum size for a file that can be used with the file upload option. This will still be limited by the server.
+ */
+ file_max_size?: number
+ /**
+ * (text, multi_line_text) Flag to validate the length of a text or multi-line text input.
+ */
+ text_characters_limited?: boolean
+ /**
+ * (text, multi_line_text) The minimum length allowed for a text or multi-line text option.
+ */
+ text_min_length?: number
+ /**
+ * (text, multi_line_text) The maximum length allowed for a text or multi line text option.
+ */
+ text_max_length?: number
+ /**
+ * (multi_line_text) Flag to validate the maximum number of lines allowed on a multi-line text input.
+ */
+ text_lines_limited?: boolean
+ /**
+ * (multi_line_text) The maximum number of lines allowed on a multi-line text input.
+ */
+ text_max_lines?: number
+ /**
+ * (numbers_only_text) Flag to limit the value of a number option.
+ */
+ number_limited?: boolean
+ /**
+ * (numbers_only_text) The type of limit on values entered for a number option.
+ */
+ number_limit_mode?: 'lowest' | 'highest' | 'range'
+ /**
+ * (numbers_only_text) The lowest allowed value for a number option if `number_limited` is true.
+ */
+ number_lowest_value?: number
+ /**
+ * (numbers_only_text) The highest allowed value for a number option if `number_limited` is true.
+ */
+ number_highest_value?: number
+ /**
+ * (numbers_only_text) Flag to limit the input on a number option to whole numbers only.
+ */
+ number_integers_only?: boolean
+ /**
+ * (product_list, product_list_with_images) Flag for automatically adjusting inventory on a product included in the list.
+ */
+ product_list_adjusts_inventory?: boolean
+ /**
+ * (product_list, product_list_with_images) Flag to add the optional product's price to the main product's price.
+ */
+ product_list_adjusts_pricing?: boolean
+ /**
+ * (product_list, product_list_with_images) How to factor the optional product's weight and package dimensions into the shipping quote. Values: `none` - don't adjust; `weight` - use shipping weight only; `package` - use weight and dimensions.
+ */
+ product_list_shipping_calc?: 'none' | 'weight' | 'package'
+ }
+ adjusters_Full: {
+ price?: definitions['adjuster_Full']
+ weight?: definitions['adjuster_Full']
+ /**
+ * The URL for an image displayed on the storefront when the modifier value is selected.Limit of 8MB per file.
+ */
+ image_url?: string
+ purchasing_disabled?: {
+ /**
+ * Flag for whether the modifier value disables purchasing when selected on the storefront. This can be used for temporarily disabling a particular modifier value.
+ */
+ status?: boolean
+ /**
+ * The message displayed on the storefront when the purchasing disabled status is `true`.
+ */
+ message?: string
+ }
+ }
+ /**
+ * Variant properties used on:
+ * * `/catalog/products/variants`
+ * * `/catalog/variants`
+ */
+ variant_Base: {
+ /**
+ * The cost price of the variant. Not affected by Price List prices.
+ */
+ cost_price?: number
+ /**
+ * This variant's base price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is `null`, the product's default price (set in the Product resource's `price` field) will be used as the base price.
+ */
+ price?: number
+ /**
+ * This variant's sale price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is null, the product's sale price (set in the Product resource's `price` field) will be used as the sale price.
+ */
+ sale_price?: number
+ /**
+ * This variant's retail price on the storefront. If a Price List ID is used, the Price List value will be used. If a Price List ID is not used, and this value is null, the product's retail price (set in the Product resource's `price` field) will be used as the retail price.
+ */
+ retail_price?: number
+ /**
+ * This variant's base weight on the storefront. If this value is null, the product's default weight (set in the Product resource's weight field) will be used as the base weight.
+ */
+ weight?: number
+ /**
+ * Width of the variant, which can be used when calculating shipping costs. If this value is `null`, the product's default width (set in the Product resource's `width` field) will be used as the base width.
+ */
+ width?: number
+ /**
+ * Height of the variant, which can be used when calculating shipping costs. If this value is `null`, the product's default height (set in the Product resource's `height` field) will be used as the base height.
+ */
+ height?: number
+ /**
+ * Depth of the variant, which can be used when calculating shipping costs. If this value is `null`, the product's default depth (set in the Product resource's `depth` field) will be used as the base depth.
+ */
+ depth?: number
+ /**
+ * Flag used to indicate whether the variant has free shipping. If `true`, the shipping cost for the variant will be zero.
+ */
+ is_free_shipping?: boolean
+ /**
+ * A fixed shipping cost for the variant. If defined, this value will be used during checkout instead of normal shipping-cost calculation.
+ */
+ fixed_cost_shipping_price?: number
+ /**
+ * If `true`, this variant will not be purchasable on the storefront.
+ */
+ purchasing_disabled?: boolean
+ /**
+ * If `purchasing_disabled` is `true`, this message should show on the storefront when the variant is selected.
+ */
+ purchasing_disabled_message?: string
+ /**
+ * The UPC code used in feeds for shopping comparison sites and external channel integrations.
+ */
+ upc?: string
+ /**
+ * Inventory level for the variant, which is used when the product's inventory_tracking is set to `variant`.
+ */
+ inventory_level?: number
+ /**
+ * When the variant hits this inventory level, it is considered low stock.
+ */
+ inventory_warning_level?: number
+ /**
+ * Identifies where in a warehouse the variant is located.
+ */
+ bin_picking_number?: string
+ }
+ /**
+ * Shared `Product` properties used in:
+ * * `POST`
+ * * `PUT`
+ * * `GET`
+ */
+ product_Base: {
+ /**
+ * The product name.
+ */
+ name?: string
+ /**
+ * The product type. One of: `physical` - a physical stock unit, `digital` - a digital download.
+ */
+ type?: 'physical' | 'digital'
+ /**
+ * User defined product code/stock keeping unit (SKU).
+ */
+ sku?: string
+ /**
+ * The product description, which can include HTML formatting.
+ */
+ description?: string
+ /**
+ * Weight of the product, which can be used when calculating shipping costs. This is based on the unit set on the store
+ */
+ weight?: number
+ /**
+ * Width of the product, which can be used when calculating shipping costs.
+ */
+ width?: number
+ /**
+ * Depth of the product, which can be used when calculating shipping costs.
+ */
+ depth?: number
+ /**
+ * Height of the product, which can be used when calculating shipping costs.
+ */
+ height?: number
+ /**
+ * The price of the product. The price should include or exclude tax, based on the store settings.
+ */
+ price?: number
+ /**
+ * The cost price of the product. Stored for reference only; it is not used or displayed anywhere on the store.
+ */
+ cost_price?: number
+ /**
+ * The retail cost of the product. If entered, the retail cost price will be shown on the product page.
+ */
+ retail_price?: number
+ /**
+ * If entered, the sale price will be used instead of value in the price field when calculating the product's cost.
+ */
+ sale_price?: number
+ /**
+ * The ID of the tax class applied to the product. (NOTE: Value ignored if automatic tax is enabled.)
+ */
+ tax_class_id?: number
+ /**
+ * Accepts AvaTax System Tax Codes, which identify products and services that fall into special sales-tax categories. By using these codes, merchants who subscribe to BigCommerce's Avalara Premium integration can calculate sales taxes more accurately. Stores without Avalara Premium will ignore the code when calculating sales tax. Do not pass more than one code. The codes are case-sensitive. For details, please see Avalara's documentation.
+ */
+ product_tax_code?: string
+ /**
+ * An array of IDs for the categories to which this product belongs. When updating a product, if an array of categories is supplied, all product categories will be overwritten. Does not accept more than 1,000 ID values.
+ */
+ categories?: number[]
+ /**
+ * A product can be added to an existing brand during a product /PUT or /POST.
+ */
+ brand_id?: number
+ /**
+ * Current inventory level of the product. Simple inventory tracking must be enabled (See the `inventory_tracking` field) for this to take any effect.
+ */
+ inventory_level?: number
+ /**
+ * Inventory warning level for the product. When the product's inventory level drops below the warning level, the store owner will be informed. Simple inventory tracking must be enabled (see the `inventory_tracking` field) for this to take any effect.
+ */
+ inventory_warning_level?: number
+ /**
+ * The type of inventory tracking for the product. Values are: `none` - inventory levels will not be tracked; `product` - inventory levels will be tracked using the `inventory_level` and `inventory_warning_level` fields; `variant` - inventory levels will be tracked based on variants, which maintain their own warning levels and inventory levels.
+ */
+ inventory_tracking?: 'none' | 'product' | 'variant'
+ /**
+ * A fixed shipping cost for the product. If defined, this value will be used during checkout instead of normal shipping-cost calculation.
+ */
+ fixed_cost_shipping_price?: number
+ /**
+ * Flag used to indicate whether the product has free shipping. If `true`, the shipping cost for the product will be zero.
+ */
+ is_free_shipping?: boolean
+ /**
+ * Flag to determine whether the product should be displayed to customers browsing the store. If `true`, the product will be displayed. If `false`, the product will be hidden from view.
+ */
+ is_visible?: boolean
+ /**
+ * Flag to determine whether the product should be included in the `featured products` panel when viewing the store.
+ */
+ is_featured?: boolean
+ /**
+ * An array of IDs for the related products.
+ */
+ related_products?: number[]
+ /**
+ * Warranty information displayed on the product page. Can include HTML formatting.
+ */
+ warranty?: string
+ /**
+ * The BIN picking number for the product.
+ */
+ bin_picking_number?: string
+ /**
+ * The layout template file used to render this product category. This field is writable only for stores with a Blueprint theme applied.
+ */
+ layout_file?: string
+ /**
+ * The product UPC code, which is used in feeds for shopping comparison sites and external channel integrations.
+ */
+ upc?: string
+ /**
+ * A comma-separated list of keywords that can be used to locate the product when searching the store.
+ */
+ search_keywords?: string
+ /**
+ * Availability of the product. Availability options are: `available` - the product can be purchased on the storefront; `disabled` - the product is listed in the storefront, but cannot be purchased; `preorder` - the product is listed for pre-orders.
+ */
+ availability?: 'available' | 'disabled' | 'preorder'
+ /**
+ * Availability text displayed on the checkout page, under the product title. Tells the customer how long it will normally take to ship this product, such as: 'Usually ships in 24 hours.'
+ */
+ availability_description?: string
+ /**
+ * Type of gift-wrapping options. Values: `any` - allow any gift-wrapping options in the store; `none` - disallow gift-wrapping on the product; `list` – provide a list of IDs in the `gift_wrapping_options_list` field.
+ */
+ gift_wrapping_options_type?: 'any' | 'none' | 'list'
+ /**
+ * A list of gift-wrapping option IDs.
+ */
+ gift_wrapping_options_list?: number[]
+ /**
+ * Priority to give this product when included in product lists on category pages and in search results. Lower integers will place the product closer to the top of the results.
+ */
+ sort_order?: number
+ /**
+ * The product condition. Will be shown on the product page if the `is_condition_shown` field's value is `true`. Possible values: `New`, `Used`, `Refurbished`.
+ */
+ condition?: 'New' | 'Used' | 'Refurbished'
+ /**
+ * Flag used to determine whether the product condition is shown to the customer on the product page.
+ */
+ is_condition_shown?: boolean
+ /**
+ * The minimum quantity an order must contain, to be eligible to purchase this product.
+ */
+ order_quantity_minimum?: number
+ /**
+ * The maximum quantity an order can contain when purchasing the product.
+ */
+ order_quantity_maximum?: number
+ /**
+ * Custom title for the product page. If not defined, the product name will be used as the meta title.
+ */
+ page_title?: string
+ /**
+ * Custom meta keywords for the product page. If not defined, the store's default keywords will be used.
+ */
+ meta_keywords?: string[]
+ /**
+ * Custom meta description for the product page. If not defined, the store's default meta description will be used.
+ */
+ meta_description?: string
+ /**
+ * The number of times the product has been viewed.
+ */
+ view_count?: number
+ /**
+ * Pre-order release date. See the `availability` field for details on setting a product's availability to accept pre-orders.
+ */
+ preorder_release_date?: string
+ /**
+ * Custom expected-date message to display on the product page. If undefined, the message defaults to the storewide setting. Can contain the `%%DATE%%` placeholder, which will be substituted for the release date.
+ */
+ preorder_message?: string
+ /**
+ * If set to true then on the preorder release date the preorder status will automatically be removed.
+ * If set to false, then on the release date the preorder status **will not** be removed. It will need to be changed manually either in the
+ * control panel or using the API. Using the API set `availability` to `available`.
+ */
+ is_preorder_only?: boolean
+ /**
+ * False by default, indicating that this product's price should be shown on the product page. If set to `true`, the price is hidden. (NOTE: To successfully set `is_price_hidden` to `true`, the `availability` value must be `disabled`.)
+ */
+ is_price_hidden?: boolean
+ /**
+ * By default, an empty string. If `is_price_hidden` is `true`, the value of `price_hidden_label` is displayed instead of the price. (NOTE: To successfully set a non-empty string value with `is_price_hidden` set to `true`, the `availability` value must be `disabled`.)
+ */
+ price_hidden_label?: string
+ custom_url?: definitions['customUrl_Full']
+ /**
+ * Type of product, defaults to `product`.
+ */
+ open_graph_type?:
+ | 'product'
+ | 'album'
+ | 'book'
+ | 'drink'
+ | 'food'
+ | 'game'
+ | 'movie'
+ | 'song'
+ | 'tv_show'
+ /**
+ * Title of the product, if not specified the product name will be used instead.
+ */
+ open_graph_title?: string
+ /**
+ * Description to use for the product, if not specified then the meta_description will be used instead.
+ */
+ open_graph_description?: string
+ /**
+ * Flag to determine if product description or open graph description is used.
+ */
+ open_graph_use_meta_description?: boolean
+ /**
+ * Flag to determine if product name or open graph name is used.
+ */
+ open_graph_use_product_name?: boolean
+ /**
+ * Flag to determine if product image or open graph image is used.
+ */
+ open_graph_use_image?: boolean
+ /**
+ * The brand can be created during a product PUT or POST request. If the brand already exists then the product will be added. If not the brand will be created and the product added. If using `brand_name` it performs a fuzzy match and adds the brand. eg. "Common Good" and "Common good" are the same. Brand name does not return as part of a product response. Only the `brand_id`.
+ */
+ 'brand_name or brand_id'?: string
+ /**
+ * Global Trade Item Number
+ */
+ gtin?: string
+ /**
+ * Manufacturer Part Number
+ */
+ mpn?: string
+ /**
+ * The total rating for the product.
+ */
+ reviews_rating_sum?: number
+ /**
+ * The number of times the product has been rated.
+ */
+ reviews_count?: number
+ /**
+ * The total quantity of this product sold.
+ */
+ total_sold?: number
+ custom_fields?: definitions['productCustomField_Put'][]
+ bulk_pricing_rules?: definitions['bulkPricingRule_Full'][]
+ images?: definitions['productImage_Full'][]
+ primary_image?: definitions['productImage_Full']
+ videos?: definitions['productVideo_Full'][]
+ }
+ /**
+ * Properties for updating metafields.
+ */
+ metafield_Put: {
+ /**
+ * Unique ID of the *Metafield*. Read-Only.
+ */
+ id?: number
+ } & definitions['metafield_Base']
+ metafield_Full: definitions['metafield_Put'] & {
+ /**
+ * Date and time of the metafield's creation. Read-Only.
+ */
+ date_created?: string
+ /**
+ * Date and time when the metafield was last updated. Read-Only.
+ */
+ date_modified?: string
+ }
+ /**
+ * The model for a PUT to update variants on a product.
+ */
+ productVariant_Put: definitions['productVariant_Base'] & {
+ product_id?: number
+ sku?: string
+ }
+}
diff --git a/framework/bigcommerce/api/definitions/store-content.ts b/framework/bigcommerce/api/definitions/store-content.ts
new file mode 100644
index 000000000..f00c28844
--- /dev/null
+++ b/framework/bigcommerce/api/definitions/store-content.ts
@@ -0,0 +1,329 @@
+/**
+ * This file was auto-generated by swagger-to-ts.
+ * Do not make direct changes to the file.
+ */
+
+export interface definitions {
+ blogPost_Full: {
+ /**
+ * ID of this blog post. (READ-ONLY)
+ */
+ id?: number
+ } & definitions['blogPost_Base']
+ addresses: {
+ /**
+ * Full URL of where the resource is located.
+ */
+ url?: string
+ /**
+ * Resource being accessed.
+ */
+ resource?: string
+ }
+ formField: {
+ /**
+ * Name of the form field
+ */
+ name?: string
+ /**
+ * Value of the form field
+ */
+ value?: string
+ }
+ page_Full: {
+ /**
+ * ID of the page.
+ */
+ id?: number
+ } & definitions['page_Base']
+ redirect: {
+ /**
+ * Numeric ID of the redirect.
+ */
+ id?: number
+ /**
+ * The path from which to redirect.
+ */
+ path: string
+ forward: definitions['forward']
+ /**
+ * URL of the redirect. READ-ONLY
+ */
+ url?: string
+ }
+ forward: {
+ /**
+ * The type of redirect. If it is a `manual` redirect then type will always be manual. Dynamic redirects will have the type of the page. Such as product or category.
+ */
+ type?: string
+ /**
+ * Reference of the redirect. Dynamic redirects will have the category or product number. Manual redirects will have the url that is being directed to.
+ */
+ ref?: number
+ }
+ customer_Full: {
+ /**
+ * Unique numeric ID of this customer. This is a READ-ONLY field; do not set or modify its value in a POST or PUT request.
+ */
+ id?: number
+ /**
+ * Not returned in any responses, but accepts up to two fields allowing you to set the customer’s password. If a password is not supplied, it is generated automatically. For further information about using this object, please see the Customers resource documentation.
+ */
+ _authentication?: {
+ force_reset?: string
+ password?: string
+ password_confirmation?: string
+ }
+ /**
+ * The name of the company for which the customer works.
+ */
+ company?: string
+ /**
+ * First name of the customer.
+ */
+ first_name: string
+ /**
+ * Last name of the customer.
+ */
+ last_name: string
+ /**
+ * Email address of the customer.
+ */
+ email: string
+ /**
+ * Phone number of the customer.
+ */
+ phone?: string
+ /**
+ * Date on which the customer registered from the storefront or was created in the control panel. This is a READ-ONLY field; do not set or modify its value in a POST or PUT request.
+ */
+ date_created?: string
+ /**
+ * Date on which the customer updated their details in the storefront or was updated in the control panel. This is a READ-ONLY field; do not set or modify its value in a POST or PUT request.
+ */
+ date_modified?: string
+ /**
+ * The amount of credit the customer has. (Float, Float as String, Integer)
+ */
+ store_credit?: string
+ /**
+ * The customer’s IP address when they signed up.
+ */
+ registration_ip_address?: string
+ /**
+ * The group to which the customer belongs.
+ */
+ customer_group_id?: number
+ /**
+ * Store-owner notes on the customer.
+ */
+ notes?: string
+ /**
+ * Used to identify customers who fall into special sales-tax categories – in particular, those who are fully or partially exempt from paying sales tax. Can be blank, or can contain a single AvaTax code. (The codes are case-sensitive.) Stores that subscribe to BigCommerce’s Avalara Premium integration will use this code to determine how/whether to apply sales tax. Does not affect sales-tax calculations for stores that do not subscribe to Avalara Premium.
+ */
+ tax_exempt_category?: string
+ /**
+ * Records whether the customer would like to receive marketing content from this store. READ-ONLY.This is a READ-ONLY field; do not set or modify its value in a POST or PUT request.
+ */
+ accepts_marketing?: boolean
+ addresses?: definitions['addresses']
+ /**
+ * Array of custom fields. This is a READ-ONLY field; do not set or modify its value in a POST or PUT request.
+ */
+ form_fields?: definitions['formField'][]
+ /**
+ * Force a password change on next login.
+ */
+ reset_pass_on_login?: boolean
+ }
+ categoryAccessLevel: {
+ /**
+ * + `all` - Customers can access all categories
+ * + `specific` - Customers can access a specific list of categories
+ * + `none` - Customers are prevented from viewing any of the categories in this group.
+ */
+ type?: 'all' | 'specific' | 'none'
+ /**
+ * Is an array of category IDs and should be supplied only if `type` is specific.
+ */
+ categories?: string[]
+ }
+ timeZone: {
+ /**
+ * a string identifying the time zone, in the format: /.
+ */
+ name?: string
+ /**
+ * a negative or positive number, identifying the offset from UTC/GMT, in seconds, during winter/standard time.
+ */
+ raw_offset?: number
+ /**
+ * "-/+" offset from UTC/GMT, in seconds, during summer/daylight saving time.
+ */
+ dst_offset?: number
+ /**
+ * a boolean indicating whether this time zone observes daylight saving time.
+ */
+ dst_correction?: boolean
+ date_format?: definitions['dateFormat']
+ }
+ count_Response: { count?: number }
+ dateFormat: {
+ /**
+ * string that defines dates’ display format, in the pattern: M jS Y
+ */
+ display?: string
+ /**
+ * string that defines the CSV export format for orders, customers, and products, in the pattern: M jS Y
+ */
+ export?: string
+ /**
+ * string that defines dates’ extended-display format, in the pattern: M jS Y @ g:i A.
+ */
+ extended_display?: string
+ }
+ blogTags: { tag?: string; post_ids?: number[] }[]
+ blogPost_Base: {
+ /**
+ * Title of this blog post.
+ */
+ title: string
+ /**
+ * URL for the public blog post.
+ */
+ url?: string
+ /**
+ * URL to preview the blog post. (READ-ONLY)
+ */
+ preview_url?: string
+ /**
+ * Text body of the blog post.
+ */
+ body: string
+ /**
+ * Tags to characterize the blog post.
+ */
+ tags?: string[]
+ /**
+ * Summary of the blog post. (READ-ONLY)
+ */
+ summary?: string
+ /**
+ * Whether the blog post is published.
+ */
+ is_published?: boolean
+ published_date?: definitions['publishedDate']
+ /**
+ * Published date in `ISO 8601` format.
+ */
+ published_date_iso8601?: string
+ /**
+ * Description text for this blog post’s `` element.
+ */
+ meta_description?: string
+ /**
+ * Keywords for this blog post’s `` element.
+ */
+ meta_keywords?: string
+ /**
+ * Name of the blog post’s author.
+ */
+ author?: string
+ /**
+ * Local path to a thumbnail uploaded to `product_images/` via [WebDav](https://support.bigcommerce.com/s/article/File-Access-WebDAV).
+ */
+ thumbnail_path?: string
+ }
+ publishedDate: { timezone_type?: string; date?: string; timezone?: string }
+ /**
+ * Not returned in any responses, but accepts up to two fields allowing you to set the customer’s password. If a password is not supplied, it is generated automatically. For further information about using this object, please see the Customers resource documentation.
+ */
+ authentication: {
+ force_reset?: string
+ password?: string
+ password_confirmation?: string
+ }
+ customer_Base: { [key: string]: any }
+ page_Base: {
+ /**
+ * ID of any parent Web page.
+ */
+ parent_id?: number
+ /**
+ * `page`: free-text page
+ * `link`: link to another web address
+ * `rss_feed`: syndicated content from an RSS feed
+ * `contact_form`: When the store's contact form is used.
+ */
+ type: 'page' | 'rss_feed' | 'contact_form' | 'raw' | 'link'
+ /**
+ * Where the page’s type is a contact form: object whose members are the fields enabled (in the control panel) for storefront display. Possible members are:`fullname`: full name of the customer submitting the form; `phone`: customer’s phone number, as submitted on the form; `companyname`: customer’s submitted company name; `orderno`: customer’s submitted order number; `rma`: customer’s submitted RMA (Return Merchandise Authorization) number.
+ */
+ contact_fields?: string
+ /**
+ * Where the page’s type is a contact form: email address that receives messages sent via the form.
+ */
+ email?: string
+ /**
+ * Page name, as displayed on the storefront.
+ */
+ name: string
+ /**
+ * Relative URL on the storefront for this page.
+ */
+ url?: string
+ /**
+ * Description contained within this page’s `` element.
+ */
+ meta_description?: string
+ /**
+ * HTML or variable that populates this page’s `` element, in default/desktop view. Required in POST if page type is `raw`.
+ */
+ body: string
+ /**
+ * HTML to use for this page's body when viewed in the mobile template (deprecated).
+ */
+ mobile_body?: string
+ /**
+ * If true, this page has a mobile version.
+ */
+ has_mobile_version?: boolean
+ /**
+ * If true, this page appears in the storefront’s navigation menu.
+ */
+ is_visible?: boolean
+ /**
+ * If true, this page is the storefront’s home page.
+ */
+ is_homepage?: boolean
+ /**
+ * Text specified for this page’s `` element. (If empty, the value of the name property is used.)
+ */
+ meta_title?: string
+ /**
+ * Layout template for this page. This field is writable only for stores with a Blueprint theme applied.
+ */
+ layout_file?: string
+ /**
+ * Order in which this page should display on the storefront. (Lower integers specify earlier display.)
+ */
+ sort_order?: number
+ /**
+ * Comma-separated list of keywords that shoppers can use to locate this page when searching the store.
+ */
+ search_keywords?: string
+ /**
+ * Comma-separated list of SEO-relevant keywords to include in the page’s `` element.
+ */
+ meta_keywords?: string
+ /**
+ * If page type is `rss_feed` the n this field is visisble. Required in POST required for `rss page` type.
+ */
+ feed: string
+ /**
+ * If page type is `link` this field is returned. Required in POST to create a `link` page.
+ */
+ link: string
+ content_type?: 'application/json' | 'text/javascript' | 'text/html'
+ }
+}
diff --git a/framework/bigcommerce/api/definitions/wishlist.ts b/framework/bigcommerce/api/definitions/wishlist.ts
new file mode 100644
index 000000000..6ec21c103
--- /dev/null
+++ b/framework/bigcommerce/api/definitions/wishlist.ts
@@ -0,0 +1,142 @@
+/**
+ * This file was auto-generated by swagger-to-ts.
+ * Do not make direct changes to the file.
+ */
+
+export interface definitions {
+ wishlist_Post: {
+ /**
+ * The customer id.
+ */
+ customer_id: number
+ /**
+ * Whether the wishlist is available to the public.
+ */
+ is_public?: boolean
+ /**
+ * The title of the wishlist.
+ */
+ name?: string
+ /**
+ * Array of Wishlist items.
+ */
+ items?: {
+ /**
+ * The ID of the product.
+ */
+ product_id?: number
+ /**
+ * The variant ID of the product.
+ */
+ variant_id?: number
+ }[]
+ }
+ wishlist_Put: {
+ /**
+ * The customer id.
+ */
+ customer_id: number
+ /**
+ * Whether the wishlist is available to the public.
+ */
+ is_public?: boolean
+ /**
+ * The title of the wishlist.
+ */
+ name?: string
+ /**
+ * Array of Wishlist items.
+ */
+ items?: {
+ /**
+ * The ID of the item
+ */
+ id?: number
+ /**
+ * The ID of the product.
+ */
+ product_id?: number
+ /**
+ * The variant ID of the item.
+ */
+ variant_id?: number
+ }[]
+ }
+ wishlist_Full: {
+ /**
+ * Wishlist ID, provided after creating a wishlist with a POST.
+ */
+ id?: number
+ /**
+ * The ID the customer to which the wishlist belongs.
+ */
+ customer_id?: number
+ /**
+ * The Wishlist's name.
+ */
+ name?: string
+ /**
+ * Whether the Wishlist is available to the public.
+ */
+ is_public?: boolean
+ /**
+ * The token of the Wishlist. This is created internally within BigCommerce. The Wishlist ID is to be used for external apps. Read-Only
+ */
+ token?: string
+ /**
+ * Array of Wishlist items
+ */
+ items?: definitions['wishlistItem_Full'][]
+ }
+ wishlistItem_Full: {
+ /**
+ * The ID of the item
+ */
+ id?: number
+ /**
+ * The ID of the product.
+ */
+ product_id?: number
+ /**
+ * The variant ID of the item.
+ */
+ variant_id?: number
+ }
+ wishlistItem_Post: {
+ /**
+ * The ID of the product.
+ */
+ product_id?: number
+ /**
+ * The variant ID of the product.
+ */
+ variant_id?: number
+ }
+ /**
+ * Data about the response, including pagination and collection totals.
+ */
+ pagination: {
+ /**
+ * Total number of items in the result set.
+ */
+ total?: number
+ /**
+ * Total number of items in the collection response.
+ */
+ count?: number
+ /**
+ * The amount of items returned in the collection per page, controlled by the limit parameter.
+ */
+ per_page?: number
+ /**
+ * The page you are currently on within the collection.
+ */
+ current_page?: number
+ /**
+ * The total number of pages in the collection.
+ */
+ total_pages?: number
+ }
+ error: { status?: number; title?: string; type?: string }
+ metaCollection: { pagination?: definitions['pagination'] }
+}
diff --git a/framework/bigcommerce/api/fragments/category-tree.ts b/framework/bigcommerce/api/fragments/category-tree.ts
new file mode 100644
index 000000000..e26f17195
--- /dev/null
+++ b/framework/bigcommerce/api/fragments/category-tree.ts
@@ -0,0 +1,9 @@
+export const categoryTreeItemFragment = /* GraphQL */ `
+ fragment categoryTreeItem on CategoryTreeItem {
+ entityId
+ name
+ path
+ description
+ productCount
+ }
+`
diff --git a/framework/bigcommerce/api/fragments/product.ts b/framework/bigcommerce/api/fragments/product.ts
new file mode 100644
index 000000000..d266b8c92
--- /dev/null
+++ b/framework/bigcommerce/api/fragments/product.ts
@@ -0,0 +1,113 @@
+export const productPrices = /* GraphQL */ `
+ fragment productPrices on Prices {
+ price {
+ value
+ currencyCode
+ }
+ salePrice {
+ value
+ currencyCode
+ }
+ retailPrice {
+ value
+ currencyCode
+ }
+ }
+`
+
+export const swatchOptionFragment = /* GraphQL */ `
+ fragment swatchOption on SwatchOptionValue {
+ isDefault
+ hexColors
+ }
+`
+
+export const multipleChoiceOptionFragment = /* GraphQL */ `
+ fragment multipleChoiceOption on MultipleChoiceOption {
+ values {
+ edges {
+ node {
+ label
+ ...swatchOption
+ }
+ }
+ }
+ }
+
+ ${swatchOptionFragment}
+`
+
+export const productInfoFragment = /* GraphQL */ `
+ fragment productInfo on Product {
+ entityId
+ name
+ path
+ brand {
+ entityId
+ }
+ description
+ prices {
+ ...productPrices
+ }
+ images {
+ edges {
+ node {
+ urlOriginal
+ altText
+ isDefault
+ }
+ }
+ }
+ variants {
+ edges {
+ node {
+ entityId
+ defaultImage {
+ urlOriginal
+ altText
+ isDefault
+ }
+ }
+ }
+ }
+ productOptions {
+ edges {
+ node {
+ __typename
+ entityId
+ displayName
+ ...multipleChoiceOption
+ }
+ }
+ }
+ localeMeta: metafields(namespace: $locale, keys: ["name", "description"])
+ @include(if: $hasLocale) {
+ edges {
+ node {
+ key
+ value
+ }
+ }
+ }
+ }
+
+ ${productPrices}
+ ${multipleChoiceOptionFragment}
+`
+
+export const productConnectionFragment = /* GraphQL */ `
+ fragment productConnnection on ProductConnection {
+ pageInfo {
+ startCursor
+ endCursor
+ }
+ edges {
+ cursor
+ node {
+ ...productInfo
+ }
+ }
+ }
+
+ ${productInfoFragment}
+`
diff --git a/framework/bigcommerce/api/index.ts b/framework/bigcommerce/api/index.ts
new file mode 100644
index 000000000..0216fe61b
--- /dev/null
+++ b/framework/bigcommerce/api/index.ts
@@ -0,0 +1,88 @@
+import type { RequestInit } from '@vercel/fetch'
+import type { CommerceAPIConfig } from '@commerce/api'
+import fetchGraphqlApi from './utils/fetch-graphql-api'
+import fetchStoreApi from './utils/fetch-store-api'
+
+export interface BigcommerceConfig extends CommerceAPIConfig {
+ // Indicates if the returned metadata with translations should be applied to the
+ // data or returned as it is
+ applyLocale?: boolean
+ storeApiUrl: string
+ storeApiToken: string
+ storeApiClientId: string
+ storeChannelId?: string
+ storeApiFetch(endpoint: string, options?: RequestInit): Promise
+}
+
+const API_URL = process.env.BIGCOMMERCE_STOREFRONT_API_URL
+const API_TOKEN = process.env.BIGCOMMERCE_STOREFRONT_API_TOKEN
+const STORE_API_URL = process.env.BIGCOMMERCE_STORE_API_URL
+const STORE_API_TOKEN = process.env.BIGCOMMERCE_STORE_API_TOKEN
+const STORE_API_CLIENT_ID = process.env.BIGCOMMERCE_STORE_API_CLIENT_ID
+const STORE_CHANNEL_ID = process.env.BIGCOMMERCE_CHANNEL_ID
+
+if (!API_URL) {
+ throw new Error(
+ `The environment variable BIGCOMMERCE_STOREFRONT_API_URL is missing and it's required to access your store`
+ )
+}
+
+if (!API_TOKEN) {
+ throw new Error(
+ `The environment variable BIGCOMMERCE_STOREFRONT_API_TOKEN is missing and it's required to access your store`
+ )
+}
+
+if (!(STORE_API_URL && STORE_API_TOKEN && STORE_API_CLIENT_ID)) {
+ throw new Error(
+ `The environment variables BIGCOMMERCE_STORE_API_URL, BIGCOMMERCE_STORE_API_TOKEN, BIGCOMMERCE_STORE_API_CLIENT_ID have to be set in order to access the REST API of your store`
+ )
+}
+
+export class Config {
+ private config: BigcommerceConfig
+
+ constructor(config: Omit) {
+ this.config = {
+ ...config,
+ // The customerCookie is not customizable for now, BC sets the cookie and it's
+ // not important to rename it
+ customerCookie: 'SHOP_TOKEN',
+ }
+ }
+
+ getConfig(userConfig: Partial = {}) {
+ return Object.entries(userConfig).reduce(
+ (cfg, [key, value]) => Object.assign(cfg, { [key]: value }),
+ { ...this.config }
+ )
+ }
+
+ setConfig(newConfig: Partial) {
+ Object.assign(this.config, newConfig)
+ }
+}
+
+const ONE_DAY = 60 * 60 * 24
+const config = new Config({
+ commerceUrl: API_URL,
+ apiToken: API_TOKEN,
+ cartCookie: process.env.BIGCOMMERCE_CART_COOKIE ?? 'bc_cartId',
+ cartCookieMaxAge: ONE_DAY * 30,
+ fetch: fetchGraphqlApi,
+ applyLocale: true,
+ // REST API only
+ storeApiUrl: STORE_API_URL,
+ storeApiToken: STORE_API_TOKEN,
+ storeApiClientId: STORE_API_CLIENT_ID,
+ storeChannelId: STORE_CHANNEL_ID,
+ storeApiFetch: fetchStoreApi,
+})
+
+export function getConfig(userConfig?: Partial) {
+ return config.getConfig(userConfig)
+}
+
+export function setConfig(newConfig: Partial) {
+ return config.setConfig(newConfig)
+}
diff --git a/framework/bigcommerce/api/operations/get-all-pages.ts b/framework/bigcommerce/api/operations/get-all-pages.ts
new file mode 100644
index 000000000..9fe83f2a1
--- /dev/null
+++ b/framework/bigcommerce/api/operations/get-all-pages.ts
@@ -0,0 +1,43 @@
+import type { RecursivePartial, RecursiveRequired } from '../utils/types'
+import { BigcommerceConfig, getConfig } from '..'
+import { definitions } from '../definitions/store-content'
+
+export type Page = definitions['page_Full']
+
+export type GetAllPagesResult<
+ T extends { pages: any[] } = { pages: Page[] }
+> = T
+
+async function getAllPages(opts?: {
+ config?: BigcommerceConfig
+ preview?: boolean
+}): Promise
+
+async function getAllPages(opts: {
+ url: string
+ config?: BigcommerceConfig
+ preview?: boolean
+}): Promise>
+
+async function getAllPages({
+ config,
+ preview,
+}: {
+ url?: string
+ config?: BigcommerceConfig
+ preview?: boolean
+} = {}): Promise {
+ 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[] }>
+ >('/v3/content/pages')
+ const pages = (data as RecursiveRequired) ?? []
+
+ return {
+ pages: preview ? pages : pages.filter((p) => p.is_visible),
+ }
+}
+
+export default getAllPages
diff --git a/framework/bigcommerce/api/operations/get-all-product-paths.ts b/framework/bigcommerce/api/operations/get-all-product-paths.ts
new file mode 100644
index 000000000..71522be35
--- /dev/null
+++ b/framework/bigcommerce/api/operations/get-all-product-paths.ts
@@ -0,0 +1,71 @@
+import type {
+ GetAllProductPathsQuery,
+ GetAllProductPathsQueryVariables,
+} from '../../schema'
+import type { RecursivePartial, RecursiveRequired } from '../utils/types'
+import filterEdges from '../utils/filter-edges'
+import { BigcommerceConfig, getConfig } from '..'
+
+export const getAllProductPathsQuery = /* GraphQL */ `
+ query getAllProductPaths($first: Int = 100) {
+ site {
+ products(first: $first) {
+ edges {
+ node {
+ path
+ }
+ }
+ }
+ }
+ }
+`
+
+export type ProductPath = NonNullable<
+ NonNullable[0]
+>
+
+export type ProductPaths = ProductPath[]
+
+export type { GetAllProductPathsQueryVariables }
+
+export type GetAllProductPathsResult<
+ T extends { products: any[] } = { products: ProductPaths }
+> = T
+
+async function getAllProductPaths(opts?: {
+ variables?: GetAllProductPathsQueryVariables
+ config?: BigcommerceConfig
+}): Promise
+
+async function getAllProductPaths<
+ T extends { products: any[] },
+ V = any
+>(opts: {
+ query: string
+ variables?: V
+ config?: BigcommerceConfig
+}): Promise>
+
+async function getAllProductPaths({
+ query = getAllProductPathsQuery,
+ variables,
+ config,
+}: {
+ query?: string
+ variables?: GetAllProductPathsQueryVariables
+ config?: BigcommerceConfig
+} = {}): Promise {
+ config = getConfig(config)
+ // RecursivePartial forces the method to check for every prop in the data, which is
+ // required in case there's a custom `query`
+ const { data } = await config.fetch<
+ RecursivePartial
+ >(query, { variables })
+ const products = data.site?.products?.edges
+
+ return {
+ products: filterEdges(products as RecursiveRequired),
+ }
+}
+
+export default getAllProductPaths
diff --git a/framework/bigcommerce/api/operations/get-all-products.ts b/framework/bigcommerce/api/operations/get-all-products.ts
new file mode 100644
index 000000000..0cd9737c2
--- /dev/null
+++ b/framework/bigcommerce/api/operations/get-all-products.ts
@@ -0,0 +1,132 @@
+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 '..'
+
+export const getAllProductsQuery = /* GraphQL */ `
+ query getAllProducts(
+ $hasLocale: Boolean = false
+ $locale: String = "null"
+ $entityIds: [Int!]
+ $first: Int = 10
+ $products: Boolean = false
+ $featuredProducts: Boolean = false
+ $bestSellingProducts: Boolean = false
+ $newestProducts: Boolean = false
+ ) {
+ site {
+ products(first: $first, entityIds: $entityIds) @include(if: $products) {
+ ...productConnnection
+ }
+ featuredProducts(first: $first) @include(if: $featuredProducts) {
+ ...productConnnection
+ }
+ bestSellingProducts(first: $first) @include(if: $bestSellingProducts) {
+ ...productConnnection
+ }
+ newestProducts(first: $first) @include(if: $newestProducts) {
+ ...productConnnection
+ }
+ }
+ }
+
+ ${productConnectionFragment}
+`
+
+export type ProductEdge = NonNullable<
+ NonNullable[0]
+>
+
+export type ProductNode = ProductEdge['node']
+
+export type GetAllProductsResult<
+ T extends Record = {
+ products: ProductEdge[]
+ }
+> = T
+
+const FIELDS = [
+ 'products',
+ 'featuredProducts',
+ 'bestSellingProducts',
+ 'newestProducts',
+]
+
+export type ProductTypes =
+ | 'products'
+ | 'featuredProducts'
+ | 'bestSellingProducts'
+ | 'newestProducts'
+
+export type ProductVariables = { field?: ProductTypes } & Omit<
+ GetAllProductsQueryVariables,
+ ProductTypes | 'hasLocale'
+>
+
+async function getAllProducts(opts?: {
+ variables?: ProductVariables
+ config?: BigcommerceConfig
+ preview?: boolean
+}): Promise
+
+async function getAllProducts<
+ T extends Record,
+ V = any
+>(opts: {
+ query: string
+ variables?: V
+ config?: BigcommerceConfig
+ preview?: boolean
+}): Promise>
+
+async function getAllProducts({
+ query = getAllProductsQuery,
+ variables: { field = 'products', ...vars } = {},
+ config,
+}: {
+ query?: string
+ variables?: ProductVariables
+ config?: BigcommerceConfig
+ preview?: boolean
+} = {}): Promise {
+ config = getConfig(config)
+
+ const locale = vars.locale || config.locale
+ const variables: GetAllProductsQueryVariables = {
+ ...vars,
+ locale,
+ hasLocale: !!locale,
+ }
+
+ if (!FIELDS.includes(field)) {
+ throw new Error(
+ `The field variable has to match one of ${FIELDS.join(', ')}`
+ )
+ }
+
+ variables[field] = true
+
+ // RecursivePartial forces the method to check for every prop in the data, which is
+ // required in case there's a custom `query`
+ const { data } = await config.fetch>(
+ query,
+ { variables }
+ )
+ const edges = data.site?.[field]?.edges
+ const products = filterEdges(edges as RecursiveRequired)
+
+ if (locale && config.applyLocale) {
+ products.forEach((product: RecursivePartial) => {
+ if (product.node) setProductLocaleMeta(product.node)
+ })
+ }
+
+ return { products }
+}
+
+export default getAllProducts
diff --git a/framework/bigcommerce/api/operations/get-customer-id.ts b/framework/bigcommerce/api/operations/get-customer-id.ts
new file mode 100644
index 000000000..7fe84093e
--- /dev/null
+++ b/framework/bigcommerce/api/operations/get-customer-id.ts
@@ -0,0 +1,34 @@
+import { GetCustomerIdQuery } from '../../schema'
+import { BigcommerceConfig, getConfig } from '..'
+
+export const getCustomerIdQuery = /* GraphQL */ `
+ query getCustomerId {
+ customer {
+ entityId
+ }
+ }
+`
+
+async function getCustomerId({
+ customerToken,
+ config,
+}: {
+ customerToken: string
+ config?: BigcommerceConfig
+}): Promise {
+ config = getConfig(config)
+
+ const { data } = await config.fetch(
+ getCustomerIdQuery,
+ undefined,
+ {
+ headers: {
+ cookie: `${config.customerCookie}=${customerToken}`,
+ },
+ }
+ )
+
+ return data?.customer?.entityId
+}
+
+export default getCustomerId
diff --git a/framework/bigcommerce/api/operations/get-customer-wishlist.ts b/framework/bigcommerce/api/operations/get-customer-wishlist.ts
new file mode 100644
index 000000000..2c1299b46
--- /dev/null
+++ b/framework/bigcommerce/api/operations/get-customer-wishlist.ts
@@ -0,0 +1,87 @@
+import type { RecursivePartial, RecursiveRequired } from '../utils/types'
+import { definitions } from '../definitions/wishlist'
+import { BigcommerceConfig, getConfig } from '..'
+import getAllProducts, { ProductEdge } from './get-all-products'
+
+export type Wishlist = Omit & {
+ items?: WishlistItem[]
+}
+
+export type WishlistItem = NonNullable<
+ definitions['wishlist_Full']['items']
+>[0] & {
+ product?: ProductEdge['node']
+}
+
+export type GetCustomerWishlistResult<
+ T extends { wishlist?: any } = { wishlist?: Wishlist }
+> = T
+
+export type GetCustomerWishlistVariables = {
+ customerId: number
+}
+
+async function getCustomerWishlist(opts: {
+ variables: GetCustomerWishlistVariables
+ config?: BigcommerceConfig
+ includeProducts?: boolean
+}): Promise
+
+async function getCustomerWishlist<
+ T extends { wishlist?: any },
+ V = any
+>(opts: {
+ url: string
+ variables: V
+ config?: BigcommerceConfig
+ includeProducts?: boolean
+}): Promise>
+
+async function getCustomerWishlist({
+ config,
+ variables,
+ includeProducts,
+}: {
+ url?: string
+ variables: GetCustomerWishlistVariables
+ config?: BigcommerceConfig
+ includeProducts?: boolean
+}): Promise {
+ config = getConfig(config)
+
+ const { data = [] } = await config.storeApiFetch<
+ RecursivePartial<{ data: Wishlist[] }>
+ >(`/v3/wishlists?customer_id=${variables.customerId}`)
+ const wishlist = data[0]
+
+ if (includeProducts && wishlist?.items?.length) {
+ const entityIds = wishlist.items
+ ?.map((item) => item?.product_id)
+ .filter((id): id is number => !!id)
+
+ if (entityIds?.length) {
+ const graphqlData = await getAllProducts({
+ variables: { first: 100, 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
+ }>((prods, p) => {
+ prods[p.node.entityId] = p
+ 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
+ }
+ })
+ }
+ }
+
+ return { wishlist: wishlist as RecursiveRequired }
+}
+
+export default getCustomerWishlist
diff --git a/framework/bigcommerce/api/operations/get-page.ts b/framework/bigcommerce/api/operations/get-page.ts
new file mode 100644
index 000000000..3010dd34c
--- /dev/null
+++ b/framework/bigcommerce/api/operations/get-page.ts
@@ -0,0 +1,53 @@
+import type { RecursivePartial, RecursiveRequired } from '../utils/types'
+import { BigcommerceConfig, getConfig } from '..'
+import { definitions } from '../definitions/store-content'
+
+export type Page = definitions['page_Full']
+
+export type GetPageResult = T
+
+export type PageVariables = {
+ id: number
+}
+
+async function getPage(opts: {
+ url?: string
+ variables: PageVariables
+ config?: BigcommerceConfig
+ preview?: boolean
+}): Promise
+
+async function getPage(opts: {
+ url: string
+ variables: V
+ config?: BigcommerceConfig
+ preview?: boolean
+}): Promise>
+
+async function getPage({
+ url,
+ variables,
+ config,
+ preview,
+}: {
+ url?: string
+ variables: PageVariables
+ config?: BigcommerceConfig
+ preview?: boolean
+}): Promise {
+ 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>(
+ url || `/v3/content/pages?id=${variables.id}&include=body`
+ )
+ const firstPage = data?.[0]
+ const page = firstPage as RecursiveRequired
+
+ if (preview || page?.is_visible) {
+ return { page }
+ }
+ return {}
+}
+
+export default getPage
diff --git a/framework/bigcommerce/api/operations/get-product.ts b/framework/bigcommerce/api/operations/get-product.ts
new file mode 100644
index 000000000..e75e87607
--- /dev/null
+++ b/framework/bigcommerce/api/operations/get-product.ts
@@ -0,0 +1,118 @@
+import type { GetProductQuery, GetProductQueryVariables } from '../../schema'
+import setProductLocaleMeta from '../utils/set-product-locale-meta'
+import { productInfoFragment } from '../fragments/product'
+import { BigcommerceConfig, getConfig } from '..'
+
+export const getProductQuery = /* GraphQL */ `
+ query getProduct(
+ $hasLocale: Boolean = false
+ $locale: String = "null"
+ $path: String!
+ ) {
+ site {
+ route(path: $path) {
+ node {
+ __typename
+ ... on Product {
+ ...productInfo
+ variants {
+ edges {
+ node {
+ entityId
+ defaultImage {
+ urlOriginal
+ altText
+ isDefault
+ }
+ prices {
+ ...productPrices
+ }
+ inventory {
+ aggregated {
+ availableToSell
+ warningLevel
+ }
+ isInStock
+ }
+ productOptions {
+ edges {
+ node {
+ __typename
+ entityId
+ displayName
+ ...multipleChoiceOption
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ${productInfoFragment}
+`
+
+export type ProductNode = Extract<
+ GetProductQuery['site']['route']['node'],
+ { __typename: 'Product' }
+>
+
+export type GetProductResult<
+ T extends { product?: any } = { product?: ProductNode }
+> = T
+
+export type ProductVariables = { locale?: string } & (
+ | { path: string; slug?: never }
+ | { path?: never; slug: string }
+)
+
+async function getProduct(opts: {
+ variables: ProductVariables
+ config?: BigcommerceConfig
+ preview?: boolean
+}): Promise
+
+async function getProduct(opts: {
+ query: string
+ variables: V
+ config?: BigcommerceConfig
+ preview?: boolean
+}): Promise>
+
+async function getProduct({
+ query = getProductQuery,
+ variables: { slug, ...vars },
+ config,
+}: {
+ query?: string
+ variables: ProductVariables
+ config?: BigcommerceConfig
+ preview?: boolean
+}): Promise {
+ config = getConfig(config)
+
+ const locale = vars.locale || config.locale
+ const variables: GetProductQueryVariables = {
+ ...vars,
+ locale,
+ hasLocale: !!locale,
+ path: slug ? `/${slug}/` : vars.path!,
+ }
+ const { data } = await config.fetch(query, { variables })
+ const product = data.site?.route?.node
+
+ if (product?.__typename === 'Product') {
+ if (locale && config.applyLocale) {
+ setProductLocaleMeta(product)
+ }
+ return { product }
+ }
+
+ return {}
+}
+
+export default getProduct
diff --git a/framework/bigcommerce/api/operations/get-site-info.ts b/framework/bigcommerce/api/operations/get-site-info.ts
new file mode 100644
index 000000000..44c0cfeb5
--- /dev/null
+++ b/framework/bigcommerce/api/operations/get-site-info.ts
@@ -0,0 +1,106 @@
+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'
+
+// Get 3 levels of categories
+export const getSiteInfoQuery = /* GraphQL */ `
+ query getSiteInfo {
+ site {
+ categoryTree {
+ ...categoryTreeItem
+ children {
+ ...categoryTreeItem
+ children {
+ ...categoryTreeItem
+ }
+ }
+ }
+ brands {
+ pageInfo {
+ startCursor
+ endCursor
+ }
+ edges {
+ cursor
+ node {
+ entityId
+ name
+ defaultImage {
+ urlOriginal
+ altText
+ }
+ pageTitle
+ metaDesc
+ metaKeywords
+ searchKeywords
+ path
+ }
+ }
+ }
+ }
+ }
+ ${categoryTreeItemFragment}
+`
+
+export type CategoriesTree = NonNullable<
+ GetSiteInfoQuery['site']['categoryTree']
+>
+
+export type BrandEdge = NonNullable<
+ NonNullable[0]
+>
+
+export type Brands = BrandEdge[]
+
+export type GetSiteInfoResult<
+ T extends { categories: any[]; brands: any[] } = {
+ categories: CategoriesTree
+ brands: Brands
+ }
+> = T
+
+async function getSiteInfo(opts?: {
+ variables?: GetSiteInfoQueryVariables
+ config?: BigcommerceConfig
+ preview?: boolean
+}): Promise
+
+async function getSiteInfo<
+ T extends { categories: any[]; brands: any[] },
+ V = any
+>(opts: {
+ query: string
+ variables?: V
+ config?: BigcommerceConfig
+ preview?: boolean
+}): Promise>
+
+async function getSiteInfo({
+ query = getSiteInfoQuery,
+ variables,
+ config,
+}: {
+ query?: string
+ variables?: GetSiteInfoQueryVariables
+ config?: BigcommerceConfig
+ preview?: boolean
+} = {}): Promise {
+ config = getConfig(config)
+ // RecursivePartial forces the method to check for every prop in the data, which is
+ // required in case there's a custom `query`
+ const { data } = await config.fetch>(
+ query,
+ { variables }
+ )
+ const categories = data.site?.categoryTree
+ const brands = data.site?.brands?.edges
+
+ return {
+ categories: (categories as RecursiveRequired) ?? [],
+ brands: filterEdges(brands as RecursiveRequired),
+ }
+}
+
+export default getSiteInfo
diff --git a/framework/bigcommerce/api/operations/login.ts b/framework/bigcommerce/api/operations/login.ts
new file mode 100644
index 000000000..8a8502705
--- /dev/null
+++ b/framework/bigcommerce/api/operations/login.ts
@@ -0,0 +1,73 @@
+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 '..'
+
+export const loginMutation = /* GraphQL */ `
+ mutation login($email: String!, $password: String!) {
+ login(email: $email, password: $password) {
+ result
+ }
+ }
+`
+
+export type LoginResult = T
+
+export type LoginVariables = LoginMutationVariables
+
+async function login(opts: {
+ variables: LoginVariables
+ config?: BigcommerceConfig
+ res: ServerResponse
+}): Promise
+
+async function login(opts: {
+ query: string
+ variables: V
+ res: ServerResponse
+ config?: BigcommerceConfig
+}): Promise>
+
+async function login({
+ query = loginMutation,
+ variables,
+ res: response,
+ config,
+}: {
+ query?: string
+ variables: LoginVariables
+ res: ServerResponse
+ config?: BigcommerceConfig
+}): Promise {
+ config = getConfig(config)
+
+ const { data, res } = await config.fetch>(
+ query,
+ { variables }
+ )
+ // Bigcommerce returns a Set-Cookie header with the auth cookie
+ let cookie = res.headers.get('Set-Cookie')
+
+ if (cookie && typeof cookie === 'string') {
+ // In development, don't set a secure cookie or the browser will ignore it
+ 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
+ // this case insensitive
+ cookie = cookie.replace(/; SameSite=none/gi, '; SameSite=lax')
+ }
+
+ response.setHeader(
+ 'Set-Cookie',
+ concatHeader(response.getHeader('Set-Cookie'), cookie)!
+ )
+ }
+
+ return {
+ result: data.login?.result,
+ }
+}
+
+export default login
diff --git a/framework/bigcommerce/api/utils/concat-cookie.ts b/framework/bigcommerce/api/utils/concat-cookie.ts
new file mode 100644
index 000000000..362e12e99
--- /dev/null
+++ b/framework/bigcommerce/api/utils/concat-cookie.ts
@@ -0,0 +1,14 @@
+type Header = string | number | string[] | undefined
+
+export default function concatHeader(prev: Header, val: Header) {
+ if (!val) return prev
+ if (!prev) return val
+
+ if (Array.isArray(prev)) return prev.concat(String(val))
+
+ prev = String(prev)
+
+ if (Array.isArray(val)) return [prev].concat(val)
+
+ return [prev, String(val)]
+}
diff --git a/framework/bigcommerce/api/utils/create-api-handler.ts b/framework/bigcommerce/api/utils/create-api-handler.ts
new file mode 100644
index 000000000..315ec464b
--- /dev/null
+++ b/framework/bigcommerce/api/utils/create-api-handler.ts
@@ -0,0 +1,58 @@
+import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'
+import { BigcommerceConfig, getConfig } from '..'
+
+export type BigcommerceApiHandler<
+ T = any,
+ H extends BigcommerceHandlers = {},
+ Options extends {} = {}
+> = (
+ req: NextApiRequest,
+ res: NextApiResponse>,
+ config: BigcommerceConfig,
+ handlers: H,
+ // Custom configs that may be used by a particular handler
+ options: Options
+) => void | Promise
+
+export type BigcommerceHandler = (options: {
+ req: NextApiRequest
+ res: NextApiResponse>
+ config: BigcommerceConfig
+ body: Body
+}) => void | Promise
+
+export type BigcommerceHandlers = {
+ [k: string]: BigcommerceHandler
+}
+
+export type BigcommerceApiResponse = {
+ data: T | null
+ errors?: { message: string; code?: string }[]
+}
+
+export default function createApiHandler<
+ T = any,
+ H extends BigcommerceHandlers = {},
+ Options extends {} = {}
+>(
+ handler: BigcommerceApiHandler,
+ handlers: H,
+ defaultOptions: Options
+) {
+ return function getApiHandler({
+ config,
+ operations,
+ options,
+ }: {
+ config?: BigcommerceConfig
+ operations?: Partial
+ options?: Options extends {} ? Partial : 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/bigcommerce/api/utils/errors.ts b/framework/bigcommerce/api/utils/errors.ts
new file mode 100644
index 000000000..77e2007fc
--- /dev/null
+++ b/framework/bigcommerce/api/utils/errors.ts
@@ -0,0 +1,25 @@
+import type { Response } from '@vercel/fetch'
+
+// Used for GraphQL errors
+export class BigcommerceGraphQLError extends Error {}
+
+export class BigcommerceApiError extends Error {
+ status: number
+ res: Response
+ data: any
+
+ constructor(msg: string, res: Response, data?: any) {
+ super(msg)
+ this.name = 'BigcommerceApiError'
+ this.status = res.status
+ this.res = res
+ this.data = data
+ }
+}
+
+export class BigcommerceNetworkError extends Error {
+ constructor(msg: string) {
+ super(msg)
+ this.name = 'BigcommerceNetworkError'
+ }
+}
diff --git a/framework/bigcommerce/api/utils/fetch-graphql-api.ts b/framework/bigcommerce/api/utils/fetch-graphql-api.ts
new file mode 100644
index 000000000..a449b81e0
--- /dev/null
+++ b/framework/bigcommerce/api/utils/fetch-graphql-api.ts
@@ -0,0 +1,38 @@
+import { FetcherError } from '@commerce/utils/errors'
+import type { GraphQLFetcher } from '@commerce/api'
+import { getConfig } from '..'
+import fetch from './fetch'
+
+const fetchGraphqlApi: GraphQLFetcher = async (
+ query: string,
+ { variables, preview } = {},
+ fetchOptions
+) => {
+ // log.warn(query)
+ const config = getConfig()
+ const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), {
+ ...fetchOptions,
+ method: 'POST',
+ headers: {
+ Authorization: `Bearer ${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 Bigcommerce API' }],
+ status: res.status,
+ })
+ }
+
+ return { data: json.data, res }
+}
+
+export default fetchGraphqlApi
diff --git a/framework/bigcommerce/api/utils/fetch-store-api.ts b/framework/bigcommerce/api/utils/fetch-store-api.ts
new file mode 100644
index 000000000..7e59b9f06
--- /dev/null
+++ b/framework/bigcommerce/api/utils/fetch-store-api.ts
@@ -0,0 +1,71 @@
+import type { RequestInit, Response } from '@vercel/fetch'
+import { getConfig } from '..'
+import { BigcommerceApiError, BigcommerceNetworkError } from './errors'
+import fetch from './fetch'
+
+export default async function fetchStoreApi(
+ endpoint: string,
+ options?: RequestInit
+): Promise {
+ const config = getConfig()
+ let res: Response
+
+ try {
+ res = await fetch(config.storeApiUrl + endpoint, {
+ ...options,
+ headers: {
+ ...options?.headers,
+ 'Content-Type': 'application/json',
+ 'X-Auth-Token': config.storeApiToken,
+ 'X-Auth-Client': config.storeApiClientId,
+ },
+ })
+ } catch (error) {
+ throw new BigcommerceNetworkError(
+ `Fetch to Bigcommerce failed: ${error.message}`
+ )
+ }
+
+ const contentType = res.headers.get('Content-Type')
+ const isJSON = contentType?.includes('application/json')
+
+ if (!res.ok) {
+ const data = isJSON ? await res.json() : await getTextOrNull(res)
+ const headers = getRawHeaders(res)
+ const msg = `Big Commerce API error (${
+ res.status
+ }) \nHeaders: ${JSON.stringify(headers, null, 2)}\n${
+ typeof data === 'string' ? data : JSON.stringify(data, null, 2)
+ }`
+
+ throw new BigcommerceApiError(msg, res, data)
+ }
+
+ if (res.status !== 204 && !isJSON) {
+ throw new BigcommerceApiError(
+ `Fetch to Bigcommerce API failed, expected JSON content but found: ${contentType}`,
+ res
+ )
+ }
+
+ // If something was removed, the response will be empty
+ return res.status === 204 ? null : await res.json()
+}
+
+function getRawHeaders(res: Response) {
+ const headers: { [key: string]: string } = {}
+
+ res.headers.forEach((value, key) => {
+ headers[key] = value
+ })
+
+ return headers
+}
+
+function getTextOrNull(res: Response) {
+ try {
+ return res.text()
+ } catch (err) {
+ return null
+ }
+}
diff --git a/framework/bigcommerce/api/utils/fetch.ts b/framework/bigcommerce/api/utils/fetch.ts
new file mode 100644
index 000000000..9d9fff3ed
--- /dev/null
+++ b/framework/bigcommerce/api/utils/fetch.ts
@@ -0,0 +1,3 @@
+import zeitFetch from '@vercel/fetch'
+
+export default zeitFetch()
diff --git a/framework/bigcommerce/api/utils/filter-edges.ts b/framework/bigcommerce/api/utils/filter-edges.ts
new file mode 100644
index 000000000..09cd20640
--- /dev/null
+++ b/framework/bigcommerce/api/utils/filter-edges.ts
@@ -0,0 +1,5 @@
+export default function filterEdges(
+ edges: (T | null | undefined)[] | null | undefined
+) {
+ return edges?.filter((edge): edge is T => !!edge) ?? []
+}
diff --git a/framework/bigcommerce/api/utils/get-cart-cookie.ts b/framework/bigcommerce/api/utils/get-cart-cookie.ts
new file mode 100644
index 000000000..7ca6cd5e4
--- /dev/null
+++ b/framework/bigcommerce/api/utils/get-cart-cookie.ts
@@ -0,0 +1,20 @@
+import { serialize, CookieSerializeOptions } from 'cookie'
+
+export default function getCartCookie(
+ name: string,
+ cartId?: string,
+ maxAge?: number
+) {
+ const options: CookieSerializeOptions =
+ cartId && maxAge
+ ? {
+ maxAge,
+ expires: new Date(Date.now() + maxAge * 1000),
+ secure: process.env.NODE_ENV === 'production',
+ path: '/',
+ sameSite: 'lax',
+ }
+ : { maxAge: -1, path: '/' } // Removes the cookie
+
+ return serialize(name, cartId || '', options)
+}
diff --git a/framework/bigcommerce/api/utils/is-allowed-method.ts b/framework/bigcommerce/api/utils/is-allowed-method.ts
new file mode 100644
index 000000000..78bbba568
--- /dev/null
+++ b/framework/bigcommerce/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/bigcommerce/api/utils/parse-item.ts b/framework/bigcommerce/api/utils/parse-item.ts
new file mode 100644
index 000000000..132f269f6
--- /dev/null
+++ b/framework/bigcommerce/api/utils/parse-item.ts
@@ -0,0 +1,14 @@
+import type { ItemBody as WishlistItemBody } from '../wishlist'
+import type { ItemBody } from '../cart'
+
+export const parseWishlistItem = (item: WishlistItemBody) => ({
+ product_id: item.productId,
+ variant_id: item.variantId,
+})
+
+export const parseCartItem = (item: ItemBody) => ({
+ quantity: item.quantity,
+ product_id: item.productId,
+ variant_id: item.variantId,
+ option_selections: item.optionSelections
+})
diff --git a/framework/bigcommerce/api/utils/set-product-locale-meta.ts b/framework/bigcommerce/api/utils/set-product-locale-meta.ts
new file mode 100644
index 000000000..767286477
--- /dev/null
+++ b/framework/bigcommerce/api/utils/set-product-locale-meta.ts
@@ -0,0 +1,21 @@
+import type { ProductNode } from '../operations/get-all-products'
+import type { RecursivePartial } from './types'
+
+export default function setProductLocaleMeta(
+ node: RecursivePartial
+) {
+ if (node.localeMeta?.edges) {
+ node.localeMeta.edges = node.localeMeta.edges.filter((edge) => {
+ const { key, value } = edge?.node ?? {}
+ if (key && key in node) {
+ ;(node as any)[key] = value
+ return false
+ }
+ return true
+ })
+
+ if (!node.localeMeta.edges.length) {
+ delete node.localeMeta
+ }
+ }
+}
diff --git a/framework/bigcommerce/api/utils/types.ts b/framework/bigcommerce/api/utils/types.ts
new file mode 100644
index 000000000..56f9c1728
--- /dev/null
+++ b/framework/bigcommerce/api/utils/types.ts
@@ -0,0 +1,7 @@
+export type RecursivePartial = {
+ [P in keyof T]?: RecursivePartial
+}
+
+export type RecursiveRequired = {
+ [P in keyof T]-?: RecursiveRequired
+}
diff --git a/framework/bigcommerce/api/wishlist/handlers/add-item.ts b/framework/bigcommerce/api/wishlist/handlers/add-item.ts
new file mode 100644
index 000000000..a02ef4434
--- /dev/null
+++ b/framework/bigcommerce/api/wishlist/handlers/add-item.ts
@@ -0,0 +1,56 @@
+import type { WishlistHandlers } from '..'
+import getCustomerId from '../../operations/get-customer-id'
+import getCustomerWishlist from '../../operations/get-customer-wishlist'
+import { parseWishlistItem } from '../../utils/parse-item'
+
+// Returns the wishlist of the signed customer
+const addItem: WishlistHandlers['addItem'] = async ({
+ res,
+ body: { customerToken, item },
+ config,
+}) => {
+ if (!item) {
+ return res.status(400).json({
+ data: null,
+ errors: [{ message: 'Missing item' }],
+ })
+ }
+
+ const customerId =
+ customerToken && (await getCustomerId({ customerToken, config }))
+
+ if (!customerId) {
+ return res.status(400).json({
+ data: null,
+ errors: [{ message: 'Invalid request' }],
+ })
+ }
+
+ const { wishlist } = await getCustomerWishlist({
+ variables: { customerId },
+ config,
+ })
+ const options = {
+ method: 'POST',
+ body: JSON.stringify(
+ wishlist
+ ? {
+ items: [parseWishlistItem(item)],
+ }
+ : {
+ name: 'Wishlist',
+ customer_id: customerId,
+ items: [parseWishlistItem(item)],
+ is_public: false,
+ }
+ ),
+ }
+
+ const { data } = wishlist
+ ? await config.storeApiFetch(`/v3/wishlists/${wishlist.id}/items`, options)
+ : await config.storeApiFetch('/v3/wishlists', options)
+
+ res.status(200).json({ data })
+}
+
+export default addItem
diff --git a/framework/bigcommerce/api/wishlist/handlers/get-wishlist.ts b/framework/bigcommerce/api/wishlist/handlers/get-wishlist.ts
new file mode 100644
index 000000000..3eb3000cc
--- /dev/null
+++ b/framework/bigcommerce/api/wishlist/handlers/get-wishlist.ts
@@ -0,0 +1,37 @@
+import getCustomerId from '../../operations/get-customer-id'
+import getCustomerWishlist from '../../operations/get-customer-wishlist'
+import type { Wishlist, WishlistHandlers } from '..'
+
+// Return wishlist info
+const getWishlist: WishlistHandlers['getWishlist'] = async ({
+ res,
+ body: { customerToken, includeProducts },
+ config,
+}) => {
+ let result: { data?: Wishlist } = {}
+
+ if (customerToken) {
+ const customerId =
+ customerToken && (await getCustomerId({ customerToken, config }))
+
+ if (!customerId) {
+ // If the customerToken is invalid, then this request is too
+ return res.status(404).json({
+ data: null,
+ errors: [{ message: 'Wishlist not found' }],
+ })
+ }
+
+ const { wishlist } = await getCustomerWishlist({
+ variables: { customerId },
+ includeProducts,
+ config,
+ })
+
+ result = { data: wishlist }
+ }
+
+ res.status(200).json({ data: result.data ?? null })
+}
+
+export default getWishlist
diff --git a/framework/bigcommerce/api/wishlist/handlers/remove-item.ts b/framework/bigcommerce/api/wishlist/handlers/remove-item.ts
new file mode 100644
index 000000000..29b6eff60
--- /dev/null
+++ b/framework/bigcommerce/api/wishlist/handlers/remove-item.ts
@@ -0,0 +1,39 @@
+import getCustomerId from '../../operations/get-customer-id'
+import getCustomerWishlist, {
+ Wishlist,
+} from '../../operations/get-customer-wishlist'
+import type { WishlistHandlers } from '..'
+
+// Return current wishlist info
+const removeItem: WishlistHandlers['removeItem'] = async ({
+ res,
+ body: { customerToken, itemId },
+ config,
+}) => {
+ const customerId =
+ customerToken && (await getCustomerId({ customerToken, config }))
+ const { wishlist } =
+ (customerId &&
+ (await getCustomerWishlist({
+ variables: { customerId },
+ config,
+ }))) ||
+ {}
+
+ if (!wishlist || !itemId) {
+ return res.status(400).json({
+ data: null,
+ errors: [{ message: 'Invalid request' }],
+ })
+ }
+
+ const result = await config.storeApiFetch<{ data: Wishlist } | null>(
+ `/v3/wishlists/${wishlist.id}/items/${itemId}`,
+ { method: 'DELETE' }
+ )
+ const data = result?.data ?? null
+
+ res.status(200).json({ data })
+}
+
+export default removeItem
diff --git a/framework/bigcommerce/api/wishlist/index.ts b/framework/bigcommerce/api/wishlist/index.ts
new file mode 100644
index 000000000..94194dd41
--- /dev/null
+++ b/framework/bigcommerce/api/wishlist/index.ts
@@ -0,0 +1,103 @@
+import isAllowedMethod from '../utils/is-allowed-method'
+import createApiHandler, {
+ BigcommerceApiHandler,
+ BigcommerceHandler,
+} from '../utils/create-api-handler'
+import { BigcommerceApiError } from '../utils/errors'
+import type {
+ Wishlist,
+ WishlistItem,
+} from '../operations/get-customer-wishlist'
+import getWishlist from './handlers/get-wishlist'
+import addItem from './handlers/add-item'
+import removeItem from './handlers/remove-item'
+
+export type { Wishlist, WishlistItem }
+
+export type ItemBody = {
+ productId: number
+ variantId: number
+}
+
+export type AddItemBody = { item: ItemBody }
+
+export type RemoveItemBody = { itemId: string }
+
+export type WishlistBody = {
+ customer_id: number
+ is_public: number
+ name: string
+ items: any[]
+}
+
+export type AddWishlistBody = { wishlist: WishlistBody }
+
+export type WishlistHandlers = {
+ getWishlist: BigcommerceHandler<
+ Wishlist,
+ { customerToken?: string; includeProducts?: boolean }
+ >
+ addItem: BigcommerceHandler<
+ Wishlist,
+ { customerToken?: string } & Partial
+ >
+ removeItem: BigcommerceHandler<
+ Wishlist,
+ { customerToken?: string } & Partial
+ >
+}
+
+const METHODS = ['GET', 'POST', 'DELETE']
+
+// TODO: a complete implementation should have schema validation for `req.body`
+const wishlistApi: BigcommerceApiHandler = async (
+ req,
+ res,
+ config,
+ handlers
+) => {
+ if (!isAllowedMethod(req, res, METHODS)) return
+
+ const { cookies } = req
+ const customerToken = cookies[config.customerCookie]
+
+ try {
+ // Return current wishlist info
+ if (req.method === 'GET') {
+ const body = {
+ customerToken,
+ includeProducts: req.query.products === '1',
+ }
+ return await handlers['getWishlist']({ req, res, config, body })
+ }
+
+ // Add an item to the wishlist
+ if (req.method === 'POST') {
+ const body = { ...req.body, customerToken }
+ return await handlers['addItem']({ req, res, config, body })
+ }
+
+ // Remove an item from the wishlist
+ if (req.method === 'DELETE') {
+ const body = { ...req.body, customerToken }
+ return await handlers['removeItem']({ req, res, config, body })
+ }
+ } catch (error) {
+ console.error(error)
+
+ const message =
+ error instanceof BigcommerceApiError
+ ? 'An unexpected error ocurred with the Bigcommerce API'
+ : 'An unexpected error ocurred'
+
+ res.status(500).json({ data: null, errors: [{ message }] })
+ }
+}
+
+export const handlers = {
+ getWishlist,
+ addItem,
+ removeItem,
+}
+
+export default createApiHandler(wishlistApi, handlers, {})
diff --git a/framework/bigcommerce/cart/use-add-item.tsx b/framework/bigcommerce/cart/use-add-item.tsx
new file mode 100644
index 000000000..ab8e634a2
--- /dev/null
+++ b/framework/bigcommerce/cart/use-add-item.tsx
@@ -0,0 +1,56 @@
+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'
+
+const defaultOpts = {
+ url: '/api/bigcommerce/cart',
+ method: 'POST',
+}
+
+export type AddItemInput = ItemBody
+
+export const fetcher: HookFetcher = (
+ 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',
+ })
+ }
+
+ return fetch({
+ ...defaultOpts,
+ ...options,
+ body: { item },
+ })
+}
+
+export function extendHook(customFetcher: typeof fetcher) {
+ const useAddItem = () => {
+ 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
+}
+
+export default extendHook(fetcher)
diff --git a/framework/bigcommerce/cart/use-cart-actions.tsx b/framework/bigcommerce/cart/use-cart-actions.tsx
new file mode 100644
index 000000000..abb4a998e
--- /dev/null
+++ b/framework/bigcommerce/cart/use-cart-actions.tsx
@@ -0,0 +1,13 @@
+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/bigcommerce/cart/use-cart.tsx b/framework/bigcommerce/cart/use-cart.tsx
new file mode 100644
index 000000000..83743096e
--- /dev/null
+++ b/framework/bigcommerce/cart/use-cart.tsx
@@ -0,0 +1,50 @@
+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'
+
+const defaultOpts = {
+ url: '/api/bigcommerce/cart',
+ method: 'GET',
+}
+
+export type { Cart }
+
+export const fetcher: HookFetcher = (
+ options,
+ { cartId },
+ fetch
+) => {
+ return cartId ? fetch({ ...defaultOpts, ...options }) : null
+}
+
+export function extendHook(
+ customFetcher: typeof fetcher,
+ swrOptions?: SwrOptions
+) {
+ const useCart = () => {
+ const response = useCommerceCart(defaultOpts, [], customFetcher, {
+ 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
+ Object.defineProperty(response, 'isEmpty', {
+ get() {
+ return Object.values(response.data?.line_items ?? {}).every(
+ (items) => !items.length
+ )
+ },
+ set: (x) => x,
+ })
+
+ return response
+ }
+
+ useCart.extend = extendHook
+
+ return useCart
+}
+
+export default extendHook(fetcher)
diff --git a/framework/bigcommerce/cart/use-remove-item.tsx b/framework/bigcommerce/cart/use-remove-item.tsx
new file mode 100644
index 000000000..e38c267af
--- /dev/null
+++ b/framework/bigcommerce/cart/use-remove-item.tsx
@@ -0,0 +1,51 @@
+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'
+
+const defaultOpts = {
+ url: '/api/bigcommerce/cart',
+ method: 'DELETE',
+}
+
+export type RemoveItemInput = {
+ id: string
+}
+
+export const fetcher: HookFetcher = (
+ options,
+ { itemId },
+ fetch
+) => {
+ return fetch({
+ ...defaultOpts,
+ ...options,
+ body: { itemId },
+ })
+}
+
+export function extendHook(customFetcher: typeof fetcher) {
+ const useRemoveItem = (item?: any) => {
+ const { mutate } = useCart()
+ const fn = useCartRemoveItem(
+ defaultOpts,
+ customFetcher
+ )
+
+ return useCallback(
+ async function removeItem(input: RemoveItemInput) {
+ const data = await fn({ itemId: input.id ?? item?.id })
+ await mutate(data, false)
+ return data
+ },
+ [fn, mutate]
+ )
+ }
+
+ useRemoveItem.extend = extendHook
+
+ return useRemoveItem
+}
+
+export default extendHook(fetcher)
diff --git a/framework/bigcommerce/cart/use-update-item.tsx b/framework/bigcommerce/cart/use-update-item.tsx
new file mode 100644
index 000000000..ca5703505
--- /dev/null
+++ b/framework/bigcommerce/cart/use-update-item.tsx
@@ -0,0 +1,70 @@
+import { useCallback } from 'react'
+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 { fetcher as removeFetcher } from './use-remove-item'
+import useCart, { Cart } from './use-cart'
+
+const defaultOpts = {
+ url: '/api/bigcommerce/cart',
+ method: 'PUT',
+}
+
+export type UpdateItemInput = Partial<{ id: string } & ItemBody>
+
+export const fetcher: HookFetcher = (
+ 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)
+ }
+ } else if (item.quantity) {
+ throw new CommerceError({
+ message: 'The item quantity has to be a valid integer',
+ })
+ }
+
+ return fetch({
+ ...defaultOpts,
+ ...options,
+ body: { itemId, item },
+ })
+}
+
+function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) {
+ const useUpdateItem = (item?: any) => {
+ const { mutate } = useCart()
+ const fn = useCartUpdateItem(
+ defaultOpts,
+ customFetcher
+ )
+
+ return useCallback(
+ debounce(async (input: UpdateItemInput) => {
+ 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),
+ [fn, mutate]
+ )
+ }
+
+ useUpdateItem.extend = extendHook
+
+ return useUpdateItem
+}
+
+export default extendHook(fetcher)
diff --git a/framework/bigcommerce/index.tsx b/framework/bigcommerce/index.tsx
new file mode 100644
index 000000000..a4c9fffa5
--- /dev/null
+++ b/framework/bigcommerce/index.tsx
@@ -0,0 +1,61 @@
+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'
+
+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
+
+export type BigcommerceProps = {
+ children?: ReactNode
+ locale: string
+} & BigcommerceConfig
+
+export function CommerceProvider({ children, ...config }: BigcommerceProps) {
+ return (
+
+ {children}
+
+ )
+}
+
+export const useCommerce = () => useCoreCommerce()
diff --git a/framework/bigcommerce/products/use-search.tsx b/framework/bigcommerce/products/use-search.tsx
new file mode 100644
index 000000000..ade0bbca2
--- /dev/null
+++ b/framework/bigcommerce/products/use-search.tsx
@@ -0,0 +1,64 @@
+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'
+
+const defaultOpts = {
+ url: '/api/bigcommerce/catalog/products',
+ method: 'GET',
+}
+
+export type SearchProductsInput = {
+ search?: string
+ categoryId?: number
+ brandId?: number
+ sort?: string
+}
+
+export const fetcher: HookFetcher = (
+ 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(categoryId))
+ 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
+) {
+ 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)
diff --git a/framework/bigcommerce/schema.d.ts b/framework/bigcommerce/schema.d.ts
new file mode 100644
index 000000000..04824e263
--- /dev/null
+++ b/framework/bigcommerce/schema.d.ts
@@ -0,0 +1,2064 @@
+export type Maybe = T | null
+export type Exact = {
+ [K in keyof T]: T[K]
+}
+/** All built-in and custom scalars, mapped to their actual values */
+export type Scalars = {
+ ID: string
+ String: string
+ Boolean: boolean
+ Int: number
+ Float: number
+ DateTime: any
+ /** The `BigDecimal` scalar type represents signed fractional values with arbitrary precision. */
+ BigDecimal: any
+ /** The `Long` scalar type represents non-fractional signed whole numeric values. Long can represent values between -(2^63) and 2^63 - 1. */
+ Long: any
+}
+
+/** Login result */
+export type LoginResult = {
+ __typename?: 'LoginResult'
+ /** The result of a login */
+ result: Scalars['String']
+}
+
+/** Logout result */
+export type LogoutResult = {
+ __typename?: 'LogoutResult'
+ /** The result of a logout */
+ result: Scalars['String']
+}
+
+export type Mutation = {
+ __typename?: 'Mutation'
+ login: LoginResult
+ logout: LogoutResult
+}
+
+export type MutationLoginArgs = {
+ email: Scalars['String']
+ password: Scalars['String']
+}
+
+/** Aggregated */
+export type Aggregated = {
+ __typename?: 'Aggregated'
+ /** Number of available products in stock. This can be 'null' if inventory is not set orif the store's Inventory Settings disable displaying stock levels on the storefront. */
+ availableToSell: Scalars['Long']
+ /** Indicates a threshold low-stock level. This can be 'null' if the inventory warning level is not set or if the store's Inventory Settings disable displaying stock levels on the storefront. */
+ warningLevel: Scalars['Int']
+}
+
+/** Aggregated Product Inventory */
+export type AggregatedInventory = {
+ __typename?: 'AggregatedInventory'
+ /** Number of available products in stock. This can be 'null' if inventory is not set orif the store's Inventory Settings disable displaying stock levels on the storefront. */
+ availableToSell: Scalars['Int']
+ /** Indicates a threshold low-stock level. This can be 'null' if the inventory warning level is not set or if the store's Inventory Settings disable displaying stock levels on the storefront. */
+ warningLevel: Scalars['Int']
+}
+
+/** Brand */
+export type Brand = Node & {
+ __typename?: 'Brand'
+ /** The ID of an object */
+ id: Scalars['ID']
+ /** Id of the brand. */
+ entityId: Scalars['Int']
+ /** Name of the brand. */
+ name: Scalars['String']
+ /** Default image for brand. */
+ defaultImage?: Maybe
+ /** Page title for the brand. */
+ pageTitle: Scalars['String']
+ /** Meta description for the brand. */
+ metaDesc: Scalars['String']
+ /** Meta keywords for the brand. */
+ metaKeywords: Array
+ /** Search keywords for the brand. */
+ searchKeywords: Array
+ /** Path for the brand page. */
+ path: Scalars['String']
+ products: ProductConnection
+ /** Metafield data related to a brand. */
+ metafields: MetafieldConnection
+}
+
+/** Brand */
+export type BrandProductsArgs = {
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+}
+
+/** Brand */
+export type BrandMetafieldsArgs = {
+ namespace: Scalars['String']
+ keys?: Maybe>
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+}
+
+/** A connection to a list of items. */
+export type BrandConnection = {
+ __typename?: 'BrandConnection'
+ /** Information to aid in pagination. */
+ pageInfo: PageInfo
+ /** A list of edges. */
+ edges?: Maybe>>
+}
+
+/** An edge in a connection. */
+export type BrandEdge = {
+ __typename?: 'BrandEdge'
+ /** The item at the end of the edge. */
+ node: Brand
+ /** A cursor for use in pagination. */
+ cursor: Scalars['String']
+}
+
+/** Breadcrumb */
+export type Breadcrumb = {
+ __typename?: 'Breadcrumb'
+ /** Category id. */
+ entityId: Scalars['Int']
+ /** Name of the category. */
+ name: Scalars['String']
+}
+
+/** A connection to a list of items. */
+export type BreadcrumbConnection = {
+ __typename?: 'BreadcrumbConnection'
+ /** Information to aid in pagination. */
+ pageInfo: PageInfo
+ /** A list of edges. */
+ edges?: Maybe>>
+}
+
+/** An edge in a connection. */
+export type BreadcrumbEdge = {
+ __typename?: 'BreadcrumbEdge'
+ /** The item at the end of the edge. */
+ node: Breadcrumb
+ /** A cursor for use in pagination. */
+ cursor: Scalars['String']
+}
+
+/** Bulk pricing tier that sets a fixed price for the product or variant. */
+export type BulkPricingFixedPriceDiscount = BulkPricingTier & {
+ __typename?: 'BulkPricingFixedPriceDiscount'
+ /** This price will override the current product price. */
+ price: Scalars['BigDecimal']
+ /** Minimum item quantity that applies to this bulk pricing tier. */
+ minimumQuantity: Scalars['Int']
+ /** Maximum item quantity that applies to this bulk pricing tier - if not defined then the tier does not have an upper bound. */
+ maximumQuantity?: Maybe
+}
+
+/** Bulk pricing tier that reduces the price of the product or variant by a percentage. */
+export type BulkPricingPercentageDiscount = BulkPricingTier & {
+ __typename?: 'BulkPricingPercentageDiscount'
+ /** The percentage that will be removed from the product price. */
+ percentOff: Scalars['BigDecimal']
+ /** Minimum item quantity that applies to this bulk pricing tier. */
+ minimumQuantity: Scalars['Int']
+ /** Maximum item quantity that applies to this bulk pricing tier - if not defined then the tier does not have an upper bound. */
+ maximumQuantity?: Maybe
+}
+
+/** Bulk pricing tier that will subtract an amount from the price of the product or variant. */
+export type BulkPricingRelativePriceDiscount = BulkPricingTier & {
+ __typename?: 'BulkPricingRelativePriceDiscount'
+ /** The price of the product/variant will be reduced by this priceAdjustment. */
+ priceAdjustment: Scalars['BigDecimal']
+ /** Minimum item quantity that applies to this bulk pricing tier. */
+ minimumQuantity: Scalars['Int']
+ /** Maximum item quantity that applies to this bulk pricing tier - if not defined then the tier does not have an upper bound. */
+ maximumQuantity?: Maybe
+}
+
+/** A set of bulk pricing tiers that define price discounts which apply when purchasing specified quantities of a product or variant. */
+export type BulkPricingTier = {
+ /** Minimum item quantity that applies to this bulk pricing tier. */
+ minimumQuantity: Scalars['Int']
+ /** Maximum item quantity that applies to this bulk pricing tier - if not defined then the tier does not have an upper bound. */
+ maximumQuantity?: Maybe
+}
+
+/** Product Option */
+export type CatalogProductOption = {
+ /** Unique ID for the option. */
+ entityId: Scalars['Int']
+ /** Display name for the option. */
+ displayName: Scalars['String']
+ /** One of the option values is required to be selected for the checkout. */
+ isRequired: Scalars['Boolean']
+}
+
+/** Product Option Value */
+export type CatalogProductOptionValue = {
+ /** Unique ID for the option value. */
+ entityId: Scalars['Int']
+ /** Label for the option value. */
+ label: Scalars['String']
+ /** Indicates whether this value is the chosen default selected value. */
+ isDefault: Scalars['Boolean']
+}
+
+/** Category */
+export type Category = Node & {
+ __typename?: 'Category'
+ /** The ID of an object */
+ id: Scalars['ID']
+ /** Unique ID for the category. */
+ entityId: Scalars['Int']
+ /** Category name. */
+ name: Scalars['String']
+ /** Category path. */
+ path: Scalars['String']
+ /** Default image for the category. */
+ defaultImage?: Maybe
+ /** Category description. */
+ description: Scalars['String']
+ /** Category breadcrumbs. */
+ breadcrumbs: BreadcrumbConnection
+ products: ProductConnection
+ /** Metafield data related to a category. */
+ metafields: MetafieldConnection
+}
+
+/** Category */
+export type CategoryBreadcrumbsArgs = {
+ depth: Scalars['Int']
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+}
+
+/** Category */
+export type CategoryProductsArgs = {
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+}
+
+/** Category */
+export type CategoryMetafieldsArgs = {
+ namespace: Scalars['String']
+ keys?: Maybe>
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+}
+
+/** A connection to a list of items. */
+export type CategoryConnection = {
+ __typename?: 'CategoryConnection'
+ /** Information to aid in pagination. */
+ pageInfo: PageInfo
+ /** A list of edges. */
+ edges?: Maybe>>
+}
+
+/** An edge in a connection. */
+export type CategoryEdge = {
+ __typename?: 'CategoryEdge'
+ /** The item at the end of the edge. */
+ node: Category
+ /** A cursor for use in pagination. */
+ cursor: Scalars['String']
+}
+
+/** An item in a tree of categories. */
+export type CategoryTreeItem = {
+ __typename?: 'CategoryTreeItem'
+ /** The id category. */
+ entityId: Scalars['Int']
+ /** The name of category. */
+ name: Scalars['String']
+ /** Path assigned to this category */
+ path: Scalars['String']
+ /** The description of this category. */
+ description: Scalars['String']
+ /** The number of products in this category. */
+ productCount: Scalars['Int']
+ /** Subcategories of this category */
+ children: Array
+}
+
+/** A simple yes/no question represented by a checkbox. */
+export type CheckboxOption = CatalogProductOption & {
+ __typename?: 'CheckboxOption'
+ /** Indicates the default checked status. */
+ checkedByDefault: Scalars['Boolean']
+ /** Unique ID for the option. */
+ entityId: Scalars['Int']
+ /** Display name for the option. */
+ displayName: Scalars['String']
+ /** One of the option values is required to be selected for the checkout. */
+ isRequired: Scalars['Boolean']
+}
+
+/** Contact field */
+export type ContactField = {
+ __typename?: 'ContactField'
+ /** Store address line. */
+ address: Scalars['String']
+ /** Store country. */
+ country: Scalars['String']
+ /** Store address type. */
+ addressType: Scalars['String']
+ /** Store email. */
+ email: Scalars['String']
+ /** Store phone number. */
+ phone: Scalars['String']
+}
+
+/** Custom field */
+export type CustomField = {
+ __typename?: 'CustomField'
+ /** Custom field id. */
+ entityId: Scalars['Int']
+ /** Name of the custom field. */
+ name: Scalars['String']
+ /** Value of the custom field. */
+ value: Scalars['String']
+}
+
+/** A connection to a list of items. */
+export type CustomFieldConnection = {
+ __typename?: 'CustomFieldConnection'
+ /** Information to aid in pagination. */
+ pageInfo: PageInfo
+ /** A list of edges. */
+ edges?: Maybe>>
+}
+
+/** An edge in a connection. */
+export type CustomFieldEdge = {
+ __typename?: 'CustomFieldEdge'
+ /** The item at the end of the edge. */
+ node: CustomField
+ /** A cursor for use in pagination. */
+ cursor: Scalars['String']
+}
+
+/** A customer that shops on a store */
+export type Customer = {
+ __typename?: 'Customer'
+ /** The ID of the customer. */
+ entityId: Scalars['Int']
+ /** The company name of the customer. */
+ company: Scalars['String']
+ /** The customer group id of the customer. */
+ customerGroupId: Scalars['Int']
+ /** The email address of the customer. */
+ email: Scalars['String']
+ /** The first name of the customer. */
+ firstName: Scalars['String']
+ /** The last name of the customer. */
+ lastName: Scalars['String']
+ /** The notes of the customer. */
+ notes: Scalars['String']
+ /** The phone number of the customer. */
+ phone: Scalars['String']
+ /** The tax exempt category of the customer. */
+ taxExemptCategory: Scalars['String']
+ /** Customer addresses count. */
+ addressCount: Scalars['Int']
+ /** Customer attributes count. */
+ attributeCount: Scalars['Int']
+ /** Customer store credit. */
+ storeCredit: Array
+ /** Customer attributes. */
+ attributes: CustomerAttributes
+}
+
+/** A custom, store-specific attribute for a customer */
+export type CustomerAttribute = {
+ __typename?: 'CustomerAttribute'
+ /** The ID of the custom customer attribute */
+ entityId: Scalars['Int']
+ /** The value of the custom customer attribute */
+ value?: Maybe
+ /** The name of the custom customer attribute */
+ name: Scalars['String']
+}
+
+/** Custom, store-specific customer attributes */
+export type CustomerAttributes = {
+ __typename?: 'CustomerAttributes'
+ attribute: CustomerAttribute
+}
+
+/** Custom, store-specific customer attributes */
+export type CustomerAttributesAttributeArgs = {
+ entityId: Scalars['Int']
+}
+
+/** A calendar for allowing selection of a date. */
+export type DateFieldOption = CatalogProductOption & {
+ __typename?: 'DateFieldOption'
+ /** Unique ID for the option. */
+ entityId: Scalars['Int']
+ /** Display name for the option. */
+ displayName: Scalars['String']
+ /** One of the option values is required to be selected for the checkout. */
+ isRequired: Scalars['Boolean']
+}
+
+/** Date Time Extended */
+export type DateTimeExtended = {
+ __typename?: 'DateTimeExtended'
+ /** ISO-8601 formatted date in UTC */
+ utc: Scalars['DateTime']
+}
+
+/** Display field */
+export type DisplayField = {
+ __typename?: 'DisplayField'
+ /** Short date format. */
+ shortDateFormat: Scalars['String']
+ /** Extended date format. */
+ extendedDateFormat: Scalars['String']
+}
+
+/** A form allowing selection and uploading of a file from the user's local computer. */
+export type FileUploadFieldOption = CatalogProductOption & {
+ __typename?: 'FileUploadFieldOption'
+ /** Unique ID for the option. */
+ entityId: Scalars['Int']
+ /** Display name for the option. */
+ displayName: Scalars['String']
+ /** One of the option values is required to be selected for the checkout. */
+ isRequired: Scalars['Boolean']
+}
+
+/** Image */
+export type Image = {
+ __typename?: 'Image'
+ /** Absolute path to image using store CDN. */
+ url: Scalars['String']
+ /** Absolute path to original image using store CDN. */
+ urlOriginal: Scalars['String']
+ /** Text description of an image that can be used for SEO and/or accessibility purposes. */
+ altText: Scalars['String']
+ /** Indicates whether this is the primary image. */
+ isDefault: Scalars['Boolean']
+}
+
+/** Image */
+export type ImageUrlArgs = {
+ width: Scalars['Int']
+ height?: Maybe
+}
+
+/** A connection to a list of items. */
+export type ImageConnection = {
+ __typename?: 'ImageConnection'
+ /** Information to aid in pagination. */
+ pageInfo: PageInfo
+ /** A list of edges. */
+ edges?: Maybe>>
+}
+
+/** An edge in a connection. */
+export type ImageEdge = {
+ __typename?: 'ImageEdge'
+ /** The item at the end of the edge. */
+ node: Image
+ /** A cursor for use in pagination. */
+ cursor: Scalars['String']
+}
+
+/** An inventory */
+export type Inventory = {
+ __typename?: 'Inventory'
+ /** Locations */
+ locations: LocationConnection
+}
+
+/** An inventory */
+export type InventoryLocationsArgs = {
+ entityIds?: Maybe>
+ codes?: Maybe>
+ typeIds?: Maybe>
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+}
+
+/** Inventory By Locations */
+export type InventoryByLocations = {
+ __typename?: 'InventoryByLocations'
+ /** Location id. */
+ locationEntityId: Scalars['Long']
+ /** Number of available products in stock. */
+ availableToSell: Scalars['Long']
+ /** Indicates a threshold low-stock level. */
+ warningLevel: Scalars['Int']
+ /** Indicates whether this product is in stock. */
+ isInStock: Scalars['Boolean']
+}
+
+/** A connection to a list of items. */
+export type LocationConnection = {
+ __typename?: 'LocationConnection'
+ /** Information to aid in pagination. */
+ pageInfo: PageInfo
+ /** A list of edges. */
+ edges?: Maybe>>
+}
+
+/** An edge in a connection. */
+export type LocationEdge = {
+ __typename?: 'LocationEdge'
+ /** The item at the end of the edge. */
+ node: InventoryByLocations
+ /** A cursor for use in pagination. */
+ cursor: Scalars['String']
+}
+
+/** Logo field */
+export type LogoField = {
+ __typename?: 'LogoField'
+ /** Logo title. */
+ title: Scalars['String']
+ /** Store logo image. */
+ image: Image
+}
+
+/** Measurement */
+export type Measurement = {
+ __typename?: 'Measurement'
+ /** Unformatted weight measurement value. */
+ value: Scalars['Float']
+ /** Unit of measurement. */
+ unit: Scalars['String']
+}
+
+/** A connection to a list of items. */
+export type MetafieldConnection = {
+ __typename?: 'MetafieldConnection'
+ /** Information to aid in pagination. */
+ pageInfo: PageInfo
+ /** A list of edges. */
+ edges?: Maybe>>
+}
+
+/** An edge in a connection. */
+export type MetafieldEdge = {
+ __typename?: 'MetafieldEdge'
+ /** The item at the end of the edge. */
+ node: Metafields
+ /** A cursor for use in pagination. */
+ cursor: Scalars['String']
+}
+
+/** Key/Value pairs of data attached tied to a resource entity (product, brand, category, etc.) */
+export type Metafields = {
+ __typename?: 'Metafields'
+ /** The ID of an object */
+ id: Scalars['ID']
+ /** The ID of the metafield when referencing via our backend API. */
+ entityId: Scalars['Int']
+ /** A label for identifying a metafield data value. */
+ key: Scalars['String']
+ /** A metafield value. */
+ value: Scalars['String']
+}
+
+/** A money object - includes currency code and a money amount */
+export type Money = {
+ __typename?: 'Money'
+ /** Currency code of the current money. */
+ currencyCode: Scalars['String']
+ /** The amount of money. */
+ value: Scalars['BigDecimal']
+}
+
+/** A min and max pair of money objects */
+export type MoneyRange = {
+ __typename?: 'MoneyRange'
+ /** Minimum money object. */
+ min: Money
+ /** Maximum money object. */
+ max: Money
+}
+
+/** A multi-line text input field, aka a text box. */
+export type MultiLineTextFieldOption = CatalogProductOption & {
+ __typename?: 'MultiLineTextFieldOption'
+ /** Unique ID for the option. */
+ entityId: Scalars['Int']
+ /** Display name for the option. */
+ displayName: Scalars['String']
+ /** One of the option values is required to be selected for the checkout. */
+ isRequired: Scalars['Boolean']
+}
+
+/** An option type that has a fixed list of values. */
+export type MultipleChoiceOption = CatalogProductOption & {
+ __typename?: 'MultipleChoiceOption'
+ /** The chosen display style for this multiple choice option. */
+ displayStyle: Scalars['String']
+ /** List of option values. */
+ values: ProductOptionValueConnection
+ /** Unique ID for the option. */
+ entityId: Scalars['Int']
+ /** Display name for the option. */
+ displayName: Scalars['String']
+ /** One of the option values is required to be selected for the checkout. */
+ isRequired: Scalars['Boolean']
+}
+
+/** An option type that has a fixed list of values. */
+export type MultipleChoiceOptionValuesArgs = {
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+}
+
+/** A simple multiple choice value comprised of an id and a label. */
+export type MultipleChoiceOptionValue = CatalogProductOptionValue & {
+ __typename?: 'MultipleChoiceOptionValue'
+ /** Unique ID for the option value. */
+ entityId: Scalars['Int']
+ /** Label for the option value. */
+ label: Scalars['String']
+ /** Indicates whether this value is the chosen default selected value. */
+ isDefault: Scalars['Boolean']
+}
+
+/** An object with an ID */
+export type Node = {
+ /** The id of the object. */
+ id: Scalars['ID']
+}
+
+/** A single line text input field that only accepts numbers. */
+export type NumberFieldOption = CatalogProductOption & {
+ __typename?: 'NumberFieldOption'
+ /** Unique ID for the option. */
+ entityId: Scalars['Int']
+ /** Display name for the option. */
+ displayName: Scalars['String']
+ /** One of the option values is required to be selected for the checkout. */
+ isRequired: Scalars['Boolean']
+}
+
+/** A connection to a list of items. */
+export type OptionConnection = {
+ __typename?: 'OptionConnection'
+ /** Information to aid in pagination. */
+ pageInfo: PageInfo
+ /** A list of edges. */
+ edges?: Maybe>>
+}
+
+/** An edge in a connection. */
+export type OptionEdge = {
+ __typename?: 'OptionEdge'
+ /** The item at the end of the edge. */
+ node: ProductOption
+ /** A cursor for use in pagination. */
+ cursor: Scalars['String']
+}
+
+/** A connection to a list of items. */
+export type OptionValueConnection = {
+ __typename?: 'OptionValueConnection'
+ /** Information to aid in pagination. */
+ pageInfo: PageInfo
+ /** A list of edges. */
+ edges?: Maybe>>
+}
+
+/** An edge in a connection. */
+export type OptionValueEdge = {
+ __typename?: 'OptionValueEdge'
+ /** The item at the end of the edge. */
+ node: ProductOptionValue
+ /** A cursor for use in pagination. */
+ cursor: Scalars['String']
+}
+
+export type OptionValueId = {
+ optionEntityId: Scalars['Int']
+ valueEntityId: Scalars['Int']
+}
+
+/** Information about pagination in a connection. */
+export type PageInfo = {
+ __typename?: 'PageInfo'
+ /** When paginating forwards, are there more items? */
+ hasNextPage: Scalars['Boolean']
+ /** When paginating backwards, are there more items? */
+ hasPreviousPage: Scalars['Boolean']
+ /** When paginating backwards, the cursor to continue. */
+ startCursor?: Maybe
+ /** When paginating forwards, the cursor to continue. */
+ endCursor?: Maybe
+}
+
+/** The min and max range of prices that apply to this product. */
+export type PriceRanges = {
+ __typename?: 'PriceRanges'
+ /** Product price min/max range. */
+ priceRange: MoneyRange
+ /** Product retail price min/max range. */
+ retailPriceRange?: Maybe
+}
+
+/** The various prices that can be set on a product. */
+export type Prices = {
+ __typename?: 'Prices'
+ /** Calculated price of the product. */
+ price: Money
+ /** Sale price of the product. */
+ salePrice?: Maybe
+ /** Original price of the product. */
+ basePrice?: Maybe
+ /** Retail price of the product. */
+ retailPrice?: Maybe
+ /** Minimum advertised price of the product. */
+ mapPrice?: Maybe
+ /** Product price min/max range. */
+ priceRange: MoneyRange
+ /** Product retail price min/max range. */
+ retailPriceRange?: Maybe
+ /** The difference between the retail price (MSRP) and the current price, which can be presented to the shopper as their savings. */
+ saved?: Maybe
+ /** List of bulk pricing tiers applicable to a product or variant. */
+ bulkPricing: Array
+}
+
+/** Product */
+export type Product = Node & {
+ __typename?: 'Product'
+ /** The ID of an object */
+ id: Scalars['ID']
+ /** Id of the product. */
+ entityId: Scalars['Int']
+ /** Default product variant when no options are selected. */
+ sku: Scalars['String']
+ /** Relative URL path to product page. */
+ path: Scalars['String']
+ /** Name of the product. */
+ name: Scalars['String']
+ /** Description of the product. */
+ description: Scalars['String']
+ /** Description of the product in plain text. */
+ plainTextDescription: Scalars['String']
+ /** Warranty information of the product. */
+ warranty: Scalars['String']
+ /** Minimum purchasable quantity for this product in a single order. */
+ minPurchaseQuantity?: Maybe
+ /** Maximum purchasable quantity for this product in a single order. */
+ maxPurchaseQuantity?: Maybe
+ /** Absolute URL path for adding a product to cart. */
+ addToCartUrl: Scalars['String']
+ /** Absolute URL path for adding a product to customer's wishlist. */
+ addToWishlistUrl: Scalars['String']
+ /** Prices object determined by supplied product ID, variant ID, and selected option IDs. */
+ prices?: Maybe
+ /**
+ * The minimum and maximum price of this product based on variant pricing and/or modifier price rules.
+ * @deprecated Use priceRanges inside prices node instead.
+ */
+ priceRanges?: Maybe
+ /** Weight of the product. */
+ weight?: Maybe
+ /** Height of the product. */
+ height?: Maybe
+ /** Width of the product. */
+ width?: Maybe
+ /** Depth of the product. */
+ depth?: Maybe
+ /** Product options. */
+ options: OptionConnection
+ /** Product options. */
+ productOptions: ProductOptionConnection
+ /** Summary of the product reviews, includes the total number of reviews submitted and summation of the ratings on the reviews (ratings range from 0-5 per review). */
+ reviewSummary: Reviews
+ /** Type of product, ex: physical, digital */
+ type: Scalars['String']
+ /**
+ * The availability state of the product.
+ * @deprecated Use status inside availabilityV2 instead.
+ */
+ availability: Scalars['String']
+ /**
+ * A few words telling the customer how long it will normally take to ship this product, such as 'Usually ships in 24 hours'.
+ * @deprecated Use description inside availabilityV2 instead.
+ */
+ availabilityDescription: Scalars['String']
+ /** The availability state of the product. */
+ availabilityV2: ProductAvailability
+ /** List of categories associated with the product. */
+ categories: CategoryConnection
+ /** Brand associated with the product. */
+ brand?: Maybe
+ /** Variants associated with the product. */
+ variants: VariantConnection
+ /** Custom fields of the product. */
+ customFields: CustomFieldConnection
+ /** A list of the images for a product. */
+ images: ImageConnection
+ /** Default image for a product. */
+ defaultImage?: Maybe
+ /** Related products for this product. */
+ relatedProducts: RelatedProductsConnection
+ /** Inventory information of the product. */
+ inventory: ProductInventory
+ /** Metafield data related to a product. */
+ metafields: MetafieldConnection
+ /**
+ * Product creation date
+ * @deprecated Alpha version. Do not use in production.
+ */
+ createdAt: DateTimeExtended
+}
+
+/** Product */
+export type ProductPlainTextDescriptionArgs = {
+ characterLimit?: Maybe
+}
+
+/** Product */
+export type ProductPricesArgs = {
+ includeTax?: Maybe
+ currencyCode?: Maybe
+}
+
+/** Product */
+export type ProductPriceRangesArgs = {
+ includeTax?: Maybe
+}
+
+/** Product */
+export type ProductOptionsArgs = {
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+}
+
+/** Product */
+export type ProductProductOptionsArgs = {
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+}
+
+/** Product */
+export type ProductCategoriesArgs = {
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+}
+
+/** Product */
+export type ProductVariantsArgs = {
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+ entityIds?: Maybe>
+ optionValueIds?: Maybe>
+}
+
+/** Product */
+export type ProductCustomFieldsArgs = {
+ names?: Maybe>
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+}
+
+/** Product */
+export type ProductImagesArgs = {
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+}
+
+/** Product */
+export type ProductRelatedProductsArgs = {
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+}
+
+/** Product */
+export type ProductMetafieldsArgs = {
+ namespace: Scalars['String']
+ keys?: Maybe>
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+}
+
+/** Product availability */
+export type ProductAvailability = {
+ /** The availability state of the product. */
+ status: ProductAvailabilityStatus
+ /** A few words telling the customer how long it will normally take to ship this product, such as 'Usually ships in 24 hours'. */
+ description: Scalars['String']
+}
+
+/** Product availability status */
+export enum ProductAvailabilityStatus {
+ Available = 'Available',
+ Preorder = 'Preorder',
+ Unavailable = 'Unavailable',
+}
+
+/** Available Product */
+export type ProductAvailable = ProductAvailability & {
+ __typename?: 'ProductAvailable'
+ /** The availability state of the product. */
+ status: ProductAvailabilityStatus
+ /** A few words telling the customer how long it will normally take to ship this product, such as 'Usually ships in 24 hours'. */
+ description: Scalars['String']
+}
+
+/** A connection to a list of items. */
+export type ProductConnection = {
+ __typename?: 'ProductConnection'
+ /** Information to aid in pagination. */
+ pageInfo: PageInfo
+ /** A list of edges. */
+ edges?: Maybe>>
+}
+
+/** An edge in a connection. */
+export type ProductEdge = {
+ __typename?: 'ProductEdge'
+ /** The item at the end of the edge. */
+ node: Product
+ /** A cursor for use in pagination. */
+ cursor: Scalars['String']
+}
+
+/** Product Inventory Information */
+export type ProductInventory = {
+ __typename?: 'ProductInventory'
+ /** Indicates whether this product is in stock. */
+ isInStock: Scalars['Boolean']
+ /** Indicates whether this product's inventory is being tracked on variant level. If true, you may wish to check the variants node to understand the true inventory of each individual variant, rather than relying on this product-level aggregate to understand how many items may be added to cart. */
+ hasVariantInventory: Scalars['Boolean']
+ /** Aggregated product inventory information. This data may not be available if not set or if the store's Inventory Settings have disabled displaying stock levels on the storefront. */
+ aggregated?: Maybe
+}
+
+/** Product Option */
+export type ProductOption = {
+ __typename?: 'ProductOption'
+ /** Unique ID for the option. */
+ entityId: Scalars['Int']
+ /** Display name for the option. */
+ displayName: Scalars['String']
+ /** One of the option values is required to be selected for the checkout. */
+ isRequired: Scalars['Boolean']
+ /** Option values. */
+ values: OptionValueConnection
+}
+
+/** Product Option */
+export type ProductOptionValuesArgs = {
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+}
+
+/** A connection to a list of items. */
+export type ProductOptionConnection = {
+ __typename?: 'ProductOptionConnection'
+ /** Information to aid in pagination. */
+ pageInfo: PageInfo
+ /** A list of edges. */
+ edges?: Maybe>>
+}
+
+/** An edge in a connection. */
+export type ProductOptionEdge = {
+ __typename?: 'ProductOptionEdge'
+ /** The item at the end of the edge. */
+ node: CatalogProductOption
+ /** A cursor for use in pagination. */
+ cursor: Scalars['String']
+}
+
+/** Product Option Value */
+export type ProductOptionValue = {
+ __typename?: 'ProductOptionValue'
+ /** Unique ID for the option value. */
+ entityId: Scalars['Int']
+ /** Label for the option value. */
+ label: Scalars['String']
+}
+
+/** A connection to a list of items. */
+export type ProductOptionValueConnection = {
+ __typename?: 'ProductOptionValueConnection'
+ /** Information to aid in pagination. */
+ pageInfo: PageInfo
+ /** A list of edges. */
+ edges?: Maybe>>
+}
+
+/** An edge in a connection. */
+export type ProductOptionValueEdge = {
+ __typename?: 'ProductOptionValueEdge'
+ /** The item at the end of the edge. */
+ node: CatalogProductOptionValue
+ /** A cursor for use in pagination. */
+ cursor: Scalars['String']
+}
+
+/** A Product PickList Value - a product to be mapped to the base product if selected. */
+export type ProductPickListOptionValue = CatalogProductOptionValue & {
+ __typename?: 'ProductPickListOptionValue'
+ /** The ID of the product associated with this option value. */
+ productId: Scalars['Int']
+ /** Unique ID for the option value. */
+ entityId: Scalars['Int']
+ /** Label for the option value. */
+ label: Scalars['String']
+ /** Indicates whether this value is the chosen default selected value. */
+ isDefault: Scalars['Boolean']
+}
+
+/** PreOrder Product */
+export type ProductPreOrder = ProductAvailability & {
+ __typename?: 'ProductPreOrder'
+ /** The message to be shown in the store when a product is put into the pre-order availability state, e.g. "Expected release date is %%DATE%%" */
+ message?: Maybe
+ /** Product release date */
+ willBeReleasedAt?: Maybe
+ /** The availability state of the product. */
+ status: ProductAvailabilityStatus
+ /** A few words telling the customer how long it will normally take to ship this product, such as 'Usually ships in 24 hours'. */
+ description: Scalars['String']
+}
+
+/** Unavailable Product */
+export type ProductUnavailable = ProductAvailability & {
+ __typename?: 'ProductUnavailable'
+ /** The message to be shown in the store when "Call for pricing" is enabled for this product, e.g. "Contact us at 555-5555" */
+ message?: Maybe
+ /** The availability state of the product. */
+ status: ProductAvailabilityStatus
+ /** A few words telling the customer how long it will normally take to ship this product, such as 'Usually ships in 24 hours'. */
+ description: Scalars['String']
+}
+
+export type Query = {
+ __typename?: 'Query'
+ site: Site
+ /** The currently logged in customer. */
+ customer?: Maybe
+ /** Fetches an object given its ID */
+ node?: Maybe
+ /** @deprecated Alpha version. Do not use in production. */
+ inventory: Inventory
+}
+
+export type QueryNodeArgs = {
+ id: Scalars['ID']
+}
+
+/** A connection to a list of items. */
+export type RelatedProductsConnection = {
+ __typename?: 'RelatedProductsConnection'
+ /** Information to aid in pagination. */
+ pageInfo: PageInfo
+ /** A list of edges. */
+ edges?: Maybe>>
+}
+
+/** An edge in a connection. */
+export type RelatedProductsEdge = {
+ __typename?: 'RelatedProductsEdge'
+ /** The item at the end of the edge. */
+ node: Product
+ /** A cursor for use in pagination. */
+ cursor: Scalars['String']
+}
+
+/** Review Rating Summary */
+export type Reviews = {
+ __typename?: 'Reviews'
+ /** Total number of reviews on product. */
+ numberOfReviews: Scalars['Int']
+ /** Summation of rating scores from each review. */
+ summationOfRatings: Scalars['Int']
+}
+
+/** route */
+export type Route = {
+ __typename?: 'Route'
+ /** node */
+ node?: Maybe
+}
+
+/** Store settings information from the control panel. */
+export type Settings = {
+ __typename?: 'Settings'
+ /** The name of the store. */
+ storeName: Scalars['String']
+ /** The hash of the store. */
+ storeHash: Scalars['String']
+ /** The current store status. */
+ status: StorefrontStatusType
+ /** Logo information for the store. */
+ logo: LogoField
+ /** Contact information for the store. */
+ contact?: Maybe
+ /** Store urls. */
+ url: UrlField
+ /** Store display format information. */
+ display: DisplayField
+ /** Channel ID. */
+ channelId: Scalars['Long']
+}
+
+/** A site */
+export type Site = {
+ __typename?: 'Site'
+ categoryTree: Array
+ /** Details of the brand. */
+ brands: BrandConnection
+ /** Details of the products. */
+ products: ProductConnection
+ /** Details of the newest products. */
+ newestProducts: ProductConnection
+ /** Details of the best selling products. */
+ bestSellingProducts: ProductConnection
+ /** Details of the featured products. */
+ featuredProducts: ProductConnection
+ /** A single product object with variant pricing overlay capabilities. */
+ product?: Maybe
+ /** Route for a node */
+ route: Route
+ /** Store settings. */
+ settings?: Maybe
+}
+
+/** A site */
+export type SiteBrandsArgs = {
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+ productEntityIds?: Maybe>
+}
+
+/** A site */
+export type SiteProductsArgs = {
+ before?: Maybe
+ after?: Maybe
+ first?: Maybe
+ last?: Maybe
+ ids?: Maybe>
+ entityIds?: Maybe