@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/dist/index.js CHANGED
@@ -44,10 +44,12 @@ __export(src_exports, {
44
44
  ConfigurationError: () => ConfigurationError,
45
45
  MAGENTO_CAPABILITIES: () => MAGENTO_CAPABILITIES,
46
46
  MEDUSA_CAPABILITIES: () => MEDUSA_CAPABILITIES,
47
+ MOCK_CAPABILITIES: () => MOCK_CAPABILITIES,
47
48
  MagentoProvider: () => MagentoProvider,
48
49
  MedusaMetadataAdapter: () => MedusaMetadataAdapter,
49
50
  MedusaProvider: () => MedusaProvider,
50
51
  MetricsService: () => MetricsService,
52
+ MockProvider: () => MockProvider,
51
53
  ProviderAPIError: () => ProviderAPIError,
52
54
  ProviderNotFoundError: () => ProviderNotFoundError,
53
55
  ResourceNotFoundError: () => ResourceNotFoundError,
@@ -94,6 +96,31 @@ module.exports = __toCommonJS(src_exports);
94
96
  var import_types2 = require("@thorprovider/types");
95
97
 
96
98
  // src/types/capabilities.types.ts
99
+ var MOCK_CAPABILITIES = {
100
+ updateProfile: false,
101
+ updateEmail: false,
102
+ changePasswordDirect: false,
103
+ changePasswordViaEmail: false,
104
+ deleteAccount: false,
105
+ uploadAvatar: false,
106
+ avatarViaMetadata: false,
107
+ customAttributes: false,
108
+ fetchOrders: true,
109
+ cancelOrder: true,
110
+ manageAddresses: false,
111
+ defaultAddress: false,
112
+ applyDiscounts: true,
113
+ shippingCalculation: true,
114
+ guestCheckout: true,
115
+ savedPaymentMethods: false,
116
+ listPaymentProviders: true,
117
+ regionSpecificPayments: true,
118
+ wishlist: false,
119
+ advancedSearch: false,
120
+ facetedFilters: false,
121
+ productReviews: false,
122
+ loyaltyProgram: false
123
+ };
97
124
  var MEDUSA_CAPABILITIES = {
98
125
  // Profile Management
99
126
  updateProfile: true,
@@ -8042,6 +8069,1011 @@ var MagentoProvider = class {
8042
8069
  }
8043
8070
  };
8044
8071
 
8072
+ // src/providers/mock/mock.ts
8073
+ var FIXTURE_TIMESTAMP = "2026-01-01T00:00:00.000Z";
8074
+ var DEFAULT_SITE_URL = "https://mock.thorcommerce.local";
8075
+ var regionFixtures = [
8076
+ {
8077
+ id: "mock-region-us",
8078
+ name: "United States",
8079
+ currencyCode: "USD",
8080
+ locale: "en-US",
8081
+ taxRate: 0.08,
8082
+ countries: [{ code: "US", name: "United States" }]
8083
+ },
8084
+ {
8085
+ id: "mock-region-eu",
8086
+ name: "Europe",
8087
+ currencyCode: "EUR",
8088
+ locale: "en-ES",
8089
+ taxRate: 0.21,
8090
+ countries: [
8091
+ { code: "ES", name: "Spain" },
8092
+ { code: "FR", name: "France" },
8093
+ { code: "DE", name: "Germany" }
8094
+ ]
8095
+ }
8096
+ ];
8097
+ var collectionFixtures = [
8098
+ {
8099
+ id: "mock-collection-new-arrivals",
8100
+ handle: "new-arrivals",
8101
+ title: "New Arrivals",
8102
+ description: "Fresh mock catalog items for layout and merchandising flows."
8103
+ },
8104
+ {
8105
+ id: "mock-collection-best-sellers",
8106
+ handle: "best-sellers",
8107
+ title: "Best Sellers",
8108
+ description: "High-performing mock products used to design hero grids and featured sections."
8109
+ },
8110
+ {
8111
+ id: "mock-collection-studio-essentials",
8112
+ handle: "studio-essentials",
8113
+ title: "Studio Essentials",
8114
+ description: "A balanced fixture set for mock storefront navigation and detail pages."
8115
+ }
8116
+ ];
8117
+ var categoryFixtures = [
8118
+ {
8119
+ id: "mock-category-jackets",
8120
+ handle: "jackets",
8121
+ name: "Jackets",
8122
+ description: "Outerwear and layering pieces."
8123
+ },
8124
+ {
8125
+ id: "mock-category-dresses",
8126
+ handle: "dresses",
8127
+ name: "Dresses",
8128
+ description: "Editorial silhouettes for seasonal collections."
8129
+ },
8130
+ {
8131
+ id: "mock-category-home",
8132
+ handle: "home",
8133
+ name: "Home",
8134
+ description: "Decor and living objects for lifestyle layouts."
8135
+ },
8136
+ {
8137
+ id: "mock-category-accessories",
8138
+ handle: "accessories",
8139
+ name: "Accessories",
8140
+ description: "Complementary products for cart and bundle flows."
8141
+ }
8142
+ ];
8143
+ var productFixtures = [
8144
+ {
8145
+ id: "mock-product-aurora-jacket",
8146
+ handle: "aurora-jacket",
8147
+ title: "Aurora Quilted Jacket",
8148
+ description: "Lightweight quilted outerwear with tonal hardware and a soft structured collar.",
8149
+ imageLabel: "Aurora Jacket",
8150
+ imageHue: 222,
8151
+ collectionHandles: ["new-arrivals", "best-sellers"],
8152
+ categoryHandle: "jackets",
8153
+ productType: "outerwear",
8154
+ vendor: "Thor Atelier",
8155
+ tags: ["outerwear", "quilted", "winter"],
8156
+ options: [
8157
+ { id: "aurora-size", name: "Size", values: ["S", "M", "L"] },
8158
+ { id: "aurora-color", name: "Color", values: ["Midnight"] }
8159
+ ],
8160
+ variants: [
8161
+ {
8162
+ id: "mock-variant-aurora-s",
8163
+ title: "S / Midnight",
8164
+ price: 129,
8165
+ compareAtPrice: 159,
8166
+ quantityAvailable: 8,
8167
+ selectedOptions: [
8168
+ { name: "Size", value: "S" },
8169
+ { name: "Color", value: "Midnight" }
8170
+ ]
8171
+ },
8172
+ {
8173
+ id: "mock-variant-aurora-m",
8174
+ title: "M / Midnight",
8175
+ price: 129,
8176
+ compareAtPrice: 159,
8177
+ quantityAvailable: 12,
8178
+ selectedOptions: [
8179
+ { name: "Size", value: "M" },
8180
+ { name: "Color", value: "Midnight" }
8181
+ ]
8182
+ }
8183
+ ]
8184
+ },
8185
+ {
8186
+ id: "mock-product-solis-dress",
8187
+ handle: "solis-dress",
8188
+ title: "Solis Pleated Dress",
8189
+ description: "A fluid pleated dress with a clean neckline designed for hero storytelling.",
8190
+ imageLabel: "Solis Dress",
8191
+ imageHue: 18,
8192
+ collectionHandles: ["new-arrivals", "studio-essentials"],
8193
+ categoryHandle: "dresses",
8194
+ productType: "dress",
8195
+ vendor: "Thor Atelier",
8196
+ tags: ["dress", "occasion", "pleated"],
8197
+ options: [
8198
+ { id: "solis-size", name: "Size", values: ["XS", "S", "M"] },
8199
+ { id: "solis-color", name: "Color", values: ["Clay"] }
8200
+ ],
8201
+ variants: [
8202
+ {
8203
+ id: "mock-variant-solis-xs",
8204
+ title: "XS / Clay",
8205
+ price: 164,
8206
+ quantityAvailable: 4,
8207
+ selectedOptions: [
8208
+ { name: "Size", value: "XS" },
8209
+ { name: "Color", value: "Clay" }
8210
+ ]
8211
+ },
8212
+ {
8213
+ id: "mock-variant-solis-s",
8214
+ title: "S / Clay",
8215
+ price: 164,
8216
+ quantityAvailable: 9,
8217
+ selectedOptions: [
8218
+ { name: "Size", value: "S" },
8219
+ { name: "Color", value: "Clay" }
8220
+ ]
8221
+ }
8222
+ ]
8223
+ },
8224
+ {
8225
+ id: "mock-product-helios-lamp",
8226
+ handle: "helios-lamp",
8227
+ title: "Helios Table Lamp",
8228
+ description: "Warm ambient lamp with sculptural proportions for lifestyle and home grids.",
8229
+ imageLabel: "Helios Lamp",
8230
+ imageHue: 45,
8231
+ collectionHandles: ["best-sellers", "studio-essentials"],
8232
+ categoryHandle: "home",
8233
+ productType: "lighting",
8234
+ vendor: "Thor Home",
8235
+ tags: ["home", "lighting", "decor"],
8236
+ options: [{ id: "helios-finish", name: "Finish", values: ["Brass"] }],
8237
+ variants: [
8238
+ {
8239
+ id: "mock-variant-helios-brass",
8240
+ title: "Brass",
8241
+ price: 96,
8242
+ quantityAvailable: 15,
8243
+ selectedOptions: [{ name: "Finish", value: "Brass" }]
8244
+ }
8245
+ ]
8246
+ },
8247
+ {
8248
+ id: "mock-product-luna-bag",
8249
+ handle: "luna-bag",
8250
+ title: "Luna Crossbody Bag",
8251
+ description: "Compact everyday bag for accessory rails, checkout add-ons, and bundle modules.",
8252
+ imageLabel: "Luna Bag",
8253
+ imageHue: 306,
8254
+ collectionHandles: ["studio-essentials"],
8255
+ categoryHandle: "accessories",
8256
+ productType: "bag",
8257
+ vendor: "Thor Atelier",
8258
+ tags: ["bag", "accessory", "everyday"],
8259
+ options: [{ id: "luna-color", name: "Color", values: ["Berry"] }],
8260
+ variants: [
8261
+ {
8262
+ id: "mock-variant-luna-berry",
8263
+ title: "Berry",
8264
+ price: 82,
8265
+ quantityAvailable: 18,
8266
+ selectedOptions: [{ name: "Color", value: "Berry" }]
8267
+ }
8268
+ ]
8269
+ }
8270
+ ];
8271
+ var stockLocationFixture = {
8272
+ id: "mock-stock-primary",
8273
+ name: "Mock Main Warehouse",
8274
+ address: {
8275
+ address1: "100 Mock Avenue",
8276
+ city: "Design City",
8277
+ countryCode: "US",
8278
+ postalCode: "10001",
8279
+ province: "NY"
8280
+ },
8281
+ metadata: {
8282
+ timezone: "America/New_York"
8283
+ }
8284
+ };
8285
+ var cartStore = /* @__PURE__ */ new Map();
8286
+ var orderStore = /* @__PURE__ */ new Map();
8287
+ var sequence = 0;
8288
+ function nextId(prefix) {
8289
+ sequence += 1;
8290
+ return `${prefix}-${sequence}`;
8291
+ }
8292
+ function createMoney(amount, currencyCode) {
8293
+ return {
8294
+ amount: amount.toFixed(2),
8295
+ currencyCode
8296
+ };
8297
+ }
8298
+ function createImage(label, hue) {
8299
+ const svg = `
8300
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 1200" role="img" aria-label="${label}">
8301
+ <defs>
8302
+ <linearGradient id="g" x1="0" x2="1" y1="0" y2="1">
8303
+ <stop offset="0%" stop-color="hsl(${hue}, 72%, 72%)" />
8304
+ <stop offset="100%" stop-color="hsl(${(hue + 48) % 360}, 62%, 42%)" />
8305
+ </linearGradient>
8306
+ </defs>
8307
+ <rect width="1200" height="1200" fill="url(#g)" rx="48" />
8308
+ <circle cx="910" cy="280" r="180" fill="rgba(255,255,255,0.22)" />
8309
+ <text x="90" y="990" fill="white" font-family="Arial, sans-serif" font-size="96" font-weight="700">${label}</text>
8310
+ </svg>
8311
+ `.trim();
8312
+ return {
8313
+ url: `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg)}`,
8314
+ altText: label,
8315
+ width: 1200,
8316
+ height: 1200
8317
+ };
8318
+ }
8319
+ function getRegionFixture(regionId) {
8320
+ if (!regionId) {
8321
+ return regionFixtures[0];
8322
+ }
8323
+ return regionFixtures.find((region) => region.id === regionId) ?? regionFixtures[0];
8324
+ }
8325
+ function createProductImageSet(label, hue) {
8326
+ const featuredImage = createImage(label, hue);
8327
+ return {
8328
+ featuredImage,
8329
+ images: [featuredImage, createImage(`${label} Detail`, (hue + 18) % 360)]
8330
+ };
8331
+ }
8332
+ function buildCollections() {
8333
+ return collectionFixtures.map((collection) => ({
8334
+ id: collection.id,
8335
+ handle: collection.handle,
8336
+ title: collection.title,
8337
+ description: collection.description,
8338
+ path: `/collections/${collection.handle}`,
8339
+ updatedAt: FIXTURE_TIMESTAMP
8340
+ }));
8341
+ }
8342
+ function buildCategories() {
8343
+ return categoryFixtures.map((category) => ({
8344
+ id: category.id,
8345
+ handle: category.handle,
8346
+ name: category.name,
8347
+ description: category.description,
8348
+ parentCategoryId: category.parentCategoryId,
8349
+ productCount: productFixtures.filter((product) => product.categoryHandle === category.handle).length,
8350
+ image: createImage(category.name, 180).url
8351
+ }));
8352
+ }
8353
+ function buildProducts(currencyCode) {
8354
+ const collections = buildCollections();
8355
+ return productFixtures.map((product) => {
8356
+ const { featuredImage, images } = createProductImageSet(product.imageLabel, product.imageHue);
8357
+ const variants = product.variants.map((variant) => ({
8358
+ id: variant.id,
8359
+ title: variant.title,
8360
+ availableForSale: variant.quantityAvailable > 0,
8361
+ selectedOptions: variant.selectedOptions,
8362
+ price: createMoney(variant.price, currencyCode),
8363
+ compareAtPrice: variant.compareAtPrice ? createMoney(variant.compareAtPrice, currencyCode) : void 0,
8364
+ quantityAvailable: variant.quantityAvailable,
8365
+ image: featuredImage
8366
+ }));
8367
+ const prices = product.variants.map((variant) => variant.price);
8368
+ return {
8369
+ id: product.id,
8370
+ handle: product.handle,
8371
+ title: product.title,
8372
+ description: product.description,
8373
+ availableForSale: variants.some((variant) => variant.availableForSale),
8374
+ options: product.options,
8375
+ variants,
8376
+ featuredImage,
8377
+ images,
8378
+ priceRange: {
8379
+ minVariantPrice: createMoney(Math.min(...prices), currencyCode),
8380
+ maxVariantPrice: createMoney(Math.max(...prices), currencyCode)
8381
+ },
8382
+ collections: product.collectionHandles.map((handle) => collections.find((collection) => collection.handle === handle)).filter((collection) => Boolean(collection)).map((collection) => ({
8383
+ id: collection.id,
8384
+ title: collection.title,
8385
+ handle: collection.handle
8386
+ })),
8387
+ productType: product.productType,
8388
+ vendor: product.vendor,
8389
+ tags: product.tags,
8390
+ createdAt: FIXTURE_TIMESTAMP,
8391
+ updatedAt: FIXTURE_TIMESTAMP,
8392
+ metadata: {
8393
+ categoryHandle: product.categoryHandle,
8394
+ collectionHandles: product.collectionHandles
8395
+ }
8396
+ };
8397
+ });
8398
+ }
8399
+ function filterProducts(products, options) {
8400
+ const query = options.query?.trim().toLowerCase();
8401
+ const categoryIds = new Set(options.categoryIds ?? []);
8402
+ const collectionIds = new Set(options.collectionIds ?? []);
8403
+ return products.filter((product) => {
8404
+ if (query) {
8405
+ const matchesQuery = [product.title, product.description, ...product.tags ?? []].join(" ").toLowerCase().includes(query);
8406
+ if (!matchesQuery) {
8407
+ return false;
8408
+ }
8409
+ }
8410
+ if (categoryIds.size > 0) {
8411
+ const categoryHandle = String(product.metadata?.categoryHandle ?? "");
8412
+ const category = categoryFixtures.find((item) => item.handle === categoryHandle);
8413
+ const matchesCategory = category ? categoryIds.has(category.id) || categoryIds.has(category.handle) : false;
8414
+ if (!matchesCategory) {
8415
+ return false;
8416
+ }
8417
+ }
8418
+ if (collectionIds.size > 0) {
8419
+ const productCollections = (product.collections ?? []).map((collection) => collection.handle);
8420
+ const matchesCollection = (product.collections ?? []).some(
8421
+ (collection) => collectionIds.has(collection.id) || collectionIds.has(collection.handle)
8422
+ ) || productCollections.some((handle) => collectionIds.has(handle));
8423
+ if (!matchesCollection) {
8424
+ return false;
8425
+ }
8426
+ }
8427
+ return true;
8428
+ });
8429
+ }
8430
+ function sortProducts(products, sortKey, reverse) {
8431
+ const sorted = [...products];
8432
+ switch (sortKey) {
8433
+ case "price":
8434
+ case "price-asc":
8435
+ case "price-desc":
8436
+ sorted.sort(
8437
+ (left, right) => Number(left.priceRange.minVariantPrice.amount) - Number(right.priceRange.minVariantPrice.amount)
8438
+ );
8439
+ break;
8440
+ case "created_at":
8441
+ case "createdAt":
8442
+ case "newest":
8443
+ sorted.sort((left, right) => left.createdAt.localeCompare(right.createdAt));
8444
+ break;
8445
+ case "title":
8446
+ case "name":
8447
+ default:
8448
+ sorted.sort((left, right) => left.title.localeCompare(right.title));
8449
+ break;
8450
+ }
8451
+ const shouldReverse = reverse || sortKey === "price-desc" || sortKey === "newest";
8452
+ return shouldReverse ? sorted.reverse() : sorted;
8453
+ }
8454
+ function paginateProducts(products, skip, first) {
8455
+ const start = Math.max(0, skip ?? 0);
8456
+ const end = first ? start + first : void 0;
8457
+ return products.slice(start, end);
8458
+ }
8459
+ function resolveVariant(currencyCode, merchandiseId) {
8460
+ const products = buildProducts(currencyCode);
8461
+ for (const product of products) {
8462
+ const variant = product.variants.find((item) => item.id === merchandiseId);
8463
+ if (variant) {
8464
+ return { product, variant };
8465
+ }
8466
+ }
8467
+ return void 0;
8468
+ }
8469
+ function getShippingPrice(methodId) {
8470
+ switch (methodId) {
8471
+ case "mock-shipping-express":
8472
+ return 18;
8473
+ case "mock-shipping-standard":
8474
+ return 8;
8475
+ default:
8476
+ return 0;
8477
+ }
8478
+ }
8479
+ function buildExternalCart(state, currencyCode, siteUrl) {
8480
+ const region = getRegionFixture(state.regionId);
8481
+ const regionCurrency = region.currencyCode || currencyCode;
8482
+ const lines = state.lines.flatMap((line) => {
8483
+ const resolved = resolveVariant(regionCurrency, line.merchandiseId);
8484
+ if (!resolved || !resolved.product.featuredImage) {
8485
+ return [];
8486
+ }
8487
+ const lineTotal = Number(resolved.variant.price.amount) * line.quantity;
8488
+ const stockStatus = resolved.variant.quantityAvailable && resolved.variant.quantityAvailable > 0 ? "in_stock" : "out_of_stock";
8489
+ return [{
8490
+ id: line.id,
8491
+ quantity: line.quantity,
8492
+ stockStatus,
8493
+ cost: {
8494
+ totalAmount: createMoney(lineTotal, regionCurrency)
8495
+ },
8496
+ merchandise: {
8497
+ id: resolved.variant.id,
8498
+ title: resolved.variant.title,
8499
+ selectedOptions: resolved.variant.selectedOptions,
8500
+ product: {
8501
+ id: resolved.product.id,
8502
+ handle: resolved.product.handle,
8503
+ title: resolved.product.title,
8504
+ featuredImage: resolved.product.featuredImage
8505
+ }
8506
+ }
8507
+ }];
8508
+ });
8509
+ const subtotal = lines.reduce(
8510
+ (sum, line) => sum + Number(line.cost.totalAmount.amount),
8511
+ 0
8512
+ );
8513
+ const shipping = getShippingPrice(state.shippingMethodId);
8514
+ const discount = state.discountCode ? subtotal * 0.1 : 0;
8515
+ const taxableBase = Math.max(subtotal + shipping - discount, 0);
8516
+ const totalTax = region.taxRate ? taxableBase * region.taxRate : 0;
8517
+ const total = taxableBase + totalTax;
8518
+ return {
8519
+ id: state.id,
8520
+ checkoutUrl: `${siteUrl}/checkout?cart=${state.id}`,
8521
+ cost: {
8522
+ subtotalAmount: createMoney(subtotal, regionCurrency),
8523
+ totalAmount: createMoney(total, regionCurrency),
8524
+ totalTaxAmount: createMoney(totalTax, regionCurrency)
8525
+ },
8526
+ lines,
8527
+ totalQuantity: lines.reduce((sum, line) => sum + line.quantity, 0),
8528
+ completedAt: state.completedAt
8529
+ };
8530
+ }
8531
+ function buildOrderFromCart(state, currencyCode, siteUrl) {
8532
+ const cart = buildExternalCart(state, currencyCode, siteUrl);
8533
+ return {
8534
+ id: `mock-order-${state.id}`,
8535
+ orderNumber: `${1e3 + sequence}`,
8536
+ email: "mock-customer@thorprovider.dev",
8537
+ status: "completed",
8538
+ paymentStatus: state.paymentProviderId ? "paid" : "pending",
8539
+ fulfillmentStatus: state.shippingMethodId ? "fulfilled" : "unfulfilled",
8540
+ items: cart.lines.map((line) => ({
8541
+ id: line.id ?? nextId("mock-order-line"),
8542
+ title: line.merchandise.product.title,
8543
+ variantTitle: line.merchandise.title,
8544
+ quantity: line.quantity,
8545
+ price: line.merchandise.product.featuredImage ? createMoney(Number(line.cost.totalAmount.amount) / Math.max(line.quantity, 1), cart.cost.totalAmount.currencyCode) : createMoney(0, cart.cost.totalAmount.currencyCode),
8546
+ total: line.cost.totalAmount,
8547
+ productId: line.merchandise.product.id,
8548
+ variantId: line.merchandise.id,
8549
+ image: line.merchandise.product.featuredImage
8550
+ })),
8551
+ subtotal: cart.cost.subtotalAmount,
8552
+ total: cart.cost.totalAmount,
8553
+ tax: cart.cost.totalTaxAmount,
8554
+ shipping: createMoney(getShippingPrice(state.shippingMethodId), cart.cost.totalAmount.currencyCode),
8555
+ discount: createMoney(state.discountCode ? Number(cart.cost.subtotalAmount.amount) * 0.1 : 0, cart.cost.totalAmount.currencyCode),
8556
+ createdAt: FIXTURE_TIMESTAMP,
8557
+ updatedAt: FIXTURE_TIMESTAMP,
8558
+ completedAt: state.completedAt,
8559
+ metadata: {
8560
+ provider: "mock",
8561
+ checkoutUrl: cart.checkoutUrl
8562
+ }
8563
+ };
8564
+ }
8565
+ function getMockCollections() {
8566
+ return buildCollections();
8567
+ }
8568
+ function getMockCategories() {
8569
+ return buildCategories();
8570
+ }
8571
+ function getMockProducts(options) {
8572
+ const region = getRegionFixture(options?.regionId);
8573
+ const currencyCode = options?.currencyCode ?? region.currencyCode;
8574
+ const products = buildProducts(currencyCode);
8575
+ const filtered = filterProducts(products, {
8576
+ query: options?.query,
8577
+ categoryIds: options?.categoryIds,
8578
+ collectionIds: options?.collectionIds
8579
+ });
8580
+ const sorted = sortProducts(filtered, options?.sortKey, options?.reverse);
8581
+ return paginateProducts(sorted, options?.skip, options?.first);
8582
+ }
8583
+ function getMockProduct(handle, regionId) {
8584
+ const region = getRegionFixture(regionId);
8585
+ return buildProducts(region.currencyCode).find(
8586
+ (product) => product.handle === handle || product.id === handle
8587
+ );
8588
+ }
8589
+ function getMockProductRecommendations(productId, regionId) {
8590
+ const product = getMockProduct(productId, regionId) ?? buildProducts(getRegionFixture(regionId).currencyCode).find((item) => item.id === productId);
8591
+ if (!product) {
8592
+ return [];
8593
+ }
8594
+ const collectionHandles = new Set((product.collections ?? []).map((collection) => collection.handle));
8595
+ return buildProducts(getRegionFixture(regionId).currencyCode).filter((candidate) => candidate.id !== product.id).filter(
8596
+ (candidate) => (candidate.collections ?? []).some((collection) => collectionHandles.has(collection.handle))
8597
+ ).slice(0, 4);
8598
+ }
8599
+ function getMockCollection(handle) {
8600
+ return buildCollections().find(
8601
+ (collection) => collection.handle === handle || collection.id === handle
8602
+ );
8603
+ }
8604
+ function getMockCategory(idOrHandle) {
8605
+ return buildCategories().find(
8606
+ (category) => category.handle === idOrHandle || category.id === idOrHandle
8607
+ );
8608
+ }
8609
+ function getMockRegions() {
8610
+ return regionFixtures.map((region) => ({
8611
+ id: region.id,
8612
+ name: region.name,
8613
+ currency_code: region.currencyCode,
8614
+ countries: region.countries.map((country) => ({
8615
+ id: `country-${country.code.toLowerCase()}`,
8616
+ iso_2: country.code.toLowerCase(),
8617
+ name: country.name,
8618
+ display_name: country.name
8619
+ }))
8620
+ }));
8621
+ }
8622
+ function getMockAdminRegions() {
8623
+ return regionFixtures.map((region) => ({
8624
+ id: region.id,
8625
+ name: region.name,
8626
+ countryCodes: region.countries.map((country) => country.code),
8627
+ currency: region.currencyCode,
8628
+ taxRate: region.taxRate,
8629
+ isEnabled: true
8630
+ }));
8631
+ }
8632
+ function getMockRegionCountries(regionId) {
8633
+ const region = regionFixtures.find((item) => item.id === regionId);
8634
+ return region?.countries ?? [];
8635
+ }
8636
+ function createMockCart(regionId) {
8637
+ const cart = {
8638
+ id: nextId("mock-cart"),
8639
+ regionId: regionId ?? regionFixtures[0].id,
8640
+ lines: []
8641
+ };
8642
+ cartStore.set(cart.id, cart);
8643
+ return cart;
8644
+ }
8645
+ function getMockCartState(cartId) {
8646
+ return cartStore.get(cartId);
8647
+ }
8648
+ function saveMockCartState(cart) {
8649
+ cartStore.set(cart.id, cart);
8650
+ return cart;
8651
+ }
8652
+ function buildMockCart(cart, currencyCode, siteUrl) {
8653
+ return buildExternalCart(
8654
+ cart,
8655
+ currencyCode ?? getRegionFixture(cart.regionId).currencyCode,
8656
+ siteUrl ?? DEFAULT_SITE_URL
8657
+ );
8658
+ }
8659
+ function getMockShippingMethods(regionId) {
8660
+ const currencyCode = getRegionFixture(regionId).currencyCode;
8661
+ return [
8662
+ {
8663
+ id: "mock-shipping-standard",
8664
+ name: "Standard Shipping",
8665
+ description: "Fixture shipping option for checkout and cart layouts.",
8666
+ price: createMoney(8, currencyCode),
8667
+ estimatedDays: "3-5 business days"
8668
+ },
8669
+ {
8670
+ id: "mock-shipping-express",
8671
+ name: "Express Shipping",
8672
+ description: "Faster fixture shipping option for premium checkout flows.",
8673
+ price: createMoney(18, currencyCode),
8674
+ estimatedDays: "1-2 business days"
8675
+ }
8676
+ ];
8677
+ }
8678
+ function getMockPaymentMethods(currencyCode) {
8679
+ return [
8680
+ {
8681
+ id: "mock-card",
8682
+ name: "Mock Card",
8683
+ type: "card",
8684
+ isEnabled: true,
8685
+ requiresSetup: true,
8686
+ description: `Fixture card checkout for ${currencyCode}.`
8687
+ },
8688
+ {
8689
+ id: "mock-cash",
8690
+ name: "Cash on Delivery",
8691
+ type: "cash_on_delivery",
8692
+ isEnabled: true,
8693
+ requiresSetup: false,
8694
+ description: "Mock cash payment option for checkout prototypes."
8695
+ }
8696
+ ];
8697
+ }
8698
+ function getMockStockLocations() {
8699
+ return [stockLocationFixture];
8700
+ }
8701
+ function getMockInventoryLevels(variantId) {
8702
+ for (const region of regionFixtures) {
8703
+ const resolved = resolveVariant(region.currencyCode, variantId);
8704
+ if (resolved) {
8705
+ return [{
8706
+ locationId: stockLocationFixture.id,
8707
+ locationName: stockLocationFixture.name,
8708
+ availableQuantity: resolved.variant.quantityAvailable ?? 0,
8709
+ reservedQuantity: 0,
8710
+ stockedQuantity: resolved.variant.quantityAvailable ?? 0
8711
+ }];
8712
+ }
8713
+ }
8714
+ return [];
8715
+ }
8716
+ function validateMockCartStock(cart) {
8717
+ const currencyCode = getRegionFixture(cart.regionId).currencyCode;
8718
+ const unavailableItems = cart.lines.flatMap((line) => {
8719
+ const resolved = resolveVariant(currencyCode, line.merchandiseId);
8720
+ if (!resolved) {
8721
+ return [];
8722
+ }
8723
+ const availableQuantity = resolved.variant.quantityAvailable ?? 0;
8724
+ if (line.quantity <= availableQuantity) {
8725
+ return [];
8726
+ }
8727
+ return [{
8728
+ variantId: resolved.variant.id,
8729
+ productName: resolved.product.title,
8730
+ requestedQuantity: line.quantity,
8731
+ availableQuantity
8732
+ }];
8733
+ });
8734
+ return {
8735
+ allAvailable: unavailableItems.length === 0,
8736
+ unavailableItems
8737
+ };
8738
+ }
8739
+ function getMockFulfillmentOptions(regionId) {
8740
+ return getMockShippingMethods(regionId).map((method) => ({
8741
+ id: method.id,
8742
+ providerId: "mock-fulfillment",
8743
+ name: method.name,
8744
+ description: method.description,
8745
+ price: method.price,
8746
+ estimatedDays: method.estimatedDays,
8747
+ stockLocation: stockLocationFixture
8748
+ }));
8749
+ }
8750
+ function completeMockCheckout(cart, currencyCode, siteUrl) {
8751
+ const completedAt = (/* @__PURE__ */ new Date()).toISOString();
8752
+ const nextCart = {
8753
+ ...cart,
8754
+ completedAt
8755
+ };
8756
+ cartStore.set(nextCart.id, nextCart);
8757
+ const order = buildOrderFromCart(
8758
+ nextCart,
8759
+ currencyCode ?? getRegionFixture(nextCart.regionId).currencyCode,
8760
+ siteUrl ?? DEFAULT_SITE_URL
8761
+ );
8762
+ orderStore.set(order.id, order);
8763
+ return order;
8764
+ }
8765
+ function getMockAdminOrders() {
8766
+ const orders = Array.from(orderStore.values()).sort(
8767
+ (left, right) => right.createdAt.localeCompare(left.createdAt)
8768
+ );
8769
+ return {
8770
+ orders,
8771
+ count: orders.length
8772
+ };
8773
+ }
8774
+ function getMockOrder(orderId) {
8775
+ return orderStore.get(orderId);
8776
+ }
8777
+ function saveMockOrder(order) {
8778
+ orderStore.set(order.id, order);
8779
+ return order;
8780
+ }
8781
+
8782
+ // src/providers/mock/index.ts
8783
+ var MockProvider = class {
8784
+ name = "mock";
8785
+ version = "1.0.0";
8786
+ capabilities = MOCK_CAPABILITIES;
8787
+ isMock = true;
8788
+ currencyCode;
8789
+ siteUrl;
8790
+ constructor(config = {}) {
8791
+ this.currencyCode = config.currencyCode ?? "USD";
8792
+ this.siteUrl = config.siteUrl ?? "https://mock.thorcommerce.local";
8793
+ }
8794
+ hasVariant(merchandiseId, regionId) {
8795
+ return getMockProducts({
8796
+ currencyCode: this.currencyCode,
8797
+ regionId
8798
+ }).some(
8799
+ (product) => product.variants.some((variant) => variant.id === merchandiseId)
8800
+ );
8801
+ }
8802
+ async getProduct(handle, options) {
8803
+ return getMockProduct(handle, options?.regionId);
8804
+ }
8805
+ async getProducts(options) {
8806
+ return getMockProducts({
8807
+ currencyCode: this.currencyCode,
8808
+ query: options?.query,
8809
+ sortKey: options?.sortKey,
8810
+ reverse: options?.reverse,
8811
+ first: options?.first,
8812
+ skip: options?.skip,
8813
+ categoryIds: options?.categoryIds,
8814
+ collectionIds: options?.collectionIds,
8815
+ regionId: options?.regionId
8816
+ });
8817
+ }
8818
+ async getProductRecommendations(productId) {
8819
+ return getMockProductRecommendations(productId);
8820
+ }
8821
+ async getCollection(handle) {
8822
+ return getMockCollection(handle);
8823
+ }
8824
+ async getCollections(options) {
8825
+ const collections = getMockCollections();
8826
+ const query = options?.query?.trim().toLowerCase();
8827
+ const filtered = query ? collections.filter(
8828
+ (collection) => [collection.title, collection.description, collection.handle].join(" ").toLowerCase().includes(query)
8829
+ ) : collections;
8830
+ return filtered.slice(0, options?.limit ?? filtered.length);
8831
+ }
8832
+ async getCollectionProducts(handle, options) {
8833
+ const collection = getMockCollection(handle);
8834
+ if (!collection) {
8835
+ return [];
8836
+ }
8837
+ return getMockProducts({
8838
+ currencyCode: this.currencyCode,
8839
+ collectionIds: [collection.id],
8840
+ sortKey: options?.sortKey,
8841
+ reverse: options?.reverse,
8842
+ first: options?.first
8843
+ });
8844
+ }
8845
+ async getCategory(idOrHandle) {
8846
+ return getMockCategory(idOrHandle);
8847
+ }
8848
+ async getCategories(options) {
8849
+ const categories = getMockCategories();
8850
+ const query = options?.query?.trim().toLowerCase();
8851
+ let filtered = query ? categories.filter(
8852
+ (category) => [category.name, category.description ?? "", category.handle].join(" ").toLowerCase().includes(query)
8853
+ ) : categories;
8854
+ if (options?.parentCategoryId) {
8855
+ filtered = filtered.filter((category) => category.parentCategoryId === options.parentCategoryId);
8856
+ }
8857
+ return filtered.slice(0, options?.limit ?? filtered.length);
8858
+ }
8859
+ async getCategoryProducts(handle, options) {
8860
+ const category = getMockCategory(handle);
8861
+ if (!category) {
8862
+ return [];
8863
+ }
8864
+ return getMockProducts({
8865
+ currencyCode: this.currencyCode,
8866
+ categoryIds: [category.id],
8867
+ sortKey: options?.sortKey,
8868
+ reverse: options?.reverse,
8869
+ first: options?.first
8870
+ });
8871
+ }
8872
+ async createCart(options) {
8873
+ const cart = createMockCart(options?.regionId);
8874
+ return buildMockCart(cart, this.currencyCode, this.siteUrl);
8875
+ }
8876
+ async getCart(cartId) {
8877
+ const cart = getMockCartState(cartId);
8878
+ return cart ? buildMockCart(cart, this.currencyCode, this.siteUrl) : void 0;
8879
+ }
8880
+ async addToCart(cartId, lines) {
8881
+ const cart = getMockCartState(cartId);
8882
+ if (!cart) {
8883
+ throw new ResourceNotFoundError("cart", cartId);
8884
+ }
8885
+ for (const line of lines) {
8886
+ if (!this.hasVariant(line.merchandiseId, cart.regionId)) {
8887
+ throw new ResourceNotFoundError("variant", line.merchandiseId);
8888
+ }
8889
+ }
8890
+ const nextLines = [...cart.lines];
8891
+ for (const line of lines) {
8892
+ const existing = nextLines.find((item) => item.merchandiseId === line.merchandiseId);
8893
+ if (existing) {
8894
+ existing.quantity += line.quantity;
8895
+ } else {
8896
+ nextLines.push({
8897
+ id: `mock-line-${line.merchandiseId}`,
8898
+ merchandiseId: line.merchandiseId,
8899
+ quantity: line.quantity
8900
+ });
8901
+ }
8902
+ }
8903
+ const saved = saveMockCartState({ ...cart, lines: nextLines });
8904
+ return buildMockCart(saved, this.currencyCode, this.siteUrl);
8905
+ }
8906
+ async removeFromCart(cartId, lineIds) {
8907
+ const cart = getMockCartState(cartId);
8908
+ if (!cart) {
8909
+ throw new ResourceNotFoundError("cart", cartId);
8910
+ }
8911
+ const saved = saveMockCartState({
8912
+ ...cart,
8913
+ lines: cart.lines.filter((line) => !lineIds.includes(line.id))
8914
+ });
8915
+ return buildMockCart(saved, this.currencyCode, this.siteUrl);
8916
+ }
8917
+ async updateCart(cartId, lines) {
8918
+ const cart = getMockCartState(cartId);
8919
+ if (!cart) {
8920
+ throw new ResourceNotFoundError("cart", cartId);
8921
+ }
8922
+ for (const line of lines) {
8923
+ if (!this.hasVariant(line.merchandiseId, cart.regionId)) {
8924
+ throw new ResourceNotFoundError("variant", line.merchandiseId);
8925
+ }
8926
+ }
8927
+ const updates = new Map(lines.map((line) => [line.id, line]));
8928
+ const nextLines = cart.lines.map((line) => {
8929
+ const update = updates.get(line.id);
8930
+ if (!update) {
8931
+ return line;
8932
+ }
8933
+ return {
8934
+ ...line,
8935
+ merchandiseId: update.merchandiseId,
8936
+ quantity: update.quantity
8937
+ };
8938
+ }).filter((line) => line.quantity > 0);
8939
+ const saved = saveMockCartState({ ...cart, lines: nextLines });
8940
+ return buildMockCart(saved, this.currencyCode, this.siteUrl);
8941
+ }
8942
+ async getRegions() {
8943
+ return getMockRegions();
8944
+ }
8945
+ async completeCheckout(cartId) {
8946
+ const cart = getMockCartState(cartId);
8947
+ if (!cart) {
8948
+ throw new ResourceNotFoundError("cart", cartId);
8949
+ }
8950
+ const order = completeMockCheckout(cart, this.currencyCode, this.siteUrl);
8951
+ return order;
8952
+ }
8953
+ async getShippingMethods(cartId) {
8954
+ const cart = getMockCartState(cartId);
8955
+ return getMockShippingMethods(cart?.regionId);
8956
+ }
8957
+ async getRegionCountries(regionId) {
8958
+ return getMockRegionCountries(regionId);
8959
+ }
8960
+ async addShippingMethod(cartId, methodId) {
8961
+ const cart = getMockCartState(cartId);
8962
+ if (!cart) {
8963
+ throw new ResourceNotFoundError("cart", cartId);
8964
+ }
8965
+ const allowed = getMockShippingMethods(cart.regionId).some((method) => method.id === methodId);
8966
+ if (!allowed) {
8967
+ throw new ProviderAPIError(`Unknown mock shipping method: ${methodId}`, "mock", 400);
8968
+ }
8969
+ const saved = saveMockCartState({ ...cart, shippingMethodId: methodId });
8970
+ return buildMockCart(saved, this.currencyCode, this.siteUrl);
8971
+ }
8972
+ async initiatePaymentSession(cartId, providerId) {
8973
+ const cart = getMockCartState(cartId);
8974
+ if (!cart) {
8975
+ throw new ResourceNotFoundError("cart", cartId);
8976
+ }
8977
+ const allowed = getMockPaymentMethods(this.currencyCode).some((method) => method.id === providerId);
8978
+ if (!allowed) {
8979
+ throw new ProviderAPIError(`Unknown mock payment method: ${providerId}`, "mock", 400);
8980
+ }
8981
+ saveMockCartState({ ...cart, paymentProviderId: providerId });
8982
+ }
8983
+ async applyDiscountCode(cartId, code) {
8984
+ const cart = getMockCartState(cartId);
8985
+ if (!cart) {
8986
+ throw new ResourceNotFoundError("cart", cartId);
8987
+ }
8988
+ const normalized = code.trim();
8989
+ if (!normalized) {
8990
+ throw new ProviderAPIError("Discount code is required.", "mock", 400);
8991
+ }
8992
+ const saved = saveMockCartState({ ...cart, discountCode: normalized });
8993
+ return buildMockCart(saved, this.currencyCode, this.siteUrl);
8994
+ }
8995
+ async removeDiscountCode(cartId, _code) {
8996
+ const cart = getMockCartState(cartId);
8997
+ if (!cart) {
8998
+ throw new ResourceNotFoundError("cart", cartId);
8999
+ }
9000
+ const saved = saveMockCartState({ ...cart, discountCode: void 0 });
9001
+ return buildMockCart(saved, this.currencyCode, this.siteUrl);
9002
+ }
9003
+ async getOrders(_options) {
9004
+ return getMockAdminOrders().orders;
9005
+ }
9006
+ async getPaymentMethods(options) {
9007
+ return getMockPaymentMethods(options.currencyCode ?? this.currencyCode);
9008
+ }
9009
+ async getStockLocations() {
9010
+ return getMockStockLocations();
9011
+ }
9012
+ async checkInventoryLevels(variantId) {
9013
+ return getMockInventoryLevels(variantId);
9014
+ }
9015
+ async validateCartStock(cartId) {
9016
+ const cart = getMockCartState(cartId);
9017
+ if (!cart) {
9018
+ throw new ResourceNotFoundError("cart", cartId);
9019
+ }
9020
+ return validateMockCartStock(cart);
9021
+ }
9022
+ async getFulfillmentOptions(cartId, _salesChannelId) {
9023
+ const cart = getMockCartState(cartId);
9024
+ return getMockFulfillmentOptions(cart?.regionId);
9025
+ }
9026
+ async cancelOrder(orderId) {
9027
+ const order = getMockOrder(orderId);
9028
+ if (!order) {
9029
+ throw new ResourceNotFoundError("order", orderId);
9030
+ }
9031
+ return saveMockOrder({
9032
+ ...order,
9033
+ status: "cancelled",
9034
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
9035
+ cancelledAt: (/* @__PURE__ */ new Date()).toISOString()
9036
+ });
9037
+ }
9038
+ async createFulfillment(_orderId, _items) {
9039
+ return;
9040
+ }
9041
+ async createRefund(orderId, _amount, _reason) {
9042
+ const order = getMockOrder(orderId);
9043
+ if (!order) {
9044
+ throw new ResourceNotFoundError("order", orderId);
9045
+ }
9046
+ saveMockOrder({
9047
+ ...order,
9048
+ status: "refunded",
9049
+ paymentStatus: "refunded",
9050
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
9051
+ });
9052
+ }
9053
+ async getAdminOrders() {
9054
+ return getMockAdminOrders();
9055
+ }
9056
+ async getAdminRegions() {
9057
+ return getMockAdminRegions();
9058
+ }
9059
+ async getStorefrontContext() {
9060
+ return {
9061
+ id: "mock-storefront",
9062
+ name: "Mock Storefront",
9063
+ platformType: "mock",
9064
+ requiresProductScoping: false,
9065
+ currencyCode: this.currencyCode,
9066
+ locale: "en-US",
9067
+ metadata: {
9068
+ mode: "design"
9069
+ }
9070
+ };
9071
+ }
9072
+ isStorefrontRequired() {
9073
+ return false;
9074
+ }
9075
+ };
9076
+
8045
9077
  // src/factory/index.ts
8046
9078
  function createProvider(options) {
8047
9079
  if (!options || !options.provider) {
@@ -8052,6 +9084,8 @@ function createProvider(options) {
8052
9084
  }
8053
9085
  const config = options.config;
8054
9086
  switch (options.provider) {
9087
+ case "mock":
9088
+ return new MockProvider(config);
8055
9089
  case "medusa":
8056
9090
  return new MedusaProvider(config);
8057
9091
  case "shopify":
@@ -8087,10 +9121,12 @@ function isProviderType(provider, type) {
8087
9121
  ConfigurationError,
8088
9122
  MAGENTO_CAPABILITIES,
8089
9123
  MEDUSA_CAPABILITIES,
9124
+ MOCK_CAPABILITIES,
8090
9125
  MagentoProvider,
8091
9126
  MedusaMetadataAdapter,
8092
9127
  MedusaProvider,
8093
9128
  MetricsService,
9129
+ MockProvider,
8094
9130
  ProviderAPIError,
8095
9131
  ProviderNotFoundError,
8096
9132
  ResourceNotFoundError,