@anker-in/lib 0.0.0-beta.1 → 0.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitkeep +0 -0
- package/.turbo/turbo-build.log +22 -0
- package/.turbo/turbo-test.log +5 -0
- package/dist/cjs/context/HeadlessProvider.js +1 -1
- package/dist/cjs/context/HeadlessProvider.js.map +2 -2
- package/dist/context/HeadlessProvider.d.ts.map +1 -1
- package/dist/esm/cart/const.js +2 -0
- package/dist/esm/cart/const.js.map +7 -0
- package/dist/esm/cart/hooks/basic/useBuyNow.js +2 -0
- package/dist/esm/cart/hooks/basic/useBuyNow.js.map +7 -0
- package/dist/esm/cart/index.js +2 -0
- package/dist/esm/cart/index.js.map +7 -0
- package/dist/esm/cart/track/fbq.js +2 -0
- package/dist/esm/cart/track/fbq.js.map +7 -0
- package/dist/esm/cart/types/index.js +1 -0
- package/dist/esm/cart/types/index.js.map +7 -0
- package/dist/esm/cart/utils/index.js +2 -0
- package/dist/esm/cart/utils/index.js.map +7 -0
- package/dist/esm/context/HeadlessProvider.js +2 -0
- package/dist/esm/context/HeadlessProvider.js.map +7 -0
- package/dist/esm/context/config.js +1 -0
- package/dist/esm/context/config.js.map +7 -0
- package/dist/esm/context/index.js +2 -0
- package/dist/esm/context/index.js.map +7 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +7 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/shopify/fetchers/add-cart-lines.js +2 -0
- package/dist/esm/shopify/fetchers/add-cart-lines.js.map +7 -0
- package/dist/esm/shopify/fetchers/create-cart.js +2 -0
- package/dist/esm/shopify/fetchers/create-cart.js.map +7 -0
- package/dist/esm/shopify/fetchers/get-cart.js +2 -0
- package/dist/esm/shopify/fetchers/get-cart.js.map +7 -0
- package/dist/esm/shopify/fetchers/get-products-by-handles.js +2 -0
- package/dist/esm/shopify/fetchers/get-products-by-handles.js.map +7 -0
- package/dist/esm/shopify/fetchers/index.js +2 -0
- package/dist/esm/shopify/fetchers/index.js.map +7 -0
- package/dist/esm/shopify/fetchers/remove-cart-lines.js +2 -0
- package/dist/esm/shopify/fetchers/remove-cart-lines.js.map +7 -0
- package/dist/esm/shopify/fetchers/update-cart-attributes.js +2 -0
- package/dist/esm/shopify/fetchers/update-cart-attributes.js.map +7 -0
- package/dist/esm/shopify/fetchers/update-cart-buyer-identity.js +2 -0
- package/dist/esm/shopify/fetchers/update-cart-buyer-identity.js.map +7 -0
- package/dist/esm/shopify/fetchers/update-cart-codes.js +2 -0
- package/dist/esm/shopify/fetchers/update-cart-codes.js.map +7 -0
- package/dist/esm/shopify/fetchers/update-cart-lines.js +2 -0
- package/dist/esm/shopify/fetchers/update-cart-lines.js.map +7 -0
- package/dist/esm/shopify/fragments/article.js +50 -0
- package/dist/esm/shopify/fragments/article.js.map +7 -0
- package/dist/esm/shopify/fragments/blog.js +11 -0
- package/dist/esm/shopify/fragments/blog.js.map +7 -0
- package/dist/esm/shopify/fragments/cart.js +189 -0
- package/dist/esm/shopify/fragments/cart.js.map +7 -0
- package/dist/esm/shopify/fragments/collection.js +16 -0
- package/dist/esm/shopify/fragments/collection.js.map +7 -0
- package/dist/esm/shopify/fragments/image.js +9 -0
- package/dist/esm/shopify/fragments/image.js.map +7 -0
- package/dist/esm/shopify/fragments/index.js +2 -0
- package/dist/esm/shopify/fragments/index.js.map +7 -0
- package/dist/esm/shopify/fragments/metafields.js +16 -0
- package/dist/esm/shopify/fragments/metafields.js.map +7 -0
- package/dist/esm/shopify/fragments/page-info.js +9 -0
- package/dist/esm/shopify/fragments/page-info.js.map +7 -0
- package/dist/esm/shopify/fragments/page.js +13 -0
- package/dist/esm/shopify/fragments/page.js.map +7 -0
- package/dist/esm/shopify/fragments/product.js +72 -0
- package/dist/esm/shopify/fragments/product.js.map +7 -0
- package/dist/esm/shopify/fragments/seo.js +7 -0
- package/dist/esm/shopify/fragments/seo.js.map +7 -0
- package/dist/esm/shopify/fragments/variant.js +30 -0
- package/dist/esm/shopify/fragments/variant.js.map +7 -0
- package/dist/esm/shopify/gql/fragment-masking.js +2 -0
- package/dist/esm/shopify/gql/fragment-masking.js.map +7 -0
- package/dist/esm/shopify/gql/gql.js +2 -0
- package/dist/esm/shopify/gql/gql.js.map +7 -0
- package/dist/esm/shopify/gql/graphql.js +2 -0
- package/dist/esm/shopify/gql/graphql.js.map +7 -0
- package/dist/esm/shopify/gql/index.js +2 -0
- package/dist/esm/shopify/gql/index.js.map +7 -0
- package/dist/esm/shopify/hooks/index.js +2 -0
- package/dist/esm/shopify/hooks/index.js.map +7 -0
- package/dist/esm/shopify/hooks/useProductsByHandles.js +2 -0
- package/dist/esm/shopify/hooks/useProductsByHandles.js.map +7 -0
- package/dist/esm/shopify/index.js +2 -0
- package/dist/esm/shopify/index.js.map +7 -0
- package/dist/esm/shopify/mutations/add-cart-lines.js +15 -0
- package/dist/esm/shopify/mutations/add-cart-lines.js.map +7 -0
- package/dist/esm/shopify/mutations/create-cart.js +21 -0
- package/dist/esm/shopify/mutations/create-cart.js.map +7 -0
- package/dist/esm/shopify/mutations/remove-cart-items.js +15 -0
- package/dist/esm/shopify/mutations/remove-cart-items.js.map +7 -0
- package/dist/esm/shopify/mutations/update-cart-attributes.js +15 -0
- package/dist/esm/shopify/mutations/update-cart-attributes.js.map +7 -0
- package/dist/esm/shopify/mutations/update-cart-buyer-identity.js +15 -0
- package/dist/esm/shopify/mutations/update-cart-buyer-identity.js.map +7 -0
- package/dist/esm/shopify/mutations/update-cart-discount-code.js +15 -0
- package/dist/esm/shopify/mutations/update-cart-discount-code.js.map +7 -0
- package/dist/esm/shopify/mutations/update-cart-items.js +15 -0
- package/dist/esm/shopify/mutations/update-cart-items.js.map +7 -0
- package/dist/esm/shopify/queries/get-cart.js +13 -0
- package/dist/esm/shopify/queries/get-cart.js.map +7 -0
- package/dist/esm/shopify/queries/get-product-by-handles.js +29 -0
- package/dist/esm/shopify/queries/get-product-by-handles.js.map +7 -0
- package/dist/esm/shopify/queries/index.js +2 -0
- package/dist/esm/shopify/queries/index.js.map +7 -0
- package/dist/esm/shopify/types/article.js +1 -0
- package/dist/esm/shopify/types/article.js.map +7 -0
- package/dist/esm/shopify/types/cart.js +1 -0
- package/dist/esm/shopify/types/cart.js.map +7 -0
- package/dist/esm/shopify/types/checkout.js +1 -0
- package/dist/esm/shopify/types/checkout.js.map +7 -0
- package/dist/esm/shopify/types/collection.js +1 -0
- package/dist/esm/shopify/types/collection.js.map +7 -0
- package/dist/esm/shopify/types/common.js +1 -0
- package/dist/esm/shopify/types/common.js.map +7 -0
- package/dist/esm/shopify/types/config.js +1 -0
- package/dist/esm/shopify/types/config.js.map +7 -0
- package/dist/esm/shopify/types/customer.js +1 -0
- package/dist/esm/shopify/types/customer.js.map +7 -0
- package/dist/esm/shopify/types/fetcher.js +2 -0
- package/dist/esm/shopify/types/fetcher.js.map +7 -0
- package/dist/esm/shopify/types/index.js +2 -0
- package/dist/esm/shopify/types/index.js.map +7 -0
- package/dist/esm/shopify/types/page.js +1 -0
- package/dist/esm/shopify/types/page.js.map +7 -0
- package/dist/esm/shopify/types/product.js +1 -0
- package/dist/esm/shopify/types/product.js.map +7 -0
- package/dist/esm/shopify/types/search.js +1 -0
- package/dist/esm/shopify/types/search.js.map +7 -0
- package/dist/esm/shopify/types/site.js +1 -0
- package/dist/esm/shopify/types/site.js.map +7 -0
- package/dist/esm/shopify/types/type-helper.js +1 -0
- package/dist/esm/shopify/types/type-helper.js.map +7 -0
- package/dist/esm/shopify/utils/colors.js +2 -0
- package/dist/esm/shopify/utils/colors.js.map +7 -0
- package/dist/esm/shopify/utils/const.js +2 -0
- package/dist/esm/shopify/utils/const.js.map +7 -0
- package/dist/esm/shopify/utils/cookie.js +2 -0
- package/dist/esm/shopify/utils/cookie.js.map +7 -0
- package/dist/esm/shopify/utils/errors.js +3 -0
- package/dist/esm/shopify/utils/errors.js.map +7 -0
- package/dist/esm/shopify/utils/fetch-graphql-api.js +3 -0
- package/dist/esm/shopify/utils/fetch-graphql-api.js.map +7 -0
- package/dist/esm/shopify/utils/handle-fetch-response.js +2 -0
- package/dist/esm/shopify/utils/handle-fetch-response.js.map +7 -0
- package/dist/esm/shopify/utils/helper.js +2 -0
- package/dist/esm/shopify/utils/helper.js.map +7 -0
- package/dist/esm/shopify/utils/normalize/cart.js +2 -0
- package/dist/esm/shopify/utils/normalize/cart.js.map +7 -0
- package/dist/esm/shopify/utils/normalize/customer.js +2 -0
- package/dist/esm/shopify/utils/normalize/customer.js.map +7 -0
- package/dist/esm/shopify/utils/normalize/index.js +2 -0
- package/dist/esm/shopify/utils/normalize/index.js.map +7 -0
- package/dist/esm/shopify/utils/normalize/metafield.js +2 -0
- package/dist/esm/shopify/utils/normalize/metafield.js.map +7 -0
- package/dist/esm/shopify/utils/normalize/product.js +2 -0
- package/dist/esm/shopify/utils/normalize/product.js.map +7 -0
- package/dist/esm/shopify/utils/store.js +2 -0
- package/dist/esm/shopify/utils/store.js.map +7 -0
- package/dist/esm/shopify/utils/type-helper.js +1 -0
- package/dist/esm/shopify/utils/type-helper.js.map +7 -0
- package/esbuild-cjs.mjs +23 -0
- package/esbuild-esm.mjs +30 -0
- package/jest.config.ts +12 -0
- package/package.json +10 -13
- package/src/cart/const.ts +23 -0
- package/src/cart/hooks/basic/useBuyNow.ts +105 -0
- package/src/cart/index.ts +19 -0
- package/src/cart/track/fbq.ts +63 -0
- package/src/cart/types/index.ts +25 -0
- package/src/cart/utils/index.ts +22 -0
- package/src/context/HeadlessProvider.tsx +30 -0
- package/src/context/config.ts +10 -0
- package/src/context/index.ts +3 -0
- package/src/index.ts +22 -0
- package/src/shopify/fetchers/add-cart-lines.ts +53 -0
- package/src/shopify/fetchers/create-cart.ts +43 -0
- package/src/shopify/fetchers/get-cart.ts +48 -0
- package/src/shopify/fetchers/get-products-by-handles.ts +48 -0
- package/src/shopify/fetchers/index.ts +10 -0
- package/src/shopify/fetchers/remove-cart-lines.ts +31 -0
- package/src/shopify/fetchers/update-cart-attributes.ts +29 -0
- package/src/shopify/fetchers/update-cart-buyer-identity.ts +32 -0
- package/src/shopify/fetchers/update-cart-codes.ts +32 -0
- package/src/shopify/fetchers/update-cart-lines.ts +42 -0
- package/src/shopify/fragments/article.ts +51 -0
- package/src/shopify/fragments/blog.ts +10 -0
- package/src/shopify/fragments/cart.ts +193 -0
- package/src/shopify/fragments/collection.ts +15 -0
- package/src/shopify/fragments/image.ts +8 -0
- package/src/shopify/fragments/index.ts +11 -0
- package/src/shopify/fragments/metafields.ts +17 -0
- package/src/shopify/fragments/page-info.ts +8 -0
- package/src/shopify/fragments/page.ts +12 -0
- package/src/shopify/fragments/product.ts +71 -0
- package/src/shopify/fragments/seo.ts +6 -0
- package/src/shopify/fragments/variant.ts +29 -0
- package/src/shopify/gql/fragment-masking.ts +85 -0
- package/src/shopify/gql/gql.ts +110 -0
- package/src/shopify/gql/graphql.ts +11780 -0
- package/src/shopify/gql/index.ts +4 -0
- package/src/shopify/hooks/index.ts +1 -0
- package/src/shopify/hooks/useProductsByHandles.ts +24 -0
- package/src/shopify/index.ts +5 -0
- package/src/shopify/mutations/add-cart-lines.ts +16 -0
- package/src/shopify/mutations/create-cart.ts +22 -0
- package/src/shopify/mutations/remove-cart-items.ts +16 -0
- package/src/shopify/mutations/update-cart-attributes.ts +16 -0
- package/src/shopify/mutations/update-cart-buyer-identity.ts +16 -0
- package/src/shopify/mutations/update-cart-discount-code.ts +16 -0
- package/src/shopify/mutations/update-cart-items.ts +16 -0
- package/src/shopify/queries/get-cart.ts +14 -0
- package/src/shopify/queries/get-product-by-handles.ts +32 -0
- package/src/shopify/queries/index.ts +2 -0
- package/src/shopify/types/article.ts +46 -0
- package/src/shopify/types/cart.ts +204 -0
- package/src/shopify/types/checkout.ts +44 -0
- package/src/shopify/types/collection.ts +104 -0
- package/src/shopify/types/common.ts +53 -0
- package/src/shopify/types/config.ts +14 -0
- package/src/shopify/types/customer.ts +31 -0
- package/src/shopify/types/fetcher.ts +67 -0
- package/src/shopify/types/index.ts +11 -0
- package/src/shopify/types/page.ts +45 -0
- package/src/shopify/types/product.ts +176 -0
- package/src/shopify/types/search.ts +62 -0
- package/src/shopify/types/site.ts +38 -0
- package/src/shopify/types/type-helper.ts +5 -0
- package/src/shopify/utils/colors.ts +206 -0
- package/src/shopify/utils/const.ts +115 -0
- package/src/shopify/utils/cookie.ts +28 -0
- package/src/shopify/utils/errors.ts +65 -0
- package/src/shopify/utils/fetch-graphql-api.ts +67 -0
- package/src/shopify/utils/handle-fetch-response.ts +60 -0
- package/src/shopify/utils/helper.ts +89 -0
- package/src/shopify/utils/normalize/cart.ts +106 -0
- package/src/shopify/utils/normalize/customer.ts +18 -0
- package/src/shopify/utils/normalize/index.ts +2 -0
- package/src/shopify/utils/normalize/metafield.ts +69 -0
- package/src/shopify/utils/normalize/product.ts +193 -0
- package/src/shopify/utils/store.ts +5 -0
- package/src/shopify/utils/type-helper.ts +7 -0
- package/tsconfig.json +20 -0
- package/tsup.config.ts +17 -0
- package/dist/index.esm.js +0 -1669
- package/dist/index.js +0 -1679
- package/dist/index.mjs +0 -1669
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// 购物车数据来源
|
|
2
|
+
export enum CartDataSource {
|
|
3
|
+
Storefront = 'storefront',
|
|
4
|
+
Theme = 'theme',
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const REVALIDATE_TIME = 60 * 5
|
|
8
|
+
|
|
9
|
+
export const SOLD_OUT_PRICE = 9999999.99
|
|
10
|
+
|
|
11
|
+
export enum TemplateType {
|
|
12
|
+
Page = 'page',
|
|
13
|
+
Product = 'product',
|
|
14
|
+
Collection = 'collection',
|
|
15
|
+
Blog = 'blog',
|
|
16
|
+
Article = 'article',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export enum DiscountType {
|
|
20
|
+
fixedAmount = 'fixed_amount',
|
|
21
|
+
percentage = 'percentage',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// 工具函数:根据配置生成常量
|
|
25
|
+
export const createConstants = (config: {
|
|
26
|
+
env: string
|
|
27
|
+
brand: string
|
|
28
|
+
locales: string[]
|
|
29
|
+
googleClientId?: string
|
|
30
|
+
facebookAppId?: string
|
|
31
|
+
applicationName?: string
|
|
32
|
+
}) => {
|
|
33
|
+
const { env, brand, locales, googleClientId, facebookAppId, applicationName } = config
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
locales,
|
|
37
|
+
env,
|
|
38
|
+
applicationName: applicationName ? JSON.parse(applicationName) : undefined,
|
|
39
|
+
host: env === 'production' ? `www.${brand}.com` : `beta.${brand}.com`,
|
|
40
|
+
brand,
|
|
41
|
+
googleClientId,
|
|
42
|
+
facebookAppId,
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
export const COLOR_LABEL = [
|
|
48
|
+
'color',
|
|
49
|
+
'colour',
|
|
50
|
+
'couleur',
|
|
51
|
+
'cor',
|
|
52
|
+
'colore',
|
|
53
|
+
'farbe',
|
|
54
|
+
'색',
|
|
55
|
+
'色',
|
|
56
|
+
'カラー',
|
|
57
|
+
'färg',
|
|
58
|
+
'farve',
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
export enum CouponChannel {
|
|
62
|
+
LIGHTNING_DEAL = 'LIGHTNING_DEAL',
|
|
63
|
+
DEAL = 'LIGHTNING_DEAL',
|
|
64
|
+
COUPON = 'COUPON',
|
|
65
|
+
PED = 'PED',
|
|
66
|
+
WS24 = 'WS24',
|
|
67
|
+
WSTD = 'WSTD',
|
|
68
|
+
WSCH = 'WSCH',
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const PRICE_SYMBOL = {
|
|
72
|
+
us: '$',
|
|
73
|
+
uk: '£',
|
|
74
|
+
ca: '$',
|
|
75
|
+
eu: '€',
|
|
76
|
+
pl: 'zł',
|
|
77
|
+
fr: '€',
|
|
78
|
+
de: '€',
|
|
79
|
+
vn: '₫',
|
|
80
|
+
cl: '$',
|
|
81
|
+
sg: '$',
|
|
82
|
+
ae: 'AED',
|
|
83
|
+
es: '€',
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const CURRENCY_CODE = {
|
|
87
|
+
us: 'USD',
|
|
88
|
+
uk: 'GBP',
|
|
89
|
+
ca: 'CAD',
|
|
90
|
+
eu: 'EUR',
|
|
91
|
+
pl: 'PL',
|
|
92
|
+
fr: 'EUR',
|
|
93
|
+
de: 'EUR',
|
|
94
|
+
vn: 'VND',
|
|
95
|
+
cl: 'CLP',
|
|
96
|
+
es: 'EUR',
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const COUNTRY_CODE = {
|
|
100
|
+
pl: 'PL',
|
|
101
|
+
} as Record<string, string>
|
|
102
|
+
|
|
103
|
+
export const LANGUAGE_CODE = {
|
|
104
|
+
pl: 'PL',
|
|
105
|
+
} as Record<string, string>
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
export const CUSTOMER_ATTRIBUTE_KEY = '_discounts_function_env'
|
|
109
|
+
export const CUSTOMER_SCRIPT_GIFT_KEY = '_giveaway_gradient_gifts'
|
|
110
|
+
|
|
111
|
+
export const CODE_AMOUNT_KEY = '_sku_code_money'
|
|
112
|
+
export const SCRIPT_CODE_AMOUNT_KEY = '_code_money'
|
|
113
|
+
|
|
114
|
+
// 主产品公开 code
|
|
115
|
+
export const MAIN_PRODUCT_CODE = ['WS24', 'WSTD', 'WS7D', 'WSCP', 'WSPE', 'WSPD']
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import Cookies from 'js-cookie'
|
|
2
|
+
|
|
3
|
+
export const SHOPIFY_COOKIE_EXPIRE = 30
|
|
4
|
+
export const SHOPIFY_CART_COOKIE_EXPIRE = 14
|
|
5
|
+
|
|
6
|
+
export enum CookieCategory {
|
|
7
|
+
Necessary = 'necessary', // 必要类cookie
|
|
8
|
+
Functional = 'functional', // 功能类、配置类cookie
|
|
9
|
+
Performance = 'performance', // 性能、统计类cookie
|
|
10
|
+
Targeting = 'targeting', // 广告、追踪类cookie
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const createCartCookieOptions = () => {
|
|
14
|
+
const hostUrl = window.location.host.split('.')
|
|
15
|
+
return {
|
|
16
|
+
category: CookieCategory.Necessary,
|
|
17
|
+
domain: hostUrl.reverse().slice(0, 2).reverse().join('.'), // 一二级域名
|
|
18
|
+
expires: SHOPIFY_CART_COOKIE_EXPIRE,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const setCartCookies = (name: string, value: string) => {
|
|
23
|
+
Cookies.set(name, value, createCartCookieOptions())
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const removeCartCookies = (name: string) => {
|
|
27
|
+
Cookies.remove(name, createCartCookieOptions())
|
|
28
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export type ErrorData = {
|
|
2
|
+
message: string
|
|
3
|
+
code?: string
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export type ErrorProps = {
|
|
7
|
+
code?: string
|
|
8
|
+
} & (
|
|
9
|
+
| { message: string; errors?: never }
|
|
10
|
+
| { message?: never; errors: ErrorData[] }
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
export class CommerceError extends Error {
|
|
14
|
+
code?: string
|
|
15
|
+
errors: ErrorData[]
|
|
16
|
+
|
|
17
|
+
constructor({ message, code, errors }: ErrorProps) {
|
|
18
|
+
const error: ErrorData = message
|
|
19
|
+
? { message, ...(code ? { code } : {}) }
|
|
20
|
+
: errors![0]
|
|
21
|
+
|
|
22
|
+
super(error.message)
|
|
23
|
+
this.errors = message ? [error] : errors!
|
|
24
|
+
|
|
25
|
+
if (error.code) this.code = error.code
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Used for errors that come from a bad implementation of the hooks
|
|
30
|
+
export class ValidationError extends CommerceError {
|
|
31
|
+
constructor(options: ErrorProps) {
|
|
32
|
+
super(options)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class CommerceFetcherError extends CommerceError {
|
|
37
|
+
status: number
|
|
38
|
+
|
|
39
|
+
constructor(
|
|
40
|
+
options: {
|
|
41
|
+
status: number
|
|
42
|
+
} & ErrorProps
|
|
43
|
+
) {
|
|
44
|
+
super(options)
|
|
45
|
+
this.status = options.status
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class FetcherError extends Error {
|
|
50
|
+
status: number
|
|
51
|
+
errors: string[]
|
|
52
|
+
errorCode: string
|
|
53
|
+
constructor(options: {
|
|
54
|
+
status: number
|
|
55
|
+
errors?: string[]
|
|
56
|
+
message?: string
|
|
57
|
+
errorCode?: string
|
|
58
|
+
}) {
|
|
59
|
+
const errorMessage = options.message || options.errors?.join('\n')
|
|
60
|
+
super(errorMessage)
|
|
61
|
+
this.status = options.status
|
|
62
|
+
this.errors = options.errors || []
|
|
63
|
+
this.errorCode = options.errorCode || ''
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import fetchPonyfill from 'fetch-ponyfill'
|
|
2
|
+
import { ShopifyConfig } from '../types/config'
|
|
3
|
+
|
|
4
|
+
import { getCommerceError } from '../utils/handle-fetch-response'
|
|
5
|
+
|
|
6
|
+
const { fetch } = fetchPonyfill()
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Graphql Api 接口 fetch 方法
|
|
10
|
+
* @param query: 查询语句
|
|
11
|
+
* @param variables: 查询参数
|
|
12
|
+
* @param config: 店铺配置
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
export async function fetchGraphqlApi<Data, Variables = undefined>({
|
|
16
|
+
query,
|
|
17
|
+
variables,
|
|
18
|
+
fetchOptions,
|
|
19
|
+
headers,
|
|
20
|
+
config,
|
|
21
|
+
}: {
|
|
22
|
+
query: string
|
|
23
|
+
variables?: Variables
|
|
24
|
+
headers?: Record<string, any>
|
|
25
|
+
fetchOptions?: Omit<RequestInit, 'headers' | 'method' | 'body'>
|
|
26
|
+
config: ShopifyConfig
|
|
27
|
+
}): Promise<{ status: number; data: Data; errors?: any } | never> {
|
|
28
|
+
const { storefrontToken, storeDomain } = config
|
|
29
|
+
|
|
30
|
+
let result: Response | undefined
|
|
31
|
+
try {
|
|
32
|
+
result = await fetch(`https://${storeDomain}/api/2025-07/graphql.json`, {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: {
|
|
35
|
+
'Content-Type': 'application/json',
|
|
36
|
+
'X-Shopify-Storefront-Access-Token': storefrontToken,
|
|
37
|
+
...headers,
|
|
38
|
+
},
|
|
39
|
+
body: JSON.stringify({
|
|
40
|
+
...(query && { query }),
|
|
41
|
+
...(variables && { variables }),
|
|
42
|
+
}),
|
|
43
|
+
...fetchOptions,
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const { data, errors, status } = await result.json()
|
|
47
|
+
|
|
48
|
+
if (errors?.length) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`Status: ${status}, ${result.status}, GraphQL errors: ` +
|
|
51
|
+
errors.map(({ message }: { message: string }) => message).join(', ')
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return { data, status, ...(errors && { errors }) }
|
|
56
|
+
} catch (err: any) {
|
|
57
|
+
console.error('fetchGraphqlApi url:', storeDomain, result?.status)
|
|
58
|
+
throw getCommerceError(
|
|
59
|
+
[
|
|
60
|
+
{
|
|
61
|
+
message: `status: ${result?.status}, ${err.message} \n Most likely related to an unexpected output. e.g the store might be protected with password or not available.`,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
500
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { CommerceFetcherError, FetcherError } from './errors'
|
|
2
|
+
|
|
3
|
+
export function getCommerceError(errors: any[] | null, status = 500) {
|
|
4
|
+
errors = errors ?? [{ message: 'Failed to fetch Shopify API' }]
|
|
5
|
+
return new CommerceFetcherError({ errors, status })
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function getFetcherError(
|
|
9
|
+
error: string | string[],
|
|
10
|
+
statusCode: any,
|
|
11
|
+
errorCode: any
|
|
12
|
+
) {
|
|
13
|
+
let message = '',
|
|
14
|
+
errors: string[] = []
|
|
15
|
+
if (typeof error === 'string') {
|
|
16
|
+
message = error
|
|
17
|
+
} else if (Array.isArray(error)) {
|
|
18
|
+
errors = error
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return new FetcherError({
|
|
22
|
+
message,
|
|
23
|
+
errors,
|
|
24
|
+
status: statusCode,
|
|
25
|
+
errorCode: errorCode || '',
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function getAsyncError(res: Response, responseData: any) {
|
|
30
|
+
return getFetcherError(
|
|
31
|
+
responseData?.errors ||
|
|
32
|
+
responseData?.message ||
|
|
33
|
+
(Array.isArray(responseData?.data.message)
|
|
34
|
+
? responseData?.data.message.join('. ')
|
|
35
|
+
: responseData?.data.message) ||
|
|
36
|
+
responseData?.data?.error ||
|
|
37
|
+
res.statusText,
|
|
38
|
+
res.status,
|
|
39
|
+
responseData?.data?.error_code || ''
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const handleFetchResponse = async (res: Response) => {
|
|
44
|
+
const responseText = await res.text()
|
|
45
|
+
let responseData
|
|
46
|
+
let requestErrorMessage
|
|
47
|
+
|
|
48
|
+
if (responseText) {
|
|
49
|
+
try {
|
|
50
|
+
responseData = JSON.parse(responseText)
|
|
51
|
+
requestErrorMessage = responseData?.error || responseData?.data?.error
|
|
52
|
+
} catch (err) {}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (res.ok && !requestErrorMessage) {
|
|
56
|
+
return responseData?.data || responseData
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
throw await getAsyncError(res, responseData)
|
|
60
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { HasMetafieldsIdentifier } from '../gql'
|
|
2
|
+
import { HasMetafieldQueryRoot } from '../types/fetcher'
|
|
3
|
+
import { PartialRecord } from '../utils/type-helper'
|
|
4
|
+
import Cookies from 'js-cookie'
|
|
5
|
+
import { CouponChannel } from '../utils/const'
|
|
6
|
+
import { ProductVariant } from '../types/product'
|
|
7
|
+
|
|
8
|
+
// 判断对象是否为空
|
|
9
|
+
export function isObjEmpty(obj?: Record<string, any>) {
|
|
10
|
+
if (!obj) return true
|
|
11
|
+
else {
|
|
12
|
+
return Object.keys(obj).length === 0
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function atobID(id: string) {
|
|
17
|
+
if (id && typeof id === 'string' && id.includes('/')) {
|
|
18
|
+
return id.split('/').pop()?.split('?')?.shift()
|
|
19
|
+
} else {
|
|
20
|
+
return id
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function btoaID(id: string, type: 'ProductVariant' | 'Product' = 'ProductVariant') {
|
|
25
|
+
return `gid://shopify/${type}/${id}`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 皮肤站三级域名
|
|
29
|
+
export const getThemeLocale = (locale: string) => {
|
|
30
|
+
if (locale === 'eu-de') return 'de'
|
|
31
|
+
if (locale === 'eu-en') return 'eu'
|
|
32
|
+
if (locale === 'au') return 'au'
|
|
33
|
+
return locale
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 生成 metafieldIdentifiers 查询参数
|
|
38
|
+
* {
|
|
39
|
+
* product: { namespace: 'product', key: 'img' },
|
|
40
|
+
* variant: { namespace: 'variant', key: 'img' }
|
|
41
|
+
* }
|
|
42
|
+
* =>
|
|
43
|
+
* {
|
|
44
|
+
* productMetafieldIdentifiers: [{ namespace: 'combo', key: 'product' }]
|
|
45
|
+
* variantMetafieldIdentifiers: [{ namespace: 'combo', key: 'variant' }]
|
|
46
|
+
* }
|
|
47
|
+
*/
|
|
48
|
+
export function constructMetafieldIdentifiersQueryParams<T extends HasMetafieldQueryRoot>(
|
|
49
|
+
metafieldIdentifiers: PartialRecord<T, HasMetafieldsIdentifier[]> = {},
|
|
50
|
+
metafieldsNamespace: string = ''
|
|
51
|
+
): PartialRecord<`${T}MetafieldIdentifiers`, HasMetafieldsIdentifier[]> {
|
|
52
|
+
const identifiers = Object.entries(metafieldIdentifiers).reduce<
|
|
53
|
+
PartialRecord<`${T}MetafieldIdentifiers`, HasMetafieldsIdentifier[]>
|
|
54
|
+
>((queryInput, [key, value]) => {
|
|
55
|
+
const metafieldIdentifiers = value as HasMetafieldsIdentifier[]
|
|
56
|
+
queryInput[`${key}MetafieldIdentifiers` as `${T}MetafieldIdentifiers`] = metafieldIdentifiers
|
|
57
|
+
.filter(item => item.namespace !== metafieldsNamespace)
|
|
58
|
+
.map(item => ({
|
|
59
|
+
namespace: metafieldsNamespace,
|
|
60
|
+
key: item.namespace,
|
|
61
|
+
}))
|
|
62
|
+
.concat(metafieldIdentifiers)
|
|
63
|
+
return queryInput
|
|
64
|
+
}, {})
|
|
65
|
+
return identifiers
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const referralAttributes = () => {
|
|
69
|
+
const inviteCode = Cookies.get('invite_code')
|
|
70
|
+
const playModeId = Cookies.get('playModeId')
|
|
71
|
+
const popup = Cookies.get('_popup')
|
|
72
|
+
if (inviteCode && playModeId) {
|
|
73
|
+
return popup
|
|
74
|
+
? [
|
|
75
|
+
{ key: '_invite_code', value: inviteCode ? inviteCode : '' },
|
|
76
|
+
{ key: '_play_mode_id', value: playModeId ? playModeId : '' },
|
|
77
|
+
{ key: '_popup', value: popup },
|
|
78
|
+
]
|
|
79
|
+
: [
|
|
80
|
+
{ key: '_invite_code', value: inviteCode ? inviteCode : '' },
|
|
81
|
+
{ key: '_play_mode_id', value: playModeId ? playModeId : '' },
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
return []
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const getNormalCoupon = ({ variant }: { variant?: ProductVariant }) => {
|
|
88
|
+
return variant?.coupons?.find((coupon: any) => coupon.discount_type !== CouponChannel.WSCH)
|
|
89
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { ProductVariant as ShopifyProductVariant, CartLine as ShopifyCartLine } from '../../gql'
|
|
2
|
+
import type { Cart, LineItem } from '../../types/cart'
|
|
3
|
+
import type { Cart as ShopifyCart } from '../../gql'
|
|
4
|
+
|
|
5
|
+
import { normalizeMetafields } from './metafield'
|
|
6
|
+
|
|
7
|
+
export function normalizeCart(cart: ShopifyCart, metafieldsNamespace: string = ''): Cart {
|
|
8
|
+
const lineItems = cart.lines?.edges?.map(item => normalizeLineItem(item, metafieldsNamespace))
|
|
9
|
+
const orderDiscounts = cart?.discountAllocations?.reduce((pre, cur) => {
|
|
10
|
+
return Number(cur?.discountedAmount?.amount) + pre
|
|
11
|
+
}, 0)
|
|
12
|
+
const [totalLineItemsDiscount, lineItemsSubtotalPrice] = lineItems?.reduce(
|
|
13
|
+
([t, l], cur) => {
|
|
14
|
+
const currentDiscount = cur?.discountAllocations?.reduce((p, c) => Number(c?.amount) + p, 0) || 0
|
|
15
|
+
return [currentDiscount + t, Number(cur?.subtotalAmount) + l]
|
|
16
|
+
},
|
|
17
|
+
[0, 0]
|
|
18
|
+
) || [0, 0]
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
customAttributes: cart.attributes,
|
|
22
|
+
totalLineItemsDiscount: orderDiscounts + totalLineItemsDiscount,
|
|
23
|
+
id: cart.id,
|
|
24
|
+
url: cart.checkoutUrl,
|
|
25
|
+
ready: true,
|
|
26
|
+
customerId: cart?.buyerIdentity?.customer?.id || '',
|
|
27
|
+
email: cart?.buyerIdentity?.email || '',
|
|
28
|
+
createdAt: cart.createdAt,
|
|
29
|
+
currency: {
|
|
30
|
+
code: cart.cost?.subtotalAmount?.currencyCode,
|
|
31
|
+
},
|
|
32
|
+
taxesIncluded: cart.cost?.totalTaxAmountEstimated,
|
|
33
|
+
lineItems: cart.lines?.edges?.map(item => normalizeLineItem(item, metafieldsNamespace)),
|
|
34
|
+
subtotalPrice: cart.cost?.subtotalAmount?.amount,
|
|
35
|
+
totalPrice: cart.cost?.totalAmount?.amount - (cart.cost?.totalTaxAmount?.amount || 0),
|
|
36
|
+
// 预估税额现在逻辑没理清楚,暂时 写死为 0
|
|
37
|
+
// totalTaxAmount: cart.cost?.totalTaxAmount?.amount,
|
|
38
|
+
totalTaxAmount: 0,
|
|
39
|
+
// lineItemsSubtotalPrice: cart.cost?.subtotalAmount?.amount,
|
|
40
|
+
lineItemsSubtotalPrice,
|
|
41
|
+
discountCodes: cart.discountCodes || [],
|
|
42
|
+
discountAllocations:
|
|
43
|
+
cart?.discountAllocations?.map(item => ({
|
|
44
|
+
code: item?.code || item?.title || '',
|
|
45
|
+
amount: item?.discountedAmount?.amount,
|
|
46
|
+
})) || [],
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function normalizeLineItem({
|
|
51
|
+
node: { id, merchandise: variant, quantity, discountAllocations, attributes, cost },
|
|
52
|
+
}: {
|
|
53
|
+
node: ShopifyCartLine
|
|
54
|
+
}, metafieldNamespacePrefix: string = ''): LineItem {
|
|
55
|
+
const price = variant?.price?.amount
|
|
56
|
+
const name = variant?.product?.title
|
|
57
|
+
return {
|
|
58
|
+
id,
|
|
59
|
+
name,
|
|
60
|
+
quantity,
|
|
61
|
+
variantId: String(variant?.id),
|
|
62
|
+
productId: String(variant?.product?.id),
|
|
63
|
+
totalAmount: cost?.totalAmount?.amount,
|
|
64
|
+
subtotalAmount: cost?.subtotalAmount?.amount,
|
|
65
|
+
discountAllocations:
|
|
66
|
+
discountAllocations?.map(item => ({
|
|
67
|
+
code: item?.code || item?.title || '',
|
|
68
|
+
amount: item?.discountedAmount?.amount,
|
|
69
|
+
})) || [],
|
|
70
|
+
customAttributes:
|
|
71
|
+
attributes?.map(item => ({
|
|
72
|
+
key: item?.key || '',
|
|
73
|
+
value: item?.value || '',
|
|
74
|
+
})) || [],
|
|
75
|
+
variant: {
|
|
76
|
+
id: String(variant?.id),
|
|
77
|
+
options: [],
|
|
78
|
+
price,
|
|
79
|
+
listPrice: variant?.compareAtPrice?.amount,
|
|
80
|
+
sku: variant?.sku ?? '',
|
|
81
|
+
name: variant?.title!,
|
|
82
|
+
requiresShipping: variant?.requiresShipping ?? false,
|
|
83
|
+
availableForSale: availableVariant({
|
|
84
|
+
...variant,
|
|
85
|
+
price: variant?.price,
|
|
86
|
+
} as unknown as ShopifyProductVariant),
|
|
87
|
+
quantityAvailable: variant?.quantityAvailable || 0,
|
|
88
|
+
currentlyNotInStock: variant?.currentlyNotInStock || false,
|
|
89
|
+
metafields: normalizeMetafields(variant.metafields, metafieldNamespacePrefix),
|
|
90
|
+
},
|
|
91
|
+
product: variant?.product as any,
|
|
92
|
+
path: `/${variant?.product?.handle}`,
|
|
93
|
+
discounts: [],
|
|
94
|
+
options: variant?.title == 'Default Title' ? [] : variant?.selectedOptions,
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const availableVariant = (variant: ShopifyProductVariant) => {
|
|
99
|
+
//availableForSale 是否可售 true:可售
|
|
100
|
+
if (!variant.availableForSale) return false
|
|
101
|
+
//currentlyNotInStock 是否可超卖 true:是
|
|
102
|
+
//quantityAvailable 库存
|
|
103
|
+
if (!variant.quantityAvailable && !variant.currentlyNotInStock) return false
|
|
104
|
+
if (variant.price?.amount >= 9999999.99) return false
|
|
105
|
+
return true
|
|
106
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Customer } from '../../types/customer'
|
|
2
|
+
import { Customer as ShopifyCustomer } from '../../gql'
|
|
3
|
+
|
|
4
|
+
export function normalizeCustomer(customer: ShopifyCustomer): Customer {
|
|
5
|
+
const { id, firstName, lastName, displayName, email, phone, tags, acceptsMarketing, createdAt, orders } = customer
|
|
6
|
+
return {
|
|
7
|
+
id,
|
|
8
|
+
firstName: firstName || '',
|
|
9
|
+
lastName: lastName || '',
|
|
10
|
+
displayName: displayName || '',
|
|
11
|
+
email: email || '',
|
|
12
|
+
phone: phone || '',
|
|
13
|
+
tags: tags || [],
|
|
14
|
+
acceptsMarketing: acceptsMarketing || false,
|
|
15
|
+
createdAt: createdAt || '',
|
|
16
|
+
orders: orders?.nodes?.map(order => order) || [],
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Maybe } from 'graphql/jsutils/Maybe'
|
|
2
|
+
import { Metafield } from '../../gql'
|
|
3
|
+
|
|
4
|
+
export const normalizeMetafields = (
|
|
5
|
+
metafields: Maybe<Metafield>[],
|
|
6
|
+
metafieldNamespacePrefix: string
|
|
7
|
+
): Record<string, any> => {
|
|
8
|
+
// combo namespace 的优先级低于普通namespace的优先级
|
|
9
|
+
return metafields?.reduce(
|
|
10
|
+
(prev, cur) => {
|
|
11
|
+
if (cur) {
|
|
12
|
+
let namespace = cur.namespace
|
|
13
|
+
if (namespace.startsWith(`${metafieldNamespacePrefix}combo`)) {
|
|
14
|
+
namespace = cur.key
|
|
15
|
+
prev[namespace] = prev[namespace] || {}
|
|
16
|
+
|
|
17
|
+
const parsedMetafields: {
|
|
18
|
+
[key: string]: Metafield
|
|
19
|
+
} = parseMetafield(cur)
|
|
20
|
+
if (parsedMetafields) {
|
|
21
|
+
Object.entries(parsedMetafields).forEach(([key, innerMetafields]) => {
|
|
22
|
+
prev[namespace][key] = prev[namespace][key] ?? (innerMetafields ? parseMetafield(innerMetafields) : null)
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
prev[namespace] = prev[namespace] || {}
|
|
27
|
+
prev[namespace][cur.key] = cur ? parseMetafield(cur) : null
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return prev
|
|
31
|
+
},
|
|
32
|
+
{} as Record<string, any>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function parseJSON(str: string) {
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(str)
|
|
39
|
+
} catch (err) {
|
|
40
|
+
return {}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const parseDesc = (desc: string) => {
|
|
45
|
+
const cfg = parseJSON(desc) || {}
|
|
46
|
+
return {
|
|
47
|
+
parseType: cfg.type, // JSON_STRING
|
|
48
|
+
descContent: cfg.content || '', // 描述
|
|
49
|
+
status: cfg.status || 'Active', // 状态
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const parseMetafield = (metafield: Metafield) => {
|
|
54
|
+
const type = metafield?.type && metafield?.type.toLowerCase()
|
|
55
|
+
const { status } = parseDesc(metafield?.description || '')
|
|
56
|
+
if (status.toLowerCase() !== 'active') return null
|
|
57
|
+
|
|
58
|
+
switch (type) {
|
|
59
|
+
case 'json':
|
|
60
|
+
case 'json_string':
|
|
61
|
+
case 'rating':
|
|
62
|
+
case 'volume':
|
|
63
|
+
case 'weight':
|
|
64
|
+
case 'dimension':
|
|
65
|
+
return JSON.parse(metafield.value)
|
|
66
|
+
default:
|
|
67
|
+
return metafield?.value || metafield
|
|
68
|
+
}
|
|
69
|
+
}
|