@thorprovider/adapters 3.0.2

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 (95) hide show
  1. package/.turbo/turbo-build.log +18 -0
  2. package/.turbo/turbo-test.log +1 -0
  3. package/.turbo/turbo-type-check.log +0 -0
  4. package/AGENTS.md +1 -0
  5. package/CHANGELOG.md +271 -0
  6. package/README.md +634 -0
  7. package/dist/.tsbuildinfo +1 -0
  8. package/dist/index.d.mts +2524 -0
  9. package/dist/index.d.ts +2524 -0
  10. package/dist/index.js +8136 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/index.mjs +8045 -0
  13. package/dist/index.mjs.map +1 -0
  14. package/docs/RETRY_SOLUTION_SUMMARY.md +55 -0
  15. package/docs/authentication.md +165 -0
  16. package/docs/capabilities-system.md +143 -0
  17. package/docs/category-customization.md +309 -0
  18. package/docs/fetch-reliability-solution.md +399 -0
  19. package/docs/getting-started.md +154 -0
  20. package/docs/multi-tenant-channel-scoping.md +225 -0
  21. package/docs/profile-management.md +147 -0
  22. package/docs/providers-overview.md +85 -0
  23. package/docs/services-overview.md +135 -0
  24. package/docs/update-documentation.md +113 -0
  25. package/package.json +43 -0
  26. package/publish.log +93 -0
  27. package/quick-reference.md +169 -0
  28. package/src/admin/AdminAuthService.ts +88 -0
  29. package/src/admin/AdminSessionService.ts +91 -0
  30. package/src/admin/MetricsService.ts +145 -0
  31. package/src/admin/index.ts +13 -0
  32. package/src/backend/BackendMetadataAdapter.ts +88 -0
  33. package/src/backend/factory.ts +107 -0
  34. package/src/backend/index.ts +12 -0
  35. package/src/backend/medusa/MedusaMetadataAdapter.ts +139 -0
  36. package/src/core/errors.js +161 -0
  37. package/src/core/errors.ts +132 -0
  38. package/src/core/fetch-utils.js +240 -0
  39. package/src/core/fetch-utils.ts +227 -0
  40. package/src/core/index.ts +8 -0
  41. package/src/core/logger.js +63 -0
  42. package/src/core/logger.ts +37 -0
  43. package/src/factory/index.ts +102 -0
  44. package/src/index.ts +205 -0
  45. package/src/lib/money-transform.ts +80 -0
  46. package/src/metadata/channel-scope-validation.js +136 -0
  47. package/src/metadata/channel-scope-validation.ts +151 -0
  48. package/src/metadata/channel-scope.js +174 -0
  49. package/src/metadata/channel-scope.ts +215 -0
  50. package/src/metadata/index.js +31 -0
  51. package/src/metadata/index.ts +37 -0
  52. package/src/providers/bigcommerce/bigcommerce.ts +6 -0
  53. package/src/providers/bigcommerce/index.ts +315 -0
  54. package/src/providers/bigcommerce/transforms.ts +227 -0
  55. package/src/providers/index.ts +22 -0
  56. package/src/providers/magento/index.ts +387 -0
  57. package/src/providers/magento/magento.ts +5 -0
  58. package/src/providers/magento/queries.ts +267 -0
  59. package/src/providers/magento/transforms.ts +249 -0
  60. package/src/providers/medusa/auth.js +601 -0
  61. package/src/providers/medusa/auth.ts +496 -0
  62. package/src/providers/medusa/fulfillment-planner.ts +225 -0
  63. package/src/providers/medusa/index.js +2485 -0
  64. package/src/providers/medusa/index.ts +4239 -0
  65. package/src/providers/medusa/medusa.ts +8 -0
  66. package/src/providers/medusa/payment.js +265 -0
  67. package/src/providers/medusa/payment.ts +269 -0
  68. package/src/providers/medusa/transforms.js +505 -0
  69. package/src/providers/medusa/transforms.ts +625 -0
  70. package/src/providers/shopify/index.ts +444 -0
  71. package/src/providers/shopify/mutations.ts +102 -0
  72. package/src/providers/shopify/queries.ts +324 -0
  73. package/src/providers/shopify/shopify.ts +7 -0
  74. package/src/providers/shopify/transforms.ts +270 -0
  75. package/src/providers/spree/index.ts +344 -0
  76. package/src/providers/spree/spree.ts +5 -0
  77. package/src/providers/spree/transforms.ts +254 -0
  78. package/src/providers/woocommerce/index.ts +310 -0
  79. package/src/providers/woocommerce/transforms.ts +254 -0
  80. package/src/providers/woocommerce/woocommerce.ts +5 -0
  81. package/src/resilience/README.md +299 -0
  82. package/src/resilience/circuit-breaker.ts +222 -0
  83. package/src/resilience/index.ts +10 -0
  84. package/src/types/capabilities.types.js +141 -0
  85. package/src/types/capabilities.types.ts +366 -0
  86. package/src/types/index.ts +24 -0
  87. package/src/types/provider.js +9 -0
  88. package/src/types/provider.ts +9 -0
  89. package/src/types/ui-responses.ts +261 -0
  90. package/src/utils/city-coordinates.ts +40 -0
  91. package/src/utils/geolocation.ts +52 -0
  92. package/src/utils/payment-utils.js +152 -0
  93. package/src/utils/payment-utils.ts +203 -0
  94. package/tsconfig.json +15 -0
  95. package/tsup.config.ts +14 -0
