@thorprovider/adapters 4.2.1 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/PROVIDER_IMPLEMENTATION_ANALYSIS.md +659 -0
  2. package/dist/index.d.mts +225 -387
  3. package/dist/index.d.ts +225 -387
  4. package/dist/index.js +613 -3062
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +609 -3052
  7. package/dist/index.mjs.map +1 -1
  8. package/docs/RETRY_SOLUTION_SUMMARY.md +26 -11
  9. package/docs/authentication.md +115 -62
  10. package/docs/capabilities-system.md +4 -2
  11. package/docs/category-customization.md +37 -29
  12. package/docs/fetch-reliability-solution.md +127 -278
  13. package/docs/getting-started.md +43 -29
  14. package/docs/profile-management.md +68 -38
  15. package/docs/services-overview.md +4 -2
  16. package/package.json +1 -1
  17. package/quick-reference.md +33 -0
  18. package/src/backend/factory.ts +1 -11
  19. package/src/factory/index.ts +74 -52
  20. package/src/index.ts +5 -15
  21. package/src/providers/index.ts +1 -16
  22. package/src/providers/medusa/admin.test.ts +404 -0
  23. package/src/providers/medusa/admin.ts +442 -0
  24. package/src/providers/medusa/auth.test.ts +137 -0
  25. package/src/providers/medusa/auth.ts +96 -1
  26. package/src/providers/medusa/index.ts +21 -1
  27. package/src/resilience/README.md +70 -48
  28. package/src/types/capabilities.types.ts +0 -250
  29. package/src/core/errors.js +0 -161
  30. package/src/core/fetch-utils.js +0 -240
  31. package/src/core/logger.js +0 -63
  32. package/src/metadata/channel-scope-validation.js +0 -136
  33. package/src/metadata/channel-scope.js +0 -174
  34. package/src/metadata/index.js +0 -31
  35. package/src/providers/bigcommerce/bigcommerce.ts +0 -6
  36. package/src/providers/bigcommerce/index.ts +0 -315
  37. package/src/providers/bigcommerce/transforms.ts +0 -227
  38. package/src/providers/magento/index.ts +0 -387
  39. package/src/providers/magento/magento.ts +0 -5
  40. package/src/providers/magento/queries.ts +0 -267
  41. package/src/providers/magento/transforms.ts +0 -249
  42. package/src/providers/medusa/auth.js +0 -601
  43. package/src/providers/medusa/index.js +0 -2485
  44. package/src/providers/medusa/payment.js +0 -265
  45. package/src/providers/medusa/transforms.js +0 -505
  46. package/src/providers/shopify/auth.ts +0 -402
  47. package/src/providers/shopify/index.ts +0 -444
  48. package/src/providers/shopify/mutations.ts +0 -247
  49. package/src/providers/shopify/queries.ts +0 -324
  50. package/src/providers/shopify/shopify.ts +0 -7
  51. package/src/providers/shopify/transforms.ts +0 -275
  52. package/src/providers/spree/index.ts +0 -344
  53. package/src/providers/spree/spree.ts +0 -5
  54. package/src/providers/spree/transforms.ts +0 -254
  55. package/src/providers/woocommerce/index.ts +0 -310
  56. package/src/providers/woocommerce/transforms.ts +0 -254
  57. package/src/providers/woocommerce/woocommerce.ts +0 -5
  58. package/src/types/capabilities.types.js +0 -141
  59. package/src/types/provider.js +0 -9
  60. package/src/utils/payment-utils.js +0 -152
