@thorprovider/adapters 4.1.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +18 -18
- package/CHANGELOG.md +11 -0
- package/README.md +17 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.d.mts +82 -1
- package/dist/index.d.ts +82 -1
- package/dist/index.js +1036 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1034 -0
- package/dist/index.mjs.map +1 -1
- package/docs/getting-started.md +32 -11
- package/docs/providers-overview.md +9 -3
- package/package.json +1 -1
- package/src/factory/index.ts +4 -0
- package/src/index.ts +3 -0
- package/src/providers/index.ts +3 -0
- package/src/providers/mock/index.ts +427 -0
- package/src/providers/mock/mock.ts +917 -0
- package/src/types/capabilities.types.ts +33 -0
- package/.turbo/turbo-test.log +0 -1
- package/.turbo/turbo-type-check.log +0 -0
- package/publish.log +0 -93
package/docs/getting-started.md
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
|
-
# Getting Started
|
|
2
|
-
|
|
3
1
|
---
|
|
2
|
+
title: "Getting started"
|
|
3
|
+
purpose: "Provide a quick setup guide for integrating Thor adapters in mock or live backend mode"
|
|
4
|
+
lastReviewed: "2026-05-01"
|
|
4
5
|
type: how-to
|
|
5
6
|
tags: [setup, installation, adapters, medusa, configuration]
|
|
6
7
|
related:
|
|
7
8
|
- services-overview.md
|
|
8
9
|
- providers-overview.md
|
|
9
10
|
- authentication.md
|
|
10
|
-
updated: 2026-02-28
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
# Getting Started
|
|
14
|
+
|
|
15
|
+
Quick setup guide for integrating Thor adapters into our project. Start with `mock` for backend-optional development, or configure a real backend immediately.
|
|
14
16
|
|
|
15
17
|
## Prerequisites
|
|
16
18
|
|
|
17
19
|
- Node.js 18+
|
|
18
20
|
- Yarn 4+ (PnP mode)
|
|
19
|
-
-
|
|
20
|
-
- Medusa Publishable API Key
|
|
21
|
+
- Optional: access to a Medusa.js v2 backend
|
|
22
|
+
- Optional: Medusa Publishable API Key
|
|
21
23
|
|
|
22
24
|
## Quick Example
|
|
23
25
|
|
|
@@ -25,6 +27,14 @@ Quick setup guide for integrating Thor adapters into our project.
|
|
|
25
27
|
import { createProvider } from '@thorprovider/adapters';
|
|
26
28
|
|
|
27
29
|
const commerce = createProvider({
|
|
30
|
+
provider: 'mock',
|
|
31
|
+
config: {
|
|
32
|
+
currencyCode: 'USD',
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Or use a real backend
|
|
37
|
+
const liveCommerce = createProvider({
|
|
28
38
|
provider: 'medusa',
|
|
29
39
|
config: {
|
|
30
40
|
baseUrl: process.env.NEXT_PUBLIC_COMMERCE_API_URL!,
|
|
@@ -53,6 +63,14 @@ Create a shared instance for our application.
|
|
|
53
63
|
import { createProvider } from '@thorprovider/adapters';
|
|
54
64
|
|
|
55
65
|
export const commerce = createProvider({
|
|
66
|
+
provider: 'mock',
|
|
67
|
+
config: {
|
|
68
|
+
debug: process.env.NODE_ENV === 'development',
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Switch to a real backend when ready
|
|
73
|
+
export const liveCommerce = createProvider({
|
|
56
74
|
provider: 'medusa',
|
|
57
75
|
config: {
|
|
58
76
|
baseUrl: process.env.NEXT_PUBLIC_COMMERCE_API_URL!,
|
|
@@ -64,14 +82,17 @@ export const commerce = createProvider({
|
|
|
64
82
|
|
|
65
83
|
### Step 3: Configure Environment Variables
|
|
66
84
|
|
|
67
|
-
Create `.env.local` to store
|
|
85
|
+
Create `.env.local` to store runtime configuration.
|
|
68
86
|
|
|
69
87
|
```env
|
|
70
|
-
#
|
|
71
|
-
NEXT_PUBLIC_COMMERCE_PROVIDER=
|
|
88
|
+
# Mock-first configuration
|
|
89
|
+
NEXT_PUBLIC_COMMERCE_PROVIDER=mock
|
|
90
|
+
NEXT_PUBLIC_DEFAULT_CURRENCY=USD
|
|
91
|
+
|
|
92
|
+
# Live backend configuration (only required for real providers)
|
|
93
|
+
# NEXT_PUBLIC_COMMERCE_PROVIDER=medusa
|
|
72
94
|
NEXT_PUBLIC_COMMERCE_API_URL=http://localhost:9000
|
|
73
95
|
NEXT_PUBLIC_COMMERCE_API_KEY=your_publishable_key_here
|
|
74
|
-
NEXT_PUBLIC_DEFAULT_CURRENCY=USD
|
|
75
96
|
```
|
|
76
97
|
|
|
77
98
|
### Step 4: Use in Server Components (Next.js)
|
|
@@ -96,7 +117,7 @@ export default async function ProductsPage() {
|
|
|
96
117
|
## ✅ DO's
|
|
97
118
|
|
|
98
119
|
1. **Use Singleton Pattern:** Create one instance of the provider and export it.
|
|
99
|
-
2. **Handle Environment Variables:**
|
|
120
|
+
2. **Handle Environment Variables:** Require `baseUrl` only for real backends.
|
|
100
121
|
3. **Prefer Server-Side:** Fetch data in Server Components whenever possible.
|
|
101
122
|
4. **Use TypeScript Types:** Leverage the exported types from `@thorprovider/adapters` for better DX.
|
|
102
123
|
5. **Configure Debug Mode:** Enable `debug: true` during development to trace API issues.
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
# Providers Overview
|
|
2
|
-
|
|
3
1
|
---
|
|
2
|
+
title: "Providers overview"
|
|
3
|
+
purpose: "Describe the supported commerce providers and how Thor Adapters abstracts them behind a unified interface"
|
|
4
|
+
lastReviewed: "2026-05-01"
|
|
4
5
|
type: guide
|
|
5
6
|
tags: [architecture, providers, medusa, shopify, roadmap]
|
|
6
7
|
related:
|
|
7
8
|
- capabilities-system.md
|
|
8
9
|
- services-overview.md
|
|
9
10
|
- getting-started.md
|
|
10
|
-
updated: 2026-02-28
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
# Providers Overview
|
|
14
|
+
|
|
13
15
|
Overview of the e-commerce platforms supported by Thor Adapters and how they are abstracted into a unified interface.
|
|
14
16
|
|
|
15
17
|
## Overview
|
|
@@ -28,6 +30,10 @@ Thor Adapters eliminate these problems by wrapping the data layer in a consisten
|
|
|
28
30
|
|
|
29
31
|
## Current Support Matrix
|
|
30
32
|
|
|
33
|
+
### Mock
|
|
34
|
+
**Status:** ✅ Production Ready
|
|
35
|
+
Fixture-backed provider for local development, previews, design-mode storefront work, and backend-optional scaffolds.
|
|
36
|
+
|
|
31
37
|
### Medusa (v2.0)
|
|
32
38
|
**Status:** ✅ Production Ready
|
|
33
39
|
Our primary integration, highly optimized for headless performance.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thorprovider/adapters",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "Framework-agnostic commerce adapters for Thor Commerce ecosystem - Multi-platform support with unified API",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org",
|
package/src/factory/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { BigCommerceProvider, type BigCommerceProviderConfig } from '../provider
|
|
|
17
17
|
import { WooCommerceProvider, type WooCommerceProviderConfig } from '../providers/woocommerce';
|
|
18
18
|
import { SpreeProvider, type SpreeProviderConfig } from '../providers/spree';
|
|
19
19
|
import { MagentoProvider, type MagentoProviderConfig } from '../providers/magento';
|
|
20
|
+
import { MockProvider, type MockProviderConfig } from '../providers/mock';
|
|
20
21
|
import { ProviderNotFoundError, ConfigurationError } from '../core/errors';
|
|
21
22
|
import type { ProviderTypeString } from '@thorprovider/types';
|
|
22
23
|
|
|
@@ -30,6 +31,7 @@ export type ProviderType = ProviderTypeString;
|
|
|
30
31
|
* Provider configuration discriminated union
|
|
31
32
|
*/
|
|
32
33
|
export type ProviderFactoryConfig =
|
|
34
|
+
| { provider: 'mock'; config: MockProviderConfig }
|
|
33
35
|
| { provider: 'medusa'; config: MedusaProviderConfig }
|
|
34
36
|
| { provider: 'shopify'; config: ShopifyProviderConfig }
|
|
35
37
|
| { provider: 'bigcommerce'; config: BigCommerceProviderConfig }
|
|
@@ -73,6 +75,8 @@ export function createProvider(options: ProviderFactoryConfig | RuntimeProviderC
|
|
|
73
75
|
const config = options.config as any;
|
|
74
76
|
|
|
75
77
|
switch (options.provider) {
|
|
78
|
+
case 'mock':
|
|
79
|
+
return new MockProvider(config);
|
|
76
80
|
case 'medusa':
|
|
77
81
|
return new MedusaProvider(config);
|
|
78
82
|
case 'shopify':
|
package/src/index.ts
CHANGED
|
@@ -38,6 +38,7 @@ export {
|
|
|
38
38
|
// Capabilities
|
|
39
39
|
// ============================================
|
|
40
40
|
export {
|
|
41
|
+
MOCK_CAPABILITIES,
|
|
41
42
|
MEDUSA_CAPABILITIES,
|
|
42
43
|
SHOPIFY_CAPABILITIES,
|
|
43
44
|
BIGCOMMERCE_CAPABILITIES,
|
|
@@ -192,6 +193,8 @@ export {
|
|
|
192
193
|
export {
|
|
193
194
|
MedusaProvider,
|
|
194
195
|
type MedusaProviderConfig,
|
|
196
|
+
MockProvider,
|
|
197
|
+
type MockProviderConfig,
|
|
195
198
|
ShopifyProvider,
|
|
196
199
|
type ShopifyProviderConfig,
|
|
197
200
|
BigCommerceProvider,
|
package/src/providers/index.ts
CHANGED
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Cart,
|
|
3
|
+
CartLineInput,
|
|
4
|
+
CartLineUpdate,
|
|
5
|
+
Collection,
|
|
6
|
+
CollectionProductsOptions,
|
|
7
|
+
GetCategoriesOptions,
|
|
8
|
+
GetCollectionsOptions,
|
|
9
|
+
GetOrdersOptions,
|
|
10
|
+
GetProductsOptions,
|
|
11
|
+
InventoryLevel,
|
|
12
|
+
Order,
|
|
13
|
+
PaymentMethodsOptions,
|
|
14
|
+
Product,
|
|
15
|
+
ProductCategory,
|
|
16
|
+
Region,
|
|
17
|
+
ShippingMethod,
|
|
18
|
+
StockLocation,
|
|
19
|
+
StockValidation,
|
|
20
|
+
StorefrontContext,
|
|
21
|
+
FulfillmentOption,
|
|
22
|
+
PaymentMethod,
|
|
23
|
+
} from '@thorprovider/types';
|
|
24
|
+
import type { CommerceProvider, ProviderConfig } from '../../types/provider';
|
|
25
|
+
import { ProviderAPIError, ResourceNotFoundError } from '../../core/errors';
|
|
26
|
+
import { MOCK_CAPABILITIES } from '../../types/capabilities.types';
|
|
27
|
+
import {
|
|
28
|
+
buildMockCart,
|
|
29
|
+
completeMockCheckout,
|
|
30
|
+
createMockCart,
|
|
31
|
+
getMockAdminOrders,
|
|
32
|
+
getMockAdminRegions,
|
|
33
|
+
getMockCategories,
|
|
34
|
+
getMockCategory,
|
|
35
|
+
getMockCollection,
|
|
36
|
+
getMockCollections,
|
|
37
|
+
getMockCartState,
|
|
38
|
+
getMockFulfillmentOptions,
|
|
39
|
+
getMockInventoryLevels,
|
|
40
|
+
getMockOrder,
|
|
41
|
+
getMockPaymentMethods,
|
|
42
|
+
getMockProduct,
|
|
43
|
+
getMockProductRecommendations,
|
|
44
|
+
getMockProducts,
|
|
45
|
+
getMockRegionCountries,
|
|
46
|
+
getMockRegions,
|
|
47
|
+
getMockShippingMethods,
|
|
48
|
+
getMockStockLocations,
|
|
49
|
+
saveMockCartState,
|
|
50
|
+
saveMockOrder,
|
|
51
|
+
validateMockCartStock,
|
|
52
|
+
} from './mock';
|
|
53
|
+
|
|
54
|
+
export interface MockProviderConfig extends ProviderConfig {
|
|
55
|
+
currencyCode?: string;
|
|
56
|
+
siteUrl?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class MockProvider implements CommerceProvider {
|
|
60
|
+
readonly name = 'mock';
|
|
61
|
+
readonly version = '1.0.0';
|
|
62
|
+
readonly capabilities = MOCK_CAPABILITIES;
|
|
63
|
+
readonly isMock = true;
|
|
64
|
+
|
|
65
|
+
private readonly currencyCode: string;
|
|
66
|
+
private readonly siteUrl: string;
|
|
67
|
+
|
|
68
|
+
constructor(config: MockProviderConfig = {}) {
|
|
69
|
+
this.currencyCode = config.currencyCode ?? 'USD';
|
|
70
|
+
this.siteUrl = config.siteUrl ?? 'https://mock.thorcommerce.local';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private hasVariant(merchandiseId: string, regionId?: string): boolean {
|
|
74
|
+
return getMockProducts({
|
|
75
|
+
currencyCode: this.currencyCode,
|
|
76
|
+
regionId,
|
|
77
|
+
}).some((product) =>
|
|
78
|
+
product.variants.some((variant) => variant.id === merchandiseId)
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async getProduct(handle: string, options?: { regionId?: string }): Promise<Product | undefined> {
|
|
83
|
+
return getMockProduct(handle, options?.regionId);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async getProducts(options?: GetProductsOptions): Promise<Product[]> {
|
|
87
|
+
return getMockProducts({
|
|
88
|
+
currencyCode: this.currencyCode,
|
|
89
|
+
query: options?.query,
|
|
90
|
+
sortKey: options?.sortKey,
|
|
91
|
+
reverse: options?.reverse,
|
|
92
|
+
first: options?.first,
|
|
93
|
+
skip: options?.skip,
|
|
94
|
+
categoryIds: options?.categoryIds,
|
|
95
|
+
collectionIds: options?.collectionIds,
|
|
96
|
+
regionId: options?.regionId,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async getProductRecommendations(productId: string): Promise<Product[]> {
|
|
101
|
+
return getMockProductRecommendations(productId);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async getCollection(handle: string): Promise<Collection | undefined> {
|
|
105
|
+
return getMockCollection(handle);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async getCollections(options?: GetCollectionsOptions): Promise<Collection[]> {
|
|
109
|
+
const collections = getMockCollections();
|
|
110
|
+
const query = options?.query?.trim().toLowerCase();
|
|
111
|
+
const filtered = query
|
|
112
|
+
? collections.filter((collection) =>
|
|
113
|
+
[collection.title, collection.description, collection.handle]
|
|
114
|
+
.join(' ')
|
|
115
|
+
.toLowerCase()
|
|
116
|
+
.includes(query)
|
|
117
|
+
)
|
|
118
|
+
: collections;
|
|
119
|
+
|
|
120
|
+
return filtered.slice(0, options?.limit ?? filtered.length);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async getCollectionProducts(handle: string, options?: CollectionProductsOptions): Promise<Product[]> {
|
|
124
|
+
const collection = getMockCollection(handle);
|
|
125
|
+
if (!collection) {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return getMockProducts({
|
|
130
|
+
currencyCode: this.currencyCode,
|
|
131
|
+
collectionIds: [collection.id],
|
|
132
|
+
sortKey: options?.sortKey,
|
|
133
|
+
reverse: options?.reverse,
|
|
134
|
+
first: options?.first,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async getCategory(idOrHandle: string): Promise<ProductCategory | undefined> {
|
|
139
|
+
return getMockCategory(idOrHandle);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async getCategories(options?: GetCategoriesOptions): Promise<ProductCategory[]> {
|
|
143
|
+
const categories = getMockCategories();
|
|
144
|
+
const query = options?.query?.trim().toLowerCase();
|
|
145
|
+
|
|
146
|
+
let filtered = query
|
|
147
|
+
? categories.filter((category) =>
|
|
148
|
+
[category.name, category.description ?? '', category.handle]
|
|
149
|
+
.join(' ')
|
|
150
|
+
.toLowerCase()
|
|
151
|
+
.includes(query)
|
|
152
|
+
)
|
|
153
|
+
: categories;
|
|
154
|
+
|
|
155
|
+
if (options?.parentCategoryId) {
|
|
156
|
+
filtered = filtered.filter((category) => category.parentCategoryId === options.parentCategoryId);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return filtered.slice(0, options?.limit ?? filtered.length);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async getCategoryProducts(handle: string, options?: CollectionProductsOptions): Promise<Product[]> {
|
|
163
|
+
const category = getMockCategory(handle);
|
|
164
|
+
if (!category) {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return getMockProducts({
|
|
169
|
+
currencyCode: this.currencyCode,
|
|
170
|
+
categoryIds: [category.id],
|
|
171
|
+
sortKey: options?.sortKey,
|
|
172
|
+
reverse: options?.reverse,
|
|
173
|
+
first: options?.first,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async createCart(options?: { regionId?: string }): Promise<Cart> {
|
|
178
|
+
const cart = createMockCart(options?.regionId);
|
|
179
|
+
return buildMockCart(cart, this.currencyCode, this.siteUrl);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async getCart(cartId: string): Promise<Cart | undefined> {
|
|
183
|
+
const cart = getMockCartState(cartId);
|
|
184
|
+
return cart ? buildMockCart(cart, this.currencyCode, this.siteUrl) : undefined;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async addToCart(cartId: string, lines: CartLineInput[]): Promise<Cart> {
|
|
188
|
+
const cart = getMockCartState(cartId);
|
|
189
|
+
if (!cart) {
|
|
190
|
+
throw new ResourceNotFoundError('cart', cartId);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
for (const line of lines) {
|
|
194
|
+
if (!this.hasVariant(line.merchandiseId, cart.regionId)) {
|
|
195
|
+
throw new ResourceNotFoundError('variant', line.merchandiseId);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const nextLines = [...cart.lines];
|
|
200
|
+
for (const line of lines) {
|
|
201
|
+
const existing = nextLines.find((item) => item.merchandiseId === line.merchandiseId);
|
|
202
|
+
if (existing) {
|
|
203
|
+
existing.quantity += line.quantity;
|
|
204
|
+
} else {
|
|
205
|
+
nextLines.push({
|
|
206
|
+
id: `mock-line-${line.merchandiseId}`,
|
|
207
|
+
merchandiseId: line.merchandiseId,
|
|
208
|
+
quantity: line.quantity,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const saved = saveMockCartState({ ...cart, lines: nextLines });
|
|
214
|
+
return buildMockCart(saved, this.currencyCode, this.siteUrl);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async removeFromCart(cartId: string, lineIds: string[]): Promise<Cart> {
|
|
218
|
+
const cart = getMockCartState(cartId);
|
|
219
|
+
if (!cart) {
|
|
220
|
+
throw new ResourceNotFoundError('cart', cartId);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const saved = saveMockCartState({
|
|
224
|
+
...cart,
|
|
225
|
+
lines: cart.lines.filter((line) => !lineIds.includes(line.id)),
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
return buildMockCart(saved, this.currencyCode, this.siteUrl);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async updateCart(cartId: string, lines: CartLineUpdate[]): Promise<Cart> {
|
|
232
|
+
const cart = getMockCartState(cartId);
|
|
233
|
+
if (!cart) {
|
|
234
|
+
throw new ResourceNotFoundError('cart', cartId);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
for (const line of lines) {
|
|
238
|
+
if (!this.hasVariant(line.merchandiseId, cart.regionId)) {
|
|
239
|
+
throw new ResourceNotFoundError('variant', line.merchandiseId);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const updates = new Map(lines.map((line) => [line.id, line]));
|
|
244
|
+
const nextLines = cart.lines
|
|
245
|
+
.map((line) => {
|
|
246
|
+
const update = updates.get(line.id);
|
|
247
|
+
if (!update) {
|
|
248
|
+
return line;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
...line,
|
|
253
|
+
merchandiseId: update.merchandiseId,
|
|
254
|
+
quantity: update.quantity,
|
|
255
|
+
};
|
|
256
|
+
})
|
|
257
|
+
.filter((line) => line.quantity > 0);
|
|
258
|
+
|
|
259
|
+
const saved = saveMockCartState({ ...cart, lines: nextLines });
|
|
260
|
+
return buildMockCart(saved, this.currencyCode, this.siteUrl);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async getRegions(): Promise<Array<{ id: string; name: string; currency_code: string; countries?: Array<{ id: string; iso_2: string; name: string; display_name?: string }> }>> {
|
|
264
|
+
return getMockRegions();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async completeCheckout(cartId: string): Promise<Order> {
|
|
268
|
+
const cart = getMockCartState(cartId);
|
|
269
|
+
if (!cart) {
|
|
270
|
+
throw new ResourceNotFoundError('cart', cartId);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const order = completeMockCheckout(cart, this.currencyCode, this.siteUrl);
|
|
274
|
+
return order;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async getShippingMethods(cartId: string): Promise<ShippingMethod[]> {
|
|
278
|
+
const cart = getMockCartState(cartId);
|
|
279
|
+
return getMockShippingMethods(cart?.regionId);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async getRegionCountries(regionId: string) {
|
|
283
|
+
return getMockRegionCountries(regionId);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async addShippingMethod(cartId: string, methodId: string): Promise<Cart> {
|
|
287
|
+
const cart = getMockCartState(cartId);
|
|
288
|
+
if (!cart) {
|
|
289
|
+
throw new ResourceNotFoundError('cart', cartId);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const allowed = getMockShippingMethods(cart.regionId).some((method) => method.id === methodId);
|
|
293
|
+
if (!allowed) {
|
|
294
|
+
throw new ProviderAPIError(`Unknown mock shipping method: ${methodId}`, 'mock', 400);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const saved = saveMockCartState({ ...cart, shippingMethodId: methodId });
|
|
298
|
+
return buildMockCart(saved, this.currencyCode, this.siteUrl);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async initiatePaymentSession(cartId: string, providerId: string): Promise<void> {
|
|
302
|
+
const cart = getMockCartState(cartId);
|
|
303
|
+
if (!cart) {
|
|
304
|
+
throw new ResourceNotFoundError('cart', cartId);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const allowed = getMockPaymentMethods(this.currencyCode).some((method) => method.id === providerId);
|
|
308
|
+
if (!allowed) {
|
|
309
|
+
throw new ProviderAPIError(`Unknown mock payment method: ${providerId}`, 'mock', 400);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
saveMockCartState({ ...cart, paymentProviderId: providerId });
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async applyDiscountCode(cartId: string, code: string): Promise<Cart> {
|
|
316
|
+
const cart = getMockCartState(cartId);
|
|
317
|
+
if (!cart) {
|
|
318
|
+
throw new ResourceNotFoundError('cart', cartId);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const normalized = code.trim();
|
|
322
|
+
if (!normalized) {
|
|
323
|
+
throw new ProviderAPIError('Discount code is required.', 'mock', 400);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const saved = saveMockCartState({ ...cart, discountCode: normalized });
|
|
327
|
+
return buildMockCart(saved, this.currencyCode, this.siteUrl);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async removeDiscountCode(cartId: string, _code: string): Promise<Cart> {
|
|
331
|
+
const cart = getMockCartState(cartId);
|
|
332
|
+
if (!cart) {
|
|
333
|
+
throw new ResourceNotFoundError('cart', cartId);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const saved = saveMockCartState({ ...cart, discountCode: undefined });
|
|
337
|
+
return buildMockCart(saved, this.currencyCode, this.siteUrl);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async getOrders(_options: GetOrdersOptions): Promise<Order[]> {
|
|
341
|
+
return getMockAdminOrders().orders;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
async getPaymentMethods(options: PaymentMethodsOptions): Promise<PaymentMethod[]> {
|
|
345
|
+
return getMockPaymentMethods(options.currencyCode ?? this.currencyCode);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async getStockLocations(): Promise<StockLocation[]> {
|
|
349
|
+
return getMockStockLocations();
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async checkInventoryLevels(variantId: string): Promise<InventoryLevel[]> {
|
|
353
|
+
return getMockInventoryLevels(variantId);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async validateCartStock(cartId: string): Promise<StockValidation> {
|
|
357
|
+
const cart = getMockCartState(cartId);
|
|
358
|
+
if (!cart) {
|
|
359
|
+
throw new ResourceNotFoundError('cart', cartId);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return validateMockCartStock(cart);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
async getFulfillmentOptions(cartId: string, _salesChannelId: string): Promise<FulfillmentOption[]> {
|
|
366
|
+
const cart = getMockCartState(cartId);
|
|
367
|
+
return getMockFulfillmentOptions(cart?.regionId);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async cancelOrder(orderId: string): Promise<Order> {
|
|
371
|
+
const order = getMockOrder(orderId);
|
|
372
|
+
if (!order) {
|
|
373
|
+
throw new ResourceNotFoundError('order', orderId);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return saveMockOrder({
|
|
377
|
+
...order,
|
|
378
|
+
status: 'cancelled',
|
|
379
|
+
updatedAt: new Date().toISOString(),
|
|
380
|
+
cancelledAt: new Date().toISOString(),
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
async createFulfillment(_orderId: string, _items?: Array<{ id: string; quantity: number }>): Promise<void> {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
async createRefund(orderId: string, _amount?: number, _reason?: string): Promise<void> {
|
|
389
|
+
const order = getMockOrder(orderId);
|
|
390
|
+
if (!order) {
|
|
391
|
+
throw new ResourceNotFoundError('order', orderId);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
saveMockOrder({
|
|
395
|
+
...order,
|
|
396
|
+
status: 'refunded',
|
|
397
|
+
paymentStatus: 'refunded',
|
|
398
|
+
updatedAt: new Date().toISOString(),
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
async getAdminOrders(): Promise<{ orders: Order[]; count: number }> {
|
|
403
|
+
return getMockAdminOrders();
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async getAdminRegions(): Promise<Region[]> {
|
|
407
|
+
return getMockAdminRegions();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
async getStorefrontContext(): Promise<StorefrontContext> {
|
|
411
|
+
return {
|
|
412
|
+
id: 'mock-storefront',
|
|
413
|
+
name: 'Mock Storefront',
|
|
414
|
+
platformType: 'mock',
|
|
415
|
+
requiresProductScoping: false,
|
|
416
|
+
currencyCode: this.currencyCode,
|
|
417
|
+
locale: 'en-US',
|
|
418
|
+
metadata: {
|
|
419
|
+
mode: 'design',
|
|
420
|
+
},
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
isStorefrontRequired(): boolean {
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
}
|