@@ -0,0 +1,299 @@
1
+ # Resilience Patterns
2
+
3
+ **Framework-agnostic resilience patterns for robust backend integration.**
4
+
5
+ ---
6
+
7
+ ## 🎯 Overview
8
+
9
+ This module provides enterprise-grade resilience patterns for handling backend failures gracefully. All patterns are framework-agnostic and can be used in any JavaScript/TypeScript environment.
10
+
11
+ ---
12
+
13
+ ## 📦 Export
14
+
15
+ ```typescript
16
+ import { CircuitBreaker, backendCircuitBreaker } from '@thorprovider/adapters/resilience';
17
+ ```
18
+
19
+ ---
20
+
21
+ ## 🔌 Circuit Breaker Pattern
22
+
23
+ The Circuit Breaker pattern prevents cascading failures when the backend is unavailable by failing fast after a threshold of consecutive failures.
24
+
25
+ ### How It Works
26
+
27
+ ```
28
+ ┌──────────┐
29
+ │ CLOSED │ ◄─── Normal operation (requests pass through)
30
+ └────┬─────┘
31
+ │ 5 failures
32
+ ↓
33
+ ┌──────────┐
34
+ │ OPEN │ ◄─── Failing fast (requests rejected immediately)
35
+ └────┬─────┘
36
+ │ 60 seconds timeout
37
+ ↓
38
+ ┌──────────┐
39
+ │ HALF_OPEN│ ◄─── Testing recovery (1 request allowed)
40
+ └────┬─────┘
41
+ │ Success? → CLOSED
42
+ │ Failure? → OPEN
43
+ ```
44
+
45
+ ### States
46
+
47
+ | State | Behavior | Purpose |
48
+ |-------|----------|---------|
49
+ | **CLOSED** | All requests pass through normally | Normal operation |
50
+ | **OPEN** | Requests fail immediately with fallback | Prevent hammering failed backend |
51
+ | **HALF_OPEN** | Single test request allowed | Check if backend recovered |
52
+
53
+ ### Basic Usage
54
+
55
+ ```typescript
56
+ import { CircuitBreaker } from '@thorprovider/adapters/resilience';
57
+
58
+ const breaker = new CircuitBreaker({
59
+ failureThreshold: 5, // Open after 5 failures
60
+ resetTimeout: 60000, // Try recovery after 1 minute
61
+ name: 'my-api'
62
+ });
63
+
64
+ // With fallback
65
+ const data = await breaker.execute(
66
+ () => fetch('/api/data'),
67
+ { default: 'value' } // Fallback if circuit is open
68
+ );
69
+
70
+ // Without fallback (throws on failure)
71
+ try {
72
+ const result = await breaker.execute(() => apiCall());
73
+ } catch (error) {
74
+ // Circuit is open or API call failed
75
+ }
76
+ ```
77
+
78
+ ### Configuration Options
79
+
80
+ ```typescript
81
+ interface CircuitBreakerConfig {
82
+ /** Number of failures before opening circuit */
83
+ failureThreshold: number;
84
+
85
+ /** Time in ms to wait before attempting recovery */
86
+ resetTimeout: number;
87
+
88
+ /** Name for logging/监控 */
89
+ name?: string;
90
+ }
91
+ ```
92
+
93
+ ### Monitoring
94
+
95
+ ```typescript
96
+ // Get current state
97
+ console.log(breaker.state); // 'CLOSED' | 'OPEN' | 'HALF_OPEN'
98
+
99
+ // Get statistics
100
+ const stats = breaker.stats;
101
+ console.log({
102
+ state: stats.state,
103
+ failureCount: stats.failureCount,
104
+ totalRequests: stats.totalRequests,
105
+ totalFailures: stats.totalFailures,
106
+ totalSuccesses: stats.totalSuccesses
107
+ });
108
+
109
+ // Manual reset (force recovery)
110
+ breaker.manualReset();
111
+ ```
112
+
113
+ ---
114
+
115
+ ## 🌐 Global Backend Circuit Breaker
116
+
117
+ A pre-configured global instance for backend API calls:
118
+
119
+ ```typescript
120
+ import { backendCircuitBreaker } from '@thorprovider/adapters/resilience';
121
+
122
+ // Use in adapters
123
+ async function getProducts() {
124
+ return backendCircuitBreaker.execute(
125
+ () => fetch('/api/products'),
126
+ [] // Empty array fallback
127
+ );
128
+ }
129
+ ```
130
+
131
+ **Configuration:**
132
+ - `failureThreshold`: 5 failures
133
+ - `resetTimeout`: 60,000ms (1 minute)
134
+ - `name`: 'backend-api'
135
+
136
+ ---
137
+
138
+ ## 🎨 Usage Patterns
139
+
140
+ ### Pattern 1: L2 Adapter with Graceful Degradation
141
+
142
+ ```typescript
143
+ // packages/adapters/src/medusa/product-adapter.ts
144
+ import { backendCircuitBreaker } from '@thorprovider/adapters/resilience';
145
+
146
+ export async function getProducts(options: GetProductsOptions) {
147
+ return backendCircuitBreaker.execute(
148
+ () => medusaClient.products.list(options),
149
+ [] // Return empty array if backend is down
150
+ );
151
+ }
152
+ ```
153
+
154
+ **Benefits:**
155
+ - ✅ Frontend stays functional with empty state
156
+ - ✅ User sees "No products" instead of error page
157
+ - ✅ Backend gets time to recover
158
+
159
+ ### Pattern 2: L5 Server Component with Fallback UI
160
+
161
+ ```typescript
162
+ // packages/core/app/products/page.tsx
163
+ import { backendCircuitBreaker } from '@thorprovider/adapters/resilience';
164
+ import { getProducts } from '@thorprovider/adapters/medusa';
165
+
166
+ export default async function ProductsPage() {
167
+ const products = await backendCircuitBreaker.execute(
168
+ () => getProducts(),
169
+ [] // Fallback to empty array
170
+ );
171
+
172
+ if (products.length === 0) {
173
+ return <EmptyState message="Products temporarily unavailable" />;
174
+ }
175
+
176
+ return <ProductsGrid products={products} />;
177
+ }
178
+ ```
179
+
180
+ ### Pattern 3: Critical Operations (No Fallback)
181
+
182
+ ```typescript
183
+ // Critical operations should fail explicitly
184
+ async function completeCheckout(data: CheckoutData) {
185
+ try {
186
+ return await backendCircuitBreaker.execute(
187
+ () => medusaClient.carts.complete(data)
188
+ // NO fallback - we want this to fail if backend is down
189
+ );
190
+ } catch (error) {
191
+ // Show error message to user
192
+ throw new Error('Checkout unavailable. Please try again later.');
193
+ }
194
+ }
195
+ ```
196
+
197
+ ---
198
+
199
+ ## 🧪 Testing
200
+
201
+ ### Unit Test Example
202
+
203
+ ```typescript
204
+ import { CircuitBreaker } from '@thorprovider/adapters/resilience';
205
+
206
+ describe('CircuitBreaker', () => {
207
+ it('opens after threshold failures', async () => {
208
+ const breaker = new CircuitBreaker({
209
+ failureThreshold: 3,
210
+ resetTimeout: 1000,
211
+ });
212
+
213
+ const mockFn = jest.fn().mockRejectedValue(new Error('fail'));
214
+
215
+ // 3 failures
216
+ await expect(breaker.execute(mockFn)).rejects.toThrow();
217
+ await expect(breaker.execute(mockFn)).rejects.toThrow();
218
+ await expect(breaker.execute(mockFn)).rejects.toThrow();
219
+
220
+ expect(breaker.state).toBe('OPEN');
221
+ });
222
+
223
+ it('uses fallback when circuit is open', async () => {
224
+ const breaker = new CircuitBreaker({
225
+ failureThreshold: 1,
226
+ resetTimeout: 1000,
227
+ });
228
+
229
+ const mockFn = jest.fn().mockRejectedValue(new Error('fail'));
230
+
231
+ // Open circuit
232
+ await breaker.execute(mockFn, 'fallback').catch(() => {});
233
+
234
+ // Should return fallback immediately
235
+ const result = await breaker.execute(mockFn, 'fallback');
236
+ expect(result).toBe('fallback');
237
+ expect(mockFn).toHaveBeenCalledTimes(1); // Not called again
238
+ });
239
+ });
240
+ ```
241
+
242
+ ---
243
+
244
+ ## 📐 Architecture Decisions
245
+
246
+ ### Why L2 (Adapters)?
247
+
248
+ 1. **Framework Agnostic**: Works in Next.js, Remix, Vite, Node.js
249
+ 2. **Reusable**: All starters can use the same circuit breaker
250
+ 3. **No Dependencies**: Zero external dependencies (pure TypeScript)
251
+ 4. **Type Safe**: Full TypeScript support
252
+
253
+ ### Why Not L5 (Starters)?
254
+
255
+ - ❌ Not reusable across starters
256
+ - ❌ Mixed with framework-specific code
257
+ - ❌ Harder to test in isolation
258
+
259
+ ---
260
+
261
+ ## 🚀 Best Practices
262
+
263
+ ### DO ✅
264
+
265
+ - Use for all backend API calls
266
+ - Provide sensible fallbacks for non-critical operations
267
+ - Monitor circuit breaker stats in production
268
+ - Use separate instances for different services
269
+
270
+ ### DON'T ❌
271
+
272
+ - Don't use for client-side only operations
273
+ - Don't ignore OPEN state errors silently
274
+ - Don't set failureThreshold too low (causes flapping)
275
+ - Don't set resetTimeout too high (slow recovery)
276
+
277
+ ---
278
+
279
+ ## 🔮 Future Enhancements
280
+
281
+ - [ ] **Retry Pattern**: Exponential backoff for transient failures
282
+ - [ ] **Bulkhead Pattern**: Isolate resource pools
283
+ - [ ] **Rate Limiting**: Prevent API abuse
284
+ - [ ] **Metrics Export**: Prometheus/Datadog integration
285
+ - [ ] **Health Check Integration**: Auto-open circuit on health check fail
286
+
287
+ ---
288
+
289
+ ## 📚 References
290
+
291
+ - [Martin Fowler - Circuit Breaker](https://martinfowler.com/bliki/CircuitBreaker.html)
292
+ - [Microsoft - Resilience Patterns](https://docs.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker)
293
+ - [Netflix Hystrix](https://github.com/Netflix/Hystrix/wiki)
294
+
295
+ ---
296
+
297
+ **Last Updated**: March 24, 2026
298
+ **Status**: ✅ Active
299
+ **Layer**: L2 (Adapters)
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Circuit Breaker Pattern Implementation
3
+ *
4
+ * Prevents cascading failures when the backend is unavailable.
5
+ * Implements the Circuit Breaker pattern as described by Martin Fowler.
6
+ *
7
+ * States:
8
+ * - CLOSED: Normal operation, requests pass through
9
+ * - OPEN: Too many failures, requests fail fast
10
+ * - HALF_OPEN: Testing if backend has recovered
11
+ *
12
+ * @see https://martinfowler.com/bliki/CircuitBreaker.html
13
+ */
14
+
15
+ export type CircuitState = 'CLOSED' | 'OPEN' | 'HALF_OPEN';
16
+
17
+ export interface CircuitBreakerConfig {
18
+ /** Number of failures before opening circuit */
19
+ failureThreshold: number;
20
+ /** Time in ms to wait before attempting recovery */
21
+ resetTimeout: number;
22
+ /** Name for logging/monitoring */
23
+ name?: string;
24
+ }
25
+
26
+ export interface CircuitBreakerStats {
27
+ state: CircuitState;
28
+ failureCount: number;
29
+ lastFailureTime: number | null;
30
+ totalRequests: number;
31
+ totalFailures: number;
32
+ totalSuccesses: number;
33
+ }
34
+
35
+ /**
36
+ * Circuit Breaker Class
37
+ *
38
+ * Monitors backend calls and prevents cascading failures by
39
+ * failing fast when the backend is unhealthy.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * const breaker = new CircuitBreaker({
44
+ * failureThreshold: 3,
45
+ * resetTimeout: 60000, // 1 minute
46
+ * name: 'backend-api'
47
+ * });
48
+ *
49
+ * const result = await breaker.execute(
50
+ * () => fetch('/api/data'),
51
+ * fallbackData
52
+ * );
53
+ * ```
54
+ */
55
+ export class CircuitBreaker {
56
+ private failureCount = 0;
57
+ private lastFailureTime: number | null = null;
58
+ private totalRequests = 0;
59
+ private totalFailures = 0;
60
+ private totalSuccesses = 0;
61
+
62
+ constructor(private config: CircuitBreakerConfig) {
63
+ this.config = {
64
+ failureThreshold: config.failureThreshold || 5,
65
+ resetTimeout: config.resetTimeout || 60000, // 1 minute default
66
+ name: config.name || 'circuit-breaker',
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Get current circuit state
72
+ */
73
+ get state(): CircuitState {
74
+ if (this.failureCount >= this.config.failureThreshold) {
75
+ // Circuit is open (failing fast)
76
+ if (this.shouldAttemptReset()) {
77
+ return 'HALF_OPEN'; // Try to recover
78
+ }
79
+ return 'OPEN';
80
+ }
81
+ return 'CLOSED'; // Normal operation
82
+ }
83
+
84
+ /**
85
+ * Get circuit breaker statistics
86
+ */
87
+ get stats(): CircuitBreakerStats {
88
+ return {
89
+ state: this.state,
90
+ failureCount: this.failureCount,
91
+ lastFailureTime: this.lastFailureTime,
92
+ totalRequests: this.totalRequests,
93
+ totalFailures: this.totalFailures,
94
+ totalSuccesses: this.totalSuccesses,
95
+ };
96
+ }
97
+
98
+ /**
99
+ * Execute a function with circuit breaker protection
100
+ *
101
+ * @param fn - Function to execute
102
+ * @param fallback - Fallback value if circuit is open
103
+ * @returns Result from fn or fallback
104
+ */
105
+ async execute<T>(
106
+ fn: () => Promise<T>,
107
+ fallback?: T
108
+ ): Promise<T> {
109
+ this.totalRequests++;
110
+
111
+ const currentState = this.state;
112
+
113
+ // If circuit is OPEN and we have a fallback, return it immediately
114
+ if (currentState === 'OPEN') {
115
+ this.log('Circuit is OPEN - failing fast');
116
+ if (fallback !== undefined) {
117
+ return fallback;
118
+ }
119
+ throw new CircuitBreakerOpenError(
120
+ `Circuit breaker "${this.config.name}" is OPEN - backend may be unavailable`
121
+ );
122
+ }
123
+
124
+ // Try to execute the function
125
+ try {
126
+ const result = await fn();
127
+ this.onSuccess();
128
+ return result;
129
+ } catch (error) {
130
+ this.onFailure();
131
+
132
+ // If we have a fallback, use it
133
+ if (fallback !== undefined) {
134
+ this.log('Returning fallback due to error:', error);
135
+ return fallback;
136
+ }
137
+
138
+ // Otherwise, re-throw the error
139
+ throw error;
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Check if circuit should attempt to reset
145
+ */
146
+ private shouldAttemptReset(): boolean {
147
+ if (!this.lastFailureTime) return false;
148
+
149
+ const timeSinceLastFailure = Date.now() - this.lastFailureTime;
150
+ return timeSinceLastFailure >= this.config.resetTimeout;
151
+ }
152
+
153
+ /**
154
+ * Record a successful call
155
+ */
156
+ private onSuccess(): void {
157
+ this.totalSuccesses++;
158
+ this.reset();
159
+ this.log('Request succeeded - circuit reset');
160
+ }
161
+
162
+ /**
163
+ * Record a failed call
164
+ */
165
+ private onFailure(): void {
166
+ this.failureCount++;
167
+ this.totalFailures++;
168
+ this.lastFailureTime = Date.now();
169
+
170
+ if (this.state === 'OPEN') {
171
+ this.log(`Circuit OPENED after ${this.failureCount} failures`);
172
+ } else {
173
+ this.log(`Failure recorded (${this.failureCount}/${this.config.failureThreshold})`);
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Reset circuit to CLOSED state
179
+ */
180
+ private reset(): void {
181
+ this.failureCount = 0;
182
+ this.lastFailureTime = null;
183
+ }
184
+
185
+ /**
186
+ * Manually reset the circuit breaker
187
+ */
188
+ public manualReset(): void {
189
+ this.reset();
190
+ this.log('Circuit manually reset');
191
+ }
192
+
193
+ /**
194
+ * Log messages (only in development)
195
+ */
196
+ private log(...args: unknown[]): void {
197
+ if (process.env.NODE_ENV === 'development') {
198
+ console.log(`[CircuitBreaker:${this.config.name}]`, ...args);
199
+ }
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Custom error for when circuit is open
205
+ */
206
+ export class CircuitBreakerOpenError extends Error {
207
+ constructor(message: string) {
208
+ super(message);
209
+ this.name = 'CircuitBreakerOpenError';
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Global circuit breaker instance for backend API calls
215
+ *
216
+ * Can be imported and used throughout the application
217
+ */
218
+ export const backendCircuitBreaker = new CircuitBreaker({
219
+ failureThreshold: 5, // Open after 5 failures
220
+ resetTimeout: 60000, // Try to recover after 1 minute
221
+ name: 'backend-api',
222
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Resilience Patterns
3
+ *
4
+ * Framework-agnostic resilience patterns for robust backend integration.
5
+ * Includes circuit breaker, retry logic, and failure handling.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+
10
+ export * from './circuit-breaker';
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ /**
3
+ * @thorprovider/adapters v2.0
4
+ * Backend Capabilities System
5
+ *
6
+ * Defines what operations each backend platform supports.
7
+ * This is a compile-time capability detection system, NOT runtime feature flags.
8
+ *
9
+ * NOTE: BackendCapabilities interface is defined in @thorprovider/types (L1: Foundation).
10
+ * This file re-exports it and provides platform-specific implementations.
11
+ *
12
+ * @see packages/core/docs/BACKEND_CAPABILITIES_ANALYSIS.md
13
+ * @see packages/core/docs/COMMERCE_PROVIDER_ARCHITECTURE.md
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.SHOPIFY_CAPABILITIES = exports.MEDUSA_CAPABILITIES = void 0;
17
+ exports.isBackendCapabilities = isBackendCapabilities;
18
+ exports.getCapabilityDescription = getCapabilityDescription;
19
+ /**
20
+ * Medusa backend capabilities
21
+ *
22
+ * @see https://docs.medusajs.com/api/store
23
+ */
24
+ exports.MEDUSA_CAPABILITIES = {
25
+ // Profile Management
26
+ updateProfile: true,
27
+ updateEmail: false, // Security restriction
28
+ changePasswordDirect: false, // Email flow only
29
+ changePasswordViaEmail: true,
30
+ deleteAccount: true, // Implemented via ThorMedusaClient
31
+ // Avatar
32
+ uploadAvatar: false,
33
+ avatarViaMetadata: true,
34
+ // Custom Attributes
35
+ customAttributes: true,
36
+ // Orders
37
+ fetchOrders: true,
38
+ cancelOrder: true,
39
+ // Addresses
40
+ manageAddresses: true,
41
+ defaultAddress: true,
42
+ // Cart/Checkout
43
+ applyDiscounts: true,
44
+ shippingCalculation: true,
45
+ guestCheckout: true,
46
+ savedPaymentMethods: false, // Depends on payment provider
47
+ listPaymentProviders: true,
48
+ regionSpecificPayments: true,
49
+ // Wishlist
50
+ wishlist: false, // Requires custom implementation
51
+ // Search
52
+ advancedSearch: true,
53
+ facetedFilters: true,
54
+ // Reviews
55
+ productReviews: false, // Requires custom implementation
56
+ // Loyalty
57
+ loyaltyProgram: false, // Requires custom implementation
58
+ };
59
+ /**
60
+ * Shopify backend capabilities
61
+ *
62
+ * @see https://shopify.dev/docs/api/storefront
63
+ */
64
+ exports.SHOPIFY_CAPABILITIES = {
65
+ // Profile Management
66
+ updateProfile: true,
67
+ updateEmail: true,
68
+ changePasswordDirect: true,
69
+ changePasswordViaEmail: true,
70
+ deleteAccount: false, // Requires admin API
71
+ // Avatar
72
+ uploadAvatar: false,
73
+ avatarViaMetadata: true,
74
+ // Custom Attributes
75
+ customAttributes: true,
76
+ // Orders
77
+ fetchOrders: true,
78
+ cancelOrder: true,
79
+ // Addresses
80
+ manageAddresses: true,
81
+ defaultAddress: true,
82
+ // Cart/Checkout
83
+ applyDiscounts: true,
84
+ shippingCalculation: true,
85
+ guestCheckout: true,
86
+ savedPaymentMethods: true,
87
+ listPaymentProviders: true,
88
+ regionSpecificPayments: false, // Global payment methods
89
+ // Wishlist
90
+ wishlist: false, // Requires app
91
+ // Search
92
+ advancedSearch: true,
93
+ facetedFilters: true,
94
+ // Reviews
95
+ productReviews: false, // Requires app
96
+ // Loyalty
97
+ loyaltyProgram: false, // Requires app
98
+ };
99
+ /**
100
+ * Type guard to check if an object is a valid BackendCapabilities
101
+ */
102
+ function isBackendCapabilities(obj) {
103
+ if (typeof obj !== 'object' || obj === null)
104
+ return false;
105
+ var capabilities = obj;
106
+ // Check required profile management capabilities
107
+ return (typeof capabilities.updateProfile === 'boolean' &&
108
+ typeof capabilities.updateEmail === 'boolean' &&
109
+ typeof capabilities.changePasswordDirect === 'boolean');
110
+ }
111
+ /**
112
+ * Get capability description for UI display
113
+ */
114
+ function getCapabilityDescription(capability) {
115
+ var descriptions = {
116
+ updateProfile: 'Update profile information (name, phone)',
117
+ updateEmail: 'Change email address',
118
+ changePasswordDirect: 'Change password directly',
119
+ changePasswordViaEmail: 'Request password reset via email',
120
+ deleteAccount: 'Delete account (self-service)',
121
+ uploadAvatar: 'Upload profile picture',
122
+ avatarViaMetadata: 'Store avatar URL in metadata',
123
+ customAttributes: 'Custom customer attributes',
124
+ fetchOrders: 'View order history',
125
+ cancelOrder: 'Cancel orders',
126
+ manageAddresses: 'Add/update/delete addresses',
127
+ defaultAddress: 'Set default address',
128
+ applyDiscounts: 'Apply discount codes',
129
+ shippingCalculation: 'Calculate shipping rates',
130
+ guestCheckout: 'Checkout without account',
131
+ savedPaymentMethods: 'Save payment methods',
132
+ listPaymentProviders: 'List available payment providers',
133
+ regionSpecificPayments: 'Region-specific payment methods',
134
+ wishlist: 'Wishlist/favorites',
135
+ advancedSearch: 'Advanced product search',
136
+ facetedFilters: 'Filter by product attributes',
137
+ productReviews: 'Product reviews and ratings',
138
+ loyaltyProgram: 'Loyalty/rewards program',
139
+ };
140
+ return descriptions[capability] || capability;
141
+ }