@@ -1,174 +0,0 @@
1
- "use strict";
2
- /**
3
- * Channel Scope — Backend-Agnostic Multi-Tenant Helpers
4
- *
5
- * Provides utilities for tagging, reading and filtering entity metadata by
6
- * sales-channel (or any future tenant key). Works with any backend that
7
- * exposes a writable `metadata: Record<string, unknown>` field on its
8
- * entities (Medusa, Shopify metafields, WooCommerce meta, etc.).
9
- *
10
- * ## Design decisions
11
- *
12
- * | Decision | Rationale |
13
- * |----------|-----------|
14
- * | Flat key (`sales_channel_id`) | Simpler queries, no prefix parsing required |
15
- * | Metadata, not custom fields | Zero DB migrations; works across backends |
16
- * | Pure functions | No side effects → easily testable; usable in any layer |
17
- * | `assigned_at` timestamp | Audit trail for dropshipper / admin assignment |
18
- *
19
- * @module metadata/channel-scope
20
- */
21
- Object.defineProperty(exports, "__esModule", { value: true });
22
- exports.CHANNEL_ASSIGNED_BY_KEY = exports.CHANNEL_ASSIGNED_AT_KEY = exports.CHANNEL_SCOPE_KEY = void 0;
23
- exports.buildChannelScopeMetadata = buildChannelScopeMetadata;
24
- exports.getChannelScope = getChannelScope;
25
- exports.isScopedToChannel = isScopedToChannel;
26
- exports.isGlobalEntity = isGlobalEntity;
27
- exports.filterByChannel = filterByChannel;
28
- exports.filterGlobalOnly = filterGlobalOnly;
29
- exports.partitionByChannel = partitionByChannel;
30
- // ────────────────────────────────────────────────────────────────────
31
- // Constants
32
- // ────────────────────────────────────────────────────────────────────
33
- /**
34
- * Well-known metadata key used to store the owning sales-channel on any
35
- * entity that Medusa (or the backend) does not natively scope.
36
- *
37
- * Convention: every entity whose metadata contains this key is
38
- * considered "owned" by the referenced channel. Entities without it
39
- * are global / unscoped.
40
- */
41
- exports.CHANNEL_SCOPE_KEY = 'sales_channel_id';
42
- /**
43
- * Optional audit key — ISO-8601 timestamp of when the channel was assigned.
44
- */
45
- exports.CHANNEL_ASSIGNED_AT_KEY = 'channel_assigned_at';
46
- /**
47
- * Optional audit key — who assigned the entity to the channel.
48
- * Value is a free-form string (admin email, user id, "system", etc.).
49
- */
50
- exports.CHANNEL_ASSIGNED_BY_KEY = 'channel_assigned_by';
51
- // ────────────────────────────────────────────────────────────────────
52
- // Builders
53
- // ────────────────────────────────────────────────────────────────────
54
- /**
55
- * Build channel-scope metadata payload.
56
- *
57
- * The returned object should be **merged** into the entity's existing
58
- * `metadata` field when calling `updateCustomer`, `updateOrder`, etc.
59
- *
60
- * @param channelId - Sales-channel ID (e.g. `"sc_01HXXX"`)
61
- * @param assignedBy - Optional identifier of who performed the assignment
62
- * @returns Metadata fragment ready to spread into an update payload
63
- *
64
- * @example
65
- * ```typescript
66
- * const scope = buildChannelScopeMetadata('sc_01HXXX', 'admin@store.com');
67
- * await commerce.updateCustomer(customerId, {
68
- * metadata: { ...existingMeta, ...scope },
69
- * });
70
- * ```
71
- */
72
- function buildChannelScopeMetadata(channelId, assignedBy) {
73
- var _a;
74
- var payload = (_a = {},
75
- _a[exports.CHANNEL_SCOPE_KEY] = channelId,
76
- _a[exports.CHANNEL_ASSIGNED_AT_KEY] = new Date().toISOString(),
77
- _a);
78
- if (assignedBy) {
79
- payload[exports.CHANNEL_ASSIGNED_BY_KEY] = assignedBy;
80
- }
81
- return payload;
82
- }
83
- // ────────────────────────────────────────────────────────────────────
84
- // Readers
85
- // ────────────────────────────────────────────────────────────────────
86
- /**
87
- * Read the channel-scope value from an entity's metadata.
88
- *
89
- * @returns The channel ID string, or `null` if the entity has no scope set.
90
- */
91
- function getChannelScope(entity) {
92
- var _a;
93
- var value = (_a = entity.metadata) === null || _a === void 0 ? void 0 : _a[exports.CHANNEL_SCOPE_KEY];
94
- return typeof value === 'string' && value.length > 0 ? value : null;
95
- }
96
- /**
97
- * Check if an entity is scoped to a specific channel.
98
- *
99
- * @param entity - Any entity with optional `metadata`
100
- * @param channelId - Channel ID to match against
101
- * @returns `true` if entity's metadata contains a matching scope key
102
- */
103
- function isScopedToChannel(entity, channelId) {
104
- return getChannelScope(entity) === channelId;
105
- }
106
- /**
107
- * Check if an entity has **no** channel scope (i.e. it is "global").
108
- */
109
- function isGlobalEntity(entity) {
110
- return getChannelScope(entity) === null;
111
- }
112
- // ────────────────────────────────────────────────────────────────────
113
- // Filters
114
- // ────────────────────────────────────────────────────────────────────
115
- /**
116
- * Filter an array of entities to only those belonging to a channel.
117
- *
118
- * If `channelId` is `undefined` or empty, returns the full array unfiltered.
119
- * This preserves backward compatibility for admin views that intentionally
120
- * list all entities regardless of tenant.
121
- *
122
- * @param entities - Array of channel-scopeable entities
123
- * @param channelId - Optional channel ID to filter by
124
- * @returns Filtered (or unfiltered) array
125
- *
126
- * @example
127
- * ```typescript
128
- * const allCustomers = await commerce.getCustomers();
129
- * const myCustomers = filterByChannel(allCustomers, salesChannelId);
130
- * ```
131
- */
132
- function filterByChannel(entities, channelId) {
133
- if (!channelId)
134
- return entities;
135
- return entities.filter(function (e) { return isScopedToChannel(e, channelId); });
136
- }
137
- /**
138
- * Filter an array of entities to only those that are global (unscoped).
139
- *
140
- * Useful for admin views that want to list entities not yet assigned to
141
- * any channel.
142
- */
143
- function filterGlobalOnly(entities) {
144
- return entities.filter(isGlobalEntity);
145
- }
146
- /**
147
- * Partition entities into `{ scoped, global }` groups.
148
- *
149
- * @param entities - Array of channel-scopeable entities
150
- * @param channelId - Channel ID to partition against
151
- * @returns Object with `scoped` (matching channel) and `global` (no scope) arrays
152
- *
153
- * @example
154
- * ```typescript
155
- * const { scoped, global } = partitionByChannel(customers, 'sc_123');
156
- * // scoped → customers assigned to sc_123
157
- * // global → customers not assigned to any channel
158
- * ```
159
- */
160
- function partitionByChannel(entities, channelId) {
161
- var scoped = [];
162
- var global = [];
163
- for (var _i = 0, entities_1 = entities; _i < entities_1.length; _i++) {
164
- var entity = entities_1[_i];
165
- if (isScopedToChannel(entity, channelId)) {
166
- scoped.push(entity);
167
- }
168
- else if (isGlobalEntity(entity)) {
169
- global.push(entity);
170
- }
171
- // Entities scoped to a *different* channel are silently excluded.
172
- }
173
- return { scoped: scoped, global: global };
174
- }
@@ -1,31 +0,0 @@
1
- "use strict";
2
- /**
3
- * Metadata — Channel Scope Utilities
4
- *
5
- * Backend-agnostic helpers for multi-tenant channel scoping.
6
- * Used by L2 adapters to tag, validate and filter entities by sales channel.
7
- *
8
- * @module metadata
9
- */
10
- Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.assertEntityBelongsToChannel = exports.validateChannelScopeAssignment = exports.validateAssignedBy = exports.validateEntityId = exports.validateChannelId = exports.ChannelScopeValidationError = exports.partitionByChannel = exports.filterGlobalOnly = exports.filterByChannel = exports.isGlobalEntity = exports.isScopedToChannel = exports.getChannelScope = exports.buildChannelScopeMetadata = exports.CHANNEL_ASSIGNED_BY_KEY = exports.CHANNEL_ASSIGNED_AT_KEY = exports.CHANNEL_SCOPE_KEY = void 0;
12
- // Builders, readers & filters
13
- var channel_scope_1 = require("./channel-scope");
14
- Object.defineProperty(exports, "CHANNEL_SCOPE_KEY", { enumerable: true, get: function () { return channel_scope_1.CHANNEL_SCOPE_KEY; } });
15
- Object.defineProperty(exports, "CHANNEL_ASSIGNED_AT_KEY", { enumerable: true, get: function () { return channel_scope_1.CHANNEL_ASSIGNED_AT_KEY; } });
16
- Object.defineProperty(exports, "CHANNEL_ASSIGNED_BY_KEY", { enumerable: true, get: function () { return channel_scope_1.CHANNEL_ASSIGNED_BY_KEY; } });
17
- Object.defineProperty(exports, "buildChannelScopeMetadata", { enumerable: true, get: function () { return channel_scope_1.buildChannelScopeMetadata; } });
18
- Object.defineProperty(exports, "getChannelScope", { enumerable: true, get: function () { return channel_scope_1.getChannelScope; } });
19
- Object.defineProperty(exports, "isScopedToChannel", { enumerable: true, get: function () { return channel_scope_1.isScopedToChannel; } });
20
- Object.defineProperty(exports, "isGlobalEntity", { enumerable: true, get: function () { return channel_scope_1.isGlobalEntity; } });
21
- Object.defineProperty(exports, "filterByChannel", { enumerable: true, get: function () { return channel_scope_1.filterByChannel; } });
22
- Object.defineProperty(exports, "filterGlobalOnly", { enumerable: true, get: function () { return channel_scope_1.filterGlobalOnly; } });
23
- Object.defineProperty(exports, "partitionByChannel", { enumerable: true, get: function () { return channel_scope_1.partitionByChannel; } });
24
- // Validation
25
- var channel_scope_validation_1 = require("./channel-scope-validation");
26
- Object.defineProperty(exports, "ChannelScopeValidationError", { enumerable: true, get: function () { return channel_scope_validation_1.ChannelScopeValidationError; } });
27
- Object.defineProperty(exports, "validateChannelId", { enumerable: true, get: function () { return channel_scope_validation_1.validateChannelId; } });
28
- Object.defineProperty(exports, "validateEntityId", { enumerable: true, get: function () { return channel_scope_validation_1.validateEntityId; } });
29
- Object.defineProperty(exports, "validateAssignedBy", { enumerable: true, get: function () { return channel_scope_validation_1.validateAssignedBy; } });
30
- Object.defineProperty(exports, "validateChannelScopeAssignment", { enumerable: true, get: function () { return channel_scope_validation_1.validateChannelScopeAssignment; } });
31
- Object.defineProperty(exports, "assertEntityBelongsToChannel", { enumerable: true, get: function () { return channel_scope_validation_1.assertEntityBelongsToChannel; } });
@@ -1,6 +0,0 @@
1
- /**
2
- * @thorprovider/adapters — BigCommerce Provider exports
3
- */
4
- export { BigCommerceProvider } from './index';
5
- export type { BigCommerceProviderConfig } from './index';
6
- export * from './transforms';
@@ -1,315 +0,0 @@
1
- /**
2
- * @thorprovider/adapters — BigCommerce Provider
3
- * Implements CommerceProvider for BigCommerce REST Storefront API
4
- *
5
- * Uses raw fetch against BigCommerce Storefront REST API.
6
- * The official `@bigcommerce/storefront-data-hooks` is deprecated (3 years unmaintained).
7
- */
8
-
9
- import type {
10
- Product,
11
- GetProductsOptions,
12
- Cart,
13
- CartLineInput,
14
- CartLineUpdate,
15
- Collection,
16
- CollectionProductsOptions,
17
- GetCollectionsOptions,
18
- ProductCategory,
19
- GetCategoriesOptions,
20
- Order,
21
- GetOrdersOptions,
22
- Customer,
23
- Address,
24
- PaymentMethod,
25
- PaymentMethodsOptions,
26
- ShippingMethod,
27
- StockLocation,
28
- InventoryLevel,
29
- StockValidation,
30
- FulfillmentOption,
31
- StorefrontContext,
32
- BackendCapabilities,
33
- } from '@thorprovider/types';
34
- import type { CommerceProvider, ProviderConfig } from '../../types/provider';
35
- import { BIGCOMMERCE_CAPABILITIES } from '../../types/capabilities.types';
36
- import { createLogger } from '../../core/logger';
37
- import { ProviderAPIError } from '../../core/errors';
38
- import {
39
- transformProduct,
40
- transformCart,
41
- transformCollection,
42
- transformCustomer,
43
- transformOrder,
44
- } from './transforms';
45
-
46
- /**
47
- * BigCommerce provider configuration
48
- */
49
- export interface BigCommerceProviderConfig extends ProviderConfig {
50
- /** BigCommerce store hash */
51
- storeHash: string;
52
- /** Storefront API token (JWT) */
53
- storefrontApiToken: string;
54
- /** Storefront API URL (usually https://store-{hash}.mybigcommerce.com) */
55
- storefrontApiUrl: string;
56
- /** Channel ID */
57
- channelId: string;
58
- /** Default currency code (default: 'USD') */
59
- currencyCode?: string;
60
- }
61
-
62
- export class BigCommerceProvider implements CommerceProvider {
63
- readonly name = 'bigcommerce';
64
- readonly version = '1.0.0';
65
- readonly capabilities: BackendCapabilities = BIGCOMMERCE_CAPABILITIES;
66
-
67
- private logger: ReturnType<typeof createLogger>;
68
- private storefrontApiUrl: string;
69
- private storeHash: string;
70
- private channelId: string;
71
- private currencyCode: string;
72
- private headers: Record<string, string>;
73
-
74
- constructor(config: BigCommerceProviderConfig) {
75
- this.logger = createLogger('BigCommerceProvider', config.debug);
76
- this.storefrontApiUrl = config.storefrontApiUrl.replace(/\/$/, '');
77
- this.storeHash = config.storeHash;
78
- this.channelId = config.channelId;
79
- this.currencyCode = config.currencyCode ?? 'USD';
80
- this.headers = {
81
- 'Content-Type': 'application/json',
82
- 'Authorization': `Bearer ${config.storefrontApiToken}`,
83
- };
84
- this.logger.info('Initializing BigCommerce Provider', { storeHash: this.storeHash, channelId: this.channelId });
85
- }
86
-
87
- private async fetchApi<T = any>(path: string, init?: RequestInit): Promise<T> {
88
- const url = `${this.storefrontApiUrl}${path}`;
89
- const response = await fetch(url, {
90
- ...init,
91
- headers: { ...this.headers, ...init?.headers },
92
- });
93
-
94
- if (!response.ok) {
95
- throw new ProviderAPIError(
96
- `BigCommerce API error: ${response.status} ${response.statusText}`,
97
- 'BIGCOMMERCE_API_ERROR',
98
- response.status,
99
- );
100
- }
101
-
102
- return response.json() as Promise<T>;
103
- }
104
-
105
- // ============================================
106
- // Product Operations
107
- // ============================================
108
-
109
- async getProduct(handle: string): Promise<Product | undefined> {
110
- try {
111
- const data = await this.fetchApi<{ data: any[] }>(`/api/storefront/products?include=variants,options,images&keyword=${encodeURIComponent(handle)}`);
112
- const product = data.data?.[0];
113
- return product ? transformProduct(product, this.currencyCode) : undefined;
114
- } catch {
115
- return undefined;
116
- }
117
- }
118
-
119
- async getProducts(options?: GetProductsOptions): Promise<Product[]> {
120
- const limit = options?.first ?? 20;
121
- let path = `/api/storefront/products?include=variants,options,images&limit=${limit}`;
122
- if (options?.query) path += `&keyword=${encodeURIComponent(options.query)}`;
123
-
124
- const data = await this.fetchApi<{ data: any[] }>(path);
125
- return (data.data ?? []).map((p) => transformProduct(p, this.currencyCode));
126
- }
127
-
128
- async getProductRecommendations(_productId: string): Promise<Product[]> {
129
- return []; // BigCommerce doesn't have a Storefront recommendation API
130
- }
131
-
132
- // ============================================
133
- // Collection Operations (BigCommerce Categories)
134
- // ============================================
135
-
136
- async getCollection(handle: string): Promise<Collection | undefined> {
137
- try {
138
- const data = await this.fetchApi<{ data: any[] }>(`/api/storefront/categories?url=/${handle}/`);
139
- const cat = data.data?.[0];
140
- return cat ? transformCollection(cat) : undefined;
141
- } catch {
142
- return undefined;
143
- }
144
- }
145
-
146
- async getCollections(options?: GetCollectionsOptions): Promise<Collection[]> {
147
- const limit = options?.limit ?? 50;
148
- const data = await this.fetchApi<{ data: any[] }>(`/api/storefront/categories?limit=${limit}`);
149
- return (data.data ?? []).map(transformCollection);
150
- }
151
-
152
- async getCollectionProducts(handle: string, options?: CollectionProductsOptions): Promise<Product[]> {
153
- const limit = options?.first ?? 20;
154
- const data = await this.fetchApi<{ data: any[] }>(
155
- `/api/storefront/products?include=variants,options,images&categories:in=${handle}&limit=${limit}`,
156
- );
157
- return (data.data ?? []).map((p) => transformProduct(p, this.currencyCode));
158
- }
159
-
160
- // ============================================
161
- // Category Operations
162
- // ============================================
163
-
164
- async getCategory(idOrHandle: string): Promise<ProductCategory | undefined> {
165
- return this.getCollection(idOrHandle) as any;
166
- }
167
-
168
- async getCategories(options?: GetCategoriesOptions): Promise<ProductCategory[]> {
169
- return this.getCollections(options) as any;
170
- }
171
-
172
- async getCategoryProducts(handle: string, options?: CollectionProductsOptions): Promise<Product[]> {
173
- return this.getCollectionProducts(handle, options);
174
- }
175
-
176
- // ============================================
177
- // Cart Operations
178
- // ============================================
179
-
180
- async createCart(): Promise<Cart> {
181
- const data = await this.fetchApi<{ data: any }>('/api/storefront/carts', {
182
- method: 'POST',
183
- body: JSON.stringify({ line_items: [] }),
184
- });
185
- return transformCart(data.data ?? data, this.currencyCode);
186
- }
187
-
188
- async getCart(cartId: string): Promise<Cart | undefined> {
189
- try {
190
- const data = await this.fetchApi<{ data: any }>(`/api/storefront/carts/${cartId}?include=line_items.physical_items.options`);
191
- return transformCart(data.data ?? data, this.currencyCode);
192
- } catch {
193
- return undefined;
194
- }
195
- }
196
-
197
- async addToCart(cartId: string, lines: CartLineInput[]): Promise<Cart> {
198
- const lineItems = lines.map((l) => ({
199
- product_id: parseInt(l.merchandiseId, 10),
200
- quantity: l.quantity,
201
- }));
202
-
203
- const data = await this.fetchApi<{ data: any }>(`/api/storefront/carts/${cartId}/items`, {
204
- method: 'POST',
205
- body: JSON.stringify({ line_items: lineItems }),
206
- });
207
-
208
- return transformCart(data.data ?? data, this.currencyCode);
209
- }
210
-
211
- async removeFromCart(cartId: string, lineIds: string[]): Promise<Cart> {
212
- for (const lineId of lineIds) {
213
- await this.fetchApi(`/api/storefront/carts/${cartId}/items/${lineId}`, { method: 'DELETE' });
214
- }
215
-
216
- const cart = await this.getCart(cartId);
217
- if (!cart) throw new ProviderAPIError('Cart not found after removal', 'BIGCOMMERCE_CART_ERROR');
218
- return cart;
219
- }
220
-
221
- async updateCart(cartId: string, lines: CartLineUpdate[]): Promise<Cart> {
222
- for (const line of lines) {
223
- await this.fetchApi(`/api/storefront/carts/${cartId}/items/${line.id}`, {
224
- method: 'PUT',
225
- body: JSON.stringify({ line_item: { quantity: line.quantity } }),
226
- });
227
- }
228
-
229
- const cart = await this.getCart(cartId);
230
- if (!cart) throw new ProviderAPIError('Cart not found after update', 'BIGCOMMERCE_CART_ERROR');
231
- return cart;
232
- }
233
-
234
- // ============================================
235
- // Checkout Operations (Hosted)
236
- // ============================================
237
-
238
- async completeCheckout(_cartId: string): Promise<Order> {
239
- throw new ProviderAPIError(
240
- 'BigCommerce uses Embedded Checkout. Redirect the buyer to cart.checkoutUrl.',
241
- 'BIGCOMMERCE_HOSTED_CHECKOUT',
242
- );
243
- }
244
-
245
- async getShippingMethods(_cartId: string): Promise<ShippingMethod[]> {
246
- return [];
247
- }
248
-
249
- async getRegionCountries(): Promise<import('@thorprovider/types').Country[]> {
250
- return [];
251
- }
252
-
253
- async addShippingMethod(_cartId: string, _methodId: string): Promise<Cart> {
254
- throw new ProviderAPIError('BigCommerce handles shipping in Embedded Checkout.', 'BIGCOMMERCE_HOSTED_CHECKOUT');
255
- }
256
-
257
- async initiatePaymentSession(_cartId: string, _providerId: string): Promise<void> {
258
- throw new ProviderAPIError('BigCommerce handles payment in Embedded Checkout.', 'BIGCOMMERCE_HOSTED_CHECKOUT');
259
- }
260
-
261
- async applyDiscountCode(cartId: string, code: string): Promise<Cart> {
262
- const data = await this.fetchApi<{ data: any }>(`/api/storefront/carts/${cartId}/coupons`, {
263
- method: 'POST',
264
- body: JSON.stringify({ coupon_code: code }),
265
- });
266
- return transformCart(data.data ?? data, this.currencyCode);
267
- }
268
-
269
- async removeDiscountCode(cartId: string, code: string): Promise<Cart> {
270
- await this.fetchApi(`/api/storefront/carts/${cartId}/coupons/${encodeURIComponent(code)}`, { method: 'DELETE' });
271
- const cart = await this.getCart(cartId);
272
- if (!cart) throw new ProviderAPIError('Cart not found after coupon removal', 'BIGCOMMERCE_CART_ERROR');
273
- return cart;
274
- }
275
-
276
- // ============================================
277
- // Customer, Order, Payment, Inventory, Admin
278
- // ============================================
279
-
280
- async getOrders(_options: GetOrdersOptions): Promise<Order[]> {
281
- return [];
282
- }
283
-
284
- async getPaymentMethods(_options: PaymentMethodsOptions): Promise<PaymentMethod[]> {
285
- return [];
286
- }
287
-
288
- async getStockLocations(): Promise<StockLocation[]> { return []; }
289
- async checkInventoryLevels(): Promise<InventoryLevel[]> { return []; }
290
- async validateCartStock(): Promise<StockValidation> { return { allAvailable: true, unavailableItems: [] }; }
291
- async getFulfillmentOptions(): Promise<FulfillmentOption[]> { return []; }
292
-
293
- async cancelOrder(_orderId: string): Promise<Order> {
294
- throw new ProviderAPIError('Order cancellation requires BigCommerce Management API.', 'BIGCOMMERCE_ADMIN_REQUIRED');
295
- }
296
- async createFulfillment(_orderId: string): Promise<void> {
297
- throw new ProviderAPIError('Fulfillment requires BigCommerce Management API.', 'BIGCOMMERCE_ADMIN_REQUIRED');
298
- }
299
- async createRefund(_orderId: string): Promise<void> {
300
- throw new ProviderAPIError('Refunds require BigCommerce Management API.', 'BIGCOMMERCE_ADMIN_REQUIRED');
301
- }
302
- async getAdminOrders(): Promise<{ orders: Order[]; count: number }> { return { orders: [], count: 0 }; }
303
-
304
- async getStorefrontContext(): Promise<StorefrontContext> {
305
- return {
306
- id: this.channelId,
307
- name: `BigCommerce Store ${this.storeHash}`,
308
- platformType: 'bigcommerce',
309
- requiresProductScoping: false,
310
- currencyCode: this.currencyCode,
311
- };
312
- }
313
-
314
- isStorefrontRequired(): boolean { return false; }
315
- }