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.
- package/.medusa/server/src/api/store/product-helper/products/route.js +76 -0
- package/.medusa/server/src/api/store/product-helper/products/validators.js +2 -1
- package/.medusa/server/src/config/product-helper-options.js +15 -1
- package/.medusa/server/src/index.js +101 -0
- package/.medusa/server/src/providers/filter-providers/availability-provider.js +96 -0
- package/.medusa/server/src/providers/filter-providers/base-filter-provider.js +32 -0
- package/.medusa/server/src/providers/filter-providers/base-product-provider.js +122 -0
- package/.medusa/server/src/providers/filter-providers/category-provider.js +55 -0
- package/.medusa/server/src/providers/filter-providers/collection-provider.js +53 -0
- package/.medusa/server/src/providers/filter-providers/index.js +94 -0
- package/.medusa/server/src/providers/filter-providers/metadata-provider.js +88 -0
- package/.medusa/server/src/providers/filter-providers/price-range-provider.js +108 -0
- package/.medusa/server/src/providers/filter-providers/promotion-provider.js +197 -0
- package/.medusa/server/src/providers/filter-providers/promotion-window-provider.js +125 -0
- package/.medusa/server/src/providers/filter-providers/rating-provider.js +92 -0
- package/.medusa/server/src/services/dynamic-filter-service.js +814 -0
- package/.medusa/server/src/services/filter-provider-loader.js +155 -0
- package/.medusa/server/src/services/filter-provider-registry.js +142 -0
- package/.medusa/server/src/services/product-filter-service.js +230 -0
- package/.medusa/server/src/utils/query-parser.js +103 -0
- package/README.md +89 -0
- 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0YWRhdGEtcHJvdmlkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvcHJvdmlkZXJzL2ZpbHRlci1wcm92aWRlcnMvbWV0YWRhdGEtcHJvdmlkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsaUVBQStFO0FBRS9FOzs7Ozs7Ozs7OztHQVdHO0FBQ0gsTUFBYSxzQkFBdUIsU0FBUSx5Q0FBa0I7SUFNNUQsS0FBSyxDQUNILE9BQWdDLEVBQ2hDLEtBQWMsRUFDZCxPQUF1QjtRQUV2QixJQUFJLENBQUMsS0FBSyxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDaEUsT0FBTyxPQUFPLENBQUE7UUFDaEIsQ0FBQztRQUVELE1BQU0sZUFBZSxHQUFHLEtBQWdDLENBQUE7UUFFeEQsNkJBQTZCO1FBQzdCLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxHQUFHLEVBQVUsQ0FBQTtRQUMzQyxJQUFJLE9BQU8sRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsQ0FBQztZQUN0RCxLQUFLLE1BQU0sVUFBVSxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxXQUFXLEVBQUUsQ0FBQztnQkFDdkUsSUFBSSxVQUFVLENBQUMsVUFBVSxFQUFFLENBQUM7b0JBQzFCLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUE7Z0JBQ3ZDLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUVELGdEQUFnRDtRQUNoRCxJQUFJLGlCQUFpQixDQUFDLElBQUksS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUNqQyxPQUFPLE9BQU8sQ0FBQTtRQUNoQixDQUFDO1FBRUQsK0NBQStDO1FBQy9DLE1BQU0sZUFBZSxHQUE0QixFQUFFLENBQUE7UUFDbkQsS0FBSyxN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
|
|
@@ -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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
|