@thorprovider/adapters 5.0.7 → 5.0.9
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.d.mts +43 -3
- package/dist/index.d.ts +43 -3
- package/dist/index.js +155 -34
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +155 -34
- package/dist/index.mjs.map +1 -1
- package/docs/multi-tenant-channel-scoping.md +42 -4
- package/package.json +2 -2
- package/src/core/errors.ts +2 -1
- package/src/providers/medusa/index.test.ts +40 -0
- package/src/providers/medusa/index.ts +173 -36
- package/src/providers/medusa/transforms.ts +21 -13
package/dist/index.d.mts
CHANGED
|
@@ -320,7 +320,8 @@ declare class ResourceNotFoundError extends CommerceError {
|
|
|
320
320
|
*/
|
|
321
321
|
declare class ProviderAPIError extends CommerceError {
|
|
322
322
|
readonly provider: string;
|
|
323
|
-
|
|
323
|
+
readonly details?: Record<string, unknown> | undefined;
|
|
324
|
+
constructor(message: string, provider: string, status?: number, details?: Record<string, unknown> | undefined);
|
|
324
325
|
}
|
|
325
326
|
/**
|
|
326
327
|
* Extract error message from various error types
|
|
@@ -1500,6 +1501,7 @@ declare class MedusaProvider implements CommerceProvider {
|
|
|
1500
1501
|
private retryConfig;
|
|
1501
1502
|
private baseUrl;
|
|
1502
1503
|
private publishableKey;
|
|
1504
|
+
private regionId?;
|
|
1503
1505
|
/** Secret API key for admin operations (if configured). */
|
|
1504
1506
|
private adminApiKey?;
|
|
1505
1507
|
/**
|
|
@@ -1598,6 +1600,28 @@ declare class MedusaProvider implements CommerceProvider {
|
|
|
1598
1600
|
private mapAttributeTypeToFilterType;
|
|
1599
1601
|
getCollection(handle: string): Promise<Collection | undefined>;
|
|
1600
1602
|
getCollections(options?: GetCollectionsOptions): Promise<Collection[]>;
|
|
1603
|
+
/**
|
|
1604
|
+
* Returns the set of product-collection IDs that have at least one product
|
|
1605
|
+
* available in the given sales channel (legacy fallback).
|
|
1606
|
+
*
|
|
1607
|
+
* @deprecated Use `_getChannelCollections()` instead (backed by Thor Commerce plugin).
|
|
1608
|
+
*/
|
|
1609
|
+
private _getActiveCollectionIds;
|
|
1610
|
+
/**
|
|
1611
|
+
* Fetch channel-scoped collections from Thor Commerce plugin.
|
|
1612
|
+
* Uses `GET /store/thor/collections` — the endpoint resolves the sales
|
|
1613
|
+
* channel from the publishable key header itself.
|
|
1614
|
+
*
|
|
1615
|
+
* Falls back to `_getActiveCollectionIds()` (legacy N+1) when the plugin is
|
|
1616
|
+
* not available; the legacy fallback needs an explicit `salesChannelId`.
|
|
1617
|
+
* Returns `null` when no scoping could be performed.
|
|
1618
|
+
*/
|
|
1619
|
+
private _getChannelCollections;
|
|
1620
|
+
/**
|
|
1621
|
+
* Legacy fallback for `_getChannelCollections()` when the Thor plugin is not
|
|
1622
|
+
* installed. Requires an explicit `salesChannelId`; returns `null` otherwise.
|
|
1623
|
+
*/
|
|
1624
|
+
private _legacyChannelCollections;
|
|
1601
1625
|
getCollectionProducts(handle: string, options?: CollectionProductsOptions): Promise<Product[]>;
|
|
1602
1626
|
getCategory(idOrHandle: string): Promise<ProductCategory | undefined>;
|
|
1603
1627
|
/**
|
|
@@ -1616,14 +1640,30 @@ declare class MedusaProvider implements CommerceProvider {
|
|
|
1616
1640
|
private _getActiveCategoryIds;
|
|
1617
1641
|
/**
|
|
1618
1642
|
* Fetch channel-scoped categories from Thor Commerce plugin.
|
|
1619
|
-
* Uses `GET /store/thor/categories` — a single indexed query.
|
|
1620
|
-
*
|
|
1643
|
+
* Uses `GET /store/thor/categories` — a single indexed query. The endpoint
|
|
1644
|
+
* resolves the sales channel from the publishable key header itself, so no
|
|
1645
|
+
* explicit channel id is required when the plugin is installed.
|
|
1646
|
+
*
|
|
1647
|
+
* Falls back to `_getActiveCategoryIds()` (legacy N+1) when the plugin is
|
|
1648
|
+
* not available. The legacy fallback needs an explicit `salesChannelId`;
|
|
1649
|
+
* when none is provided and the plugin is unavailable, returns `null` to
|
|
1650
|
+
* signal that scoping is not possible and the caller should skip filtering.
|
|
1651
|
+
*
|
|
1652
|
+
* @param salesChannelId - Only used by the legacy fallback (plugin absent).
|
|
1653
|
+
* @returns Set of category IDs active in the channel, or `null` when no
|
|
1654
|
+
* scoping could be performed.
|
|
1621
1655
|
*/
|
|
1622
1656
|
private _getChannelCategories;
|
|
1657
|
+
/**
|
|
1658
|
+
* Legacy fallback for `_getChannelCategories()` when the Thor plugin is not
|
|
1659
|
+
* installed. Requires an explicit `salesChannelId`; returns `null` otherwise.
|
|
1660
|
+
*/
|
|
1661
|
+
private _legacyChannelCategories;
|
|
1623
1662
|
getCategories(options?: GetCategoriesOptions): Promise<ProductCategory[]>;
|
|
1624
1663
|
getCategoryProducts(handle: string, options?: CollectionProductsOptions): Promise<Product[]>;
|
|
1625
1664
|
createCart(options?: {
|
|
1626
1665
|
regionId?: string;
|
|
1666
|
+
salesChannelId?: string;
|
|
1627
1667
|
}): Promise<Cart>;
|
|
1628
1668
|
getCart(cartId: string): Promise<Cart | undefined>;
|
|
1629
1669
|
addToCart(cartId: string, lines: CartLineInput[]): Promise<Cart>;
|
package/dist/index.d.ts
CHANGED
|
@@ -320,7 +320,8 @@ declare class ResourceNotFoundError extends CommerceError {
|
|
|
320
320
|
*/
|
|
321
321
|
declare class ProviderAPIError extends CommerceError {
|
|
322
322
|
readonly provider: string;
|
|
323
|
-
|
|
323
|
+
readonly details?: Record<string, unknown> | undefined;
|
|
324
|
+
constructor(message: string, provider: string, status?: number, details?: Record<string, unknown> | undefined);
|
|
324
325
|
}
|
|
325
326
|
/**
|
|
326
327
|
* Extract error message from various error types
|
|
@@ -1500,6 +1501,7 @@ declare class MedusaProvider implements CommerceProvider {
|
|
|
1500
1501
|
private retryConfig;
|
|
1501
1502
|
private baseUrl;
|
|
1502
1503
|
private publishableKey;
|
|
1504
|
+
private regionId?;
|
|
1503
1505
|
/** Secret API key for admin operations (if configured). */
|
|
1504
1506
|
private adminApiKey?;
|
|
1505
1507
|
/**
|
|
@@ -1598,6 +1600,28 @@ declare class MedusaProvider implements CommerceProvider {
|
|
|
1598
1600
|
private mapAttributeTypeToFilterType;
|
|
1599
1601
|
getCollection(handle: string): Promise<Collection | undefined>;
|
|
1600
1602
|
getCollections(options?: GetCollectionsOptions): Promise<Collection[]>;
|
|
1603
|
+
/**
|
|
1604
|
+
* Returns the set of product-collection IDs that have at least one product
|
|
1605
|
+
* available in the given sales channel (legacy fallback).
|
|
1606
|
+
*
|
|
1607
|
+
* @deprecated Use `_getChannelCollections()` instead (backed by Thor Commerce plugin).
|
|
1608
|
+
*/
|
|
1609
|
+
private _getActiveCollectionIds;
|
|
1610
|
+
/**
|
|
1611
|
+
* Fetch channel-scoped collections from Thor Commerce plugin.
|
|
1612
|
+
* Uses `GET /store/thor/collections` — the endpoint resolves the sales
|
|
1613
|
+
* channel from the publishable key header itself.
|
|
1614
|
+
*
|
|
1615
|
+
* Falls back to `_getActiveCollectionIds()` (legacy N+1) when the plugin is
|
|
1616
|
+
* not available; the legacy fallback needs an explicit `salesChannelId`.
|
|
1617
|
+
* Returns `null` when no scoping could be performed.
|
|
1618
|
+
*/
|
|
1619
|
+
private _getChannelCollections;
|
|
1620
|
+
/**
|
|
1621
|
+
* Legacy fallback for `_getChannelCollections()` when the Thor plugin is not
|
|
1622
|
+
* installed. Requires an explicit `salesChannelId`; returns `null` otherwise.
|
|
1623
|
+
*/
|
|
1624
|
+
private _legacyChannelCollections;
|
|
1601
1625
|
getCollectionProducts(handle: string, options?: CollectionProductsOptions): Promise<Product[]>;
|
|
1602
1626
|
getCategory(idOrHandle: string): Promise<ProductCategory | undefined>;
|
|
1603
1627
|
/**
|
|
@@ -1616,14 +1640,30 @@ declare class MedusaProvider implements CommerceProvider {
|
|
|
1616
1640
|
private _getActiveCategoryIds;
|
|
1617
1641
|
/**
|
|
1618
1642
|
* Fetch channel-scoped categories from Thor Commerce plugin.
|
|
1619
|
-
* Uses `GET /store/thor/categories` — a single indexed query.
|
|
1620
|
-
*
|
|
1643
|
+
* Uses `GET /store/thor/categories` — a single indexed query. The endpoint
|
|
1644
|
+
* resolves the sales channel from the publishable key header itself, so no
|
|
1645
|
+
* explicit channel id is required when the plugin is installed.
|
|
1646
|
+
*
|
|
1647
|
+
* Falls back to `_getActiveCategoryIds()` (legacy N+1) when the plugin is
|
|
1648
|
+
* not available. The legacy fallback needs an explicit `salesChannelId`;
|
|
1649
|
+
* when none is provided and the plugin is unavailable, returns `null` to
|
|
1650
|
+
* signal that scoping is not possible and the caller should skip filtering.
|
|
1651
|
+
*
|
|
1652
|
+
* @param salesChannelId - Only used by the legacy fallback (plugin absent).
|
|
1653
|
+
* @returns Set of category IDs active in the channel, or `null` when no
|
|
1654
|
+
* scoping could be performed.
|
|
1621
1655
|
*/
|
|
1622
1656
|
private _getChannelCategories;
|
|
1657
|
+
/**
|
|
1658
|
+
* Legacy fallback for `_getChannelCategories()` when the Thor plugin is not
|
|
1659
|
+
* installed. Requires an explicit `salesChannelId`; returns `null` otherwise.
|
|
1660
|
+
*/
|
|
1661
|
+
private _legacyChannelCategories;
|
|
1623
1662
|
getCategories(options?: GetCategoriesOptions): Promise<ProductCategory[]>;
|
|
1624
1663
|
getCategoryProducts(handle: string, options?: CollectionProductsOptions): Promise<Product[]>;
|
|
1625
1664
|
createCart(options?: {
|
|
1626
1665
|
regionId?: string;
|
|
1666
|
+
salesChannelId?: string;
|
|
1627
1667
|
}): Promise<Cart>;
|
|
1628
1668
|
getCart(cartId: string): Promise<Cart | undefined>;
|
|
1629
1669
|
addToCart(cartId: string, lines: CartLineInput[]): Promise<Cart>;
|
package/dist/index.js
CHANGED
|
@@ -470,12 +470,14 @@ var ResourceNotFoundError = class extends CommerceError {
|
|
|
470
470
|
}
|
|
471
471
|
};
|
|
472
472
|
var ProviderAPIError = class extends CommerceError {
|
|
473
|
-
constructor(message, provider, status) {
|
|
473
|
+
constructor(message, provider, status, details) {
|
|
474
474
|
super(message, "PROVIDER_API_ERROR", status);
|
|
475
475
|
this.provider = provider;
|
|
476
|
+
this.details = details;
|
|
476
477
|
this.name = "ProviderAPIError";
|
|
477
478
|
}
|
|
478
479
|
provider;
|
|
480
|
+
details;
|
|
479
481
|
};
|
|
480
482
|
function extractErrorMessage(error) {
|
|
481
483
|
if (error instanceof Error && error.message) {
|
|
@@ -1152,6 +1154,12 @@ function transformCartItem(item, cartCurrencyCode) {
|
|
|
1152
1154
|
const product = variant?.product;
|
|
1153
1155
|
const currencyCode = resolveMedusaCartItemCurrencyCode(item, cartCurrencyCode);
|
|
1154
1156
|
const totalCents = resolveMedusaCartItemTotal(item);
|
|
1157
|
+
const merchandiseId = variant?.id ?? item.variant_id ?? "";
|
|
1158
|
+
const merchandiseTitle = variant?.title ?? item.variant_title ?? "";
|
|
1159
|
+
const productId = product?.id ?? item.product_id ?? "";
|
|
1160
|
+
const productHandle = product?.handle ?? item.product_handle ?? "";
|
|
1161
|
+
const productTitle = product?.title ?? item.product_title ?? "";
|
|
1162
|
+
const thumbnail = product?.thumbnail ?? item.thumbnail ?? "";
|
|
1155
1163
|
return {
|
|
1156
1164
|
id: item.id,
|
|
1157
1165
|
quantity: item.quantity ?? 1,
|
|
@@ -1159,17 +1167,17 @@ function transformCartItem(item, cartCurrencyCode) {
|
|
|
1159
1167
|
totalAmount: transformMoney(totalCents, currencyCode)
|
|
1160
1168
|
},
|
|
1161
1169
|
merchandise: {
|
|
1162
|
-
id:
|
|
1163
|
-
title:
|
|
1170
|
+
id: merchandiseId,
|
|
1171
|
+
title: merchandiseTitle,
|
|
1164
1172
|
selectedOptions: variant?.options?.map((opt) => ({
|
|
1165
1173
|
name: opt.option?.title ?? "",
|
|
1166
1174
|
value: opt.value ?? ""
|
|
1167
1175
|
})) ?? [],
|
|
1168
1176
|
product: {
|
|
1169
|
-
id:
|
|
1170
|
-
handle:
|
|
1171
|
-
title:
|
|
1172
|
-
featuredImage:
|
|
1177
|
+
id: productId,
|
|
1178
|
+
handle: productHandle,
|
|
1179
|
+
title: productTitle,
|
|
1180
|
+
featuredImage: thumbnail ? transformImage(thumbnail, productTitle) : transformImage("", "")
|
|
1173
1181
|
}
|
|
1174
1182
|
}
|
|
1175
1183
|
};
|
|
@@ -2306,6 +2314,7 @@ var MedusaProvider = class {
|
|
|
2306
2314
|
retryConfig;
|
|
2307
2315
|
baseUrl;
|
|
2308
2316
|
publishableKey;
|
|
2317
|
+
regionId;
|
|
2309
2318
|
/** Secret API key for admin operations (if configured). */
|
|
2310
2319
|
adminApiKey;
|
|
2311
2320
|
/**
|
|
@@ -2349,6 +2358,7 @@ var MedusaProvider = class {
|
|
|
2349
2358
|
this.currencyCode = config.currencyCode ?? "USD";
|
|
2350
2359
|
this.baseUrl = config.baseUrl;
|
|
2351
2360
|
this.publishableKey = config.publishableKey;
|
|
2361
|
+
this.regionId = config.regionId;
|
|
2352
2362
|
this.retryConfig = {
|
|
2353
2363
|
maxRetries: config.retry?.maxRetries ?? 3,
|
|
2354
2364
|
timeoutMs: config.retry?.timeoutMs ?? 1e4,
|
|
@@ -2483,16 +2493,17 @@ var MedusaProvider = class {
|
|
|
2483
2493
|
try {
|
|
2484
2494
|
return await this.withRetry(
|
|
2485
2495
|
async () => {
|
|
2496
|
+
const regionId = options?.regionId ?? this.regionId;
|
|
2486
2497
|
const baseFields = "+variants,+variants.inventory_quantity,+options,+images,+tags";
|
|
2487
2498
|
const pricingFields = ",+variants.calculated_price,+variants.prices";
|
|
2488
|
-
const fields =
|
|
2499
|
+
const fields = regionId ? baseFields + pricingFields : baseFields;
|
|
2489
2500
|
const queryParams = {
|
|
2490
2501
|
handle,
|
|
2491
2502
|
limit: 1,
|
|
2492
2503
|
fields
|
|
2493
2504
|
};
|
|
2494
|
-
if (
|
|
2495
|
-
queryParams.region_id =
|
|
2505
|
+
if (regionId) {
|
|
2506
|
+
queryParams.region_id = regionId;
|
|
2496
2507
|
}
|
|
2497
2508
|
const salesChannelId = process.env.NEXT_PUBLIC_SALES_CHANNEL_ID;
|
|
2498
2509
|
if (salesChannelId) {
|
|
@@ -2522,9 +2533,10 @@ var MedusaProvider = class {
|
|
|
2522
2533
|
try {
|
|
2523
2534
|
return await this.withRetry(
|
|
2524
2535
|
async () => {
|
|
2536
|
+
const regionId = options.regionId ?? this.regionId;
|
|
2525
2537
|
const baseFields = "+variants,+variants.inventory_quantity,+options,+images,+tags";
|
|
2526
2538
|
const pricingFields = ",+variants.calculated_price,+variants.prices";
|
|
2527
|
-
const fields =
|
|
2539
|
+
const fields = regionId ? baseFields + pricingFields : baseFields;
|
|
2528
2540
|
const SORT_KEY_MAP = {
|
|
2529
2541
|
featured: null,
|
|
2530
2542
|
// server default order
|
|
@@ -2551,8 +2563,8 @@ var MedusaProvider = class {
|
|
|
2551
2563
|
queryParams.order = mappedOrder;
|
|
2552
2564
|
}
|
|
2553
2565
|
}
|
|
2554
|
-
if (
|
|
2555
|
-
queryParams.region_id =
|
|
2566
|
+
if (regionId) {
|
|
2567
|
+
queryParams.region_id = regionId;
|
|
2556
2568
|
}
|
|
2557
2569
|
if (options.categoryIds && options.categoryIds.length > 0) {
|
|
2558
2570
|
queryParams.category_id = options.categoryIds;
|
|
@@ -2945,6 +2957,11 @@ var MedusaProvider = class {
|
|
|
2945
2957
|
limit
|
|
2946
2958
|
});
|
|
2947
2959
|
let result = collections.map(transformCollection);
|
|
2960
|
+
const salesChannelId = options?.salesChannelId ?? process.env.NEXT_PUBLIC_SALES_CHANNEL_ID;
|
|
2961
|
+
const activeCollectionIds = await this._getChannelCollections(salesChannelId);
|
|
2962
|
+
if (activeCollectionIds) {
|
|
2963
|
+
result = result.filter((c) => activeCollectionIds.has(c.id));
|
|
2964
|
+
}
|
|
2948
2965
|
if (options?.query) {
|
|
2949
2966
|
const q = options.query.toLowerCase();
|
|
2950
2967
|
result = result.filter(
|
|
@@ -2961,6 +2978,89 @@ var MedusaProvider = class {
|
|
|
2961
2978
|
);
|
|
2962
2979
|
}
|
|
2963
2980
|
}
|
|
2981
|
+
/**
|
|
2982
|
+
* Returns the set of product-collection IDs that have at least one product
|
|
2983
|
+
* available in the given sales channel (legacy fallback).
|
|
2984
|
+
*
|
|
2985
|
+
* @deprecated Use `_getChannelCollections()` instead (backed by Thor Commerce plugin).
|
|
2986
|
+
*/
|
|
2987
|
+
async _getActiveCollectionIds(salesChannelId) {
|
|
2988
|
+
this.logger.debug("_getActiveCollectionIds", { salesChannelId });
|
|
2989
|
+
const collectionIds = /* @__PURE__ */ new Set();
|
|
2990
|
+
const limit = 500;
|
|
2991
|
+
let offset = 0;
|
|
2992
|
+
while (true) {
|
|
2993
|
+
const { products } = await this.sdk.store.product.list({
|
|
2994
|
+
sales_channel_id: [salesChannelId],
|
|
2995
|
+
fields: "+collections.id",
|
|
2996
|
+
limit,
|
|
2997
|
+
offset
|
|
2998
|
+
});
|
|
2999
|
+
for (const product of products ?? []) {
|
|
3000
|
+
for (const col of product.collections ?? []) {
|
|
3001
|
+
if (col?.id) collectionIds.add(col.id);
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
if (!products || products.length < limit) break;
|
|
3005
|
+
offset += limit;
|
|
3006
|
+
}
|
|
3007
|
+
this.logger.debug("_getActiveCollectionIds result", { count: collectionIds.size });
|
|
3008
|
+
return collectionIds;
|
|
3009
|
+
}
|
|
3010
|
+
/**
|
|
3011
|
+
* Fetch channel-scoped collections from Thor Commerce plugin.
|
|
3012
|
+
* Uses `GET /store/thor/collections` — the endpoint resolves the sales
|
|
3013
|
+
* channel from the publishable key header itself.
|
|
3014
|
+
*
|
|
3015
|
+
* Falls back to `_getActiveCollectionIds()` (legacy N+1) when the plugin is
|
|
3016
|
+
* not available; the legacy fallback needs an explicit `salesChannelId`.
|
|
3017
|
+
* Returns `null` when no scoping could be performed.
|
|
3018
|
+
*/
|
|
3019
|
+
async _getChannelCollections(salesChannelId) {
|
|
3020
|
+
try {
|
|
3021
|
+
const url = `${this.baseUrl}/store/thor/collections`;
|
|
3022
|
+
const response = await fetchWithRetry(
|
|
3023
|
+
url,
|
|
3024
|
+
{
|
|
3025
|
+
method: "GET",
|
|
3026
|
+
headers: {
|
|
3027
|
+
"x-publishable-api-key": this.publishableKey,
|
|
3028
|
+
"Content-Type": "application/json"
|
|
3029
|
+
}
|
|
3030
|
+
},
|
|
3031
|
+
this.retryConfig
|
|
3032
|
+
);
|
|
3033
|
+
if (response.status === 404 || !response.ok) {
|
|
3034
|
+
this.logger.debug("Thor plugin collections endpoint unavailable, falling back to legacy method");
|
|
3035
|
+
return this._legacyChannelCollections(salesChannelId);
|
|
3036
|
+
}
|
|
3037
|
+
const data = await response.json();
|
|
3038
|
+
const collectionIds = /* @__PURE__ */ new Set();
|
|
3039
|
+
for (const col of data.collections ?? []) {
|
|
3040
|
+
if (col?.id) collectionIds.add(col.id);
|
|
3041
|
+
}
|
|
3042
|
+
this.logger.debug("_getChannelCollections result (Thor plugin)", { count: collectionIds.size });
|
|
3043
|
+
return collectionIds;
|
|
3044
|
+
} catch (error) {
|
|
3045
|
+
this.logger.warn("Failed to fetch channel collections from Thor plugin, falling back to legacy method", {
|
|
3046
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3047
|
+
});
|
|
3048
|
+
return this._legacyChannelCollections(salesChannelId);
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
/**
|
|
3052
|
+
* Legacy fallback for `_getChannelCollections()` when the Thor plugin is not
|
|
3053
|
+
* installed. Requires an explicit `salesChannelId`; returns `null` otherwise.
|
|
3054
|
+
*/
|
|
3055
|
+
async _legacyChannelCollections(salesChannelId) {
|
|
3056
|
+
if (!salesChannelId) {
|
|
3057
|
+
this.logger.warn(
|
|
3058
|
+
"Cannot scope collections: Thor plugin unavailable and no sales channel id provided; returning unscoped collections"
|
|
3059
|
+
);
|
|
3060
|
+
return null;
|
|
3061
|
+
}
|
|
3062
|
+
return this._getActiveCollectionIds(salesChannelId);
|
|
3063
|
+
}
|
|
2964
3064
|
async getCollectionProducts(handle, options = {}) {
|
|
2965
3065
|
this.logger.debug("getCollectionProducts", { handle, options });
|
|
2966
3066
|
try {
|
|
@@ -3055,8 +3155,18 @@ var MedusaProvider = class {
|
|
|
3055
3155
|
}
|
|
3056
3156
|
/**
|
|
3057
3157
|
* Fetch channel-scoped categories from Thor Commerce plugin.
|
|
3058
|
-
* Uses `GET /store/thor/categories` — a single indexed query.
|
|
3059
|
-
*
|
|
3158
|
+
* Uses `GET /store/thor/categories` — a single indexed query. The endpoint
|
|
3159
|
+
* resolves the sales channel from the publishable key header itself, so no
|
|
3160
|
+
* explicit channel id is required when the plugin is installed.
|
|
3161
|
+
*
|
|
3162
|
+
* Falls back to `_getActiveCategoryIds()` (legacy N+1) when the plugin is
|
|
3163
|
+
* not available. The legacy fallback needs an explicit `salesChannelId`;
|
|
3164
|
+
* when none is provided and the plugin is unavailable, returns `null` to
|
|
3165
|
+
* signal that scoping is not possible and the caller should skip filtering.
|
|
3166
|
+
*
|
|
3167
|
+
* @param salesChannelId - Only used by the legacy fallback (plugin absent).
|
|
3168
|
+
* @returns Set of category IDs active in the channel, or `null` when no
|
|
3169
|
+
* scoping could be performed.
|
|
3060
3170
|
*/
|
|
3061
3171
|
async _getChannelCategories(salesChannelId) {
|
|
3062
3172
|
try {
|
|
@@ -3072,15 +3182,9 @@ var MedusaProvider = class {
|
|
|
3072
3182
|
},
|
|
3073
3183
|
this.retryConfig
|
|
3074
3184
|
);
|
|
3075
|
-
if (response.status === 404) {
|
|
3076
|
-
this.logger.debug("Thor plugin categories endpoint
|
|
3077
|
-
return this.
|
|
3078
|
-
}
|
|
3079
|
-
if (!response.ok) {
|
|
3080
|
-
this.logger.warn("Thor plugin categories endpoint returned non-OK status", {
|
|
3081
|
-
status: response.status
|
|
3082
|
-
});
|
|
3083
|
-
return this._getActiveCategoryIds(salesChannelId);
|
|
3185
|
+
if (response.status === 404 || !response.ok) {
|
|
3186
|
+
this.logger.debug("Thor plugin categories endpoint unavailable, falling back to legacy method");
|
|
3187
|
+
return this._legacyChannelCategories(salesChannelId);
|
|
3084
3188
|
}
|
|
3085
3189
|
const data = await response.json();
|
|
3086
3190
|
const categoryIds = /* @__PURE__ */ new Set();
|
|
@@ -3093,8 +3197,21 @@ var MedusaProvider = class {
|
|
|
3093
3197
|
this.logger.warn("Failed to fetch channel categories from Thor plugin, falling back to legacy method", {
|
|
3094
3198
|
error: error instanceof Error ? error.message : String(error)
|
|
3095
3199
|
});
|
|
3096
|
-
return this.
|
|
3200
|
+
return this._legacyChannelCategories(salesChannelId);
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
/**
|
|
3204
|
+
* Legacy fallback for `_getChannelCategories()` when the Thor plugin is not
|
|
3205
|
+
* installed. Requires an explicit `salesChannelId`; returns `null` otherwise.
|
|
3206
|
+
*/
|
|
3207
|
+
async _legacyChannelCategories(salesChannelId) {
|
|
3208
|
+
if (!salesChannelId) {
|
|
3209
|
+
this.logger.warn(
|
|
3210
|
+
"Cannot scope categories: Thor plugin unavailable and no sales channel id provided; returning unscoped categories"
|
|
3211
|
+
);
|
|
3212
|
+
return null;
|
|
3097
3213
|
}
|
|
3214
|
+
return this._getActiveCategoryIds(salesChannelId);
|
|
3098
3215
|
}
|
|
3099
3216
|
async getCategories(options = {}) {
|
|
3100
3217
|
this.logger.debug("getCategories", { options });
|
|
@@ -3115,8 +3232,8 @@ var MedusaProvider = class {
|
|
|
3115
3232
|
const { product_categories } = await this.sdk.store.category.list(queryParams);
|
|
3116
3233
|
let result = product_categories.map(transformCategory);
|
|
3117
3234
|
const salesChannelId = options.salesChannelId ?? process.env.NEXT_PUBLIC_SALES_CHANNEL_ID;
|
|
3118
|
-
|
|
3119
|
-
|
|
3235
|
+
const activeCategoryIds = await this._getChannelCategories(salesChannelId);
|
|
3236
|
+
if (activeCategoryIds) {
|
|
3120
3237
|
result = result.filter((c) => activeCategoryIds.has(c.id));
|
|
3121
3238
|
}
|
|
3122
3239
|
if (options.query) {
|
|
@@ -3149,6 +3266,9 @@ var MedusaProvider = class {
|
|
|
3149
3266
|
offset: options.after ? parseInt(options.after, 10) : void 0,
|
|
3150
3267
|
fields: "+variants,+variants.prices,+variants.calculated_prices,+variants.inventory_quantity,+options,+images,+tags"
|
|
3151
3268
|
};
|
|
3269
|
+
if (this.regionId) {
|
|
3270
|
+
categoryQueryParams.region_id = this.regionId;
|
|
3271
|
+
}
|
|
3152
3272
|
const salesChannelId = process.env.NEXT_PUBLIC_SALES_CHANNEL_ID;
|
|
3153
3273
|
if (salesChannelId) {
|
|
3154
3274
|
categoryQueryParams.sales_channel_id = [salesChannelId];
|
|
@@ -3169,7 +3289,10 @@ var MedusaProvider = class {
|
|
|
3169
3289
|
// Cart Operations
|
|
3170
3290
|
// ============================================
|
|
3171
3291
|
async createCart(options) {
|
|
3172
|
-
this.logger.debug("createCart", {
|
|
3292
|
+
this.logger.debug("createCart", {
|
|
3293
|
+
regionId: options?.regionId,
|
|
3294
|
+
salesChannelId: options?.salesChannelId
|
|
3295
|
+
});
|
|
3173
3296
|
try {
|
|
3174
3297
|
const createPayload = {};
|
|
3175
3298
|
if (options?.regionId) {
|
|
@@ -3177,6 +3300,10 @@ var MedusaProvider = class {
|
|
|
3177
3300
|
} else {
|
|
3178
3301
|
createPayload.currency_code = this.currencyCode.toLowerCase();
|
|
3179
3302
|
}
|
|
3303
|
+
const salesChannelId = options?.salesChannelId ?? process.env.NEXT_PUBLIC_SALES_CHANNEL_ID;
|
|
3304
|
+
if (salesChannelId) {
|
|
3305
|
+
createPayload.sales_channel_id = salesChannelId;
|
|
3306
|
+
}
|
|
3180
3307
|
const { cart } = await this.sdk.store.cart.create(createPayload);
|
|
3181
3308
|
return transformCart(cart);
|
|
3182
3309
|
} catch (error) {
|
|
@@ -3218,13 +3345,11 @@ var MedusaProvider = class {
|
|
|
3218
3345
|
"retrieveCartBeforeAdd",
|
|
3219
3346
|
retrieveCtx
|
|
3220
3347
|
);
|
|
3221
|
-
console.log("retrieveResult =", JSON.stringify(retrieveResult, null, 2));
|
|
3222
3348
|
const backendCart = retrieveResult?.cart;
|
|
3223
3349
|
if (!backendCart) {
|
|
3224
3350
|
this.logger.warn("Cart not found before addToCart", { cartId });
|
|
3225
3351
|
throw new ProviderAPIError("Cart not found. Please refresh and try again.", "medusa", 404);
|
|
3226
3352
|
}
|
|
3227
|
-
console.log("backendCart =", JSON.stringify(backendCart, null, 2));
|
|
3228
3353
|
if (!backendCart.region_id) {
|
|
3229
3354
|
this.logger.warn("Attempted to add item to cart with no region_id", { cartId });
|
|
3230
3355
|
throw new ProviderAPIError(
|
|
@@ -3273,15 +3398,11 @@ var MedusaProvider = class {
|
|
|
3273
3398
|
404
|
|
3274
3399
|
);
|
|
3275
3400
|
}
|
|
3276
|
-
console.log("Found Variant:", JSON.stringify(foundVariant, null, 2));
|
|
3277
3401
|
const variant = foundVariant;
|
|
3278
3402
|
let hasPriceForRegion = false;
|
|
3279
3403
|
if (variant?.calculated_price && (variant.calculated_price.calculated_amount ?? null) !== null) {
|
|
3280
3404
|
const calcCurrency = (variant.calculated_price.currency_code || "").toLowerCase();
|
|
3281
3405
|
const cartCurrency = (backendCart.currency_code || this.currencyCode || "").toLowerCase();
|
|
3282
|
-
console.log("calcCurrency =", calcCurrency);
|
|
3283
|
-
console.log("cartCurrency =", cartCurrency);
|
|
3284
|
-
console.log("this.currencyCode =", this.currencyCode);
|
|
3285
3406
|
if (!cartCurrency || calcCurrency === cartCurrency || calcCurrency === this.currencyCode.toLowerCase()) {
|
|
3286
3407
|
hasPriceForRegion = true;
|
|
3287
3408
|
}
|