4
0
forked from crowetic/commerce

Update types (#831)

* Update product types

* Cart types progress, add zod & initial schema validator

* Update normalize.ts

* Update with-schema-parser.ts

* Updated types, schemas & providers

* Fix providers after schema parse errors

* Fix paths

* More provider fixes

* Fix kibocommerce & commercejs

* Add customer updated types & fixes

* Add checkout & customer types

* Import core types only from commerce

* Update tsconfig.json

* Convert hooks interfaces to types

* Requested changes

* Change to relative paths

* Move Zod dependency
This commit is contained in:
Catalin Pinte 2022-10-05 09:02:29 +03:00 committed by GitHub
parent 8398a96215
commit 6c2610584d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
291 changed files with 1992 additions and 1808 deletions

View File

@ -23,8 +23,8 @@ Demo live at: [demo.vercel.store](https://demo.vercel.store/)
> To run a minimal version of Next.js Commerce you can start with the default local provider `@vercel/commerce-local` that has disabled all features (cart, auth) and use static files for the backend
```bash
pnpm install # run this command in root folder of the mono repo
pnpm dev
pnpm install & pnpm build # run this commands in root folder of the mono repo
pnpm dev # run this commands in the site folder
```
> If you encounter any problems while installing and running for the first time, please see the Troubleshoot section
@ -111,10 +111,11 @@ Our commitment to Open Source can be found [here](https://vercel.com/oss).
1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device.
2. Create a new branch `git checkout -b MY_BRANCH_NAME`
3. Install the dependencies: `pnpm install`
4. Duplicate `site/.env.template` and rename it to `site/.env.local`
5. Add proper store values to `site/.env.local`
6. Run `pnpm dev` to build the packages and watch for code changes
7. Run `pnpm turbo run build` to check the build after your changes
4. Build the packages: `pnpm build`
5. Duplicate `site/.env.template` and rename it to `site/.env.local`
6. Add proper store values to `site/.env.local`
7. Run `cd site` & `pnpm dev` to watch for code changes
8. Run `pnpm turbo run build` to check the build after your changes
## Work in progress
@ -191,7 +192,7 @@ info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this comm
The error usually occurs when running `pnpm dev` inside of the `/site/` folder after installing a fresh repository.
In order to fix this, run `pnpm dev` in the monorepo root folder first.
In order to fix this, run `pnpm build` in the monorepo root folder first.
> Using `pnpm dev` from the root is recommended for developing, which will run watch mode on all packages.

View File

@ -1130,7 +1130,7 @@ export interface definitions {
*/
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 used for this category on the storefront. Images can be uploaded via form file post to `/{brandId}/image`, or by providing a publicly accessible URL in this field.
*/
image_url?: string
custom_url?: definitions['customUrl_Full']

View File

@ -2,7 +2,7 @@
import { normalizeCart } from '../../../lib/normalize'
import { BigcommerceApiError } from '../../utils/errors'
import getCartCookie from '../../utils/get-cart-cookie'
import type { BigcommerceCart } from '../../../types/cart'
import type { BigcommerceCart } from '../../../types'
import type { CartEndpoint } from '.'
// Return current cart info

View File

@ -1,6 +1,6 @@
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
import cartEndpoint from '@vercel/commerce/api/endpoints/cart'
import type { CartSchema } from '../../../types/cart'
import type { CartSchema } from '@vercel/commerce/types/cart'
import type { BigcommerceAPI } from '../..'
import getCart from './get-cart'
import addItem from './add-item'

View File

@ -1,6 +1,6 @@
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
import productsEndpoint from '@vercel/commerce/api/endpoints/catalog/products'
import type { ProductsSchema } from '../../../../types/product'
import type { ProductsSchema } from '@vercel/commerce/types/product'
import type { BigcommerceAPI } from '../../..'
import getProducts from './get-products'

View File

@ -1,6 +1,6 @@
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
import checkoutEndpoint from '@vercel/commerce/api/endpoints/checkout'
import type { CheckoutSchema } from '../../../types/checkout'
import type { CheckoutSchema } from '@vercel/commerce/types/checkout'
import type { BigcommerceAPI } from '../..'
import getCheckout from './get-checkout'

View File

@ -47,7 +47,19 @@ const getLoggedInCustomer: CustomerEndpoint['handlers']['getLoggedInCustomer'] =
})
}
return res.status(200).json({ data: { customer } })
return res.status(200).json({
data: {
customer: {
id: String(customer.entityId),
firstName: customer.firstName,
lastName: customer.lastName,
email: customer.email,
company: customer.company,
phone: customer.phone,
notes: customer.notes,
},
},
})
}
res.status(200).json({ data: null })

View File

@ -1,6 +1,6 @@
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
import customerEndpoint from '@vercel/commerce/api/endpoints/customer'
import type { CustomerSchema } from '../../../types/customer'
import type { CustomerSchema } from '@vercel/commerce/types/customer'
import type { BigcommerceAPI } from '../..'
import getLoggedInCustomer from './get-logged-in-customer'

View File

@ -1,6 +1,6 @@
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
import loginEndpoint from '@vercel/commerce/api/endpoints/login'
import type { LoginSchema } from '../../../types/login'
import type { LoginSchema } from '@vercel/commerce/types/login'
import type { BigcommerceAPI } from '../..'
import login from './login'

View File

@ -1,6 +1,6 @@
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
import logoutEndpoint from '@vercel/commerce/api/endpoints/logout'
import type { LogoutSchema } from '../../../types/logout'
import type { LogoutSchema } from '@vercel/commerce/types/logout'
import type { BigcommerceAPI } from '../..'
import logout from './logout'

View File

@ -1,6 +1,6 @@
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
import signupEndpoint from '@vercel/commerce/api/endpoints/signup'
import type { SignupSchema } from '../../../types/signup'
import type { SignupSchema } from '@vercel/commerce/types/signup'
import type { BigcommerceAPI } from '../..'
import signup from './signup'

View File

