@reactionary/medusa 0.6.3

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 (72) hide show
  1. package/README.md +11 -0
  2. package/capabilities/cart.capability.js +517 -0
  3. package/capabilities/category.capability.js +216 -0
  4. package/capabilities/checkout.capability.js +533 -0
  5. package/capabilities/identity.capability.js +128 -0
  6. package/capabilities/inventory.capability.js +142 -0
  7. package/capabilities/order-search.capability.js +161 -0
  8. package/capabilities/order.capability.js +179 -0
  9. package/capabilities/price.capability.js +120 -0
  10. package/capabilities/product-associations.capability.js +148 -0
  11. package/capabilities/product-recommendations.capability.js +116 -0
  12. package/capabilities/product-search.capability.js +177 -0
  13. package/capabilities/product.capability.js +252 -0
  14. package/capabilities/profile.capability.js +333 -0
  15. package/core/client.js +269 -0
  16. package/core/initialize.js +290 -0
  17. package/core/initialize.types.js +13 -0
  18. package/factories/cart/cart.factory.js +17 -0
  19. package/factories/category/category.factory.js +17 -0
  20. package/factories/checkout/checkout.factory.js +22 -0
  21. package/factories/identity/identity.factory.js +12 -0
  22. package/factories/index.js +12 -0
  23. package/factories/inventory/inventory.factory.js +12 -0
  24. package/factories/order/order.factory.js +12 -0
  25. package/factories/order-search/order-search.factory.js +12 -0
  26. package/factories/price/price.factory.js +12 -0
  27. package/factories/product/product.factory.js +12 -0
  28. package/factories/product-associations/product-associations.factory.js +12 -0
  29. package/factories/product-search/product-search.factory.js +12 -0
  30. package/factories/profile/profile.factory.js +12 -0
  31. package/index.js +17 -0
  32. package/package.json +17 -0
  33. package/schema/capabilities.schema.js +43 -0
  34. package/schema/configuration.schema.js +11 -0
  35. package/schema/medusa.schema.js +26 -0
  36. package/src/capabilities/cart.capability.d.ts +100 -0
  37. package/src/capabilities/category.capability.d.ts +36 -0
  38. package/src/capabilities/checkout.capability.d.ts +88 -0
  39. package/src/capabilities/identity.capability.d.ts +13 -0
  40. package/src/capabilities/inventory.capability.d.ts +21 -0
  41. package/src/capabilities/order-search.capability.d.ts +53 -0
  42. package/src/capabilities/order.capability.d.ts +49 -0
  43. package/src/capabilities/price.capability.d.ts +16 -0
  44. package/src/capabilities/product-associations.capability.d.ts +19 -0
  45. package/src/capabilities/product-recommendations.capability.d.ts +41 -0
  46. package/src/capabilities/product-search.capability.d.ts +45 -0
  47. package/src/capabilities/product.capability.d.ts +32 -0
  48. package/src/capabilities/profile.capability.d.ts +39 -0
  49. package/src/core/client.d.ts +81 -0
  50. package/src/core/initialize.d.ts +5 -0
  51. package/src/core/initialize.types.d.ts +108 -0
  52. package/src/factories/cart/cart.factory.d.ts +9 -0
  53. package/src/factories/category/category.factory.d.ts +9 -0
  54. package/src/factories/checkout/checkout.factory.d.ts +11 -0
  55. package/src/factories/identity/identity.factory.d.ts +7 -0
  56. package/src/factories/index.d.ts +12 -0
  57. package/src/factories/inventory/inventory.factory.d.ts +7 -0
  58. package/src/factories/order/order.factory.d.ts +7 -0
  59. package/src/factories/order-search/order-search.factory.d.ts +7 -0
  60. package/src/factories/price/price.factory.d.ts +9 -0
  61. package/src/factories/product/product.factory.d.ts +7 -0
  62. package/src/factories/product-associations/product-associations.factory.d.ts +7 -0
  63. package/src/factories/product-search/product-search.factory.d.ts +7 -0
  64. package/src/factories/profile/profile.factory.d.ts +7 -0
  65. package/src/index.d.ts +17 -0
  66. package/src/schema/capabilities.schema.d.ts +116 -0
  67. package/src/schema/configuration.schema.d.ts +9 -0
  68. package/src/schema/medusa.schema.d.ts +33 -0
  69. package/src/test/test-utils.d.ts +2 -0
  70. package/src/utils/medusa-helpers.d.ts +35 -0
  71. package/test/test-utils.js +12 -0
  72. package/utils/medusa-helpers.js +72 -0
