@adobe/spacecat-shared-utils 1.52.0 → 1.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/schemas.js +49 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [@adobe/spacecat-shared-utils-v1.53.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.52.0...@adobe/spacecat-shared-utils-v1.53.0) (2025-10-02)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* Add regions to categories and related validation on dependent entities ([#996](https://github.com/adobe/spacecat-shared/issues/996)) ([60c52e2](https://github.com/adobe/spacecat-shared/commit/60c52e2d9c7e79a67132ae2b26e40e617e8af358))
|
|
7
|
+
|
|
1
8
|
# [@adobe/spacecat-shared-utils-v1.52.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.51.1...@adobe/spacecat-shared-utils-v1.52.0) (2025-09-29)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
package/src/schemas.js
CHANGED
|
@@ -36,14 +36,14 @@ import * as z from 'zod';
|
|
|
36
36
|
|
|
37
37
|
const nonEmptyString = z.string().min(1);
|
|
38
38
|
|
|
39
|
+
const region = z.string().length(2).regex(/^[a-z][a-z]$/i);
|
|
40
|
+
|
|
39
41
|
const entity = z.union([
|
|
40
|
-
z.object({ type: z.literal('category'), name: nonEmptyString }),
|
|
42
|
+
z.object({ type: z.literal('category'), name: nonEmptyString, region: z.union([region, z.array(region)]).optional() }),
|
|
41
43
|
z.object({ type: z.literal('topic'), name: nonEmptyString }),
|
|
42
44
|
z.object({ type: nonEmptyString }),
|
|
43
45
|
]);
|
|
44
46
|
|
|
45
|
-
const region = z.string().length(2).regex(/^[a-z][a-z]$/i);
|
|
46
|
-
|
|
47
47
|
export const llmoConfig = z.object({
|
|
48
48
|
entities: z.record(z.uuid(), entity),
|
|
49
49
|
brands: z.object({
|
|
@@ -73,10 +73,12 @@ export const llmoConfig = z.object({
|
|
|
73
73
|
brands.aliases.forEach((alias, index) => {
|
|
74
74
|
ensureEntityType(entities, ctx, alias.category, 'category', ['brands', 'aliases', index, 'category'], 'category');
|
|
75
75
|
ensureEntityType(entities, ctx, alias.topic, 'topic', ['brands', 'aliases', index, 'topic'], 'topic');
|
|
76
|
+
ensureRegionCompatibility(entities, ctx, alias.category, alias.region, ['brands', 'aliases', index, 'region'], 'brand alias');
|
|
76
77
|
});
|
|
77
78
|
|
|
78
79
|
competitors.competitors.forEach((competitor, index) => {
|
|
79
80
|
ensureEntityType(entities, ctx, competitor.category, 'category', ['competitors', 'competitors', index, 'category'], 'category');
|
|
81
|
+
ensureRegionCompatibility(entities, ctx, competitor.category, competitor.region, ['competitors', 'competitors', index, 'region'], 'competitor');
|
|
80
82
|
});
|
|
81
83
|
});
|
|
82
84
|
|
|
@@ -107,3 +109,47 @@ function ensureEntityType(entities, ctx, id, expectedType, path, refLabel) {
|
|
|
107
109
|
});
|
|
108
110
|
}
|
|
109
111
|
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {LLMOConfig['entities']} entities
|
|
115
|
+
* @param {z.RefinementCtx} ctx
|
|
116
|
+
* @param {string} categoryId
|
|
117
|
+
* @param {string | string[]} itemRegion
|
|
118
|
+
* @param {Array<number | string>} path
|
|
119
|
+
* @param {string} itemLabel
|
|
120
|
+
*/
|
|
121
|
+
function ensureRegionCompatibility(entities, ctx, categoryId, itemRegion, path, itemLabel) {
|
|
122
|
+
const categoryEntity = entities[categoryId];
|
|
123
|
+
if (!categoryEntity) {
|
|
124
|
+
// Category validation is handled by ensureEntityType
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const categoryRegions = categoryEntity.region;
|
|
129
|
+
|
|
130
|
+
// If category has no regions defined, item should not have regions
|
|
131
|
+
if (!categoryRegions) {
|
|
132
|
+
ctx.addIssue({
|
|
133
|
+
code: 'custom',
|
|
134
|
+
path,
|
|
135
|
+
message: `${itemLabel} cannot have regions when the referenced category has no regions defined`,
|
|
136
|
+
});
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Normalize regions to arrays for comparison
|
|
141
|
+
const categoryRegionArray = Array.isArray(categoryRegions) ? categoryRegions : [categoryRegions];
|
|
142
|
+
const itemRegionArray = Array.isArray(itemRegion) ? itemRegion : [itemRegion];
|
|
143
|
+
|
|
144
|
+
// Check if all item regions are contained in category regions
|
|
145
|
+
const invalidRegions = itemRegionArray.filter(
|
|
146
|
+
(regionItem) => !categoryRegionArray.includes(regionItem),
|
|
147
|
+
);
|
|
148
|
+
if (invalidRegions.length > 0) {
|
|
149
|
+
ctx.addIssue({
|
|
150
|
+
code: 'custom',
|
|
151
|
+
path,
|
|
152
|
+
message: `${itemLabel} regions [${invalidRegions.join(', ')}] are not allowed. Category only supports regions: [${categoryRegionArray.join(', ')}]`,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|