@ -1,7 +1,6 @@
import type { Wishlist } from '../../../types/wishlist'
import type { Wishlist } from '@vercel/commerce/types/wishlist'
import type { WishlistEndpoint } from '.'
import getCustomerId from '../../utils/get-customer-id'
import getCustomerWishlist from '../../operations/get-customer-wishlist'
// Return wishlist info
const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({

View File

@ -1,6 +1,6 @@
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
import wishlistEndpoint from '@vercel/commerce/api/endpoints/wishlist'
import type { WishlistSchema } from '../../../types/wishlist'
import type { WishlistSchema } from '@vercel/commerce/types/wishlist'
import type { BigcommerceAPI } from '../..'
import getWishlist from './get-wishlist'
import addItem from './add-item'

View File

@ -1,5 +1,4 @@
import type { Wishlist } from '../../../types/wishlist'
import getCustomerWishlist from '../../operations/get-customer-wishlist'
import type { Wishlist } from '@vercel/commerce/types/wishlist'
import getCustomerId from '../../utils/get-customer-id'
import type { WishlistEndpoint } from '.'

View File

@ -2,10 +2,13 @@ import type {
OperationContext,
OperationOptions,
} from '@vercel/commerce/api/operations'
import type { Page, GetAllPagesOperation } from '../../types/page'
import type { GetAllPagesOperation } from '@vercel/commerce/types/page'
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
import { BigcommerceConfig, Provider } from '..'
import { definitions } from '../definitions/store-content'
import { normalizePage } from '../../lib/normalize'
export default function getAllPagesOperation({
commerce,
}: OperationContext<Provider>) {
@ -33,12 +36,14 @@ export default function getAllPagesOperation({
// 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 cfg.storeApiFetch<
RecursivePartial<{ data: Page[] }>
RecursivePartial<{ data: definitions['page_Full'][] }>
>('/v3/content/pages')
const pages = (data as RecursiveRequired<typeof data>) ?? []
return {
pages: preview ? pages : pages.filter((p) => p.is_visible),
pages: preview
? pages.map(normalizePage)
: pages.filter((p) => p.is_visible).map(normalizePage),
}
}

View File

@ -3,7 +3,7 @@ import type {
OperationOptions,
} from '@vercel/commerce/api/operations'
import type { GetAllProductPathsQuery } from '../../../schema'
import type { GetAllProductPathsOperation } from '../../types/product'
import type { GetAllProductPathsOperation } from '@vercel/commerce/types/product'
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
import filterEdges from '../utils/filter-edges'
import { BigcommerceConfig, Provider } from '..'

View File

@ -6,7 +6,7 @@ import type {
GetAllProductsQuery,
GetAllProductsQueryVariables,
} from '../../../schema'
import type { GetAllProductsOperation } from '../../types/product'
import type { GetAllProductsOperation } from '@vercel/commerce/types/product'
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
import filterEdges from '../utils/filter-edges'
import setProductLocaleMeta from '../utils/set-product-locale-meta'

View File

@ -5,7 +5,7 @@ import type {
import type {
GetCustomerWishlistOperation,
Wishlist,
} from '../../types/wishlist'
} from '@vercel/commerce/types/wishlist'
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
import { BigcommerceConfig, Provider } from '..'
import getAllProducts, { ProductEdge } from './get-all-products'
@ -49,7 +49,7 @@ export default function getCustomerWishlistOperation({
if (includeProducts && wishlist?.items?.length) {
const ids = wishlist.items
?.map((item) => (item?.product_id ? String(item?.product_id) : null))
?.map((item) => (item?.productId ? String(item?.productId) : null))
.filter((id): id is string => !!id)
if (ids?.length) {
@ -66,7 +66,7 @@ export default function getCustomerWishlistOperation({
}, {})
// Populate the wishlist items with the graphql products
wishlist.items.forEach((item) => {
const product = item && productsById[item.product_id!]
const product = item && productsById[Number(item.productId)]
if (item && product) {
// @ts-ignore Fix this type when the wishlist type is properly defined
item.product = product

View File

@ -2,7 +2,7 @@ import type {
OperationContext,
OperationOptions,
} from '@vercel/commerce/api/operations'
import type { GetPageOperation, Page } from '../../types/page'
import type { GetPageOperation, Page } from '@vercel/commerce/types/page'
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
import type { BigcommerceConfig, Provider } from '..'
import { normalizePage } from '../../lib/normalize'

View File

@ -2,7 +2,7 @@ import type {
OperationContext,
OperationOptions,
} from '@vercel/commerce/api/operations'
import type { GetProductOperation } from '../../types/product'
import type { GetProductOperation } from '@vercel/commerce/types/product'
import type { GetProductQuery, GetProductQueryVariables } from '../../../schema'
import setProductLocaleMeta from '../utils/set-product-locale-meta'
import { productInfoFragment } from '../fragments/product'
@ -100,7 +100,7 @@ export default function getAllProductPathsOperation({
const variables: GetProductQueryVariables = {
locale,
hasLocale: !!locale,
path: slug ? `/${slug}/` : vars.path!,
path: slug ? `/${slug}` : vars.path!,
}
const { data } = await config.fetch<GetProductQuery>(query, { variables })
const product = data.site?.route?.node

View File

@ -2,12 +2,12 @@ import type {
OperationContext,
OperationOptions,
} from '@vercel/commerce/api/operations'
import type { GetSiteInfoOperation } from '../../types/site'
import type { GetSiteInfoOperation } from '@vercel/commerce/types/site'
import type { GetSiteInfoQuery } from '../../../schema'
import filterEdges from '../utils/filter-edges'
import type { BigcommerceConfig, Provider } from '..'
import { categoryTreeItemFragment } from '../fragments/category-tree'
import { normalizeCategory } from '../../lib/normalize'
import { normalizeBrand, normalizeCategory } from '../../lib/normalize'
// Get 3 levels of categories
export const getSiteInfoQuery = /* GraphQL */ `
@ -79,7 +79,7 @@ export default function getSiteInfoOperation({
return {
categories: categories ?? [],
brands: filterEdges(brands),
brands: filterEdges(brands).map(normalizeBrand),
}
}

View File

@ -3,7 +3,7 @@ import type {
OperationContext,
OperationOptions,
} from '@vercel/commerce/api/operations'
import type { LoginOperation } from '../../types/login'
import type { LoginOperation } from '@vercel/commerce/types/login'
import type { LoginMutation } from '../../../schema'
import type { RecursivePartial } from '../utils/types'
import concatHeader from '../utils/concat-cookie'

View File

@ -1,5 +1,5 @@
import type { WishlistItemBody } from '../../types/wishlist'
import type { CartItemBody, OptionSelections } from '../../types/cart'
import type { WishlistItemBody } from '@vercel/commerce/types/wishlist'
import type { CartItemBody, SelectedOption } from '@vercel/commerce/types/cart'
type BCWishlistItemBody = {
product_id: number
@ -10,7 +10,7 @@ type BCCartItemBody = {
product_id: number
variant_id: number
quantity?: number
option_selections?: OptionSelections[]
option_selections?: SelectedOption[]
}
export const parseWishlistItem = (
@ -24,5 +24,5 @@ export const parseCartItem = (item: CartItemBody): BCCartItemBody => ({
quantity: item.quantity,
product_id: Number(item.productId),
variant_id: Number(item.variantId),
option_selections: item.optionSelections,
option_selections: item.optionsSelected,
})

View File

@ -2,7 +2,7 @@ import { useCallback } from 'react'
import type { MutationHook } from '@vercel/commerce/utils/types'
import { CommerceError } from '@vercel/commerce/utils/errors'
import useLogin, { UseLogin } from '@vercel/commerce/auth/use-login'
import type { LoginHook } from '../types/login'
import type { LoginHook } from '@vercel/commerce/types/login'
import useCustomer from '../customer/use-customer'
export default useLogin as UseLogin<typeof handler>

View File

@ -1,7 +1,7 @@
import { useCallback } from 'react'
import type { MutationHook } from '@vercel/commerce/utils/types'
import useLogout, { UseLogout } from '@vercel/commerce/auth/use-logout'
import type { LogoutHook } from '../types/logout'
import type { LogoutHook } from '@vercel/commerce/types/logout'
import useCustomer from '../customer/use-customer'
export default useLogout as UseLogout<typeof handler>

View File

@ -1,8 +1,8 @@
import { useCallback } from 'react'
import type { MutationHook } from '@vercel/commerce/utils/types'
import { CommerceError } from '@vercel/commerce/utils/errors'
import useSignup, { UseSignup } from '@vercel/commerce/auth/use-signup'
import type { SignupHook } from '../types/signup'
import useSignup, { type UseSignup } from '@vercel/commerce/auth/use-signup'
import type { SignupHook } from '@vercel/commerce/types/signup'
import useCustomer from '../customer/use-customer'
export default useSignup as UseSignup<typeof handler>

View File

@ -4,8 +4,14 @@ import type {
HookFetcherContext,
} from '@vercel/commerce/utils/types'
import { ValidationError } from '@vercel/commerce/utils/errors'
import useRemoveItem, { UseRemoveItem } from '@vercel/commerce/cart/use-remove-item'
import type { Cart, LineItem, RemoveItemHook } from '@vercel/commerce/types/cart'
import useRemoveItem, {
UseRemoveItem,
} from '@vercel/commerce/cart/use-remove-item'
import type {
Cart,
LineItem,
RemoveItemHook,
} from '@vercel/commerce/types/cart'
import useCart from './use-cart'
export type RemoveItemFn<T = any> = T extends LineItem

View File

@ -1,6 +1,8 @@
import { SWRHook } from '@vercel/commerce/utils/types'
import useCustomer, { UseCustomer } from '@vercel/commerce/customer/use-customer'
import type { CustomerHook } from '../types/customer'
import type { SWRHook } from '@vercel/commerce/utils/types'
import useCustomer, {
type UseCustomer,
} from '@vercel/commerce/customer/use-customer'
import type { CustomerHook } from '@vercel/commerce/types/customer'
export default useCustomer as UseCustomer<typeof handler>

View File

@ -1,7 +1,9 @@
import type { Product } from '../types/product'
import type { Cart, BigcommerceCart, LineItem } from '../types/cart'
import type { Page } from '../types/page'
import type { BCCategory, Category } from '../types/site'
import type { Page } from '@vercel/commerce/types/page'
import type { Product } from '@vercel/commerce/types/product'
import type { Cart, LineItem } from '@vercel/commerce/types/cart'
import type { Category, Brand } from '@vercel/commerce/types/site'
import type { BigcommerceCart, BCCategory, BCBrand } from '../types'
import { definitions } from '../api/definitions/store-content'
import update from './immutability'
import getSlug from './get-slug'
@ -12,7 +14,7 @@ function normalizeProductOption(productOption: any) {
} = productOption
return {
id: entityId,
id: String(entityId),
values: edges?.map(({ node }: any) => node),
...rest,
}
@ -41,7 +43,7 @@ export function normalizeProduct(productNode: any): Product {
variants: {
$apply: ({ edges }: any) =>
edges?.map(({ node: { entityId, productOptions, ...rest } }: any) => ({
id: entityId,
id: String(entityId),
options: productOptions?.edges
? productOptions.edges.map(normalizeProductOption)
: [],
@ -54,7 +56,7 @@ export function normalizeProduct(productNode: any): Product {
: [],
},
brand: {
$apply: (brand: any) => (brand?.entityId ? brand?.entityId : null),
$apply: (brand: any) => (brand?.id ? brand.id : null),
},
slug: {
$set: path?.replace(/^\/+|\/+$/g, ''),
@ -75,7 +77,8 @@ export function normalizePage(page: definitions['page_Full']): Page {
name: page.name,
is_visible: page.is_visible,
sort_order: page.sort_order,
body: page.body,
body: page.body ?? '',
url: page.url,
}
}
@ -134,3 +137,12 @@ export function normalizeCategory(category: BCCategory): Category {
path: category.path,
}
}
export function normalizeBrand(brand: BCBrand): Brand {
return {
id: `${brand.node.entityId}`,
name: brand.node.name,
slug: getSlug(brand.node.path),
path: brand.node.path,
}
}

View File

@ -1,6 +1,6 @@
import { SWRHook } from '@vercel/commerce/utils/types'
import useSearch, { UseSearch } from '@vercel/commerce/product/use-search'
import type { SearchProductsHook } from '../types/product'
import type { SearchProductsHook } from '@vercel/commerce/types/product'
export default useSearch as UseSearch<typeof handler>

View File

@ -0,0 +1,32 @@
import type { GetSiteInfoQuery } from '../schema'
export type BCCategory = NonNullable<
GetSiteInfoQuery['site']['categoryTree']
>[0]
export type BCBrand = NonNullable<
NonNullable<GetSiteInfoQuery['site']['brands']['edges']>[0]
>
// TODO: this type should match:
// https://developer.bigcommerce.com/api-reference/cart-checkout/server-server-cart-api/cart/getacart#responses
export type BigcommerceCart = {
id: string
parent_id?: string
customer_id: number
email: string
currency: { code: string }
tax_included: boolean
base_amount: number
discount_amount: number
cart_amount: number
line_items: {
custom_items: any[]
digital_items: any[]
gift_certificates: any[]
physical_items: any[]
}
created_time: string
discounts?: { id: number; discounted_amount: number }[]
// TODO: add missing fields
}

View File

@ -1,66 +0,0 @@
import * as Core from '@vercel/commerce/types/cart'
export * from '@vercel/commerce/types/cart'
// TODO: this type should match:
// https://developer.bigcommerce.com/api-reference/cart-checkout/server-server-cart-api/cart/getacart#responses
export type BigcommerceCart = {
id: string
parent_id?: string
customer_id: number
email: string
currency: { code: string }
tax_included: boolean
base_amount: number
discount_amount: number
cart_amount: number
line_items: {
custom_items: any[]
digital_items: any[]
gift_certificates: any[]
physical_items: any[]
}
created_time: string
discounts?: { id: number; discounted_amount: number }[]
// TODO: add missing fields
}
/**
* Extend core cart types
*/
export type Cart = Core.Cart & {
lineItems: Core.LineItem[]
}
export type OptionSelections = {
option_id: number
option_value: number | string
}
export type CartItemBody = Core.CartItemBody & {
productId: string // The product id is always required for BC
optionSelections?: OptionSelections[]
}
export type CartTypes = {
cart: Cart
item: Core.LineItem
itemBody: CartItemBody
}
export type CartHooks = Core.CartHooks<CartTypes>
export type GetCartHook = CartHooks['getCart']
export type AddItemHook = CartHooks['addItem']
export type UpdateItemHook = CartHooks['updateItem']
export type RemoveItemHook = CartHooks['removeItem']
export type CartSchema = Core.CartSchema<CartTypes>
export type CartHandlers = Core.CartHandlers<CartTypes>
export type GetCartHandler = CartHandlers['getCart']
export type AddItemHandler = CartHandlers['addItem']
export type UpdateItemHandler = CartHandlers['updateItem']
export type RemoveItemHandler = CartHandlers['removeItem']

View File

@ -1 +0,0 @@
export * from '@vercel/commerce/types/checkout'

View File

@ -1 +0,0 @@
export * from '@vercel/commerce/types/common'

View File

@ -1,5 +0,0 @@
import * as Core from '@vercel/commerce/types/customer'
export * from '@vercel/commerce/types/customer'
export type CustomerSchema = Core.CustomerSchema

View File

@ -1,25 +0,0 @@
import * as Cart from './cart'
import * as Checkout from './checkout'
import * as Common from './common'
import * as Customer from './customer'
import * as Login from './login'
import * as Logout from './logout'
import * as Page from './page'
import * as Product from './product'
import * as Signup from './signup'
import * as Site from './site'
import * as Wishlist from './wishlist'
export type {
Cart,
Checkout,
Common,
Customer,
Login,
Logout,
Page,
Product,
Signup,
Site,
Wishlist,
}

View File

@ -1,8 +0,0 @@
import * as Core from '@vercel/commerce/types/login'
import type { LoginMutationVariables } from '../../schema'
export * from '@vercel/commerce/types/login'
export type LoginOperation = Core.LoginOperation & {
variables: LoginMutationVariables
}

View File

@ -1 +0,0 @@
export * from '@vercel/commerce/types/logout'

View File

@ -1,11 +0,0 @@
import * as Core from '@vercel/commerce/types/page'
export * from '@vercel/commerce/types/page'
export type Page = Core.Page
export type PageTypes = {
page: Page
}
export type GetAllPagesOperation = Core.GetAllPagesOperation<PageTypes>
export type GetPageOperation = Core.GetPageOperation<PageTypes>

View File

@ -1 +0,0 @@
export * from '@vercel/commerce/types/product'

View File

@ -1 +0,0 @@
export * from '@vercel/commerce/types/signup'

View File

@ -1,19 +0,0 @@
import * as Core from '@vercel/commerce/types/site'
import type { GetSiteInfoQuery, GetSiteInfoQueryVariables } from '../../schema'
export * from '@vercel/commerce/types/site'
export type BCCategory = NonNullable<
GetSiteInfoQuery['site']['categoryTree']
>[0]
export type Brand = NonNullable<
NonNullable<GetSiteInfoQuery['site']['brands']['edges']>[0]
>
export type SiteTypes = {
category: Core.Category
brand: Brand
}
export type GetSiteInfoOperation = Core.GetSiteInfoOperation<SiteTypes>

View File

@ -1,24 +0,0 @@
import * as Core from '@vercel/commerce/types/wishlist'
import { definitions } from '../api/definitions/wishlist'
import type { ProductEdge } from '../api/operations/get-all-products'
export * from '@vercel/commerce/types/wishlist'
export type WishlistItem = NonNullable<
definitions['wishlist_Full']['items']
>[0] & {
product?: ProductEdge['node']
}
export type Wishlist = Omit<definitions['wishlist_Full'], 'items'> & {
items?: WishlistItem[]
}
export type WishlistTypes = {
wishlist: Wishlist
itemBody: Core.WishlistItemBody
}
export type WishlistSchema = Core.WishlistSchema<WishlistTypes>
export type GetCustomerWishlistOperation =
Core.GetCustomerWishlistOperation<WishlistTypes>

View File

@ -1,8 +1,10 @@
import { useCallback } from 'react'
import type { MutationHook } from '@vercel/commerce/utils/types'
import { CommerceError } from '@vercel/commerce/utils/errors'
import useAddItem, { UseAddItem } from '@vercel/commerce/wishlist/use-add-item'
import type { AddItemHook } from '../types/wishlist'
import useAddItem, {
type UseAddItem,
} from '@vercel/commerce/wishlist/use-add-item'
import type { AddItemHook } from '@vercel/commerce/types/wishlist'
import useCustomer from '../customer/use-customer'
import useWishlist from './use-wishlist'

View File

@ -2,9 +2,9 @@ import { useCallback } from 'react'
import type { MutationHook } from '@vercel/commerce/utils/types'
import { CommerceError } from '@vercel/commerce/utils/errors'
import useRemoveItem, {
UseRemoveItem,
type UseRemoveItem,
} from '@vercel/commerce/wishlist/use-remove-item'
import type { RemoveItemHook } from '../types/wishlist'
import type { RemoveItemHook } from '@vercel/commerce/types/wishlist'
import useCustomer from '../customer/use-customer'
import useWishlist from './use-wishlist'

View File

@ -1,11 +1,11 @@
import { useMemo } from 'react'
import { SWRHook } from '@vercel/commerce/utils/types'
import useWishlist, {
UseWishlist,
type UseWishlist,
} from '@vercel/commerce/wishlist/use-wishlist'
import useCustomer from '../customer/use-customer'
import type { GetWishlistHook } from '../types/wishlist'
import type { GetWishlistHook } from '@vercel/commerce/types/wishlist'
export default useWishlist as UseWishlist<typeof handler>
export const handler: SWRHook<GetWishlistHook> = {
@ -32,7 +32,7 @@ export const handler: SWRHook<GetWishlistHook> = {
const { data: customer } = useCustomer()
const response = useData({
input: [
['customerId', customer?.entityId],
['customerId', customer?.id],
['includeProducts', input?.includeProducts],
],
swrOptions: {

View File

@ -52,7 +52,8 @@
"import-cwd": "^3.0.0",
"js-cookie": "^3.0.1",
"swr": "^1.3.0",
"node-fetch": "^2.6.7"
"node-fetch": "^2.6.7",
"zod": "^3.19.0"
},
"peerDependencies": {
"next": "^12",

View File

@ -3,58 +3,60 @@ import { CommerceAPIError } from '../utils/errors'
import isAllowedOperation from '../utils/is-allowed-operation'
import type { GetAPISchema } from '..'
const cartEndpoint: GetAPISchema<any, CartSchema<any>>['endpoint']['handler'] =
async (ctx) => {
const { req, res, handlers, config } = ctx
const cartEndpoint: GetAPISchema<
any,
CartSchema
>['endpoint']['handler'] = async (ctx) => {
const { req, res, handlers, config } = ctx
if (
!isAllowedOperation(req, res, {
GET: handlers['getCart'],
POST: handlers['addItem'],
PUT: handlers['updateItem'],
DELETE: handlers['removeItem'],
})
) {
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']({ ...ctx, body })
}
// Create or add an item to the cart
if (req.method === 'POST') {
const body = { ...req.body, cartId }
return await handlers['addItem']({ ...ctx, body })
}
// Update item in cart
if (req.method === 'PUT') {
const body = { ...req.body, cartId }
return await handlers['updateItem']({ ...ctx, body })
}
// Remove an item from the cart
if (req.method === 'DELETE') {
const body = { ...req.body, cartId }
return await handlers['removeItem']({ ...ctx, body })
}
} catch (error) {
console.error(error)
const message =
error instanceof CommerceAPIError
? 'An unexpected error ocurred with the Commerce API'
: 'An unexpected error ocurred'
res.status(500).json({ data: null, errors: [{ message }] })
}
if (
!isAllowedOperation(req, res, {
GET: handlers['getCart'],
POST: handlers['addItem'],
PUT: handlers['updateItem'],
DELETE: handlers['removeItem'],
})
) {
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']({ ...ctx, body })
}
// Create or add an item to the cart
if (req.method === 'POST') {
const body = { ...req.body, cartId }
return await handlers['addItem']({ ...ctx, body })
}
// Update item in cart
if (req.method === 'PUT') {
const body = { ...req.body, cartId }
return await handlers['updateItem']({ ...ctx, body })
}
// Remove an item from the cart
if (req.method === 'DELETE') {
const body = { ...req.body, cartId }
return await handlers['removeItem']({ ...ctx, body })
}
} catch (error) {
console.error(error)
const message =
error instanceof CommerceAPIError
? 'An unexpected error ocurred with the Commerce API'
: 'An unexpected error ocurred'
res.status(500).json({ data: null, errors: [{ message }] })
}
}
export default cartEndpoint

View File

@ -6,7 +6,7 @@ import isAllowedOperation from '../../utils/is-allowed-operation'
const customerEndpoint: GetAPISchema<
any,
CustomerSchema<any>
CustomerSchema
>['endpoint']['handler'] = async (ctx) => {
const { req, res, handlers } = ctx

View File

@ -5,7 +5,7 @@ import type { GetAPISchema } from '..'
const loginEndpoint: GetAPISchema<
any,
LoginSchema<any>
LoginSchema
>['endpoint']['handler'] = async (ctx) => {
const { req, res, handlers } = ctx

View File

@ -5,7 +5,7 @@ import type { GetAPISchema } from '..'
const wishlistEndpoint: GetAPISchema<
any,
WishlistSchema<any>
WishlistSchema
>['endpoint']['handler'] = async (ctx) => {
const { req, res, handlers, config } = ctx

View File

@ -11,11 +11,14 @@ import type { WishlistSchema } from '../types/wishlist'
import type { CheckoutSchema } from '../types/checkout'
import type { CustomerCardSchema } from '../types/customer/card'
import type { CustomerAddressSchema } from '../types/customer/address'
import { withOperationCallback } from './utils/with-operation-callback'
import {
defaultOperations,
OPERATIONS,
AllOperations,
APIOperations,
defaultOperations,
} from './operations'
export type APISchemas =
@ -106,7 +109,10 @@ export function getCommerceApi<P extends APIProvider>(
OPERATIONS.forEach((k) => {
const op = ops[k]
if (op) {
commerce[k] = op({ commerce }) as AllOperations<P>[typeof k]
commerce[k] = withOperationCallback(
k,
op({ commerce })
) as AllOperations<P>[typeof k]
}
})

View File

@ -25,6 +25,13 @@ export const OPERATIONS = [
'getProduct',
] as const
export type Operation = {
[O in AllowedOperations]: {
name: O
data: Awaited<ReturnType<Operations<APIProvider>[O]>>
}
}[AllowedOperations]
export const defaultOperations = OPERATIONS.reduce((ops, k) => {
ops[k] = noop
return ops

View File

@ -1,4 +1,7 @@
import { ZodError } from 'zod'
import type { Response } from '@vercel/fetch'
import { CommerceError } from '../../utils/errors'
export class CommerceAPIError extends Error {
status: number
@ -20,3 +23,25 @@ export class CommerceNetworkError extends Error {
this.name = 'CommerceNetworkError'
}
}
export const getOperationError = (operation: string, error: unknown) => {
if (error instanceof ZodError) {
return new CommerceError({
code: 'SCHEMA_VALIDATION_ERROR',
message:
`The ${operation} operation returned invalid data and has ${
error.issues.length
} parse ${error.issues.length === 1 ? 'error' : 'errors'}: \n` +
error.issues
.map(
(e, index) =>
`Error #${index + 1} ${
e.path.length > 0 ? `Path: ${e.path.join('.')}, ` : ''
}Code: ${e.code}, Message: ${e.message}`
)
.join('\n'),
})
}
return error
}

View File

@ -0,0 +1,42 @@
import type { AllowedOperations, Operation } from '../operations'
import { z } from 'zod'
import { getOperationError } from './errors'
import { pageSchema } from '../../schemas/page'
import { siteInfoSchema } from '../../schemas/site'
import { productSchema, productsPathsSchema } from '../../schemas/product'
export const withOperationCallback =
(name: AllowedOperations, fn: (...args: any[]) => Promise<any>) =>
async (...args: any[]) => {
try {
const data = await fn(...args)
parse({ name, data })
return data
} catch (error) {
throw getOperationError(name, error)
}
}
const parse = ({ name, data }: Operation) => {
switch (name) {
case 'getProduct':
productSchema.nullable().parse(data.product)
break
case 'getAllProducts':
z.array(productSchema).parse(data.products)
break
case 'getAllProductPaths':
productsPathsSchema.parse(data.products)
break
case 'getPage':
pageSchema.nullable().parse(data.page)
break
case 'getAllPages':
z.array(pageSchema).parse(data.pages)
break
case 'getSiteInfo':
siteInfoSchema.parse(data)
break
}
}

View File

@ -5,7 +5,7 @@ import type { LoginHook } from '../types/login'
import type { Provider } from '..'
export type UseLogin<
H extends MutationHook<LoginHook<any>> = MutationHook<LoginHook>
H extends MutationHook<LoginHook> = MutationHook<LoginHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<LoginHook> = mutationFetcher

View File

@ -5,7 +5,7 @@ import type { LogoutHook } from '../types/logout'
import type { Provider } from '..'
export type UseLogout<
H extends MutationHook<LogoutHook<any>> = MutationHook<LogoutHook>
H extends MutationHook<LogoutHook> = MutationHook<LogoutHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<LogoutHook> = mutationFetcher

View File

@ -5,7 +5,7 @@ import type { SignupHook } from '../types/signup'
import type { Provider } from '..'
export type UseSignup<
H extends MutationHook<SignupHook<any>> = MutationHook<SignupHook>
H extends MutationHook<SignupHook> = MutationHook<SignupHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<SignupHook> = mutationFetcher

View File

@ -5,7 +5,7 @@ import type { AddItemHook } from '../types/cart'
import type { Provider } from '..'
export type UseAddItem<
H extends MutationHook<AddItemHook<any>> = MutationHook<AddItemHook>
H extends MutationHook<AddItemHook> = MutationHook<AddItemHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<AddItemHook> = mutationFetcher

View File

@ -4,9 +4,8 @@ import type { SWRHook, HookFetcherFn } from '../utils/types'
import type { GetCartHook } from '../types/cart'
import { Provider, useCommerce } from '..'
export type UseCart<
H extends SWRHook<GetCartHook<any>> = SWRHook<GetCartHook>
> = ReturnType<H['useHook']>
export type UseCart<H extends SWRHook<GetCartHook> = SWRHook<GetCartHook>> =
ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<GetCartHook> = async ({
options,

View File

@ -5,7 +5,7 @@ import type { RemoveItemHook } from '../types/cart'
import type { Provider } from '..'
export type UseRemoveItem<
H extends MutationHook<RemoveItemHook<any>> = MutationHook<RemoveItemHook>
H extends MutationHook<RemoveItemHook> = MutationHook<RemoveItemHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<RemoveItemHook> = mutationFetcher

View File

@ -5,7 +5,7 @@ import type { UpdateItemHook } from '../types/cart'
import type { Provider } from '..'
export type UseUpdateItem<
H extends MutationHook<UpdateItemHook<any>> = MutationHook<UpdateItemHook>
H extends MutationHook<UpdateItemHook> = MutationHook<UpdateItemHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<UpdateItemHook> = mutationFetcher

View File

@ -7,7 +7,7 @@ import { useHook, useSWRHook } from '../utils/use-hook'
import { Provider, useCommerce } from '..'
export type UseCheckout<
H extends SWRHook<GetCheckoutHook<any>> = SWRHook<GetCheckoutHook>
H extends SWRHook<GetCheckoutHook> = SWRHook<GetCheckoutHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<GetCheckoutHook> = async ({

View File

@ -6,9 +6,7 @@ import { useHook, useMutationHook } from '../utils/use-hook'
import { mutationFetcher } from '../utils/default-fetcher'
export type UseSubmitCheckout<
H extends MutationHook<
SubmitCheckoutHook<any>
> = MutationHook<SubmitCheckoutHook>
H extends MutationHook<SubmitCheckoutHook> = MutationHook<SubmitCheckoutHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<SubmitCheckoutHook> = mutationFetcher

View File

@ -6,7 +6,7 @@ import { useHook, useMutationHook } from '../../utils/use-hook'
import { mutationFetcher } from '../../utils/default-fetcher'
export type UseAddItem<
H extends MutationHook<AddItemHook<any>> = MutationHook<AddItemHook>
H extends MutationHook<AddItemHook> = MutationHook<AddItemHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<AddItemHook> = mutationFetcher

View File

@ -7,7 +7,7 @@ import { useHook, useSWRHook } from '../../utils/use-hook'
import { Provider, useCommerce } from '../..'
export type UseAddresses<
H extends SWRHook<GetAddressesHook<any>> = SWRHook<GetAddressesHook>
H extends SWRHook<GetAddressesHook> = SWRHook<GetAddressesHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<GetAddressesHook> = async ({

View File

@ -6,7 +6,7 @@ import { useHook, useMutationHook } from '../../utils/use-hook'
import { mutationFetcher } from '../../utils/default-fetcher'
export type UseRemoveItem<
H extends MutationHook<RemoveItemHook<any>> = MutationHook<RemoveItemHook>
H extends MutationHook<RemoveItemHook> = MutationHook<RemoveItemHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<RemoveItemHook> = mutationFetcher

View File

@ -6,7 +6,7 @@ import { useHook, useMutationHook } from '../../utils/use-hook'
import { mutationFetcher } from '../../utils/default-fetcher'
export type UseUpdateItem<
H extends MutationHook<UpdateItemHook<any>> = MutationHook<UpdateItemHook>
H extends MutationHook<UpdateItemHook> = MutationHook<UpdateItemHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<UpdateItemHook> = mutationFetcher

View File

@ -6,7 +6,7 @@ import { useHook, useMutationHook } from '../../utils/use-hook'
import { mutationFetcher } from '../../utils/default-fetcher'
export type UseAddItem<
H extends MutationHook<AddItemHook<any>> = MutationHook<AddItemHook>
H extends MutationHook<AddItemHook> = MutationHook<AddItemHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<AddItemHook> = mutationFetcher

View File

@ -6,9 +6,8 @@ import Cookies from 'js-cookie'
import { useHook, useSWRHook } from '../../utils/use-hook'
import { Provider, useCommerce } from '../..'
export type UseCards<
H extends SWRHook<GetCardsHook<any>> = SWRHook<GetCardsHook>
> = ReturnType<H['useHook']>
export type UseCards<H extends SWRHook<GetCardsHook> = SWRHook<GetCardsHook>> =
ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<GetCardsHook> = async ({
options,

View File

@ -6,7 +6,7 @@ import { useHook, useMutationHook } from '../../utils/use-hook'
import { mutationFetcher } from '../../utils/default-fetcher'
export type UseRemoveItem<
H extends MutationHook<RemoveItemHook<any>> = MutationHook<RemoveItemHook>
H extends MutationHook<RemoveItemHook> = MutationHook<RemoveItemHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<RemoveItemHook> = mutationFetcher

View File

@ -6,7 +6,7 @@ import { useHook, useMutationHook } from '../../utils/use-hook'
import { mutationFetcher } from '../../utils/default-fetcher'
export type UseUpdateItem<
H extends MutationHook<UpdateItemHook<any>> = MutationHook<UpdateItemHook>
H extends MutationHook<UpdateItemHook> = MutationHook<UpdateItemHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<UpdateItemHook> = mutationFetcher

View File

@ -5,7 +5,7 @@ import type { HookFetcherFn, SWRHook } from '../utils/types'
import type { Provider } from '..'
export type UseCustomer<
H extends SWRHook<CustomerHook<any>> = SWRHook<CustomerHook>
H extends SWRHook<CustomerHook> = SWRHook<CustomerHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<CustomerHook> = SWRFetcher

View File

@ -5,7 +5,7 @@ import type { SearchProductsHook } from '../types/product'
import type { Provider } from '..'
export type UseSearch<
H extends SWRHook<SearchProductsHook<any>> = SWRHook<SearchProductsHook>
H extends SWRHook<SearchProductsHook> = SWRHook<SearchProductsHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<SearchProductsHook> = SWRFetcher

View File

@ -0,0 +1,18 @@
import { z } from 'zod'
export const pageSchema = z.object({
id: z.string(),
name: z.string(),
url: z.string().startsWith('/').optional(),
body: z.string(),
is_visible: z.boolean().optional(),
sort_order: z.number().optional(),
})
export const pagesPathsSchema = z.array(
z.object({
page: z.object({
path: z.string().startsWith('/'),
}),
})
)

View File

@ -0,0 +1,60 @@
import { z } from 'zod'
export const productPriceSchema = z.object({
value: z.number(),
currencyCode: z.string().max(3).optional(),
retailPrice: z.number().optional(),
})
export const productOptionSchema = z.object({
id: z.string(),
displayName: z.string(),
values: z.array(
z.object({
label: z.string(),
hexColors: z.array(z.string()).optional(),
})
),
})
export const productImageSchema = z.object({
url: z.string().url().or(z.string().startsWith('/')),
alt: z.string().optional(),
width: z.number().optional(),
height: z.number().optional(),
})
export const productVariantSchema = z.object({
id: z.string(),
sku: z.string().nullish(),
name: z.string().optional(),
options: z.array(productOptionSchema),
image: productImageSchema.optional(),
})
export const productSchema = z.object({
id: z.string(),
name: z.string(),
description: z.string(),
descriptionHtml: z.string().optional(),
sku: z.string().nullish(),
slug: z.string(),
path: z.string().startsWith('/'),
images: z.array(productImageSchema),
variants: z.array(productVariantSchema),
price: productPriceSchema,
options: z.array(productOptionSchema),
vendor: z.string().optional(),
})
export const productsPathsSchema = z.array(
z.object({ path: z.string().startsWith('/') })
)
export const searchProductBodySchema = z.object({
search: z.string(),
categoryId: z.string(),
brandId: z.string().optional(),
sort: z.string().optional(),
locale: z.string().optional(),
})

View File

@ -0,0 +1,18 @@
import { z } from 'zod'
export const siteInfoSchema = z.object({
categories: z.array(
z.object({
id: z.string(),
name: z.string(),
path: z.string().startsWith('/'),
})
),
brands: z.array(
z.object({
id: z.string(),
name: z.string(),
path: z.string().startsWith('/'),
})
),
})

View File

@ -1,177 +1,264 @@
import type { Discount, Measurement, Image } from './common'
import type { Discount, Image, Measurement } from './common'
export type SelectedOption = {
// The option's id.
id?: string
// The product options name.
name: string
/// The product options value.
value: string
}
export type LineItem = {
// TODO: This should use the same type as the `ProductVariant` type from `product.ts`
export interface ProductVariant {
/**
* The unique identifier for the variant.
*/
id: string
variantId: string
productId: string
/**
* The SKU (stock keeping unit) associated with the product variant.
*/
sku?: string
/**
* The product variants name, or the product's name.
*/
name: string
quantity: number
discounts: Discount[]
// A human-friendly unique string automatically generated from the products name
path: string
variant: ProductVariant
options?: SelectedOption[]
}
export type ProductVariant = {
id: string
// The SKU (stock keeping unit) associated with the product variant.
sku: string
// The product variants title, or the product's name.
name: string
// Whether a customer needs to provide a shipping address when placing
// an order for the product variant.
requiresShipping: boolean
// The product variants price after all discounts are applied.
/**
* The product variants price after all discounts are applied.
*/
price: number
// Product variants price, as quoted by the manufacturer/distributor.
/**
* The product variants price before discounts are applied.
*/
listPrice: number
// Image associated with the product variant. Falls back to the product image
// if no image is available.
image?: Image
// Indicates whether this product variant is in stock.
isInStock?: boolean
// Indicates if the product variant is available for sale.
/**
* Indicates if the variant is available for sale.
*/
availableForSale?: boolean
// The variant's weight. If a weight was not explicitly specified on the
// variant this will be the product's weight.
/**
* Whether a customer needs to provide a shipping address when placing
* an order for the product variant.
*/
requiresShipping?: boolean
/**
* The image associated with the variant.
*/
image?: Image
/**
* The variant's weight. If a weight was not explicitly specified on the
* variant, this will be the product's weight.
*/
weight?: Measurement
// The variant's height. If a height was not explicitly specified on the
// variant, this will be the product's height.
/**
* The variant's height. If a height was not explicitly specified on the
* variant, this will be the product's height.
*/
height?: Measurement
// The variant's width. If a width was not explicitly specified on the
// variant, this will be the product's width.
/**
* The variant's width. If a width was not explicitly specified on the
* variant, this will be the product's width.
*/
width?: Measurement
// The variant's depth. If a depth was not explicitly specified on the
// variant, this will be the product's depth.
/**
* The variant's depth. If a depth was not explicitly specified on the
* variant, this will be the product's depth.
*/
depth?: Measurement
}
// Shopping cart, a.k.a Checkout
export type Cart = {
export interface SelectedOption {
/**
* The unique identifier for the option.
*/
id?: string
/**
* The product options name, such as "Color" or "Size".
*/
name: string
/**
* The product options value, such as "Red" or "XL".
*/
value: string
}
export interface LineItem {
/**
* The unique identifier for the line item.
*/
id: string
// ID of the customer to which the cart belongs.
/**
* The unique identifier for the product variant.
*/
variantId: string
/**
* The unique identifier for the product, if the variant is not provided.
*/
productId: string
/**
* This is usually the product's name.
*/
name: string
/**
* The quantity of the product variant in the line item.
*/
quantity: number
/**
* List of discounts applied to the line item.
*/
discounts: Discount[]
/**
* A human-friendly unique string automatically generated from the products name.
*/
path: string
/**
* The product variant.
*/
variant: ProductVariant
/**
* List of selected options, to be used when displaying the line item, such as Color: Red, Size: XL.
*/
options?: SelectedOption[]
}
/**
* Shopping cart, a.k.a Checkout
*/
export interface Cart {
/**
* The unique identifier for the cart.
*/
id: string
/**
* ID of the customer to which the cart belongs.
*/
customerId?: string
// The email assigned to this cart
/**
* The URL of the cart.
*/
url?: string
/**
* The email assigned to this cart.
*/
email?: string
// The date and time when the cart was created.
/**
* The date and time when the cart was created.
*/
createdAt: string
// The currency used for this cart
/**
* The currency used for this cart */
currency: { code: string }
// Specifies if taxes are included in the line items.
/**
* Indicates if taxes are included in the line items.
*/
taxesIncluded: boolean
/**
* List of cart line items.
*/
lineItems: LineItem[]
// The sum of all the prices of all the items in the cart.
// Duties, taxes, shipping and discounts excluded.
/**
* The sum of all the pricexs of all the items in the cart.
* Duties, taxes, shipping and discounts excluded.
*/
lineItemsSubtotalPrice: number
// Price of the cart before duties, shipping and taxes.
/**
* Price of the cart before duties, shipping and taxes.*/
subtotalPrice: number
// The sum of all the prices of all the items in the cart.
// Duties, taxes and discounts included.
/**
* The sum of all the prices of all the items in the cart.
* Duties, taxes and discounts included.
*/
totalPrice: number
// Discounts that have been applied on the cart.
/**
* Discounts that have been applied on the cart.
*/
discounts?: Discount[]
}
/**
* Base cart item body used for cart mutations
*/
export type CartItemBody = {
export interface CartItemBody {
/**
* The unique identifier for the product variant.
*/
variantId: string
/**
* The unique identifier for the product, if the variant is not provided.
*/
productId?: string
/**
* The quantity of the product variant.
*/
quantity?: number
/**
* The product variant's selected options.
*/
optionsSelected?: SelectedOption[]
}
/**
* Hooks schema
* Cart Hooks for add, update and remove items from the cart
*/
export type CartTypes = {
cart?: Cart
item: LineItem
itemBody: CartItemBody
export type CartHooks = {
getCart: GetCartHook
addItem: AddItemHook
updateItem: UpdateItemHook
removeItem: RemoveItemHook
}
export type CartHooks<T extends CartTypes = CartTypes> = {
getCart: GetCartHook<T>
addItem: AddItemHook<T>
updateItem: UpdateItemHook<T>
removeItem: RemoveItemHook<T>
}
export type GetCartHook<T extends CartTypes = CartTypes> = {
data: T['cart'] | null
export type GetCartHook = {
data: Cart | null
input: {}
fetcherInput: { cartId?: string }
swrState: { isEmpty: boolean }
}
export type AddItemHook<T extends CartTypes = CartTypes> = {
data: T['cart']
input?: T['itemBody']
fetcherInput: T['itemBody']
body: { item: T['itemBody'] }
actionInput: T['itemBody']
export type AddItemHook = {
data: Cart
input?: CartItemBody
fetcherInput: CartItemBody
body: { item: CartItemBody }
actionInput: CartItemBody
}
export type UpdateItemHook<T extends CartTypes = CartTypes> = {
data: T['cart'] | null
input: { item?: T['item']; wait?: number }
fetcherInput: { itemId: string; item: T['itemBody'] }
body: { itemId: string; item: T['itemBody'] }
actionInput: T['itemBody'] & { id: string }
export type UpdateItemHook = {
data: Cart | null | undefined
input: { item?: LineItem; wait?: number }
fetcherInput: { itemId: string; item: CartItemBody }
body: { itemId: string; item: CartItemBody }
actionInput: CartItemBody & { id: string }
}
export type RemoveItemHook<T extends CartTypes = CartTypes> = {
data: T['cart'] | null
input: { item?: T['item'] }
export type RemoveItemHook = {
data: Cart | null | undefined
input: { item?: LineItem }
fetcherInput: { itemId: string }
body: { itemId: string }
actionInput: { id: string }
}
/**
* API Schema
* Cart API endpoitns & handlers for add, update and remove items from the cart
*/
export type CartSchema<T extends CartTypes = CartTypes> = {
export type CartSchema = {
endpoint: {
options: {}
handlers: CartHandlers<T>
handlers: CartHandlers
}
}
export type CartHandlers<T extends CartTypes = CartTypes> = {
getCart: GetCartHandler<T>
addItem: AddItemHandler<T>
updateItem: UpdateItemHandler<T>
removeItem: RemoveItemHandler<T>
export type CartHandlers = {
getCart: GetCartHandler
addItem: AddItemHandler
updateItem: UpdateItemHandler
removeItem: RemoveItemHandler
}
export type GetCartHandler<T extends CartTypes = CartTypes> = GetCartHook<T> & {
export type GetCartHandler = GetCartHook & {
body: { cartId?: string }
}
export type AddItemHandler<T extends CartTypes = CartTypes> = AddItemHook<T> & {
export type AddItemHandler = AddItemHook & {
body: { cartId: string }
}
export type UpdateItemHandler<T extends CartTypes = CartTypes> =
UpdateItemHook<T> & {
data: T['cart']
body: { cartId: string }
}
export type UpdateItemHandler = UpdateItemHook & {
data: Cart
body: { cartId: string }
}
export type RemoveItemHandler<T extends CartTypes = CartTypes> =
RemoveItemHook<T> & {
body: { cartId: string }
}
export type RemoveItemHandler = RemoveItemHook & {
body: { cartId: string }
}

View File

@ -1,57 +1,89 @@
import type { UseSubmitCheckout } from '../checkout/use-submit-checkout'
import type { Address, AddressFields } from './customer/address'
import type { AddressFields } from './customer/address'
import type { Card, CardFields } from './customer/card'
import type { LineItem } from './cart'
// Index
export type Checkout = any
export type CheckoutTypes = {
card?: Card | CardFields
address?: Address | AddressFields
checkout?: Checkout
hasPayment?: boolean
hasShipping?: boolean
export interface Checkout {
/**
* Indicates if the checkout has payment iformation collected.
*/
hasPayment: boolean
/**
* Indicates if the checkout has shipping information collected.
*/
hasShipping: boolean
/**
* The unique identifier for the address that the customer has selected for shipping.
*/
addressId: string
/**
* The list of payment cards that the customer has available.
*/
payments?: Card[]
/**
* The unique identifier of the card that the customer has selected for payment.
*/
cardId?: string
/**
* List of items in the checkout.
*/
lineItems?: LineItem[]
}
export type SubmitCheckoutHook<T extends CheckoutTypes = CheckoutTypes> = {
data: T
input?: T
fetcherInput: T
body: { item: T }
actionInput: T
export interface CheckoutBody {
/**
* The unique identifier for the cart.
*/
cartId?: string
/**
* The Card information.
* @see CardFields
*/
card: CardFields
/**
* The Address information.
* @see AddressFields
*/
address: AddressFields
}
export type GetCheckoutHook<T extends CheckoutTypes = CheckoutTypes> = {
data: T['checkout'] | null
export type SubmitCheckoutHook = {
data: Checkout
input?: CheckoutBody
fetcherInput: CheckoutBody
body: { item: CheckoutBody }
actionInput: CheckoutBody
}
export type GetCheckoutHook = {
data: Checkout | null
input: {}
fetcherInput: { cartId?: string }
swrState: { isEmpty: boolean }
mutations: { submit: UseSubmitCheckout }
}
export type CheckoutHooks<T extends CheckoutTypes = CheckoutTypes> = {
submitCheckout?: SubmitCheckoutHook<T>
getCheckout: GetCheckoutHook<T>
export type CheckoutHooks = {
submitCheckout?: SubmitCheckoutHook
getCheckout: GetCheckoutHook
}
export type GetCheckoutHandler<T extends CheckoutTypes = CheckoutTypes> =
GetCheckoutHook<T> & {
body: { cartId: string }
}
export type SubmitCheckoutHandler<T extends CheckoutTypes = CheckoutTypes> =
SubmitCheckoutHook<T> & {
body: { cartId: string }
}
export type CheckoutHandlers<T extends CheckoutTypes = CheckoutTypes> = {
getCheckout: GetCheckoutHandler<T>
submitCheckout?: SubmitCheckoutHandler<T>
export type GetCheckoutHandler = GetCheckoutHook & {
body: { cartId: string }
}
export type CheckoutSchema<T extends CheckoutTypes = CheckoutTypes> = {
export type SubmitCheckoutHandler = SubmitCheckoutHook & {
body: { cartId: string }
}
export type CheckoutHandlers = {
getCheckout: GetCheckoutHandler
submitCheckout?: SubmitCheckoutHandler
}
export type CheckoutSchema = {
endpoint: {
options: {}
handlers: CheckoutHandlers<T>
handlers: CheckoutHandlers
}
}

View File

@ -1,16 +1,36 @@
export type Discount = {
// The value of the discount, can be an amount or percentage
export interface Discount {
/**
* The value of the discount, can be an amount or percentage.
*/
value: number
}
export type Measurement = {
export interface Measurement {
/**
* The measurement's value.
*/
value: number
/**
* The measurement's unit, such as "KILOGRAMS", "GRAMS", "POUNDS" & "OOUNCES".
*/
unit: 'KILOGRAMS' | 'GRAMS' | 'POUNDS' | 'OUNCES'
}
export type Image = {
export interface Image {
/**
* The URL of the image.
*/
url: string
altText?: string
/**
* A word or phrase that describes the content of an image.
*/
alt?: string
/**
* The image's width.
*/
width?: number
/**
* The image's height.
*/
height?: number
}

View File

@ -1,111 +1,123 @@
export interface Address {
/**
* The unique identifier for the address.
*/
id: string
/**
* The customer's first name.
*/
mask: string
}
export interface AddressFields {
/**
* The type of address.
* @example "billing, shipping"
*/
type: string
/**
* The customer's first name.
*/
firstName: string
/**
* The customer's last name.
*/
lastName: string
/**
* Company name.
*/
company: string
/**
* The customer's billing address street number.
*/
streetNumber: string
/**
* The customer's billing address apartment number.
*/
apartments: string
/**
* The customer's billing address zip code.
*/
zipCode: string
/**
* The customer's billing address city.
*/
city: string
/**
* The customer's billing address country.
*/
country: string
}
export type CustomerAddressTypes = {
address?: Address
fields: AddressFields
}
/**
* Hooks for managing a customer's addresses.
*/
export type GetAddressesHook<
T extends CustomerAddressTypes = CustomerAddressTypes
> = {
data: T['address'][] | null
export type GetAddressesHook = {
data: Address[] | null
input: {}
fetcherInput: { cartId?: string }
swrState: { isEmpty: boolean }
}
export type AddItemHook<T extends CustomerAddressTypes = CustomerAddressTypes> =
{
data: T['address']
input?: T['fields']
fetcherInput: T['fields']
body: { item: T['fields'] }
actionInput: T['fields']
}
export type UpdateItemHook<
T extends CustomerAddressTypes = CustomerAddressTypes
> = {
data: T['address'] | null
input: { item?: T['fields']; wait?: number }
fetcherInput: { itemId: string; item: T['fields'] }
body: { itemId: string; item: T['fields'] }
actionInput: T['fields'] & { id: string }
export type AddItemHook = {
data: Address
input?: AddressFields
fetcherInput: AddressFields
body: { item: AddressFields }
actionInput: AddressFields
}
export type RemoveItemHook<
T extends CustomerAddressTypes = CustomerAddressTypes
> = {
data: T['address'] | null
input: { item?: T['address'] }
export type UpdateItemHook = {
data: Address | null
input: { item?: AddressFields; wait?: number }
fetcherInput: { itemId: string; item: AddressFields }
body: { itemId: string; item: AddressFields }
actionInput: AddressFields & { id: string }
}
export type RemoveItemHook = {
data: Address | null | undefined
input: { item?: Address }
fetcherInput: { itemId: string }
body: { itemId: string }
actionInput: { id: string }
}
export type CustomerAddressHooks<
T extends CustomerAddressTypes = CustomerAddressTypes
> = {
getAddresses: GetAddressesHook<T>
addItem: AddItemHook<T>
updateItem: UpdateItemHook<T>
removeItem: RemoveItemHook<T>
export type CustomerAddressHooks = {
getAddresses: GetAddressesHook
addItem: AddItemHook
updateItem: UpdateItemHook
removeItem: RemoveItemHook
}
export type AddressHandler<
T extends CustomerAddressTypes = CustomerAddressTypes
> = GetAddressesHook<T> & {
body: { cartId?: string }
}
/**
* API endpoints for managing a customer's addresses.
*/
export type AddItemHandler<
T extends CustomerAddressTypes = CustomerAddressTypes
> = AddItemHook<T> & {
export type AddItemHandler = AddItemHook & {
body: { cartId: string }
}
export type UpdateItemHandler<
T extends CustomerAddressTypes = CustomerAddressTypes
> = UpdateItemHook<T> & {
data: T['address']
export type UpdateItemHandler = UpdateItemHook & {
data: Address
body: { cartId: string }
}
export type RemoveItemHandler<
T extends CustomerAddressTypes = CustomerAddressTypes
> = RemoveItemHook<T> & {
export type RemoveItemHandler = RemoveItemHook & {
body: { cartId: string }
}
export type CustomerAddressHandlers<
T extends CustomerAddressTypes = CustomerAddressTypes
> = {
getAddresses: GetAddressesHook<T>
addItem: AddItemHandler<T>
updateItem: UpdateItemHandler<T>
removeItem: RemoveItemHandler<T>
export type CustomerAddressHandlers = {
getAddresses: GetAddressesHook
addItem: AddItemHandler
updateItem: UpdateItemHandler
removeItem: RemoveItemHandler
}
export type CustomerAddressSchema<
T extends CustomerAddressTypes = CustomerAddressTypes
> = {
export type CustomerAddressSchema = {
endpoint: {
options: {}
handlers: CustomerAddressHandlers<T>
handlers: CustomerAddressHandlers
}
}

View File

@ -1,102 +1,139 @@
export interface Card {
/**
* Unique identifier for the card.
*/
id: string
/**
* Masked card number. Contains only the last 4 digits.
* @example "4242"
*/
mask: string
/**
* The card's brand.
* @example "Visa, Mastercard, etc."
*/
provider: string
}
/**
* The fields required to create a new card.
*/
export interface CardFields {
/**
* Name on the card.
*/
cardHolder: string
/**
* The card's number, consisting of 16 digits.
*/
cardNumber: string
/**
* The card's expiry month and year, in the format MM/YY.
* @example "01/25"
*/
cardExpireDate: string
/**
* The card's security code, consisting of 3 digits.
*/
cardCvc: string
/**
* The customer's first name.
*/
firstName: string
/**
* The customer's last name.
*/
lastName: string
/**
* Company name.
*/
company: string
/**
* The customer's billing address street number.
*/
streetNumber: string
/**
* The customer's billing address zip code.
*/
zipCode: string
/**
* The customer's billing address city.
*/
city: string
/**
* The customer's billing address country.
*/
country: string
}
export type CustomerCardTypes = {
card?: Card
fields: CardFields
}
/**
* Hooks for managing a customer's cards.
*/
export type GetCardsHook<T extends CustomerCardTypes = CustomerCardTypes> = {
data: T['card'][] | null
export type GetCardsHook = {
data: Card[] | null
input: {}
fetcherInput: { cartId?: string }
swrState: { isEmpty: boolean }
}
export type AddItemHook<T extends CustomerCardTypes = CustomerCardTypes> = {
data: T['card']
input?: T['fields']
fetcherInput: T['fields']
body: { item: T['fields'] }
actionInput: T['fields']
export type AddItemHook = {
data: Card
input?: CardFields
fetcherInput: CardFields
body: { item: CardFields }
actionInput: CardFields
}
export type UpdateItemHook<T extends CustomerCardTypes = CustomerCardTypes> = {
data: T['card'] | null
input: { item?: T['fields']; wait?: number }
fetcherInput: { itemId: string; item: T['fields'] }
body: { itemId: string; item: T['fields'] }
actionInput: T['fields'] & { id: string }
export type UpdateItemHook = {
data: Card | null | undefined
input: { item?: CardFields; wait?: number }
fetcherInput: { itemId: string; item: CardFields }
body: { itemId: string; item: CardFields }
actionInput: CardFields & { id: string }
}
export type RemoveItemHook<T extends CustomerCardTypes = CustomerCardTypes> = {
data: T['card'] | null
input: { item?: T['card'] }
export type RemoveItemHook = {
data: Card | null | undefined
input: { item?: Card }
fetcherInput: { itemId: string }
body: { itemId: string }
actionInput: { id: string }
}
export type CustomerCardHooks<T extends CustomerCardTypes = CustomerCardTypes> =
{
getCards: GetCardsHook<T>
addItem: AddItemHook<T>
updateItem: UpdateItemHook<T>
removeItem: RemoveItemHook<T>
}
export type CardsHandler<T extends CustomerCardTypes = CustomerCardTypes> =
GetCardsHook<T> & {
body: { cartId?: string }
}
export type AddItemHandler<T extends CustomerCardTypes = CustomerCardTypes> =
AddItemHook<T> & {
body: { cartId: string }
}
export type UpdateItemHandler<T extends CustomerCardTypes = CustomerCardTypes> =
UpdateItemHook<T> & {
data: T['card']
body: { cartId: string }
}
export type RemoveItemHandler<T extends CustomerCardTypes = CustomerCardTypes> =
RemoveItemHook<T> & {
body: { cartId: string }
}
export type CustomerCardHandlers<
T extends CustomerCardTypes = CustomerCardTypes
> = {
getCards: GetCardsHook<T>
addItem: AddItemHandler<T>
updateItem: UpdateItemHandler<T>
removeItem: RemoveItemHandler<T>
export interface CustomerCardHooks {
getCards: GetCardsHook
addItem: AddItemHook
updateItem: UpdateItemHook
removeItem: RemoveItemHook
}
export type CustomerCardSchema<
T extends CustomerCardTypes = CustomerCardTypes
> = {
/**
* Customer card API handlers.
*/
export type AddItemHandler = AddItemHook & {
body: { cartId: string }
}
export type UpdateItemHandler = UpdateItemHook & {
data: Card
body: { cartId: string }
}
export type RemoveItemHandler = RemoveItemHook & {
body: { cartId: string }
}
export type CustomerCardHandlers = {
getCards: GetCardsHook
addItem: AddItemHandler
updateItem: UpdateItemHandler
removeItem: RemoveItemHandler
}
export type CustomerCardSchema = {
endpoint: {
options: {}
handlers: CustomerCardHandlers<T>
handlers: CustomerCardHandlers
}
}

View File

@ -1,24 +1,53 @@
export * as Card from './card'
export * as Address from './address'
// TODO: define this type
export type Customer = any
export type CustomerTypes = {
customer: Customer
export interface Customer {
/**
* The unique identifier for the customer.
*/
id: string
/**
* The customer's first name.
*/
firstName: string
/**
* The customer's last name.
*/
lastName: string
/**
* The customer's email address.
*/
email?: string
/**
* The customer's phone number.
* @optional
*/
phone?: string
/**
* The customer's company name.
*/
company?: string
/**
* The customer's notes.
*/
notes?: string
/**
* Indicates wathever the customer accepts marketing, such as email newsletters.
*/
acceptsMarketing?: boolean
}
export type CustomerHook<T extends CustomerTypes = CustomerTypes> = {
data: T['customer'] | null
fetchData: { customer: T['customer'] } | null
export type CustomerHook = {
data: Customer | null | undefined
fetchData: { customer: Customer } | null
}
export type CustomerSchema<T extends CustomerTypes = CustomerTypes> = {
export type CustomerSchema = {
endpoint: {
options: {}
handlers: {
getLoggedInCustomer: {
data: { customer: T['customer'] } | null
data: { customer: Customer } | null
}
}
}

View File

@ -1,24 +1,26 @@
export type LoginBody = {
export interface LoginBody {
/**
* The user's email address.
*/
email: string
/**
* The user's password.
*/
password: string
}
export type LoginTypes = {
body: LoginBody
}
export type LoginHook<T extends LoginTypes = LoginTypes> = {
export type LoginHook = {
data: null
actionInput: LoginBody
fetcherInput: LoginBody
body: T['body']
body: LoginBody
}
export type LoginSchema<T extends LoginTypes = LoginTypes> = {
export type LoginSchema = {
endpoint: {
options: {}
handlers: {
login: LoginHook<T>
login: LoginHook
}
}
}

View File

@ -1,17 +1,15 @@
export type LogoutTypes = {
body: { redirectTo?: string }
}
export type LogoutHook<T extends LogoutTypes = LogoutTypes> = {
export type LogoutHook = {
data: null
body: T['body']
body: {
redirectTo?: string
}
}
export type LogoutSchema<T extends LogoutTypes = LogoutTypes> = {
export type LogoutSchema = {
endpoint: {
options: {}
handlers: {
logout: LogoutHook<T>
logout: LogoutHook
}
}
}

View File

@ -1,28 +1,43 @@
// TODO: define this type
export type Page = {
// ID of the Web page.
/**
* The unique identifier for the page.
*/
id: string
// Page name, as displayed on the storefront.
/**
* Page name, as displayed on the storefront.
*/
name: string
// Relative URL on the storefront for this page.
/**
* Relative URL on the storefront for this page.
*/
url?: string
// HTML or variable that populates this pages `<body>` element, in default/desktop view. Required in POST if page type is `raw`.
/**
* HTML or variable that populates this pages `<body>` element, in default/desktop view. Required in POST if page type is `raw`.
*/
body: string
// If true, this page appears in the storefronts navigation menu.
/**
* If true, this page appears in the storefronts navigation menu.
*/
is_visible?: boolean
// Order in which this page should display on the storefront. (Lower integers specify earlier display.)
/**
* Order in which this page should display on the storefront. (Lower integers specify earlier display.)
*/
sort_order?: number
}
export type PageTypes = {
page: Page
/**
* Operation to get all pages.
*/
export type GetAllPagesOperation = {
data: { pages: Page[] }
}
export type GetAllPagesOperation<T extends PageTypes = PageTypes> = {
data: { pages: T['page'][] }
}
export type GetPageOperation<T extends PageTypes = PageTypes> = {
data: { page?: T['page'] }
variables: { id: string }
export type GetPageOperation = {
data: { page?: Page }
variables: {
/**
* The unique identifier of the page.
*/
id: string
}
}

View File

@ -1,91 +1,207 @@
export type ProductImage = {
url: string
alt?: string
}
import { Image } from './common'
export type ProductPrice = {
export interface ProductPrice {
/**
* The price after all discounts are applied.
*/
value: number
/**
* The currency code for the price. This is a 3-letter ISO 4217 code.
* @example USD
*/
currencyCode?: 'USD' | 'EUR' | 'ARS' | 'GBP' | string
/**
* The retail price of the product. This can be used to mark a product as on sale, when `retailPrice` is higher than the price a.k.a `value`.
*/
retailPrice?: number
salePrice?: number
listPrice?: number
extendedSalePrice?: number
extendedListPrice?: number
}
export type ProductOption = {
export interface ProductOption {
__typename?: 'MultipleChoiceOption'
/**
* The unique identifier for the option.
*/
id: string
/**
* The product options name.
* @example `Color` or `Size`
*/
displayName: string
/**
* List of option values.
* @example `["Red", "Green", "Blue"]`
*/
values: ProductOptionValues[]
}
export type ProductOptionValues = {
export interface ProductOptionValues {
/**
* A string that uniquely identifies the option value.
*/
label: string
/**
* List of hex colors used to display the actual colors in the swatches instead of the name.
*/
hexColors?: string[]
}
export type ProductVariant = {
id: string | number
export interface ProductVariant {
/**
* The unique identifier for the variant.
*/
id: string
/**
* The SKU (stock keeping unit) associated with the product variant.
*/
sku?: string
/**
* The product variants name, or the product's name.
*/
name?: string
/**
* List of product options.
*/
options: ProductOption[]
/**
* The product variants price after all discounts are applied.
*/
price?: ProductPrice
/**
* The retail price of the product. This can be used to mark a product as on sale, when `retailPrice` is higher than the `price`.
*/
retailPrice?: ProductPrice
/**
* Indicates if the variant is available for sale.
*/
availableForSale?: boolean
/**
* Whether a customer needs to provide a shipping address when placing an order for the product variant.
*/
requiresShipping?: boolean
/**
* The image associated with the variant.
*/
image?: Image
}
export type Product = {
export interface Product {
/**
* The unique identifier for the product.
*/
id: string
/**
* The name of the product.
*/
name: string
/**
* Stripped description of the product, single line.
*/
description: string
/**
* The description of the product, complete with HTML formatting.
*/
descriptionHtml?: string
/**
* The SKU (stock keeping unit) associated with the product.
*/
sku?: string
/**
* A human-friendly unique string for the product, automatically generated from its title.
*/
slug?: string
/**
* Relative URL on the storefront for the product.
*/
path?: string
images: ProductImage[]
/**
* List of images associated with the product.
*/
images: Image[]
/**
* List of the products variants.
*/
variants: ProductVariant[]
/**
* The product's base price. Could be the minimum value, or default variant price.
*/
price: ProductPrice
/**
* List of product's options.
*/
options: ProductOption[]
/**
* The products vendor name.
*/
vendor?: string
}
export type SearchProductsBody = {
export interface SearchProductsBody {
/**
* The search query string to filter the products by.
*/
search?: string
categoryId?: string | number
brandId?: string | number
/**
* The category ID to filter the products by.
*/
categoryId?: string
/**
* The brand ID to filter the products by.
*/
brandId?: string
/**
* The sort key to sort the products by.
* @example 'trending-desc' | 'latest-desc' | 'price-asc' | 'price-desc'
*/
sort?: string
/**
* The locale code, used to localize the product data (if the provider supports it).
*/
locale?: string
}
export type ProductTypes = {
product: Product
searchBody: SearchProductsBody
}
export type SearchProductsHook<T extends ProductTypes = ProductTypes> = {
/**
* Fetches a list of products based on the given search criteria.
*/
export type SearchProductsHook = {
data: {
products: T['product'][]
/**
* List of products matching the query.
*/
products: Product[]
/**
* Indicates if there are any products matching the query.
*/
found: boolean
}
body: T['searchBody']
input: T['searchBody']
fetcherInput: T['searchBody']
body: SearchProductsBody
input: SearchProductsBody
fetcherInput: SearchProductsBody
}
export type ProductsSchema<T extends ProductTypes = ProductTypes> = {
/**
* Product API schema
*/
export type ProductsSchema = {
endpoint: {
options: {}
handlers: {
getProducts: SearchProductsHook<T>
getProducts: SearchProductsHook
}
}
}
export type GetAllProductPathsOperation<T extends ProductTypes = ProductTypes> =
{
data: { products: Pick<T['product'], 'path'>[] }
variables: { first?: number }
}
/**
* Product operations
*/
export type GetAllProductsOperation<T extends ProductTypes = ProductTypes> = {
data: { products: T['product'][] }
export type GetAllProductPathsOperation = {
data: { products: Pick<Product, 'path'>[] }
variables: { first?: number }
}
export type GetAllProductsOperation = {
data: { products: Product[] }
variables: {
relevance?: 'featured' | 'best_selling' | 'newest'
ids?: string[]
@ -93,7 +209,7 @@ export type GetAllProductsOperation<T extends ProductTypes = ProductTypes> = {
}
}
export type GetProductOperation<T extends ProductTypes = ProductTypes> = {
data: { product?: T['product'] }
export type GetProductOperation = {
data: { product?: Product }
variables: { path: string; slug?: never } | { path?: never; slug: string }
}

View File

@ -1,26 +1,34 @@
export type SignupBody = {
export interface SignupBody {
/**
* The user's first name.
*/
firstName: string
/**
* The user's last name.
*/
lastName: string
/**
* The user's email address.
*/
email: string
/**
* The user's password.
*/
password: string
}
export type SignupTypes = {
body: SignupBody
}
export type SignupHook<T extends SignupTypes = SignupTypes> = {
export type SignupHook = {
data: null
body: T['body']
actionInput: T['body']
fetcherInput: T['body']
body: SignupBody
actionInput: SignupBody
fetcherInput: SignupBody
}
export type SignupSchema<T extends SignupTypes = SignupTypes> = {
export type SignupSchema = {
endpoint: {
options: {}
handlers: {
signup: SignupHook<T>
signup: SignupHook
}
}
}

View File

@ -1,20 +1,51 @@
export type Category = {
export interface Category {
/**
* Unique identifier for the category.
*/
id: string
/**
* Name of the category.
*/
name: string
/**
* A human-friendly unique string for the category, automatically generated from its name.
* @example "t-shirts"
*/
slug: string
/**
* Relative URL on the storefront for the category.
* @example /t-shirts
*/
path: string
}
export type Brand = any
export type SiteTypes = {
category: Category
brand: Brand
export interface Brand {
/**
* Unique identifier for the brand.
*/
id: string
/**
* Name of the brand.
*/
name: string
/**
* A human-friendly unique string for the category, automatically generated from its name.
* @example "acme"
*/
slug: string
/**
* Relative URL on the storefront for this brand.
* @example "/acme"
*/
path: string
}
export type GetSiteInfoOperation<T extends SiteTypes = SiteTypes> = {
/**
* Operation to get site information. This includes categories and brands.
*/
export type GetSiteInfoOperation = {
data: {
categories: T['category'][]
brands: T['brand'][]
categories: Category[]
brands: Brand[]
}
}

View File

@ -1,60 +1,97 @@
// TODO: define this type
export type Wishlist = any
import { Product } from './product'
export type WishlistItemBody = {
variantId: string | number
export interface WishlistItem {
/**
* The unique identifier for the item.
*/
id: string
/**
* The unique identifier for the product associated with the wishlist item.
*/
productId: string
/**
* The unique identifier for the product variant associated with the wishlist item.
*/
variantId: string
/**
* The product associated with the wishlist item.
*/
product: Product
}
export type WishlistTypes = {
wishlist: Wishlist
itemBody: WishlistItemBody
export interface Wishlist {
/**
* The unique identifier for the wishlist.
*/
id: string
/**
* List of items in the wishlist.
*/
items: WishlistItem[]
/**
* Some providers require a token to add an item to a wishlist
*/
token?: string
}
export type GetWishlistHook<T extends WishlistTypes = WishlistTypes> = {
data: T['wishlist'] | null
export interface WishlistItemBody {
/**
* The unique identifier for the product variant to associate with the wishlist.
*/
variantId: string
/**
* The unique identifier for the product to associate with the wishlist.
*/
productId: string
/**
* Some providers require to provide a token to make a request
*/
wishlistToken?: string
}
export type GetWishlistHook = {
data: Wishlist | null | undefined
body: { includeProducts?: boolean }
input: { includeProducts?: boolean }
fetcherInput: { customerId: string; includeProducts?: boolean }
swrState: { isEmpty: boolean }
}
export type AddItemHook<T extends WishlistTypes = WishlistTypes> = {
data: T['wishlist']
body: { item: T['itemBody'] }
fetcherInput: { item: T['itemBody'] }
actionInput: T['itemBody']
export type AddItemHook = {
data: Wishlist | null | undefined
body: { item: WishlistItemBody }
fetcherInput: { item: WishlistItemBody }
actionInput: WishlistItemBody
}
export type RemoveItemHook<T extends WishlistTypes = WishlistTypes> = {
data: T['wishlist'] | null
body: { itemId: string }
fetcherInput: { itemId: string }
export type RemoveItemHook = {
data: Wishlist | null | undefined
body: { itemId: string; wishlistToken?: string }
fetcherInput: { itemId: string; wishlistToken?: string }
actionInput: { id: string }
input: { wishlist?: { includeProducts?: boolean } }
}
export type WishlistSchema<T extends WishlistTypes = WishlistTypes> = {
export type WishlistSchema = {
endpoint: {
options: {}
handlers: {
getWishlist: GetWishlistHook<T> & {
data: T['wishlist'] | null
getWishlist: GetWishlistHook & {
data: Wishlist | null
body: { customerToken?: string }
}
addItem: AddItemHook<T> & {
addItem: AddItemHook & {
body: { customerToken?: string }
}
removeItem: RemoveItemHook<T> & {
removeItem: RemoveItemHook & {
body: { customerToken?: string }
}
}
}
}
export type GetCustomerWishlistOperation<
T extends WishlistTypes = WishlistTypes
> = {
data: { wishlist?: T['wishlist'] }
export type GetCustomerWishlistOperation = {
data: { wishlist?: Wishlist }
variables: { customerId: string }
}

View File

@ -5,7 +5,7 @@ import type { AddItemHook } from '../types/wishlist'
import type { Provider } from '..'
export type UseAddItem<
H extends MutationHook<AddItemHook<any>> = MutationHook<AddItemHook>
H extends MutationHook<AddItemHook> = MutationHook<AddItemHook>
> = ReturnType<H['useHook']>
export const fetcher = mutationFetcher

View File

@ -5,7 +5,7 @@ import type { RemoveItemHook } from '../types/wishlist'
import type { Provider } from '..'
export type UseRemoveItem<
H extends MutationHook<RemoveItemHook<any>> = MutationHook<RemoveItemHook>
H extends MutationHook<RemoveItemHook> = MutationHook<RemoveItemHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<RemoveItemHook> = mutationFetcher

View File

@ -5,7 +5,7 @@ import type { GetWishlistHook } from '../types/wishlist'
import type { Provider } from '..'
export type UseWishlist<
H extends SWRHook<GetWishlistHook<any>> = SWRHook<GetWishlistHook>
H extends SWRHook<GetWishlistHook> = SWRHook<GetWishlistHook>
> = ReturnType<H['useHook']>
export const fetcher: HookFetcherFn<GetWishlistHook> = SWRFetcher

View File

@ -1,6 +1,6 @@
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
import checkoutEndpoint from '@vercel/commerce/api/endpoints/checkout'
import type { CheckoutSchema } from '../../../types/checkout'
import type { CheckoutSchema } from '@vercel/commerce/types/checkout'
import type { CommercejsAPI } from '../..'
import submitCheckout from './submit-checkout'

View File

@ -1,6 +1,6 @@
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
import loginEndpoint from '@vercel/commerce/api/endpoints/login'
import type { LoginSchema } from '../../../types/login'
import type { LoginSchema } from '@vercel/commerce/types/login'
import type { CommercejsAPI } from '../..'
import login from './login'

View File

@ -1,5 +1,5 @@
import type { CommercejsConfig } from '..'
import { GetAllPagesOperation } from '../../types/page'
import { GetAllPagesOperation } from '@vercel/commerce/types/page'
export type Page = { url: string }
export type GetAllPagesResult = { pages: Page[] }

View File

@ -1,9 +1,5 @@
import type { OperationContext } from '@vercel/commerce/api/operations'
import type {
GetAllProductPathsOperation,
CommercejsProduct,
} from '../../types/product'
import type { GetAllProductPathsOperation } from '@vercel/commerce/types/product'
import type { CommercejsConfig, Provider } from '..'
export type GetAllProductPathsResult = {
@ -22,7 +18,7 @@ export default function getAllProductPathsOperation({
const { data } = await sdkFetch('products', 'list')
// Match a path for every product retrieved
const productPaths = data.map(({ permalink }: CommercejsProduct) => ({
const productPaths = data.map(({ permalink }: { permalink: string }) => ({
path: `/${permalink}`,
}))

View File

@ -1,5 +1,5 @@
import type { OperationContext } from '@vercel/commerce/api/operations'
import type { GetAllProductsOperation } from '../../types/product'
import type { GetAllProductsOperation } from '@vercel/commerce/types/product'
import type { CommercejsConfig, Provider } from '../index'
import { normalizeProduct } from '../../utils/normalize-product'

View File

@ -1,4 +1,4 @@
import { GetPageOperation } from '../../types/page'
import { GetPageOperation } from '@vercel/commerce/types/page'
export type Page = any
export type GetPageResult = { page?: Page }

Some files were not shown because too many files have changed in this diff Show More