medusa-product-helper 0.0.13 → 0.0.16

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 (22) hide show
  1. package/.medusa/server/src/api/store/product-helper/products/route.js +76 -0
  2. package/.medusa/server/src/api/store/product-helper/products/validators.js +2 -1
  3. package/.medusa/server/src/config/product-helper-options.js +15 -1
  4. package/.medusa/server/src/index.js +101 -0
  5. package/.medusa/server/src/providers/filter-providers/availability-provider.js +96 -0
  6. package/.medusa/server/src/providers/filter-providers/base-filter-provider.js +32 -0
  7. package/.medusa/server/src/providers/filter-providers/base-product-provider.js +122 -0
  8. package/.medusa/server/src/providers/filter-providers/category-provider.js +55 -0
  9. package/.medusa/server/src/providers/filter-providers/collection-provider.js +53 -0
  10. package/.medusa/server/src/providers/filter-providers/index.js +94 -0
  11. package/.medusa/server/src/providers/filter-providers/metadata-provider.js +88 -0
  12. package/.medusa/server/src/providers/filter-providers/price-range-provider.js +108 -0
  13. package/.medusa/server/src/providers/filter-providers/promotion-provider.js +197 -0
  14. package/.medusa/server/src/providers/filter-providers/promotion-window-provider.js +125 -0
  15. package/.medusa/server/src/providers/filter-providers/rating-provider.js +92 -0
  16. package/.medusa/server/src/services/dynamic-filter-service.js +814 -0
  17. package/.medusa/server/src/services/filter-provider-loader.js +155 -0
  18. package/.medusa/server/src/services/filter-provider-registry.js +142 -0
  19. package/.medusa/server/src/services/product-filter-service.js +230 -0
  20. package/.medusa/server/src/utils/query-parser.js +103 -0
  21. package/README.md +89 -0
  22. package/package.json +3 -3
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CollectionFilterProvider = void 0;
4
+ const base_filter_provider_1 = require("./base-filter-provider");
5
+ /**
6
+ * Filter provider for filtering products by collection IDs.
7
+ *
8
+ * Handles the `collection_id` filter parameter, which accepts:
9
+ * - A single collection ID string
10
+ * - An array of collection ID strings
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * // Single collection
15
+ * { collection_id: "col_123" }
16
+ *
17
+ * // Multiple collections
18
+ * { collection_id: ["col_123", "col_456"] }
19
+ * ```
20
+ */
21
+ class CollectionFilterProvider extends base_filter_provider_1.BaseFilterProvider {
22
+ apply(filters, value, context) {
23
+ if (!value) {
24
+ return filters;
25
+ }
26
+ // Normalize to array
27
+ const collectionIds = Array.isArray(value) ? value : [value];
28
+ // Filter out empty values
29
+ const validIds = collectionIds.filter((id) => id !== null && id !== undefined && id !== "");
30
+ if (validIds.length === 0) {
31
+ return filters;
32
+ }
33
+ // Apply collection filter
34
+ return {
35
+ ...filters,
36
+ collection_id: validIds,
37
+ };
38
+ }
39
+ validate(value) {
40
+ if (value === undefined || value === null) {
41
+ return;
42
+ }
43
+ const ids = Array.isArray(value) ? value : [value];
44
+ if (!ids.every((id) => typeof id === "string" && id.trim().length > 0)) {
45
+ throw new Error("collection_id must be a string or array of non-empty strings");
46
+ }
47
+ }
48
+ }
49
+ exports.CollectionFilterProvider = CollectionFilterProvider;
50
+ CollectionFilterProvider.identifier = "collection_id";
51
+ CollectionFilterProvider.displayName = "Collection Filter";
52
+ CollectionFilterProvider.description = "Filters products by collection IDs";
53
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29sbGVjdGlvbi1wcm92aWRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9wcm92aWRlcnMvZmlsdGVyLXByb3ZpZGVycy9jb2xsZWN0aW9uLXByb3ZpZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLGlFQUErRTtBQUUvRTs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7QUFDSCxNQUFhLHdCQUF5QixTQUFRLHlDQUFrQjtJQUs5RCxLQUFLLENBQ0gsT0FBZ0MsRUFDaEMsS0FBYyxFQUNkLE9BQXVCO1FBRXZCLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNYLE9BQU8sT0FBTyxDQUFBO1FBQ2hCLENBQUM7UUFFRCxxQkFBcUI7UUFDckIsTUFBTSxhQUFhLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFBO1FBRTVELDBCQUEwQjtRQUMxQixNQUFNLFFBQVEsR0FBRyxhQUFhLENBQUMsTUFBTSxDQUNuQyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsRUFBRSxLQUFLLElBQUksSUFBSSxFQUFFLEtBQUssU0FBUyxJQUFJLEVBQUUsS0FBSyxFQUFFLENBQ3JELENBQUE7UUFFRCxJQUFJLFFBQVEsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDMUIsT0FBTyxPQUFPLENBQUE7UUFDaEIsQ0FBQztRQUVELDBCQUEwQjtRQUMxQixPQUFPO1lBQ0wsR0FBRyxPQUFPO1lBQ1YsYUFBYSxFQUFFLFFBQVE7U0FDeEIsQ0FBQTtJQUNILENBQUM7SUFFRCxRQUFRLENBQUMsS0FBYztRQUNyQixJQUFJLEtBQUssS0FBSyxTQUFTLElBQUksS0FBSyxLQUFLLElBQUksRUFBRSxDQUFDO1lBQzFDLE9BQU07UUFDUixDQUFDO1FBRUQsTUFBTSxHQUFHLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFBO1FBRWxELElBQ0UsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUNSLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxPQUFPLEVBQUUsS0FBSyxRQUFRLElBQUksRUFBRSxDQUFDLElBQUksRUFBRSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQ3ZELEVBQ0QsQ0FBQztZQUNELE1BQU0sSUFBSSxLQUFLLENBQ2IsOERBQThELENBQy9ELENBQUE7UUFDSCxDQUFDO0lBQ0gsQ0FBQzs7QUFqREgsNERBa0RDO0FBakRpQixtQ0FBVSxHQUFHLGVBQWUsQ0FBQTtBQUM1QixvQ0FBVyxHQUFHLG1CQUFtQixDQUFBO0FBQ2pDLG9DQUFXLEdBQUcsb0NBQW9DLENBQUEifQ==
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ /**
3
+ * Filter Providers Module
4
+ *
5
+ * This module exports all filter providers and provides utilities
6
+ * for registering built-in providers.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.BUILT_IN_PROVIDERS = exports.BaseProductFilterProvider = exports.RatingFilterProvider = exports.AvailabilityFilterProvider = exports.PromotionFilterProvider = exports.PromotionWindowFilterProvider = exports.PriceRangeFilterProvider = exports.MetadataFilterProvider = exports.CollectionFilterProvider = exports.CategoryFilterProvider = exports.BaseFilterProvider = void 0;
10
+ exports.registerBuiltInProviders = registerBuiltInProviders;
11
+ exports.getBuiltInProviderIdentifiers = getBuiltInProviderIdentifiers;
12
+ var base_filter_provider_1 = require("./base-filter-provider");
13
+ Object.defineProperty(exports, "BaseFilterProvider", { enumerable: true, get: function () { return base_filter_provider_1.BaseFilterProvider; } });
14
+ var category_provider_1 = require("./category-provider");
15
+ Object.defineProperty(exports, "CategoryFilterProvider", { enumerable: true, get: function () { return category_provider_1.CategoryFilterProvider; } });
16
+ var collection_provider_1 = require("./collection-provider");
17
+ Object.defineProperty(exports, "CollectionFilterProvider", { enumerable: true, get: function () { return collection_provider_1.CollectionFilterProvider; } });
18
+ var metadata_provider_1 = require("./metadata-provider");
19
+ Object.defineProperty(exports, "MetadataFilterProvider", { enumerable: true, get: function () { return metadata_provider_1.MetadataFilterProvider; } });
20
+ var price_range_provider_1 = require("./price-range-provider");
21
+ Object.defineProperty(exports, "PriceRangeFilterProvider", { enumerable: true, get: function () { return price_range_provider_1.PriceRangeFilterProvider; } });
22
+ var promotion_window_provider_1 = require("./promotion-window-provider");
23
+ Object.defineProperty(exports, "PromotionWindowFilterProvider", { enumerable: true, get: function () { return promotion_window_provider_1.PromotionWindowFilterProvider; } });
24
+ var promotion_provider_1 = require("./promotion-provider");
25
+ Object.defineProperty(exports, "PromotionFilterProvider", { enumerable: true, get: function () { return promotion_provider_1.PromotionFilterProvider; } });
26
+ var availability_provider_1 = require("./availability-provider");
27
+ Object.defineProperty(exports, "AvailabilityFilterProvider", { enumerable: true, get: function () { return availability_provider_1.AvailabilityFilterProvider; } });
28
+ var rating_provider_1 = require("./rating-provider");
29
+ Object.defineProperty(exports, "RatingFilterProvider", { enumerable: true, get: function () { return rating_provider_1.RatingFilterProvider; } });
30
+ var base_product_provider_1 = require("./base-product-provider");
31
+ Object.defineProperty(exports, "BaseProductFilterProvider", { enumerable: true, get: function () { return base_product_provider_1.BaseProductFilterProvider; } });
32
+ const category_provider_2 = require("./category-provider");
33
+ const collection_provider_2 = require("./collection-provider");
34
+ const metadata_provider_2 = require("./metadata-provider");
35
+ const price_range_provider_2 = require("./price-range-provider");
36
+ const promotion_window_provider_2 = require("./promotion-window-provider");
37
+ const promotion_provider_2 = require("./promotion-provider");
38
+ const availability_provider_2 = require("./availability-provider");
39
+ const rating_provider_2 = require("./rating-provider");
40
+ const base_product_provider_2 = require("./base-product-provider");
41
+ /**
42
+ * List of all built-in filter provider classes.
43
+ */
44
+ exports.BUILT_IN_PROVIDERS = [
45
+ category_provider_2.CategoryFilterProvider,
46
+ collection_provider_2.CollectionFilterProvider,
47
+ metadata_provider_2.MetadataFilterProvider,
48
+ price_range_provider_2.PriceRangeFilterProvider,
49
+ promotion_window_provider_2.PromotionWindowFilterProvider,
50
+ promotion_provider_2.PromotionFilterProvider,
51
+ availability_provider_2.AvailabilityFilterProvider,
52
+ rating_provider_2.RatingFilterProvider,
53
+ base_product_provider_2.BaseProductFilterProvider,
54
+ ];
55
+ /**
56
+ * Register all built-in filter providers with the registry.
57
+ *
58
+ * This function instantiates and registers all built-in providers.
59
+ * Providers that are disabled (via disableBuiltInProviders option)
60
+ * will be skipped.
61
+ *
62
+ * @param registry - Filter provider registry instance
63
+ * @param disabledProviders - Optional array of provider identifiers to skip
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * const registry = new FilterProviderRegistry()
68
+ * registerBuiltInProviders(registry)
69
+ *
70
+ * // Or with disabled providers
71
+ * registerBuiltInProviders(registry, ["rating"])
72
+ * ```
73
+ */
74
+ function registerBuiltInProviders(registry, disabledProviders = []) {
75
+ for (const ProviderClass of exports.BUILT_IN_PROVIDERS) {
76
+ const identifier = ProviderClass.identifier;
77
+ // Skip disabled providers
78
+ if (disabledProviders.includes(identifier)) {
79
+ continue;
80
+ }
81
+ // Instantiate and register provider
82
+ const provider = new ProviderClass();
83
+ registry.register(provider);
84
+ }
85
+ }
86
+ /**
87
+ * Get all built-in provider identifiers.
88
+ *
89
+ * @returns Array of provider identifiers
90
+ */
91
+ function getBuiltInProviderIdentifiers() {
92
+ return exports.BUILT_IN_PROVIDERS.map((ProviderClass) => ProviderClass.identifier);
93
+ }
94
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL2ZpbHRlci1wcm92aWRlcnMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7OztHQUtHOzs7QUFnRUgsNERBZ0JDO0FBT0Qsc0VBRUM7QUF2RkQsK0RBSStCO0FBSDdCLDBIQUFBLGtCQUFrQixPQUFBO0FBS3BCLHlEQUE0RDtBQUFuRCwySEFBQSxzQkFBc0IsT0FBQTtBQUMvQiw2REFBZ0U7QUFBdkQsK0hBQUEsd0JBQXdCLE9BQUE7QUFDakMseURBQTREO0FBQW5ELDJIQUFBLHNCQUFzQixPQUFBO0FBQy9CLCtEQUFpRTtBQUF4RCxnSUFBQSx3QkFBd0IsT0FBQTtBQUNqQyx5RUFBMkU7QUFBbEUsMElBQUEsNkJBQTZCLE9BQUE7QUFDdEMsMkRBQThEO0FBQXJELDZIQUFBLHVCQUF1QixPQUFBO0FBQ2hDLGlFQUFvRTtBQUEzRCxtSUFBQSwwQkFBMEIsT0FBQTtBQUNuQyxxREFBd0Q7QUFBL0MsdUhBQUEsb0JBQW9CLE9BQUE7QUFDN0IsaUVBQW1FO0FBQTFELGtJQUFBLHlCQUF5QixPQUFBO0FBR2xDLDJEQUE0RDtBQUM1RCwrREFBZ0U7QUFDaEUsMkRBQTREO0FBQzVELGlFQUFpRTtBQUNqRSwyRUFBMkU7QUFDM0UsNkRBQThEO0FBQzlELG1FQUFvRTtBQUNwRSx1REFBd0Q7QUFDeEQsbUVBQW1FO0FBR25FOztHQUVHO0FBQ1UsUUFBQSxrQkFBa0IsR0FBRztJQUNoQywwQ0FBc0I7SUFDdEIsOENBQXdCO0lBQ3hCLDBDQUFzQjtJQUN0QiwrQ0FBd0I7SUFDeEIseURBQTZCO0lBQzdCLDRDQUF1QjtJQUN2QixrREFBMEI7SUFDMUIsc0NBQW9CO0lBQ3BCLGlEQUF5QjtDQUNqQixDQUFBO0FBRVY7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWtCRztBQUNILFNBQWdCLHdCQUF3QixDQUN0QyxRQUFnQyxFQUNoQyxvQkFBOEIsRUFBRTtJQUVoQyxLQUFLLE1BQU0sYUFBYSxJQUFJLDBCQUFrQixFQUFFLENBQUM7UUFDL0MsTUFBTSxVQUFVLEdBQUcsYUFBYSxDQUFDLFVBQVUsQ0FBQTtRQUUzQywwQkFBMEI7UUFDMUIsSUFBSSxpQkFBaUIsQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQztZQUMzQyxTQUFRO1FBQ1YsQ0FBQztRQUVELG9DQUFvQztRQUNwQyxNQUFNLFFBQVEsR0FBRyxJQUFJLGFBQWEsRUFBRSxDQUFBO1FBQ3BDLFFBQVEsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUE7SUFDN0IsQ0FBQztBQUNILENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsU0FBZ0IsNkJBQTZCO0lBQzNDLE9BQU8sMEJBQWtCLENBQUMsR0FBRyxDQUFDLENBQUMsYUFBYSxFQUFFLEVBQUUsQ0FBQyxhQUFhLENBQUMsVUFBVSxDQUFDLENBQUE7QUFDNUUsQ0FBQyJ9
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MetadataFilterProvider = void 0;
4
+ const base_filter_provider_1 = require("./base-filter-provider");
5
+ /**
6
+ * Filter provider for filtering products by metadata.
7
+ *
8
+ * Handles the `metadata` filter parameter, which accepts an object
9
+ * with metadata key-value pairs. Only metadata keys that are marked
10
+ * as filterable in the plugin options are allowed.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * { metadata: { brand: "nike", color: ["red", "blue"] } }
15
+ * ```
16
+ */
17
+ class MetadataFilterProvider extends base_filter_provider_1.BaseFilterProvider {
18
+ apply(filters, value, context) {
19
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
20
+ return filters;
21
+ }
22
+ const metadataFilters = value;
23
+ // Get allowlist from options
24
+ const metadataAllowList = new Set();
25
+ if (context?.options?.metadata?.products?.descriptors) {
26
+ for (const descriptor of context.options.metadata.products.descriptors) {
27
+ if (descriptor.filterable) {
28
+ metadataAllowList.add(descriptor.key);
29
+ }
30
+ }
31
+ }
32
+ // If no allowlist, don't apply metadata filters
33
+ if (metadataAllowList.size === 0) {
34
+ return filters;
35
+ }
36
+ // Filter metadata to only include allowed keys
37
+ const allowedMetadata = {};
38
+ for (const [key, val] of Object.entries(metadataFilters)) {
39
+ if (metadataAllowList.has(key)) {
40
+ allowedMetadata[key] = val;
41
+ }
42
+ }
43
+ if (Object.keys(allowedMetadata).length === 0) {
44
+ return filters;
45
+ }
46
+ // Apply metadata filter
47
+ return {
48
+ ...filters,
49
+ metadata: allowedMetadata,
50
+ };
51
+ }
52
+ validate(value) {
53
+ if (value === undefined || value === null) {
54
+ return;
55
+ }
56
+ if (typeof value !== "object" || Array.isArray(value)) {
57
+ throw new Error("metadata must be an object");
58
+ }
59
+ const metadataFilters = value;
60
+ // Validate each metadata value
61
+ for (const [key, val] of Object.entries(metadataFilters)) {
62
+ if (typeof key !== "string" || key.trim().length === 0) {
63
+ throw new Error("metadata keys must be non-empty strings");
64
+ }
65
+ // Metadata values can be string, number, boolean, or array of these
66
+ if (val !== null &&
67
+ typeof val !== "string" &&
68
+ typeof val !== "number" &&
69
+ typeof val !== "boolean" &&
70
+ !Array.isArray(val)) {
71
+ throw new Error(`metadata.${key} must be a string, number, boolean, or array of these types`);
72
+ }
73
+ // If array, validate elements
74
+ if (Array.isArray(val)) {
75
+ if (!val.every((item) => typeof item === "string" ||
76
+ typeof item === "number" ||
77
+ typeof item === "boolean")) {
78
+ throw new Error(`metadata.${key} array elements must be strings, numbers, or booleans`);
79
+ }
80
+ }
81
+ }
82
+ }
83
+ }
84
+ exports.MetadataFilterProvider = MetadataFilterProvider;
85
+ MetadataFilterProvider.identifier = "metadata";
86
+ MetadataFilterProvider.displayName = "Metadata Filter";
87
+ MetadataFilterProvider.description = "Filters products by metadata with allowlist validation";
88
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0YWRhdGEtcHJvdmlkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL2ZpbHRlci1wcm92aWRlcnMvbWV0YWRhdGEtcHJvdmlkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsaUVBQStFO0FBRS9FOzs7Ozs7Ozs7OztHQVdHO0FBQ0gsTUFBYSxzQkFBdUIsU0FBUSx5Q0FBa0I7SUFNNUQsS0FBSyxDQUNILE9BQWdDLEVBQ2hDLEtBQWMsRUFDZCxPQUF1QjtRQUV2QixJQUFJLENBQUMsS0FBSyxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDaEUsT0FBTyxPQUFPLENBQUE7UUFDaEIsQ0FBQztRQUVELE1BQU0sZUFBZSxHQUFHLEtBQWdDLENBQUE7UUFFeEQsNkJBQTZCO1FBQzdCLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxHQUFHLEVBQVUsQ0FBQTtRQUMzQyxJQUFJLE9BQU8sRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsQ0FBQztZQUN0RCxLQUFLLE1BQU0sVUFBVSxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxXQUFXLEVBQUUsQ0FBQztnQkFDdkUsSUFBSSxVQUFVLENBQUMsVUFBVSxFQUFFLENBQUM7b0JBQzFCLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUE7Z0JBQ3ZDLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUVELGdEQUFnRDtRQUNoRCxJQUFJLGlCQUFpQixDQUFDLElBQUksS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUNqQyxPQUFPLE9BQU8sQ0FBQTtRQUNoQixDQUFDO1FBRUQsK0NBQStDO1FBQy9DLE1BQU0sZUFBZSxHQUE0QixFQUFFLENBQUE7UUFDbkQsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDLEVBQUUsQ0FBQztZQUN6RCxJQUFJLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUMvQixlQUFlLENBQUMsR0FBRyxDQUFDLEdBQUcsR0FBRyxDQUFBO1lBQzVCLENBQUM7UUFDSCxDQUFDO1FBRUQsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUM5QyxPQUFPLE9BQU8sQ0FBQTtRQUNoQixDQUFDO1FBRUQsd0JBQXdCO1FBQ3hCLE9BQU87WUFDTCxHQUFHLE9BQU87WUFDVixRQUFRLEVBQUUsZUFBZTtTQUMxQixDQUFBO0lBQ0gsQ0FBQztJQUVELFFBQVEsQ0FBQyxLQUFjO1FBQ3JCLElBQUksS0FBSyxLQUFLLFNBQVMsSUFBSSxLQUFLLEtBQUssSUFBSSxFQUFFLENBQUM7WUFDMUMsT0FBTTtRQUNSLENBQUM7UUFFRCxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDdEQsTUFBTSxJQUFJLEtBQUssQ0FBQyw0QkFBNEIsQ0FBQyxDQUFBO1FBQy9DLENBQUM7UUFFRCxNQUFNLGVBQWUsR0FBRyxLQUFnQyxDQUFBO1FBRXhELCtCQUErQjtRQUMvQixLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxlQUFlLENBQUMsRUFBRSxDQUFDO1lBQ3pELElBQUksT0FBTyxHQUFHLEtBQUssUUFBUSxJQUFJLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQ3ZELE1BQU0sSUFBSSxLQUFLLENBQUMseUNBQXlDLENBQUMsQ0FBQTtZQUM1RCxDQUFDO1lBRUQsb0VBQW9FO1lBQ3BFLElBQ0UsR0FBRyxLQUFLLElBQUk7Z0JBQ1osT0FBTyxHQUFHLEtBQUssUUFBUTtnQkFDdkIsT0FBTyxHQUFHLEtBQUssUUFBUTtnQkFDdkIsT0FBTyxHQUFHLEtBQUssU0FBUztnQkFDeEIsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUNuQixDQUFDO2dCQUNELE1BQU0sSUFBSSxLQUFLLENBQ2IsWUFBWSxHQUFHLDZEQUE2RCxDQUM3RSxDQUFBO1lBQ0gsQ0FBQztZQUVELDhCQUE4QjtZQUM5QixJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztnQkFDdkIsSUFDRSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQ1IsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUNQLE9BQU8sSUFBSSxLQUFLLFFBQVE7b0JBQ3hCLE9BQU8sSUFBSSxLQUFLLFFBQVE7b0JBQ3hCLE9BQU8sSUFBSSxLQUFLLFNBQVMsQ0FDNUIsRUFDRCxDQUFDO29CQUNELE1BQU0sSUFBSSxLQUFLLENBQ2IsWUFBWSxHQUFHLHVEQUF1RCxDQUN2RSxDQUFBO2dCQUNILENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7O0FBakdILHdEQWtHQztBQWpHaUIsaUNBQVUsR0FBRyxVQUFVLENBQUE7QUFDdkIsa0NBQVcsR0FBRyxpQkFBaUIsQ0FBQTtBQUMvQixrQ0FBVyxHQUN6Qix3REFBd0QsQ0FBQSJ9
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PriceRangeFilterProvider = void 0;
4
+ const base_filter_provider_1 = require("./base-filter-provider");
5
+ /**
6
+ * Filter provider for filtering products by price range.
7
+ *
8
+ * Handles price filtering with min/max values. Can also apply
9
+ * default price range from plugin options if no explicit range is provided.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * // Explicit range
14
+ * { price_range: { min: 10, max: 100 } }
15
+ *
16
+ * // Min only
17
+ * { price_range: { min: 10 } }
18
+ *
19
+ * // Max only
20
+ * { price_range: { max: 100 } }
21
+ * ```
22
+ */
23
+ class PriceRangeFilterProvider extends base_filter_provider_1.BaseFilterProvider {
24
+ apply(filters, value, context) {
25
+ let priceRange;
26
+ // If explicit price range provided, use it
27
+ if (value && typeof value === "object" && !Array.isArray(value)) {
28
+ const range = value;
29
+ if (range.min !== undefined || range.max !== undefined) {
30
+ // Handle string-to-number conversion for query parameters
31
+ const min = typeof range.min === "string" || typeof range.min === "number"
32
+ ? (typeof range.min === "string" ? Number(range.min) : range.min)
33
+ : undefined;
34
+ const max = typeof range.max === "string" || typeof range.max === "number"
35
+ ? (typeof range.max === "string" ? Number(range.max) : range.max)
36
+ : undefined;
37
+ if ((min !== undefined && !Number.isNaN(min)) || (max !== undefined && !Number.isNaN(max))) {
38
+ priceRange = {
39
+ min: min !== undefined && !Number.isNaN(min) ? min : undefined,
40
+ max: max !== undefined && !Number.isNaN(max) ? max : undefined,
41
+ currency_code: typeof range.currency_code === "string" ? range.currency_code : undefined,
42
+ };
43
+ }
44
+ }
45
+ }
46
+ // If no explicit range, check for default from options
47
+ if (!priceRange && context?.options?.default_price_range) {
48
+ const defaultRange = context.options.default_price_range;
49
+ if (defaultRange.min !== null || defaultRange.max !== null) {
50
+ priceRange = {
51
+ min: defaultRange.min ?? undefined,
52
+ max: defaultRange.max ?? undefined,
53
+ currency_code: defaultRange.currency_code,
54
+ };
55
+ }
56
+ }
57
+ // If still no range, return filters unchanged
58
+ if (!priceRange || (priceRange.min === undefined && priceRange.max === undefined)) {
59
+ return filters;
60
+ }
61
+ /**
62
+ * Store price range in a special internal filter key for post-query filtering.
63
+ *
64
+ * This key (`__price_range_filter__`) is an internal convention used by
65
+ * DynamicFilterService to identify filters that require post-query processing.
66
+ *
67
+ * Price filtering requires post-query filtering because Medusa's query API doesn't
68
+ * support price range filtering directly - we need to filter products after
69
+ * retrieving them and checking variant prices.
70
+ *
71
+ * @internal This key is for internal use by DynamicFilterService only.
72
+ */
73
+ filters.__price_range_filter__ = priceRange;
74
+ return filters;
75
+ }
76
+ validate(value) {
77
+ if (value === undefined || value === null) {
78
+ return;
79
+ }
80
+ if (typeof value !== "object" || Array.isArray(value)) {
81
+ throw new Error("price_range must be an object with min and/or max");
82
+ }
83
+ const range = value;
84
+ if (range.min !== undefined) {
85
+ if (typeof range.min !== "number" || range.min < 0) {
86
+ throw new Error("price_range.min must be a non-negative number");
87
+ }
88
+ }
89
+ if (range.max !== undefined) {
90
+ if (typeof range.max !== "number" || range.max < 0) {
91
+ throw new Error("price_range.max must be a non-negative number");
92
+ }
93
+ }
94
+ if (range.min !== undefined && range.max !== undefined && range.min > range.max) {
95
+ throw new Error("price_range.min must be less than or equal to price_range.max");
96
+ }
97
+ if (range.currency_code !== undefined) {
98
+ if (typeof range.currency_code !== "string" || range.currency_code.length !== 3) {
99
+ throw new Error("price_range.currency_code must be a 3-character ISO currency code");
100
+ }
101
+ }
102
+ }
103
+ }
104
+ exports.PriceRangeFilterProvider = PriceRangeFilterProvider;
105
+ PriceRangeFilterProvider.identifier = "price_range";
106
+ PriceRangeFilterProvider.displayName = "Price Range Filter";
107
+ PriceRangeFilterProvider.description = "Filters products by price range (min/max)";
108
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJpY2UtcmFuZ2UtcHJvdmlkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL2ZpbHRlci1wcm92aWRlcnMvcHJpY2UtcmFuZ2UtcHJvdmlkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsaUVBQStFO0FBRS9FOzs7Ozs7Ozs7Ozs7Ozs7OztHQWlCRztBQUNILE1BQWEsd0JBQXlCLFNBQVEseUNBQWtCO0lBSzlELEtBQUssQ0FDSCxPQUFnQyxFQUNoQyxLQUFjLEVBQ2QsT0FBdUI7UUFFdkIsSUFBSSxVQUE4RSxDQUFBO1FBRWxGLDJDQUEyQztRQUMzQyxJQUFJLEtBQUssSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDaEUsTUFBTSxLQUFLLEdBQUcsS0FBaUUsQ0FBQTtZQUMvRSxJQUFJLEtBQUssQ0FBQyxHQUFHLEtBQUssU0FBUyxJQUFJLEtBQUssQ0FBQyxHQUFHLEtBQUssU0FBUyxFQUFFLENBQUM7Z0JBQ3ZELDBEQUEwRDtnQkFDMUQsTUFBTSxHQUFHLEdBQUcsT0FBTyxLQUFLLENBQUMsR0FBRyxLQUFLLFFBQVEsSUFBSSxPQUFPLEtBQUssQ0FBQyxHQUFHLEtBQUssUUFBUTtvQkFDeEUsQ0FBQyxDQUFDLENBQUMsT0FBTyxLQUFLLENBQUMsR0FBRyxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQztvQkFDakUsQ0FBQyxDQUFDLFNBQVMsQ0FBQTtnQkFDYixNQUFNLEdBQUcsR0FBRyxPQUFPLEtBQUssQ0FBQyxHQUFHLEtBQUssUUFBUSxJQUFJLE9BQU8sS0FBSyxDQUFDLEdBQUcsS0FBSyxRQUFRO29CQUN4RSxDQUFDLENBQUMsQ0FBQyxPQUFPLEtBQUssQ0FB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
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PromotionFilterProvider = void 0;
4
+ const base_filter_provider_1 = require("./base-filter-provider");
5
+ /**
6
+ * Filter provider for filtering products by Medusa promotions.
7
+ *
8
+ * Handles filtering products that have active promotions matching criteria:
9
+ * - Promotion type (amount_off_product, percentage_off_product, etc.)
10
+ * - Minimum discount percentage (e.g., products with 10% or more discount)
11
+ * - Date range (starts_at, ends_at)
12
+ * - Promotion status
13
+ * - Include open-ended promotions (default: true)
14
+ *
15
+ * This provider queries Medusa's promotion module directly to get actual
16
+ * promotion data, unlike PromotionWindowFilterProvider which uses metadata.
17
+ *
18
+ * **Default Behavior:**
19
+ * - `include_open_ended` defaults to `true` - open-ended promotions (without an end date)
20
+ * are included by default when filtering by date range or status.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * // Filter products with 10% or more discount
25
+ * { promotion: { min_discount_percentage: 10 } }
26
+ *
27
+ * // Filter products with specific promotion type
28
+ * { promotion: { promotion_type: "percentage_off_product", min_discount_percentage: 15 } }
29
+ *
30
+ * // Filter products with promotions active in date range
31
+ * { promotion: { starts_at: "2024-01-01", ends_at: "2024-12-31" } }
32
+ *
33
+ * // Exclude open-ended promotions
34
+ * { promotion: { min_discount_percentage: 10, include_open_ended: false } }
35
+ * ```
36
+ */
37
+ class PromotionFilterProvider extends base_filter_provider_1.BaseFilterProvider {
38
+ apply(filters, value, context) {
39
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
40
+ return filters;
41
+ }
42
+ const promotionFilter = value;
43
+ // Build promotion filter criteria
44
+ const filterCriteria = {};
45
+ // Handle min_discount_percentage
46
+ if (promotionFilter.min_discount_percentage !== undefined) {
47
+ const minDiscount = typeof promotionFilter.min_discount_percentage === "string"
48
+ ? Number(promotionFilter.min_discount_percentage)
49
+ : promotionFilter.min_discount_percentage;
50
+ if (!Number.isNaN(minDiscount) && minDiscount >= 0 && minDiscount <= 100) {
51
+ filterCriteria.min_discount_percentage = minDiscount;
52
+ }
53
+ }
54
+ // Handle promotion_type
55
+ if (promotionFilter.promotion_type) {
56
+ filterCriteria.promotion_type = promotionFilter.promotion_type;
57
+ }
58
+ // Handle date range
59
+ if (promotionFilter.starts_at) {
60
+ filterCriteria.starts_at =
61
+ promotionFilter.starts_at instanceof Date
62
+ ? promotionFilter.starts_at
63
+ : new Date(promotionFilter.starts_at);
64
+ }
65
+ if (promotionFilter.ends_at) {
66
+ filterCriteria.ends_at =
67
+ promotionFilter.ends_at instanceof Date
68
+ ? promotionFilter.ends_at
69
+ : new Date(promotionFilter.ends_at);
70
+ }
71
+ // Handle status
72
+ if (promotionFilter.status) {
73
+ filterCriteria.status = promotionFilter.status;
74
+ }
75
+ // Handle include_open_ended
76
+ // Default: true (include open-ended promotions that don't have an end date)
77
+ // This allows filtering products with ongoing promotions by default
78
+ if (promotionFilter.include_open_ended !== undefined) {
79
+ filterCriteria.include_open_ended = promotionFilter.include_open_ended;
80
+ }
81
+ else {
82
+ // Default to including open-ended promotions
83
+ filterCriteria.include_open_ended = true;
84
+ }
85
+ // If no valid criteria, return filters unchanged
86
+ if (filterCriteria.min_discount_percentage === undefined &&
87
+ !filterCriteria.promotion_type &&
88
+ !filterCriteria.starts_at &&
89
+ !filterCriteria.ends_at &&
90
+ !filterCriteria.status) {
91
+ return filters;
92
+ }
93
+ /**
94
+ * Store promotion filter in a special internal filter key for post-query filtering.
95
+ *
96
+ * This key (`__promotion_filter__`) is an internal convention used by
97
+ * DynamicFilterService to identify filters that require post-query processing.
98
+ *
99
+ * Promotion filtering requires post-query filtering because it needs to:
100
+ * 1. Query Medusa's promotion module to get active promotions
101
+ * 2. Match products via promotion rules/targets (product IDs, collection IDs, etc.)
102
+ * 3. Calculate discount percentages based on promotion type
103
+ * 4. Filter products that meet the discount threshold
104
+ *
105
+ * This complex logic cannot be done directly in the product query.
106
+ *
107
+ * @internal This key is for internal use by DynamicFilterService only.
108
+ */
109
+ filters.__promotion_filter__ = filterCriteria;
110
+ return filters;
111
+ }
112
+ validate(value) {
113
+ if (value === undefined || value === null) {
114
+ return;
115
+ }
116
+ if (typeof value !== "object" || Array.isArray(value)) {
117
+ throw new Error(`promotion filter must be an object, got ${Array.isArray(value) ? "array" : typeof value}`);
118
+ }
119
+ const promotionFilter = value;
120
+ // Validate min_discount_percentage
121
+ if (promotionFilter.min_discount_percentage !== undefined) {
122
+ const minDiscount = typeof promotionFilter.min_discount_percentage === "string"
123
+ ? Number(promotionFilter.min_discount_percentage)
124
+ : promotionFilter.min_discount_percentage;
125
+ if (typeof minDiscount !== "number" || Number.isNaN(minDiscount)) {
126
+ throw new Error(`promotion.min_discount_percentage must be a number, got ${typeof promotionFilter.min_discount_percentage}`);
127
+ }
128
+ if (minDiscount < 0 || minDiscount > 100) {
129
+ throw new Error(`promotion.min_discount_percentage must be between 0 and 100, got ${minDiscount}`);
130
+ }
131
+ }
132
+ // Validate promotion_type
133
+ if (promotionFilter.promotion_type !== undefined) {
134
+ if (typeof promotionFilter.promotion_type !== "string") {
135
+ throw new Error(`promotion.promotion_type must be a string, got ${typeof promotionFilter.promotion_type}`);
136
+ }
137
+ const validTypes = [
138
+ "amount_off_product",
139
+ "percentage_off_product",
140
+ "amount_off_order",
141
+ "percentage_off_order",
142
+ "buy_x_get_y",
143
+ "free_shipping",
144
+ ];
145
+ if (!validTypes.includes(promotionFilter.promotion_type)) {
146
+ throw new Error(`promotion.promotion_type must be one of: ${validTypes.join(", ")}, got "${promotionFilter.promotion_type}"`);
147
+ }
148
+ }
149
+ // Validate starts_at
150
+ if (promotionFilter.starts_at !== undefined) {
151
+ if (!(promotionFilter.starts_at instanceof Date) &&
152
+ typeof promotionFilter.starts_at !== "string") {
153
+ throw new Error(`promotion.starts_at must be a Date or ISO string, got ${typeof promotionFilter.starts_at}`);
154
+ }
155
+ // If string, validate it's a valid date
156
+ if (typeof promotionFilter.starts_at === "string") {
157
+ const date = new Date(promotionFilter.starts_at);
158
+ if (Number.isNaN(date.getTime())) {
159
+ throw new Error(`promotion.starts_at must be a valid ISO date string, got "${promotionFilter.starts_at}"`);
160
+ }
161
+ }
162
+ }
163
+ // Validate ends_at
164
+ if (promotionFilter.ends_at !== undefined) {
165
+ if (!(promotionFilter.ends_at instanceof Date) &&
166
+ typeof promotionFilter.ends_at !== "string") {
167
+ throw new Error(`promotion.ends_at must be a Date or ISO string, got ${typeof promotionFilter.ends_at}`);
168
+ }
169
+ // If string, validate it's a valid date
170
+ if (typeof promotionFilter.ends_at === "string") {
171
+ const date = new Date(promotionFilter.ends_at);
172
+ if (Number.isNaN(date.getTime())) {
173
+ throw new Error(`promotion.ends_at must be a valid ISO date string, got "${promotionFilter.ends_at}"`);
174
+ }
175
+ }
176
+ }
177
+ // Validate status
178
+ if (promotionFilter.status !== undefined) {
179
+ if (typeof promotionFilter.status !== "string") {
180
+ throw new Error(`promotion.status must be a string, got ${typeof promotionFilter.status}`);
181
+ }
182
+ }
183
+ // Validate include_open_ended
184
+ // Note: Also accepts string "true"/"false" for query parameter compatibility
185
+ if (promotionFilter.include_open_ended !== undefined) {
186
+ if (typeof promotionFilter.include_open_ended !== "boolean" &&
187
+ typeof promotionFilter.include_open_ended !== "string") {
188
+ throw new Error(`promotion.include_open_ended must be a boolean or "true"/"false" string, got ${typeof promotionFilter.include_open_ended}`);
189
+ }
190
+ }
191
+ }
192
+ }
193
+ exports.PromotionFilterProvider = PromotionFilterProvider;
194
+ PromotionFilterProvider.identifier = "promotion";
195
+ PromotionFilterProvider.displayName = "Promotion Filter";
196
+ PromotionFilterProvider.description = "Filters products by Medusa promotions with discount percentage support";
197
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvbW90aW9uLXByb3ZpZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL3Byb3ZpZGVycy9maWx0ZXItcHJvdmlkZXJzL3Byb21vdGlvbi1wcm92aWRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxpRUFBK0U7QUFFL0U7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0ErQkc7QUFDSCxNQUFhLHVCQUF3QixTQUFRLHlDQUFrQjtJQU03RCxLQUFLLENBQ0gsT0FBZ0MsRUFDaEMsS0FBYyxFQUNkLE9BQXVCO1FBRXZCLElBQUksQ0FBQyxLQUFLLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUNoRSxPQUFPLE9BQU8sQ0FBQTtRQUNoQixDQUFDO1FBRUQsTUFBTSxlQUFlLEdBQUcsS0FPdkIsQ0FBQTtRQUVELGtDQUFrQztRQUNsQyxNQUFNLGNBQWMsR0FPaEIsRUFBRSxDQUFBO1FBRU4saUNBQWlDO1FBQ2pDLElBQUksZUFBZSxDQUFDLHVCQUF1QixLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQzFELE1BQU0sV0FBVyxHQUNmLE9BQU8sZUFBZSxDQUFDLHVCQUF1QixLQUFLLFFBQVE7Z0JBQ3pELENBQUMsQ0FBQyxNQUFNLENBQUMsZUFBZSxDQUFDLHVCQUF1QixDQUFDO2dCQUNqRCxDQUFDLENBQUMsZUFBZSxDQUFDLHVCQUF1QixDQUFBO1lBRTdDLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxJQUFJLFdBQVcsSUFBSSxDQUFDLElBQUksV0FBVyxJQUFJLEdBQUcsRUFBRSxDQUFDO2dCQUN6RSxjQUFjLENBQUMsdUJBQXVCLEdBQUcsV0FBVyxDQUFBO1lBQ3RELENBQUM7UUFDSCxDQUFDO1FBRUQsd0JBQXdCO1FBQ3hCLElBQUksZUFBZSxDQUFDLGNBQWMsRUFBRSxDQUFDO1lB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