@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
package/dist/index.js
CHANGED
|
@@ -32,8 +32,6 @@ var src_exports = {};
|
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
AdminAuthService: () => AdminAuthService,
|
|
34
34
|
AdminSessionService: () => AdminSessionService,
|
|
35
|
-
BIGCOMMERCE_CAPABILITIES: () => BIGCOMMERCE_CAPABILITIES,
|
|
36
|
-
BigCommerceProvider: () => BigCommerceProvider,
|
|
37
35
|
CHANNEL_ASSIGNED_AT_KEY: () => CHANNEL_ASSIGNED_AT_KEY,
|
|
38
36
|
CHANNEL_ASSIGNED_BY_KEY: () => CHANNEL_ASSIGNED_BY_KEY,
|
|
39
37
|
CHANNEL_SCOPE_KEY: () => CHANNEL_SCOPE_KEY,
|
|
@@ -42,10 +40,8 @@ __export(src_exports, {
|
|
|
42
40
|
CircuitBreakerOpenError: () => CircuitBreakerOpenError,
|
|
43
41
|
CommerceError: () => CommerceError,
|
|
44
42
|
ConfigurationError: () => ConfigurationError,
|
|
45
|
-
MAGENTO_CAPABILITIES: () => MAGENTO_CAPABILITIES,
|
|
46
43
|
MEDUSA_CAPABILITIES: () => MEDUSA_CAPABILITIES,
|
|
47
44
|
MOCK_CAPABILITIES: () => MOCK_CAPABILITIES,
|
|
48
|
-
MagentoProvider: () => MagentoProvider,
|
|
49
45
|
MedusaMetadataAdapter: () => MedusaMetadataAdapter,
|
|
50
46
|
MedusaProvider: () => MedusaProvider,
|
|
51
47
|
MetricsService: () => MetricsService,
|
|
@@ -53,15 +49,9 @@ __export(src_exports, {
|
|
|
53
49
|
ProviderAPIError: () => ProviderAPIError,
|
|
54
50
|
ProviderNotFoundError: () => ProviderNotFoundError,
|
|
55
51
|
ResourceNotFoundError: () => ResourceNotFoundError,
|
|
56
|
-
SHOPIFY_CAPABILITIES: () => SHOPIFY_CAPABILITIES,
|
|
57
|
-
SPREE_CAPABILITIES: () => SPREE_CAPABILITIES,
|
|
58
52
|
STELORDER_CAPABILITIES: () => STELORDER_CAPABILITIES,
|
|
59
|
-
ShopifyProvider: () => ShopifyProvider,
|
|
60
|
-
SpreeProvider: () => SpreeProvider,
|
|
61
53
|
StelOrderProvider: () => StelOrderProvider,
|
|
62
54
|
StorefrontConfigError: () => import_types2.StorefrontConfigError,
|
|
63
|
-
WOOCOMMERCE_CAPABILITIES: () => WOOCOMMERCE_CAPABILITIES,
|
|
64
|
-
WooCommerceProvider: () => WooCommerceProvider,
|
|
65
55
|
assertEntityBelongsToChannel: () => assertEntityBelongsToChannel,
|
|
66
56
|
backendCircuitBreaker: () => backendCircuitBreaker,
|
|
67
57
|
buildChannelScopeMetadata: () => buildChannelScopeMetadata,
|
|
@@ -69,12 +59,15 @@ __export(src_exports, {
|
|
|
69
59
|
createBackendMetadataAdapter: () => createBackendMetadataAdapter,
|
|
70
60
|
createLogger: () => createLogger,
|
|
71
61
|
createProvider: () => createProvider,
|
|
62
|
+
extractErrorMessage: () => extractErrorMessage,
|
|
63
|
+
fetchWithRetry: () => fetchWithRetry,
|
|
72
64
|
filterByChannel: () => filterByChannel,
|
|
73
65
|
filterGlobalOnly: () => filterGlobalOnly,
|
|
74
66
|
geocodeAddress: () => geocodeAddress,
|
|
75
67
|
getBackendProviderId: () => getBackendProviderId,
|
|
76
68
|
getCapabilityDescription: () => getCapabilityDescription,
|
|
77
69
|
getChannelScope: () => getChannelScope,
|
|
70
|
+
getRegisteredProviders: () => getRegisteredProviders,
|
|
78
71
|
isBackendCapabilities: () => isBackendCapabilities,
|
|
79
72
|
isGlobalEntity: () => isGlobalEntity,
|
|
80
73
|
isProviderType: () => isProviderType,
|
|
@@ -84,6 +77,7 @@ __export(src_exports, {
|
|
|
84
77
|
numberToMoney: () => numberToMoney,
|
|
85
78
|
partitionByChannel: () => partitionByChannel,
|
|
86
79
|
planFulfillment: () => planFulfillment,
|
|
80
|
+
registerProvider: () => registerProvider,
|
|
87
81
|
transformFulfillmentOption: () => transformFulfillmentOption,
|
|
88
82
|
transformInventoryLevel: () => transformInventoryLevel,
|
|
89
83
|
transformStockLocation: () => transformStockLocation,
|
|
@@ -200,209 +194,6 @@ var STELORDER_CAPABILITIES = {
|
|
|
200
194
|
// Loyalty
|
|
201
195
|
loyaltyProgram: false
|
|
202
196
|
};
|
|
203
|
-
var SHOPIFY_CAPABILITIES = {
|
|
204
|
-
// Profile Management
|
|
205
|
-
updateProfile: true,
|
|
206
|
-
updateEmail: true,
|
|
207
|
-
changePasswordDirect: true,
|
|
208
|
-
changePasswordViaEmail: true,
|
|
209
|
-
deleteAccount: false,
|
|
210
|
-
// Requires admin API
|
|
211
|
-
// Avatar
|
|
212
|
-
uploadAvatar: false,
|
|
213
|
-
avatarViaMetadata: true,
|
|
214
|
-
// Custom Attributes
|
|
215
|
-
customAttributes: true,
|
|
216
|
-
// Orders
|
|
217
|
-
fetchOrders: true,
|
|
218
|
-
cancelOrder: true,
|
|
219
|
-
// Addresses
|
|
220
|
-
manageAddresses: true,
|
|
221
|
-
defaultAddress: true,
|
|
222
|
-
// Cart/Checkout
|
|
223
|
-
applyDiscounts: true,
|
|
224
|
-
shippingCalculation: true,
|
|
225
|
-
guestCheckout: true,
|
|
226
|
-
savedPaymentMethods: true,
|
|
227
|
-
listPaymentProviders: true,
|
|
228
|
-
regionSpecificPayments: false,
|
|
229
|
-
// Global payment methods
|
|
230
|
-
// Wishlist
|
|
231
|
-
wishlist: false,
|
|
232
|
-
// Requires app
|
|
233
|
-
// Search
|
|
234
|
-
advancedSearch: true,
|
|
235
|
-
facetedFilters: true,
|
|
236
|
-
// Reviews
|
|
237
|
-
productReviews: false,
|
|
238
|
-
// Requires app
|
|
239
|
-
// Loyalty
|
|
240
|
-
loyaltyProgram: false
|
|
241
|
-
// Requires app
|
|
242
|
-
};
|
|
243
|
-
var BIGCOMMERCE_CAPABILITIES = {
|
|
244
|
-
// Profile Management
|
|
245
|
-
updateProfile: true,
|
|
246
|
-
updateEmail: true,
|
|
247
|
-
changePasswordDirect: true,
|
|
248
|
-
changePasswordViaEmail: true,
|
|
249
|
-
deleteAccount: false,
|
|
250
|
-
// Avatar
|
|
251
|
-
uploadAvatar: false,
|
|
252
|
-
avatarViaMetadata: false,
|
|
253
|
-
// Custom Attributes
|
|
254
|
-
customAttributes: true,
|
|
255
|
-
// Orders
|
|
256
|
-
fetchOrders: true,
|
|
257
|
-
cancelOrder: false,
|
|
258
|
-
// Admin API only
|
|
259
|
-
// Addresses
|
|
260
|
-
manageAddresses: true,
|
|
261
|
-
defaultAddress: false,
|
|
262
|
-
// Cart/Checkout
|
|
263
|
-
applyDiscounts: true,
|
|
264
|
-
shippingCalculation: true,
|
|
265
|
-
guestCheckout: true,
|
|
266
|
-
savedPaymentMethods: true,
|
|
267
|
-
listPaymentProviders: true,
|
|
268
|
-
regionSpecificPayments: false,
|
|
269
|
-
// Wishlist
|
|
270
|
-
wishlist: true,
|
|
271
|
-
// Native support
|
|
272
|
-
// Search
|
|
273
|
-
advancedSearch: true,
|
|
274
|
-
facetedFilters: true,
|
|
275
|
-
// Reviews
|
|
276
|
-
productReviews: true,
|
|
277
|
-
// Native support
|
|
278
|
-
// Loyalty
|
|
279
|
-
loyaltyProgram: false
|
|
280
|
-
};
|
|
281
|
-
var WOOCOMMERCE_CAPABILITIES = {
|
|
282
|
-
// Profile Management
|
|
283
|
-
updateProfile: true,
|
|
284
|
-
updateEmail: true,
|
|
285
|
-
changePasswordDirect: false,
|
|
286
|
-
// WordPress handles auth
|
|
287
|
-
changePasswordViaEmail: true,
|
|
288
|
-
deleteAccount: false,
|
|
289
|
-
// Avatar
|
|
290
|
-
uploadAvatar: false,
|
|
291
|
-
avatarViaMetadata: false,
|
|
292
|
-
// Gravatar-based
|
|
293
|
-
// Custom Attributes
|
|
294
|
-
customAttributes: true,
|
|
295
|
-
// Via meta_data
|
|
296
|
-
// Orders
|
|
297
|
-
fetchOrders: true,
|
|
298
|
-
cancelOrder: true,
|
|
299
|
-
// Addresses
|
|
300
|
-
manageAddresses: true,
|
|
301
|
-
defaultAddress: false,
|
|
302
|
-
// Billing/shipping only
|
|
303
|
-
// Cart/Checkout
|
|
304
|
-
applyDiscounts: true,
|
|
305
|
-
// Coupon codes
|
|
306
|
-
shippingCalculation: true,
|
|
307
|
-
guestCheckout: true,
|
|
308
|
-
savedPaymentMethods: false,
|
|
309
|
-
listPaymentProviders: true,
|
|
310
|
-
regionSpecificPayments: false,
|
|
311
|
-
// Wishlist
|
|
312
|
-
wishlist: false,
|
|
313
|
-
// Requires plugin
|
|
314
|
-
// Search
|
|
315
|
-
advancedSearch: false,
|
|
316
|
-
// Basic WP search
|
|
317
|
-
facetedFilters: false,
|
|
318
|
-
// Requires plugin
|
|
319
|
-
// Reviews
|
|
320
|
-
productReviews: true,
|
|
321
|
-
// Native WP reviews
|
|
322
|
-
// Loyalty
|
|
323
|
-
loyaltyProgram: false
|
|
324
|
-
};
|
|
325
|
-
var SPREE_CAPABILITIES = {
|
|
326
|
-
// Profile Management
|
|
327
|
-
updateProfile: true,
|
|
328
|
-
updateEmail: true,
|
|
329
|
-
changePasswordDirect: true,
|
|
330
|
-
changePasswordViaEmail: true,
|
|
331
|
-
deleteAccount: false,
|
|
332
|
-
// Avatar
|
|
333
|
-
uploadAvatar: false,
|
|
334
|
-
avatarViaMetadata: false,
|
|
335
|
-
// Custom Attributes
|
|
336
|
-
customAttributes: false,
|
|
337
|
-
// Orders
|
|
338
|
-
fetchOrders: true,
|
|
339
|
-
cancelOrder: true,
|
|
340
|
-
// Addresses
|
|
341
|
-
manageAddresses: true,
|
|
342
|
-
defaultAddress: true,
|
|
343
|
-
// Cart/Checkout
|
|
344
|
-
applyDiscounts: true,
|
|
345
|
-
shippingCalculation: true,
|
|
346
|
-
guestCheckout: true,
|
|
347
|
-
savedPaymentMethods: false,
|
|
348
|
-
listPaymentProviders: true,
|
|
349
|
-
regionSpecificPayments: false,
|
|
350
|
-
// Wishlist
|
|
351
|
-
wishlist: true,
|
|
352
|
-
// Native support
|
|
353
|
-
// Search
|
|
354
|
-
advancedSearch: false,
|
|
355
|
-
facetedFilters: true,
|
|
356
|
-
// Taxon-based filtering
|
|
357
|
-
// Reviews
|
|
358
|
-
productReviews: false,
|
|
359
|
-
// Requires extension
|
|
360
|
-
// Loyalty
|
|
361
|
-
loyaltyProgram: false
|
|
362
|
-
};
|
|
363
|
-
var MAGENTO_CAPABILITIES = {
|
|
364
|
-
// Profile Management
|
|
365
|
-
updateProfile: true,
|
|
366
|
-
updateEmail: true,
|
|
367
|
-
changePasswordDirect: true,
|
|
368
|
-
changePasswordViaEmail: true,
|
|
369
|
-
deleteAccount: false,
|
|
370
|
-
// Admin only
|
|
371
|
-
// Avatar
|
|
372
|
-
uploadAvatar: false,
|
|
373
|
-
avatarViaMetadata: false,
|
|
374
|
-
// Custom Attributes
|
|
375
|
-
customAttributes: true,
|
|
376
|
-
// EAV model
|
|
377
|
-
// Orders
|
|
378
|
-
fetchOrders: true,
|
|
379
|
-
cancelOrder: true,
|
|
380
|
-
// Addresses
|
|
381
|
-
manageAddresses: true,
|
|
382
|
-
defaultAddress: true,
|
|
383
|
-
// Cart/Checkout
|
|
384
|
-
applyDiscounts: true,
|
|
385
|
-
shippingCalculation: true,
|
|
386
|
-
guestCheckout: true,
|
|
387
|
-
savedPaymentMethods: true,
|
|
388
|
-
listPaymentProviders: true,
|
|
389
|
-
regionSpecificPayments: true,
|
|
390
|
-
// Store-view scoped
|
|
391
|
-
// Wishlist
|
|
392
|
-
wishlist: true,
|
|
393
|
-
// Native support
|
|
394
|
-
// Search
|
|
395
|
-
advancedSearch: true,
|
|
396
|
-
// Elasticsearch/OpenSearch
|
|
397
|
-
facetedFilters: true,
|
|
398
|
-
// Layered navigation
|
|
399
|
-
// Reviews
|
|
400
|
-
productReviews: true,
|
|
401
|
-
// Native support
|
|
402
|
-
// Loyalty
|
|
403
|
-
loyaltyProgram: false
|
|
404
|
-
// Requires extension
|
|
405
|
-
};
|
|
406
197
|
function isBackendCapabilities(obj) {
|
|
407
198
|
if (typeof obj !== "object" || obj === null) return false;
|
|
408
199
|
const capabilities = obj;
|
|
@@ -1622,13 +1413,7 @@ function createBackendMetadataAdapter(config) {
|
|
|
1622
1413
|
});
|
|
1623
1414
|
return new MedusaMetadataAdapter(medusa);
|
|
1624
1415
|
}
|
|
1625
|
-
|
|
1626
|
-
throw new Error("Shopify metadata adapter not yet implemented");
|
|
1627
|
-
}
|
|
1628
|
-
if (provider === "woocommerce") {
|
|
1629
|
-
throw new Error("WooCommerce metadata adapter not yet implemented");
|
|
1630
|
-
}
|
|
1631
|
-
throw new Error(`Unsupported provider: ${provider}`);
|
|
1416
|
+
throw new Error(`Unsupported provider for metadata adapter: ${provider}. Only 'medusa' is currently supported.`);
|
|
1632
1417
|
}
|
|
1633
1418
|
|
|
1634
1419
|
// src/metadata/channel-scope.ts
|
|
@@ -1823,11 +1608,17 @@ var MedusaAuthProvider = class {
|
|
|
1823
1608
|
logger;
|
|
1824
1609
|
authConfig;
|
|
1825
1610
|
storageManager;
|
|
1826
|
-
|
|
1611
|
+
/** Medusa backend base URL — needed for raw fetch calls */
|
|
1612
|
+
baseUrl;
|
|
1613
|
+
/** Publishable API key — sent as x-publishable-api-key on store calls */
|
|
1614
|
+
publishableKey;
|
|
1615
|
+
constructor(sdk, authConfig, debug = false, baseUrl = "", publishableKey = "") {
|
|
1827
1616
|
this.sdk = sdk;
|
|
1828
1617
|
this.authConfig = authConfig;
|
|
1829
1618
|
this.logger = createLogger("MedusaAuthProvider", debug);
|
|
1830
1619
|
this.storageManager = new AuthStorageManager(authConfig);
|
|
1620
|
+
this.baseUrl = baseUrl;
|
|
1621
|
+
this.publishableKey = publishableKey;
|
|
1831
1622
|
this.logger.info("Medusa Auth Provider initialized", {
|
|
1832
1623
|
method: authConfig.method,
|
|
1833
1624
|
storage: authConfig.storage
|
|
@@ -2103,6 +1894,81 @@ var MedusaAuthProvider = class {
|
|
|
2103
1894
|
void 0
|
|
2104
1895
|
);
|
|
2105
1896
|
}
|
|
1897
|
+
// ========================================
|
|
1898
|
+
// Multi-Tenant — Channel Link
|
|
1899
|
+
// ========================================
|
|
1900
|
+
/**
|
|
1901
|
+
* Link the currently authenticated customer to the storefront's sales channel.
|
|
1902
|
+
*
|
|
1903
|
+
* Must be called immediately after a successful `login()` so the customer
|
|
1904
|
+
* appears in the admin panel of the correct storefront channel.
|
|
1905
|
+
*
|
|
1906
|
+
* Implements `POST /store/thor/customers/me/channels`.
|
|
1907
|
+
* Idempotent — returns `{ linked: false, reason: 'already_linked' }` when
|
|
1908
|
+
* the link already exists.
|
|
1909
|
+
*
|
|
1910
|
+
* @throws {ProviderAPIError} When not authenticated (401) or channel cannot
|
|
1911
|
+
* be resolved from the publishable key (400).
|
|
1912
|
+
*/
|
|
1913
|
+
async linkCustomerToChannel() {
|
|
1914
|
+
this.logger.debug("linkCustomerToChannel");
|
|
1915
|
+
if (!this.baseUrl || !this.publishableKey) {
|
|
1916
|
+
throw new ProviderAPIError(
|
|
1917
|
+
"linkCustomerToChannel requires baseUrl and publishableKey to be set on the auth provider.",
|
|
1918
|
+
"CONFIGURATION_ERROR",
|
|
1919
|
+
void 0
|
|
1920
|
+
);
|
|
1921
|
+
}
|
|
1922
|
+
const token = await this.storageManager.getToken();
|
|
1923
|
+
const headers = {
|
|
1924
|
+
"x-publishable-api-key": this.publishableKey,
|
|
1925
|
+
"Content-Type": "application/json"
|
|
1926
|
+
};
|
|
1927
|
+
if (token) {
|
|
1928
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
1929
|
+
}
|
|
1930
|
+
try {
|
|
1931
|
+
const url = `${this.baseUrl}/store/thor/customers/me/channels`;
|
|
1932
|
+
const response = await fetch(url, {
|
|
1933
|
+
method: "POST",
|
|
1934
|
+
headers
|
|
1935
|
+
});
|
|
1936
|
+
if (response.status === 401) {
|
|
1937
|
+
throw new ProviderAPIError(
|
|
1938
|
+
"linkCustomerToChannel: not authenticated. Call login() first.",
|
|
1939
|
+
"NOT_AUTHENTICATED",
|
|
1940
|
+
401
|
|
1941
|
+
);
|
|
1942
|
+
}
|
|
1943
|
+
if (response.status === 400) {
|
|
1944
|
+
const body = await response.json().catch(() => ({}));
|
|
1945
|
+
throw new ProviderAPIError(
|
|
1946
|
+
`linkCustomerToChannel: could not resolve sales channel from publishable key. ${body?.message ?? ""}`.trim(),
|
|
1947
|
+
"CHANNEL_RESOLUTION_FAILED",
|
|
1948
|
+
400
|
|
1949
|
+
);
|
|
1950
|
+
}
|
|
1951
|
+
if (!response.ok) {
|
|
1952
|
+
const body = await response.json().catch(() => ({}));
|
|
1953
|
+
throw new ProviderAPIError(
|
|
1954
|
+
`linkCustomerToChannel failed with status ${response.status}: ${body?.message ?? "Unknown error"}`,
|
|
1955
|
+
"LINK_CHANNEL_FAILED",
|
|
1956
|
+
response.status
|
|
1957
|
+
);
|
|
1958
|
+
}
|
|
1959
|
+
const data = await response.json();
|
|
1960
|
+
this.logger.info("Customer linked to channel", { linked: data.linked, reason: data.reason });
|
|
1961
|
+
return data;
|
|
1962
|
+
} catch (error) {
|
|
1963
|
+
if (error instanceof ProviderAPIError) throw error;
|
|
1964
|
+
this.logger.error("linkCustomerToChannel failed", error);
|
|
1965
|
+
throw new ProviderAPIError(
|
|
1966
|
+
`linkCustomerToChannel failed: ${error.message}`,
|
|
1967
|
+
"LINK_CHANNEL_FAILED",
|
|
1968
|
+
error
|
|
1969
|
+
);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
2106
1972
|
};
|
|
2107
1973
|
|
|
2108
1974
|
// src/providers/medusa/payment.ts
|
|
@@ -2245,48 +2111,344 @@ async function getPaymentMethods(sdk, options) {
|
|
|
2245
2111
|
}
|
|
2246
2112
|
}
|
|
2247
2113
|
|
|
2248
|
-
// src/providers/medusa/
|
|
2249
|
-
var
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2114
|
+
// src/providers/medusa/admin.ts
|
|
2115
|
+
var MedusaAdminClient = class {
|
|
2116
|
+
baseUrl;
|
|
2117
|
+
apiKey;
|
|
2118
|
+
logger;
|
|
2119
|
+
constructor(config) {
|
|
2120
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
2121
|
+
this.apiKey = config.apiKey;
|
|
2122
|
+
this.logger = createLogger("MedusaAdminClient", config.debug);
|
|
2123
|
+
}
|
|
2124
|
+
// -----------------------------------------------------------------------
|
|
2125
|
+
// Private — HTTP helper
|
|
2126
|
+
// -----------------------------------------------------------------------
|
|
2127
|
+
async request(method, path, body) {
|
|
2128
|
+
const url = `${this.baseUrl}${path}`;
|
|
2129
|
+
this.logger.debug(`[admin] ${method} ${path}`);
|
|
2130
|
+
const headers = {
|
|
2131
|
+
"x-medusa-access-token": this.apiKey,
|
|
2132
|
+
"Content-Type": "application/json"
|
|
2133
|
+
};
|
|
2134
|
+
const init = {
|
|
2135
|
+
method,
|
|
2136
|
+
headers,
|
|
2137
|
+
...body !== void 0 ? { body: JSON.stringify(body) } : {}
|
|
2138
|
+
};
|
|
2139
|
+
let response;
|
|
2140
|
+
try {
|
|
2141
|
+
response = await fetch(url, init);
|
|
2142
|
+
} catch (networkError) {
|
|
2143
|
+
throw new ProviderAPIError(
|
|
2144
|
+
`[admin] Network error on ${method} ${path}: ${networkError.message}`,
|
|
2145
|
+
"NETWORK_ERROR",
|
|
2146
|
+
networkError
|
|
2147
|
+
);
|
|
2148
|
+
}
|
|
2149
|
+
if (!response.ok) {
|
|
2150
|
+
let message = `HTTP ${response.status}`;
|
|
2151
|
+
try {
|
|
2152
|
+
const json = await response.json();
|
|
2153
|
+
message = json?.message ?? message;
|
|
2154
|
+
} catch {
|
|
2155
|
+
}
|
|
2156
|
+
throw new ProviderAPIError(
|
|
2157
|
+
`[admin] ${method} ${path} failed: ${message}`,
|
|
2158
|
+
`ADMIN_API_${response.status}`,
|
|
2159
|
+
response.status
|
|
2160
|
+
);
|
|
2161
|
+
}
|
|
2162
|
+
return response.json();
|
|
2163
|
+
}
|
|
2164
|
+
// -----------------------------------------------------------------------
|
|
2165
|
+
// 3.1 — Sales Channel: Customers
|
|
2166
|
+
// -----------------------------------------------------------------------
|
|
2253
2167
|
/**
|
|
2254
|
-
*
|
|
2255
|
-
*
|
|
2256
|
-
* `GET /
|
|
2168
|
+
* Paginated list of customers linked to a sales channel.
|
|
2169
|
+
*
|
|
2170
|
+
* `GET /admin/thor/sales-channels/:id/customers`
|
|
2257
2171
|
*/
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2172
|
+
async getChannelCustomers(salesChannelId, options = {}) {
|
|
2173
|
+
const { limit = 20, offset = 0 } = options;
|
|
2174
|
+
return this.request(
|
|
2175
|
+
"GET",
|
|
2176
|
+
`/admin/thor/sales-channels/${salesChannelId}/customers?limit=${limit}&offset=${offset}`
|
|
2177
|
+
);
|
|
2178
|
+
}
|
|
2179
|
+
// -----------------------------------------------------------------------
|
|
2180
|
+
// 3.2 — Sales Channel: API Keys
|
|
2181
|
+
// -----------------------------------------------------------------------
|
|
2261
2182
|
/**
|
|
2262
|
-
*
|
|
2183
|
+
* Publishable API keys associated with a sales channel.
|
|
2184
|
+
*
|
|
2185
|
+
* `GET /admin/thor/sales-channels/:id/api-keys`
|
|
2263
2186
|
*/
|
|
2264
|
-
|
|
2265
|
-
return this.
|
|
2187
|
+
async getChannelApiKeys(salesChannelId) {
|
|
2188
|
+
return this.request(
|
|
2189
|
+
"GET",
|
|
2190
|
+
`/admin/thor/sales-channels/${salesChannelId}/api-keys`
|
|
2191
|
+
);
|
|
2266
2192
|
}
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
retryConfig;
|
|
2271
|
-
baseUrl;
|
|
2272
|
-
publishableKey;
|
|
2273
|
-
/** Secret API key for admin operations (if configured). */
|
|
2274
|
-
adminApiKey;
|
|
2193
|
+
// -----------------------------------------------------------------------
|
|
2194
|
+
// 3.3 — Sales Channel: Categories
|
|
2195
|
+
// -----------------------------------------------------------------------
|
|
2275
2196
|
/**
|
|
2276
|
-
*
|
|
2277
|
-
*
|
|
2278
|
-
*
|
|
2279
|
-
* Only created when adminConfig.apiKey is provided.
|
|
2197
|
+
* Product categories assigned to a sales channel.
|
|
2198
|
+
*
|
|
2199
|
+
* `GET /admin/thor/sales-channels/:id/categories`
|
|
2280
2200
|
*/
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2201
|
+
async getChannelCategories(salesChannelId, options = {}) {
|
|
2202
|
+
const { limit = 20, offset = 0, include_descendants = false } = options;
|
|
2203
|
+
const qs = new URLSearchParams({
|
|
2204
|
+
limit: String(limit),
|
|
2205
|
+
offset: String(offset),
|
|
2206
|
+
include_descendants: String(include_descendants)
|
|
2207
|
+
});
|
|
2208
|
+
return this.request(
|
|
2209
|
+
"GET",
|
|
2210
|
+
`/admin/thor/sales-channels/${salesChannelId}/categories?${qs}`
|
|
2211
|
+
);
|
|
2212
|
+
}
|
|
2213
|
+
// -----------------------------------------------------------------------
|
|
2214
|
+
// 3.4 — Category: Sales Channels
|
|
2215
|
+
// -----------------------------------------------------------------------
|
|
2284
2216
|
/**
|
|
2285
|
-
*
|
|
2286
|
-
*
|
|
2217
|
+
* Sales channels to which a category is assigned.
|
|
2218
|
+
*
|
|
2219
|
+
* `GET /admin/thor/categories/:id/sales-channels`
|
|
2287
2220
|
*/
|
|
2288
|
-
|
|
2289
|
-
|
|
2221
|
+
async getCategorySalesChannels(categoryId) {
|
|
2222
|
+
return this.request(
|
|
2223
|
+
"GET",
|
|
2224
|
+
`/admin/thor/categories/${categoryId}/sales-channels`
|
|
2225
|
+
);
|
|
2226
|
+
}
|
|
2227
|
+
// -----------------------------------------------------------------------
|
|
2228
|
+
// 3.5 — Category: Assign to Sales Channels
|
|
2229
|
+
// -----------------------------------------------------------------------
|
|
2230
|
+
/**
|
|
2231
|
+
* Assign a category to one or more sales channels (idempotent).
|
|
2232
|
+
*
|
|
2233
|
+
* `POST /admin/thor/categories/:id/sales-channels`
|
|
2234
|
+
*/
|
|
2235
|
+
async assignCategoryToChannels(categoryId, body) {
|
|
2236
|
+
return this.request(
|
|
2237
|
+
"POST",
|
|
2238
|
+
`/admin/thor/categories/${categoryId}/sales-channels`,
|
|
2239
|
+
body
|
|
2240
|
+
);
|
|
2241
|
+
}
|
|
2242
|
+
// -----------------------------------------------------------------------
|
|
2243
|
+
// 3.6 — Category: Unassign from Sales Channels
|
|
2244
|
+
// -----------------------------------------------------------------------
|
|
2245
|
+
/**
|
|
2246
|
+
* Remove category assignment from one or more sales channels (idempotent).
|
|
2247
|
+
*
|
|
2248
|
+
* `DELETE /admin/thor/categories/:id/sales-channels`
|
|
2249
|
+
*/
|
|
2250
|
+
async unassignCategoryFromChannels(categoryId, body) {
|
|
2251
|
+
return this.request(
|
|
2252
|
+
"DELETE",
|
|
2253
|
+
`/admin/thor/categories/${categoryId}/sales-channels`,
|
|
2254
|
+
body
|
|
2255
|
+
);
|
|
2256
|
+
}
|
|
2257
|
+
// -----------------------------------------------------------------------
|
|
2258
|
+
// 3.7 — Collection: Sales Channels
|
|
2259
|
+
// -----------------------------------------------------------------------
|
|
2260
|
+
/**
|
|
2261
|
+
* Sales channels to which a collection is assigned.
|
|
2262
|
+
*
|
|
2263
|
+
* `GET /admin/thor/collections/:id/sales-channels`
|
|
2264
|
+
*/
|
|
2265
|
+
async getCollectionSalesChannels(collectionId) {
|
|
2266
|
+
return this.request(
|
|
2267
|
+
"GET",
|
|
2268
|
+
`/admin/thor/collections/${collectionId}/sales-channels`
|
|
2269
|
+
);
|
|
2270
|
+
}
|
|
2271
|
+
// -----------------------------------------------------------------------
|
|
2272
|
+
// 3.8 — Collection: Assign to Sales Channels
|
|
2273
|
+
// -----------------------------------------------------------------------
|
|
2274
|
+
/**
|
|
2275
|
+
* Assign a collection to one or more sales channels (idempotent).
|
|
2276
|
+
*
|
|
2277
|
+
* `POST /admin/thor/collections/:id/sales-channels`
|
|
2278
|
+
*/
|
|
2279
|
+
async assignCollectionToChannels(collectionId, body) {
|
|
2280
|
+
return this.request(
|
|
2281
|
+
"POST",
|
|
2282
|
+
`/admin/thor/collections/${collectionId}/sales-channels`,
|
|
2283
|
+
body
|
|
2284
|
+
);
|
|
2285
|
+
}
|
|
2286
|
+
// -----------------------------------------------------------------------
|
|
2287
|
+
// 3.9 — Collection: Unassign from Sales Channels
|
|
2288
|
+
// -----------------------------------------------------------------------
|
|
2289
|
+
/**
|
|
2290
|
+
* Remove collection assignment from one or more sales channels (idempotent).
|
|
2291
|
+
*
|
|
2292
|
+
* `DELETE /admin/thor/collections/:id/sales-channels`
|
|
2293
|
+
*/
|
|
2294
|
+
async unassignCollectionFromChannels(collectionId, body) {
|
|
2295
|
+
return this.request(
|
|
2296
|
+
"DELETE",
|
|
2297
|
+
`/admin/thor/collections/${collectionId}/sales-channels`,
|
|
2298
|
+
body
|
|
2299
|
+
);
|
|
2300
|
+
}
|
|
2301
|
+
// -----------------------------------------------------------------------
|
|
2302
|
+
// 3.10 — Storefront Config: Read
|
|
2303
|
+
// -----------------------------------------------------------------------
|
|
2304
|
+
/**
|
|
2305
|
+
* Read the visual storefront configuration for a channel.
|
|
2306
|
+
* `:id` is the `sales_channel_id`.
|
|
2307
|
+
*
|
|
2308
|
+
* `GET /admin/thor/storefront-config/:id`
|
|
2309
|
+
*/
|
|
2310
|
+
async getStorefrontConfig(salesChannelId) {
|
|
2311
|
+
return this.request(
|
|
2312
|
+
"GET",
|
|
2313
|
+
`/admin/thor/storefront-config/${salesChannelId}`
|
|
2314
|
+
);
|
|
2315
|
+
}
|
|
2316
|
+
// -----------------------------------------------------------------------
|
|
2317
|
+
// 3.11 — Storefront Config: Update
|
|
2318
|
+
// -----------------------------------------------------------------------
|
|
2319
|
+
/**
|
|
2320
|
+
* Update the visual storefront configuration for a channel.
|
|
2321
|
+
* Triggers the `storefront_config.updated` event → ISR webhook.
|
|
2322
|
+
*
|
|
2323
|
+
* `PUT /admin/thor/storefront-config/:id`
|
|
2324
|
+
*/
|
|
2325
|
+
async updateStorefrontConfig(salesChannelId, body) {
|
|
2326
|
+
return this.request(
|
|
2327
|
+
"PUT",
|
|
2328
|
+
`/admin/thor/storefront-config/${salesChannelId}`,
|
|
2329
|
+
body
|
|
2330
|
+
);
|
|
2331
|
+
}
|
|
2332
|
+
// -----------------------------------------------------------------------
|
|
2333
|
+
// 3.12 — Site Designer Config: Read
|
|
2334
|
+
// -----------------------------------------------------------------------
|
|
2335
|
+
/**
|
|
2336
|
+
* Read the active Site Designer configuration for a channel, including the
|
|
2337
|
+
* last 10 history entries.
|
|
2338
|
+
*
|
|
2339
|
+
* `GET /admin/thor/site-config/:sales_channel_id`
|
|
2340
|
+
*/
|
|
2341
|
+
async getSiteConfig(salesChannelId) {
|
|
2342
|
+
return this.request(
|
|
2343
|
+
"GET",
|
|
2344
|
+
`/admin/thor/site-config/${salesChannelId}`
|
|
2345
|
+
);
|
|
2346
|
+
}
|
|
2347
|
+
// -----------------------------------------------------------------------
|
|
2348
|
+
// 3.13 — Site Designer Config: Update
|
|
2349
|
+
// -----------------------------------------------------------------------
|
|
2350
|
+
/**
|
|
2351
|
+
* Update the Site Designer configuration for a channel.
|
|
2352
|
+
* Creates a new history entry on every PUT.
|
|
2353
|
+
*
|
|
2354
|
+
* `PUT /admin/thor/site-config/:sales_channel_id`
|
|
2355
|
+
*/
|
|
2356
|
+
async updateSiteConfig(salesChannelId, body) {
|
|
2357
|
+
return this.request(
|
|
2358
|
+
"PUT",
|
|
2359
|
+
`/admin/thor/site-config/${salesChannelId}`,
|
|
2360
|
+
body
|
|
2361
|
+
);
|
|
2362
|
+
}
|
|
2363
|
+
// -----------------------------------------------------------------------
|
|
2364
|
+
// 3.14 — Site Designer Config: History
|
|
2365
|
+
// -----------------------------------------------------------------------
|
|
2366
|
+
/**
|
|
2367
|
+
* Full paginated history of Site Designer versions for a channel.
|
|
2368
|
+
* Entries are immutable — they are never deleted.
|
|
2369
|
+
*
|
|
2370
|
+
* `GET /admin/thor/site-config/:sales_channel_id/history`
|
|
2371
|
+
*/
|
|
2372
|
+
async getSiteConfigHistory(salesChannelId, options = {}) {
|
|
2373
|
+
const { limit = 20, offset = 0 } = options;
|
|
2374
|
+
return this.request(
|
|
2375
|
+
"GET",
|
|
2376
|
+
`/admin/thor/site-config/${salesChannelId}/history?limit=${limit}&offset=${offset}`
|
|
2377
|
+
);
|
|
2378
|
+
}
|
|
2379
|
+
// -----------------------------------------------------------------------
|
|
2380
|
+
// 3.15 — Site Designer Config: Restore
|
|
2381
|
+
// -----------------------------------------------------------------------
|
|
2382
|
+
/**
|
|
2383
|
+
* Restore a previous Site Designer version.
|
|
2384
|
+
* Internally runs the update workflow with the stored config of the target
|
|
2385
|
+
* version — this creates a **new** history entry and does NOT mutate past
|
|
2386
|
+
* entries.
|
|
2387
|
+
*
|
|
2388
|
+
* `POST /admin/thor/site-config/:sales_channel_id/restore/:version`
|
|
2389
|
+
*/
|
|
2390
|
+
async restoreSiteConfig(salesChannelId, version) {
|
|
2391
|
+
return this.request(
|
|
2392
|
+
"POST",
|
|
2393
|
+
`/admin/thor/site-config/${salesChannelId}/restore/${encodeURIComponent(version)}`
|
|
2394
|
+
);
|
|
2395
|
+
}
|
|
2396
|
+
};
|
|
2397
|
+
|
|
2398
|
+
// src/providers/medusa/index.ts
|
|
2399
|
+
var MEDUSA_CART_RETRIEVE_FIELDS = "+items,+items.variant,+items.variant.product,+items.variant.product.thumbnail,+items.variant.options,+items.variant.options.option";
|
|
2400
|
+
var MedusaProvider = class {
|
|
2401
|
+
name = "medusa";
|
|
2402
|
+
version = "2.0.0";
|
|
2403
|
+
/**
|
|
2404
|
+
* Medusa backend capabilities.
|
|
2405
|
+
* Starts with static defaults, replaced by dynamic values from
|
|
2406
|
+
* `GET /store/thor/capabilities` once the fetch completes.
|
|
2407
|
+
*/
|
|
2408
|
+
_dynamicCapabilities = null;
|
|
2409
|
+
/** Promise that resolves when capabilities fetch completes (success or failure) */
|
|
2410
|
+
_capabilitiesPromise = null;
|
|
2411
|
+
/**
|
|
2412
|
+
* Getter returns dynamic capabilities if available, otherwise static fallback.
|
|
2413
|
+
*/
|
|
2414
|
+
get capabilities() {
|
|
2415
|
+
return this._dynamicCapabilities ?? MEDUSA_CAPABILITIES;
|
|
2416
|
+
}
|
|
2417
|
+
sdk;
|
|
2418
|
+
logger;
|
|
2419
|
+
currencyCode;
|
|
2420
|
+
retryConfig;
|
|
2421
|
+
baseUrl;
|
|
2422
|
+
publishableKey;
|
|
2423
|
+
/** Secret API key for admin operations (if configured). */
|
|
2424
|
+
adminApiKey;
|
|
2425
|
+
/**
|
|
2426
|
+
* Dedicated SDK instance for admin calls.
|
|
2427
|
+
* Separate from `this.sdk` to prevent the admin API key from being overwritten
|
|
2428
|
+
* by customer JWT tokens set during storefront authentication.
|
|
2429
|
+
* Only created when adminConfig.apiKey is provided.
|
|
2430
|
+
*/
|
|
2431
|
+
sdkAdmin;
|
|
2432
|
+
/**
|
|
2433
|
+
* Admin client for Thor multi-tenant endpoints (`/admin/thor/`).
|
|
2434
|
+
* Available when `adminConfig.apiKey` is provided.
|
|
2435
|
+
*
|
|
2436
|
+
* @example
|
|
2437
|
+
* ```typescript
|
|
2438
|
+
* const { customers } = await commerce.admin.getChannelCustomers('sc_01JXXXXX');
|
|
2439
|
+
* const { api_keys } = await commerce.admin.getChannelApiKeys('sc_01JXXXXX');
|
|
2440
|
+
* await commerce.admin.updateStorefrontConfig('sc_01JXXXXX', { theme_accent_color: '#EF4444', ... });
|
|
2441
|
+
* ```
|
|
2442
|
+
*/
|
|
2443
|
+
admin;
|
|
2444
|
+
/** Cached distinct product status values — rarely changes, cached for process lifetime */
|
|
2445
|
+
_cachedProductStatuses = null;
|
|
2446
|
+
/**
|
|
2447
|
+
* In-memory cache for region countries
|
|
2448
|
+
* Structure: Map<regionId, { countries: Country[], expiresAt: number }>
|
|
2449
|
+
*/
|
|
2450
|
+
regionCountriesCache = /* @__PURE__ */ new Map();
|
|
2451
|
+
/**
|
|
2290
2452
|
* Cache TTL for region countries (5 minutes)
|
|
2291
2453
|
*/
|
|
2292
2454
|
REGION_COUNTRIES_CACHE_TTL = 5 * 60 * 1e3;
|
|
@@ -2349,10 +2511,15 @@ var MedusaProvider = class {
|
|
|
2349
2511
|
debug: config.debug ?? false
|
|
2350
2512
|
// No publishableKey or auth config: this instance is admin-only, no JWT needed
|
|
2351
2513
|
});
|
|
2514
|
+
this.admin = new MedusaAdminClient({
|
|
2515
|
+
baseUrl: config.baseUrl,
|
|
2516
|
+
apiKey: config.adminConfig.apiKey,
|
|
2517
|
+
debug: config.debug
|
|
2518
|
+
});
|
|
2352
2519
|
this.logger.info("[Admin Auth] Dedicated admin SDK instance created with API key");
|
|
2353
2520
|
}
|
|
2354
2521
|
if (config.auth) {
|
|
2355
|
-
this.auth = new MedusaAuthProvider(this.sdk, config.auth, config.debug);
|
|
2522
|
+
this.auth = new MedusaAuthProvider(this.sdk, config.auth, config.debug, config.baseUrl, config.publishableKey);
|
|
2356
2523
|
this.logger.info("Auth provider initialized");
|
|
2357
2524
|
}
|
|
2358
2525
|
this._capabilitiesPromise = this._fetchCapabilities();
|
|
@@ -5308,2801 +5475,194 @@ var MedusaProvider = class {
|
|
|
5308
5475
|
} catch (error) {
|
|
5309
5476
|
if (error instanceof import_types.StorefrontConfigError) throw error;
|
|
5310
5477
|
const errorMessage = extractErrorMessage(error);
|
|
5311
|
-
throw new ProviderAPIError(
|
|
5312
|
-
`Failed to initialize storefront context: ${errorMessage}`,
|
|
5313
|
-
"medusa",
|
|
5314
|
-
error?.status ?? 500
|
|
5315
|
-
);
|
|
5316
|
-
}
|
|
5317
|
-
}
|
|
5318
|
-
/**
|
|
5319
|
-
* Medusa always requires storefront configuration for multi-tenant support.
|
|
5320
|
-
*/
|
|
5321
|
-
isStorefrontRequired() {
|
|
5322
|
-
return true;
|
|
5323
|
-
}
|
|
5324
|
-
// ============================================
|
|
5325
|
-
// Thor Plugin — Storefront Configuration
|
|
5326
|
-
// ============================================
|
|
5327
|
-
/** In-memory cache for storefront config (60s TTL) */
|
|
5328
|
-
_storefrontConfigCache = null;
|
|
5329
|
-
/** In-memory cache for designer config (60s TTL) */
|
|
5330
|
-
_designerConfigCache = null;
|
|
5331
|
-
/** Cache TTL for Thor plugin configs (60 seconds) */
|
|
5332
|
-
THOR_CONFIG_CACHE_TTL = 60 * 1e3;
|
|
5333
|
-
/**
|
|
5334
|
-
* Get per-channel storefront configuration from Thor Commerce plugin.
|
|
5335
|
-
* Calls `GET /store/thor/config`.
|
|
5336
|
-
*
|
|
5337
|
-
* @returns StorefrontConfig or null if plugin is not installed / config not set
|
|
5338
|
-
*/
|
|
5339
|
-
async getStorefrontConfig() {
|
|
5340
|
-
if (this._storefrontConfigCache && Date.now() < this._storefrontConfigCache.expiresAt) {
|
|
5341
|
-
return this._storefrontConfigCache.config;
|
|
5342
|
-
}
|
|
5343
|
-
try {
|
|
5344
|
-
const url = `${this.baseUrl}/store/thor/config`;
|
|
5345
|
-
const response = await fetchWithRetry(
|
|
5346
|
-
url,
|
|
5347
|
-
{
|
|
5348
|
-
method: "GET",
|
|
5349
|
-
headers: {
|
|
5350
|
-
"x-publishable-api-key": this.publishableKey,
|
|
5351
|
-
"Content-Type": "application/json"
|
|
5352
|
-
}
|
|
5353
|
-
},
|
|
5354
|
-
this.retryConfig
|
|
5355
|
-
);
|
|
5356
|
-
if (response.status === 404) {
|
|
5357
|
-
this._storefrontConfigCache = { config: null, expiresAt: Date.now() + this.THOR_CONFIG_CACHE_TTL };
|
|
5358
|
-
return null;
|
|
5359
|
-
}
|
|
5360
|
-
if (!response.ok) {
|
|
5361
|
-
this.logger.warn("[StorefrontConfig] Non-OK response", { status: response.status });
|
|
5362
|
-
return null;
|
|
5363
|
-
}
|
|
5364
|
-
const data = await response.json();
|
|
5365
|
-
const config = transformStorefrontConfig(data.storefront_config ?? data);
|
|
5366
|
-
this._storefrontConfigCache = { config, expiresAt: Date.now() + this.THOR_CONFIG_CACHE_TTL };
|
|
5367
|
-
this.logger.debug("[StorefrontConfig] Loaded from backend", { id: config.id });
|
|
5368
|
-
return config;
|
|
5369
|
-
} catch (error) {
|
|
5370
|
-
this.logger.warn("[StorefrontConfig] Failed to fetch", {
|
|
5371
|
-
error: error instanceof Error ? error.message : String(error)
|
|
5372
|
-
});
|
|
5373
|
-
return null;
|
|
5374
|
-
}
|
|
5375
|
-
}
|
|
5376
|
-
/**
|
|
5377
|
-
* Get active Site Designer configuration from Thor Commerce plugin.
|
|
5378
|
-
* Calls `GET /store/thor/site-config`.
|
|
5379
|
-
*
|
|
5380
|
-
* @returns Versioned DesignerConfig or null if plugin is not installed / config not set
|
|
5381
|
-
*/
|
|
5382
|
-
async getDesignerConfig() {
|
|
5383
|
-
if (this._designerConfigCache && Date.now() < this._designerConfigCache.expiresAt) {
|
|
5384
|
-
return this._designerConfigCache.result;
|
|
5385
|
-
}
|
|
5386
|
-
try {
|
|
5387
|
-
const url = `${this.baseUrl}/store/thor/site-config`;
|
|
5388
|
-
const response = await fetchWithRetry(
|
|
5389
|
-
url,
|
|
5390
|
-
{
|
|
5391
|
-
method: "GET",
|
|
5392
|
-
headers: {
|
|
5393
|
-
"x-publishable-api-key": this.publishableKey,
|
|
5394
|
-
"Content-Type": "application/json"
|
|
5395
|
-
}
|
|
5396
|
-
},
|
|
5397
|
-
this.retryConfig
|
|
5398
|
-
);
|
|
5399
|
-
if (response.status === 404) {
|
|
5400
|
-
this._designerConfigCache = { result: null, expiresAt: Date.now() + this.THOR_CONFIG_CACHE_TTL };
|
|
5401
|
-
return null;
|
|
5402
|
-
}
|
|
5403
|
-
if (!response.ok) {
|
|
5404
|
-
this.logger.warn("[DesignerConfig] Non-OK response", { status: response.status });
|
|
5405
|
-
return null;
|
|
5406
|
-
}
|
|
5407
|
-
const data = await response.json();
|
|
5408
|
-
const result = {
|
|
5409
|
-
version: data.version ?? "",
|
|
5410
|
-
config: data.config
|
|
5411
|
-
};
|
|
5412
|
-
this._designerConfigCache = { result, expiresAt: Date.now() + this.THOR_CONFIG_CACHE_TTL };
|
|
5413
|
-
this.logger.debug("[DesignerConfig] Loaded from backend", { version: result.version });
|
|
5414
|
-
return result;
|
|
5415
|
-
} catch (error) {
|
|
5416
|
-
this.logger.warn("[DesignerConfig] Failed to fetch", {
|
|
5417
|
-
error: error instanceof Error ? error.message : String(error)
|
|
5418
|
-
});
|
|
5419
|
-
return null;
|
|
5420
|
-
}
|
|
5421
|
-
}
|
|
5422
|
-
// ============================================
|
|
5423
|
-
// Thor Plugin — Admin Site Designer
|
|
5424
|
-
// ============================================
|
|
5425
|
-
/**
|
|
5426
|
-
* Get admin Site Designer config + history for a sales channel.
|
|
5427
|
-
* Calls `GET /admin/thor/site-config/:salesChannelId`.
|
|
5428
|
-
*/
|
|
5429
|
-
async getAdminDesignerConfig(salesChannelId) {
|
|
5430
|
-
return this.makeAdminRequest(async () => {
|
|
5431
|
-
const path = `/admin/thor/site-config/${encodeURIComponent(salesChannelId)}`;
|
|
5432
|
-
let data;
|
|
5433
|
-
try {
|
|
5434
|
-
data = await this.sdk.client.fetch(path, { method: "GET" });
|
|
5435
|
-
} catch (error) {
|
|
5436
|
-
if (error?.status === 404) return null;
|
|
5437
|
-
throw new ProviderAPIError(
|
|
5438
|
-
`Failed to fetch admin designer config: ${error?.message ?? error?.status}`,
|
|
5439
|
-
"medusa",
|
|
5440
|
-
error?.status
|
|
5441
|
-
);
|
|
5442
|
-
}
|
|
5443
|
-
return {
|
|
5444
|
-
version: data.version ?? "",
|
|
5445
|
-
config: data.config,
|
|
5446
|
-
history: data.history
|
|
5447
|
-
};
|
|
5448
|
-
});
|
|
5449
|
-
}
|
|
5450
|
-
/**
|
|
5451
|
-
* Update the Site Designer config for a sales channel.
|
|
5452
|
-
* Calls `PUT /admin/thor/site-config/:salesChannelId`.
|
|
5453
|
-
*/
|
|
5454
|
-
async updateAdminDesignerConfig(salesChannelId, config) {
|
|
5455
|
-
return this.makeAdminRequest(async () => {
|
|
5456
|
-
const path = `/admin/thor/site-config/${encodeURIComponent(salesChannelId)}`;
|
|
5457
|
-
let data;
|
|
5458
|
-
try {
|
|
5459
|
-
data = await this.sdk.client.fetch(path, {
|
|
5460
|
-
method: "PUT",
|
|
5461
|
-
body: JSON.stringify({ config }),
|
|
5462
|
-
headers: { "Content-Type": "application/json" }
|
|
5463
|
-
});
|
|
5464
|
-
} catch (error) {
|
|
5465
|
-
throw new ProviderAPIError(
|
|
5466
|
-
`Failed to update admin designer config: ${error?.message ?? error?.status}`,
|
|
5467
|
-
"medusa",
|
|
5468
|
-
error?.status
|
|
5469
|
-
);
|
|
5470
|
-
}
|
|
5471
|
-
return {
|
|
5472
|
-
version: data.version ?? "",
|
|
5473
|
-
config: data.config
|
|
5474
|
-
};
|
|
5475
|
-
});
|
|
5476
|
-
}
|
|
5477
|
-
/**
|
|
5478
|
-
* Restore a previous Site Designer config version.
|
|
5479
|
-
* Calls `POST /admin/thor/site-config/:salesChannelId/restore/:version`.
|
|
5480
|
-
*/
|
|
5481
|
-
async restoreAdminDesignerConfig(salesChannelId, version) {
|
|
5482
|
-
return this.makeAdminRequest(async () => {
|
|
5483
|
-
const path = `/admin/thor/site-config/${encodeURIComponent(salesChannelId)}/restore/${encodeURIComponent(version)}`;
|
|
5484
|
-
let data;
|
|
5485
|
-
try {
|
|
5486
|
-
data = await this.sdk.client.fetch(path, { method: "POST" });
|
|
5487
|
-
} catch (error) {
|
|
5488
|
-
throw new ProviderAPIError(
|
|
5489
|
-
`Failed to restore admin designer config: ${error?.message ?? error?.status}`,
|
|
5490
|
-
"medusa",
|
|
5491
|
-
error?.status
|
|
5492
|
-
);
|
|
5493
|
-
}
|
|
5494
|
-
return {
|
|
5495
|
-
version: data.version ?? "",
|
|
5496
|
-
config: data.config
|
|
5497
|
-
};
|
|
5498
|
-
});
|
|
5499
|
-
}
|
|
5500
|
-
};
|
|
5501
|
-
|
|
5502
|
-
// src/providers/shopify/transforms.ts
|
|
5503
|
-
function transformMoney2(shopifyMoney) {
|
|
5504
|
-
return {
|
|
5505
|
-
amount: shopifyMoney?.amount ?? "0.00",
|
|
5506
|
-
currencyCode: shopifyMoney?.currencyCode?.toUpperCase() ?? "USD"
|
|
5507
|
-
};
|
|
5508
|
-
}
|
|
5509
|
-
function transformImage2(shopifyImage) {
|
|
5510
|
-
return {
|
|
5511
|
-
url: shopifyImage?.url ?? "",
|
|
5512
|
-
altText: shopifyImage?.altText ?? "",
|
|
5513
|
-
width: shopifyImage?.width ?? 800,
|
|
5514
|
-
height: shopifyImage?.height ?? 800
|
|
5515
|
-
};
|
|
5516
|
-
}
|
|
5517
|
-
function transformVariant(raw) {
|
|
5518
|
-
return {
|
|
5519
|
-
id: raw.id,
|
|
5520
|
-
title: raw.title ?? "",
|
|
5521
|
-
availableForSale: raw.availableForSale ?? true,
|
|
5522
|
-
price: transformMoney2(raw.price),
|
|
5523
|
-
compareAtPrice: raw.compareAtPrice ? transformMoney2(raw.compareAtPrice) : void 0,
|
|
5524
|
-
selectedOptions: (raw.selectedOptions ?? []).map((opt) => ({
|
|
5525
|
-
name: opt.name,
|
|
5526
|
-
value: opt.value
|
|
5527
|
-
})),
|
|
5528
|
-
image: raw.image ? transformImage2(raw.image) : void 0
|
|
5529
|
-
};
|
|
5530
|
-
}
|
|
5531
|
-
function transformOption(raw) {
|
|
5532
|
-
return {
|
|
5533
|
-
id: raw.id,
|
|
5534
|
-
name: raw.name,
|
|
5535
|
-
values: raw.values ?? []
|
|
5536
|
-
};
|
|
5537
|
-
}
|
|
5538
|
-
function transformProduct2(raw) {
|
|
5539
|
-
const images = (raw.images?.edges ?? []).map((edge) => transformImage2(edge.node));
|
|
5540
|
-
const variants = (raw.variants?.edges ?? []).map((edge) => transformVariant(edge.node));
|
|
5541
|
-
return {
|
|
5542
|
-
id: raw.id,
|
|
5543
|
-
handle: raw.handle,
|
|
5544
|
-
title: raw.title,
|
|
5545
|
-
description: raw.description ?? "",
|
|
5546
|
-
descriptionHtml: raw.descriptionHtml ?? "",
|
|
5547
|
-
availableForSale: raw.availableForSale ?? true,
|
|
5548
|
-
featuredImage: raw.featuredImage ? transformImage2(raw.featuredImage) : images[0] ?? { url: "", altText: "", width: 800, height: 800 },
|
|
5549
|
-
images,
|
|
5550
|
-
options: (raw.options ?? []).map(transformOption),
|
|
5551
|
-
variants,
|
|
5552
|
-
priceRange: {
|
|
5553
|
-
minVariantPrice: transformMoney2(raw.priceRange?.minVariantPrice),
|
|
5554
|
-
maxVariantPrice: transformMoney2(raw.priceRange?.maxVariantPrice)
|
|
5555
|
-
},
|
|
5556
|
-
seo: {
|
|
5557
|
-
title: raw.seo?.title ?? raw.title ?? "",
|
|
5558
|
-
description: raw.seo?.description ?? raw.description ?? ""
|
|
5559
|
-
},
|
|
5560
|
-
tags: raw.tags ?? [],
|
|
5561
|
-
createdAt: raw.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
5562
|
-
updatedAt: raw.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
5563
|
-
vendor: raw.vendor,
|
|
5564
|
-
productType: raw.productType
|
|
5565
|
-
};
|
|
5566
|
-
}
|
|
5567
|
-
function transformCollection2(raw) {
|
|
5568
|
-
return {
|
|
5569
|
-
id: raw.id ?? "",
|
|
5570
|
-
handle: raw.handle,
|
|
5571
|
-
title: raw.title,
|
|
5572
|
-
description: raw.description ?? "",
|
|
5573
|
-
seo: {
|
|
5574
|
-
title: raw.seo?.title ?? raw.title ?? "",
|
|
5575
|
-
description: raw.seo?.description ?? raw.description ?? ""
|
|
5576
|
-
},
|
|
5577
|
-
updatedAt: raw.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
5578
|
-
path: `/collections/${raw.handle}`
|
|
5579
|
-
};
|
|
5580
|
-
}
|
|
5581
|
-
function transformCartItem2(raw) {
|
|
5582
|
-
const merchandise = raw.merchandise ?? {};
|
|
5583
|
-
const product = merchandise.product ?? {};
|
|
5584
|
-
return {
|
|
5585
|
-
id: raw.id,
|
|
5586
|
-
quantity: raw.quantity ?? 1,
|
|
5587
|
-
cost: {
|
|
5588
|
-
totalAmount: transformMoney2(raw.cost?.totalAmount)
|
|
5589
|
-
},
|
|
5590
|
-
merchandise: {
|
|
5591
|
-
id: merchandise.id ?? "",
|
|
5592
|
-
title: merchandise.title ?? "",
|
|
5593
|
-
selectedOptions: (merchandise.selectedOptions ?? []).map((opt) => ({
|
|
5594
|
-
name: opt.name,
|
|
5595
|
-
value: opt.value
|
|
5596
|
-
})),
|
|
5597
|
-
product: {
|
|
5598
|
-
id: product.id ?? "",
|
|
5599
|
-
handle: product.handle ?? "",
|
|
5600
|
-
title: product.title ?? "",
|
|
5601
|
-
featuredImage: product.featuredImage ? transformImage2(product.featuredImage) : { url: "", altText: "", width: 800, height: 800 }
|
|
5602
|
-
}
|
|
5603
|
-
}
|
|
5604
|
-
};
|
|
5605
|
-
}
|
|
5606
|
-
function transformCart2(raw) {
|
|
5607
|
-
const lines = (raw.lines?.edges ?? []).map((edge) => transformCartItem2(edge.node));
|
|
5608
|
-
return {
|
|
5609
|
-
id: raw.id,
|
|
5610
|
-
checkoutUrl: raw.checkoutUrl ?? "",
|
|
5611
|
-
totalQuantity: raw.totalQuantity ?? 0,
|
|
5612
|
-
cost: {
|
|
5613
|
-
subtotalAmount: transformMoney2(raw.cost?.subtotalAmount),
|
|
5614
|
-
totalAmount: transformMoney2(raw.cost?.totalAmount),
|
|
5615
|
-
totalTaxAmount: raw.cost?.totalTaxAmount ? transformMoney2(raw.cost.totalTaxAmount) : void 0
|
|
5616
|
-
},
|
|
5617
|
-
lines
|
|
5618
|
-
};
|
|
5619
|
-
}
|
|
5620
|
-
|
|
5621
|
-
// src/providers/shopify/queries.ts
|
|
5622
|
-
var PRODUCT_FRAGMENT = `
|
|
5623
|
-
fragment ProductFields on Product {
|
|
5624
|
-
id
|
|
5625
|
-
title
|
|
5626
|
-
handle
|
|
5627
|
-
description
|
|
5628
|
-
descriptionHtml
|
|
5629
|
-
vendor
|
|
5630
|
-
productType
|
|
5631
|
-
tags
|
|
5632
|
-
availableForSale
|
|
5633
|
-
createdAt
|
|
5634
|
-
updatedAt
|
|
5635
|
-
seo {
|
|
5636
|
-
title
|
|
5637
|
-
description
|
|
5638
|
-
}
|
|
5639
|
-
featuredImage {
|
|
5640
|
-
url
|
|
5641
|
-
altText
|
|
5642
|
-
width
|
|
5643
|
-
height
|
|
5644
|
-
}
|
|
5645
|
-
images(first: 10) {
|
|
5646
|
-
edges {
|
|
5647
|
-
node {
|
|
5648
|
-
url
|
|
5649
|
-
altText
|
|
5650
|
-
width
|
|
5651
|
-
height
|
|
5652
|
-
}
|
|
5653
|
-
}
|
|
5654
|
-
}
|
|
5655
|
-
priceRange {
|
|
5656
|
-
minVariantPrice {
|
|
5657
|
-
amount
|
|
5658
|
-
currencyCode
|
|
5659
|
-
}
|
|
5660
|
-
maxVariantPrice {
|
|
5661
|
-
amount
|
|
5662
|
-
currencyCode
|
|
5663
|
-
}
|
|
5664
|
-
}
|
|
5665
|
-
options {
|
|
5666
|
-
id
|
|
5667
|
-
name
|
|
5668
|
-
values
|
|
5669
|
-
}
|
|
5670
|
-
variants(first: 100) {
|
|
5671
|
-
edges {
|
|
5672
|
-
node {
|
|
5673
|
-
id
|
|
5674
|
-
title
|
|
5675
|
-
availableForSale
|
|
5676
|
-
price {
|
|
5677
|
-
amount
|
|
5678
|
-
currencyCode
|
|
5679
|
-
}
|
|
5680
|
-
compareAtPrice {
|
|
5681
|
-
amount
|
|
5682
|
-
currencyCode
|
|
5683
|
-
}
|
|
5684
|
-
selectedOptions {
|
|
5685
|
-
name
|
|
5686
|
-
value
|
|
5687
|
-
}
|
|
5688
|
-
image {
|
|
5689
|
-
url
|
|
5690
|
-
altText
|
|
5691
|
-
width
|
|
5692
|
-
height
|
|
5693
|
-
}
|
|
5694
|
-
}
|
|
5695
|
-
}
|
|
5696
|
-
}
|
|
5697
|
-
}
|
|
5698
|
-
`;
|
|
5699
|
-
var GET_PRODUCT_BY_HANDLE = `
|
|
5700
|
-
query GetProductByHandle($handle: String!) {
|
|
5701
|
-
product(handle: $handle) {
|
|
5702
|
-
...ProductFields
|
|
5703
|
-
}
|
|
5704
|
-
}
|
|
5705
|
-
${PRODUCT_FRAGMENT}
|
|
5706
|
-
`;
|
|
5707
|
-
var GET_PRODUCTS = `
|
|
5708
|
-
query GetProducts($first: Int!, $query: String, $sortKey: ProductSortKeys, $reverse: Boolean, $after: String) {
|
|
5709
|
-
products(first: $first, query: $query, sortKey: $sortKey, reverse: $reverse, after: $after) {
|
|
5710
|
-
edges {
|
|
5711
|
-
node {
|
|
5712
|
-
...ProductFields
|
|
5713
|
-
}
|
|
5714
|
-
cursor
|
|
5715
|
-
}
|
|
5716
|
-
pageInfo {
|
|
5717
|
-
hasNextPage
|
|
5718
|
-
hasPreviousPage
|
|
5719
|
-
}
|
|
5720
|
-
}
|
|
5721
|
-
}
|
|
5722
|
-
${PRODUCT_FRAGMENT}
|
|
5723
|
-
`;
|
|
5724
|
-
var GET_PRODUCT_RECOMMENDATIONS = `
|
|
5725
|
-
query GetProductRecommendations($productId: ID!) {
|
|
5726
|
-
productRecommendations(productId: $productId) {
|
|
5727
|
-
...ProductFields
|
|
5728
|
-
}
|
|
5729
|
-
}
|
|
5730
|
-
${PRODUCT_FRAGMENT}
|
|
5731
|
-
`;
|
|
5732
|
-
var COLLECTION_FRAGMENT = `
|
|
5733
|
-
fragment CollectionFields on Collection {
|
|
5734
|
-
id
|
|
5735
|
-
title
|
|
5736
|
-
handle
|
|
5737
|
-
description
|
|
5738
|
-
image {
|
|
5739
|
-
url
|
|
5740
|
-
altText
|
|
5741
|
-
width
|
|
5742
|
-
height
|
|
5743
|
-
}
|
|
5744
|
-
seo {
|
|
5745
|
-
title
|
|
5746
|
-
description
|
|
5747
|
-
}
|
|
5748
|
-
updatedAt
|
|
5749
|
-
}
|
|
5750
|
-
`;
|
|
5751
|
-
var GET_COLLECTION_BY_HANDLE = `
|
|
5752
|
-
query GetCollectionByHandle($handle: String!) {
|
|
5753
|
-
collection(handle: $handle) {
|
|
5754
|
-
...CollectionFields
|
|
5755
|
-
}
|
|
5756
|
-
}
|
|
5757
|
-
${COLLECTION_FRAGMENT}
|
|
5758
|
-
`;
|
|
5759
|
-
var GET_COLLECTIONS = `
|
|
5760
|
-
query GetCollections($first: Int!) {
|
|
5761
|
-
collections(first: $first) {
|
|
5762
|
-
edges {
|
|
5763
|
-
node {
|
|
5764
|
-
...CollectionFields
|
|
5765
|
-
}
|
|
5766
|
-
}
|
|
5767
|
-
}
|
|
5768
|
-
}
|
|
5769
|
-
${COLLECTION_FRAGMENT}
|
|
5770
|
-
`;
|
|
5771
|
-
var GET_COLLECTION_PRODUCTS = `
|
|
5772
|
-
query GetCollectionProducts($handle: String!, $first: Int!, $sortKey: ProductCollectionSortKeys, $reverse: Boolean, $after: String) {
|
|
5773
|
-
collection(handle: $handle) {
|
|
5774
|
-
products(first: $first, sortKey: $sortKey, reverse: $reverse, after: $after) {
|
|
5775
|
-
edges {
|
|
5776
|
-
node {
|
|
5777
|
-
...ProductFields
|
|
5778
|
-
}
|
|
5779
|
-
cursor
|
|
5780
|
-
}
|
|
5781
|
-
pageInfo {
|
|
5782
|
-
hasNextPage
|
|
5783
|
-
}
|
|
5784
|
-
}
|
|
5785
|
-
}
|
|
5786
|
-
}
|
|
5787
|
-
${PRODUCT_FRAGMENT}
|
|
5788
|
-
`;
|
|
5789
|
-
var CART_FRAGMENT = `
|
|
5790
|
-
fragment CartFields on Cart {
|
|
5791
|
-
id
|
|
5792
|
-
checkoutUrl
|
|
5793
|
-
totalQuantity
|
|
5794
|
-
cost {
|
|
5795
|
-
subtotalAmount {
|
|
5796
|
-
amount
|
|
5797
|
-
currencyCode
|
|
5798
|
-
}
|
|
5799
|
-
totalAmount {
|
|
5800
|
-
amount
|
|
5801
|
-
currencyCode
|
|
5802
|
-
}
|
|
5803
|
-
totalTaxAmount {
|
|
5804
|
-
amount
|
|
5805
|
-
currencyCode
|
|
5806
|
-
}
|
|
5807
|
-
}
|
|
5808
|
-
lines(first: 100) {
|
|
5809
|
-
edges {
|
|
5810
|
-
node {
|
|
5811
|
-
id
|
|
5812
|
-
quantity
|
|
5813
|
-
cost {
|
|
5814
|
-
totalAmount {
|
|
5815
|
-
amount
|
|
5816
|
-
currencyCode
|
|
5817
|
-
}
|
|
5818
|
-
}
|
|
5819
|
-
merchandise {
|
|
5820
|
-
... on ProductVariant {
|
|
5821
|
-
id
|
|
5822
|
-
title
|
|
5823
|
-
selectedOptions {
|
|
5824
|
-
name
|
|
5825
|
-
value
|
|
5826
|
-
}
|
|
5827
|
-
product {
|
|
5828
|
-
id
|
|
5829
|
-
handle
|
|
5830
|
-
title
|
|
5831
|
-
featuredImage {
|
|
5832
|
-
url
|
|
5833
|
-
altText
|
|
5834
|
-
width
|
|
5835
|
-
height
|
|
5836
|
-
}
|
|
5837
|
-
}
|
|
5838
|
-
}
|
|
5839
|
-
}
|
|
5840
|
-
}
|
|
5841
|
-
}
|
|
5842
|
-
}
|
|
5843
|
-
}
|
|
5844
|
-
`;
|
|
5845
|
-
var GET_CART = `
|
|
5846
|
-
query GetCart($cartId: ID!) {
|
|
5847
|
-
cart(id: $cartId) {
|
|
5848
|
-
...CartFields
|
|
5849
|
-
}
|
|
5850
|
-
}
|
|
5851
|
-
${CART_FRAGMENT}
|
|
5852
|
-
`;
|
|
5853
|
-
|
|
5854
|
-
// src/providers/shopify/mutations.ts
|
|
5855
|
-
var CART_CREATE = `
|
|
5856
|
-
mutation CartCreate($input: CartInput!) {
|
|
5857
|
-
cartCreate(input: $input) {
|
|
5858
|
-
cart {
|
|
5859
|
-
...CartFields
|
|
5860
|
-
}
|
|
5861
|
-
userErrors {
|
|
5862
|
-
field
|
|
5863
|
-
message
|
|
5864
|
-
code
|
|
5865
|
-
}
|
|
5866
|
-
}
|
|
5867
|
-
}
|
|
5868
|
-
${CART_FRAGMENT}
|
|
5869
|
-
`;
|
|
5870
|
-
var CART_LINES_ADD = `
|
|
5871
|
-
mutation CartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
|
|
5872
|
-
cartLinesAdd(cartId: $cartId, lines: $lines) {
|
|
5873
|
-
cart {
|
|
5874
|
-
...CartFields
|
|
5875
|
-
}
|
|
5876
|
-
userErrors {
|
|
5877
|
-
field
|
|
5878
|
-
message
|
|
5879
|
-
code
|
|
5880
|
-
}
|
|
5881
|
-
}
|
|
5882
|
-
}
|
|
5883
|
-
${CART_FRAGMENT}
|
|
5884
|
-
`;
|
|
5885
|
-
var CART_LINES_UPDATE = `
|
|
5886
|
-
mutation CartLinesUpdate($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
|
|
5887
|
-
cartLinesUpdate(cartId: $cartId, lines: $lines) {
|
|
5888
|
-
cart {
|
|
5889
|
-
...CartFields
|
|
5890
|
-
}
|
|
5891
|
-
userErrors {
|
|
5892
|
-
field
|
|
5893
|
-
message
|
|
5894
|
-
code
|
|
5895
|
-
}
|
|
5896
|
-
}
|
|
5897
|
-
}
|
|
5898
|
-
${CART_FRAGMENT}
|
|
5899
|
-
`;
|
|
5900
|
-
var CART_LINES_REMOVE = `
|
|
5901
|
-
mutation CartLinesRemove($cartId: ID!, $lineIds: [ID!]!) {
|
|
5902
|
-
cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
|
|
5903
|
-
cart {
|
|
5904
|
-
...CartFields
|
|
5905
|
-
}
|
|
5906
|
-
userErrors {
|
|
5907
|
-
field
|
|
5908
|
-
message
|
|
5909
|
-
code
|
|
5910
|
-
}
|
|
5911
|
-
}
|
|
5912
|
-
}
|
|
5913
|
-
${CART_FRAGMENT}
|
|
5914
|
-
`;
|
|
5915
|
-
var CART_DISCOUNT_CODES_UPDATE = `
|
|
5916
|
-
mutation CartDiscountCodesUpdate($cartId: ID!, $discountCodes: [String!]!) {
|
|
5917
|
-
cartDiscountCodesUpdate(cartId: $cartId, discountCodes: $discountCodes) {
|
|
5918
|
-
cart {
|
|
5919
|
-
...CartFields
|
|
5920
|
-
}
|
|
5921
|
-
userErrors {
|
|
5922
|
-
field
|
|
5923
|
-
message
|
|
5924
|
-
code
|
|
5925
|
-
}
|
|
5926
|
-
}
|
|
5927
|
-
}
|
|
5928
|
-
${CART_FRAGMENT}
|
|
5929
|
-
`;
|
|
5930
|
-
|
|
5931
|
-
// src/providers/shopify/index.ts
|
|
5932
|
-
var ShopifyProvider = class {
|
|
5933
|
-
name = "shopify";
|
|
5934
|
-
version = "1.0.0";
|
|
5935
|
-
capabilities = SHOPIFY_CAPABILITIES;
|
|
5936
|
-
logger;
|
|
5937
|
-
storeDomain;
|
|
5938
|
-
apiVersion;
|
|
5939
|
-
endpoint;
|
|
5940
|
-
headers;
|
|
5941
|
-
constructor(config) {
|
|
5942
|
-
this.logger = createLogger("ShopifyProvider", config.debug);
|
|
5943
|
-
this.storeDomain = config.storeDomain.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
5944
|
-
this.apiVersion = config.apiVersion ?? "2026-04";
|
|
5945
|
-
this.endpoint = `https://${this.storeDomain}/api/${this.apiVersion}/graphql.json`;
|
|
5946
|
-
this.headers = {
|
|
5947
|
-
"Content-Type": "application/json",
|
|
5948
|
-
...config.privateAccessToken ? { "Shopify-Storefront-Private-Token": config.privateAccessToken } : { "X-Shopify-Storefront-Access-Token": config.publicAccessToken }
|
|
5949
|
-
};
|
|
5950
|
-
this.logger.info("Initializing Shopify Provider", {
|
|
5951
|
-
storeDomain: this.storeDomain,
|
|
5952
|
-
apiVersion: this.apiVersion,
|
|
5953
|
-
authType: config.privateAccessToken ? "private" : "public"
|
|
5954
|
-
});
|
|
5955
|
-
}
|
|
5956
|
-
// ============================================
|
|
5957
|
-
// GraphQL Client
|
|
5958
|
-
// ============================================
|
|
5959
|
-
async query(gql, variables) {
|
|
5960
|
-
const response = await fetch(this.endpoint, {
|
|
5961
|
-
method: "POST",
|
|
5962
|
-
headers: this.headers,
|
|
5963
|
-
body: JSON.stringify({ query: gql, variables })
|
|
5964
|
-
});
|
|
5965
|
-
if (!response.ok) {
|
|
5966
|
-
throw new ProviderAPIError(
|
|
5967
|
-
`Shopify API error: ${response.status} ${response.statusText}`,
|
|
5968
|
-
"SHOPIFY_API_ERROR",
|
|
5969
|
-
response.status
|
|
5970
|
-
);
|
|
5971
|
-
}
|
|
5972
|
-
const json = await response.json();
|
|
5973
|
-
if (json.errors?.length) {
|
|
5974
|
-
const message = json.errors.map((e) => e.message).join("; ");
|
|
5975
|
-
throw new ProviderAPIError(`Shopify GraphQL error: ${message}`, "SHOPIFY_GRAPHQL_ERROR");
|
|
5976
|
-
}
|
|
5977
|
-
return json.data;
|
|
5978
|
-
}
|
|
5979
|
-
// ============================================
|
|
5980
|
-
// Product Operations
|
|
5981
|
-
// ============================================
|
|
5982
|
-
async getProduct(handle) {
|
|
5983
|
-
const data = await this.query(GET_PRODUCT_BY_HANDLE, { handle });
|
|
5984
|
-
return data.product ? transformProduct2(data.product) : void 0;
|
|
5985
|
-
}
|
|
5986
|
-
async getProducts(options) {
|
|
5987
|
-
const first = options?.first ?? 20;
|
|
5988
|
-
const sortKeyMap = {
|
|
5989
|
-
"title-asc": "TITLE",
|
|
5990
|
-
"title-desc": "TITLE",
|
|
5991
|
-
"price-asc": "PRICE",
|
|
5992
|
-
"price-desc": "PRICE",
|
|
5993
|
-
"best-selling": "BEST_SELLING",
|
|
5994
|
-
"created-desc": "CREATED_AT",
|
|
5995
|
-
"relevance": "RELEVANCE"
|
|
5996
|
-
};
|
|
5997
|
-
const sortKey = options?.sortKey ? sortKeyMap[options.sortKey] ?? "RELEVANCE" : "RELEVANCE";
|
|
5998
|
-
const reverse = options?.sortKey?.endsWith("-desc") ?? false;
|
|
5999
|
-
const data = await this.query(GET_PRODUCTS, {
|
|
6000
|
-
first,
|
|
6001
|
-
query: options?.query,
|
|
6002
|
-
sortKey,
|
|
6003
|
-
reverse
|
|
6004
|
-
});
|
|
6005
|
-
return data.products.edges.map((edge) => transformProduct2(edge.node));
|
|
6006
|
-
}
|
|
6007
|
-
async getProductRecommendations(productId) {
|
|
6008
|
-
const data = await this.query(GET_PRODUCT_RECOMMENDATIONS, {
|
|
6009
|
-
productId
|
|
6010
|
-
});
|
|
6011
|
-
return (data.productRecommendations ?? []).map(transformProduct2);
|
|
6012
|
-
}
|
|
6013
|
-
// ============================================
|
|
6014
|
-
// Collection Operations
|
|
6015
|
-
// ============================================
|
|
6016
|
-
async getCollection(handle) {
|
|
6017
|
-
const data = await this.query(GET_COLLECTION_BY_HANDLE, { handle });
|
|
6018
|
-
return data.collection ? transformCollection2(data.collection) : void 0;
|
|
6019
|
-
}
|
|
6020
|
-
async getCollections(options) {
|
|
6021
|
-
const data = await this.query(GET_COLLECTIONS, {
|
|
6022
|
-
first: options?.limit ?? 50
|
|
6023
|
-
});
|
|
6024
|
-
return data.collections.edges.map((edge) => transformCollection2(edge.node));
|
|
6025
|
-
}
|
|
6026
|
-
async getCollectionProducts(handle, options) {
|
|
6027
|
-
const data = await this.query(
|
|
6028
|
-
GET_COLLECTION_PRODUCTS,
|
|
6029
|
-
{
|
|
6030
|
-
handle,
|
|
6031
|
-
first: options?.first ?? 20,
|
|
6032
|
-
sortKey: options?.sortKey === "price-asc" || options?.sortKey === "price-desc" ? "PRICE" : "BEST_SELLING",
|
|
6033
|
-
reverse: options?.sortKey?.endsWith("-desc") ?? false
|
|
6034
|
-
}
|
|
6035
|
-
);
|
|
6036
|
-
return (data.collection?.products?.edges ?? []).map((edge) => transformProduct2(edge.node));
|
|
6037
|
-
}
|
|
6038
|
-
// ============================================
|
|
6039
|
-
// Category Operations (Shopify does not have native categories)
|
|
6040
|
-
// ============================================
|
|
6041
|
-
async getCategory() {
|
|
6042
|
-
this.logger.warn("Shopify does not support native categories. Use collections instead.");
|
|
6043
|
-
return void 0;
|
|
6044
|
-
}
|
|
6045
|
-
async getCategories() {
|
|
6046
|
-
return [];
|
|
6047
|
-
}
|
|
6048
|
-
async getCategoryProducts() {
|
|
6049
|
-
return [];
|
|
6050
|
-
}
|
|
6051
|
-
// ============================================
|
|
6052
|
-
// Cart Operations
|
|
6053
|
-
// ============================================
|
|
6054
|
-
async createCart() {
|
|
6055
|
-
const data = await this.query(CART_CREATE, {
|
|
6056
|
-
input: {}
|
|
6057
|
-
});
|
|
6058
|
-
if (data.cartCreate.userErrors?.length) {
|
|
6059
|
-
throw new ProviderAPIError(
|
|
6060
|
-
`Cart creation failed: ${data.cartCreate.userErrors.map((e) => e.message).join("; ")}`,
|
|
6061
|
-
"SHOPIFY_CART_ERROR"
|
|
6062
|
-
);
|
|
6063
|
-
}
|
|
6064
|
-
return transformCart2(data.cartCreate.cart);
|
|
6065
|
-
}
|
|
6066
|
-
async getCart(cartId) {
|
|
6067
|
-
const data = await this.query(GET_CART, { cartId });
|
|
6068
|
-
return data.cart ? transformCart2(data.cart) : void 0;
|
|
6069
|
-
}
|
|
6070
|
-
async addToCart(cartId, lines) {
|
|
6071
|
-
const data = await this.query(CART_LINES_ADD, {
|
|
6072
|
-
cartId,
|
|
6073
|
-
lines: lines.map((l) => ({ merchandiseId: l.merchandiseId, quantity: l.quantity }))
|
|
6074
|
-
});
|
|
6075
|
-
if (data.cartLinesAdd.userErrors?.length) {
|
|
6076
|
-
throw new ProviderAPIError(
|
|
6077
|
-
`Add to cart failed: ${data.cartLinesAdd.userErrors.map((e) => e.message).join("; ")}`,
|
|
6078
|
-
"SHOPIFY_CART_ERROR"
|
|
6079
|
-
);
|
|
6080
|
-
}
|
|
6081
|
-
return transformCart2(data.cartLinesAdd.cart);
|
|
6082
|
-
}
|
|
6083
|
-
async removeFromCart(cartId, lineIds) {
|
|
6084
|
-
const data = await this.query(CART_LINES_REMOVE, {
|
|
6085
|
-
cartId,
|
|
6086
|
-
lineIds
|
|
6087
|
-
});
|
|
6088
|
-
if (data.cartLinesRemove.userErrors?.length) {
|
|
6089
|
-
throw new ProviderAPIError(
|
|
6090
|
-
`Remove from cart failed: ${data.cartLinesRemove.userErrors.map((e) => e.message).join("; ")}`,
|
|
6091
|
-
"SHOPIFY_CART_ERROR"
|
|
6092
|
-
);
|
|
6093
|
-
}
|
|
6094
|
-
return transformCart2(data.cartLinesRemove.cart);
|
|
6095
|
-
}
|
|
6096
|
-
async updateCart(cartId, lines) {
|
|
6097
|
-
const data = await this.query(CART_LINES_UPDATE, {
|
|
6098
|
-
cartId,
|
|
6099
|
-
lines: lines.map((l) => ({ id: l.id, quantity: l.quantity }))
|
|
6100
|
-
});
|
|
6101
|
-
if (data.cartLinesUpdate.userErrors?.length) {
|
|
6102
|
-
throw new ProviderAPIError(
|
|
6103
|
-
`Update cart failed: ${data.cartLinesUpdate.userErrors.map((e) => e.message).join("; ")}`,
|
|
6104
|
-
"SHOPIFY_CART_ERROR"
|
|
6105
|
-
);
|
|
6106
|
-
}
|
|
6107
|
-
return transformCart2(data.cartLinesUpdate.cart);
|
|
6108
|
-
}
|
|
6109
|
-
// ============================================
|
|
6110
|
-
// Checkout Operations
|
|
6111
|
-
// ============================================
|
|
6112
|
-
async completeCheckout(_cartId) {
|
|
6113
|
-
throw new ProviderAPIError(
|
|
6114
|
-
"Shopify uses hosted checkout. Redirect the buyer to cart.checkoutUrl instead of calling completeCheckout().",
|
|
6115
|
-
"SHOPIFY_HOSTED_CHECKOUT"
|
|
6116
|
-
);
|
|
6117
|
-
}
|
|
6118
|
-
async getShippingMethods(_cartId) {
|
|
6119
|
-
return [];
|
|
6120
|
-
}
|
|
6121
|
-
async getRegionCountries() {
|
|
6122
|
-
return [];
|
|
6123
|
-
}
|
|
6124
|
-
async addShippingMethod(_cartId, _methodId) {
|
|
6125
|
-
throw new ProviderAPIError(
|
|
6126
|
-
"Shopify handles shipping selection in hosted checkout.",
|
|
6127
|
-
"SHOPIFY_HOSTED_CHECKOUT"
|
|
6128
|
-
);
|
|
6129
|
-
}
|
|
6130
|
-
async initiatePaymentSession(_cartId, _providerId) {
|
|
6131
|
-
throw new ProviderAPIError(
|
|
6132
|
-
"Shopify handles payment in hosted checkout.",
|
|
6133
|
-
"SHOPIFY_HOSTED_CHECKOUT"
|
|
6134
|
-
);
|
|
6135
|
-
}
|
|
6136
|
-
async applyDiscountCode(cartId, code) {
|
|
6137
|
-
const currentCart = await this.getCart(cartId);
|
|
6138
|
-
const existingCodes = [];
|
|
6139
|
-
existingCodes.push(code);
|
|
6140
|
-
const data = await this.query(
|
|
6141
|
-
CART_DISCOUNT_CODES_UPDATE,
|
|
6142
|
-
{ cartId, discountCodes: existingCodes }
|
|
6143
|
-
);
|
|
6144
|
-
if (data.cartDiscountCodesUpdate.userErrors?.length) {
|
|
6145
|
-
throw new ProviderAPIError(
|
|
6146
|
-
`Discount code failed: ${data.cartDiscountCodesUpdate.userErrors.map((e) => e.message).join("; ")}`,
|
|
6147
|
-
"SHOPIFY_DISCOUNT_ERROR"
|
|
6148
|
-
);
|
|
6149
|
-
}
|
|
6150
|
-
return transformCart2(data.cartDiscountCodesUpdate.cart);
|
|
6151
|
-
}
|
|
6152
|
-
async removeDiscountCode(cartId, _code) {
|
|
6153
|
-
const data = await this.query(
|
|
6154
|
-
CART_DISCOUNT_CODES_UPDATE,
|
|
6155
|
-
{ cartId, discountCodes: [] }
|
|
6156
|
-
);
|
|
6157
|
-
return transformCart2(data.cartDiscountCodesUpdate.cart);
|
|
6158
|
-
}
|
|
6159
|
-
// ============================================
|
|
6160
|
-
// Customer Operations
|
|
6161
|
-
// ============================================
|
|
6162
|
-
async getOrders(options) {
|
|
6163
|
-
this.logger.warn("Shopify orders require Customer Account API (planned upgrade path).");
|
|
6164
|
-
return [];
|
|
6165
|
-
}
|
|
6166
|
-
// ============================================
|
|
6167
|
-
// Payment Operations
|
|
6168
|
-
// ============================================
|
|
6169
|
-
async getPaymentMethods(_options) {
|
|
6170
|
-
return [];
|
|
6171
|
-
}
|
|
6172
|
-
// ============================================
|
|
6173
|
-
// Inventory Operations (not available via Storefront API)
|
|
6174
|
-
// ============================================
|
|
6175
|
-
async getStockLocations() {
|
|
6176
|
-
return [];
|
|
6177
|
-
}
|
|
6178
|
-
async checkInventoryLevels() {
|
|
6179
|
-
return [];
|
|
6180
|
-
}
|
|
6181
|
-
async validateCartStock() {
|
|
6182
|
-
return { allAvailable: true, unavailableItems: [] };
|
|
6183
|
-
}
|
|
6184
|
-
async getFulfillmentOptions() {
|
|
6185
|
-
return [];
|
|
6186
|
-
}
|
|
6187
|
-
// ============================================
|
|
6188
|
-
// Admin Operations (not available via Storefront API)
|
|
6189
|
-
// ============================================
|
|
6190
|
-
async cancelOrder(_orderId) {
|
|
6191
|
-
throw new ProviderAPIError(
|
|
6192
|
-
"Order cancellation requires Shopify Admin API (not available in Storefront API).",
|
|
6193
|
-
"SHOPIFY_ADMIN_REQUIRED"
|
|
6194
|
-
);
|
|
6195
|
-
}
|
|
6196
|
-
async createFulfillment(_orderId) {
|
|
6197
|
-
throw new ProviderAPIError(
|
|
6198
|
-
"Fulfillment requires Shopify Admin API.",
|
|
6199
|
-
"SHOPIFY_ADMIN_REQUIRED"
|
|
6200
|
-
);
|
|
6201
|
-
}
|
|
6202
|
-
async createRefund(_orderId) {
|
|
6203
|
-
throw new ProviderAPIError(
|
|
6204
|
-
"Refunds require Shopify Admin API.",
|
|
6205
|
-
"SHOPIFY_ADMIN_REQUIRED"
|
|
6206
|
-
);
|
|
6207
|
-
}
|
|
6208
|
-
async getAdminOrders() {
|
|
6209
|
-
return { orders: [], count: 0 };
|
|
6210
|
-
}
|
|
6211
|
-
// ============================================
|
|
6212
|
-
// Storefront / Multi-Tenant
|
|
6213
|
-
// ============================================
|
|
6214
|
-
async getStorefrontContext() {
|
|
6215
|
-
return {
|
|
6216
|
-
id: this.storeDomain,
|
|
6217
|
-
name: this.storeDomain,
|
|
6218
|
-
platformType: "shopify",
|
|
6219
|
-
requiresProductScoping: false,
|
|
6220
|
-
currencyCode: "USD"
|
|
6221
|
-
};
|
|
6222
|
-
}
|
|
6223
|
-
isStorefrontRequired() {
|
|
6224
|
-
return false;
|
|
6225
|
-
}
|
|
6226
|
-
};
|
|
6227
|
-
|
|
6228
|
-
// src/providers/bigcommerce/transforms.ts
|
|
6229
|
-
function transformMoney3(amount, currencyCode = "USD") {
|
|
6230
|
-
const numAmount = typeof amount === "string" ? parseFloat(amount) : amount ?? 0;
|
|
6231
|
-
return {
|
|
6232
|
-
amount: numAmount.toFixed(2),
|
|
6233
|
-
currencyCode: currencyCode.toUpperCase()
|
|
6234
|
-
};
|
|
6235
|
-
}
|
|
6236
|
-
function transformImage3(url, alt = "") {
|
|
6237
|
-
return { url, altText: alt, width: 800, height: 800 };
|
|
6238
|
-
}
|
|
6239
|
-
function transformProduct3(raw, currencyCode = "USD") {
|
|
6240
|
-
const images = (raw.images ?? []).map(
|
|
6241
|
-
(img) => transformImage3(img.url_standard ?? img.url_thumbnail ?? "", img.description ?? "")
|
|
6242
|
-
);
|
|
6243
|
-
const variants = (raw.variants ?? []).map((v) => ({
|
|
6244
|
-
id: String(v.id),
|
|
6245
|
-
title: v.option_display_name ?? `Variant ${v.id}`,
|
|
6246
|
-
availableForSale: v.purchasing_disabled !== true,
|
|
6247
|
-
price: transformMoney3(v.price ?? raw.price, currencyCode),
|
|
6248
|
-
compareAtPrice: v.sale_price ? transformMoney3(v.sale_price, currencyCode) : void 0,
|
|
6249
|
-
selectedOptions: (v.option_values ?? []).map((ov) => ({
|
|
6250
|
-
name: ov.option_display_name ?? "",
|
|
6251
|
-
value: ov.label ?? ""
|
|
6252
|
-
}))
|
|
6253
|
-
}));
|
|
6254
|
-
const options = (raw.options ?? []).map((opt) => ({
|
|
6255
|
-
id: String(opt.id),
|
|
6256
|
-
name: opt.display_name ?? "",
|
|
6257
|
-
values: (opt.option_values ?? []).map((val) => val.label ?? "")
|
|
6258
|
-
}));
|
|
6259
|
-
const price = raw.price ?? raw.calculated_price?.as_entered ?? 0;
|
|
6260
|
-
return {
|
|
6261
|
-
id: String(raw.id),
|
|
6262
|
-
handle: raw.custom_url?.url?.replace(/^\//, "").replace(/\/$/, "") ?? String(raw.id),
|
|
6263
|
-
title: raw.name ?? "",
|
|
6264
|
-
description: raw.plain_text_description ?? raw.description ?? "",
|
|
6265
|
-
descriptionHtml: raw.description ?? "",
|
|
6266
|
-
availableForSale: raw.availability === "available" || raw.is_visible !== false,
|
|
6267
|
-
featuredImage: images[0] ?? { url: "", altText: "", width: 800, height: 800 },
|
|
6268
|
-
images,
|
|
6269
|
-
options,
|
|
6270
|
-
variants,
|
|
6271
|
-
priceRange: {
|
|
6272
|
-
minVariantPrice: transformMoney3(price, currencyCode),
|
|
6273
|
-
maxVariantPrice: transformMoney3(price, currencyCode)
|
|
6274
|
-
},
|
|
6275
|
-
seo: {
|
|
6276
|
-
title: raw.page_title ?? raw.name ?? "",
|
|
6277
|
-
description: raw.meta_description ?? ""
|
|
6278
|
-
},
|
|
6279
|
-
tags: [],
|
|
6280
|
-
createdAt: raw.date_created ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
6281
|
-
updatedAt: raw.date_modified ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
6282
|
-
};
|
|
6283
|
-
}
|
|
6284
|
-
function transformCollection3(raw) {
|
|
6285
|
-
return {
|
|
6286
|
-
id: String(raw.id ?? ""),
|
|
6287
|
-
handle: raw.custom_url?.url?.replace(/^\//, "").replace(/\/$/, "") ?? String(raw.id),
|
|
6288
|
-
title: raw.name ?? "",
|
|
6289
|
-
description: raw.description ?? "",
|
|
6290
|
-
seo: {
|
|
6291
|
-
title: raw.page_title ?? raw.name ?? "",
|
|
6292
|
-
description: raw.meta_description ?? ""
|
|
6293
|
-
},
|
|
6294
|
-
updatedAt: raw.date_modified ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
6295
|
-
path: `/collections/${raw.custom_url?.url?.replace(/^\//, "").replace(/\/$/, "") ?? raw.id}`
|
|
6296
|
-
};
|
|
6297
|
-
}
|
|
6298
|
-
function transformCart3(raw, currencyCode = "USD") {
|
|
6299
|
-
const lineItems = Object.values(raw.line_items ?? {}).flat();
|
|
6300
|
-
const lines = lineItems.map((item) => ({
|
|
6301
|
-
id: item.id,
|
|
6302
|
-
quantity: item.quantity ?? 1,
|
|
6303
|
-
cost: {
|
|
6304
|
-
totalAmount: transformMoney3(item.extended_sale_price ?? item.sale_price ?? 0, currencyCode)
|
|
6305
|
-
},
|
|
6306
|
-
merchandise: {
|
|
6307
|
-
id: String(item.variant_id ?? item.product_id),
|
|
6308
|
-
title: item.name ?? "",
|
|
6309
|
-
selectedOptions: (item.options ?? []).map((opt) => ({
|
|
6310
|
-
name: opt.name ?? "",
|
|
6311
|
-
value: opt.value ?? ""
|
|
6312
|
-
})),
|
|
6313
|
-
product: {
|
|
6314
|
-
id: String(item.product_id),
|
|
6315
|
-
handle: String(item.product_id),
|
|
6316
|
-
title: item.name ?? "",
|
|
6317
|
-
featuredImage: item.image_url ? transformImage3(item.image_url) : { url: "", altText: "", width: 800, height: 800 }
|
|
6318
|
-
}
|
|
6319
|
-
}
|
|
6320
|
-
}));
|
|
6321
|
-
return {
|
|
6322
|
-
id: raw.id,
|
|
6323
|
-
checkoutUrl: raw.redirect_urls?.checkout_url ?? "",
|
|
6324
|
-
totalQuantity: lines.reduce((sum, l) => sum + l.quantity, 0),
|
|
6325
|
-
cost: {
|
|
6326
|
-
subtotalAmount: transformMoney3(raw.base_amount ?? 0, currencyCode),
|
|
6327
|
-
totalAmount: transformMoney3(raw.cart_amount ?? 0, currencyCode)
|
|
6328
|
-
},
|
|
6329
|
-
lines
|
|
6330
|
-
};
|
|
6331
|
-
}
|
|
6332
|
-
|
|
6333
|
-
// src/providers/bigcommerce/index.ts
|
|
6334
|
-
var BigCommerceProvider = class {
|
|
6335
|
-
name = "bigcommerce";
|
|
6336
|
-
version = "1.0.0";
|
|
6337
|
-
capabilities = BIGCOMMERCE_CAPABILITIES;
|
|
6338
|
-
logger;
|
|
6339
|
-
storefrontApiUrl;
|
|
6340
|
-
storeHash;
|
|
6341
|
-
channelId;
|
|
6342
|
-
currencyCode;
|
|
6343
|
-
headers;
|
|
6344
|
-
constructor(config) {
|
|
6345
|
-
this.logger = createLogger("BigCommerceProvider", config.debug);
|
|
6346
|
-
this.storefrontApiUrl = config.storefrontApiUrl.replace(/\/$/, "");
|
|
6347
|
-
this.storeHash = config.storeHash;
|
|
6348
|
-
this.channelId = config.channelId;
|
|
6349
|
-
this.currencyCode = config.currencyCode ?? "USD";
|
|
6350
|
-
this.headers = {
|
|
6351
|
-
"Content-Type": "application/json",
|
|
6352
|
-
"Authorization": `Bearer ${config.storefrontApiToken}`
|
|
6353
|
-
};
|
|
6354
|
-
this.logger.info("Initializing BigCommerce Provider", { storeHash: this.storeHash, channelId: this.channelId });
|
|
6355
|
-
}
|
|
6356
|
-
async fetchApi(path, init) {
|
|
6357
|
-
const url = `${this.storefrontApiUrl}${path}`;
|
|
6358
|
-
const response = await fetch(url, {
|
|
6359
|
-
...init,
|
|
6360
|
-
headers: { ...this.headers, ...init?.headers }
|
|
6361
|
-
});
|
|
6362
|
-
if (!response.ok) {
|
|
6363
|
-
throw new ProviderAPIError(
|
|
6364
|
-
`BigCommerce API error: ${response.status} ${response.statusText}`,
|
|
6365
|
-
"BIGCOMMERCE_API_ERROR",
|
|
6366
|
-
response.status
|
|
6367
|
-
);
|
|
6368
|
-
}
|
|
6369
|
-
return response.json();
|
|
6370
|
-
}
|
|
6371
|
-
// ============================================
|
|
6372
|
-
// Product Operations
|
|
6373
|
-
// ============================================
|
|
6374
|
-
async getProduct(handle) {
|
|
6375
|
-
try {
|
|
6376
|
-
const data = await this.fetchApi(`/api/storefront/products?include=variants,options,images&keyword=${encodeURIComponent(handle)}`);
|
|
6377
|
-
const product = data.data?.[0];
|
|
6378
|
-
return product ? transformProduct3(product, this.currencyCode) : void 0;
|
|
6379
|
-
} catch {
|
|
6380
|
-
return void 0;
|
|
6381
|
-
}
|
|
6382
|
-
}
|
|
6383
|
-
async getProducts(options) {
|
|
6384
|
-
const limit = options?.first ?? 20;
|
|
6385
|
-
let path = `/api/storefront/products?include=variants,options,images&limit=${limit}`;
|
|
6386
|
-
if (options?.query) path += `&keyword=${encodeURIComponent(options.query)}`;
|
|
6387
|
-
const data = await this.fetchApi(path);
|
|
6388
|
-
return (data.data ?? []).map((p) => transformProduct3(p, this.currencyCode));
|
|
6389
|
-
}
|
|
6390
|
-
async getProductRecommendations(_productId) {
|
|
6391
|
-
return [];
|
|
6392
|
-
}
|
|
6393
|
-
// ============================================
|
|
6394
|
-
// Collection Operations (BigCommerce Categories)
|
|
6395
|
-
// ============================================
|
|
6396
|
-
async getCollection(handle) {
|
|
6397
|
-
try {
|
|
6398
|
-
const data = await this.fetchApi(`/api/storefront/categories?url=/${handle}/`);
|
|
6399
|
-
const cat = data.data?.[0];
|
|
6400
|
-
return cat ? transformCollection3(cat) : void 0;
|
|
6401
|
-
} catch {
|
|
6402
|
-
return void 0;
|
|
6403
|
-
}
|
|
6404
|
-
}
|
|
6405
|
-
async getCollections(options) {
|
|
6406
|
-
const limit = options?.limit ?? 50;
|
|
6407
|
-
const data = await this.fetchApi(`/api/storefront/categories?limit=${limit}`);
|
|
6408
|
-
return (data.data ?? []).map(transformCollection3);
|
|
6409
|
-
}
|
|
6410
|
-
async getCollectionProducts(handle, options) {
|
|
6411
|
-
const limit = options?.first ?? 20;
|
|
6412
|
-
const data = await this.fetchApi(
|
|
6413
|
-
`/api/storefront/products?include=variants,options,images&categories:in=${handle}&limit=${limit}`
|
|
6414
|
-
);
|
|
6415
|
-
return (data.data ?? []).map((p) => transformProduct3(p, this.currencyCode));
|
|
6416
|
-
}
|
|
6417
|
-
// ============================================
|
|
6418
|
-
// Category Operations
|
|
6419
|
-
// ============================================
|
|
6420
|
-
async getCategory(idOrHandle) {
|
|
6421
|
-
return this.getCollection(idOrHandle);
|
|
6422
|
-
}
|
|
6423
|
-
async getCategories(options) {
|
|
6424
|
-
return this.getCollections(options);
|
|
6425
|
-
}
|
|
6426
|
-
async getCategoryProducts(handle, options) {
|
|
6427
|
-
return this.getCollectionProducts(handle, options);
|
|
6428
|
-
}
|
|
6429
|
-
// ============================================
|
|
6430
|
-
// Cart Operations
|
|
6431
|
-
// ============================================
|
|
6432
|
-
async createCart() {
|
|
6433
|
-
const data = await this.fetchApi("/api/storefront/carts", {
|
|
6434
|
-
method: "POST",
|
|
6435
|
-
body: JSON.stringify({ line_items: [] })
|
|
6436
|
-
});
|
|
6437
|
-
return transformCart3(data.data ?? data, this.currencyCode);
|
|
6438
|
-
}
|
|
6439
|
-
async getCart(cartId) {
|
|
6440
|
-
try {
|
|
6441
|
-
const data = await this.fetchApi(`/api/storefront/carts/${cartId}?include=line_items.physical_items.options`);
|
|
6442
|
-
return transformCart3(data.data ?? data, this.currencyCode);
|
|
6443
|
-
} catch {
|
|
6444
|
-
return void 0;
|
|
6445
|
-
}
|
|
6446
|
-
}
|
|
6447
|
-
async addToCart(cartId, lines) {
|
|
6448
|
-
const lineItems = lines.map((l) => ({
|
|
6449
|
-
product_id: parseInt(l.merchandiseId, 10),
|
|
6450
|
-
quantity: l.quantity
|
|
6451
|
-
}));
|
|
6452
|
-
const data = await this.fetchApi(`/api/storefront/carts/${cartId}/items`, {
|
|
6453
|
-
method: "POST",
|
|
6454
|
-
body: JSON.stringify({ line_items: lineItems })
|
|
6455
|
-
});
|
|
6456
|
-
return transformCart3(data.data ?? data, this.currencyCode);
|
|
6457
|
-
}
|
|
6458
|
-
async removeFromCart(cartId, lineIds) {
|
|
6459
|
-
for (const lineId of lineIds) {
|
|
6460
|
-
await this.fetchApi(`/api/storefront/carts/${cartId}/items/${lineId}`, { method: "DELETE" });
|
|
6461
|
-
}
|
|
6462
|
-
const cart = await this.getCart(cartId);
|
|
6463
|
-
if (!cart) throw new ProviderAPIError("Cart not found after removal", "BIGCOMMERCE_CART_ERROR");
|
|
6464
|
-
return cart;
|
|
6465
|
-
}
|
|
6466
|
-
async updateCart(cartId, lines) {
|
|
6467
|
-
for (const line of lines) {
|
|
6468
|
-
await this.fetchApi(`/api/storefront/carts/${cartId}/items/${line.id}`, {
|
|
6469
|
-
method: "PUT",
|
|
6470
|
-
body: JSON.stringify({ line_item: { quantity: line.quantity } })
|
|
6471
|
-
});
|
|
6472
|
-
}
|
|
6473
|
-
const cart = await this.getCart(cartId);
|
|
6474
|
-
if (!cart) throw new ProviderAPIError("Cart not found after update", "BIGCOMMERCE_CART_ERROR");
|
|
6475
|
-
return cart;
|
|
6476
|
-
}
|
|
6477
|
-
// ============================================
|
|
6478
|
-
// Checkout Operations (Hosted)
|
|
6479
|
-
// ============================================
|
|
6480
|
-
async completeCheckout(_cartId) {
|
|
6481
|
-
throw new ProviderAPIError(
|
|
6482
|
-
"BigCommerce uses Embedded Checkout. Redirect the buyer to cart.checkoutUrl.",
|
|
6483
|
-
"BIGCOMMERCE_HOSTED_CHECKOUT"
|
|
6484
|
-
);
|
|
6485
|
-
}
|
|
6486
|
-
async getShippingMethods(_cartId) {
|
|
6487
|
-
return [];
|
|
6488
|
-
}
|
|
6489
|
-
async getRegionCountries() {
|
|
6490
|
-
return [];
|
|
6491
|
-
}
|
|
6492
|
-
async addShippingMethod(_cartId, _methodId) {
|
|
6493
|
-
throw new ProviderAPIError("BigCommerce handles shipping in Embedded Checkout.", "BIGCOMMERCE_HOSTED_CHECKOUT");
|
|
6494
|
-
}
|
|
6495
|
-
async initiatePaymentSession(_cartId, _providerId) {
|
|
6496
|
-
throw new ProviderAPIError("BigCommerce handles payment in Embedded Checkout.", "BIGCOMMERCE_HOSTED_CHECKOUT");
|
|
6497
|
-
}
|
|
6498
|
-
async applyDiscountCode(cartId, code) {
|
|
6499
|
-
const data = await this.fetchApi(`/api/storefront/carts/${cartId}/coupons`, {
|
|
6500
|
-
method: "POST",
|
|
6501
|
-
body: JSON.stringify({ coupon_code: code })
|
|
6502
|
-
});
|
|
6503
|
-
return transformCart3(data.data ?? data, this.currencyCode);
|
|
6504
|
-
}
|
|
6505
|
-
async removeDiscountCode(cartId, code) {
|
|
6506
|
-
await this.fetchApi(`/api/storefront/carts/${cartId}/coupons/${encodeURIComponent(code)}`, { method: "DELETE" });
|
|
6507
|
-
const cart = await this.getCart(cartId);
|
|
6508
|
-
if (!cart) throw new ProviderAPIError("Cart not found after coupon removal", "BIGCOMMERCE_CART_ERROR");
|
|
6509
|
-
return cart;
|
|
6510
|
-
}
|
|
6511
|
-
// ============================================
|
|
6512
|
-
// Customer, Order, Payment, Inventory, Admin
|
|
6513
|
-
// ============================================
|
|
6514
|
-
async getOrders(_options) {
|
|
6515
|
-
return [];
|
|
6516
|
-
}
|
|
6517
|
-
async getPaymentMethods(_options) {
|
|
6518
|
-
return [];
|
|
6519
|
-
}
|
|
6520
|
-
async getStockLocations() {
|
|
6521
|
-
return [];
|
|
6522
|
-
}
|
|
6523
|
-
async checkInventoryLevels() {
|
|
6524
|
-
return [];
|
|
6525
|
-
}
|
|
6526
|
-
async validateCartStock() {
|
|
6527
|
-
return { allAvailable: true, unavailableItems: [] };
|
|
6528
|
-
}
|
|
6529
|
-
async getFulfillmentOptions() {
|
|
6530
|
-
return [];
|
|
6531
|
-
}
|
|
6532
|
-
async cancelOrder(_orderId) {
|
|
6533
|
-
throw new ProviderAPIError("Order cancellation requires BigCommerce Management API.", "BIGCOMMERCE_ADMIN_REQUIRED");
|
|
6534
|
-
}
|
|
6535
|
-
async createFulfillment(_orderId) {
|
|
6536
|
-
throw new ProviderAPIError("Fulfillment requires BigCommerce Management API.", "BIGCOMMERCE_ADMIN_REQUIRED");
|
|
6537
|
-
}
|
|
6538
|
-
async createRefund(_orderId) {
|
|
6539
|
-
throw new ProviderAPIError("Refunds require BigCommerce Management API.", "BIGCOMMERCE_ADMIN_REQUIRED");
|
|
6540
|
-
}
|
|
6541
|
-
async getAdminOrders() {
|
|
6542
|
-
return { orders: [], count: 0 };
|
|
6543
|
-
}
|
|
6544
|
-
async getStorefrontContext() {
|
|
6545
|
-
return {
|
|
6546
|
-
id: this.channelId,
|
|
6547
|
-
name: `BigCommerce Store ${this.storeHash}`,
|
|
6548
|
-
platformType: "bigcommerce",
|
|
6549
|
-
requiresProductScoping: false,
|
|
6550
|
-
currencyCode: this.currencyCode
|
|
6551
|
-
};
|
|
6552
|
-
}
|
|
6553
|
-
isStorefrontRequired() {
|
|
6554
|
-
return false;
|
|
6555
|
-
}
|
|
6556
|
-
};
|
|
6557
|
-
|
|
6558
|
-
// src/providers/woocommerce/transforms.ts
|
|
6559
|
-
function transformMoney4(amount, currencyCode = "USD") {
|
|
6560
|
-
const numAmount = typeof amount === "string" ? parseFloat(amount) : amount ?? 0;
|
|
6561
|
-
return {
|
|
6562
|
-
amount: isNaN(numAmount) ? "0.00" : numAmount.toFixed(2),
|
|
6563
|
-
currencyCode: currencyCode.toUpperCase()
|
|
6564
|
-
};
|
|
6565
|
-
}
|
|
6566
|
-
function transformImage4(url, alt = "") {
|
|
6567
|
-
return { url, altText: alt, width: 800, height: 800 };
|
|
6568
|
-
}
|
|
6569
|
-
function transformProduct4(raw, currencyCode = "USD") {
|
|
6570
|
-
const images = (raw.images ?? []).map(
|
|
6571
|
-
(img) => transformImage4(img.src ?? "", img.alt ?? img.name ?? "")
|
|
6572
|
-
);
|
|
6573
|
-
const variants = (raw.variations ?? []).map((v) => ({
|
|
6574
|
-
id: String(v.id),
|
|
6575
|
-
title: v.name ?? "",
|
|
6576
|
-
availableForSale: v.in_stock !== false,
|
|
6577
|
-
price: transformMoney4(v.prices?.price ?? v.price, currencyCode),
|
|
6578
|
-
compareAtPrice: v.prices?.regular_price ? transformMoney4(v.prices.regular_price, currencyCode) : void 0,
|
|
6579
|
-
selectedOptions: (v.attributes ?? []).map((attr) => ({
|
|
6580
|
-
name: attr.name ?? "",
|
|
6581
|
-
value: attr.value ?? ""
|
|
6582
|
-
}))
|
|
6583
|
-
}));
|
|
6584
|
-
const options = (raw.attributes ?? []).filter((attr) => attr.variation === true || attr.has_options === true).map((attr) => ({
|
|
6585
|
-
id: String(attr.id),
|
|
6586
|
-
name: attr.name ?? "",
|
|
6587
|
-
values: (attr.terms ?? attr.options ?? []).map(
|
|
6588
|
-
(t) => typeof t === "string" ? t : t.name ?? t.value ?? ""
|
|
6589
|
-
)
|
|
6590
|
-
}));
|
|
6591
|
-
const priceMinor = raw.prices?.price ?? "0";
|
|
6592
|
-
const priceDecimalPlaces = raw.prices?.currency_minor_unit ?? 2;
|
|
6593
|
-
const priceMajor = parseInt(priceMinor, 10) / Math.pow(10, priceDecimalPlaces);
|
|
6594
|
-
return {
|
|
6595
|
-
id: String(raw.id),
|
|
6596
|
-
handle: raw.slug ?? String(raw.id),
|
|
6597
|
-
title: raw.name ?? "",
|
|
6598
|
-
description: raw.short_description ?? raw.description ?? "",
|
|
6599
|
-
descriptionHtml: raw.description ?? "",
|
|
6600
|
-
availableForSale: raw.is_purchasable !== false && raw.is_in_stock !== false,
|
|
6601
|
-
featuredImage: images[0] ?? { url: "", altText: "", width: 800, height: 800 },
|
|
6602
|
-
images,
|
|
6603
|
-
options,
|
|
6604
|
-
variants,
|
|
6605
|
-
priceRange: {
|
|
6606
|
-
minVariantPrice: transformMoney4(priceMajor, currencyCode),
|
|
6607
|
-
maxVariantPrice: transformMoney4(priceMajor, currencyCode)
|
|
6608
|
-
},
|
|
6609
|
-
seo: {
|
|
6610
|
-
title: raw.name ?? "",
|
|
6611
|
-
description: raw.short_description?.replace(/<[^>]*>/g, "") ?? ""
|
|
6612
|
-
},
|
|
6613
|
-
tags: [],
|
|
6614
|
-
createdAt: raw.date_created ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
6615
|
-
updatedAt: raw.date_modified ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
6616
|
-
};
|
|
6617
|
-
}
|
|
6618
|
-
function transformCollection4(raw) {
|
|
6619
|
-
return {
|
|
6620
|
-
id: String(raw.id ?? ""),
|
|
6621
|
-
handle: raw.slug ?? String(raw.id),
|
|
6622
|
-
title: raw.name ?? "",
|
|
6623
|
-
description: raw.description ?? "",
|
|
6624
|
-
seo: {
|
|
6625
|
-
title: raw.name ?? "",
|
|
6626
|
-
description: raw.description?.replace(/<[^>]*>/g, "") ?? ""
|
|
6627
|
-
},
|
|
6628
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6629
|
-
path: `/collections/${raw.slug ?? raw.id}`
|
|
6630
|
-
};
|
|
6631
|
-
}
|
|
6632
|
-
function transformCart4(raw, currencyCode = "USD") {
|
|
6633
|
-
const lines = (raw.items ?? []).map((item) => {
|
|
6634
|
-
const minorUnit2 = item.prices?.currency_minor_unit ?? 2;
|
|
6635
|
-
const divisor2 = Math.pow(10, minorUnit2);
|
|
6636
|
-
const lineTotal = parseInt(item.totals?.line_total ?? "0", 10) / divisor2;
|
|
6637
|
-
return {
|
|
6638
|
-
id: item.key ?? String(item.id),
|
|
6639
|
-
quantity: item.quantity ?? 1,
|
|
6640
|
-
cost: {
|
|
6641
|
-
totalAmount: transformMoney4(lineTotal, currencyCode)
|
|
6642
|
-
},
|
|
6643
|
-
merchandise: {
|
|
6644
|
-
id: String(item.id),
|
|
6645
|
-
title: item.name ?? "",
|
|
6646
|
-
selectedOptions: (item.variation ?? []).map((v) => ({
|
|
6647
|
-
name: v.attribute ?? "",
|
|
6648
|
-
value: v.value ?? ""
|
|
6649
|
-
})),
|
|
6650
|
-
product: {
|
|
6651
|
-
id: String(item.id),
|
|
6652
|
-
handle: item.slug ?? String(item.id),
|
|
6653
|
-
title: item.name ?? "",
|
|
6654
|
-
featuredImage: item.images?.[0] ? transformImage4(item.images[0].src, item.images[0].alt ?? "") : { url: "", altText: "", width: 800, height: 800 }
|
|
6655
|
-
}
|
|
6656
|
-
}
|
|
6657
|
-
};
|
|
6658
|
-
});
|
|
6659
|
-
const minorUnit = raw.totals?.currency_minor_unit ?? 2;
|
|
6660
|
-
const divisor = Math.pow(10, minorUnit);
|
|
6661
|
-
const subtotal = parseInt(raw.totals?.total_items ?? "0", 10) / divisor;
|
|
6662
|
-
const total = parseInt(raw.totals?.total_price ?? "0", 10) / divisor;
|
|
6663
|
-
return {
|
|
6664
|
-
id: raw.cart_key ?? void 0,
|
|
6665
|
-
checkoutUrl: "",
|
|
6666
|
-
// WooCommerce handles checkout via its own /checkout page
|
|
6667
|
-
totalQuantity: raw.items_count ?? lines.reduce((sum, l) => sum + l.quantity, 0),
|
|
6668
|
-
cost: {
|
|
6669
|
-
subtotalAmount: transformMoney4(subtotal, currencyCode),
|
|
6670
|
-
totalAmount: transformMoney4(total, currencyCode)
|
|
6671
|
-
},
|
|
6672
|
-
lines
|
|
6673
|
-
};
|
|
6674
|
-
}
|
|
6675
|
-
function transformWooAddress(raw) {
|
|
6676
|
-
return {
|
|
6677
|
-
id: void 0,
|
|
6678
|
-
firstName: raw.first_name ?? "",
|
|
6679
|
-
lastName: raw.last_name ?? "",
|
|
6680
|
-
address1: raw.address_1 ?? "",
|
|
6681
|
-
address2: raw.address_2 ?? "",
|
|
6682
|
-
city: raw.city ?? "",
|
|
6683
|
-
province: raw.state ?? "",
|
|
6684
|
-
countryCode: raw.country ?? "",
|
|
6685
|
-
postalCode: raw.postcode ?? "",
|
|
6686
|
-
phone: raw.phone ?? "",
|
|
6687
|
-
company: raw.company ?? ""
|
|
6688
|
-
};
|
|
6689
|
-
}
|
|
6690
|
-
function transformOrder4(raw, currencyCode = "USD") {
|
|
6691
|
-
const items = (raw.line_items ?? []).map((item) => ({
|
|
6692
|
-
id: String(item.id),
|
|
6693
|
-
title: item.name ?? "",
|
|
6694
|
-
quantity: item.quantity ?? 1,
|
|
6695
|
-
price: transformMoney4(item.price ?? 0, currencyCode),
|
|
6696
|
-
total: transformMoney4(item.total ?? 0, currencyCode),
|
|
6697
|
-
productId: String(item.product_id),
|
|
6698
|
-
variantId: item.variation_id ? String(item.variation_id) : void 0
|
|
6699
|
-
}));
|
|
6700
|
-
const statusMap = {
|
|
6701
|
-
pending: "pending",
|
|
6702
|
-
processing: "processing",
|
|
6703
|
-
"on-hold": "pending",
|
|
6704
|
-
completed: "completed",
|
|
6705
|
-
cancelled: "cancelled",
|
|
6706
|
-
refunded: "refunded",
|
|
6707
|
-
failed: "cancelled"
|
|
6708
|
-
};
|
|
6709
|
-
const paymentMap = {
|
|
6710
|
-
pending: "pending",
|
|
6711
|
-
processing: "authorized",
|
|
6712
|
-
completed: "paid",
|
|
6713
|
-
refunded: "refunded"
|
|
6714
|
-
};
|
|
6715
|
-
return {
|
|
6716
|
-
id: String(raw.id),
|
|
6717
|
-
orderNumber: String(raw.number ?? raw.id),
|
|
6718
|
-
email: raw.billing?.email ?? "",
|
|
6719
|
-
status: statusMap[raw.status] ?? "pending",
|
|
6720
|
-
paymentStatus: paymentMap[raw.status] ?? "pending",
|
|
6721
|
-
fulfillmentStatus: raw.status === "completed" ? "fulfilled" : "unfulfilled",
|
|
6722
|
-
items,
|
|
6723
|
-
total: transformMoney4(raw.total ?? 0, currencyCode),
|
|
6724
|
-
subtotal: transformMoney4(raw.subtotal ?? 0, currencyCode),
|
|
6725
|
-
tax: transformMoney4(raw.total_tax ?? 0, currencyCode),
|
|
6726
|
-
shipping: transformMoney4(raw.shipping_total ?? 0, currencyCode),
|
|
6727
|
-
shippingAddress: raw.shipping ? transformWooAddress(raw.shipping) : void 0,
|
|
6728
|
-
billingAddress: raw.billing ? transformWooAddress(raw.billing) : void 0,
|
|
6729
|
-
createdAt: raw.date_created ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
6730
|
-
updatedAt: raw.date_modified ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
6731
|
-
};
|
|
6732
|
-
}
|
|
6733
|
-
|
|
6734
|
-
// src/providers/woocommerce/index.ts
|
|
6735
|
-
var WooCommerceProvider = class {
|
|
6736
|
-
name = "woocommerce";
|
|
6737
|
-
version = "1.0.0";
|
|
6738
|
-
capabilities = WOOCOMMERCE_CAPABILITIES;
|
|
6739
|
-
logger;
|
|
6740
|
-
baseUrl;
|
|
6741
|
-
storeApiBase;
|
|
6742
|
-
restApiBase;
|
|
6743
|
-
currencyCode;
|
|
6744
|
-
authHeader;
|
|
6745
|
-
constructor(config) {
|
|
6746
|
-
this.logger = createLogger("WooCommerceProvider", config.debug);
|
|
6747
|
-
this.baseUrl = config.url.replace(/\/$/, "");
|
|
6748
|
-
this.storeApiBase = `${this.baseUrl}/wp-json/wc/store/v1`;
|
|
6749
|
-
this.restApiBase = `${this.baseUrl}/wp-json/wc/v3`;
|
|
6750
|
-
this.currencyCode = config.currencyCode ?? "USD";
|
|
6751
|
-
if (config.consumerKey && config.consumerSecret) {
|
|
6752
|
-
this.authHeader = "Basic " + btoa(`${config.consumerKey}:${config.consumerSecret}`);
|
|
6753
|
-
}
|
|
6754
|
-
this.logger.info("Initializing WooCommerce Provider", { baseUrl: this.baseUrl });
|
|
6755
|
-
}
|
|
6756
|
-
async storeApiFetch(path, init) {
|
|
6757
|
-
const response = await fetch(`${this.storeApiBase}${path}`, {
|
|
6758
|
-
...init,
|
|
6759
|
-
headers: { "Content-Type": "application/json", ...init?.headers }
|
|
6760
|
-
});
|
|
6761
|
-
if (!response.ok) {
|
|
6762
|
-
throw new ProviderAPIError(`WooCommerce Store API error: ${response.status}`, "WOOCOMMERCE_API_ERROR", response.status);
|
|
6763
|
-
}
|
|
6764
|
-
return response.json();
|
|
6765
|
-
}
|
|
6766
|
-
async restApiFetch(path, init) {
|
|
6767
|
-
if (!this.authHeader) {
|
|
6768
|
-
throw new ProviderAPIError("WooCommerce REST API requires consumer key/secret.", "WOOCOMMERCE_AUTH_REQUIRED");
|
|
6769
|
-
}
|
|
6770
|
-
const response = await fetch(`${this.restApiBase}${path}`, {
|
|
6771
|
-
...init,
|
|
6772
|
-
headers: { "Content-Type": "application/json", "Authorization": this.authHeader, ...init?.headers }
|
|
6773
|
-
});
|
|
6774
|
-
if (!response.ok) {
|
|
6775
|
-
throw new ProviderAPIError(`WooCommerce REST API error: ${response.status}`, "WOOCOMMERCE_API_ERROR", response.status);
|
|
6776
|
-
}
|
|
6777
|
-
return response.json();
|
|
6778
|
-
}
|
|
6779
|
-
// ============================================
|
|
6780
|
-
// Product Operations (Store API)
|
|
6781
|
-
// ============================================
|
|
6782
|
-
async getProduct(handle) {
|
|
6783
|
-
try {
|
|
6784
|
-
const products = await this.storeApiFetch(`/products?slug=${encodeURIComponent(handle)}`);
|
|
6785
|
-
return products[0] ? transformProduct4(products[0], this.currencyCode) : void 0;
|
|
6786
|
-
} catch {
|
|
6787
|
-
return void 0;
|
|
6788
|
-
}
|
|
6789
|
-
}
|
|
6790
|
-
async getProducts(options) {
|
|
6791
|
-
let path = `/products?per_page=${options?.first ?? 20}`;
|
|
6792
|
-
if (options?.query) path += `&search=${encodeURIComponent(options.query)}`;
|
|
6793
|
-
if (options?.sortKey) {
|
|
6794
|
-
const [orderby, order] = options.sortKey.includes("-desc") ? [options.sortKey.replace("-desc", ""), "desc"] : [options.sortKey.replace("-asc", ""), "asc"];
|
|
6795
|
-
path += `&orderby=${orderby}&order=${order}`;
|
|
6796
|
-
}
|
|
6797
|
-
const products = await this.storeApiFetch(path);
|
|
6798
|
-
return products.map((p) => transformProduct4(p, this.currencyCode));
|
|
6799
|
-
}
|
|
6800
|
-
async getProductRecommendations(_productId) {
|
|
6801
|
-
return [];
|
|
6802
|
-
}
|
|
6803
|
-
// ============================================
|
|
6804
|
-
// Collection/Category Operations (Store API)
|
|
6805
|
-
// ============================================
|
|
6806
|
-
async getCollection(handle) {
|
|
6807
|
-
try {
|
|
6808
|
-
const cats = await this.storeApiFetch(`/products/categories?slug=${encodeURIComponent(handle)}`);
|
|
6809
|
-
return cats[0] ? transformCollection4(cats[0]) : void 0;
|
|
6810
|
-
} catch {
|
|
6811
|
-
return void 0;
|
|
6812
|
-
}
|
|
6813
|
-
}
|
|
6814
|
-
async getCollections(options) {
|
|
6815
|
-
const cats = await this.storeApiFetch(`/products/categories?per_page=${options?.limit ?? 50}`);
|
|
6816
|
-
return cats.map(transformCollection4);
|
|
6817
|
-
}
|
|
6818
|
-
async getCollectionProducts(handle, options) {
|
|
6819
|
-
const products = await this.storeApiFetch(
|
|
6820
|
-
`/products?category=${encodeURIComponent(handle)}&per_page=${options?.first ?? 20}`
|
|
6821
|
-
);
|
|
6822
|
-
return products.map((p) => transformProduct4(p, this.currencyCode));
|
|
6823
|
-
}
|
|
6824
|
-
async getCategory(idOrHandle) {
|
|
6825
|
-
return this.getCollection(idOrHandle);
|
|
6826
|
-
}
|
|
6827
|
-
async getCategories(options) {
|
|
6828
|
-
return this.getCollections(options);
|
|
6829
|
-
}
|
|
6830
|
-
async getCategoryProducts(handle, options) {
|
|
6831
|
-
return this.getCollectionProducts(handle, options);
|
|
6832
|
-
}
|
|
6833
|
-
// ============================================
|
|
6834
|
-
// Cart Operations (Store API)
|
|
6835
|
-
// ============================================
|
|
6836
|
-
async createCart() {
|
|
6837
|
-
const cart = await this.storeApiFetch("/cart");
|
|
6838
|
-
return transformCart4(cart, this.currencyCode);
|
|
6839
|
-
}
|
|
6840
|
-
async getCart(_cartId) {
|
|
6841
|
-
try {
|
|
6842
|
-
const cart = await this.storeApiFetch("/cart");
|
|
6843
|
-
return transformCart4(cart, this.currencyCode);
|
|
6844
|
-
} catch {
|
|
6845
|
-
return void 0;
|
|
6846
|
-
}
|
|
6847
|
-
}
|
|
6848
|
-
async addToCart(_cartId, lines) {
|
|
6849
|
-
for (const line of lines) {
|
|
6850
|
-
await this.storeApiFetch("/cart/add-item", {
|
|
6851
|
-
method: "POST",
|
|
6852
|
-
body: JSON.stringify({ id: parseInt(line.merchandiseId, 10), quantity: line.quantity })
|
|
6853
|
-
});
|
|
6854
|
-
}
|
|
6855
|
-
const cart = await this.storeApiFetch("/cart");
|
|
6856
|
-
return transformCart4(cart, this.currencyCode);
|
|
6857
|
-
}
|
|
6858
|
-
async removeFromCart(_cartId, lineIds) {
|
|
6859
|
-
for (const key of lineIds) {
|
|
6860
|
-
await this.storeApiFetch("/cart/remove-item", {
|
|
6861
|
-
method: "POST",
|
|
6862
|
-
body: JSON.stringify({ key })
|
|
6863
|
-
});
|
|
6864
|
-
}
|
|
6865
|
-
const cart = await this.storeApiFetch("/cart");
|
|
6866
|
-
return transformCart4(cart, this.currencyCode);
|
|
6867
|
-
}
|
|
6868
|
-
async updateCart(_cartId, lines) {
|
|
6869
|
-
for (const line of lines) {
|
|
6870
|
-
await this.storeApiFetch("/cart/update-item", {
|
|
6871
|
-
method: "POST",
|
|
6872
|
-
body: JSON.stringify({ key: line.id, quantity: line.quantity })
|
|
6873
|
-
});
|
|
6874
|
-
}
|
|
6875
|
-
const cart = await this.storeApiFetch("/cart");
|
|
6876
|
-
return transformCart4(cart, this.currencyCode);
|
|
6877
|
-
}
|
|
6878
|
-
// ============================================
|
|
6879
|
-
// Checkout Operations (Store API)
|
|
6880
|
-
// ============================================
|
|
6881
|
-
async completeCheckout(_cartId) {
|
|
6882
|
-
const result = await this.storeApiFetch("/checkout", { method: "POST", body: JSON.stringify({}) });
|
|
6883
|
-
return transformOrder4(result, this.currencyCode);
|
|
6884
|
-
}
|
|
6885
|
-
async getShippingMethods(_cartId) {
|
|
6886
|
-
return [];
|
|
6887
|
-
}
|
|
6888
|
-
async getRegionCountries() {
|
|
6889
|
-
return [];
|
|
6890
|
-
}
|
|
6891
|
-
async addShippingMethod(_cartId, methodId) {
|
|
6892
|
-
await this.storeApiFetch("/cart/select-shipping-rate", {
|
|
6893
|
-
method: "POST",
|
|
6894
|
-
body: JSON.stringify({ rate_id: methodId })
|
|
6895
|
-
});
|
|
6896
|
-
const cart = await this.storeApiFetch("/cart");
|
|
6897
|
-
return transformCart4(cart, this.currencyCode);
|
|
6898
|
-
}
|
|
6899
|
-
async initiatePaymentSession(_cartId, _providerId) {
|
|
6900
|
-
}
|
|
6901
|
-
async applyDiscountCode(_cartId, code) {
|
|
6902
|
-
await this.storeApiFetch("/cart/apply-coupon", {
|
|
6903
|
-
method: "POST",
|
|
6904
|
-
body: JSON.stringify({ code })
|
|
6905
|
-
});
|
|
6906
|
-
const cart = await this.storeApiFetch("/cart");
|
|
6907
|
-
return transformCart4(cart, this.currencyCode);
|
|
6908
|
-
}
|
|
6909
|
-
async removeDiscountCode(_cartId, code) {
|
|
6910
|
-
await this.storeApiFetch("/cart/remove-coupon", {
|
|
6911
|
-
method: "POST",
|
|
6912
|
-
body: JSON.stringify({ code })
|
|
6913
|
-
});
|
|
6914
|
-
const cart = await this.storeApiFetch("/cart");
|
|
6915
|
-
return transformCart4(cart, this.currencyCode);
|
|
6916
|
-
}
|
|
6917
|
-
// ============================================
|
|
6918
|
-
// Customer, Order, Payment, Inventory, Admin
|
|
6919
|
-
// ============================================
|
|
6920
|
-
async getOrders(_options) {
|
|
6921
|
-
return [];
|
|
6922
|
-
}
|
|
6923
|
-
async getPaymentMethods(_options) {
|
|
6924
|
-
return [];
|
|
6925
|
-
}
|
|
6926
|
-
async getStockLocations() {
|
|
6927
|
-
return [];
|
|
6928
|
-
}
|
|
6929
|
-
async checkInventoryLevels() {
|
|
6930
|
-
return [];
|
|
6931
|
-
}
|
|
6932
|
-
async validateCartStock() {
|
|
6933
|
-
return { allAvailable: true, unavailableItems: [] };
|
|
6934
|
-
}
|
|
6935
|
-
async getFulfillmentOptions() {
|
|
6936
|
-
return [];
|
|
6937
|
-
}
|
|
6938
|
-
async cancelOrder(_orderId) {
|
|
6939
|
-
throw new ProviderAPIError("Order cancellation requires WooCommerce REST API v3.", "WOOCOMMERCE_AUTH_REQUIRED");
|
|
6940
|
-
}
|
|
6941
|
-
async createFulfillment(_orderId) {
|
|
6942
|
-
throw new ProviderAPIError("Fulfillment requires WooCommerce REST API v3.", "WOOCOMMERCE_AUTH_REQUIRED");
|
|
6943
|
-
}
|
|
6944
|
-
async createRefund(_orderId) {
|
|
6945
|
-
throw new ProviderAPIError("Refunds require WooCommerce REST API v3.", "WOOCOMMERCE_AUTH_REQUIRED");
|
|
6946
|
-
}
|
|
6947
|
-
async getAdminOrders() {
|
|
6948
|
-
return { orders: [], count: 0 };
|
|
6949
|
-
}
|
|
6950
|
-
async getStorefrontContext() {
|
|
6951
|
-
return {
|
|
6952
|
-
id: this.baseUrl,
|
|
6953
|
-
name: `WooCommerce ${this.baseUrl}`,
|
|
6954
|
-
platformType: "woocommerce",
|
|
6955
|
-
requiresProductScoping: false,
|
|
6956
|
-
currencyCode: this.currencyCode
|
|
6957
|
-
};
|
|
6958
|
-
}
|
|
6959
|
-
isStorefrontRequired() {
|
|
6960
|
-
return false;
|
|
6961
|
-
}
|
|
6962
|
-
};
|
|
6963
|
-
|
|
6964
|
-
// src/providers/spree/transforms.ts
|
|
6965
|
-
function toMoney(amount, currencyCode) {
|
|
6966
|
-
const num = typeof amount === "string" ? parseFloat(amount) || 0 : amount;
|
|
6967
|
-
return { amount: num.toFixed(2), currencyCode };
|
|
6968
|
-
}
|
|
6969
|
-
function transformProduct5(data, included = [], currencyCode = "USD") {
|
|
6970
|
-
const attrs = data.attributes ?? data;
|
|
6971
|
-
const id = String(data.id ?? "");
|
|
6972
|
-
const images = [];
|
|
6973
|
-
const imageRels = data.relationships?.images?.data ?? [];
|
|
6974
|
-
for (const imgRef of imageRels) {
|
|
6975
|
-
const imgIncluded = included.find((i) => i.type === "image" && String(i.id) === String(imgRef.id));
|
|
6976
|
-
if (imgIncluded) {
|
|
6977
|
-
images.push({
|
|
6978
|
-
url: imgIncluded.attributes?.original_url ?? imgIncluded.attributes?.styles?.[0]?.url ?? "",
|
|
6979
|
-
altText: imgIncluded.attributes?.alt ?? attrs.name ?? "",
|
|
6980
|
-
width: void 0,
|
|
6981
|
-
height: void 0
|
|
6982
|
-
});
|
|
6983
|
-
}
|
|
6984
|
-
}
|
|
6985
|
-
if (images.length === 0 && Array.isArray(attrs.images)) {
|
|
6986
|
-
for (const img of attrs.images) {
|
|
6987
|
-
images.push({
|
|
6988
|
-
url: img.original_url ?? img.url ?? "",
|
|
6989
|
-
altText: img.alt ?? attrs.name ?? "",
|
|
6990
|
-
width: void 0,
|
|
6991
|
-
height: void 0
|
|
6992
|
-
});
|
|
6993
|
-
}
|
|
6994
|
-
}
|
|
6995
|
-
const variants = [];
|
|
6996
|
-
const variantRels = data.relationships?.variants?.data ?? [];
|
|
6997
|
-
for (const varRef of variantRels) {
|
|
6998
|
-
const varIncluded = included.find((i) => i.type === "variant" && String(i.id) === String(varRef.id));
|
|
6999
|
-
if (varIncluded) {
|
|
7000
|
-
const vAttrs = varIncluded.attributes ?? {};
|
|
7001
|
-
variants.push({
|
|
7002
|
-
id: String(varIncluded.id),
|
|
7003
|
-
title: vAttrs.name ?? vAttrs.sku ?? `Variant ${varIncluded.id}`,
|
|
7004
|
-
availableForSale: vAttrs.in_stock !== false && vAttrs.purchasable !== false,
|
|
7005
|
-
price: toMoney(vAttrs.price ?? vAttrs.display_price ?? "0", currencyCode),
|
|
7006
|
-
compareAtPrice: vAttrs.compare_at_price ? toMoney(vAttrs.compare_at_price, currencyCode) : void 0,
|
|
7007
|
-
selectedOptions: (vAttrs.option_values ?? []).map((ov) => ({
|
|
7008
|
-
name: ov.option_type_name ?? "Option",
|
|
7009
|
-
value: ov.name ?? ov.presentation ?? ""
|
|
7010
|
-
}))
|
|
7011
|
-
});
|
|
7012
|
-
}
|
|
7013
|
-
}
|
|
7014
|
-
if (variants.length === 0) {
|
|
7015
|
-
variants.push({
|
|
7016
|
-
id: attrs.default_variant_id ? String(attrs.default_variant_id) : id,
|
|
7017
|
-
title: "Default",
|
|
7018
|
-
availableForSale: attrs.in_stock !== false && attrs.purchasable !== false,
|
|
7019
|
-
price: toMoney(attrs.price ?? attrs.display_price ?? "0", currencyCode),
|
|
7020
|
-
compareAtPrice: attrs.compare_at_price ? toMoney(attrs.compare_at_price, currencyCode) : void 0,
|
|
7021
|
-
selectedOptions: []
|
|
7022
|
-
});
|
|
7023
|
-
}
|
|
7024
|
-
const options = (attrs.option_types ?? []).map((ot) => ({
|
|
7025
|
-
id: String(ot.id ?? ot.name ?? ""),
|
|
7026
|
-
name: ot.presentation ?? ot.name ?? "",
|
|
7027
|
-
values: (ot.option_values ?? []).map((ov) => ov.presentation ?? ov.name ?? "")
|
|
7028
|
-
}));
|
|
7029
|
-
return {
|
|
7030
|
-
id,
|
|
7031
|
-
handle: attrs.slug ?? id,
|
|
7032
|
-
title: attrs.name ?? "",
|
|
7033
|
-
description: attrs.description ?? "",
|
|
7034
|
-
descriptionHtml: attrs.description ?? "",
|
|
7035
|
-
availableForSale: attrs.in_stock !== false && attrs.purchasable !== false,
|
|
7036
|
-
priceRange: {
|
|
7037
|
-
minVariantPrice: variants.reduce(
|
|
7038
|
-
(min, v) => v.price.amount < min.amount ? v.price : min,
|
|
7039
|
-
variants[0]?.price ?? toMoney("0", currencyCode)
|
|
7040
|
-
),
|
|
7041
|
-
maxVariantPrice: variants.reduce(
|
|
7042
|
-
(max, v) => v.price.amount > max.amount ? v.price : max,
|
|
7043
|
-
variants[0]?.price ?? toMoney("0", currencyCode)
|
|
7044
|
-
)
|
|
7045
|
-
},
|
|
7046
|
-
variants,
|
|
7047
|
-
options,
|
|
7048
|
-
images,
|
|
7049
|
-
featuredImage: images[0] ?? { url: "", altText: attrs.name ?? "" },
|
|
7050
|
-
tags: [],
|
|
7051
|
-
seo: {
|
|
7052
|
-
title: attrs.meta_title ?? attrs.name ?? "",
|
|
7053
|
-
description: attrs.meta_description ?? ""
|
|
7054
|
-
},
|
|
7055
|
-
updatedAt: attrs.updated_at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
7056
|
-
createdAt: attrs.created_at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
7057
|
-
};
|
|
7058
|
-
}
|
|
7059
|
-
function transformCollection5(data) {
|
|
7060
|
-
const attrs = data.attributes ?? data;
|
|
7061
|
-
return {
|
|
7062
|
-
id: String(data.id ?? ""),
|
|
7063
|
-
handle: attrs.permalink ?? attrs.slug ?? String(data.id ?? ""),
|
|
7064
|
-
title: attrs.name ?? "",
|
|
7065
|
-
description: attrs.description ?? "",
|
|
7066
|
-
seo: {
|
|
7067
|
-
title: attrs.meta_title ?? attrs.name ?? "",
|
|
7068
|
-
description: attrs.meta_description ?? ""
|
|
7069
|
-
},
|
|
7070
|
-
updatedAt: attrs.updated_at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
7071
|
-
path: `/collections/${attrs.permalink ?? attrs.slug ?? data.id}`
|
|
7072
|
-
};
|
|
7073
|
-
}
|
|
7074
|
-
function transformCart5(data, currencyCode = "USD") {
|
|
7075
|
-
const attrs = data.attributes ?? data;
|
|
7076
|
-
const items = (attrs.line_items ?? data.included?.filter((i) => i.type === "line_item") ?? []).map(
|
|
7077
|
-
(item) => {
|
|
7078
|
-
const itemAttrs = item.attributes ?? item;
|
|
7079
|
-
return {
|
|
7080
|
-
id: String(item.id ?? itemAttrs.id ?? ""),
|
|
7081
|
-
quantity: itemAttrs.quantity ?? 1,
|
|
7082
|
-
cost: {
|
|
7083
|
-
totalAmount: toMoney(itemAttrs.total ?? itemAttrs.display_total ?? "0", currencyCode)
|
|
7084
|
-
},
|
|
7085
|
-
merchandise: {
|
|
7086
|
-
id: String(itemAttrs.variant_id ?? item.id ?? ""),
|
|
7087
|
-
title: itemAttrs.name ?? "",
|
|
7088
|
-
selectedOptions: [],
|
|
7089
|
-
product: {
|
|
7090
|
-
id: String(itemAttrs.product_id ?? ""),
|
|
7091
|
-
handle: itemAttrs.slug ?? "",
|
|
7092
|
-
title: itemAttrs.name ?? "",
|
|
7093
|
-
featuredImage: { url: itemAttrs.image_url ?? "", altText: itemAttrs.name ?? "" }
|
|
7094
|
-
}
|
|
7095
|
-
}
|
|
7096
|
-
};
|
|
7097
|
-
}
|
|
7098
|
-
);
|
|
7099
|
-
return {
|
|
7100
|
-
id: String(data.id ?? attrs.token ?? ""),
|
|
7101
|
-
checkoutUrl: "",
|
|
7102
|
-
totalQuantity: items.reduce((sum, i) => sum + i.quantity, 0),
|
|
7103
|
-
cost: {
|
|
7104
|
-
subtotalAmount: toMoney(attrs.item_total ?? attrs.display_item_total ?? "0", currencyCode),
|
|
7105
|
-
totalAmount: toMoney(attrs.total ?? attrs.display_total ?? "0", currencyCode),
|
|
7106
|
-
totalTaxAmount: toMoney(attrs.tax_total ?? attrs.display_tax_total ?? "0", currencyCode)
|
|
7107
|
-
},
|
|
7108
|
-
lines: items
|
|
7109
|
-
};
|
|
7110
|
-
}
|
|
7111
|
-
function transformAddress2(addr) {
|
|
7112
|
-
return {
|
|
7113
|
-
id: String(addr.id ?? ""),
|
|
7114
|
-
firstName: addr.first_name ?? addr.firstname ?? "",
|
|
7115
|
-
lastName: addr.last_name ?? addr.lastname ?? "",
|
|
7116
|
-
company: addr.company ?? "",
|
|
7117
|
-
address1: addr.address1 ?? "",
|
|
7118
|
-
address2: addr.address2 ?? "",
|
|
7119
|
-
city: addr.city ?? "",
|
|
7120
|
-
province: addr.state_name ?? addr.state ?? "",
|
|
7121
|
-
postalCode: addr.zipcode ?? addr.zip ?? "",
|
|
7122
|
-
countryCode: addr.country_iso ?? addr.country_code ?? "",
|
|
7123
|
-
phone: addr.phone ?? ""
|
|
7124
|
-
};
|
|
7125
|
-
}
|
|
7126
|
-
function transformOrder5(data, currencyCode = "USD") {
|
|
7127
|
-
const attrs = data.attributes ?? data;
|
|
7128
|
-
const items = (attrs.line_items ?? []).map((item) => ({
|
|
7129
|
-
id: String(item.id ?? ""),
|
|
7130
|
-
title: item.name ?? "",
|
|
7131
|
-
quantity: item.quantity ?? 1,
|
|
7132
|
-
price: toMoney(item.price ?? "0", currencyCode),
|
|
7133
|
-
total: toMoney(item.total ?? "0", currencyCode),
|
|
7134
|
-
productId: String(item.product_id ?? ""),
|
|
7135
|
-
variantId: String(item.variant_id ?? "")
|
|
7136
|
-
}));
|
|
7137
|
-
return {
|
|
7138
|
-
id: String(data.id ?? attrs.number ?? ""),
|
|
7139
|
-
orderNumber: attrs.number ?? String(data.id ?? ""),
|
|
7140
|
-
email: attrs.email ?? "",
|
|
7141
|
-
status: attrs.state === "complete" ? "completed" : attrs.state === "canceled" ? "cancelled" : "pending",
|
|
7142
|
-
paymentStatus: attrs.payment_state === "paid" ? "paid" : attrs.payment_state === "balance_due" ? "pending" : "pending",
|
|
7143
|
-
fulfillmentStatus: attrs.shipment_state === "shipped" ? "fulfilled" : attrs.shipment_state === "partial" ? "partially_fulfilled" : "unfulfilled",
|
|
7144
|
-
items,
|
|
7145
|
-
total: toMoney(attrs.total ?? "0", currencyCode),
|
|
7146
|
-
subtotal: toMoney(attrs.item_total ?? "0", currencyCode),
|
|
7147
|
-
tax: toMoney(attrs.tax_total ?? "0", currencyCode),
|
|
7148
|
-
shipping: toMoney(attrs.ship_total ?? "0", currencyCode),
|
|
7149
|
-
shippingAddress: attrs.ship_address ? transformAddress2(attrs.ship_address) : void 0,
|
|
7150
|
-
billingAddress: attrs.bill_address ? transformAddress2(attrs.bill_address) : void 0,
|
|
7151
|
-
createdAt: attrs.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
7152
|
-
updatedAt: attrs.updated_at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
7153
|
-
};
|
|
7154
|
-
}
|
|
7155
|
-
|
|
7156
|
-
// src/providers/spree/index.ts
|
|
7157
|
-
var SpreeProvider = class {
|
|
7158
|
-
name = "spree";
|
|
7159
|
-
version = "1.0.0";
|
|
7160
|
-
capabilities = SPREE_CAPABILITIES;
|
|
7161
|
-
logger;
|
|
7162
|
-
baseUrl;
|
|
7163
|
-
apiBase;
|
|
7164
|
-
currencyCode;
|
|
7165
|
-
bearerToken;
|
|
7166
|
-
orderToken;
|
|
7167
|
-
// guest cart token
|
|
7168
|
-
constructor(config) {
|
|
7169
|
-
this.logger = createLogger("SpreeProvider", config.debug);
|
|
7170
|
-
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
7171
|
-
this.apiBase = `${this.baseUrl}/api/v2/storefront`;
|
|
7172
|
-
this.currencyCode = config.currencyCode ?? "USD";
|
|
7173
|
-
this.bearerToken = config.bearerToken;
|
|
7174
|
-
this.logger.info("Initializing Spree Provider", { baseUrl: this.baseUrl });
|
|
7175
|
-
}
|
|
7176
|
-
async apiFetch(path, init) {
|
|
7177
|
-
const headers = {
|
|
7178
|
-
"Content-Type": "application/vnd.api+json",
|
|
7179
|
-
"Accept": "application/vnd.api+json",
|
|
7180
|
-
...init?.headers ?? {}
|
|
7181
|
-
};
|
|
7182
|
-
if (this.bearerToken) {
|
|
7183
|
-
headers["Authorization"] = `Bearer ${this.bearerToken}`;
|
|
7184
|
-
}
|
|
7185
|
-
if (this.orderToken) {
|
|
7186
|
-
headers["X-Spree-Order-Token"] = this.orderToken;
|
|
7187
|
-
}
|
|
7188
|
-
const response = await fetch(`${this.apiBase}${path}`, {
|
|
7189
|
-
...init,
|
|
7190
|
-
headers
|
|
7191
|
-
});
|
|
7192
|
-
if (!response.ok) {
|
|
7193
|
-
throw new ProviderAPIError(
|
|
7194
|
-
`Spree API error: ${response.status}`,
|
|
7195
|
-
"SPREE_API_ERROR",
|
|
7196
|
-
response.status
|
|
7197
|
-
);
|
|
7198
|
-
}
|
|
7199
|
-
return response.json();
|
|
7200
|
-
}
|
|
7201
|
-
// ============================================
|
|
7202
|
-
// Product Operations
|
|
7203
|
-
// ============================================
|
|
7204
|
-
async getProduct(handle) {
|
|
7205
|
-
try {
|
|
7206
|
-
const { data, included } = await this.apiFetch(
|
|
7207
|
-
`/products/${encodeURIComponent(handle)}?include=images,variants,option_types`
|
|
7208
|
-
);
|
|
7209
|
-
return transformProduct5(data, included ?? [], this.currencyCode);
|
|
7210
|
-
} catch {
|
|
7211
|
-
return void 0;
|
|
7212
|
-
}
|
|
7213
|
-
}
|
|
7214
|
-
async getProducts(options) {
|
|
7215
|
-
let path = `/products?include=images,variants&per_page=${options?.first ?? 20}`;
|
|
7216
|
-
if (options?.query) path += `&filter[name]=${encodeURIComponent(options.query)}`;
|
|
7217
|
-
if (options?.sortKey) {
|
|
7218
|
-
const sortMap = {
|
|
7219
|
-
"price-asc": "price",
|
|
7220
|
-
"price-desc": "-price",
|
|
7221
|
-
"title-asc": "name",
|
|
7222
|
-
"title-desc": "-name",
|
|
7223
|
-
"created-desc": "-created_at"
|
|
7224
|
-
};
|
|
7225
|
-
if (sortMap[options.sortKey]) path += `&sort=${sortMap[options.sortKey]}`;
|
|
7226
|
-
}
|
|
7227
|
-
const { data, included } = await this.apiFetch(path);
|
|
7228
|
-
return (Array.isArray(data) ? data : []).map((p) => transformProduct5(p, included ?? [], this.currencyCode));
|
|
7229
|
-
}
|
|
7230
|
-
async getProductRecommendations(_productId) {
|
|
7231
|
-
return [];
|
|
7232
|
-
}
|
|
7233
|
-
// ============================================
|
|
7234
|
-
// Collection/Taxon Operations
|
|
7235
|
-
// ============================================
|
|
7236
|
-
async getCollection(handle) {
|
|
7237
|
-
try {
|
|
7238
|
-
const { data } = await this.apiFetch(`/taxons/${encodeURIComponent(handle)}`);
|
|
7239
|
-
return transformCollection5(data);
|
|
7240
|
-
} catch {
|
|
7241
|
-
return void 0;
|
|
7242
|
-
}
|
|
7243
|
-
}
|
|
7244
|
-
async getCollections(options) {
|
|
7245
|
-
const { data } = await this.apiFetch(`/taxons?per_page=${options?.limit ?? 50}`);
|
|
7246
|
-
return (Array.isArray(data) ? data : []).map(transformCollection5);
|
|
7247
|
-
}
|
|
7248
|
-
async getCollectionProducts(handle, options) {
|
|
7249
|
-
const { data, included } = await this.apiFetch(
|
|
7250
|
-
`/products?include=images,variants&filter[taxons]=${encodeURIComponent(handle)}&per_page=${options?.first ?? 20}`
|
|
7251
|
-
);
|
|
7252
|
-
return (Array.isArray(data) ? data : []).map((p) => transformProduct5(p, included ?? [], this.currencyCode));
|
|
7253
|
-
}
|
|
7254
|
-
async getCategory(handle) {
|
|
7255
|
-
return this.getCollection(handle);
|
|
7256
|
-
}
|
|
7257
|
-
async getCategories(options) {
|
|
7258
|
-
return this.getCollections(options);
|
|
7259
|
-
}
|
|
7260
|
-
async getCategoryProducts(handle, options) {
|
|
7261
|
-
return this.getCollectionProducts(handle, options);
|
|
7262
|
-
}
|
|
7263
|
-
// ============================================
|
|
7264
|
-
// Cart Operations
|
|
7265
|
-
// ============================================
|
|
7266
|
-
async createCart() {
|
|
7267
|
-
const { data } = await this.apiFetch("/cart", {
|
|
7268
|
-
method: "POST",
|
|
7269
|
-
body: JSON.stringify({})
|
|
7270
|
-
});
|
|
7271
|
-
if (data.attributes?.token) {
|
|
7272
|
-
this.orderToken = data.attributes.token;
|
|
7273
|
-
}
|
|
7274
|
-
return transformCart5(data, this.currencyCode);
|
|
7275
|
-
}
|
|
7276
|
-
async getCart(_cartId) {
|
|
7277
|
-
try {
|
|
7278
|
-
const { data } = await this.apiFetch("/cart?include=line_items");
|
|
7279
|
-
return transformCart5(data, this.currencyCode);
|
|
7280
|
-
} catch {
|
|
7281
|
-
return void 0;
|
|
7282
|
-
}
|
|
7283
|
-
}
|
|
7284
|
-
async addToCart(_cartId, lines) {
|
|
7285
|
-
for (const line of lines) {
|
|
7286
|
-
await this.apiFetch("/cart/add_item", {
|
|
7287
|
-
method: "POST",
|
|
7288
|
-
body: JSON.stringify({ variant_id: line.merchandiseId, quantity: line.quantity })
|
|
7289
|
-
});
|
|
7290
|
-
}
|
|
7291
|
-
const { data } = await this.apiFetch("/cart?include=line_items");
|
|
7292
|
-
return transformCart5(data, this.currencyCode);
|
|
7293
|
-
}
|
|
7294
|
-
async removeFromCart(_cartId, lineIds) {
|
|
7295
|
-
for (const id of lineIds) {
|
|
7296
|
-
await this.apiFetch(`/cart/remove_line_item/${id}`, { method: "DELETE" });
|
|
7297
|
-
}
|
|
7298
|
-
const { data } = await this.apiFetch("/cart?include=line_items");
|
|
7299
|
-
return transformCart5(data, this.currencyCode);
|
|
7300
|
-
}
|
|
7301
|
-
async updateCart(_cartId, lines) {
|
|
7302
|
-
for (const line of lines) {
|
|
7303
|
-
await this.apiFetch(`/cart/set_quantity`, {
|
|
7304
|
-
method: "PATCH",
|
|
7305
|
-
body: JSON.stringify({ line_item_id: line.id, quantity: line.quantity })
|
|
7306
|
-
});
|
|
7307
|
-
}
|
|
7308
|
-
const { data } = await this.apiFetch("/cart?include=line_items");
|
|
7309
|
-
return transformCart5(data, this.currencyCode);
|
|
7310
|
-
}
|
|
7311
|
-
// ============================================
|
|
7312
|
-
// Checkout Operations
|
|
7313
|
-
// ============================================
|
|
7314
|
-
async completeCheckout(_cartId) {
|
|
7315
|
-
const { data } = await this.apiFetch("/checkout/complete", { method: "PATCH" });
|
|
7316
|
-
return transformOrder5(data, this.currencyCode);
|
|
7317
|
-
}
|
|
7318
|
-
async getShippingMethods(_cartId) {
|
|
7319
|
-
try {
|
|
7320
|
-
const { data } = await this.apiFetch("/checkout/shipping_rates");
|
|
7321
|
-
return (Array.isArray(data) ? data : []).map((rate) => ({
|
|
7322
|
-
id: String(rate.id),
|
|
7323
|
-
name: rate.attributes?.name ?? rate.name ?? "",
|
|
7324
|
-
price: { amount: String(parseFloat(rate.attributes?.final_price ?? rate.attributes?.cost ?? "0").toFixed(2)), currencyCode: this.currencyCode }
|
|
7325
|
-
}));
|
|
7326
|
-
} catch {
|
|
7327
|
-
return [];
|
|
7328
|
-
}
|
|
7329
|
-
}
|
|
7330
|
-
async getRegionCountries() {
|
|
7331
|
-
return [];
|
|
7332
|
-
}
|
|
7333
|
-
async addShippingMethod(_cartId, methodId) {
|
|
7334
|
-
await this.apiFetch("/checkout", {
|
|
7335
|
-
method: "PATCH",
|
|
7336
|
-
body: JSON.stringify({ order: { shipments_attributes: [{ selected_shipping_rate_id: methodId }] } })
|
|
7337
|
-
});
|
|
7338
|
-
const { data } = await this.apiFetch("/cart?include=line_items");
|
|
7339
|
-
return transformCart5(data, this.currencyCode);
|
|
7340
|
-
}
|
|
7341
|
-
async initiatePaymentSession(_cartId, _providerId) {
|
|
7342
|
-
}
|
|
7343
|
-
async applyDiscountCode(_cartId, code) {
|
|
7344
|
-
await this.apiFetch("/cart/apply_coupon_code", {
|
|
7345
|
-
method: "PATCH",
|
|
7346
|
-
body: JSON.stringify({ coupon_code: code })
|
|
7347
|
-
});
|
|
7348
|
-
const { data } = await this.apiFetch("/cart?include=line_items");
|
|
7349
|
-
return transformCart5(data, this.currencyCode);
|
|
7350
|
-
}
|
|
7351
|
-
async removeDiscountCode(_cartId, code) {
|
|
7352
|
-
await this.apiFetch(`/cart/remove_coupon_code/${encodeURIComponent(code)}`, { method: "DELETE" });
|
|
7353
|
-
const { data } = await this.apiFetch("/cart?include=line_items");
|
|
7354
|
-
return transformCart5(data, this.currencyCode);
|
|
7355
|
-
}
|
|
7356
|
-
// ============================================
|
|
7357
|
-
// Customer, Order, Payment, Inventory, Admin
|
|
7358
|
-
// ============================================
|
|
7359
|
-
async getOrders(_options) {
|
|
7360
|
-
try {
|
|
7361
|
-
const { data } = await this.apiFetch("/account/orders");
|
|
7362
|
-
return (Array.isArray(data) ? data : []).map((o) => transformOrder5(o, this.currencyCode));
|
|
7363
|
-
} catch {
|
|
7364
|
-
return [];
|
|
7365
|
-
}
|
|
7366
|
-
}
|
|
7367
|
-
async getPaymentMethods(_options) {
|
|
7368
|
-
try {
|
|
7369
|
-
const { data } = await this.apiFetch("/checkout/payment_methods");
|
|
7370
|
-
return (Array.isArray(data) ? data : []).map((pm) => ({
|
|
7371
|
-
id: String(pm.id),
|
|
7372
|
-
name: pm.attributes?.name ?? "",
|
|
7373
|
-
type: "other",
|
|
7374
|
-
isEnabled: true,
|
|
7375
|
-
requiresSetup: false
|
|
7376
|
-
}));
|
|
7377
|
-
} catch {
|
|
7378
|
-
return [];
|
|
7379
|
-
}
|
|
7380
|
-
}
|
|
7381
|
-
async getStockLocations() {
|
|
7382
|
-
return [];
|
|
7383
|
-
}
|
|
7384
|
-
async checkInventoryLevels() {
|
|
7385
|
-
return [];
|
|
7386
|
-
}
|
|
7387
|
-
async validateCartStock() {
|
|
7388
|
-
return { allAvailable: true, unavailableItems: [] };
|
|
7389
|
-
}
|
|
7390
|
-
async getFulfillmentOptions() {
|
|
7391
|
-
return [];
|
|
7392
|
-
}
|
|
7393
|
-
async cancelOrder(_orderId) {
|
|
7394
|
-
throw new ProviderAPIError("Order cancellation requires Spree Admin API.", "SPREE_ADMIN_REQUIRED");
|
|
7395
|
-
}
|
|
7396
|
-
async createFulfillment(_orderId) {
|
|
7397
|
-
throw new ProviderAPIError("Fulfillment requires Spree Admin API.", "SPREE_ADMIN_REQUIRED");
|
|
7398
|
-
}
|
|
7399
|
-
async createRefund(_orderId) {
|
|
7400
|
-
throw new ProviderAPIError("Refunds require Spree Admin API.", "SPREE_ADMIN_REQUIRED");
|
|
7401
|
-
}
|
|
7402
|
-
async getAdminOrders() {
|
|
7403
|
-
return { orders: [], count: 0 };
|
|
7404
|
-
}
|
|
7405
|
-
async getStorefrontContext() {
|
|
7406
|
-
return {
|
|
7407
|
-
id: this.baseUrl,
|
|
7408
|
-
name: `Spree ${this.baseUrl}`,
|
|
7409
|
-
platformType: "spree",
|
|
7410
|
-
requiresProductScoping: true,
|
|
7411
|
-
currencyCode: this.currencyCode
|
|
7412
|
-
};
|
|
7413
|
-
}
|
|
7414
|
-
isStorefrontRequired() {
|
|
7415
|
-
return true;
|
|
7416
|
-
}
|
|
7417
|
-
};
|
|
7418
|
-
|
|
7419
|
-
// src/providers/magento/transforms.ts
|
|
7420
|
-
function toMoney2(value, currencyCode) {
|
|
7421
|
-
return { amount: (value ?? 0).toFixed(2), currencyCode };
|
|
7422
|
-
}
|
|
7423
|
-
function extractPrice(priceRange, currencyCode) {
|
|
7424
|
-
const minPrice = priceRange?.minimum_price?.regular_price?.value ?? priceRange?.minimum_price?.final_price?.value ?? 0;
|
|
7425
|
-
const maxPrice = priceRange?.maximum_price?.regular_price?.value ?? priceRange?.maximum_price?.final_price?.value ?? minPrice;
|
|
7426
|
-
const currency = priceRange?.minimum_price?.regular_price?.currency ?? currencyCode;
|
|
7427
|
-
return {
|
|
7428
|
-
min: toMoney2(minPrice, currency),
|
|
7429
|
-
max: toMoney2(maxPrice, currency)
|
|
7430
|
-
};
|
|
7431
|
-
}
|
|
7432
|
-
function transformProduct6(data, currencyCode = "USD") {
|
|
7433
|
-
const id = String(data.id ?? data.uid ?? "");
|
|
7434
|
-
const { min, max } = extractPrice(data.price_range, currencyCode);
|
|
7435
|
-
const images = (data.media_gallery ?? data.media_gallery_entries ?? []).map((img) => ({
|
|
7436
|
-
url: img.url ?? (img.file ? `${img.file}` : ""),
|
|
7437
|
-
altText: img.label ?? data.name ?? "",
|
|
7438
|
-
width: void 0,
|
|
7439
|
-
height: void 0
|
|
7440
|
-
}));
|
|
7441
|
-
if (images.length === 0 && data.small_image?.url) {
|
|
7442
|
-
images.push({ url: data.small_image.url, altText: data.small_image.label ?? data.name ?? "" });
|
|
7443
|
-
}
|
|
7444
|
-
if (images.length === 0 && data.image?.url) {
|
|
7445
|
-
images.push({ url: data.image.url, altText: data.image.label ?? data.name ?? "" });
|
|
7446
|
-
}
|
|
7447
|
-
const variants = [];
|
|
7448
|
-
if (data.variants && Array.isArray(data.variants)) {
|
|
7449
|
-
for (const v of data.variants) {
|
|
7450
|
-
const product = v.product ?? {};
|
|
7451
|
-
const vPrice = product.price_range?.minimum_price?.final_price ?? {};
|
|
7452
|
-
variants.push({
|
|
7453
|
-
id: String(product.id ?? product.uid ?? product.sku ?? ""),
|
|
7454
|
-
title: product.name ?? product.sku ?? "",
|
|
7455
|
-
availableForSale: product.stock_status === "IN_STOCK",
|
|
7456
|
-
price: toMoney2(vPrice.value ?? min.amount, vPrice.currency ?? currencyCode),
|
|
7457
|
-
compareAtPrice: void 0,
|
|
7458
|
-
selectedOptions: (v.attributes ?? []).map((attr) => ({
|
|
7459
|
-
name: attr.label ?? "",
|
|
7460
|
-
value: attr.value_index != null ? String(attr.value_index) : ""
|
|
7461
|
-
}))
|
|
7462
|
-
});
|
|
7463
|
-
}
|
|
7464
|
-
}
|
|
7465
|
-
if (variants.length === 0) {
|
|
7466
|
-
variants.push({
|
|
7467
|
-
id: data.sku ?? id,
|
|
7468
|
-
title: data.name ?? "",
|
|
7469
|
-
availableForSale: data.stock_status === "IN_STOCK",
|
|
7470
|
-
price: min,
|
|
7471
|
-
compareAtPrice: void 0,
|
|
7472
|
-
selectedOptions: []
|
|
7473
|
-
});
|
|
7474
|
-
}
|
|
7475
|
-
const options = (data.configurable_options ?? []).map((opt) => ({
|
|
7476
|
-
id: String(opt.attribute_id ?? opt.attribute_code ?? ""),
|
|
7477
|
-
name: opt.label ?? opt.attribute_code ?? "",
|
|
7478
|
-
values: (opt.values ?? []).map((v) => v.label ?? v.store_label ?? String(v.value_index ?? ""))
|
|
7479
|
-
}));
|
|
7480
|
-
return {
|
|
7481
|
-
id,
|
|
7482
|
-
handle: data.url_key ?? data.sku ?? id,
|
|
7483
|
-
title: data.name ?? "",
|
|
7484
|
-
description: data.short_description?.html ?? data.description?.html ?? "",
|
|
7485
|
-
descriptionHtml: data.description?.html ?? "",
|
|
7486
|
-
availableForSale: data.stock_status === "IN_STOCK",
|
|
7487
|
-
priceRange: { minVariantPrice: min, maxVariantPrice: max },
|
|
7488
|
-
variants,
|
|
7489
|
-
options,
|
|
7490
|
-
images,
|
|
7491
|
-
featuredImage: images[0] ?? { url: "", altText: data.name ?? "" },
|
|
7492
|
-
tags: [],
|
|
7493
|
-
seo: {
|
|
7494
|
-
title: data.meta_title ?? data.name ?? "",
|
|
7495
|
-
description: data.meta_description ?? ""
|
|
7496
|
-
},
|
|
7497
|
-
updatedAt: data.updated_at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
7498
|
-
createdAt: data.created_at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
7499
|
-
};
|
|
7500
|
-
}
|
|
7501
|
-
function transformCollection6(data) {
|
|
7502
|
-
return {
|
|
7503
|
-
id: String(data.id ?? data.uid ?? ""),
|
|
7504
|
-
handle: data.url_key ?? data.url_path ?? String(data.id ?? data.uid ?? ""),
|
|
7505
|
-
title: data.name ?? "",
|
|
7506
|
-
description: data.description ?? "",
|
|
7507
|
-
seo: {
|
|
7508
|
-
title: data.meta_title ?? data.name ?? "",
|
|
7509
|
-
description: data.meta_description ?? ""
|
|
7510
|
-
},
|
|
7511
|
-
updatedAt: data.updated_at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
7512
|
-
path: `/collections/${data.url_key ?? data.url_path ?? data.id}`
|
|
7513
|
-
};
|
|
7514
|
-
}
|
|
7515
|
-
function transformCart6(data, currencyCode = "USD") {
|
|
7516
|
-
const items = (data.items ?? []).map((item) => {
|
|
7517
|
-
const product = item.product ?? {};
|
|
7518
|
-
const priceVal = item.prices?.price?.value ?? product.price_range?.minimum_price?.final_price?.value ?? 0;
|
|
7519
|
-
const totalVal2 = item.prices?.row_total?.value ?? priceVal * (item.quantity ?? 1);
|
|
7520
|
-
return {
|
|
7521
|
-
id: String(item.id ?? item.uid ?? ""),
|
|
7522
|
-
quantity: item.quantity ?? 1,
|
|
7523
|
-
cost: {
|
|
7524
|
-
totalAmount: toMoney2(totalVal2, item.prices?.price?.currency ?? currencyCode)
|
|
7525
|
-
},
|
|
7526
|
-
merchandise: {
|
|
7527
|
-
id: String(product.sku ?? item.id ?? ""),
|
|
7528
|
-
title: product.name ?? item.product?.name ?? "",
|
|
7529
|
-
selectedOptions: (item.configurable_options ?? []).map((opt) => ({
|
|
7530
|
-
name: opt.option_label ?? "",
|
|
7531
|
-
value: opt.value_label ?? ""
|
|
7532
|
-
})),
|
|
7533
|
-
product: {
|
|
7534
|
-
id: String(product.id ?? product.uid ?? ""),
|
|
7535
|
-
handle: product.url_key ?? "",
|
|
7536
|
-
title: product.name ?? "",
|
|
7537
|
-
featuredImage: {
|
|
7538
|
-
url: product.small_image?.url ?? product.thumbnail?.url ?? "",
|
|
7539
|
-
altText: product.name ?? ""
|
|
7540
|
-
}
|
|
7541
|
-
}
|
|
7542
|
-
}
|
|
7543
|
-
};
|
|
7544
|
-
});
|
|
7545
|
-
const totalVal = data.prices?.grand_total?.value ?? 0;
|
|
7546
|
-
const subtotalVal = data.prices?.subtotal_excluding_tax?.value ?? data.prices?.subtotal_including_tax?.value ?? totalVal;
|
|
7547
|
-
const taxVal = (data.prices?.applied_taxes ?? []).reduce((sum, t) => sum + (t.amount?.value ?? 0), 0);
|
|
7548
|
-
return {
|
|
7549
|
-
id: String(data.id ?? ""),
|
|
7550
|
-
checkoutUrl: "",
|
|
7551
|
-
totalQuantity: items.reduce((sum, i) => sum + i.quantity, 0),
|
|
7552
|
-
cost: {
|
|
7553
|
-
subtotalAmount: toMoney2(subtotalVal, data.prices?.grand_total?.currency ?? currencyCode),
|
|
7554
|
-
totalAmount: toMoney2(totalVal, data.prices?.grand_total?.currency ?? currencyCode),
|
|
7555
|
-
totalTaxAmount: toMoney2(taxVal, currencyCode)
|
|
7556
|
-
},
|
|
7557
|
-
lines: items
|
|
7558
|
-
};
|
|
7559
|
-
}
|
|
7560
|
-
function transformAddress3(addr) {
|
|
7561
|
-
return {
|
|
7562
|
-
id: String(addr.id ?? ""),
|
|
7563
|
-
firstName: addr.firstname ?? "",
|
|
7564
|
-
lastName: addr.lastname ?? "",
|
|
7565
|
-
company: addr.company ?? "",
|
|
7566
|
-
address1: Array.isArray(addr.street) ? addr.street[0] ?? "" : addr.street ?? "",
|
|
7567
|
-
address2: Array.isArray(addr.street) ? addr.street[1] ?? "" : "",
|
|
7568
|
-
city: addr.city ?? "",
|
|
7569
|
-
province: addr.region?.region ?? "",
|
|
7570
|
-
postalCode: addr.postcode ?? "",
|
|
7571
|
-
countryCode: addr.country_code ?? addr.country_id ?? "",
|
|
7572
|
-
phone: addr.telephone ?? ""
|
|
7573
|
-
};
|
|
7574
|
-
}
|
|
7575
|
-
function transformOrder6(data, currencyCode = "USD") {
|
|
7576
|
-
const items = (data.items ?? []).map((item) => ({
|
|
7577
|
-
id: String(item.id ?? ""),
|
|
7578
|
-
title: item.product_name ?? "",
|
|
7579
|
-
quantity: item.quantity_ordered ?? item.quantity ?? 1,
|
|
7580
|
-
price: toMoney2(item.product_sale_price?.value ?? item.price ?? 0, currencyCode),
|
|
7581
|
-
total: toMoney2((item.product_sale_price?.value ?? item.price ?? 0) * (item.quantity_ordered ?? item.quantity ?? 1), currencyCode),
|
|
7582
|
-
sku: item.product_sku ?? item.sku,
|
|
7583
|
-
productId: String(item.product_id ?? "")
|
|
7584
|
-
}));
|
|
7585
|
-
return {
|
|
7586
|
-
id: String(data.id ?? data.order_number ?? ""),
|
|
7587
|
-
orderNumber: data.order_number ?? data.number ?? String(data.id ?? ""),
|
|
7588
|
-
email: data.email ?? "",
|
|
7589
|
-
status: data.status === "complete" ? "completed" : data.status === "canceled" ? "cancelled" : data.status === "closed" ? "refunded" : "pending",
|
|
7590
|
-
paymentStatus: data.payment_status === "paid" ? "paid" : "pending",
|
|
7591
|
-
fulfillmentStatus: data.shipment_status === "shipped" ? "fulfilled" : "unfulfilled",
|
|
7592
|
-
items,
|
|
7593
|
-
total: toMoney2(data.total?.grand_total?.value ?? data.grand_total ?? 0, currencyCode),
|
|
7594
|
-
subtotal: toMoney2(data.total?.subtotal?.value ?? data.subtotal ?? 0, currencyCode),
|
|
7595
|
-
tax: toMoney2(data.total?.total_tax?.value ?? data.tax_amount ?? 0, currencyCode),
|
|
7596
|
-
shipping: toMoney2(data.total?.total_shipping?.value ?? data.shipping_amount ?? 0, currencyCode),
|
|
7597
|
-
shippingAddress: data.shipping_address ? transformAddress3(data.shipping_address) : void 0,
|
|
7598
|
-
billingAddress: data.billing_address ? transformAddress3(data.billing_address) : void 0,
|
|
7599
|
-
createdAt: data.order_date ?? data.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
7600
|
-
updatedAt: data.updated_at ?? data.order_date ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
7601
|
-
};
|
|
7602
|
-
}
|
|
7603
|
-
|
|
7604
|
-
// src/providers/magento/queries.ts
|
|
7605
|
-
var PRODUCT_FIELDS = `
|
|
7606
|
-
id
|
|
7607
|
-
uid
|
|
7608
|
-
sku
|
|
7609
|
-
name
|
|
7610
|
-
url_key
|
|
7611
|
-
stock_status
|
|
7612
|
-
short_description { html }
|
|
7613
|
-
description { html }
|
|
7614
|
-
meta_title
|
|
7615
|
-
meta_description
|
|
7616
|
-
created_at
|
|
7617
|
-
updated_at
|
|
7618
|
-
price_range {
|
|
7619
|
-
minimum_price {
|
|
7620
|
-
regular_price { value currency }
|
|
7621
|
-
final_price { value currency }
|
|
7622
|
-
}
|
|
7623
|
-
maximum_price {
|
|
7624
|
-
regular_price { value currency }
|
|
7625
|
-
final_price { value currency }
|
|
7626
|
-
}
|
|
7627
|
-
}
|
|
7628
|
-
small_image { url label }
|
|
7629
|
-
image { url label }
|
|
7630
|
-
media_gallery {
|
|
7631
|
-
url
|
|
7632
|
-
label
|
|
7633
|
-
}
|
|
7634
|
-
... on ConfigurableProduct {
|
|
7635
|
-
configurable_options {
|
|
7636
|
-
attribute_code
|
|
7637
|
-
attribute_id
|
|
7638
|
-
label
|
|
7639
|
-
values {
|
|
7640
|
-
label
|
|
7641
|
-
value_index
|
|
7642
|
-
}
|
|
7643
|
-
}
|
|
7644
|
-
variants {
|
|
7645
|
-
product {
|
|
7646
|
-
id uid sku name
|
|
7647
|
-
stock_status
|
|
7648
|
-
price_range {
|
|
7649
|
-
minimum_price {
|
|
7650
|
-
final_price { value currency }
|
|
7651
|
-
}
|
|
7652
|
-
}
|
|
7653
|
-
small_image { url label }
|
|
7654
|
-
}
|
|
7655
|
-
attributes {
|
|
7656
|
-
label
|
|
7657
|
-
value_index
|
|
7658
|
-
}
|
|
7659
|
-
}
|
|
7660
|
-
}
|
|
7661
|
-
`;
|
|
7662
|
-
var GET_PRODUCTS2 = `
|
|
7663
|
-
query GetProducts($search: String, $pageSize: Int, $currentPage: Int, $sort: ProductAttributeSortInput) {
|
|
7664
|
-
products(search: $search, pageSize: $pageSize, currentPage: $currentPage, sort: $sort) {
|
|
7665
|
-
items { ${PRODUCT_FIELDS} }
|
|
7666
|
-
total_count
|
|
7667
|
-
}
|
|
7668
|
-
}
|
|
7669
|
-
`;
|
|
7670
|
-
var GET_PRODUCT_BY_URL_KEY = `
|
|
7671
|
-
query GetProductByUrlKey($urlKey: String!) {
|
|
7672
|
-
products(filter: { url_key: { eq: $urlKey } }, pageSize: 1) {
|
|
7673
|
-
items { ${PRODUCT_FIELDS} }
|
|
7674
|
-
}
|
|
7675
|
-
}
|
|
7676
|
-
`;
|
|
7677
|
-
var GET_CATEGORIES = `
|
|
7678
|
-
query GetCategories($pageSize: Int) {
|
|
7679
|
-
categories(pageSize: $pageSize) {
|
|
7680
|
-
items {
|
|
7681
|
-
id uid name url_key url_path description meta_title meta_description
|
|
7682
|
-
}
|
|
7683
|
-
}
|
|
7684
|
-
}
|
|
7685
|
-
`;
|
|
7686
|
-
var GET_CATEGORY_BY_URL_KEY = `
|
|
7687
|
-
query GetCategoryByUrlKey($urlKey: String!) {
|
|
7688
|
-
categories(filters: { url_key: { eq: $urlKey } }, pageSize: 1) {
|
|
7689
|
-
items {
|
|
7690
|
-
id uid name url_key url_path description meta_title meta_description
|
|
7691
|
-
}
|
|
7692
|
-
}
|
|
7693
|
-
}
|
|
7694
|
-
`;
|
|
7695
|
-
var GET_CATEGORY_PRODUCTS = `
|
|
7696
|
-
query GetCategoryProducts($urlKey: String!, $pageSize: Int) {
|
|
7697
|
-
products(filter: { category_url_key: { eq: $urlKey } }, pageSize: $pageSize) {
|
|
7698
|
-
items { ${PRODUCT_FIELDS} }
|
|
7699
|
-
total_count
|
|
7700
|
-
}
|
|
7701
|
-
}
|
|
7702
|
-
`;
|
|
7703
|
-
var GET_CART2 = `
|
|
7704
|
-
query GetCart($cartId: String!) {
|
|
7705
|
-
cart(cart_id: $cartId) {
|
|
7706
|
-
id
|
|
7707
|
-
items {
|
|
7708
|
-
id uid quantity
|
|
7709
|
-
product {
|
|
7710
|
-
id uid sku name url_key
|
|
7711
|
-
small_image { url }
|
|
7712
|
-
thumbnail { url }
|
|
7713
|
-
price_range {
|
|
7714
|
-
minimum_price {
|
|
7715
|
-
final_price { value currency }
|
|
7716
|
-
}
|
|
7717
|
-
}
|
|
7718
|
-
}
|
|
7719
|
-
prices {
|
|
7720
|
-
price { value currency }
|
|
7721
|
-
row_total { value currency }
|
|
7722
|
-
}
|
|
7723
|
-
... on ConfigurableCartItem {
|
|
7724
|
-
configurable_options {
|
|
7725
|
-
option_label
|
|
7726
|
-
value_label
|
|
7727
|
-
}
|
|
7728
|
-
}
|
|
7729
|
-
}
|
|
7730
|
-
prices {
|
|
7731
|
-
grand_total { value currency }
|
|
7732
|
-
subtotal_excluding_tax { value currency }
|
|
7733
|
-
subtotal_including_tax { value currency }
|
|
7734
|
-
applied_taxes {
|
|
7735
|
-
amount { value currency }
|
|
7736
|
-
label
|
|
7737
|
-
}
|
|
7738
|
-
}
|
|
7739
|
-
}
|
|
7740
|
-
}
|
|
7741
|
-
`;
|
|
7742
|
-
var CREATE_EMPTY_CART = `
|
|
7743
|
-
mutation CreateEmptyCart {
|
|
7744
|
-
createEmptyCart
|
|
7745
|
-
}
|
|
7746
|
-
`;
|
|
7747
|
-
var ADD_TO_CART = `
|
|
7748
|
-
mutation AddToCart($cartId: String!, $cartItems: [CartItemInput!]!) {
|
|
7749
|
-
addProductsToCart(cartId: $cartId, cartItems: $cartItems) {
|
|
7750
|
-
cart {
|
|
7751
|
-
id
|
|
7752
|
-
items { id uid quantity product { sku name } prices { price { value currency } row_total { value currency } } }
|
|
7753
|
-
prices { grand_total { value currency } subtotal_excluding_tax { value currency } }
|
|
7754
|
-
}
|
|
7755
|
-
user_errors { code message }
|
|
7756
|
-
}
|
|
7757
|
-
}
|
|
7758
|
-
`;
|
|
7759
|
-
var REMOVE_FROM_CART = `
|
|
7760
|
-
mutation RemoveFromCart($cartId: String!, $cartItemUid: ID!) {
|
|
7761
|
-
removeItemFromCart(input: { cart_id: $cartId, cart_item_uid: $cartItemUid }) {
|
|
7762
|
-
cart {
|
|
7763
|
-
id
|
|
7764
|
-
items { id uid quantity product { sku name } prices { price { value currency } row_total { value currency } } }
|
|
7765
|
-
prices { grand_total { value currency } subtotal_excluding_tax { value currency } }
|
|
7766
|
-
}
|
|
7767
|
-
}
|
|
7768
|
-
}
|
|
7769
|
-
`;
|
|
7770
|
-
var UPDATE_CART_ITEMS = `
|
|
7771
|
-
mutation UpdateCartItems($cartId: String!, $cartItems: [CartItemUpdateInput!]!) {
|
|
7772
|
-
updateCartItems(input: { cart_id: $cartId, cart_items: $cartItems }) {
|
|
7773
|
-
cart {
|
|
7774
|
-
id
|
|
7775
|
-
items { id uid quantity product { sku name } prices { price { value currency } row_total { value currency } } }
|
|
7776
|
-
prices { grand_total { value currency } subtotal_excluding_tax { value currency } }
|
|
7777
|
-
}
|
|
7778
|
-
}
|
|
7779
|
-
}
|
|
7780
|
-
`;
|
|
7781
|
-
var APPLY_COUPON = `
|
|
7782
|
-
mutation ApplyCoupon($cartId: String!, $couponCode: String!) {
|
|
7783
|
-
applyCouponToCart(input: { cart_id: $cartId, coupon_code: $couponCode }) {
|
|
7784
|
-
cart {
|
|
7785
|
-
id
|
|
7786
|
-
prices { grand_total { value currency } subtotal_excluding_tax { value currency } }
|
|
7787
|
-
}
|
|
7788
|
-
}
|
|
7789
|
-
}
|
|
7790
|
-
`;
|
|
7791
|
-
var REMOVE_COUPON = `
|
|
7792
|
-
mutation RemoveCoupon($cartId: String!) {
|
|
7793
|
-
removeCouponFromCart(input: { cart_id: $cartId }) {
|
|
7794
|
-
cart {
|
|
7795
|
-
id
|
|
7796
|
-
prices { grand_total { value currency } subtotal_excluding_tax { value currency } }
|
|
7797
|
-
}
|
|
7798
|
-
}
|
|
7799
|
-
}
|
|
7800
|
-
`;
|
|
7801
|
-
var GET_CUSTOMER_ORDERS = `
|
|
7802
|
-
query GetCustomerOrders($pageSize: Int, $currentPage: Int) {
|
|
7803
|
-
customer {
|
|
7804
|
-
orders(pageSize: $pageSize, currentPage: $currentPage) {
|
|
7805
|
-
items {
|
|
7806
|
-
id order_number order_date status
|
|
7807
|
-
total {
|
|
7808
|
-
grand_total { value currency }
|
|
7809
|
-
subtotal { value currency }
|
|
7810
|
-
total_tax { value currency }
|
|
7811
|
-
total_shipping { value currency }
|
|
7812
|
-
}
|
|
7813
|
-
items {
|
|
7814
|
-
id product_name product_sku
|
|
7815
|
-
quantity_ordered
|
|
7816
|
-
product_sale_price { value currency }
|
|
7817
|
-
}
|
|
7818
|
-
shipping_address {
|
|
7819
|
-
firstname lastname street city
|
|
7820
|
-
region postcode country_code telephone
|
|
7821
|
-
}
|
|
7822
|
-
billing_address {
|
|
7823
|
-
firstname lastname street city
|
|
7824
|
-
region postcode country_code telephone
|
|
7825
|
-
}
|
|
7826
|
-
}
|
|
7827
|
-
total_count
|
|
7828
|
-
}
|
|
7829
|
-
}
|
|
7830
|
-
}
|
|
7831
|
-
`;
|
|
7832
|
-
|
|
7833
|
-
// src/providers/magento/index.ts
|
|
7834
|
-
var MagentoProvider = class {
|
|
7835
|
-
name = "magento";
|
|
7836
|
-
version = "1.0.0";
|
|
7837
|
-
capabilities = MAGENTO_CAPABILITIES;
|
|
7838
|
-
logger;
|
|
7839
|
-
baseUrl;
|
|
7840
|
-
storeCode;
|
|
7841
|
-
currencyCode;
|
|
7842
|
-
graphqlEndpoint;
|
|
7843
|
-
customerToken;
|
|
7844
|
-
accessToken;
|
|
7845
|
-
constructor(config) {
|
|
7846
|
-
this.logger = createLogger("MagentoProvider", config.debug);
|
|
7847
|
-
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
7848
|
-
this.storeCode = config.storeCode ?? "default";
|
|
7849
|
-
this.currencyCode = config.currencyCode ?? "USD";
|
|
7850
|
-
this.graphqlEndpoint = `${this.baseUrl}/graphql`;
|
|
7851
|
-
this.customerToken = config.customerToken;
|
|
7852
|
-
this.accessToken = config.accessToken;
|
|
7853
|
-
this.logger.info("Initializing Magento Provider", {
|
|
7854
|
-
baseUrl: this.baseUrl,
|
|
7855
|
-
storeCode: this.storeCode
|
|
7856
|
-
});
|
|
7857
|
-
}
|
|
7858
|
-
async graphql(query, variables) {
|
|
7859
|
-
const headers = {
|
|
7860
|
-
"Content-Type": "application/json",
|
|
7861
|
-
"Store": this.storeCode
|
|
7862
|
-
};
|
|
7863
|
-
if (this.customerToken) {
|
|
7864
|
-
headers["Authorization"] = `Bearer ${this.customerToken}`;
|
|
7865
|
-
}
|
|
7866
|
-
const response = await fetch(this.graphqlEndpoint, {
|
|
7867
|
-
method: "POST",
|
|
7868
|
-
headers,
|
|
7869
|
-
body: JSON.stringify({ query, variables })
|
|
7870
|
-
});
|
|
7871
|
-
if (!response.ok) {
|
|
7872
|
-
throw new ProviderAPIError(
|
|
7873
|
-
`Magento GraphQL error: ${response.status}`,
|
|
7874
|
-
"MAGENTO_API_ERROR",
|
|
7875
|
-
response.status
|
|
7876
|
-
);
|
|
7877
|
-
}
|
|
7878
|
-
const json = await response.json();
|
|
7879
|
-
if (json.errors?.length) {
|
|
7880
|
-
const message = json.errors.map((e) => e.message).join("; ");
|
|
7881
|
-
throw new ProviderAPIError(`Magento GraphQL error: ${message}`, "MAGENTO_GRAPHQL_ERROR");
|
|
7882
|
-
}
|
|
7883
|
-
return json.data;
|
|
7884
|
-
}
|
|
7885
|
-
// ============================================
|
|
7886
|
-
// Product Operations (GraphQL)
|
|
7887
|
-
// ============================================
|
|
7888
|
-
async getProduct(handle) {
|
|
7889
|
-
try {
|
|
7890
|
-
const data = await this.graphql(GET_PRODUCT_BY_URL_KEY, { urlKey: handle });
|
|
7891
|
-
return data.products.items[0] ? transformProduct6(data.products.items[0], this.currencyCode) : void 0;
|
|
7892
|
-
} catch {
|
|
7893
|
-
return void 0;
|
|
7894
|
-
}
|
|
7895
|
-
}
|
|
7896
|
-
async getProducts(options) {
|
|
7897
|
-
const variables = {
|
|
7898
|
-
pageSize: options?.first ?? 20,
|
|
7899
|
-
currentPage: 1
|
|
7900
|
-
};
|
|
7901
|
-
if (options?.query) variables.search = options.query;
|
|
7902
|
-
if (options?.sortKey) {
|
|
7903
|
-
const sortMap = {
|
|
7904
|
-
"price-asc": { price: "ASC" },
|
|
7905
|
-
"price-desc": { price: "DESC" },
|
|
7906
|
-
"title-asc": { name: "ASC" },
|
|
7907
|
-
"title-desc": { name: "DESC" },
|
|
7908
|
-
"created-desc": { created_at: "DESC" },
|
|
7909
|
-
"relevance": { relevance: "DESC" }
|
|
7910
|
-
};
|
|
7911
|
-
variables.sort = sortMap[options.sortKey] ?? { relevance: "DESC" };
|
|
7912
|
-
}
|
|
7913
|
-
const data = await this.graphql(GET_PRODUCTS2, variables);
|
|
7914
|
-
return (data.products?.items ?? []).map((p) => transformProduct6(p, this.currencyCode));
|
|
7915
|
-
}
|
|
7916
|
-
async getProductRecommendations(_productId) {
|
|
7917
|
-
return [];
|
|
7918
|
-
}
|
|
7919
|
-
// ============================================
|
|
7920
|
-
// Collection/Category Operations (GraphQL)
|
|
7921
|
-
// ============================================
|
|
7922
|
-
async getCollection(handle) {
|
|
7923
|
-
try {
|
|
7924
|
-
const data = await this.graphql(GET_CATEGORY_BY_URL_KEY, { urlKey: handle });
|
|
7925
|
-
return data.categories.items[0] ? transformCollection6(data.categories.items[0]) : void 0;
|
|
7926
|
-
} catch {
|
|
7927
|
-
return void 0;
|
|
5478
|
+
throw new ProviderAPIError(
|
|
5479
|
+
`Failed to initialize storefront context: ${errorMessage}`,
|
|
5480
|
+
"medusa",
|
|
5481
|
+
error?.status ?? 500
|
|
5482
|
+
);
|
|
7928
5483
|
}
|
|
7929
5484
|
}
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
return
|
|
7935
|
-
}
|
|
7936
|
-
async getCollectionProducts(handle, options) {
|
|
7937
|
-
const data = await this.graphql(GET_CATEGORY_PRODUCTS, {
|
|
7938
|
-
urlKey: handle,
|
|
7939
|
-
pageSize: options?.first ?? 20
|
|
7940
|
-
});
|
|
7941
|
-
return (data.products?.items ?? []).map((p) => transformProduct6(p, this.currencyCode));
|
|
7942
|
-
}
|
|
7943
|
-
async getCategory(handle) {
|
|
7944
|
-
return this.getCollection(handle);
|
|
7945
|
-
}
|
|
7946
|
-
async getCategories(options) {
|
|
7947
|
-
return this.getCollections(options);
|
|
7948
|
-
}
|
|
7949
|
-
async getCategoryProducts(handle, options) {
|
|
7950
|
-
return this.getCollectionProducts(handle, options);
|
|
5485
|
+
/**
|
|
5486
|
+
* Medusa always requires storefront configuration for multi-tenant support.
|
|
5487
|
+
*/
|
|
5488
|
+
isStorefrontRequired() {
|
|
5489
|
+
return true;
|
|
7951
5490
|
}
|
|
7952
5491
|
// ============================================
|
|
7953
|
-
//
|
|
5492
|
+
// Thor Plugin — Storefront Configuration
|
|
7954
5493
|
// ============================================
|
|
7955
|
-
|
|
7956
|
-
|
|
7957
|
-
|
|
7958
|
-
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
|
|
5494
|
+
/** In-memory cache for storefront config (60s TTL) */
|
|
5495
|
+
_storefrontConfigCache = null;
|
|
5496
|
+
/** In-memory cache for designer config (60s TTL) */
|
|
5497
|
+
_designerConfigCache = null;
|
|
5498
|
+
/** Cache TTL for Thor plugin configs (60 seconds) */
|
|
5499
|
+
THOR_CONFIG_CACHE_TTL = 60 * 1e3;
|
|
5500
|
+
/**
|
|
5501
|
+
* Get per-channel storefront configuration from Thor Commerce plugin.
|
|
5502
|
+
* Calls `GET /store/thor/config`.
|
|
5503
|
+
*
|
|
5504
|
+
* @returns StorefrontConfig or null if plugin is not installed / config not set
|
|
5505
|
+
*/
|
|
5506
|
+
async getStorefrontConfig() {
|
|
5507
|
+
if (this._storefrontConfigCache && Date.now() < this._storefrontConfigCache.expiresAt) {
|
|
5508
|
+
return this._storefrontConfigCache.config;
|
|
7967
5509
|
}
|
|
7968
|
-
|
|
7969
|
-
|
|
7970
|
-
|
|
7971
|
-
|
|
7972
|
-
|
|
7973
|
-
|
|
7974
|
-
|
|
7975
|
-
|
|
7976
|
-
|
|
7977
|
-
|
|
7978
|
-
|
|
7979
|
-
|
|
7980
|
-
`Add to cart failed: ${data.addProductsToCart.user_errors.map((e) => e.message).join("; ")}`,
|
|
7981
|
-
"MAGENTO_CART_ERROR"
|
|
5510
|
+
try {
|
|
5511
|
+
const url = `${this.baseUrl}/store/thor/config`;
|
|
5512
|
+
const response = await fetchWithRetry(
|
|
5513
|
+
url,
|
|
5514
|
+
{
|
|
5515
|
+
method: "GET",
|
|
5516
|
+
headers: {
|
|
5517
|
+
"x-publishable-api-key": this.publishableKey,
|
|
5518
|
+
"Content-Type": "application/json"
|
|
5519
|
+
}
|
|
5520
|
+
},
|
|
5521
|
+
this.retryConfig
|
|
7982
5522
|
);
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
5523
|
+
if (response.status === 404) {
|
|
5524
|
+
this._storefrontConfigCache = { config: null, expiresAt: Date.now() + this.THOR_CONFIG_CACHE_TTL };
|
|
5525
|
+
return null;
|
|
5526
|
+
}
|
|
5527
|
+
if (!response.ok) {
|
|
5528
|
+
this.logger.warn("[StorefrontConfig] Non-OK response", { status: response.status });
|
|
5529
|
+
return null;
|
|
5530
|
+
}
|
|
5531
|
+
const data = await response.json();
|
|
5532
|
+
const config = transformStorefrontConfig(data.storefront_config ?? data);
|
|
5533
|
+
this._storefrontConfigCache = { config, expiresAt: Date.now() + this.THOR_CONFIG_CACHE_TTL };
|
|
5534
|
+
this.logger.debug("[StorefrontConfig] Loaded from backend", { id: config.id });
|
|
5535
|
+
return config;
|
|
5536
|
+
} catch (error) {
|
|
5537
|
+
this.logger.warn("[StorefrontConfig] Failed to fetch", {
|
|
5538
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7992
5539
|
});
|
|
7993
|
-
|
|
5540
|
+
return null;
|
|
7994
5541
|
}
|
|
7995
|
-
return transformCart6(cart ?? { id: cartId, items: [], prices: {} }, this.currencyCode);
|
|
7996
|
-
}
|
|
7997
|
-
async updateCart(cartId, lines) {
|
|
7998
|
-
const cartItems = lines.map((l) => ({
|
|
7999
|
-
cart_item_uid: l.id,
|
|
8000
|
-
quantity: l.quantity
|
|
8001
|
-
}));
|
|
8002
|
-
const data = await this.graphql(UPDATE_CART_ITEMS, {
|
|
8003
|
-
cartId,
|
|
8004
|
-
cartItems
|
|
8005
|
-
});
|
|
8006
|
-
return transformCart6(data.updateCartItems.cart, this.currencyCode);
|
|
8007
|
-
}
|
|
8008
|
-
// ============================================
|
|
8009
|
-
// Checkout Operations
|
|
8010
|
-
// ============================================
|
|
8011
|
-
async completeCheckout(_cartId) {
|
|
8012
|
-
throw new ProviderAPIError(
|
|
8013
|
-
"Magento checkout requires multi-step process (set addresses, shipping, payment, place order). Use the Magento checkout flow.",
|
|
8014
|
-
"MAGENTO_CHECKOUT_MULTI_STEP"
|
|
8015
|
-
);
|
|
8016
|
-
}
|
|
8017
|
-
async getShippingMethods(_cartId) {
|
|
8018
|
-
return [];
|
|
8019
|
-
}
|
|
8020
|
-
async getRegionCountries() {
|
|
8021
|
-
return [];
|
|
8022
5542
|
}
|
|
8023
|
-
|
|
8024
|
-
|
|
8025
|
-
|
|
8026
|
-
|
|
8027
|
-
|
|
8028
|
-
|
|
8029
|
-
async
|
|
8030
|
-
|
|
8031
|
-
|
|
8032
|
-
|
|
8033
|
-
cartId,
|
|
8034
|
-
couponCode: code
|
|
8035
|
-
});
|
|
8036
|
-
return transformCart6(data.applyCouponToCart.cart, this.currencyCode);
|
|
8037
|
-
}
|
|
8038
|
-
async removeDiscountCode(cartId, _code) {
|
|
8039
|
-
const data = await this.graphql(REMOVE_COUPON, {
|
|
8040
|
-
cartId
|
|
8041
|
-
});
|
|
8042
|
-
return transformCart6(data.removeCouponFromCart.cart, this.currencyCode);
|
|
8043
|
-
}
|
|
8044
|
-
// ============================================
|
|
8045
|
-
// Customer & Orders (GraphQL — requires customerToken)
|
|
8046
|
-
// ============================================
|
|
8047
|
-
async getOrders(_options) {
|
|
8048
|
-
if (!this.customerToken) return [];
|
|
5543
|
+
/**
|
|
5544
|
+
* Get active Site Designer configuration from Thor Commerce plugin.
|
|
5545
|
+
* Calls `GET /store/thor/site-config`.
|
|
5546
|
+
*
|
|
5547
|
+
* @returns Versioned DesignerConfig or null if plugin is not installed / config not set
|
|
5548
|
+
*/
|
|
5549
|
+
async getDesignerConfig() {
|
|
5550
|
+
if (this._designerConfigCache && Date.now() < this._designerConfigCache.expiresAt) {
|
|
5551
|
+
return this._designerConfigCache.result;
|
|
5552
|
+
}
|
|
8049
5553
|
try {
|
|
8050
|
-
const
|
|
8051
|
-
|
|
8052
|
-
|
|
5554
|
+
const url = `${this.baseUrl}/store/thor/site-config`;
|
|
5555
|
+
const response = await fetchWithRetry(
|
|
5556
|
+
url,
|
|
5557
|
+
{
|
|
5558
|
+
method: "GET",
|
|
5559
|
+
headers: {
|
|
5560
|
+
"x-publishable-api-key": this.publishableKey,
|
|
5561
|
+
"Content-Type": "application/json"
|
|
5562
|
+
}
|
|
5563
|
+
},
|
|
5564
|
+
this.retryConfig
|
|
5565
|
+
);
|
|
5566
|
+
if (response.status === 404) {
|
|
5567
|
+
this._designerConfigCache = { result: null, expiresAt: Date.now() + this.THOR_CONFIG_CACHE_TTL };
|
|
5568
|
+
return null;
|
|
5569
|
+
}
|
|
5570
|
+
if (!response.ok) {
|
|
5571
|
+
this.logger.warn("[DesignerConfig] Non-OK response", { status: response.status });
|
|
5572
|
+
return null;
|
|
5573
|
+
}
|
|
5574
|
+
const data = await response.json();
|
|
5575
|
+
const result = {
|
|
5576
|
+
version: data.version ?? "",
|
|
5577
|
+
config: data.config
|
|
5578
|
+
};
|
|
5579
|
+
this._designerConfigCache = { result, expiresAt: Date.now() + this.THOR_CONFIG_CACHE_TTL };
|
|
5580
|
+
this.logger.debug("[DesignerConfig] Loaded from backend", { version: result.version });
|
|
5581
|
+
return result;
|
|
5582
|
+
} catch (error) {
|
|
5583
|
+
this.logger.warn("[DesignerConfig] Failed to fetch", {
|
|
5584
|
+
error: error instanceof Error ? error.message : String(error)
|
|
8053
5585
|
});
|
|
8054
|
-
return
|
|
8055
|
-
} catch {
|
|
8056
|
-
return [];
|
|
5586
|
+
return null;
|
|
8057
5587
|
}
|
|
8058
5588
|
}
|
|
8059
|
-
async getPaymentMethods(_options) {
|
|
8060
|
-
return [];
|
|
8061
|
-
}
|
|
8062
|
-
// ============================================
|
|
8063
|
-
// Inventory
|
|
8064
|
-
// ============================================
|
|
8065
|
-
async getStockLocations() {
|
|
8066
|
-
return [];
|
|
8067
|
-
}
|
|
8068
|
-
async checkInventoryLevels() {
|
|
8069
|
-
return [];
|
|
8070
|
-
}
|
|
8071
|
-
async validateCartStock() {
|
|
8072
|
-
return { allAvailable: true, unavailableItems: [] };
|
|
8073
|
-
}
|
|
8074
|
-
async getFulfillmentOptions() {
|
|
8075
|
-
return [];
|
|
8076
|
-
}
|
|
8077
5589
|
// ============================================
|
|
8078
|
-
// Admin
|
|
5590
|
+
// Thor Plugin — Admin Site Designer
|
|
8079
5591
|
// ============================================
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
5592
|
+
/**
|
|
5593
|
+
* Get admin Site Designer config + history for a sales channel.
|
|
5594
|
+
* Calls `GET /admin/thor/site-config/:salesChannelId`.
|
|
5595
|
+
*/
|
|
5596
|
+
async getAdminDesignerConfig(salesChannelId) {
|
|
5597
|
+
return this.makeAdminRequest(async () => {
|
|
5598
|
+
const path = `/admin/thor/site-config/${encodeURIComponent(salesChannelId)}`;
|
|
5599
|
+
let data;
|
|
5600
|
+
try {
|
|
5601
|
+
data = await this.sdk.client.fetch(path, { method: "GET" });
|
|
5602
|
+
} catch (error) {
|
|
5603
|
+
if (error?.status === 404) return null;
|
|
5604
|
+
throw new ProviderAPIError(
|
|
5605
|
+
`Failed to fetch admin designer config: ${error?.message ?? error?.status}`,
|
|
5606
|
+
"medusa",
|
|
5607
|
+
error?.status
|
|
5608
|
+
);
|
|
5609
|
+
}
|
|
5610
|
+
return {
|
|
5611
|
+
version: data.version ?? "",
|
|
5612
|
+
config: data.config,
|
|
5613
|
+
history: data.history
|
|
5614
|
+
};
|
|
5615
|
+
});
|
|
8091
5616
|
}
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
|
|
8102
|
-
|
|
5617
|
+
/**
|
|
5618
|
+
* Update the Site Designer config for a sales channel.
|
|
5619
|
+
* Calls `PUT /admin/thor/site-config/:salesChannelId`.
|
|
5620
|
+
*/
|
|
5621
|
+
async updateAdminDesignerConfig(salesChannelId, config) {
|
|
5622
|
+
return this.makeAdminRequest(async () => {
|
|
5623
|
+
const path = `/admin/thor/site-config/${encodeURIComponent(salesChannelId)}`;
|
|
5624
|
+
let data;
|
|
5625
|
+
try {
|
|
5626
|
+
data = await this.sdk.client.fetch(path, {
|
|
5627
|
+
method: "PUT",
|
|
5628
|
+
body: JSON.stringify({ config }),
|
|
5629
|
+
headers: { "Content-Type": "application/json" }
|
|
5630
|
+
});
|
|
5631
|
+
} catch (error) {
|
|
5632
|
+
throw new ProviderAPIError(
|
|
5633
|
+
`Failed to update admin designer config: ${error?.message ?? error?.status}`,
|
|
5634
|
+
"medusa",
|
|
5635
|
+
error?.status
|
|
5636
|
+
);
|
|
5637
|
+
}
|
|
5638
|
+
return {
|
|
5639
|
+
version: data.version ?? "",
|
|
5640
|
+
config: data.config
|
|
5641
|
+
};
|
|
5642
|
+
});
|
|
8103
5643
|
}
|
|
8104
|
-
|
|
8105
|
-
|
|
5644
|
+
/**
|
|
5645
|
+
* Restore a previous Site Designer config version.
|
|
5646
|
+
* Calls `POST /admin/thor/site-config/:salesChannelId/restore/:version`.
|
|
5647
|
+
*/
|
|
5648
|
+
async restoreAdminDesignerConfig(salesChannelId, version) {
|
|
5649
|
+
return this.makeAdminRequest(async () => {
|
|
5650
|
+
const path = `/admin/thor/site-config/${encodeURIComponent(salesChannelId)}/restore/${encodeURIComponent(version)}`;
|
|
5651
|
+
let data;
|
|
5652
|
+
try {
|
|
5653
|
+
data = await this.sdk.client.fetch(path, { method: "POST" });
|
|
5654
|
+
} catch (error) {
|
|
5655
|
+
throw new ProviderAPIError(
|
|
5656
|
+
`Failed to restore admin designer config: ${error?.message ?? error?.status}`,
|
|
5657
|
+
"medusa",
|
|
5658
|
+
error?.status
|
|
5659
|
+
);
|
|
5660
|
+
}
|
|
5661
|
+
return {
|
|
5662
|
+
version: data.version ?? "",
|
|
5663
|
+
config: data.config
|
|
5664
|
+
};
|
|
5665
|
+
});
|
|
8106
5666
|
}
|
|
8107
5667
|
};
|
|
8108
5668
|
|
|
@@ -10137,6 +7697,16 @@ var MockProvider = class {
|
|
|
10137
7697
|
};
|
|
10138
7698
|
|
|
10139
7699
|
// src/factory/index.ts
|
|
7700
|
+
var registry = /* @__PURE__ */ new Map();
|
|
7701
|
+
function registerProvider(type, factory) {
|
|
7702
|
+
registry.set(type, factory);
|
|
7703
|
+
}
|
|
7704
|
+
function getRegisteredProviders() {
|
|
7705
|
+
return Array.from(registry.keys());
|
|
7706
|
+
}
|
|
7707
|
+
registerProvider("mock", (config) => new MockProvider(config));
|
|
7708
|
+
registerProvider("medusa", (config) => new MedusaProvider(config));
|
|
7709
|
+
registerProvider("stelorder", (config) => new StelOrderProvider(config));
|
|
10140
7710
|
function createProvider(options) {
|
|
10141
7711
|
if (!options || !options.provider) {
|
|
10142
7712
|
throw new ConfigurationError("Provider type is required");
|
|
@@ -10144,27 +7714,14 @@ function createProvider(options) {
|
|
|
10144
7714
|
if (!options.config) {
|
|
10145
7715
|
throw new ConfigurationError("Provider configuration is required");
|
|
10146
7716
|
}
|
|
10147
|
-
const
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
|
|
10153
|
-
case "stelorder":
|
|
10154
|
-
return new StelOrderProvider(config);
|
|
10155
|
-
case "shopify":
|
|
10156
|
-
return new ShopifyProvider(config);
|
|
10157
|
-
case "bigcommerce":
|
|
10158
|
-
return new BigCommerceProvider(config);
|
|
10159
|
-
case "woocommerce":
|
|
10160
|
-
return new WooCommerceProvider(config);
|
|
10161
|
-
case "spree":
|
|
10162
|
-
return new SpreeProvider(config);
|
|
10163
|
-
case "magento":
|
|
10164
|
-
return new MagentoProvider(config);
|
|
10165
|
-
default:
|
|
10166
|
-
throw new ProviderNotFoundError(String(options.provider));
|
|
7717
|
+
const factory = registry.get(options.provider);
|
|
7718
|
+
if (!factory) {
|
|
7719
|
+
const available = getRegisteredProviders().join(", ");
|
|
7720
|
+
throw new ProviderNotFoundError(
|
|
7721
|
+
`Provider "${options.provider}" is not registered. Available: ${available}. Did you forget to import the provider package?`
|
|
7722
|
+
);
|
|
10167
7723
|
}
|
|
7724
|
+
return factory(options.config);
|
|
10168
7725
|
}
|
|
10169
7726
|
function isProviderType(provider, type) {
|
|
10170
7727
|
return provider.name === type;
|
|
@@ -10173,8 +7730,6 @@ function isProviderType(provider, type) {
|
|
|
10173
7730
|
0 && (module.exports = {
|
|
10174
7731
|
AdminAuthService,
|
|
10175
7732
|
AdminSessionService,
|
|
10176
|
-
BIGCOMMERCE_CAPABILITIES,
|
|
10177
|
-
BigCommerceProvider,
|
|
10178
7733
|
CHANNEL_ASSIGNED_AT_KEY,
|
|
10179
7734
|
CHANNEL_ASSIGNED_BY_KEY,
|
|
10180
7735
|
CHANNEL_SCOPE_KEY,
|
|
@@ -10183,10 +7738,8 @@ function isProviderType(provider, type) {
|
|
|
10183
7738
|
CircuitBreakerOpenError,
|
|
10184
7739
|
CommerceError,
|
|
10185
7740
|
ConfigurationError,
|
|
10186
|
-
MAGENTO_CAPABILITIES,
|
|
10187
7741
|
MEDUSA_CAPABILITIES,
|
|
10188
7742
|
MOCK_CAPABILITIES,
|
|
10189
|
-
MagentoProvider,
|
|
10190
7743
|
MedusaMetadataAdapter,
|
|
10191
7744
|
MedusaProvider,
|
|
10192
7745
|
MetricsService,
|
|
@@ -10194,15 +7747,9 @@ function isProviderType(provider, type) {
|
|
|
10194
7747
|
ProviderAPIError,
|
|
10195
7748
|
ProviderNotFoundError,
|
|
10196
7749
|
ResourceNotFoundError,
|
|
10197
|
-
SHOPIFY_CAPABILITIES,
|
|
10198
|
-
SPREE_CAPABILITIES,
|
|
10199
7750
|
STELORDER_CAPABILITIES,
|
|
10200
|
-
ShopifyProvider,
|
|
10201
|
-
SpreeProvider,
|
|
10202
7751
|
StelOrderProvider,
|
|
10203
7752
|
StorefrontConfigError,
|
|
10204
|
-
WOOCOMMERCE_CAPABILITIES,
|
|
10205
|
-
WooCommerceProvider,
|
|
10206
7753
|
assertEntityBelongsToChannel,
|
|
10207
7754
|
backendCircuitBreaker,
|
|
10208
7755
|
buildChannelScopeMetadata,
|
|
@@ -10210,12 +7757,15 @@ function isProviderType(provider, type) {
|
|
|
10210
7757
|
createBackendMetadataAdapter,
|
|
10211
7758
|
createLogger,
|
|
10212
7759
|
createProvider,
|
|
7760
|
+
extractErrorMessage,
|
|
7761
|
+
fetchWithRetry,
|
|
10213
7762
|
filterByChannel,
|
|
10214
7763
|
filterGlobalOnly,
|
|
10215
7764
|
geocodeAddress,
|
|
10216
7765
|
getBackendProviderId,
|
|
10217
7766
|
getCapabilityDescription,
|
|
10218
7767
|
getChannelScope,
|
|
7768
|
+
getRegisteredProviders,
|
|
10219
7769
|
isBackendCapabilities,
|
|
10220
7770
|
isGlobalEntity,
|
|
10221
7771
|
isProviderType,
|
|
@@ -10225,6 +7775,7 @@ function isProviderType(provider, type) {
|
|
|
10225
7775
|
numberToMoney,
|
|
10226
7776
|
partitionByChannel,
|
|
10227
7777
|
planFulfillment,
|
|
7778
|
+
registerProvider,
|
|
10228
7779
|
transformFulfillmentOption,
|
|
10229
7780
|
transformInventoryLevel,
|
|
10230
7781
|
transformStockLocation,
|