@thorprovider/adapters 4.2.1 → 5.0.0
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/PROVIDER_IMPLEMENTATION_ANALYSIS.md +659 -0
- package/dist/index.d.mts +225 -387
- package/dist/index.d.ts +225 -387
- package/dist/index.js +613 -3062
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +609 -3052
- package/dist/index.mjs.map +1 -1
- package/docs/RETRY_SOLUTION_SUMMARY.md +26 -11
- package/docs/authentication.md +115 -62
- package/docs/capabilities-system.md +4 -2
- package/docs/category-customization.md +37 -29
- package/docs/fetch-reliability-solution.md +127 -278
- package/docs/getting-started.md +43 -29
- package/docs/profile-management.md +68 -38
- package/docs/services-overview.md +4 -2
- package/package.json +1 -1
- package/quick-reference.md +33 -0
- package/src/backend/factory.ts +1 -11
- package/src/factory/index.ts +74 -52
- package/src/index.ts +5 -15
- package/src/providers/index.ts +1 -16
- package/src/providers/medusa/admin.test.ts +404 -0
- package/src/providers/medusa/admin.ts +442 -0
- package/src/providers/medusa/auth.test.ts +137 -0
- package/src/providers/medusa/auth.ts +96 -1
- package/src/providers/medusa/index.ts +21 -1
- package/src/resilience/README.md +70 -48
- package/src/types/capabilities.types.ts +0 -250
- package/src/core/errors.js +0 -161
- package/src/core/fetch-utils.js +0 -240
- package/src/core/logger.js +0 -63
- package/src/metadata/channel-scope-validation.js +0 -136
- package/src/metadata/channel-scope.js +0 -174
- package/src/metadata/index.js +0 -31
- package/src/providers/bigcommerce/bigcommerce.ts +0 -6
- package/src/providers/bigcommerce/index.ts +0 -315
- package/src/providers/bigcommerce/transforms.ts +0 -227
- package/src/providers/magento/index.ts +0 -387
- package/src/providers/magento/magento.ts +0 -5
- package/src/providers/magento/queries.ts +0 -267
- package/src/providers/magento/transforms.ts +0 -249
- package/src/providers/medusa/auth.js +0 -601
- package/src/providers/medusa/index.js +0 -2485
- package/src/providers/medusa/payment.js +0 -265
- package/src/providers/medusa/transforms.js +0 -505
- package/src/providers/shopify/auth.ts +0 -402
- package/src/providers/shopify/index.ts +0 -444
- package/src/providers/shopify/mutations.ts +0 -247
- package/src/providers/shopify/queries.ts +0 -324
- package/src/providers/shopify/shopify.ts +0 -7
- package/src/providers/shopify/transforms.ts +0 -275
- package/src/providers/spree/index.ts +0 -344
- package/src/providers/spree/spree.ts +0 -5
- package/src/providers/spree/transforms.ts +0 -254
- package/src/providers/woocommerce/index.ts +0 -310
- package/src/providers/woocommerce/transforms.ts +0 -254
- package/src/providers/woocommerce/woocommerce.ts +0 -5
- package/src/types/capabilities.types.js +0 -141
- package/src/types/provider.js +0 -9
- package/src/utils/payment-utils.js +0 -152
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @thorprovider/adapters — BigCommerce Provider
|
|
3
|
-
* Transform functions: BigCommerce REST API → @thorprovider/types
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type {
|
|
7
|
-
Product,
|
|
8
|
-
ProductVariant,
|
|
9
|
-
ProductOption,
|
|
10
|
-
Cart,
|
|
11
|
-
CartItem,
|
|
12
|
-
Collection,
|
|
13
|
-
Money,
|
|
14
|
-
Image,
|
|
15
|
-
Order,
|
|
16
|
-
OrderItem,
|
|
17
|
-
OrderStatus,
|
|
18
|
-
PaymentStatus,
|
|
19
|
-
FulfillmentStatus,
|
|
20
|
-
Customer,
|
|
21
|
-
Address,
|
|
22
|
-
} from '@thorprovider/types';
|
|
23
|
-
|
|
24
|
-
// ============================================
|
|
25
|
-
// Money & Image Transforms
|
|
26
|
-
// ============================================
|
|
27
|
-
|
|
28
|
-
function transformMoney(amount: number | string | null | undefined, currencyCode: string = 'USD'): Money {
|
|
29
|
-
const numAmount = typeof amount === 'string' ? parseFloat(amount) : (amount ?? 0);
|
|
30
|
-
return {
|
|
31
|
-
amount: numAmount.toFixed(2),
|
|
32
|
-
currencyCode: currencyCode.toUpperCase(),
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function transformImage(url: string, alt: string = ''): Image {
|
|
37
|
-
return { url, altText: alt, width: 800, height: 800 };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// ============================================
|
|
41
|
-
// Product Transforms
|
|
42
|
-
// ============================================
|
|
43
|
-
|
|
44
|
-
export function transformProduct(raw: any, currencyCode: string = 'USD'): Product {
|
|
45
|
-
const images: Image[] = (raw.images ?? []).map((img: any) =>
|
|
46
|
-
transformImage(img.url_standard ?? img.url_thumbnail ?? '', img.description ?? ''),
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
const variants: ProductVariant[] = (raw.variants ?? []).map((v: any) => ({
|
|
50
|
-
id: String(v.id),
|
|
51
|
-
title: v.option_display_name ?? `Variant ${v.id}`,
|
|
52
|
-
availableForSale: (v.purchasing_disabled !== true),
|
|
53
|
-
price: transformMoney(v.price ?? raw.price, currencyCode),
|
|
54
|
-
compareAtPrice: v.sale_price ? transformMoney(v.sale_price, currencyCode) : undefined,
|
|
55
|
-
selectedOptions: (v.option_values ?? []).map((ov: any) => ({
|
|
56
|
-
name: ov.option_display_name ?? '',
|
|
57
|
-
value: ov.label ?? '',
|
|
58
|
-
})),
|
|
59
|
-
}));
|
|
60
|
-
|
|
61
|
-
const options: ProductOption[] = (raw.options ?? []).map((opt: any) => ({
|
|
62
|
-
id: String(opt.id),
|
|
63
|
-
name: opt.display_name ?? '',
|
|
64
|
-
values: (opt.option_values ?? []).map((val: any) => val.label ?? ''),
|
|
65
|
-
}));
|
|
66
|
-
|
|
67
|
-
const price = raw.price ?? raw.calculated_price?.as_entered ?? 0;
|
|
68
|
-
|
|
69
|
-
return {
|
|
70
|
-
id: String(raw.id),
|
|
71
|
-
handle: raw.custom_url?.url?.replace(/^\//, '').replace(/\/$/, '') ?? String(raw.id),
|
|
72
|
-
title: raw.name ?? '',
|
|
73
|
-
description: raw.plain_text_description ?? raw.description ?? '',
|
|
74
|
-
descriptionHtml: raw.description ?? '',
|
|
75
|
-
availableForSale: raw.availability === 'available' || raw.is_visible !== false,
|
|
76
|
-
featuredImage: images[0] ?? { url: '', altText: '', width: 800, height: 800 },
|
|
77
|
-
images,
|
|
78
|
-
options,
|
|
79
|
-
variants,
|
|
80
|
-
priceRange: {
|
|
81
|
-
minVariantPrice: transformMoney(price, currencyCode),
|
|
82
|
-
maxVariantPrice: transformMoney(price, currencyCode),
|
|
83
|
-
},
|
|
84
|
-
seo: {
|
|
85
|
-
title: raw.page_title ?? raw.name ?? '',
|
|
86
|
-
description: raw.meta_description ?? '',
|
|
87
|
-
},
|
|
88
|
-
tags: [],
|
|
89
|
-
createdAt: raw.date_created ?? new Date().toISOString(),
|
|
90
|
-
updatedAt: raw.date_modified ?? new Date().toISOString(),
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// ============================================
|
|
95
|
-
// Collection Transforms (BigCommerce "Categories" or "Brands")
|
|
96
|
-
// ============================================
|
|
97
|
-
|
|
98
|
-
export function transformCollection(raw: any): Collection {
|
|
99
|
-
return {
|
|
100
|
-
id: String(raw.id ?? ''),
|
|
101
|
-
handle: raw.custom_url?.url?.replace(/^\//, '').replace(/\/$/, '') ?? String(raw.id),
|
|
102
|
-
title: raw.name ?? '',
|
|
103
|
-
description: raw.description ?? '',
|
|
104
|
-
seo: {
|
|
105
|
-
title: raw.page_title ?? raw.name ?? '',
|
|
106
|
-
description: raw.meta_description ?? '',
|
|
107
|
-
},
|
|
108
|
-
updatedAt: raw.date_modified ?? new Date().toISOString(),
|
|
109
|
-
path: `/collections/${raw.custom_url?.url?.replace(/^\//, '').replace(/\/$/, '') ?? raw.id}`,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// ============================================
|
|
114
|
-
// Cart Transforms
|
|
115
|
-
// ============================================
|
|
116
|
-
|
|
117
|
-
export function transformCart(raw: any, currencyCode: string = 'USD'): Cart {
|
|
118
|
-
const lineItems = Object.values(raw.line_items ?? {}).flat() as any[];
|
|
119
|
-
|
|
120
|
-
const lines: CartItem[] = lineItems.map((item: any) => ({
|
|
121
|
-
id: item.id,
|
|
122
|
-
quantity: item.quantity ?? 1,
|
|
123
|
-
cost: {
|
|
124
|
-
totalAmount: transformMoney(item.extended_sale_price ?? item.sale_price ?? 0, currencyCode),
|
|
125
|
-
},
|
|
126
|
-
merchandise: {
|
|
127
|
-
id: String(item.variant_id ?? item.product_id),
|
|
128
|
-
title: item.name ?? '',
|
|
129
|
-
selectedOptions: (item.options ?? []).map((opt: any) => ({
|
|
130
|
-
name: opt.name ?? '',
|
|
131
|
-
value: opt.value ?? '',
|
|
132
|
-
})),
|
|
133
|
-
product: {
|
|
134
|
-
id: String(item.product_id),
|
|
135
|
-
handle: String(item.product_id),
|
|
136
|
-
title: item.name ?? '',
|
|
137
|
-
featuredImage: item.image_url
|
|
138
|
-
? transformImage(item.image_url)
|
|
139
|
-
: { url: '', altText: '', width: 800, height: 800 },
|
|
140
|
-
},
|
|
141
|
-
},
|
|
142
|
-
}));
|
|
143
|
-
|
|
144
|
-
return {
|
|
145
|
-
id: raw.id,
|
|
146
|
-
checkoutUrl: raw.redirect_urls?.checkout_url ?? '',
|
|
147
|
-
totalQuantity: lines.reduce((sum, l) => sum + l.quantity, 0),
|
|
148
|
-
cost: {
|
|
149
|
-
subtotalAmount: transformMoney(raw.base_amount ?? 0, currencyCode),
|
|
150
|
-
totalAmount: transformMoney(raw.cart_amount ?? 0, currencyCode),
|
|
151
|
-
},
|
|
152
|
-
lines,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// ============================================
|
|
157
|
-
// Customer Transforms
|
|
158
|
-
// ============================================
|
|
159
|
-
|
|
160
|
-
export function transformCustomer(raw: any): Customer {
|
|
161
|
-
return {
|
|
162
|
-
id: String(raw.id),
|
|
163
|
-
email: raw.email ?? '',
|
|
164
|
-
firstName: raw.first_name ?? '',
|
|
165
|
-
lastName: raw.last_name ?? '',
|
|
166
|
-
phone: raw.phone ?? '',
|
|
167
|
-
hasAccount: true,
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export function transformAddress(raw: any): Address {
|
|
172
|
-
return {
|
|
173
|
-
id: String(raw.id ?? ''),
|
|
174
|
-
firstName: raw.first_name ?? '',
|
|
175
|
-
lastName: raw.last_name ?? '',
|
|
176
|
-
address1: raw.address1 ?? '',
|
|
177
|
-
address2: raw.address2 ?? '',
|
|
178
|
-
city: raw.city ?? '',
|
|
179
|
-
province: raw.state_or_province ?? '',
|
|
180
|
-
countryCode: raw.country_code ?? '',
|
|
181
|
-
postalCode: raw.postal_code ?? '',
|
|
182
|
-
phone: raw.phone ?? '',
|
|
183
|
-
company: raw.company ?? '',
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// ============================================
|
|
188
|
-
// Order Transforms
|
|
189
|
-
// ============================================
|
|
190
|
-
|
|
191
|
-
export function transformOrder(raw: any, currencyCode: string = 'USD'): Order {
|
|
192
|
-
const items: OrderItem[] = (raw.products ?? []).map((p: any) => ({
|
|
193
|
-
id: String(p.id),
|
|
194
|
-
title: p.name ?? '',
|
|
195
|
-
variantTitle: p.sku ?? '',
|
|
196
|
-
quantity: p.quantity ?? 1,
|
|
197
|
-
price: transformMoney(p.base_price ?? 0, currencyCode),
|
|
198
|
-
total: transformMoney(p.base_total ?? 0, currencyCode),
|
|
199
|
-
productId: String(p.product_id),
|
|
200
|
-
}));
|
|
201
|
-
|
|
202
|
-
const statusMap: Record<string, OrderStatus> = {
|
|
203
|
-
'Incomplete': 'pending',
|
|
204
|
-
'Pending': 'pending',
|
|
205
|
-
'Shipped': 'completed',
|
|
206
|
-
'Partially Shipped': 'processing',
|
|
207
|
-
'Refunded': 'refunded',
|
|
208
|
-
'Cancelled': 'cancelled',
|
|
209
|
-
'Completed': 'completed',
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
return {
|
|
213
|
-
id: String(raw.id),
|
|
214
|
-
orderNumber: String(raw.id),
|
|
215
|
-
email: raw.billing_address?.email ?? '',
|
|
216
|
-
status: statusMap[raw.status] ?? 'pending',
|
|
217
|
-
paymentStatus: raw.payment_status === 'paid' ? 'paid' : 'pending',
|
|
218
|
-
fulfillmentStatus: raw.status === 'Shipped' ? 'fulfilled' : 'unfulfilled',
|
|
219
|
-
items,
|
|
220
|
-
total: transformMoney(raw.total_inc_tax ?? 0, currencyCode),
|
|
221
|
-
subtotal: transformMoney(raw.subtotal_inc_tax ?? 0, currencyCode),
|
|
222
|
-
tax: transformMoney(raw.total_tax ?? 0, currencyCode),
|
|
223
|
-
shipping: transformMoney(raw.shipping_cost_inc_tax ?? 0, currencyCode),
|
|
224
|
-
createdAt: raw.date_created ?? new Date().toISOString(),
|
|
225
|
-
updatedAt: raw.date_modified ?? new Date().toISOString(),
|
|
226
|
-
};
|
|
227
|
-
}
|
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @thorprovider/adapters — Magento (Adobe Commerce) Provider
|
|
3
|
-
* Implements CommerceProvider for Magento 2 GraphQL + REST APIs
|
|
4
|
-
*
|
|
5
|
-
* Uses GraphQL Storefront for read operations (products, categories, cart).
|
|
6
|
-
* REST API for customer/order operations requiring auth tokens.
|
|
7
|
-
* PaaS-first approach (not Adobe IMS/SaaS).
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import type {
|
|
11
|
-
Product,
|
|
12
|
-
GetProductsOptions,
|
|
13
|
-
Cart,
|
|
14
|
-
CartLineInput,
|
|
15
|
-
CartLineUpdate,
|
|
16
|
-
Collection,
|
|
17
|
-
CollectionProductsOptions,
|
|
18
|
-
GetCollectionsOptions,
|
|
19
|
-
ProductCategory,
|
|
20
|
-
GetCategoriesOptions,
|
|
21
|
-
Order,
|
|
22
|
-
GetOrdersOptions,
|
|
23
|
-
Customer,
|
|
24
|
-
Address,
|
|
25
|
-
PaymentMethod,
|
|
26
|
-
PaymentMethodsOptions,
|
|
27
|
-
ShippingMethod,
|
|
28
|
-
StockLocation,
|
|
29
|
-
InventoryLevel,
|
|
30
|
-
StockValidation,
|
|
31
|
-
FulfillmentOption,
|
|
32
|
-
StorefrontContext,
|
|
33
|
-
BackendCapabilities,
|
|
34
|
-
} from '@thorprovider/types';
|
|
35
|
-
import type { CommerceProvider, ProviderConfig } from '../../types/provider';
|
|
36
|
-
import { MAGENTO_CAPABILITIES } from '../../types/capabilities.types';
|
|
37
|
-
import { createLogger } from '../../core/logger';
|
|
38
|
-
import { ProviderAPIError } from '../../core/errors';
|
|
39
|
-
import { transformProduct, transformCart, transformCollection, transformOrder, transformCustomer } from './transforms';
|
|
40
|
-
import {
|
|
41
|
-
GET_PRODUCTS,
|
|
42
|
-
GET_PRODUCT_BY_URL_KEY,
|
|
43
|
-
GET_CATEGORIES,
|
|
44
|
-
GET_CATEGORY_BY_URL_KEY,
|
|
45
|
-
GET_CATEGORY_PRODUCTS,
|
|
46
|
-
GET_CART,
|
|
47
|
-
CREATE_EMPTY_CART,
|
|
48
|
-
ADD_TO_CART,
|
|
49
|
-
REMOVE_FROM_CART,
|
|
50
|
-
UPDATE_CART_ITEMS,
|
|
51
|
-
APPLY_COUPON,
|
|
52
|
-
REMOVE_COUPON,
|
|
53
|
-
GET_CUSTOMER,
|
|
54
|
-
GET_CUSTOMER_ORDERS,
|
|
55
|
-
} from './queries';
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Magento provider configuration
|
|
59
|
-
*/
|
|
60
|
-
export interface MagentoProviderConfig extends ProviderConfig {
|
|
61
|
-
/** Magento base URL (e.g., 'https://mymagento.com') */
|
|
62
|
-
baseUrl: string;
|
|
63
|
-
/** Store code for multi-tenant (default: 'default') */
|
|
64
|
-
storeCode?: string;
|
|
65
|
-
/** Customer bearer token (for authenticated operations) */
|
|
66
|
-
customerToken?: string;
|
|
67
|
-
/** Integration access token (for admin-level operations) */
|
|
68
|
-
accessToken?: string;
|
|
69
|
-
/** Default currency code (default: 'USD') */
|
|
70
|
-
currencyCode?: string;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export class MagentoProvider implements CommerceProvider {
|
|
74
|
-
readonly name = 'magento';
|
|
75
|
-
readonly version = '1.0.0';
|
|
76
|
-
readonly capabilities: BackendCapabilities = MAGENTO_CAPABILITIES;
|
|
77
|
-
|
|
78
|
-
private logger: ReturnType<typeof createLogger>;
|
|
79
|
-
private baseUrl: string;
|
|
80
|
-
private storeCode: string;
|
|
81
|
-
private currencyCode: string;
|
|
82
|
-
private graphqlEndpoint: string;
|
|
83
|
-
private customerToken?: string;
|
|
84
|
-
private accessToken?: string;
|
|
85
|
-
|
|
86
|
-
constructor(config: MagentoProviderConfig) {
|
|
87
|
-
this.logger = createLogger('MagentoProvider', config.debug);
|
|
88
|
-
this.baseUrl = config.baseUrl.replace(/\/$/, '');
|
|
89
|
-
this.storeCode = config.storeCode ?? 'default';
|
|
90
|
-
this.currencyCode = config.currencyCode ?? 'USD';
|
|
91
|
-
this.graphqlEndpoint = `${this.baseUrl}/graphql`;
|
|
92
|
-
this.customerToken = config.customerToken;
|
|
93
|
-
this.accessToken = config.accessToken;
|
|
94
|
-
|
|
95
|
-
this.logger.info('Initializing Magento Provider', {
|
|
96
|
-
baseUrl: this.baseUrl,
|
|
97
|
-
storeCode: this.storeCode,
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
private async graphql<T = any>(query: string, variables?: Record<string, any>): Promise<T> {
|
|
102
|
-
const headers: Record<string, string> = {
|
|
103
|
-
'Content-Type': 'application/json',
|
|
104
|
-
'Store': this.storeCode,
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
if (this.customerToken) {
|
|
108
|
-
headers['Authorization'] = `Bearer ${this.customerToken}`;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const response = await fetch(this.graphqlEndpoint, {
|
|
112
|
-
method: 'POST',
|
|
113
|
-
headers,
|
|
114
|
-
body: JSON.stringify({ query, variables }),
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
if (!response.ok) {
|
|
118
|
-
throw new ProviderAPIError(
|
|
119
|
-
`Magento GraphQL error: ${response.status}`,
|
|
120
|
-
'MAGENTO_API_ERROR',
|
|
121
|
-
response.status,
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const json = await response.json() as { data?: T; errors?: Array<{ message: string }> };
|
|
126
|
-
|
|
127
|
-
if (json.errors?.length) {
|
|
128
|
-
const message = json.errors.map((e) => e.message).join('; ');
|
|
129
|
-
throw new ProviderAPIError(`Magento GraphQL error: ${message}`, 'MAGENTO_GRAPHQL_ERROR');
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return json.data as T;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// ============================================
|
|
136
|
-
// Product Operations (GraphQL)
|
|
137
|
-
// ============================================
|
|
138
|
-
|
|
139
|
-
async getProduct(handle: string): Promise<Product | undefined> {
|
|
140
|
-
try {
|
|
141
|
-
const data = await this.graphql<{ products: { items: any[] } }>(GET_PRODUCT_BY_URL_KEY, { urlKey: handle });
|
|
142
|
-
return data.products.items[0] ? transformProduct(data.products.items[0], this.currencyCode) : undefined;
|
|
143
|
-
} catch {
|
|
144
|
-
return undefined;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async getProducts(options?: GetProductsOptions): Promise<Product[]> {
|
|
149
|
-
const variables: Record<string, any> = {
|
|
150
|
-
pageSize: options?.first ?? 20,
|
|
151
|
-
currentPage: 1,
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
if (options?.query) variables.search = options.query;
|
|
155
|
-
if (options?.sortKey) {
|
|
156
|
-
const sortMap: Record<string, Record<string, string>> = {
|
|
157
|
-
'price-asc': { price: 'ASC' },
|
|
158
|
-
'price-desc': { price: 'DESC' },
|
|
159
|
-
'title-asc': { name: 'ASC' },
|
|
160
|
-
'title-desc': { name: 'DESC' },
|
|
161
|
-
'created-desc': { created_at: 'DESC' },
|
|
162
|
-
'relevance': { relevance: 'DESC' },
|
|
163
|
-
};
|
|
164
|
-
variables.sort = sortMap[options.sortKey] ?? { relevance: 'DESC' };
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const data = await this.graphql<{ products: { items: any[] } }>(GET_PRODUCTS, variables);
|
|
168
|
-
return (data.products?.items ?? []).map((p) => transformProduct(p, this.currencyCode));
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
async getProductRecommendations(_productId: string): Promise<Product[]> {
|
|
172
|
-
return [];
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// ============================================
|
|
176
|
-
// Collection/Category Operations (GraphQL)
|
|
177
|
-
// ============================================
|
|
178
|
-
|
|
179
|
-
async getCollection(handle: string): Promise<Collection | undefined> {
|
|
180
|
-
try {
|
|
181
|
-
const data = await this.graphql<{ categories: { items: any[] } }>(GET_CATEGORY_BY_URL_KEY, { urlKey: handle });
|
|
182
|
-
return data.categories.items[0] ? transformCollection(data.categories.items[0]) : undefined;
|
|
183
|
-
} catch {
|
|
184
|
-
return undefined;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
async getCollections(options?: GetCollectionsOptions): Promise<Collection[]> {
|
|
189
|
-
const data = await this.graphql<{ categories: { items: any[] } }>(GET_CATEGORIES, {
|
|
190
|
-
pageSize: options?.limit ?? 50,
|
|
191
|
-
});
|
|
192
|
-
return (data.categories?.items ?? []).map(transformCollection);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
async getCollectionProducts(handle: string, options?: CollectionProductsOptions): Promise<Product[]> {
|
|
196
|
-
const data = await this.graphql<{ products: { items: any[] } }>(GET_CATEGORY_PRODUCTS, {
|
|
197
|
-
urlKey: handle,
|
|
198
|
-
pageSize: options?.first ?? 20,
|
|
199
|
-
});
|
|
200
|
-
return (data.products?.items ?? []).map((p) => transformProduct(p, this.currencyCode));
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
async getCategory(handle: string): Promise<ProductCategory | undefined> {
|
|
204
|
-
return this.getCollection(handle) as any;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
async getCategories(options?: GetCategoriesOptions): Promise<ProductCategory[]> {
|
|
208
|
-
return this.getCollections(options) as any;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
async getCategoryProducts(handle: string, options?: CollectionProductsOptions): Promise<Product[]> {
|
|
212
|
-
return this.getCollectionProducts(handle, options);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// ============================================
|
|
216
|
-
// Cart Operations (GraphQL)
|
|
217
|
-
// ============================================
|
|
218
|
-
|
|
219
|
-
async createCart(): Promise<Cart> {
|
|
220
|
-
const data = await this.graphql<{ createEmptyCart: string }>(CREATE_EMPTY_CART);
|
|
221
|
-
const cartId = data.createEmptyCart;
|
|
222
|
-
const cartData = await this.graphql<{ cart: any }>(GET_CART, { cartId });
|
|
223
|
-
return transformCart(cartData.cart ?? { id: cartId, items: [], prices: {} }, this.currencyCode);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
async getCart(cartId: string): Promise<Cart | undefined> {
|
|
227
|
-
try {
|
|
228
|
-
const data = await this.graphql<{ cart: any }>(GET_CART, { cartId });
|
|
229
|
-
return data.cart ? transformCart(data.cart, this.currencyCode) : undefined;
|
|
230
|
-
} catch {
|
|
231
|
-
return undefined;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
async addToCart(cartId: string, lines: CartLineInput[]): Promise<Cart> {
|
|
236
|
-
const cartItems = lines.map((l) => ({
|
|
237
|
-
sku: l.merchandiseId,
|
|
238
|
-
quantity: l.quantity,
|
|
239
|
-
}));
|
|
240
|
-
|
|
241
|
-
const data = await this.graphql<{ addProductsToCart: { cart: any; user_errors: any[] } }>(ADD_TO_CART, {
|
|
242
|
-
cartId,
|
|
243
|
-
cartItems,
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
if (data.addProductsToCart.user_errors?.length) {
|
|
247
|
-
throw new ProviderAPIError(
|
|
248
|
-
`Add to cart failed: ${data.addProductsToCart.user_errors.map((e: any) => e.message).join('; ')}`,
|
|
249
|
-
'MAGENTO_CART_ERROR',
|
|
250
|
-
);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
return transformCart(data.addProductsToCart.cart, this.currencyCode);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
async removeFromCart(cartId: string, lineIds: string[]): Promise<Cart> {
|
|
257
|
-
let cart: any;
|
|
258
|
-
for (const itemUid of lineIds) {
|
|
259
|
-
const data = await this.graphql<{ removeItemFromCart: { cart: any } }>(REMOVE_FROM_CART, {
|
|
260
|
-
cartId,
|
|
261
|
-
cartItemUid: itemUid,
|
|
262
|
-
});
|
|
263
|
-
cart = data.removeItemFromCart.cart;
|
|
264
|
-
}
|
|
265
|
-
return transformCart(cart ?? { id: cartId, items: [], prices: {} }, this.currencyCode);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
async updateCart(cartId: string, lines: CartLineUpdate[]): Promise<Cart> {
|
|
269
|
-
const cartItems = lines.map((l) => ({
|
|
270
|
-
cart_item_uid: l.id,
|
|
271
|
-
quantity: l.quantity,
|
|
272
|
-
}));
|
|
273
|
-
|
|
274
|
-
const data = await this.graphql<{ updateCartItems: { cart: any } }>(UPDATE_CART_ITEMS, {
|
|
275
|
-
cartId,
|
|
276
|
-
cartItems,
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
return transformCart(data.updateCartItems.cart, this.currencyCode);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// ============================================
|
|
283
|
-
// Checkout Operations
|
|
284
|
-
// ============================================
|
|
285
|
-
|
|
286
|
-
async completeCheckout(_cartId: string): Promise<Order> {
|
|
287
|
-
throw new ProviderAPIError(
|
|
288
|
-
'Magento checkout requires multi-step process (set addresses, shipping, payment, place order). Use the Magento checkout flow.',
|
|
289
|
-
'MAGENTO_CHECKOUT_MULTI_STEP',
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
async getShippingMethods(_cartId: string): Promise<ShippingMethod[]> {
|
|
294
|
-
return [];
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
async getRegionCountries(): Promise<import('@thorprovider/types').Country[]> {
|
|
298
|
-
return [];
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
async addShippingMethod(_cartId: string, _methodId: string): Promise<Cart> {
|
|
302
|
-
throw new ProviderAPIError(
|
|
303
|
-
'Magento shipping selection requires set-shipping-methods mutation.',
|
|
304
|
-
'MAGENTO_CHECKOUT_MULTI_STEP',
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
async initiatePaymentSession(_cartId: string, _providerId: string): Promise<void> {
|
|
309
|
-
// Magento handles payment via set-payment-method mutation
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
async applyDiscountCode(cartId: string, code: string): Promise<Cart> {
|
|
313
|
-
const data = await this.graphql<{ applyCouponToCart: { cart: any } }>(APPLY_COUPON, {
|
|
314
|
-
cartId,
|
|
315
|
-
couponCode: code,
|
|
316
|
-
});
|
|
317
|
-
return transformCart(data.applyCouponToCart.cart, this.currencyCode);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
async removeDiscountCode(cartId: string, _code: string): Promise<Cart> {
|
|
321
|
-
const data = await this.graphql<{ removeCouponFromCart: { cart: any } }>(REMOVE_COUPON, {
|
|
322
|
-
cartId,
|
|
323
|
-
});
|
|
324
|
-
return transformCart(data.removeCouponFromCart.cart, this.currencyCode);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// ============================================
|
|
328
|
-
// Customer & Orders (GraphQL — requires customerToken)
|
|
329
|
-
// ============================================
|
|
330
|
-
|
|
331
|
-
async getOrders(_options: GetOrdersOptions): Promise<Order[]> {
|
|
332
|
-
if (!this.customerToken) return [];
|
|
333
|
-
try {
|
|
334
|
-
const data = await this.graphql<{ customer: { orders: { items: any[] } } }>(GET_CUSTOMER_ORDERS, {
|
|
335
|
-
pageSize: 20,
|
|
336
|
-
currentPage: 1,
|
|
337
|
-
});
|
|
338
|
-
return (data.customer?.orders?.items ?? []).map((o) => transformOrder(o, this.currencyCode));
|
|
339
|
-
} catch {
|
|
340
|
-
return [];
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
async getPaymentMethods(_options: PaymentMethodsOptions): Promise<PaymentMethod[]> {
|
|
345
|
-
return [];
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// ============================================
|
|
349
|
-
// Inventory
|
|
350
|
-
// ============================================
|
|
351
|
-
|
|
352
|
-
async getStockLocations(): Promise<StockLocation[]> { return []; }
|
|
353
|
-
async checkInventoryLevels(): Promise<InventoryLevel[]> { return []; }
|
|
354
|
-
async validateCartStock(): Promise<StockValidation> { return { allAvailable: true, unavailableItems: [] }; }
|
|
355
|
-
async getFulfillmentOptions(): Promise<FulfillmentOption[]> { return []; }
|
|
356
|
-
|
|
357
|
-
// ============================================
|
|
358
|
-
// Admin Operations
|
|
359
|
-
// ============================================
|
|
360
|
-
|
|
361
|
-
async cancelOrder(_orderId: string): Promise<Order> {
|
|
362
|
-
throw new ProviderAPIError('Order cancellation requires Magento Admin API.', 'MAGENTO_ADMIN_REQUIRED');
|
|
363
|
-
}
|
|
364
|
-
async createFulfillment(_orderId: string): Promise<void> {
|
|
365
|
-
throw new ProviderAPIError('Fulfillment requires Magento Admin API.', 'MAGENTO_ADMIN_REQUIRED');
|
|
366
|
-
}
|
|
367
|
-
async createRefund(_orderId: string): Promise<void> {
|
|
368
|
-
throw new ProviderAPIError('Refunds require Magento Admin API.', 'MAGENTO_ADMIN_REQUIRED');
|
|
369
|
-
}
|
|
370
|
-
async getAdminOrders(): Promise<{ orders: Order[]; count: number }> { return { orders: [], count: 0 }; }
|
|
371
|
-
|
|
372
|
-
// ============================================
|
|
373
|
-
// Storefront / Multi-Tenant
|
|
374
|
-
// ============================================
|
|
375
|
-
|
|
376
|
-
async getStorefrontContext(): Promise<StorefrontContext> {
|
|
377
|
-
return {
|
|
378
|
-
id: this.storeCode,
|
|
379
|
-
name: `Magento ${this.storeCode}`,
|
|
380
|
-
platformType: 'magento',
|
|
381
|
-
requiresProductScoping: true,
|
|
382
|
-
currencyCode: this.currencyCode,
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
isStorefrontRequired(): boolean { return true; }
|
|
387
|
-
}
|