@thorprovider/adapters 5.0.0 → 5.0.1

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