mirror of
https://github.com/vercel/commerce.git
synced 2025-05-18 07:26:59 +00:00
added appibase provider
This commit is contained in:
parent
52b25b85d6
commit
244ad904f7
1
packages/appibase/.env.template
Normal file
1
packages/appibase/.env.template
Normal file
@ -0,0 +1 @@
|
||||
COMMERCE_PROVIDER=appibase
|
2
packages/appibase/.prettierignore
Normal file
2
packages/appibase/.prettierignore
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
dist
|
6
packages/appibase/.prettierrc
Normal file
6
packages/appibase/.prettierrc
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false
|
||||
}
|
1
packages/appibase/README.md
Normal file
1
packages/appibase/README.md
Normal file
@ -0,0 +1 @@
|
||||
# Next.js Local Provider
|
18
packages/appibase/package-lock.json
generated
Normal file
18
packages/appibase/package-lock.json
generated
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "appibase",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"js-cookies": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/js-cookies/-/js-cookies-1.0.4.tgz",
|
||||
"integrity": "sha1-1G5XbEIP9tVULA9SttTvfWN+dU4="
|
||||
},
|
||||
"urijs": {
|
||||
"version": "1.19.10",
|
||||
"resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.10.tgz",
|
||||
"integrity": "sha512-EzauQlgKuJgsXOqoMrCiePBf4At5jVqRhXykF3Wfb8ZsOBMxPcfiVBcsHXug4Aepb/ICm2PIgqAUGMelgdrWEg=="
|
||||
}
|
||||
}
|
||||
}
|
84
packages/appibase/package.json
Normal file
84
packages/appibase/package.json
Normal file
@ -0,0 +1,84 @@
|
||||
{
|
||||
"name": "appibase",
|
||||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"release": "taskr release",
|
||||
"build": "taskr build",
|
||||
"dev": "taskr",
|
||||
"types": "tsc --emitDeclarationOnly",
|
||||
"prettier-fix": "prettier --write ."
|
||||
},
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./*": [
|
||||
"./dist/*.js",
|
||||
"./dist/*/index.js"
|
||||
],
|
||||
"./next.config": "./dist/next.config.cjs"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"*": [
|
||||
"src/*",
|
||||
"src/*/index"
|
||||
],
|
||||
"next.config": [
|
||||
"dist/next.config.d.cts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"publishConfig": {
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"*": [
|
||||
"dist/*.d.ts",
|
||||
"dist/*/index.d.ts"
|
||||
],
|
||||
"next.config": [
|
||||
"dist/next.config.d.cts"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/commerce": "^0.0.1",
|
||||
"@vercel/fetch": "^6.1.1",
|
||||
"deserialize-json-api": "^1.4.0",
|
||||
"js-cookies": "^1.0.4",
|
||||
"json-api-response-converter": "^1.6.0",
|
||||
"kitsu": "^10.0.0-alpha.22",
|
||||
"urijs": "^1.19.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"next": "^12",
|
||||
"react": "^17",
|
||||
"react-dom": "^17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@taskr/clear": "^1.1.0",
|
||||
"@taskr/esnext": "^1.1.0",
|
||||
"@taskr/watch": "^1.1.0",
|
||||
"@types/node": "^17.0.8",
|
||||
"@types/react": "^17.0.38",
|
||||
"lint-staged": "^12.1.7",
|
||||
"next": "^12.0.8",
|
||||
"prettier": "^2.5.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"taskr": "^1.1.0",
|
||||
"taskr-swc": "^0.0.1",
|
||||
"typescript": "^4.5.4"
|
||||
},
|
||||
"lint-staged": {
|
||||
"**/*.{js,jsx,ts,tsx,json}": [
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
}
|
||||
}
|
1
packages/appibase/src/api/endpoints/cart/index.ts
Normal file
1
packages/appibase/src/api/endpoints/cart/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function noopApi(...args: any[]): void {}
|
1
packages/appibase/src/api/endpoints/catalog/index.ts
Normal file
1
packages/appibase/src/api/endpoints/catalog/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function noopApi(...args: any[]): void {}
|
1
packages/appibase/src/api/endpoints/catalog/products.ts
Normal file
1
packages/appibase/src/api/endpoints/catalog/products.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function noopApi(...args: any[]): void {}
|
1
packages/appibase/src/api/endpoints/checkout/index.ts
Normal file
1
packages/appibase/src/api/endpoints/checkout/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function noopApi(...args: any[]): void {}
|
1
packages/appibase/src/api/endpoints/customer/address.ts
Normal file
1
packages/appibase/src/api/endpoints/customer/address.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function noopApi(...args: any[]): void {}
|
1
packages/appibase/src/api/endpoints/customer/card.ts
Normal file
1
packages/appibase/src/api/endpoints/customer/card.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function noopApi(...args: any[]): void {}
|
1
packages/appibase/src/api/endpoints/customer/index.ts
Normal file
1
packages/appibase/src/api/endpoints/customer/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function noopApi(...args: any[]): void {}
|
1
packages/appibase/src/api/endpoints/login/index.ts
Normal file
1
packages/appibase/src/api/endpoints/login/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function noopApi(...args: any[]): void {}
|
1
packages/appibase/src/api/endpoints/logout/index.ts
Normal file
1
packages/appibase/src/api/endpoints/logout/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function noopApi(...args: any[]): void {}
|
1
packages/appibase/src/api/endpoints/signup/index.ts
Normal file
1
packages/appibase/src/api/endpoints/signup/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function noopApi(...args: any[]): void {}
|
1
packages/appibase/src/api/endpoints/wishlist/index.tsx
Normal file
1
packages/appibase/src/api/endpoints/wishlist/index.tsx
Normal file
@ -0,0 +1 @@
|
||||
export default function noopApi(...args: any[]): void {}
|
43
packages/appibase/src/api/index.ts
Normal file
43
packages/appibase/src/api/index.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import type { CommerceAPI, CommerceAPIConfig } from '@vercel/commerce/api'
|
||||
import { getCommerceApi as commerceApi } from '@vercel/commerce/api'
|
||||
import createFetcher from './utils/fetch-appibase'
|
||||
import { API_URL } from '../const'
|
||||
|
||||
import getAllPages from './operations/get-all-pages'
|
||||
import getPage from './operations/get-page'
|
||||
import getSiteInfo from './operations/get-site-info'
|
||||
import getCustomerWishlist from './operations/get-customer-wishlist'
|
||||
import getAllProductPaths from './operations/get-all-product-paths'
|
||||
import getAllProducts from './operations/get-all-products'
|
||||
import getProduct from './operations/get-product'
|
||||
|
||||
export interface LocalConfig extends CommerceAPIConfig {}
|
||||
const config: LocalConfig = {
|
||||
commerceUrl: API_URL,
|
||||
apiToken: '',
|
||||
cartCookie: '',
|
||||
customerCookie: '',
|
||||
cartCookieMaxAge: 2592000,
|
||||
fetch: createFetcher(() => getCommerceApi().getConfig()),
|
||||
}
|
||||
|
||||
const operations = {
|
||||
getAllPages,
|
||||
getPage,
|
||||
getSiteInfo,
|
||||
getCustomerWishlist,
|
||||
getAllProductPaths,
|
||||
getAllProducts,
|
||||
getProduct,
|
||||
}
|
||||
|
||||
export const provider = { config, operations }
|
||||
|
||||
export type Provider = typeof provider
|
||||
export type LocalAPI<P extends Provider = Provider> = CommerceAPI<P | any>
|
||||
|
||||
export function getCommerceApi<P extends Provider>(
|
||||
customProvider: P = provider as any
|
||||
): LocalAPI<P> {
|
||||
return commerceApi(customProvider as any)
|
||||
}
|
21
packages/appibase/src/api/operations/get-all-pages.ts
Normal file
21
packages/appibase/src/api/operations/get-all-pages.ts
Normal file
@ -0,0 +1,21 @@
|
||||
export type Page = { url: string }
|
||||
export type GetAllPagesResult = { pages: Page[] }
|
||||
import type { LocalConfig } from '../index'
|
||||
|
||||
export default function getAllPagesOperation() {
|
||||
function getAllPages({
|
||||
config,
|
||||
preview,
|
||||
}: {
|
||||
url?: string
|
||||
config?: Partial<LocalConfig>
|
||||
preview?: boolean
|
||||
}): Promise<GetAllPagesResult> {
|
||||
console.log('GETTING ALL PAGES');
|
||||
|
||||
return Promise.resolve({
|
||||
pages: [],
|
||||
})
|
||||
}
|
||||
return getAllPages
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import data from '../../data.json'
|
||||
|
||||
export type GetAllProductPathsResult = {
|
||||
products: Array<{ path: string }>
|
||||
}
|
||||
|
||||
export default function getAllProductPathsOperation() {
|
||||
function getAllProductPaths(): Promise<GetAllProductPathsResult> {
|
||||
console.log('GETTING ALL PATHS');
|
||||
|
||||
return Promise.resolve({
|
||||
products: [] //data.products.map(({ path }) => ({ path })),
|
||||
})
|
||||
}
|
||||
|
||||
return getAllProductPaths
|
||||
}
|
33
packages/appibase/src/api/operations/get-all-products.ts
Normal file
33
packages/appibase/src/api/operations/get-all-products.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { Product } from '@vercel/commerce/types/product'
|
||||
import { GetAllProductsOperation } from '@vercel/commerce/types/product'
|
||||
import type { OperationContext } from '@vercel/commerce/api/operations'
|
||||
import type { LocalConfig, Provider } from '../index'
|
||||
import { NormalizeProduct } from '../utils/normalize'
|
||||
import type { AppibaseProduct } from '../../types'
|
||||
|
||||
export default function getAllProductsOperation({
|
||||
commerce,
|
||||
}: OperationContext<any>) {
|
||||
async function getAllProducts<T extends GetAllProductsOperation>({
|
||||
query = '',
|
||||
variables,
|
||||
config,
|
||||
}: {
|
||||
query?: string
|
||||
variables?: T['variables']
|
||||
config?: Partial<LocalConfig>
|
||||
preview?: boolean
|
||||
} = {}): Promise<{ products: Product[] | any[] }> {
|
||||
|
||||
const { fetch } = commerce.getConfig(config)
|
||||
|
||||
const { data: fetchedProducts } = await fetch('/products?filter[is_parent_true]=true&include=prices,options');
|
||||
|
||||
const products = fetchedProducts.map((p : AppibaseProduct) => <Product> NormalizeProduct(p))
|
||||
|
||||
return {
|
||||
products
|
||||
}
|
||||
}
|
||||
return getAllProducts
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
export default function getCustomerWishlistOperation() {
|
||||
function getCustomerWishlist(): any {
|
||||
return { wishlist: {} }
|
||||
}
|
||||
return getCustomerWishlist
|
||||
}
|
15
packages/appibase/src/api/operations/get-page.ts
Normal file
15
packages/appibase/src/api/operations/get-page.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export type Page = any
|
||||
export type GetPageResult = { page?: Page }
|
||||
|
||||
export type PageVariables = {
|
||||
id: number
|
||||
}
|
||||
|
||||
export default function getPageOperation() {
|
||||
function getPage(): Promise<GetPageResult> {
|
||||
console.log('GETTING PAGE');
|
||||
|
||||
return Promise.resolve({})
|
||||
}
|
||||
return getPage
|
||||
}
|
29
packages/appibase/src/api/operations/get-product.ts
Normal file
29
packages/appibase/src/api/operations/get-product.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import type { LocalConfig } from '../index'
|
||||
import { Product } from '@vercel/commerce/types/product'
|
||||
import { GetProductOperation } from '@vercel/commerce/types/product'
|
||||
import type { OperationContext } from '@vercel/commerce/api/operations'
|
||||
import { NormalizeProduct } from '../utils/normalize'
|
||||
|
||||
export default function getProductOperation({
|
||||
commerce,
|
||||
}: OperationContext<any>) {
|
||||
async function getProduct<T extends GetProductOperation>({
|
||||
query = '',
|
||||
variables,
|
||||
config,
|
||||
}: {
|
||||
query?: string
|
||||
variables?: T['variables']
|
||||
config?: Partial<LocalConfig>
|
||||
preview?: boolean
|
||||
} = {}): Promise<Product | {} | any> {
|
||||
const { fetch } = commerce.getConfig(config)
|
||||
const { data: fetchedProduct } = await fetch(`/products/${variables?.slug}?include=products,variations,options,prices,stock_items,variations.variation_options`);
|
||||
|
||||
return {
|
||||
product: NormalizeProduct(fetchedProduct),
|
||||
}
|
||||
}
|
||||
|
||||
return getProduct
|
||||
}
|
38
packages/appibase/src/api/operations/get-site-info.ts
Normal file
38
packages/appibase/src/api/operations/get-site-info.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { OperationContext } from '@vercel/commerce/api/operations'
|
||||
import { Category } from '@vercel/commerce/types/site'
|
||||
import { NormalizeCategory } from '../utils/normalize'
|
||||
import { LocalConfig } from '../index'
|
||||
import { AppibaseCollection } from '../../types'
|
||||
|
||||
export type GetSiteInfoResult<
|
||||
T extends { categories: any[]; brands: any[] } = {
|
||||
categories: Category[]
|
||||
brands: any[]
|
||||
}
|
||||
> = T
|
||||
|
||||
export default function getSiteInfoOperation({ commerce }: OperationContext<any>) {
|
||||
async function getSiteInfo({
|
||||
query,
|
||||
variables,
|
||||
config,
|
||||
}: {
|
||||
query?: string
|
||||
variables?: any
|
||||
config?: Partial<LocalConfig>
|
||||
preview?: boolean
|
||||
} = {}): Promise<GetSiteInfoResult> {
|
||||
|
||||
const { fetch } = commerce.getConfig(config)
|
||||
|
||||
const { res : { data } } = await fetch('/collections');
|
||||
const categories = data.map((p : AppibaseCollection) => <Category> NormalizeCategory(p))
|
||||
|
||||
return Promise.resolve({
|
||||
categories,
|
||||
brands: [],
|
||||
})
|
||||
}
|
||||
|
||||
return getSiteInfo
|
||||
}
|
6
packages/appibase/src/api/operations/index.ts
Normal file
6
packages/appibase/src/api/operations/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export { default as getPage } from './get-page'
|
||||
export { default as getSiteInfo } from './get-site-info'
|
||||
export { default as getAllPages } from './get-all-pages'
|
||||
export { default as getProduct } from './get-product'
|
||||
export { default as getAllProducts } from './get-all-products'
|
||||
export { default as getAllProductPaths } from './get-all-product-paths'
|
35
packages/appibase/src/api/utils/access-token.ts
Normal file
35
packages/appibase/src/api/utils/access-token.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import Cookies from 'js-cookie'
|
||||
import Kitsu from "kitsu";
|
||||
import { API_URL, SCOPE, CLIENT_ID_STOREFRONT } from '../../const'
|
||||
|
||||
const GetAccessToken = async () => {
|
||||
|
||||
const fromCookies = Cookies.get('access_token');
|
||||
|
||||
if(!fromCookies) {
|
||||
const myHeaders = new Headers();
|
||||
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
|
||||
myHeaders.append("Authorization", `Basic ${Buffer.from(CLIENT_ID_STOREFRONT).toString('base64')}`);
|
||||
myHeaders.append("Accept", "application/json");
|
||||
|
||||
const urlencoded = new URLSearchParams();
|
||||
urlencoded.append("grant_type", "client_credentials");
|
||||
urlencoded.append("scope", SCOPE);
|
||||
|
||||
const requestOptions = {
|
||||
method: 'POST',
|
||||
headers: myHeaders,
|
||||
body: urlencoded,
|
||||
};
|
||||
|
||||
const response = await fetch(API_URL + "/oauth/token", requestOptions)
|
||||
const { access_token, expires_in } = await response.json()
|
||||
|
||||
Cookies.set('access_token', access_token, { expires: expires_in / 60 / 60 / 24 })
|
||||
return access_token
|
||||
}
|
||||
else return fromCookies
|
||||
}
|
||||
|
||||
|
||||
export { GetAccessToken }
|
28
packages/appibase/src/api/utils/fetch-appibase.ts
Normal file
28
packages/appibase/src/api/utils/fetch-appibase.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { FetcherError } from '@vercel/commerce/utils/errors'
|
||||
import type { GraphQLFetcher } from '@vercel/commerce/api'
|
||||
import Kitsu from "kitsu";
|
||||
import type { LocalConfig } from '../index'
|
||||
import { API_URL } from '../../const'
|
||||
import { GetAccessToken } from './access-token'
|
||||
|
||||
const fetchApi: (getConfig: () => LocalConfig) => GraphQLFetcher =
|
||||
(getConfig) =>
|
||||
async (query: string, { variables, preview } = {}, fetchOptions) => {
|
||||
const config = getConfig()
|
||||
|
||||
const api = new Kitsu({ baseURL: API_URL + '/api/v1' })
|
||||
api.headers.Authorization = `Bearer ${await GetAccessToken()}`
|
||||
|
||||
const res = await api.get(query)
|
||||
|
||||
if (res.errors) {
|
||||
throw new FetcherError({
|
||||
errors: res.errors ?? [{ message: 'Failed to fetch for API' }],
|
||||
status: res.status,
|
||||
})
|
||||
}
|
||||
|
||||
return { data: res.data, res }
|
||||
}
|
||||
|
||||
export default fetchApi
|
3
packages/appibase/src/api/utils/fetch.ts
Normal file
3
packages/appibase/src/api/utils/fetch.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import zeitFetch from '@vercel/fetch'
|
||||
|
||||
export default zeitFetch()
|
82
packages/appibase/src/api/utils/normalize.ts
Normal file
82
packages/appibase/src/api/utils/normalize.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import { Product, ProductOption, ProductImage } from '@vercel/commerce/types/product'
|
||||
import { Category } from '@vercel/commerce/types/site'
|
||||
import { Cart, LineItem } from '@vercel/commerce/types/cart'
|
||||
import type { AppibaseProduct, AppibaseCollection, AppibaseCart } from '../../types'
|
||||
|
||||
|
||||
const NormalizeProduct = (product: AppibaseProduct): Product => {
|
||||
const options: ProductOption[] = [];
|
||||
|
||||
for(const variation of (product.variations?.data || [])) {
|
||||
const option : ProductOption | undefined = options.find(o => o.displayName === variation.name);
|
||||
if(!option) {
|
||||
options.push({
|
||||
id: `option-${variation.name.toLowerCase()}`,
|
||||
displayName : variation.name, values: variation.options?.map(o => ({ label: o.name })) || []
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: product.id,
|
||||
name: product.name,
|
||||
description: product.description,
|
||||
images: product.image_urls.map(i => <ProductImage> { url: i }),
|
||||
sku: product.sku,
|
||||
slug: product.sku,
|
||||
variants: product.children?.data?.map(p => ({
|
||||
id: p.id,
|
||||
options: p.variation_options?.data.map(o => ({
|
||||
__typename: "MultipleChoiceOption",
|
||||
id: o.id,
|
||||
displayName: o.variation_name || "",
|
||||
values: [{ label: o.name || "" }]
|
||||
})) || []
|
||||
})) || [],
|
||||
price: { value: product.prices.data[0].amount.float, currencyCode: product.prices.data[0].currency },
|
||||
options
|
||||
}
|
||||
}
|
||||
|
||||
const NormalizeCategory = (collection: AppibaseCollection): Category => {
|
||||
return {
|
||||
id: String(collection.id),
|
||||
name: collection.name,
|
||||
slug: collection.slug,
|
||||
path: '/' + collection.slug,
|
||||
}
|
||||
}
|
||||
|
||||
const NormalizeCart = (cart: AppibaseCart): Cart => {
|
||||
return {
|
||||
id: String(cart.id),
|
||||
createdAt: (new Date()).toDateString(),
|
||||
currency: {
|
||||
code: cart.currency
|
||||
},
|
||||
taxesIncluded: cart.tax_incl,
|
||||
lineItemsSubtotalPrice: cart.subtotal_amount.float,
|
||||
subtotalPrice: cart.subtotal_amount.float,
|
||||
totalPrice: cart.total_amount.float,
|
||||
lineItems: cart.cart_items?.data.map(i => ({
|
||||
id: i.id,
|
||||
variantId: i.id,
|
||||
productId: i.id,
|
||||
name: i.name,
|
||||
discounts: [],
|
||||
path: i.sku,
|
||||
variant: {
|
||||
sku: i.sku,
|
||||
id: i.id,
|
||||
name: i.name,
|
||||
requiresShipping: true,
|
||||
price: i.price.float,
|
||||
listPrice: i.price.float,
|
||||
image: { url: i.image_url }
|
||||
},
|
||||
quantity: parseInt(i.quantity)
|
||||
})) || []
|
||||
}
|
||||
}
|
||||
|
||||
export { NormalizeProduct, NormalizeCategory, NormalizeCart }
|
3
packages/appibase/src/auth/index.ts
Normal file
3
packages/appibase/src/auth/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { default as useLogin } from './use-login'
|
||||
export { default as useLogout } from './use-logout'
|
||||
export { default as useSignup } from './use-signup'
|
16
packages/appibase/src/auth/use-login.tsx
Normal file
16
packages/appibase/src/auth/use-login.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import { MutationHook } from '@vercel/commerce/utils/types'
|
||||
import useLogin, { UseLogin } from '@vercel/commerce/auth/use-login'
|
||||
|
||||
export default useLogin as UseLogin<typeof handler>
|
||||
|
||||
export const handler: MutationHook<any> = {
|
||||
fetchOptions: {
|
||||
query: '',
|
||||
},
|
||||
async fetcher() {
|
||||
return null
|
||||
},
|
||||
useHook: () => () => {
|
||||
return async function () {}
|
||||
},
|
||||
}
|
17
packages/appibase/src/auth/use-logout.tsx
Normal file
17
packages/appibase/src/auth/use-logout.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { MutationHook } from '@vercel/commerce/utils/types'
|
||||
import useLogout, { UseLogout } from '@vercel/commerce/auth/use-logout'
|
||||
|
||||
export default useLogout as UseLogout<typeof handler>
|
||||
|
||||
export const handler: MutationHook<any> = {
|
||||
fetchOptions: {
|
||||
query: '',
|
||||
},
|
||||
async fetcher() {
|
||||
return null
|
||||
},
|
||||
useHook:
|
||||
({ fetch }) =>
|
||||
() =>
|
||||
async () => {},
|
||||
}
|
19
packages/appibase/src/auth/use-signup.tsx
Normal file
19
packages/appibase/src/auth/use-signup.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { useCallback } from 'react'
|
||||
import useCustomer from '../customer/use-customer'
|
||||
import { MutationHook } from '@vercel/commerce/utils/types'
|
||||
import useSignup, { UseSignup } from '@vercel/commerce/auth/use-signup'
|
||||
|
||||
export default useSignup as UseSignup<typeof handler>
|
||||
|
||||
export const handler: MutationHook<any> = {
|
||||
fetchOptions: {
|
||||
query: '',
|
||||
},
|
||||
async fetcher() {
|
||||
return null
|
||||
},
|
||||
useHook:
|
||||
({ fetch }) =>
|
||||
() =>
|
||||
() => {},
|
||||
}
|
4
packages/appibase/src/cart/index.ts
Normal file
4
packages/appibase/src/cart/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export { default as useCart } from './use-cart'
|
||||
export { default as useAddItem } from './use-add-item'
|
||||
export { default as useRemoveItem } from './use-remove-item'
|
||||
export { default as useUpdateItem } from './use-update-item'
|
39
packages/appibase/src/cart/use-add-item.tsx
Normal file
39
packages/appibase/src/cart/use-add-item.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
import { useCallback } from 'react';
|
||||
import useAddItem, { UseAddItem } from '@vercel/commerce/cart/use-add-item'
|
||||
import { MutationHook } from '@vercel/commerce/utils/types'
|
||||
import Cookies from 'js-cookie'
|
||||
import { NormalizeCart } from '../api/utils/normalize'
|
||||
|
||||
export default useAddItem as UseAddItem<typeof handler>
|
||||
export const handler: MutationHook<any> = {
|
||||
fetchOptions: {
|
||||
query: '',
|
||||
},
|
||||
async fetcher({ input: item, options, fetch }) {
|
||||
|
||||
const fromCookies = Cookies.get('cart_id');
|
||||
|
||||
await fetch({
|
||||
query : `/carts/${fromCookies}/cart_items`,
|
||||
method: 'POST',
|
||||
body: {
|
||||
quantity: 1,
|
||||
item: {
|
||||
data : {
|
||||
type: 'product',
|
||||
id: item.variantId
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return;
|
||||
},
|
||||
useHook:
|
||||
({ fetch }) =>
|
||||
() => {
|
||||
return useCallback(async function addItem(input) {
|
||||
const data = await fetch({ input });
|
||||
return data;
|
||||
}, [fetch]);
|
||||
},
|
||||
}
|
51
packages/appibase/src/cart/use-cart.tsx
Normal file
51
packages/appibase/src/cart/use-cart.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import { useMemo } from 'react'
|
||||
import { SWRHook } from '@vercel/commerce/utils/types'
|
||||
import useCart, { UseCart } from '@vercel/commerce/cart/use-cart'
|
||||
import Cookies from 'js-cookie'
|
||||
import { NormalizeCart } from '../api/utils/normalize'
|
||||
import { STORE_ID } from '../const'
|
||||
|
||||
export default useCart as UseCart<typeof handler>
|
||||
|
||||
export const handler: SWRHook<any> = {
|
||||
fetchOptions: {
|
||||
query: ''
|
||||
},
|
||||
async fetcher({ fetch }) {
|
||||
const fromCookies = Cookies.get('cart_id');
|
||||
|
||||
if(!fromCookies) {
|
||||
|
||||
const { data } = await fetch({ query : `/stores/${STORE_ID}/carts`, method: "POST", body: { id: '' } })
|
||||
|
||||
Cookies.set('cart_id', data.id)
|
||||
|
||||
return NormalizeCart(data)
|
||||
}
|
||||
|
||||
else {
|
||||
const { data } = await fetch({ query : `/carts/${fromCookies}?include=cart_items` })
|
||||
return NormalizeCart(data)
|
||||
}
|
||||
},
|
||||
useHook:
|
||||
({ useData }) =>
|
||||
(input) => {
|
||||
const response = useData({
|
||||
swrOptions: { revalidateOnFocus: true, ...input?.swrOptions },
|
||||
})
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
Object.create(response, {
|
||||
isEmpty: {
|
||||
get() {
|
||||
return (response.data?.lineItems.length ?? 0) <= 0
|
||||
},
|
||||
enumerable: true,
|
||||
},
|
||||
}),
|
||||
[response]
|
||||
)
|
||||
},
|
||||
}
|
40
packages/appibase/src/cart/use-remove-item.tsx
Normal file
40
packages/appibase/src/cart/use-remove-item.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { MutationHook } from '@vercel/commerce/utils/types'
|
||||
import { useCallback } from 'react'
|
||||
import useRemoveItem, {
|
||||
UseRemoveItem,
|
||||
} from '@vercel/commerce/cart/use-remove-item'
|
||||
import Cookies from 'js-cookie'
|
||||
import { NormalizeCart } from '../api/utils/normalize'
|
||||
import useCart from './use-cart'
|
||||
|
||||
export default useRemoveItem as UseRemoveItem<typeof handler>
|
||||
|
||||
export const handler: MutationHook<any> = {
|
||||
fetchOptions: {
|
||||
query: '',
|
||||
},
|
||||
async fetcher({ input: item, options, fetch }) {
|
||||
await fetch({
|
||||
query : `cart_items`,
|
||||
method: 'DELETE',
|
||||
body: item.id
|
||||
})
|
||||
|
||||
const fromCookies = Cookies.get('cart_id');
|
||||
const { data } = await fetch({ query : `/carts/${fromCookies}?include=cart_items` })
|
||||
return NormalizeCart(data)
|
||||
},
|
||||
useHook:
|
||||
({ fetch }) =>
|
||||
() => {
|
||||
const { mutate } = useCart()
|
||||
|
||||
return useCallback(async function removeItem(input) {
|
||||
const data = await fetch({ input });
|
||||
|
||||
await mutate(data, true)
|
||||
|
||||
return data;
|
||||
}, [fetch, mutate]);
|
||||
},
|
||||
}
|
45
packages/appibase/src/cart/use-update-item.tsx
Normal file
45
packages/appibase/src/cart/use-update-item.tsx
Normal file
@ -0,0 +1,45 @@
|
||||
import { MutationHook } from '@vercel/commerce/utils/types'
|
||||
import { useCallback } from 'react'
|
||||
import useUpdateItem, {
|
||||
UseUpdateItem,
|
||||
} from '@vercel/commerce/cart/use-update-item'
|
||||
import { NormalizeCart } from '../api/utils/normalize'
|
||||
import Cookies from 'js-cookie'
|
||||
import useCart from './use-cart'
|
||||
|
||||
export default useUpdateItem as UseUpdateItem<any>
|
||||
|
||||
export const handler: MutationHook<any> = {
|
||||
fetchOptions: {
|
||||
query: '',
|
||||
},
|
||||
async fetcher({ input, options, fetch }) {
|
||||
await fetch({
|
||||
query : `cart_items`,
|
||||
method: 'PATCH',
|
||||
body: {
|
||||
id: input.item.id,
|
||||
type: 'cart_item',
|
||||
quantity: String(input.quantity)
|
||||
}
|
||||
})
|
||||
|
||||
const fromCookies = Cookies.get('cart_id');
|
||||
const { data } = await fetch({ query : `/carts/${fromCookies}?include=cart_items` })
|
||||
return NormalizeCart(data)
|
||||
|
||||
},
|
||||
useHook:
|
||||
({ fetch }) =>
|
||||
({ item }) => {
|
||||
const { mutate } = useCart()
|
||||
|
||||
return useCallback(async function updateItem(input) {
|
||||
const data = await fetch({ input: { item, quantity: input.quantity } });
|
||||
|
||||
await mutate(data, true)
|
||||
|
||||
return data;
|
||||
}, [fetch, mutate]);
|
||||
},
|
||||
}
|
16
packages/appibase/src/checkout/use-checkout.tsx
Normal file
16
packages/appibase/src/checkout/use-checkout.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import { SWRHook } from '@vercel/commerce/utils/types'
|
||||
import useCheckout, {
|
||||
UseCheckout,
|
||||
} from '@vercel/commerce/checkout/use-checkout'
|
||||
|
||||
export default useCheckout as UseCheckout<typeof handler>
|
||||
|
||||
export const handler: SWRHook<any> = {
|
||||
fetchOptions: {
|
||||
query: '',
|
||||
},
|
||||
async fetcher({ input, options, fetch }) {},
|
||||
useHook:
|
||||
({ useData }) =>
|
||||
async (input) => ({}),
|
||||
}
|
10
packages/appibase/src/commerce.config.json
Normal file
10
packages/appibase/src/commerce.config.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"provider": "appibase",
|
||||
"features": {
|
||||
"wishlist": false,
|
||||
"cart": true,
|
||||
"search": true,
|
||||
"customerAuth": false,
|
||||
"customCheckout": true
|
||||
}
|
||||
}
|
7
packages/appibase/src/const.ts
Normal file
7
packages/appibase/src/const.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export const API_URL = `https://appibase.com`
|
||||
|
||||
export const SCOPE = `storefront client`
|
||||
|
||||
export const CLIENT_ID_STOREFRONT = process.env.APPIBASE_CLIENT_ID_STOREFRONT
|
||||
|
||||
export const STORE_ID = process.env.APPIBASE_STORE_ID
|
17
packages/appibase/src/customer/address/use-add-item.tsx
Normal file
17
packages/appibase/src/customer/address/use-add-item.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import useAddItem, {
|
||||
UseAddItem,
|
||||
} from '@vercel/commerce/customer/address/use-add-item'
|
||||
import { MutationHook } from '@vercel/commerce/utils/types'
|
||||
|
||||
export default useAddItem as UseAddItem<typeof handler>
|
||||
|
||||
export const handler: MutationHook<any> = {
|
||||
fetchOptions: {
|
||||
query: '',
|
||||
},
|
||||
async fetcher({ input, options, fetch }) {},
|
||||
useHook:
|
||||
({ fetch }) =>
|
||||
() =>
|
||||
async () => ({}),
|
||||
}
|
17
packages/appibase/src/customer/card/use-add-item.tsx
Normal file
17
packages/appibase/src/customer/card/use-add-item.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import useAddItem, {
|
||||
UseAddItem,
|
||||
} from '@vercel/commerce/customer/card/use-add-item'
|
||||
import { MutationHook } from '@vercel/commerce/utils/types'
|
||||
|
||||
export default useAddItem as UseAddItem<typeof handler>
|
||||
|
||||
export const handler: MutationHook<any> = {
|
||||
fetchOptions: {
|
||||
query: '',
|
||||
},
|
||||
async fetcher({ input, options, fetch }) {},
|
||||
useHook:
|
||||
({ fetch }) =>
|
||||
() =>
|
||||
async () => ({}),
|
||||
}
|
1
packages/appibase/src/customer/index.ts
Normal file
1
packages/appibase/src/customer/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as useCustomer } from './use-customer'
|
17
packages/appibase/src/customer/use-customer.tsx
Normal file
17
packages/appibase/src/customer/use-customer.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { SWRHook } from '@vercel/commerce/utils/types'
|
||||
import useCustomer, {
|
||||
UseCustomer,
|
||||
} from '@vercel/commerce/customer/use-customer'
|
||||
|
||||
export default useCustomer as UseCustomer<typeof handler>
|
||||
export const handler: SWRHook<any> = {
|
||||
fetchOptions: {
|
||||
query: '',
|
||||
},
|
||||
async fetcher({ input, options, fetch }) {},
|
||||
useHook: () => () => {
|
||||
return async function addItem() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
}
|
235
packages/appibase/src/data.json
Normal file
235
packages/appibase/src/data.json
Normal file
@ -0,0 +1,235 @@
|
||||
{
|
||||
"products": [
|
||||
{
|
||||
"id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzU0NDczMjUwMjQ0MjA=",
|
||||
"name": "New Short Sleeve T-Shirt",
|
||||
"vendor": "Next.js",
|
||||
"path": "/new-short-sleeve-t-shirt",
|
||||
"slug": "new-short-sleeve-t-shirt",
|
||||
"price": { "value": 25, "currencyCode": "USD" },
|
||||
"descriptionHtml": "<p><span>Show off your love for Next.js and Vercel with this unique, </span><strong>limited edition</strong><span> t-shirt. This design is part of a limited run, numbered drop at the June 2021 Next.js Conf. It features a unique, handcrafted triangle design. Get it while supplies last – only 200 of these shirts will be made! </span><strong>All proceeds will be donated to charity.</strong></p>",
|
||||
"images": [
|
||||
{
|
||||
"url": "/assets/drop-shirt-0.png",
|
||||
"altText": "Shirt",
|
||||
"width": 1000,
|
||||
"height": 1000
|
||||
},
|
||||
{
|
||||
"url": "/assets/drop-shirt-1.png",
|
||||
"altText": "Shirt",
|
||||
"width": 1000,
|
||||
"height": 1000
|
||||
},
|
||||
{
|
||||
"url": "/assets/drop-shirt-2.png",
|
||||
"altText": "Shirt",
|
||||
"width": 1000,
|
||||
"height": 1000
|
||||
}
|
||||
],
|
||||
"variants": [
|
||||
{
|
||||
"id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzU0NDczMjUwMjQ0MjAss=",
|
||||
"options": [
|
||||
{
|
||||
"__typename": "MultipleChoiceOption",
|
||||
"id": "asd",
|
||||
"displayName": "Size",
|
||||
"values": [
|
||||
{
|
||||
"label": "XL"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"id": "option-color",
|
||||
"displayName": "Color",
|
||||
"values": [
|
||||
{
|
||||
"label": "color",
|
||||
"hexColors": ["#222"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "option-size",
|
||||
"displayName": "Size",
|
||||
"values": [
|
||||
{
|
||||
"label": "S"
|
||||
},
|
||||
{
|
||||
"label": "M"
|
||||
},
|
||||
{
|
||||
"label": "L"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "Z2lkOi8vc2hvcGlmeS9Qcm9ksdWN0LzU0NDczMjUwMjQ0MjA=",
|
||||
"name": "Lightweight Jacket",
|
||||
"vendor": "Next.js",
|
||||
"path": "/lightweight-jacket",
|
||||
"slug": "lightweight-jacket",
|
||||
"price": { "value": 249.99, "currencyCode": "USD" },
|
||||
"descriptionHtml": "<p><span>Show off your love for Next.js and Vercel with this unique, </span><strong>limited edition</strong><span> t-shirt. This design is part of a limited run, numbered drop at the June 2021 Next.js Conf. It features a unique, handcrafted triangle design. Get it while supplies last – only 200 of these shirts will be made! </span><strong>All proceeds will be donated to charity.</strong></p>",
|
||||
"images": [
|
||||
{
|
||||
"url": "/assets/lightweight-jacket-0.png",
|
||||
"altText": "Lightweight Jacket",
|
||||
"width": 1000,
|
||||
"height": 1000
|
||||
},
|
||||
{
|
||||
"url": "/assets/lightweight-jacket-1.png",
|
||||
"altText": "Lightweight Jacket",
|
||||
"width": 1000,
|
||||
"height": 1000
|
||||
},
|
||||
{
|
||||
"url": "/assets/lightweight-jacket-2.png",
|
||||
"altText": "Lightweight Jacket",
|
||||
"width": 1000,
|
||||
"height": 1000
|
||||
}
|
||||
],
|
||||
"variants": [
|
||||
{
|
||||
"id": "Z2lkOid8vc2hvcGlmeS9Qcm9kdWN0LzU0NDczMjUwMjQ0MjAss=",
|
||||
"options": [
|
||||
{
|
||||
"__typename": "MultipleChoiceOption",
|
||||
"id": "asd",
|
||||
"displayName": "Size",
|
||||
"values": [
|
||||
{
|
||||
"label": "XL"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"id": "option-color",
|
||||
"displayName": "Color",
|
||||
"values": [
|
||||
{
|
||||
"label": "color",
|
||||
"hexColors": ["#222"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "option-size",
|
||||
"displayName": "Size",
|
||||
"values": [
|
||||
{
|
||||
"label": "S"
|
||||
},
|
||||
{
|
||||
"label": "M"
|
||||
},
|
||||
{
|
||||
"label": "L"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "Z2lkOis8vc2hvcGlmsddeS9Qcm9kdWN0LzU0NDczMjUwMjQ0MjA=",
|
||||
"name": "Shirt",
|
||||
"vendor": "Next.js",
|
||||
"path": "/shirt",
|
||||
"slug": "shirt",
|
||||
"price": { "value": 25, "currencyCode": "USD" },
|
||||
"descriptionHtml": "<p><span>Show off your love for Next.js and Vercel with this unique, </span><strong>limited edition</strong><span> t-shirt. This design is part of a limited run, numbered drop at the June 2021 Next.js Conf. It features a unique, handcrafted triangle design. Get it while supplies last – only 200 of these shirts will be made! </span><strong>All proceeds will be donated to charity.</strong></p>",
|
||||
"images": [
|
||||
{
|
||||
"url": "/assets/t-shirt-0.png",
|
||||
"altText": "Shirt",
|
||||
"width": 1000,
|
||||
"height": 1000
|
||||
},
|
||||
{
|
||||
"url": "/assets/t-shirt-1.png",
|
||||
"altText": "Shirt",
|
||||
"width": 1000,
|
||||
"height": 1000
|
||||
},
|
||||
{
|
||||
"url": "/assets/t-shirt-2.png",
|
||||
"altText": "Shirt",
|
||||
"width": 1000,
|
||||
"height": 1000
|
||||
},
|
||||
{
|
||||
"url": "/assets/t-shirt-3.png",
|
||||
"altText": "Shirt",
|
||||
"width": 1000,
|
||||
"height": 1000
|
||||
},
|
||||
{
|
||||
"url": "/assets/t-shirt-4.png",
|
||||
"altText": "Shirt",
|
||||
"width": 1000,
|
||||
"height": 1000
|
||||
}
|
||||
],
|
||||
"variants": [
|
||||
{
|
||||
"id": "Z2lkOi8vc2hvcGlmeS9Qcms9kdWN0LzU0NDczMjUwMjQ0MjAss=",
|
||||
"options": [
|
||||
{
|
||||
"__typename": "MultipleChoiceOption",
|
||||
"id": "asd",
|
||||
"displayName": "Size",
|
||||
"values": [
|
||||
{
|
||||
"label": "XL"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"id": "option-color",
|
||||
"displayName": "Color",
|
||||
"values": [
|
||||
{
|
||||
"label": "color",
|
||||
"hexColors": ["#222"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "option-size",
|
||||
"displayName": "Size",
|
||||
"values": [
|
||||
{
|
||||
"label": "S"
|
||||
},
|
||||
{
|
||||
"label": "M"
|
||||
},
|
||||
{
|
||||
"label": "L"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
25
packages/appibase/src/fetcher.ts
Normal file
25
packages/appibase/src/fetcher.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { Fetcher } from '@vercel/commerce/utils/types'
|
||||
import { API_URL } from './const'
|
||||
import { handleFetchResponse } from './utils'
|
||||
import { GetAccessToken } from './api/utils/access-token'
|
||||
import Kitsu from "kitsu";
|
||||
|
||||
const fetcher: Fetcher = async ({
|
||||
method = 'GET',
|
||||
variables,
|
||||
query = '',
|
||||
body
|
||||
}) => {
|
||||
const { locale, ...vars } = variables ?? {}
|
||||
|
||||
const api = new Kitsu({ baseURL: API_URL + '/api/v1', camelCaseTypes: false, pluralize: false })
|
||||
api.headers.Authorization = `Bearer ${await GetAccessToken()}`
|
||||
|
||||
if(method === 'GET') return await api.get(query)
|
||||
else if(method === 'POST') return await api.create(query, body)
|
||||
else if(method === 'DELETE') return await api.remove(query, body)
|
||||
else if(method === 'PATCH') return await api.update(query, body)
|
||||
}
|
||||
|
||||
export default fetcher
|
||||
|
12
packages/appibase/src/index.tsx
Normal file
12
packages/appibase/src/index.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import {
|
||||
getCommerceProvider,
|
||||
useCommerce as useCoreCommerce,
|
||||
} from '@vercel/commerce'
|
||||
import { appibaseProvider, AppibaseProvider } from './provider'
|
||||
|
||||
export { appibaseProvider }
|
||||
export type { AppibaseProvider }
|
||||
|
||||
export const CommerceProvider = getCommerceProvider(appibaseProvider)
|
||||
|
||||
export const useCommerce = () => useCoreCommerce<AppibaseProvider>()
|
8
packages/appibase/src/next.config.cjs
Normal file
8
packages/appibase/src/next.config.cjs
Normal file
@ -0,0 +1,8 @@
|
||||
const commerce = require('./commerce.config.json')
|
||||
|
||||
module.exports = {
|
||||
commerce,
|
||||
images: {
|
||||
domains: ['localhost', 'shopify.vercel.store', 'vercel.store', 'demo.vercel.store'],
|
||||
},
|
||||
}
|
2
packages/appibase/src/product/index.ts
Normal file
2
packages/appibase/src/product/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { default as usePrice } from './use-price'
|
||||
export { default as useSearch } from './use-search'
|
2
packages/appibase/src/product/use-price.tsx
Normal file
2
packages/appibase/src/product/use-price.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
export * from '@vercel/commerce/product/use-price'
|
||||
export { default } from '@vercel/commerce/product/use-price'
|
54
packages/appibase/src/product/use-search.tsx
Normal file
54
packages/appibase/src/product/use-search.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import { SWRHook } from '@vercel/commerce/utils/types'
|
||||
import useSearch, { UseSearch } from '@vercel/commerce/product/use-search'
|
||||
export default useSearch as UseSearch<typeof handler>
|
||||
import type { AppibaseProduct } from '../types'
|
||||
import { NormalizeProduct } from '../api/utils/normalize'
|
||||
|
||||
export type SearchProductsInput = {
|
||||
search?: string
|
||||
categoryId?: number | string
|
||||
brandId?: number
|
||||
sort?: string
|
||||
locale?: string
|
||||
}
|
||||
|
||||
export const handler: SWRHook<any> = {
|
||||
fetchOptions: {
|
||||
url: '/products?filter[is_parent_true]=true&include=prices',
|
||||
method: 'GET',
|
||||
},
|
||||
async fetcher({ input: { search, categoryId, brandId, sort }, options, fetch }) {
|
||||
let url = options.url
|
||||
|
||||
if (search) url += `&filter[name_cont_all]=${search}`
|
||||
if (Number.isInteger(Number(categoryId)))
|
||||
url += `&filter[collections_id_eq]=${categoryId}`
|
||||
// if (Number.isInteger(brandId))
|
||||
// url.searchParams.set('brandId', String(brandId))
|
||||
// if (sort) url.searchParams.set('sort', sort)
|
||||
|
||||
|
||||
const response = await fetch({
|
||||
query: url,
|
||||
method: options.method,
|
||||
})
|
||||
|
||||
return { found: response.data.length, products: response.data.map((p : AppibaseProduct) => NormalizeProduct(p) ) }
|
||||
},
|
||||
useHook:
|
||||
({ useData }) =>
|
||||
(input = {}) => {
|
||||
return useData({
|
||||
input: [
|
||||
['search', input.search],
|
||||
['categoryId', input.categoryId],
|
||||
['brandId', input.brandId],
|
||||
['sort', input.sort],
|
||||
],
|
||||
swrOptions: {
|
||||
revalidateOnFocus: false,
|
||||
...input.swrOptions,
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
22
packages/appibase/src/provider.ts
Normal file
22
packages/appibase/src/provider.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import fetcher from './fetcher'
|
||||
import { handler as useCart } from './cart/use-cart'
|
||||
import { handler as useAddItem } from './cart/use-add-item'
|
||||
import { handler as useUpdateItem } from './cart/use-update-item'
|
||||
import { handler as useRemoveItem } from './cart/use-remove-item'
|
||||
import { handler as useCustomer } from './customer/use-customer'
|
||||
import { handler as useSearch } from './product/use-search'
|
||||
import { handler as useLogin } from './auth/use-login'
|
||||
import { handler as useLogout } from './auth/use-logout'
|
||||
import { handler as useSignup } from './auth/use-signup'
|
||||
|
||||
export const appibaseProvider = {
|
||||
locale: 'en-us',
|
||||
cartCookie: 'session',
|
||||
fetcher: fetcher,
|
||||
cart: { useCart, useAddItem, useUpdateItem, useRemoveItem },
|
||||
customer: { useCustomer },
|
||||
products: { useSearch },
|
||||
auth: { useLogin, useLogout, useSignup },
|
||||
}
|
||||
|
||||
export type AppibaseProvider = typeof appibaseProvider
|
132
packages/appibase/src/types.ts
Normal file
132
packages/appibase/src/types.ts
Normal file
@ -0,0 +1,132 @@
|
||||
export type AppibaseVariation = {
|
||||
id: string
|
||||
name: string
|
||||
options?: AppibaseVariationOption[]
|
||||
}
|
||||
|
||||
export type AppibaseVariationOption = {
|
||||
id: string
|
||||
name: string
|
||||
variation_name?: string
|
||||
variation?: AppibaseVariation
|
||||
}
|
||||
|
||||
export type PriceList = {
|
||||
id: number
|
||||
name: string
|
||||
description?: string
|
||||
currency: string
|
||||
tax_incl: boolean
|
||||
}
|
||||
|
||||
export type Amount = {
|
||||
cents: number
|
||||
float: number
|
||||
formatted: string
|
||||
}
|
||||
|
||||
export type AppibasePrices = {
|
||||
data: AppibasePrice[]
|
||||
}
|
||||
|
||||
export type AppibasePrice = {
|
||||
id: number
|
||||
currency: string
|
||||
amount: Amount
|
||||
original_amount: Amount
|
||||
price_list?: PriceList
|
||||
product?: AppibaseProduct
|
||||
}
|
||||
|
||||
export type StockLocation = {
|
||||
id: number
|
||||
name: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
export type StockItem = {
|
||||
id: number
|
||||
quantity: number
|
||||
reserved: number
|
||||
available: number
|
||||
stock_location?: StockLocation
|
||||
product?: AppibaseProduct
|
||||
}
|
||||
|
||||
export type AppibaseVariations = {
|
||||
data: AppibaseVariation[]
|
||||
}
|
||||
|
||||
export type AppibaseProductChildren = {
|
||||
data: AppibaseProduct[]
|
||||
}
|
||||
|
||||
export type AppibaseVariationOptions = {
|
||||
data: AppibaseVariationOption[]
|
||||
}
|
||||
|
||||
export type AppibaseProduct = {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
slug: string
|
||||
sku: string
|
||||
category: string
|
||||
vendor: string
|
||||
tags: string[]
|
||||
image_urls: string[]
|
||||
is_parent: boolean
|
||||
active: boolean
|
||||
livemode: boolean
|
||||
parent?: AppibaseProduct
|
||||
children?: AppibaseProductChildren
|
||||
variations?: AppibaseVariations
|
||||
variation_options?: AppibaseVariationOptions
|
||||
prices: AppibasePrices
|
||||
stock_items?: StockItem[]
|
||||
}
|
||||
|
||||
export type AppibaseCollection = {
|
||||
id: number
|
||||
name: string
|
||||
description: string
|
||||
slug: string
|
||||
image_url: string
|
||||
is_parent: boolean
|
||||
active: boolean
|
||||
livemode: boolean
|
||||
parent?: AppibaseCollection
|
||||
children?: AppibaseCollection[]
|
||||
products?: AppibaseProduct[]
|
||||
}
|
||||
|
||||
export type AppibaseCartItems = {
|
||||
data: AppibaseCartItem[]
|
||||
}
|
||||
|
||||
export type AppibaseCart = {
|
||||
id: string
|
||||
guest: boolean
|
||||
email: string
|
||||
currency: string
|
||||
tax_incl: boolean
|
||||
tax_rate: number
|
||||
subtotal_amount: Amount
|
||||
shipping_amount: Amount
|
||||
tax_amount: Amount
|
||||
total_amount: Amount
|
||||
cart_items: AppibaseCartItems
|
||||
}
|
||||
|
||||
export type AppibaseCartItem = {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
sku: string
|
||||
image_url: string
|
||||
quantity: string
|
||||
currency: string
|
||||
price: Amount
|
||||
original_price: Amount
|
||||
subtotal_amount: Amount
|
||||
}
|
27
packages/appibase/src/utils/handle-fetch-response.ts
Normal file
27
packages/appibase/src/utils/handle-fetch-response.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { FetcherError } from '@vercel/commerce/utils/errors'
|
||||
|
||||
export function getError(errors: any[] | null, status: number) {
|
||||
errors = errors ?? [{ message: 'Failed to fetch Shopify API' }]
|
||||
return new FetcherError({ errors, status })
|
||||
}
|
||||
|
||||
export async function getAsyncError(res: Response) {
|
||||
const data = await res.json()
|
||||
return getError(data.errors, res.status)
|
||||
}
|
||||
|
||||
const handleFetchResponse = async (res: Response) => {
|
||||
if (res.ok) {
|
||||
const { data, errors } = await res.json()
|
||||
|
||||
if (errors && errors.length) {
|
||||
throw getError(errors, res.status)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
throw await getAsyncError(res)
|
||||
}
|
||||
|
||||
export default handleFetchResponse
|
1
packages/appibase/src/utils/index.ts
Normal file
1
packages/appibase/src/utils/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as handleFetchResponse } from './handle-fetch-response'
|
13
packages/appibase/src/wishlist/use-add-item.tsx
Normal file
13
packages/appibase/src/wishlist/use-add-item.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { useCallback } from 'react'
|
||||
|
||||
export function emptyHook() {
|
||||
const useEmptyHook = async (options = {}) => {
|
||||
return useCallback(async function () {
|
||||
return Promise.resolve()
|
||||
}, [])
|
||||
}
|
||||
|
||||
return useEmptyHook
|
||||
}
|
||||
|
||||
export default emptyHook
|
17
packages/appibase/src/wishlist/use-remove-item.tsx
Normal file
17
packages/appibase/src/wishlist/use-remove-item.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { useCallback } from 'react'
|
||||
|
||||
type Options = {
|
||||
includeProducts?: boolean
|
||||
}
|
||||
|
||||
export function emptyHook(options?: Options) {
|
||||
const useEmptyHook = async ({ id }: { id: string | number }) => {
|
||||
return useCallback(async function () {
|
||||
return Promise.resolve()
|
||||
}, [])
|
||||
}
|
||||
|
||||
return useEmptyHook
|
||||
}
|
||||
|
||||
export default emptyHook
|
43
packages/appibase/src/wishlist/use-wishlist.tsx
Normal file
43
packages/appibase/src/wishlist/use-wishlist.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import { HookFetcher } from '@vercel/commerce/utils/types'
|
||||
import type { Product } from '@vercel/commerce/types/product'
|
||||
|
||||
const defaultOpts = {}
|
||||
|
||||
export type Wishlist = {
|
||||
items: [
|
||||
{
|
||||
product_id: number
|
||||
variant_id: number
|
||||
id: number
|
||||
product: Product
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export interface UseWishlistOptions {
|
||||
includeProducts?: boolean
|
||||
}
|
||||
|
||||
export interface UseWishlistInput extends UseWishlistOptions {
|
||||
customerId?: number
|
||||
}
|
||||
|
||||
export const fetcher: HookFetcher<Wishlist | null, UseWishlistInput> = () => {
|
||||
return null
|
||||
}
|
||||
|
||||
export function extendHook(
|
||||
customFetcher: typeof fetcher,
|
||||
// swrOptions?: SwrOptions<Wishlist | null, UseWishlistInput>
|
||||
swrOptions?: any
|
||||
) {
|
||||
const useWishlist = ({ includeProducts }: UseWishlistOptions = {}) => {
|
||||
return { data: null }
|
||||
}
|
||||
|
||||
useWishlist.extend = extendHook
|
||||
|
||||
return useWishlist
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
20
packages/appibase/taskfile.js
Normal file
20
packages/appibase/taskfile.js
Normal file
@ -0,0 +1,20 @@
|
||||
export async function build(task, opts) {
|
||||
await task
|
||||
.source('src/**/*.+(ts|tsx|js)')
|
||||
.swc({ dev: opts.dev, outDir: 'dist', baseUrl: 'src' })
|
||||
.target('dist')
|
||||
.source('src/**/*.+(cjs|json)')
|
||||
.target('dist')
|
||||
task.$.log('Compiled src files')
|
||||
}
|
||||
|
||||
export async function release(task) {
|
||||
await task.clear('dist').start('build')
|
||||
}
|
||||
|
||||
export default async function dev(task) {
|
||||
const opts = { dev: true }
|
||||
await task.clear('dist')
|
||||
await task.start('build', opts)
|
||||
await task.watch('src/**/*.+(ts|tsx|js|cjs|json)', 'build', opts)
|
||||
}
|
21
packages/appibase/tsconfig.json
Normal file
21
packages/appibase/tsconfig.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"outDir": "dist",
|
||||
"baseUrl": "src",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"declaration": true,
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"esModuleInterop": true,
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"incremental": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
@ -19,6 +19,7 @@ const PROVIDERS = [
|
||||
'@vercel/commerce-kibocommerce',
|
||||
'@vercel/commerce-spree',
|
||||
'@vercel/commerce-commercejs',
|
||||
'appibase'
|
||||
]
|
||||
|
||||
function getProviderName() {
|
||||
|
@ -4,6 +4,6 @@
|
||||
"search": true,
|
||||
"wishlist": false,
|
||||
"customerAuth": false,
|
||||
"customCheckout": false
|
||||
"customCheckout": true
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,8 @@
|
||||
"@components/*": ["components/*"],
|
||||
"@commerce": ["../packages/commerce/src"],
|
||||
"@commerce/*": ["../packages/commerce/src/*"],
|
||||
"@framework": ["../packages/local/src"],
|
||||
"@framework/*": ["../packages/local/src/*"]
|
||||
"@framework": ["../packages/appibase/src"],
|
||||
"@framework/*": ["../packages/appibase/src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"],
|
||||
|
60
yarn.lock
60
yarn.lock
@ -1985,6 +1985,13 @@ axios@^0.21.1:
|
||||
dependencies:
|
||||
follow-redirects "^1.14.0"
|
||||
|
||||
axios@^0.26.0:
|
||||
version "0.26.1"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9"
|
||||
integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==
|
||||
dependencies:
|
||||
follow-redirects "^1.14.8"
|
||||
|
||||
axobject-query@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
|
||||
@ -2802,6 +2809,15 @@ dependency-graph@^0.11.0:
|
||||
resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27"
|
||||
integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==
|
||||
|
||||
deserialize-json-api@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/deserialize-json-api/-/deserialize-json-api-1.4.0.tgz#776df0d56db1cab420d1d58e7b3d916f0140fda8"
|
||||
integrity sha512-rfViAb34ATDUygZwuU8VSi/FC1T5m+eIAjPiTwcuwCvf3V6uWdnKSYZ1Y8myHW9RCKPhnBKMHp/3ZVF/uEZISQ==
|
||||
dependencies:
|
||||
lodash.camelcase "^4.3.0"
|
||||
lodash.isarray "^4.0.0"
|
||||
lodash.isplainobject "^4.0.6"
|
||||
|
||||
detect-indent@^6.0.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6"
|
||||
@ -3469,6 +3485,11 @@ follow-redirects@^1.14.0:
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685"
|
||||
integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==
|
||||
|
||||
follow-redirects@^1.14.8:
|
||||
version "1.14.9"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7"
|
||||
integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==
|
||||
|
||||
for-in@^1.0.1, for-in@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
||||
@ -4383,6 +4404,11 @@ js-cookie@^3.0.1:
|
||||
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.1.tgz#9e39b4c6c2f56563708d7d31f6f5f21873a92414"
|
||||
integrity sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==
|
||||
|
||||
js-cookies@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/js-cookies/-/js-cookies-1.0.4.tgz#d46e576c420ff6d5542c0f52b6d4ef7d637e754e"
|
||||
integrity sha1-1G5XbEIP9tVULA9SttTvfWN+dU4=
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@ -4400,6 +4426,11 @@ jsesc@^2.5.1:
|
||||
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
|
||||
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
|
||||
|
||||
json-api-response-converter@^1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/json-api-response-converter/-/json-api-response-converter-1.6.0.tgz#66fc07c92d39da8a0a0d2eebc10b8b1b9b38d962"
|
||||
integrity sha512-PetQPJUmimf5OuDPkjWij85LdNKctG9SnGQ9EfdOQxt2nwEviraXGekEgJcEwsgeKxnrG2zD7D+nQJ6YAkzM7Q==
|
||||
|
||||
json-buffer@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
|
||||
@ -4531,6 +4562,20 @@ kind-of@^6.0.0, kind-of@^6.0.2:
|
||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
|
||||
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
|
||||
|
||||
kitsu-core@^10.0.0-alpha.22:
|
||||
version "10.0.0-alpha.22"
|
||||
resolved "https://registry.yarnpkg.com/kitsu-core/-/kitsu-core-10.0.0-alpha.22.tgz#783fa6352245f147b1ddde057aef424d4d0e4da9"
|
||||
integrity sha512-+1O27hmKlTxYSTOuJw0D5leJMD5KMpcRczb4k3CGKylRMyBlNqsCCFLb6FNDRIDVu4szRYKAnypljVJMgSkovg==
|
||||
|
||||
kitsu@^10.0.0-alpha.22:
|
||||
version "10.0.0-alpha.22"
|
||||
resolved "https://registry.yarnpkg.com/kitsu/-/kitsu-10.0.0-alpha.22.tgz#d30281bad42572bed5ea78adf6fe3e7b92e93b65"
|
||||
integrity sha512-EgDxOfiBEBj7n0FgEqzJSReqiXcUDwcKNKiVJcgbaCjISdkN9PdiSNTN3KWRYS4EuetLkIjWpH40VMkGAW4fjg==
|
||||
dependencies:
|
||||
axios "^0.26.0"
|
||||
kitsu-core "^10.0.0-alpha.22"
|
||||
pluralize "^8.0.0"
|
||||
|
||||
language-subtag-registry@~0.3.2:
|
||||
version "0.3.21"
|
||||
resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a"
|
||||
@ -4694,6 +4739,11 @@ lodash.includes@^4.3.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
|
||||
integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=
|
||||
|
||||
lodash.isarray@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-4.0.0.tgz#2aca496b28c4ca6d726715313590c02e6ea34403"
|
||||
integrity sha1-KspJayjEym1yZxUxNZDALm6jRAM=
|
||||
|
||||
lodash.isboolean@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
|
||||
@ -5520,6 +5570,11 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3:
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
pluralize@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1"
|
||||
integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==
|
||||
|
||||
posix-character-classes@^0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
|
||||
@ -7122,6 +7177,11 @@ uri-js@^4.2.2:
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
urijs@^1.19.10:
|
||||
version "1.19.10"
|
||||
resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.10.tgz#8e2fe70a8192845c180f75074884278f1eea26cb"
|
||||
integrity sha512-EzauQlgKuJgsXOqoMrCiePBf4At5jVqRhXykF3Wfb8ZsOBMxPcfiVBcsHXug4Aepb/ICm2PIgqAUGMelgdrWEg==
|
||||
|
||||
urix@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
|
||||
|
Loading…
x
Reference in New Issue
Block a user