@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,366 @@
1
+ /**
2
+ * @thorprovider/adapters v2.0
3
+ * Backend Capabilities System
4
+ *
5
+ * Defines what operations each backend platform supports.
6
+ * This is a compile-time capability detection system, NOT runtime feature flags.
7
+ *
8
+ * NOTE: BackendCapabilities interface is defined in @thorprovider/types (L1: Foundation).
9
+ * This file re-exports it and provides platform-specific implementations.
10
+ *
11
+ * @see packages/core/docs/BACKEND_CAPABILITIES_ANALYSIS.md
12
+ * @see packages/core/docs/COMMERCE_PROVIDER_ARCHITECTURE.md
13
+ */
14
+
15
+ import type { BackendCapabilities } from '@thorprovider/types';
16
+
17
+ export type { BackendCapabilities };
18
+
19
+ /**
20
+ * Medusa backend capabilities
21
+ *
22
+ * @see https://docs.medusajs.com/api/store
23
+ */
24
+ export const MEDUSA_CAPABILITIES: Readonly<BackendCapabilities> = {
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
+
32
+ // Avatar
33
+ uploadAvatar: false,
34
+ avatarViaMetadata: true,
35
+
36
+ // Custom Attributes
37
+ customAttributes: true,
38
+
39
+ // Orders
40
+ fetchOrders: true,
41
+ cancelOrder: true,
42
+
43
+ // Addresses
44
+ manageAddresses: true,
45
+ defaultAddress: true,
46
+
47
+ // Cart/Checkout
48
+ applyDiscounts: true,
49
+ shippingCalculation: true,
50
+ guestCheckout: true,
51
+ savedPaymentMethods: false, // Depends on payment provider
52
+ listPaymentProviders: true,
53
+ regionSpecificPayments: true,
54
+
55
+ // Wishlist
56
+ wishlist: false, // Requires custom implementation
57
+
58
+ // Search
59
+ advancedSearch: true,
60
+ facetedFilters: true,
61
+
62
+ // Reviews
63
+ productReviews: false, // Requires custom implementation
64
+
65
+ // Loyalty
66
+ loyaltyProgram: false, // Requires custom implementation
67
+ };
68
+
69
+ /**
70
+ * Shopify backend capabilities
71
+ *
72
+ * @see https://shopify.dev/docs/api/storefront
73
+ */
74
+ export const SHOPIFY_CAPABILITIES: Readonly<BackendCapabilities> = {
75
+ // Profile Management
76
+ updateProfile: true,
77
+ updateEmail: true,
78
+ changePasswordDirect: true,
79
+ changePasswordViaEmail: true,
80
+ deleteAccount: false, // Requires admin API
81
+
82
+ // Avatar
83
+ uploadAvatar: false,
84
+ avatarViaMetadata: true,
85
+
86
+ // Custom Attributes
87
+ customAttributes: true,
88
+
89
+ // Orders
90
+ fetchOrders: true,
91
+ cancelOrder: true,
92
+
93
+ // Addresses
94
+ manageAddresses: true,
95
+ defaultAddress: true,
96
+
97
+ // Cart/Checkout
98
+ applyDiscounts: true,
99
+ shippingCalculation: true,
100
+ guestCheckout: true,
101
+ savedPaymentMethods: true,
102
+ listPaymentProviders: true,
103
+ regionSpecificPayments: false, // Global payment methods
104
+
105
+ // Wishlist
106
+ wishlist: false, // Requires app
107
+
108
+ // Search
109
+ advancedSearch: true,
110
+ facetedFilters: true,
111
+
112
+ // Reviews
113
+ productReviews: false, // Requires app
114
+
115
+ // Loyalty
116
+ loyaltyProgram: false, // Requires app
117
+ };
118
+
119
+ /**
120
+ * BigCommerce backend capabilities
121
+ *
122
+ * @see https://developer.bigcommerce.com/docs/rest-storefront
123
+ */
124
+ export const BIGCOMMERCE_CAPABILITIES: Readonly<BackendCapabilities> = {
125
+ // Profile Management
126
+ updateProfile: true,
127
+ updateEmail: true,
128
+ changePasswordDirect: true,
129
+ changePasswordViaEmail: true,
130
+ deleteAccount: false,
131
+
132
+ // Avatar
133
+ uploadAvatar: false,
134
+ avatarViaMetadata: false,
135
+
136
+ // Custom Attributes
137
+ customAttributes: true,
138
+
139
+ // Orders
140
+ fetchOrders: true,
141
+ cancelOrder: false, // Admin API only
142
+
143
+ // Addresses
144
+ manageAddresses: true,
145
+ defaultAddress: false,
146
+
147
+ // Cart/Checkout
148
+ applyDiscounts: true,
149
+ shippingCalculation: true,
150
+ guestCheckout: true,
151
+ savedPaymentMethods: true,
152
+ listPaymentProviders: true,
153
+ regionSpecificPayments: false,
154
+
155
+ // Wishlist
156
+ wishlist: true, // Native support
157
+
158
+ // Search
159
+ advancedSearch: true,
160
+ facetedFilters: true,
161
+
162
+ // Reviews
163
+ productReviews: true, // Native support
164
+
165
+ // Loyalty
166
+ loyaltyProgram: false,
167
+ };
168
+
169
+ /**
170
+ * WooCommerce backend capabilities
171
+ *
172
+ * @see https://developer.woocommerce.com/docs/apis/store-api
173
+ */
174
+ export const WOOCOMMERCE_CAPABILITIES: Readonly<BackendCapabilities> = {
175
+ // Profile Management
176
+ updateProfile: true,
177
+ updateEmail: true,
178
+ changePasswordDirect: false, // WordPress handles auth
179
+ changePasswordViaEmail: true,
180
+ deleteAccount: false,
181
+
182
+ // Avatar
183
+ uploadAvatar: false,
184
+ avatarViaMetadata: false, // Gravatar-based
185
+
186
+ // Custom Attributes
187
+ customAttributes: true, // Via meta_data
188
+
189
+ // Orders
190
+ fetchOrders: true,
191
+ cancelOrder: true,
192
+
193
+ // Addresses
194
+ manageAddresses: true,
195
+ defaultAddress: false, // Billing/shipping only
196
+
197
+ // Cart/Checkout
198
+ applyDiscounts: true, // Coupon codes
199
+ shippingCalculation: true,
200
+ guestCheckout: true,
201
+ savedPaymentMethods: false,
202
+ listPaymentProviders: true,
203
+ regionSpecificPayments: false,
204
+
205
+ // Wishlist
206
+ wishlist: false, // Requires plugin
207
+
208
+ // Search
209
+ advancedSearch: false, // Basic WP search
210
+ facetedFilters: false, // Requires plugin
211
+
212
+ // Reviews
213
+ productReviews: true, // Native WP reviews
214
+
215
+ // Loyalty
216
+ loyaltyProgram: false,
217
+ };
218
+
219
+ /**
220
+ * Spree Commerce backend capabilities
221
+ *
222
+ * @see https://docs.spreecommerce.org/api-reference
223
+ */
224
+ export const SPREE_CAPABILITIES: Readonly<BackendCapabilities> = {
225
+ // Profile Management
226
+ updateProfile: true,
227
+ updateEmail: true,
228
+ changePasswordDirect: true,
229
+ changePasswordViaEmail: true,
230
+ deleteAccount: false,
231
+
232
+ // Avatar
233
+ uploadAvatar: false,
234
+ avatarViaMetadata: false,
235
+
236
+ // Custom Attributes
237
+ customAttributes: false,
238
+
239
+ // Orders
240
+ fetchOrders: true,
241
+ cancelOrder: true,
242
+
243
+ // Addresses
244
+ manageAddresses: true,
245
+ defaultAddress: true,
246
+
247
+ // Cart/Checkout
248
+ applyDiscounts: true,
249
+ shippingCalculation: true,
250
+ guestCheckout: true,
251
+ savedPaymentMethods: false,
252
+ listPaymentProviders: true,
253
+ regionSpecificPayments: false,
254
+
255
+ // Wishlist
256
+ wishlist: true, // Native support
257
+
258
+ // Search
259
+ advancedSearch: false,
260
+ facetedFilters: true, // Taxon-based filtering
261
+
262
+ // Reviews
263
+ productReviews: false, // Requires extension
264
+
265
+ // Loyalty
266
+ loyaltyProgram: false,
267
+ };
268
+
269
+ /**
270
+ * Adobe Commerce / Magento backend capabilities
271
+ *
272
+ * @see https://developer.adobe.com/commerce/webapi/graphql/
273
+ */
274
+ export const MAGENTO_CAPABILITIES: Readonly<BackendCapabilities> = {
275
+ // Profile Management
276
+ updateProfile: true,
277
+ updateEmail: true,
278
+ changePasswordDirect: true,
279
+ changePasswordViaEmail: true,
280
+ deleteAccount: false, // Admin only
281
+
282
+ // Avatar
283
+ uploadAvatar: false,
284
+ avatarViaMetadata: false,
285
+
286
+ // Custom Attributes
287
+ customAttributes: true, // EAV model
288
+
289
+ // Orders
290
+ fetchOrders: true,
291
+ cancelOrder: true,
292
+
293
+ // Addresses
294
+ manageAddresses: true,
295
+ defaultAddress: true,
296
+
297
+ // Cart/Checkout
298
+ applyDiscounts: true,
299
+ shippingCalculation: true,
300
+ guestCheckout: true,
301
+ savedPaymentMethods: true,
302
+ listPaymentProviders: true,
303
+ regionSpecificPayments: true, // Store-view scoped
304
+
305
+ // Wishlist
306
+ wishlist: true, // Native support
307
+
308
+ // Search
309
+ advancedSearch: true, // Elasticsearch/OpenSearch
310
+ facetedFilters: true, // Layered navigation
311
+
312
+ // Reviews
313
+ productReviews: true, // Native support
314
+
315
+ // Loyalty
316
+ loyaltyProgram: false, // Requires extension
317
+ };
318
+
319
+ /**
320
+ * Type guard to check if an object is a valid BackendCapabilities
321
+ */
322
+ export function isBackendCapabilities(obj: unknown): obj is BackendCapabilities {
323
+ if (typeof obj !== 'object' || obj === null) return false;
324
+
325
+ const capabilities = obj as Partial<BackendCapabilities>;
326
+
327
+ // Check required profile management capabilities
328
+ return (
329
+ typeof capabilities.updateProfile === 'boolean' &&
330
+ typeof capabilities.updateEmail === 'boolean' &&
331
+ typeof capabilities.changePasswordDirect === 'boolean'
332
+ );
333
+ }
334
+
335
+ /**
336
+ * Get capability description for UI display
337
+ */
338
+ export function getCapabilityDescription(capability: keyof BackendCapabilities): string {
339
+ const descriptions: Record<keyof BackendCapabilities, string> = {
340
+ updateProfile: 'Update profile information (name, phone)',
341
+ updateEmail: 'Change email address',
342
+ changePasswordDirect: 'Change password directly',
343
+ changePasswordViaEmail: 'Request password reset via email',
344
+ deleteAccount: 'Delete account (self-service)',
345
+ uploadAvatar: 'Upload profile picture',
346
+ avatarViaMetadata: 'Store avatar URL in metadata',
347
+ customAttributes: 'Custom customer attributes',
348
+ fetchOrders: 'View order history',
349
+ cancelOrder: 'Cancel orders',
350
+ manageAddresses: 'Add/update/delete addresses',
351
+ defaultAddress: 'Set default address',
352
+ applyDiscounts: 'Apply discount codes',
353
+ shippingCalculation: 'Calculate shipping rates',
354
+ guestCheckout: 'Checkout without account',
355
+ savedPaymentMethods: 'Save payment methods',
356
+ listPaymentProviders: 'List available payment providers',
357
+ regionSpecificPayments: 'Region-specific payment methods',
358
+ wishlist: 'Wishlist/favorites',
359
+ advancedSearch: 'Advanced product search',
360
+ facetedFilters: 'Filter by product attributes',
361
+ productReviews: 'Product reviews and ratings',
362
+ loyaltyProgram: 'Loyalty/rewards program',
363
+ };
364
+
365
+ return descriptions[capability] || capability;
366
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @thorprovider/adapters v2.0
3
+ * Type exports - unified commerce types
4
+ *
5
+ * Re-exports from @thorprovider/types package
6
+ */
7
+
8
+ // Re-export all shared types from @thorprovider/types
9
+ export type * from '@thorprovider/types';
10
+
11
+ // Provider-specific types (remain in adapters)
12
+ export type {
13
+ CommerceProvider,
14
+ ProviderConfig,
15
+ } from './provider';
16
+
17
+ // UI-ready response types (transformed for direct UI consumption)
18
+ export type {
19
+ UICartItem,
20
+ UICartTotals,
21
+ UIShippingMethod,
22
+ UIDiscountCode,
23
+ UIPaymentMethod,
24
+ } from './ui-responses';
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ /**
3
+ * @thorprovider/adapters v2.0
4
+ * Provider interface - unified commerce operations across all platforms
5
+ *
6
+ * NOTE: This file re-exports CommerceProvider from @thorprovider/types for backward compatibility.
7
+ * The actual interface definition lives in @thorprovider/types (L1: Foundation).
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @thorprovider/adapters v2.0
3
+ * Provider interface - unified commerce operations across all platforms
4
+ *
5
+ * NOTE: This file re-exports CommerceProvider from @thorprovider/types for backward compatibility.
6
+ * The actual interface definition lives in @thorprovider/types (L1: Foundation).
7
+ */
8
+
9
+ export type { CommerceProvider, BackendCapabilities, ProviderConfig } from '@thorprovider/types';
@@ -0,0 +1,261 @@
1
+ /**
2
+ * @thorprovider/adapters
3
+ * UI-ready response types
4
+ *
5
+ * These types extend domain types from @thorprovider/types (L1) but with transformations
6
+ * applied for direct UI consumption. This is where Money→number conversions happen.
7
+ *
8
+ * L3 (@thorprovider/components) should import these types, not the raw domain types.
9
+ */
10
+
11
+ import type {
12
+ CartItem as DomainCartItem,
13
+ ShippingMethod as DomainShippingMethod,
14
+ Address,
15
+ Customer,
16
+ DiscountCode,
17
+ Money,
18
+ } from '@thorprovider/types';
19
+
20
+ /**
21
+ * UI-ready cart item with prices as numbers
22
+ *
23
+ * Simplified cart item for UI consumption with prices in major currency units (e.g., dollars, euros).
24
+ * Transformed from domain CartItem by adapter services.
25
+ * Values come from Money.amount (decimal string in major units) parsed to number.
26
+ *
27
+ * This is a flattened structure optimized for UI display, unlike the nested
28
+ * domain CartItem which follows the Shopify/Medusa structure.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const uiItem: UICartItem = {
33
+ * id: 'item_123',
34
+ * productId: 'prod_abc',
35
+ * variantId: 'var_xyz',
36
+ * name: 'T-Shirt',
37
+ * variant: 'Red / Large',
38
+ * image: 'https://...',
39
+ * price: 19.99, // $19.99 in major units
40
+ * quantity: 2,
41
+ * total: 39.98, // $39.98 in major units
42
+ * isAvailable: true
43
+ * }
44
+ * ```
45
+ */
46
+ export interface UICartItem {
47
+ /** Unique item identifier */
48
+ id: string;
49
+
50
+ /** Product ID */
51
+ productId: string;
52
+
53
+ /** Variant ID */
54
+ variantId: string;
55
+
56
+ /** Product name */
57
+ name: string;
58
+
59
+ /** Product description (optional) */
60
+ description?: string;
61
+
62
+ /** Variant description (e.g., 'Red / Large') */
63
+ variant: string;
64
+
65
+ /** Product image URL */
66
+ image: string;
67
+
68
+ /** Unit price in major currency units (e.g., dollars, euros) */
69
+ price: number;
70
+
71
+ /** Quantity in cart */
72
+ quantity: number;
73
+
74
+ /** Total price (price × quantity) in major currency units */
75
+ total: number;
76
+
77
+ /** Whether item is available for purchase */
78
+ isAvailable?: boolean;
79
+
80
+ /** Maximum quantity available in stock */
81
+ maxQuantity?: number;
82
+ }
83
+
84
+ /**
85
+ * UI-ready cart totals with amounts as numbers
86
+ *
87
+ * All monetary values are in major currency units (e.g., dollars, euros).
88
+ * Values come from Money.amount (decimal string in major units) parsed to number.
89
+ * Transformed from domain CartCost by adapter services.
90
+ *
91
+ * @example
92
+ * ```typescript
93
+ * const totals: UICartTotals = {
94
+ * subtotal: 59.97, // $59.97
95
+ * discount: 10.00, // $10.00
96
+ * shipping: 5.00, // $5.00
97
+ * tax: 5.39, // $5.39
98
+ * total: 60.36, // $60.36
99
+ * currency: 'USD',
100
+ * itemCount: 3
101
+ * }
102
+ * ```
103
+ */
104
+ export interface UICartTotals {
105
+ /** Subtotal before discounts/shipping/tax (in major currency units) */
106
+ subtotal: number;
107
+
108
+ /** Discount amount if any (in major currency units) */
109
+ discount?: number;
110
+
111
+ /** Discount code applied */
112
+ discountCode?: string;
113
+
114
+ /** Shipping cost (in major currency units) */
115
+ shipping?: number;
116
+
117
+ /** Tax amount (in major currency units) */
118
+ tax?: number;
119
+
120
+ /** Final total amount (in major currency units) */
121
+ total: number;
122
+
123
+ /** Currency code (ISO 4217, e.g., 'USD', 'EUR') */
124
+ currency: string;
125
+
126
+ /** Total number of items in cart */
127
+ itemCount: number;
128
+ }
129
+
130
+ /**
131
+ * UI-ready shipping method with price as number
132
+ *
133
+ * Price is in the currency's smallest unit.
134
+ * Transformed from domain ShippingMethod by adapter services.
135
+ *
136
+ * Includes alias fields for compatibility with existing checkout components.
137
+ *
138
+ * @example
139
+ * ```typescript
140
+ * const method: UIShippingMethod = {
141
+ * id: 'ship_standard',
142
+ * methodId: 'ship_standard', // alias for id
143
+ * name: 'Standard Shipping',
144
+ * price: 500, // $5.00 as 500 cents
145
+ * amount: 500, // alias for price
146
+ * estimatedDays: '3-5 business days'
147
+ * }
148
+ * ```
149
+ */
150
+ export interface UIShippingMethod {
151
+ /** Unique identifier */
152
+ id: string;
153
+
154
+ /** Alias for id (for compatibility with checkout components) */
155
+ methodId: string;
156
+
157
+ /** Display name */
158
+ name: string;
159
+
160
+ /** Shipping price in smallest currency unit */
161
+ price: number;
162
+
163
+ /** Alias for price (for compatibility with checkout components) */
164
+ amount: number;
165
+
166
+ /** Optional description */
167
+ description?: string;
168
+
169
+ /** Estimated delivery time (human-readable string) */
170
+ estimatedDays?: string;
171
+ }
172
+
173
+ /**
174
+ * UI-ready discount code with value as number
175
+ *
176
+ * Transformed from domain DiscountCode by adapter services.
177
+ * Value is in the currency's smallest unit for fixed discounts,
178
+ * or a percentage (0-100) for percentage discounts.
179
+ *
180
+ * @example
181
+ * ```typescript
182
+ * // Fixed discount
183
+ * const fixedDiscount: UIDiscountCode = {
184
+ * code: 'SAVE10',
185
+ * value: 1000, // $10.00 as 1000 cents
186
+ * isPercentage: false,
187
+ * description: 'Save $10 on your order'
188
+ * }
189
+ *
190
+ * // Percentage discount
191
+ * const percentDiscount: UIDiscountCode = {
192
+ * code: 'SAVE20',
193
+ * value: 20, // 20%
194
+ * isPercentage: true,
195
+ * description: 'Save 20% on your order'
196
+ * }
197
+ * ```
198
+ */
199
+ export interface UIDiscountCode {
200
+ /** Discount code */
201
+ code: string;
202
+
203
+ /** Discount value - either amount in smallest currency unit or percentage (0-100) */
204
+ value: number;
205
+
206
+ /** Whether value is a percentage (true) or fixed amount (false) */
207
+ isPercentage: boolean;
208
+
209
+ /** Human-readable description of the discount */
210
+ description?: string;
211
+
212
+ /** Minimum purchase amount required (in smallest currency unit) */
213
+ minimumAmount?: number;
214
+
215
+ /** Expiration date */
216
+ expiresAt?: Date;
217
+
218
+ /** Maximum number of uses allowed */
219
+ maxUses?: number;
220
+
221
+ /** Current usage count */
222
+ usageCount?: number;
223
+ }
224
+
225
+ /**
226
+ * UI-ready payment method for checkout selection
227
+ *
228
+ * Simplified version of domain PaymentMethod with additional
229
+ * providerId field for backend-specific provider identification.
230
+ *
231
+ * @example
232
+ * ```typescript
233
+ * const payment: UIPaymentMethod = {
234
+ * id: 'pp_stripe_stripe',
235
+ * providerId: 'stripe',
236
+ * name: 'Credit Card',
237
+ * description: 'Visa, Mastercard, American Express'
238
+ * }
239
+ * ```
240
+ */
241
+ export interface UIPaymentMethod {
242
+ /** Unique identifier */
243
+ id: string;
244
+
245
+ /** Provider identifier (e.g., 'stripe', 'paypal', 'manual') */
246
+ providerId: string;
247
+
248
+ /** Display name for UI */
249
+ name: string;
250
+
251
+ /** Optional description */
252
+ description?: string;
253
+ }
254
+
255
+ /**
256
+ * Re-export domain types that don't need transformation
257
+ *
258
+ * These types are used as-is from L1 without modification.
259
+ * L3 can import them from here for convenience.
260
+ */
261
+ export type { Address, Customer, DiscountCode };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * City coordinates lookup table
3
+ *
4
+ * Lightweight approximation for geocoding without external APIs.
5
+ * Keep this small and extend as needed.
6
+ */
7
+
8
+ export interface CityCoordinates {
9
+ lat: number;
10
+ lng: number;
11
+ }
12
+
13
+ const CITY_COORDINATES: Record<string, Record<string, CityCoordinates>> = {
14
+ US: {
15
+ 'New York': { lat: 40.7128, lng: -74.0060 },
16
+ 'Los Angeles': { lat: 34.0522, lng: -118.2437 },
17
+ Chicago: { lat: 41.8781, lng: -87.6298 },
18
+ Miami: { lat: 25.7617, lng: -80.1918 },
19
+ },
20
+ GB: {
21
+ London: { lat: 51.5074, lng: -0.1278 },
22
+ Manchester: { lat: 53.4808, lng: -2.2426 },
23
+ },
24
+ DE: {
25
+ Berlin: { lat: 52.52, lng: 13.405 },
26
+ Hamburg: { lat: 53.5511, lng: 9.9937 },
27
+ },
28
+ ES: {
29
+ Madrid: { lat: 40.4168, lng: -3.7038 },
30
+ Barcelona: { lat: 41.3851, lng: 2.1734 },
31
+ },
32
+ };
33
+
34
+ export function getCityCoordinates(city: string, countryCode: string): CityCoordinates | null {
35
+ const country = CITY_COORDINATES[countryCode.toUpperCase()];
36
+ if (!country) return null;
37
+
38
+ const cityKey = Object.keys(country).find((key) => key.toLowerCase() === city.toLowerCase());
39
+ return cityKey ? country[cityKey] : null;
40
+ }