@thorprovider/adapters 3.0.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 (95) hide show
  1. package/.turbo/turbo-build.log +18 -0
  2. package/.turbo/turbo-test.log +1 -0
  3. package/.turbo/turbo-type-check.log +0 -0
  4. package/AGENTS.md +1 -0
  5. package/CHANGELOG.md +271 -0
  6. package/README.md +634 -0
  7. package/dist/.tsbuildinfo +1 -0
  8. package/dist/index.d.mts +2524 -0
  9. package/dist/index.d.ts +2524 -0
  10. package/dist/index.js +8136 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/index.mjs +8045 -0
  13. package/dist/index.mjs.map +1 -0
  14. package/docs/RETRY_SOLUTION_SUMMARY.md +55 -0
  15. package/docs/authentication.md +165 -0
  16. package/docs/capabilities-system.md +143 -0
  17. package/docs/category-customization.md +309 -0
  18. package/docs/fetch-reliability-solution.md +399 -0
  19. package/docs/getting-started.md +154 -0
  20. package/docs/multi-tenant-channel-scoping.md +225 -0
  21. package/docs/profile-management.md +147 -0
  22. package/docs/providers-overview.md +85 -0
  23. package/docs/services-overview.md +135 -0
  24. package/docs/update-documentation.md +113 -0
  25. package/package.json +43 -0
  26. package/publish.log +93 -0
  27. package/quick-reference.md +169 -0
  28. package/src/admin/AdminAuthService.ts +88 -0
  29. package/src/admin/AdminSessionService.ts +91 -0
  30. package/src/admin/MetricsService.ts +145 -0
  31. package/src/admin/index.ts +13 -0
  32. package/src/backend/BackendMetadataAdapter.ts +88 -0
  33. package/src/backend/factory.ts +107 -0
  34. package/src/backend/index.ts +12 -0
  35. package/src/backend/medusa/MedusaMetadataAdapter.ts +139 -0
  36. package/src/core/errors.js +161 -0
  37. package/src/core/errors.ts +132 -0
  38. package/src/core/fetch-utils.js +240 -0
  39. package/src/core/fetch-utils.ts +227 -0
  40. package/src/core/index.ts +8 -0
  41. package/src/core/logger.js +63 -0
  42. package/src/core/logger.ts +37 -0
  43. package/src/factory/index.ts +102 -0
  44. package/src/index.ts +205 -0
  45. package/src/lib/money-transform.ts +80 -0
  46. package/src/metadata/channel-scope-validation.js +136 -0
  47. package/src/metadata/channel-scope-validation.ts +151 -0
  48. package/src/metadata/channel-scope.js +174 -0
  49. package/src/metadata/channel-scope.ts +215 -0
  50. package/src/metadata/index.js +31 -0
  51. package/src/metadata/index.ts +37 -0
  52. package/src/providers/bigcommerce/bigcommerce.ts +6 -0
  53. package/src/providers/bigcommerce/index.ts +315 -0
  54. package/src/providers/bigcommerce/transforms.ts +227 -0
  55. package/src/providers/index.ts +22 -0
  56. package/src/providers/magento/index.ts +387 -0
  57. package/src/providers/magento/magento.ts +5 -0
  58. package/src/providers/magento/queries.ts +267 -0
  59. package/src/providers/magento/transforms.ts +249 -0
  60. package/src/providers/medusa/auth.js +601 -0
  61. package/src/providers/medusa/auth.ts +496 -0
  62. package/src/providers/medusa/fulfillment-planner.ts +225 -0
  63. package/src/providers/medusa/index.js +2485 -0
  64. package/src/providers/medusa/index.ts +4239 -0
  65. package/src/providers/medusa/medusa.ts +8 -0
  66. package/src/providers/medusa/payment.js +265 -0
  67. package/src/providers/medusa/payment.ts +269 -0
  68. package/src/providers/medusa/transforms.js +505 -0
  69. package/src/providers/medusa/transforms.ts +625 -0
  70. package/src/providers/shopify/index.ts +444 -0
  71. package/src/providers/shopify/mutations.ts +102 -0
  72. package/src/providers/shopify/queries.ts +324 -0
  73. package/src/providers/shopify/shopify.ts +7 -0
  74. package/src/providers/shopify/transforms.ts +270 -0
  75. package/src/providers/spree/index.ts +344 -0
  76. package/src/providers/spree/spree.ts +5 -0
  77. package/src/providers/spree/transforms.ts +254 -0
  78. package/src/providers/woocommerce/index.ts +310 -0
  79. package/src/providers/woocommerce/transforms.ts +254 -0
  80. package/src/providers/woocommerce/woocommerce.ts +5 -0
  81. package/src/resilience/README.md +299 -0
  82. package/src/resilience/circuit-breaker.ts +222 -0
  83. package/src/resilience/index.ts +10 -0
  84. package/src/types/capabilities.types.js +141 -0
  85. package/src/types/capabilities.types.ts +366 -0
  86. package/src/types/index.ts +24 -0
  87. package/src/types/provider.js +9 -0
  88. package/src/types/provider.ts +9 -0
  89. package/src/types/ui-responses.ts +261 -0
  90. package/src/utils/city-coordinates.ts +40 -0
  91. package/src/utils/geolocation.ts +52 -0
  92. package/src/utils/payment-utils.js +152 -0
  93. package/src/utils/payment-utils.ts +203 -0
  94. package/tsconfig.json +15 -0
  95. package/tsup.config.ts +14 -0