@@ -0,0 +1,148 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result)
9
+ __defProp(target, key, result);
10
+ return result;
11
+ };
12
+ import {
13
+ ProductAssociationsCapability,
14
+ Reactionary,
15
+ ImageSchema,
16
+ ProductSearchResultItemVariantSchema,
17
+ ProductVariantIdentifierSchema,
18
+ success
19
+ } from "@reactionary/core";
20
+ class MedusaProductAssociationsCapability extends ProductAssociationsCapability {
21
+ config;
22
+ medusa;
23
+ factory;
24
+ constructor(config, cache, context, medusa, factory) {
25
+ super(cache, context);
26
+ this.config = config;
27
+ this.medusa = medusa;
28
+ this.factory = factory;
29
+ }
30
+ async fetchAssociatedProductsFor(productKey, maxNumberOfAssociations, attributeName) {
31
+ const client = await this.medusa.getClient();
32
+ const productResponse = await client.store.product.list({
33
+ external_id: productKey.key,
34
+ limit: 1,
35
+ offset: 0,
36
+ fields: "metadata.*,external_id"
37
+ });
38
+ let product;
39
+ if (!productResponse.products || productResponse.products.length === 0)
40
+ return [];
41
+ else
42
+ product = productResponse.products[0];
43
+ if (!product) {
44
+ return [];
45
+ }
46
+ const associationsMetadata = (product.metadata?.[attributeName] || "").split(";");
47
+ if (!associationsMetadata || associationsMetadata.length === 0 || associationsMetadata[0] === "") {
48
+ return [];
49
+ }
50
+ const associatedProductIds = associationsMetadata.slice(0, maxNumberOfAssociations);
51
+ const associatedProductsResponse = await client.store.product.list({
52
+ external_id: associatedProductIds,
53
+ fields: "+metadata.*,+external_id",
54
+ offset: 0,
55
+ limit: maxNumberOfAssociations
56
+ });
57
+ return associatedProductsResponse.products.map((product2) => this.parseSingle(product2));
58
+ }
59
+ async getAccessories(query) {
60
+ const associatedProducts = await this.fetchAssociatedProductsFor(query.forProduct, query.numberOfAccessories || 4, "reactionaryaccessories");
61
+ const result = associatedProducts.map((product) => ({
62
+ associationIdentifier: {
63
+ key: `${query.forProduct.key}-accessory-${product.identifier.key}`
64
+ },
65
+ associationReturnType: "productSearchResultItem",
66
+ product
67
+ }));
68
+ return success(result.map((x) => this.factory.parseAssociation(this.context, x)));
69
+ }
70
+ async getSpareparts(query) {
71
+ const associatedProducts = await this.fetchAssociatedProductsFor(query.forProduct, query.numberOfSpareparts || 4, "reactionaryspareparts");
72
+ const result = associatedProducts.map((product) => ({
73
+ associationIdentifier: {
74
+ key: `${query.forProduct.key}-sparepart-${product.identifier.key}`
75
+ },
76
+ associationReturnType: "productSearchResultItem",
77
+ product
78
+ }));
79
+ return success(result.map((x) => this.factory.parseAssociation(this.context, x)));
80
+ }
81
+ async getReplacements(query) {
82
+ const associatedProducts = await this.fetchAssociatedProductsFor(query.forProduct, query.numberOfReplacements || 4, "reactionaryreplacements");
83
+ const result = associatedProducts.map((product) => ({
84
+ associationIdentifier: {
85
+ key: `${query.forProduct.key}-replacement-${product.identifier.key}`
86
+ },
87
+ associationReturnType: "productSearchResultItem",
88
+ product
89
+ }));
90
+ return success(result.map((x) => this.factory.parseAssociation(this.context, x)));
91
+ }
92
+ parseSingle(_body) {
93
+ const heroVariant = _body.variants?.[0];
94
+ const identifier = { key: _body.external_id || _body.id };
95
+ const slug = _body.handle;
96
+ const name = heroVariant?.title || _body.title;
97
+ const variants = [];
98
+ if (heroVariant) {
99
+ variants.push(this.parseVariant(heroVariant, _body));
100
+ }
101
+ const result = {
102
+ identifier,
103
+ name,
104
+ slug,
105
+ variants
106
+ };
107
+ return result;
108
+ }
109
+ parseVariant(variant, product) {
110
+ const img = ImageSchema.parse({
111
+ sourceUrl: product.images?.[0].url ?? "",
112
+ altText: product.title || void 0
113
+ });
114
+ return ProductSearchResultItemVariantSchema.parse({
115
+ variant: ProductVariantIdentifierSchema.parse({
116
+ sku: variant.sku || ""
117
+ }),
118
+ image: img
119
+ });
120
+ }
121
+ }
122
+ __decorateClass([
123
+ Reactionary({
124
+ cache: true,
125
+ cacheTimeToLiveInSeconds: 300,
126
+ currencyDependentCaching: false,
127
+ localeDependentCaching: false
128
+ })
129
+ ], MedusaProductAssociationsCapability.prototype, "getAccessories", 1);
130
+ __decorateClass([
131
+ Reactionary({
132
+ cache: true,
133
+ cacheTimeToLiveInSeconds: 300,
134
+ currencyDependentCaching: false,
135
+ localeDependentCaching: false
136
+ })
137
+ ], MedusaProductAssociationsCapability.prototype, "getSpareparts", 1);
138
+ __decorateClass([
139
+ Reactionary({
140
+ cache: true,
141
+ cacheTimeToLiveInSeconds: 300,
142
+ currencyDependentCaching: false,
143
+ localeDependentCaching: false
144
+ })
145
+ ], MedusaProductAssociationsCapability.prototype, "getReplacements", 1);
146
+ export {
147
+ MedusaProductAssociationsCapability
148
+ };
@@ -0,0 +1,116 @@
1
+ import {
2
+ error,
3
+ ImageSchema,
4
+ ProductRecommendationsCapability,
5
+ ProductSearchResultItemVariantSchema,
6
+ ProductVariantIdentifierSchema,
7
+ success
8
+ } from "@reactionary/core";
9
+ import createDebug from "debug";
10
+ const debug = createDebug("reactionary:medusa:product-recommendations");
11
+ class MedusaProductRecommendationsCapability extends ProductRecommendationsCapability {
12
+ config;
13
+ medusaApi;
14
+ constructor(config, cache, context, medusaApi) {
15
+ super(cache, context);
16
+ this.config = config;
17
+ this.medusaApi = medusaApi;
18
+ }
19
+ getCollectionPayload(query, collection) {
20
+ return {
21
+ collection_id: [collection.id],
22
+ limit: query.numberOfRecommendations,
23
+ fields: "+variants.id,+variants.sku,+external_id"
24
+ };
25
+ }
26
+ /**
27
+ * Get product recommendations from a Medusa collection
28
+ *
29
+ * This method fetches products from a specific Medusa collection by name or handle.
30
+ * It's useful for displaying curated product lists like "Featured Products",
31
+ * "New Arrivals", "Best Sellers", etc.
32
+ */
33
+ async getCollection(query) {
34
+ const client = await this.medusaApi.getClient();
35
+ try {
36
+ if (debug.enabled) {
37
+ debug(`Fetching collection: ${query.collectionName}`);
38
+ }
39
+ const collectionsResponse = await client.store.collection.list({
40
+ handle: query.collectionName,
41
+ limit: 1
42
+ });
43
+ if (!collectionsResponse.collections || collectionsResponse.collections.length === 0) {
44
+ if (debug.enabled) {
45
+ debug(`Collection not found: ${query.collectionName}`);
46
+ }
47
+ return error({
48
+ type: "NotFound",
49
+ identifier: query.collectionName
50
+ });
51
+ }
52
+ const collection = collectionsResponse.collections[0];
53
+ if (debug.enabled) {
54
+ debug(`Found collection: ${collection.title} (${collection.id})`);
55
+ }
56
+ const productsResponse = await client.store.product.list(this.getCollectionPayload(query, collection));
57
+ if (debug.enabled) {
58
+ debug(`Found ${productsResponse.products.length} products in collection`);
59
+ }
60
+ const recommendations = [];
61
+ for (const productRes of productsResponse.products) {
62
+ const product = this.parseSearchResultItem(productRes);
63
+ recommendations.push({
64
+ recommendationIdentifier: {
65
+ key: `${collection.id}_${productRes.id}`,
66
+ algorithm: "collection"
67
+ },
68
+ recommendationReturnType: "productSearchResultItem",
69
+ product
70
+ });
71
+ }
72
+ if (debug.enabled) {
73
+ debug(`Returning ${recommendations.length} recommendations`);
74
+ }
75
+ return success(recommendations);
76
+ } catch (error2) {
77
+ if (debug.enabled) {
78
+ debug(`Error fetching collection recommendations: %O`, error2);
79
+ }
80
+ console.error("Error fetching collection recommendations:", error2);
81
+ return success([]);
82
+ }
83
+ }
84
+ parseSearchResultItem(_body) {
85
+ const heroVariant = _body.variants?.[0];
86
+ const identifier = { key: _body.external_id || _body.id };
87
+ const slug = _body.handle;
88
+ const name = heroVariant?.title || _body.title;
89
+ const variants = [];
90
+ if (heroVariant) {
91
+ variants.push(this.parseVariant(heroVariant, _body));
92
+ }
93
+ const result = {
94
+ identifier,
95
+ name,
96
+ slug,
97
+ variants
98
+ };
99
+ return result;
100
+ }
101
+ parseVariant(variant, product) {
102
+ const img = ImageSchema.parse({
103
+ sourceUrl: product.images?.[0].url ?? "",
104
+ altText: product.title || void 0
105
+ });
106
+ return ProductSearchResultItemVariantSchema.parse({
107
+ variant: ProductVariantIdentifierSchema.parse({
108
+ sku: variant.sku || ""
109
+ }),
110
+ image: img
111
+ });
112
+ }
113
+ }
114
+ export {
115
+ MedusaProductRecommendationsCapability
116
+ };
@@ -0,0 +1,177 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result)
9
+ __defProp(target, key, result);
10
+ return result;
11
+ };
12
+ import {
13
+ ProductSearchCapability,
14
+ ProductSearchQueryByTermSchema,
15
+ ImageSchema,
16
+ ProductVariantIdentifierSchema,
17
+ ProductVariantOptionSchema,
18
+ ProductOptionIdentifierSchema,
19
+ ProductSearchResultItemVariantSchema,
20
+ Reactionary,
21
+ ProductSearchResultSchema,
22
+ FacetValueIdentifierSchema,
23
+ FacetIdentifierSchema,
24
+ success
25
+ } from "@reactionary/core";
26
+ import createDebug from "debug";
27
+ const debug = createDebug("reactionary:medusa:search");
28
+ class MedusaProductSearchCapability extends ProductSearchCapability {
29
+ constructor(config, cache, context, medusaApi, factory) {
30
+ super(cache, context);
31
+ this.medusaApi = medusaApi;
32
+ this.config = config;
33
+ this.factory = factory;
34
+ }
35
+ config;
36
+ factory;
37
+ async resolveCategoryIdByExternalId(externalId) {
38
+ const sdk = await this.medusaApi.getClient();
39
+ let offset = 0;
40
+ const limit = 50;
41
+ let candidate = void 0;
42
+ while (true) {
43
+ try {
44
+ const categoryResult = await sdk.store.category.list({
45
+ offset,
46
+ limit
47
+ });
48
+ if (categoryResult.product_categories.length === 0) {
49
+ break;
50
+ }
51
+ candidate = categoryResult.product_categories.find(
52
+ (cat) => cat.metadata?.["external_id"] === externalId
53
+ );
54
+ if (candidate) {
55
+ break;
56
+ }
57
+ offset += limit;
58
+ } catch (error) {
59
+ throw new Error(
60
+ "Category not found " + externalId + " due to error: " + error
61
+ );
62
+ break;
63
+ }
64
+ }
65
+ return candidate || null;
66
+ }
67
+ queryByTermPayload(payload, categoryIdToFind) {
68
+ const finalSearch = (payload.search.term || "").trim().replace("*", "");
69
+ return {
70
+ q: finalSearch,
71
+ ...categoryIdToFind ? { category_id: categoryIdToFind } : {},
72
+ limit: payload.search.paginationOptions.pageSize,
73
+ fields: "+metadata.*,+external_id",
74
+ offset: (payload.search.paginationOptions.pageNumber - 1) * payload.search.paginationOptions.pageSize
75
+ };
76
+ }
77
+ async queryByTerm(payload) {
78
+ const client = await this.medusaApi.getClient();
79
+ let categoryIdToFind = null;
80
+ if (payload.search.categoryFilter?.key) {
81
+ debug(
82
+ `Resolving category filter for key: ${payload.search.categoryFilter.key}`
83
+ );
84
+ const category = await this.resolveCategoryIdByExternalId(
85
+ payload.search.categoryFilter.key
86
+ );
87
+ if (category) {
88
+ categoryIdToFind = category.id;
89
+ debug(
90
+ `Resolved category filter key ${payload.search.categoryFilter.key} to id: ${categoryIdToFind}`
91
+ );
92
+ } else {
93
+ debug(
94
+ `Could not resolve category filter for key: ${payload.search.categoryFilter.key}`
95
+ );
96
+ }
97
+ }
98
+ const response = await client.store.product.list(this.queryByTermPayload(payload, categoryIdToFind));
99
+ const result = this.parsePaginatedResult(response, payload);
100
+ if (debug.enabled) {
101
+ debug(
102
+ `Search for term "${payload.search.term}" returned ${response.products.length} products (page ${payload.search.paginationOptions.pageNumber} of ${result.totalPages})`
103
+ );
104
+ }
105
+ return success(this.factory.parseSearchResult(this.context, result, payload));
106
+ }
107
+ parsePaginatedResult(remote, query) {
108
+ const products = remote.products.map(
109
+ (p) => this.parseSingle(p)
110
+ );
111
+ const result = {
112
+ identifier: {
113
+ ...query.search
114
+ },
115
+ pageNumber: (Math.ceil(remote.offset / remote.limit) || 0) + 1,
116
+ pageSize: remote.limit,
117
+ totalCount: remote.count,
118
+ totalPages: Math.ceil(remote.count / remote.limit || 0) + 1,
119
+ items: products,
120
+ facets: []
121
+ };
122
+ result.facets = [];
123
+ return result;
124
+ }
125
+ parseSingle(_body) {
126
+ const heroVariant = _body.variants?.[0];
127
+ const identifier = { key: _body.external_id || _body.id };
128
+ const slug = _body.handle;
129
+ const name = heroVariant?.title || _body.title;
130
+ const variants = [];
131
+ if (heroVariant) {
132
+ variants.push(this.parseVariant(heroVariant, _body));
133
+ }
134
+ const result = {
135
+ identifier,
136
+ name,
137
+ slug,
138
+ variants
139
+ };
140
+ return result;
141
+ }
142
+ parseVariant(variant, product) {
143
+ const img = ImageSchema.parse({
144
+ sourceUrl: product.images?.[0].url ?? "",
145
+ altText: product.title || void 0
146
+ });
147
+ return ProductSearchResultItemVariantSchema.parse({
148
+ variant: ProductVariantIdentifierSchema.parse({
149
+ sku: variant.sku || ""
150
+ }),
151
+ image: img
152
+ });
153
+ }
154
+ async createCategoryNavigationFilter(payload) {
155
+ const facetIdentifier = FacetIdentifierSchema.parse({
156
+ key: "categories"
157
+ });
158
+ const facetValueIdentifier = FacetValueIdentifierSchema.parse({
159
+ facet: facetIdentifier,
160
+ key: payload.categoryPath[payload.categoryPath.length - 1].identifier.key
161
+ });
162
+ return success(facetValueIdentifier);
163
+ }
164
+ }
165
+ __decorateClass([
166
+ Reactionary({
167
+ inputSchema: ProductSearchQueryByTermSchema,
168
+ outputSchema: ProductSearchResultSchema,
169
+ cache: true,
170
+ cacheTimeToLiveInSeconds: 300,
171
+ currencyDependentCaching: false,
172
+ localeDependentCaching: true
173
+ })
174
+ ], MedusaProductSearchCapability.prototype, "queryByTerm", 1);
175
+ export {
176
+ MedusaProductSearchCapability
177
+ };
@@ -0,0 +1,252 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result)
9
+ __defProp(target, key, result);
10
+ return result;
11
+ };
12
+ import {
13
+ CategoryIdentifierSchema,
14
+ ProductIdentifierSchema,
15
+ ProductCapability,
16
+ ProductQueryByIdSchema,
17
+ ProductQueryBySKUSchema,
18
+ ProductQueryBySlugSchema,
19
+ ProductSchema,
20
+ Reactionary,
21
+ success,
22
+ error
23
+ } from "@reactionary/core";
24
+ import createDebug from "debug";
25
+ const debug = createDebug("reactionary:medusa:product");
26
+ class MedusaProductCapability extends ProductCapability {
27
+ constructor(config, cache, context, medusaApi, factory) {
28
+ super(cache, context);
29
+ this.medusaApi = medusaApi;
30
+ this.config = config;
31
+ this.factory = factory;
32
+ }
33
+ config;
34
+ alwaysIncludedFields = ["+metadata.*", "+categories.metadata.*", "+external_id"];
35
+ factory;
36
+ getByIdPayload(payload) {
37
+ return {
38
+ external_id: payload.identifier.key,
39
+ limit: 1,
40
+ offset: 0,
41
+ fields: this.alwaysIncludedFields.join(",")
42
+ };
43
+ }
44
+ async getById(payload) {
45
+ const client = await this.medusaApi.getClient();
46
+ if (debug.enabled) {
47
+ debug(`Fetching product by ID: ${payload.identifier.key}`);
48
+ }
49
+ const response = await client.store.product.list(
50
+ this.getByIdPayload(payload)
51
+ );
52
+ if (response.count === 0) {
53
+ return error({
54
+ type: "NotFound",
55
+ identifier: payload
56
+ });
57
+ }
58
+ return success(this.parseSingle(response.products[0]));
59
+ }
60
+ getBySlugPayload(payload) {
61
+ return {
62
+ handle: payload.slug,
63
+ limit: 1,
64
+ offset: 0,
65
+ fields: this.alwaysIncludedFields.join(",")
66
+ };
67
+ }
68
+ async getBySlug(payload) {
69
+ const client = await this.medusaApi.getClient();
70
+ if (debug.enabled) {
71
+ debug(`Fetching product by slug: ${payload.slug}`);
72
+ }
73
+ const response = await client.store.product.list(this.getBySlugPayload(payload));
74
+ if (debug.enabled) {
75
+ debug(`Found ${response.count} products for slug: ${payload.slug}`);
76
+ }
77
+ if (response.count === 0) {
78
+ return error({
79
+ type: "NotFound",
80
+ identifier: payload
81
+ });
82
+ }
83
+ return success(this.parseSingle(response.products[0]));
84
+ }
85
+ async getBySKU(payload) {
86
+ if (debug.enabled) {
87
+ debug(`Fetching product by SKU: ${Array.isArray(payload) ? payload.join(", ") : payload}`);
88
+ }
89
+ const sku = payload.variant.sku;
90
+ const product = await this.medusaApi.resolveProductForSKU(sku);
91
+ const variant = product.variants?.find((v) => v.sku === sku);
92
+ if (!variant) {
93
+ throw new Error(`Variant with SKU ${sku} not found`);
94
+ }
95
+ product.variants = product.variants?.filter((v) => v.sku === sku).concat(product.variants?.filter((v) => v.sku !== sku)) || [];
96
+ return success(this.parseSingle(product));
97
+ }
98
+ parseSingle(_body) {
99
+ const identifier = ProductIdentifierSchema.parse({ key: _body.external_id || _body.id });
100
+ const name = _body.title;
101
+ const slug = _body.handle;
102
+ const description = _body.description || "" || _body.subtitle || "";
103
+ const parentCategories = [];
104
+ parentCategories.push(
105
+ ..._body.categories?.map((cat) => cat.metadata?.["external_id"]).map((id) => CategoryIdentifierSchema.parse({ key: id || "" })) || []
106
+ );
107
+ const sharedAttributes = this.parseAttributes(_body);
108
+ if (!_body.variants) {
109
+ debug("Product has no variants", _body);
110
+ throw new Error("Product has no variants " + _body.external_id);
111
+ }
112
+ const mainVariant = this.parseVariant(_body.variants[0], _body);
113
+ const otherVariants = [];
114
+ if (_body.variants.length > 1) {
115
+ otherVariants.push(
116
+ ..._body.variants.slice(1).map((variant) => this.parseVariant(variant, _body))
117
+ );
118
+ }
119
+ const result = {
120
+ brand: "",
121
+ description,
122
+ identifier,
123
+ longDescription: "",
124
+ mainVariant,
125
+ manufacturer: "",
126
+ name,
127
+ options: [],
128
+ parentCategories,
129
+ published: true,
130
+ sharedAttributes,
131
+ slug,
132
+ variants: otherVariants
133
+ };
134
+ return this.factory.parseProduct(this.context, result);
135
+ }
136
+ parseVariant(variant, product) {
137
+ const options = (variant.options ?? []).map((option) => {
138
+ const optionId = { key: option.option_id || "" };
139
+ const title = option.option?.title || "?";
140
+ const valueIdentifier = { key: option.option_id || "", option: optionId };
141
+ const value = option.value || "";
142
+ const result2 = {
143
+ identifier: optionId,
144
+ name: title,
145
+ value: {
146
+ identifier: valueIdentifier,
147
+ label: value
148
+ }
149
+ };
150
+ return result2;
151
+ });
152
+ const result = {
153
+ identifier: {
154
+ sku: variant.sku || ""
155
+ },
156
+ name: variant.title || product.title,
157
+ upc: variant.upc || "",
158
+ ean: variant.ean || "",
159
+ images: (product.images || []).map((img) => {
160
+ return {
161
+ sourceUrl: img.url,
162
+ altText: variant.title || product.title || ""
163
+ };
164
+ }),
165
+ options,
166
+ gtin: variant.ean || "",
167
+ barcode: variant.ean || ""
168
+ };
169
+ return result;
170
+ }
171
+ createSynthAttribute(key, name, value) {
172
+ const attributeIdentifier = { key };
173
+ const valueIdentifier = { key: `${key}-${value}` };
174
+ const attribute = {
175
+ identifier: { key },
176
+ name,
177
+ group: "",
178
+ values: [
179
+ {
180
+ identifier: valueIdentifier,
181
+ label: String(value),
182
+ value
183
+ }
184
+ ]
185
+ };
186
+ return attribute;
187
+ }
188
+ parseAttributes(_body) {
189
+ const sharedAttributes = [];
190
+ if (_body.origin_country) {
191
+ sharedAttributes.push(this.createSynthAttribute("origin_country", "Origin Country", _body.origin_country));
192
+ }
193
+ if (_body.height) {
194
+ sharedAttributes.push(this.createSynthAttribute("height", "Height", String(_body.height)));
195
+ }
196
+ if (_body.weight) {
197
+ sharedAttributes.push(this.createSynthAttribute("weight", "Weight", String(_body.weight)));
198
+ }
199
+ if (_body.length) {
200
+ sharedAttributes.push(this.createSynthAttribute("length", "Length", String(_body.length)));
201
+ }
202
+ if (_body.width) {
203
+ sharedAttributes.push(this.createSynthAttribute("width", "Width", String(_body.width)));
204
+ }
205
+ if (_body.material) {
206
+ sharedAttributes.push(this.createSynthAttribute("material", "Material", _body.material));
207
+ }
208
+ if (_body.metadata) {
209
+ const keysToExclude = ["reactionaryaccessories", "reactionaryreplacements", "reactionaryspareparts"];
210
+ for (const [key, value] of Object.entries(_body.metadata)) {
211
+ if (keysToExclude.includes(key)) {
212
+ continue;
213
+ }
214
+ sharedAttributes.push(this.createSynthAttribute(key, key, String(value)));
215
+ }
216
+ }
217
+ return sharedAttributes;
218
+ }
219
+ }
220
+ __decorateClass([
221
+ Reactionary({
222
+ inputSchema: ProductQueryByIdSchema,
223
+ outputSchema: ProductSchema,
224
+ cache: true,
225
+ cacheTimeToLiveInSeconds: 300,
226
+ currencyDependentCaching: false,
227
+ localeDependentCaching: true
228
+ })
229
+ ], MedusaProductCapability.prototype, "getById", 1);
230
+ __decorateClass([
231
+ Reactionary({
232
+ inputSchema: ProductQueryBySlugSchema,
233
+ outputSchema: ProductSchema,
234
+ cache: true,
235
+ cacheTimeToLiveInSeconds: 300,
236
+ currencyDependentCaching: false,
237
+ localeDependentCaching: true
238
+ })
239
+ ], MedusaProductCapability.prototype, "getBySlug", 1);
240
+ __decorateClass([
241
+ Reactionary({
242
+ inputSchema: ProductQueryBySKUSchema,
243
+ outputSchema: ProductSchema,
244
+ cache: true,
245
+ cacheTimeToLiveInSeconds: 300,
246
+ currencyDependentCaching: false,
247
+ localeDependentCaching: true
248
+ })
249
+ ], MedusaProductCapability.prototype, "getBySKU", 1);
250
+ export {
251
+ MedusaProductCapability
252
+ };