@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.
Files changed (247) hide show
  1. package/.gitkeep +0 -0
  2. package/.turbo/turbo-build.log +22 -0
  3. package/.turbo/turbo-test.log +5 -0
  4. package/dist/cjs/context/HeadlessProvider.js +1 -1
  5. package/dist/cjs/context/HeadlessProvider.js.map +2 -2
  6. package/dist/context/HeadlessProvider.d.ts.map +1 -1
  7. package/dist/esm/cart/const.js +2 -0
  8. package/dist/esm/cart/const.js.map +7 -0
  9. package/dist/esm/cart/hooks/basic/useBuyNow.js +2 -0
  10. package/dist/esm/cart/hooks/basic/useBuyNow.js.map +7 -0
  11. package/dist/esm/cart/index.js +2 -0
  12. package/dist/esm/cart/index.js.map +7 -0
  13. package/dist/esm/cart/track/fbq.js +2 -0
  14. package/dist/esm/cart/track/fbq.js.map +7 -0
  15. package/dist/esm/cart/types/index.js +1 -0
  16. package/dist/esm/cart/types/index.js.map +7 -0
  17. package/dist/esm/cart/utils/index.js +2 -0
  18. package/dist/esm/cart/utils/index.js.map +7 -0
  19. package/dist/esm/context/HeadlessProvider.js +2 -0
  20. package/dist/esm/context/HeadlessProvider.js.map +7 -0
  21. package/dist/esm/context/config.js +1 -0
  22. package/dist/esm/context/config.js.map +7 -0
  23. package/dist/esm/context/index.js +2 -0
  24. package/dist/esm/context/index.js.map +7 -0
  25. package/dist/esm/index.js +2 -0
  26. package/dist/esm/index.js.map +7 -0
  27. package/dist/esm/package.json +3 -0
  28. package/dist/esm/shopify/fetchers/add-cart-lines.js +2 -0
  29. package/dist/esm/shopify/fetchers/add-cart-lines.js.map +7 -0
  30. package/dist/esm/shopify/fetchers/create-cart.js +2 -0
  31. package/dist/esm/shopify/fetchers/create-cart.js.map +7 -0
  32. package/dist/esm/shopify/fetchers/get-cart.js +2 -0
  33. package/dist/esm/shopify/fetchers/get-cart.js.map +7 -0
  34. package/dist/esm/shopify/fetchers/get-products-by-handles.js +2 -0
  35. package/dist/esm/shopify/fetchers/get-products-by-handles.js.map +7 -0
  36. package/dist/esm/shopify/fetchers/index.js +2 -0
  37. package/dist/esm/shopify/fetchers/index.js.map +7 -0
  38. package/dist/esm/shopify/fetchers/remove-cart-lines.js +2 -0
  39. package/dist/esm/shopify/fetchers/remove-cart-lines.js.map +7 -0
  40. package/dist/esm/shopify/fetchers/update-cart-attributes.js +2 -0
  41. package/dist/esm/shopify/fetchers/update-cart-attributes.js.map +7 -0
  42. package/dist/esm/shopify/fetchers/update-cart-buyer-identity.js +2 -0
  43. package/dist/esm/shopify/fetchers/update-cart-buyer-identity.js.map +7 -0
  44. package/dist/esm/shopify/fetchers/update-cart-codes.js +2 -0
  45. package/dist/esm/shopify/fetchers/update-cart-codes.js.map +7 -0
  46. package/dist/esm/shopify/fetchers/update-cart-lines.js +2 -0
  47. package/dist/esm/shopify/fetchers/update-cart-lines.js.map +7 -0
  48. package/dist/esm/shopify/fragments/article.js +50 -0
  49. package/dist/esm/shopify/fragments/article.js.map +7 -0
  50. package/dist/esm/shopify/fragments/blog.js +11 -0
  51. package/dist/esm/shopify/fragments/blog.js.map +7 -0
  52. package/dist/esm/shopify/fragments/cart.js +189 -0
  53. package/dist/esm/shopify/fragments/cart.js.map +7 -0
  54. package/dist/esm/shopify/fragments/collection.js +16 -0
  55. package/dist/esm/shopify/fragments/collection.js.map +7 -0
  56. package/dist/esm/shopify/fragments/image.js +9 -0
  57. package/dist/esm/shopify/fragments/image.js.map +7 -0
  58. package/dist/esm/shopify/fragments/index.js +2 -0
  59. package/dist/esm/shopify/fragments/index.js.map +7 -0
  60. package/dist/esm/shopify/fragments/metafields.js +16 -0
  61. package/dist/esm/shopify/fragments/metafields.js.map +7 -0
  62. package/dist/esm/shopify/fragments/page-info.js +9 -0
  63. package/dist/esm/shopify/fragments/page-info.js.map +7 -0
  64. package/dist/esm/shopify/fragments/page.js +13 -0
  65. package/dist/esm/shopify/fragments/page.js.map +7 -0
  66. package/dist/esm/shopify/fragments/product.js +72 -0
  67. package/dist/esm/shopify/fragments/product.js.map +7 -0
  68. package/dist/esm/shopify/fragments/seo.js +7 -0
  69. package/dist/esm/shopify/fragments/seo.js.map +7 -0
  70. package/dist/esm/shopify/fragments/variant.js +30 -0
  71. package/dist/esm/shopify/fragments/variant.js.map +7 -0
  72. package/dist/esm/shopify/gql/fragment-masking.js +2 -0
  73. package/dist/esm/shopify/gql/fragment-masking.js.map +7 -0
  74. package/dist/esm/shopify/gql/gql.js +2 -0
  75. package/dist/esm/shopify/gql/gql.js.map +7 -0
  76. package/dist/esm/shopify/gql/graphql.js +2 -0
  77. package/dist/esm/shopify/gql/graphql.js.map +7 -0
  78. package/dist/esm/shopify/gql/index.js +2 -0
  79. package/dist/esm/shopify/gql/index.js.map +7 -0
  80. package/dist/esm/shopify/hooks/index.js +2 -0
  81. package/dist/esm/shopify/hooks/index.js.map +7 -0
  82. package/dist/esm/shopify/hooks/useProductsByHandles.js +2 -0
  83. package/dist/esm/shopify/hooks/useProductsByHandles.js.map +7 -0
  84. package/dist/esm/shopify/index.js +2 -0
  85. package/dist/esm/shopify/index.js.map +7 -0
  86. package/dist/esm/shopify/mutations/add-cart-lines.js +15 -0
  87. package/dist/esm/shopify/mutations/add-cart-lines.js.map +7 -0
  88. package/dist/esm/shopify/mutations/create-cart.js +21 -0
  89. package/dist/esm/shopify/mutations/create-cart.js.map +7 -0
  90. package/dist/esm/shopify/mutations/remove-cart-items.js +15 -0
  91. package/dist/esm/shopify/mutations/remove-cart-items.js.map +7 -0
  92. package/dist/esm/shopify/mutations/update-cart-attributes.js +15 -0
  93. package/dist/esm/shopify/mutations/update-cart-attributes.js.map +7 -0
  94. package/dist/esm/shopify/mutations/update-cart-buyer-identity.js +15 -0
  95. package/dist/esm/shopify/mutations/update-cart-buyer-identity.js.map +7 -0
  96. package/dist/esm/shopify/mutations/update-cart-discount-code.js +15 -0
  97. package/dist/esm/shopify/mutations/update-cart-discount-code.js.map +7 -0
  98. package/dist/esm/shopify/mutations/update-cart-items.js +15 -0
  99. package/dist/esm/shopify/mutations/update-cart-items.js.map +7 -0
  100. package/dist/esm/shopify/queries/get-cart.js +13 -0
  101. package/dist/esm/shopify/queries/get-cart.js.map +7 -0
  102. package/dist/esm/shopify/queries/get-product-by-handles.js +29 -0
  103. package/dist/esm/shopify/queries/get-product-by-handles.js.map +7 -0
  104. package/dist/esm/shopify/queries/index.js +2 -0
  105. package/dist/esm/shopify/queries/index.js.map +7 -0
  106. package/dist/esm/shopify/types/article.js +1 -0
  107. package/dist/esm/shopify/types/article.js.map +7 -0
  108. package/dist/esm/shopify/types/cart.js +1 -0
  109. package/dist/esm/shopify/types/cart.js.map +7 -0
  110. package/dist/esm/shopify/types/checkout.js +1 -0
  111. package/dist/esm/shopify/types/checkout.js.map +7 -0
  112. package/dist/esm/shopify/types/collection.js +1 -0
  113. package/dist/esm/shopify/types/collection.js.map +7 -0
  114. package/dist/esm/shopify/types/common.js +1 -0
  115. package/dist/esm/shopify/types/common.js.map +7 -0
  116. package/dist/esm/shopify/types/config.js +1 -0
  117. package/dist/esm/shopify/types/config.js.map +7 -0
  118. package/dist/esm/shopify/types/customer.js +1 -0
  119. package/dist/esm/shopify/types/customer.js.map +7 -0
  120. package/dist/esm/shopify/types/fetcher.js +2 -0
  121. package/dist/esm/shopify/types/fetcher.js.map +7 -0
  122. package/dist/esm/shopify/types/index.js +2 -0
  123. package/dist/esm/shopify/types/index.js.map +7 -0
  124. package/dist/esm/shopify/types/page.js +1 -0
  125. package/dist/esm/shopify/types/page.js.map +7 -0
  126. package/dist/esm/shopify/types/product.js +1 -0
  127. package/dist/esm/shopify/types/product.js.map +7 -0
  128. package/dist/esm/shopify/types/search.js +1 -0
  129. package/dist/esm/shopify/types/search.js.map +7 -0
  130. package/dist/esm/shopify/types/site.js +1 -0
  131. package/dist/esm/shopify/types/site.js.map +7 -0
  132. package/dist/esm/shopify/types/type-helper.js +1 -0
  133. package/dist/esm/shopify/types/type-helper.js.map +7 -0
  134. package/dist/esm/shopify/utils/colors.js +2 -0
  135. package/dist/esm/shopify/utils/colors.js.map +7 -0
  136. package/dist/esm/shopify/utils/const.js +2 -0
  137. package/dist/esm/shopify/utils/const.js.map +7 -0
  138. package/dist/esm/shopify/utils/cookie.js +2 -0
  139. package/dist/esm/shopify/utils/cookie.js.map +7 -0
  140. package/dist/esm/shopify/utils/errors.js +3 -0
  141. package/dist/esm/shopify/utils/errors.js.map +7 -0
  142. package/dist/esm/shopify/utils/fetch-graphql-api.js +3 -0
  143. package/dist/esm/shopify/utils/fetch-graphql-api.js.map +7 -0
  144. package/dist/esm/shopify/utils/handle-fetch-response.js +2 -0
  145. package/dist/esm/shopify/utils/handle-fetch-response.js.map +7 -0
  146. package/dist/esm/shopify/utils/helper.js +2 -0
  147. package/dist/esm/shopify/utils/helper.js.map +7 -0
  148. package/dist/esm/shopify/utils/normalize/cart.js +2 -0
  149. package/dist/esm/shopify/utils/normalize/cart.js.map +7 -0
  150. package/dist/esm/shopify/utils/normalize/customer.js +2 -0
  151. package/dist/esm/shopify/utils/normalize/customer.js.map +7 -0
  152. package/dist/esm/shopify/utils/normalize/index.js +2 -0
  153. package/dist/esm/shopify/utils/normalize/index.js.map +7 -0
  154. package/dist/esm/shopify/utils/normalize/metafield.js +2 -0
  155. package/dist/esm/shopify/utils/normalize/metafield.js.map +7 -0
  156. package/dist/esm/shopify/utils/normalize/product.js +2 -0
  157. package/dist/esm/shopify/utils/normalize/product.js.map +7 -0
  158. package/dist/esm/shopify/utils/store.js +2 -0
  159. package/dist/esm/shopify/utils/store.js.map +7 -0
  160. package/dist/esm/shopify/utils/type-helper.js +1 -0
  161. package/dist/esm/shopify/utils/type-helper.js.map +7 -0
  162. package/esbuild-cjs.mjs +23 -0
  163. package/esbuild-esm.mjs +30 -0
  164. package/jest.config.ts +12 -0
  165. package/package.json +10 -13
  166. package/src/cart/const.ts +23 -0
  167. package/src/cart/hooks/basic/useBuyNow.ts +105 -0
  168. package/src/cart/index.ts +19 -0
  169. package/src/cart/track/fbq.ts +63 -0
  170. package/src/cart/types/index.ts +25 -0
  171. package/src/cart/utils/index.ts +22 -0
  172. package/src/context/HeadlessProvider.tsx +30 -0
  173. package/src/context/config.ts +10 -0
  174. package/src/context/index.ts +3 -0
  175. package/src/index.ts +22 -0
  176. package/src/shopify/fetchers/add-cart-lines.ts +53 -0
  177. package/src/shopify/fetchers/create-cart.ts +43 -0
  178. package/src/shopify/fetchers/get-cart.ts +48 -0
  179. package/src/shopify/fetchers/get-products-by-handles.ts +48 -0
  180. package/src/shopify/fetchers/index.ts +10 -0
  181. package/src/shopify/fetchers/remove-cart-lines.ts +31 -0
  182. package/src/shopify/fetchers/update-cart-attributes.ts +29 -0
  183. package/src/shopify/fetchers/update-cart-buyer-identity.ts +32 -0
  184. package/src/shopify/fetchers/update-cart-codes.ts +32 -0
  185. package/src/shopify/fetchers/update-cart-lines.ts +42 -0
  186. package/src/shopify/fragments/article.ts +51 -0
  187. package/src/shopify/fragments/blog.ts +10 -0
  188. package/src/shopify/fragments/cart.ts +193 -0
  189. package/src/shopify/fragments/collection.ts +15 -0
  190. package/src/shopify/fragments/image.ts +8 -0
  191. package/src/shopify/fragments/index.ts +11 -0
  192. package/src/shopify/fragments/metafields.ts +17 -0
  193. package/src/shopify/fragments/page-info.ts +8 -0
  194. package/src/shopify/fragments/page.ts +12 -0
  195. package/src/shopify/fragments/product.ts +71 -0
  196. package/src/shopify/fragments/seo.ts +6 -0
  197. package/src/shopify/fragments/variant.ts +29 -0
  198. package/src/shopify/gql/fragment-masking.ts +85 -0
  199. package/src/shopify/gql/gql.ts +110 -0
  200. package/src/shopify/gql/graphql.ts +11780 -0
  201. package/src/shopify/gql/index.ts +4 -0
  202. package/src/shopify/hooks/index.ts +1 -0
  203. package/src/shopify/hooks/useProductsByHandles.ts +24 -0
  204. package/src/shopify/index.ts +5 -0
  205. package/src/shopify/mutations/add-cart-lines.ts +16 -0
  206. package/src/shopify/mutations/create-cart.ts +22 -0
  207. package/src/shopify/mutations/remove-cart-items.ts +16 -0
  208. package/src/shopify/mutations/update-cart-attributes.ts +16 -0
  209. package/src/shopify/mutations/update-cart-buyer-identity.ts +16 -0
  210. package/src/shopify/mutations/update-cart-discount-code.ts +16 -0
  211. package/src/shopify/mutations/update-cart-items.ts +16 -0
  212. package/src/shopify/queries/get-cart.ts +14 -0
  213. package/src/shopify/queries/get-product-by-handles.ts +32 -0
  214. package/src/shopify/queries/index.ts +2 -0
  215. package/src/shopify/types/article.ts +46 -0
  216. package/src/shopify/types/cart.ts +204 -0
  217. package/src/shopify/types/checkout.ts +44 -0
  218. package/src/shopify/types/collection.ts +104 -0
  219. package/src/shopify/types/common.ts +53 -0
  220. package/src/shopify/types/config.ts +14 -0
  221. package/src/shopify/types/customer.ts +31 -0
  222. package/src/shopify/types/fetcher.ts +67 -0
  223. package/src/shopify/types/index.ts +11 -0
  224. package/src/shopify/types/page.ts +45 -0
  225. package/src/shopify/types/product.ts +176 -0
  226. package/src/shopify/types/search.ts +62 -0
  227. package/src/shopify/types/site.ts +38 -0
  228. package/src/shopify/types/type-helper.ts +5 -0
  229. package/src/shopify/utils/colors.ts +206 -0
  230. package/src/shopify/utils/const.ts +115 -0
  231. package/src/shopify/utils/cookie.ts +28 -0
  232. package/src/shopify/utils/errors.ts +65 -0
  233. package/src/shopify/utils/fetch-graphql-api.ts +67 -0
  234. package/src/shopify/utils/handle-fetch-response.ts +60 -0
  235. package/src/shopify/utils/helper.ts +89 -0
  236. package/src/shopify/utils/normalize/cart.ts +106 -0
  237. package/src/shopify/utils/normalize/customer.ts +18 -0
  238. package/src/shopify/utils/normalize/index.ts +2 -0
  239. package/src/shopify/utils/normalize/metafield.ts +69 -0
  240. package/src/shopify/utils/normalize/product.ts +193 -0
  241. package/src/shopify/utils/store.ts +5 -0
  242. package/src/shopify/utils/type-helper.ts +7 -0
  243. package/tsconfig.json +20 -0
  244. package/tsup.config.ts +17 -0
  245. package/dist/index.esm.js +0 -1669
  246. package/dist/index.js +0 -1679
  247. 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,2 @@
1
+ export * from './cart'
2
+ export * from './metafield'
@@ -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
+ }