@@ -0,0 +1,444 @@
1
+ /**
2
+ * @thorprovider/adapters — Shopify Provider
3
+ * Implements CommerceProvider for Shopify Storefront API (GraphQL)
4
+ *
5
+ * Uses @shopify/storefront-api-client for type-safe GraphQL operations.
6
+ * Authentication via public/private access tokens (legacy Storefront tokens).
7
+ */
8
+
9
+ import type {
10
+ Product,
11
+ GetProductsOptions,
12
+ Cart,
13
+ CartLineInput,
14
+ CartLineUpdate,
15
+ Collection,
16
+ CollectionProductsOptions,
17
+ GetCollectionsOptions,
18
+ ProductCategory,
19
+ GetCategoriesOptions,
20
+ Order,
21
+ GetOrdersOptions,
22
+ Customer,
23
+ Address,
24
+ PaymentMethod,
25
+ PaymentMethodsOptions,
26
+ ShippingMethod,
27
+ StockLocation,
28
+ InventoryLevel,
29
+ StockValidation,
30
+ FulfillmentOption,
31
+ StorefrontContext,
32
+ BackendCapabilities,
33
+ } from '@thorprovider/types';
34
+ import type { CommerceProvider, ProviderConfig } from '../../types/provider';
35
+ import { SHOPIFY_CAPABILITIES } from '../../types/capabilities.types';
36
+ import { createLogger } from '../../core/logger';
37
+ import { ProviderAPIError, ResourceNotFoundError } from '../../core/errors';
38
+ import { transformProduct, transformCart, transformCollection, transformCustomer, transformOrder } from './transforms';
39
+ import {
40
+ GET_PRODUCT_BY_HANDLE,
41
+ GET_PRODUCTS,
42
+ GET_PRODUCT_RECOMMENDATIONS,
43
+ GET_COLLECTION_BY_HANDLE,
44
+ GET_COLLECTIONS,
45
+ GET_COLLECTION_PRODUCTS,
46
+ GET_CART,
47
+ GET_CUSTOMER,
48
+ } from './queries';
49
+ import {
50
+ CART_CREATE,
51
+ CART_LINES_ADD,
52
+ CART_LINES_UPDATE,
53
+ CART_LINES_REMOVE,
54
+ CART_DISCOUNT_CODES_UPDATE,
55
+ } from './mutations';
56
+
57
+ /**
58
+ * Shopify provider configuration
59
+ */
60
+ export interface ShopifyProviderConfig extends ProviderConfig {
61
+ /** Store domain (e.g., 'your-store.myshopify.com') */
62
+ storeDomain: string;
63
+ /** Public Storefront API access token */
64
+ publicAccessToken: string;
65
+ /** Private Storefront API access token (server-side only) */
66
+ privateAccessToken?: string;
67
+ /** API version (default: '2026-04') */
68
+ apiVersion?: string;
69
+ }
70
+
71
+ /**
72
+ * Shopify commerce provider
73
+ *
74
+ * Implements CommerceProvider for Shopify Storefront API.
75
+ * Uses raw fetch + GraphQL queries (no SDK dependency to stay L2-pure).
76
+ */
77
+ export class ShopifyProvider implements CommerceProvider {
78
+ readonly name = 'shopify';
79
+ readonly version = '1.0.0';
80
+ readonly capabilities: BackendCapabilities = SHOPIFY_CAPABILITIES;
81
+
82
+ private logger: ReturnType<typeof createLogger>;
83
+ private storeDomain: string;
84
+ private apiVersion: string;
85
+ private endpoint: string;
86
+ private headers: Record<string, string>;
87
+
88
+ constructor(config: ShopifyProviderConfig) {
89
+ this.logger = createLogger('ShopifyProvider', config.debug);
90
+ this.storeDomain = config.storeDomain.replace(/^https?:\/\//, '').replace(/\/$/, '');
91
+ this.apiVersion = config.apiVersion ?? '2026-04';
92
+ this.endpoint = `https://${this.storeDomain}/api/${this.apiVersion}/graphql.json`;
93
+
94
+ this.headers = {
95
+ 'Content-Type': 'application/json',
96
+ ...(config.privateAccessToken
97
+ ? { 'Shopify-Storefront-Private-Token': config.privateAccessToken }
98
+ : { 'X-Shopify-Storefront-Access-Token': config.publicAccessToken }),
99
+ };
100
+
101
+ this.logger.info('Initializing Shopify Provider', {
102
+ storeDomain: this.storeDomain,
103
+ apiVersion: this.apiVersion,
104
+ authType: config.privateAccessToken ? 'private' : 'public',
105
+ });
106
+ }
107
+
108
+ // ============================================
109
+ // GraphQL Client
110
+ // ============================================
111
+
112
+ private async query<T = any>(gql: string, variables?: Record<string, any>): Promise<T> {
113
+ const response = await fetch(this.endpoint, {
114
+ method: 'POST',
115
+ headers: this.headers,
116
+ body: JSON.stringify({ query: gql, variables }),
117
+ });
118
+
119
+ if (!response.ok) {
120
+ throw new ProviderAPIError(
121
+ `Shopify API error: ${response.status} ${response.statusText}`,
122
+ 'SHOPIFY_API_ERROR',
123
+ response.status,
124
+ );
125
+ }
126
+
127
+ const json = await response.json() as { data?: T; errors?: Array<{ message: string }> };
128
+
129
+ if (json.errors?.length) {
130
+ const message = json.errors.map((e) => e.message).join('; ');
131
+ throw new ProviderAPIError(`Shopify GraphQL error: ${message}`, 'SHOPIFY_GRAPHQL_ERROR');
132
+ }
133
+
134
+ return json.data as T;
135
+ }
136
+
137
+ // ============================================
138
+ // Product Operations
139
+ // ============================================
140
+
141
+ async getProduct(handle: string): Promise<Product | undefined> {
142
+ const data = await this.query<{ product: any }>(GET_PRODUCT_BY_HANDLE, { handle });
143
+ return data.product ? transformProduct(data.product) : undefined;
144
+ }
145
+
146
+ async getProducts(options?: GetProductsOptions): Promise<Product[]> {
147
+ const first = options?.first ?? 20;
148
+ const sortKeyMap: Record<string, string> = {
149
+ 'title-asc': 'TITLE',
150
+ 'title-desc': 'TITLE',
151
+ 'price-asc': 'PRICE',
152
+ 'price-desc': 'PRICE',
153
+ 'best-selling': 'BEST_SELLING',
154
+ 'created-desc': 'CREATED_AT',
155
+ 'relevance': 'RELEVANCE',
156
+ };
157
+ const sortKey = options?.sortKey ? (sortKeyMap[options.sortKey] ?? 'RELEVANCE') : 'RELEVANCE';
158
+ const reverse = options?.sortKey?.endsWith('-desc') ?? false;
159
+
160
+ const data = await this.query<{ products: { edges: Array<{ node: any }> } }>(GET_PRODUCTS, {
161
+ first,
162
+ query: options?.query,
163
+ sortKey,
164
+ reverse,
165
+ });
166
+
167
+ return data.products.edges.map((edge) => transformProduct(edge.node));
168
+ }
169
+
170
+ async getProductRecommendations(productId: string): Promise<Product[]> {
171
+ const data = await this.query<{ productRecommendations: any[] }>(GET_PRODUCT_RECOMMENDATIONS, {
172
+ productId,
173
+ });
174
+ return (data.productRecommendations ?? []).map(transformProduct);
175
+ }
176
+
177
+ // ============================================
178
+ // Collection Operations
179
+ // ============================================
180
+
181
+ async getCollection(handle: string): Promise<Collection | undefined> {
182
+ const data = await this.query<{ collection: any }>(GET_COLLECTION_BY_HANDLE, { handle });
183
+ return data.collection ? transformCollection(data.collection) : undefined;
184
+ }
185
+
186
+ async getCollections(options?: GetCollectionsOptions): Promise<Collection[]> {
187
+ const data = await this.query<{ collections: { edges: Array<{ node: any }> } }>(GET_COLLECTIONS, {
188
+ first: options?.limit ?? 50,
189
+ });
190
+ return data.collections.edges.map((edge) => transformCollection(edge.node));
191
+ }
192
+
193
+ async getCollectionProducts(handle: string, options?: CollectionProductsOptions): Promise<Product[]> {
194
+ const data = await this.query<{ collection: { products: { edges: Array<{ node: any }> } } }>(
195
+ GET_COLLECTION_PRODUCTS,
196
+ {
197
+ handle,
198
+ first: options?.first ?? 20,
199
+ sortKey: options?.sortKey === 'price-asc' || options?.sortKey === 'price-desc' ? 'PRICE' : 'BEST_SELLING',
200
+ reverse: options?.sortKey?.endsWith('-desc') ?? false,
201
+ },
202
+ );
203
+ return (data.collection?.products?.edges ?? []).map((edge) => transformProduct(edge.node));
204
+ }
205
+
206
+ // ============================================
207
+ // Category Operations (Shopify does not have native categories)
208
+ // ============================================
209
+
210
+ async getCategory(): Promise<ProductCategory | undefined> {
211
+ this.logger.warn('Shopify does not support native categories. Use collections instead.');
212
+ return undefined;
213
+ }
214
+
215
+ async getCategories(): Promise<ProductCategory[]> {
216
+ return [];
217
+ }
218
+
219
+ async getCategoryProducts(): Promise<Product[]> {
220
+ return [];
221
+ }
222
+
223
+ // ============================================
224
+ // Cart Operations
225
+ // ============================================
226
+
227
+ async createCart(): Promise<Cart> {
228
+ const data = await this.query<{ cartCreate: { cart: any; userErrors: any[] } }>(CART_CREATE, {
229
+ input: {},
230
+ });
231
+
232
+ if (data.cartCreate.userErrors?.length) {
233
+ throw new ProviderAPIError(
234
+ `Cart creation failed: ${data.cartCreate.userErrors.map((e: any) => e.message).join('; ')}`,
235
+ 'SHOPIFY_CART_ERROR',
236
+ );
237
+ }
238
+
239
+ return transformCart(data.cartCreate.cart);
240
+ }
241
+
242
+ async getCart(cartId: string): Promise<Cart | undefined> {
243
+ const data = await this.query<{ cart: any }>(GET_CART, { cartId });
244
+ return data.cart ? transformCart(data.cart) : undefined;
245
+ }
246
+
247
+ async addToCart(cartId: string, lines: CartLineInput[]): Promise<Cart> {
248
+ const data = await this.query<{ cartLinesAdd: { cart: any; userErrors: any[] } }>(CART_LINES_ADD, {
249
+ cartId,
250
+ lines: lines.map((l) => ({ merchandiseId: l.merchandiseId, quantity: l.quantity })),
251
+ });
252
+
253
+ if (data.cartLinesAdd.userErrors?.length) {
254
+ throw new ProviderAPIError(
255
+ `Add to cart failed: ${data.cartLinesAdd.userErrors.map((e: any) => e.message).join('; ')}`,
256
+ 'SHOPIFY_CART_ERROR',
257
+ );
258
+ }
259
+
260
+ return transformCart(data.cartLinesAdd.cart);
261
+ }
262
+
263
+ async removeFromCart(cartId: string, lineIds: string[]): Promise<Cart> {
264
+ const data = await this.query<{ cartLinesRemove: { cart: any; userErrors: any[] } }>(CART_LINES_REMOVE, {
265
+ cartId,
266
+ lineIds,
267
+ });
268
+
269
+ if (data.cartLinesRemove.userErrors?.length) {
270
+ throw new ProviderAPIError(
271
+ `Remove from cart failed: ${data.cartLinesRemove.userErrors.map((e: any) => e.message).join('; ')}`,
272
+ 'SHOPIFY_CART_ERROR',
273
+ );
274
+ }
275
+
276
+ return transformCart(data.cartLinesRemove.cart);
277
+ }
278
+
279
+ async updateCart(cartId: string, lines: CartLineUpdate[]): Promise<Cart> {
280
+ const data = await this.query<{ cartLinesUpdate: { cart: any; userErrors: any[] } }>(CART_LINES_UPDATE, {
281
+ cartId,
282
+ lines: lines.map((l) => ({ id: l.id, quantity: l.quantity })),
283
+ });
284
+
285
+ if (data.cartLinesUpdate.userErrors?.length) {
286
+ throw new ProviderAPIError(
287
+ `Update cart failed: ${data.cartLinesUpdate.userErrors.map((e: any) => e.message).join('; ')}`,
288
+ 'SHOPIFY_CART_ERROR',
289
+ );
290
+ }
291
+
292
+ return transformCart(data.cartLinesUpdate.cart);
293
+ }
294
+
295
+ // ============================================
296
+ // Checkout Operations
297
+ // ============================================
298
+
299
+ async completeCheckout(_cartId: string): Promise<Order> {
300
+ // Shopify checkout is hosted — redirect to cart.checkoutUrl
301
+ // This method cannot programmatically complete checkout
302
+ throw new ProviderAPIError(
303
+ 'Shopify uses hosted checkout. Redirect the buyer to cart.checkoutUrl instead of calling completeCheckout().',
304
+ 'SHOPIFY_HOSTED_CHECKOUT',
305
+ );
306
+ }
307
+
308
+ async getShippingMethods(_cartId: string): Promise<ShippingMethod[]> {
309
+ // Shopify handles shipping selection in hosted checkout
310
+ return [];
311
+ }
312
+
313
+ async getRegionCountries(): Promise<import('@thorprovider/types').Country[]> {
314
+ return [];
315
+ }
316
+
317
+ async addShippingMethod(_cartId: string, _methodId: string): Promise<Cart> {
318
+ throw new ProviderAPIError(
319
+ 'Shopify handles shipping selection in hosted checkout.',
320
+ 'SHOPIFY_HOSTED_CHECKOUT',
321
+ );
322
+ }
323
+
324
+ async initiatePaymentSession(_cartId: string, _providerId: string): Promise<void> {
325
+ throw new ProviderAPIError(
326
+ 'Shopify handles payment in hosted checkout.',
327
+ 'SHOPIFY_HOSTED_CHECKOUT',
328
+ );
329
+ }
330
+
331
+ async applyDiscountCode(cartId: string, code: string): Promise<Cart> {
332
+ const currentCart = await this.getCart(cartId);
333
+ const existingCodes: string[] = []; // Shopify replaces all codes
334
+ existingCodes.push(code);
335
+
336
+ const data = await this.query<{ cartDiscountCodesUpdate: { cart: any; userErrors: any[] } }>(
337
+ CART_DISCOUNT_CODES_UPDATE,
338
+ { cartId, discountCodes: existingCodes },
339
+ );
340
+
341
+ if (data.cartDiscountCodesUpdate.userErrors?.length) {
342
+ throw new ProviderAPIError(
343
+ `Discount code failed: ${data.cartDiscountCodesUpdate.userErrors.map((e: any) => e.message).join('; ')}`,
344
+ 'SHOPIFY_DISCOUNT_ERROR',
345
+ );
346
+ }
347
+
348
+ return transformCart(data.cartDiscountCodesUpdate.cart);
349
+ }
350
+
351
+ async removeDiscountCode(cartId: string, _code: string): Promise<Cart> {
352
+ const data = await this.query<{ cartDiscountCodesUpdate: { cart: any; userErrors: any[] } }>(
353
+ CART_DISCOUNT_CODES_UPDATE,
354
+ { cartId, discountCodes: [] },
355
+ );
356
+
357
+ return transformCart(data.cartDiscountCodesUpdate.cart);
358
+ }
359
+
360
+ // ============================================
361
+ // Customer Operations
362
+ // ============================================
363
+
364
+ async getOrders(options: GetOrdersOptions): Promise<Order[]> {
365
+ this.logger.warn('Shopify orders require Customer Account API (planned upgrade path).');
366
+ return [];
367
+ }
368
+
369
+ // ============================================
370
+ // Payment Operations
371
+ // ============================================
372
+
373
+ async getPaymentMethods(_options: PaymentMethodsOptions): Promise<PaymentMethod[]> {
374
+ // Shopify payment selection happens in hosted checkout
375
+ return [];
376
+ }
377
+
378
+ // ============================================
379
+ // Inventory Operations (not available via Storefront API)
380
+ // ============================================
381
+
382
+ async getStockLocations(): Promise<StockLocation[]> {
383
+ return [];
384
+ }
385
+
386
+ async checkInventoryLevels(): Promise<InventoryLevel[]> {
387
+ return [];
388
+ }
389
+
390
+ async validateCartStock(): Promise<StockValidation> {
391
+ return { allAvailable: true, unavailableItems: [] };
392
+ }
393
+
394
+ async getFulfillmentOptions(): Promise<FulfillmentOption[]> {
395
+ return [];
396
+ }
397
+
398
+ // ============================================
399
+ // Admin Operations (not available via Storefront API)
400
+ // ============================================
401
+
402
+ async cancelOrder(_orderId: string): Promise<Order> {
403
+ throw new ProviderAPIError(
404
+ 'Order cancellation requires Shopify Admin API (not available in Storefront API).',
405
+ 'SHOPIFY_ADMIN_REQUIRED',
406
+ );
407
+ }
408
+
409
+ async createFulfillment(_orderId: string): Promise<void> {
410
+ throw new ProviderAPIError(
411
+ 'Fulfillment requires Shopify Admin API.',
412
+ 'SHOPIFY_ADMIN_REQUIRED',
413
+ );
414
+ }
415
+
416
+ async createRefund(_orderId: string): Promise<void> {
417
+ throw new ProviderAPIError(
418
+ 'Refunds require Shopify Admin API.',
419
+ 'SHOPIFY_ADMIN_REQUIRED',
420
+ );
421
+ }
422
+
423
+ async getAdminOrders(): Promise<{ orders: Order[]; count: number }> {
424
+ return { orders: [], count: 0 };
425
+ }
426
+
427
+ // ============================================
428
+ // Storefront / Multi-Tenant
429
+ // ============================================
430
+
431
+ async getStorefrontContext(): Promise<StorefrontContext> {
432
+ return {
433
+ id: this.storeDomain,
434
+ name: this.storeDomain,
435
+ platformType: 'shopify',
436
+ requiresProductScoping: false,
437
+ currencyCode: 'USD',
438
+ };
439
+ }
440
+
441
+ isStorefrontRequired(): boolean {
442
+ return false;
443
+ }
444
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * @thorprovider/adapters — Shopify Provider
3
+ * GraphQL mutation definitions for Storefront API
4
+ */
5
+
6
+ import { CART_FRAGMENT } from './queries';
7
+
8
+ export const CART_CREATE = `
9
+ mutation CartCreate($input: CartInput!) {
10
+ cartCreate(input: $input) {
11
+ cart {
12
+ ...CartFields
13
+ }
14
+ userErrors {
15
+ field
16
+ message
17
+ code
18
+ }
19
+ }
20
+ }
21
+ ${CART_FRAGMENT}
22
+ `;
23
+
24
+ export const CART_LINES_ADD = `
25
+ mutation CartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
26
+ cartLinesAdd(cartId: $cartId, lines: $lines) {
27
+ cart {
28
+ ...CartFields
29
+ }
30
+ userErrors {
31
+ field
32
+ message
33
+ code
34
+ }
35
+ }
36
+ }
37
+ ${CART_FRAGMENT}
38
+ `;
39
+
40
+ export const CART_LINES_UPDATE = `
41
+ mutation CartLinesUpdate($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
42
+ cartLinesUpdate(cartId: $cartId, lines: $lines) {
43
+ cart {
44
+ ...CartFields
45
+ }
46
+ userErrors {
47
+ field
48
+ message
49
+ code
50
+ }
51
+ }
52
+ }
53
+ ${CART_FRAGMENT}
54
+ `;
55
+
56
+ export const CART_LINES_REMOVE = `
57
+ mutation CartLinesRemove($cartId: ID!, $lineIds: [ID!]!) {
58
+ cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
59
+ cart {
60
+ ...CartFields
61
+ }
62
+ userErrors {
63
+ field
64
+ message
65
+ code
66
+ }
67
+ }
68
+ }
69
+ ${CART_FRAGMENT}
70
+ `;
71
+
72
+ export const CART_DISCOUNT_CODES_UPDATE = `
73
+ mutation CartDiscountCodesUpdate($cartId: ID!, $discountCodes: [String!]!) {
74
+ cartDiscountCodesUpdate(cartId: $cartId, discountCodes: $discountCodes) {
75
+ cart {
76
+ ...CartFields
77
+ }
78
+ userErrors {
79
+ field
80
+ message
81
+ code
82
+ }
83
+ }
84
+ }
85
+ ${CART_FRAGMENT}
86
+ `;
87
+
88
+ export const CUSTOMER_ACCESS_TOKEN_CREATE = `
89
+ mutation CustomerAccessTokenCreate($input: CustomerAccessTokenCreateInput!) {
90
+ customerAccessTokenCreate(input: $input) {
91
+ customerAccessToken {
92
+ accessToken
93
+ expiresAt
94
+ }
95
+ customerUserErrors {
96
+ field
97
+ message
98
+ code
99
+ }
100
+ }
101
+ }
102
+ `;