@adobe/spacecat-shared-utils 1.52.0 → 1.54.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 CHANGED
@@ -1,3 +1,17 @@
1
+ # [@adobe/spacecat-shared-utils-v1.54.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.53.0...@adobe/spacecat-shared-utils-v1.54.0) (2025-10-02)
2
+
3
+
4
+ ### Features
5
+
6
+ * remove topic from brand alias and make regions mandatory in categories + make regions mandatory for categories ([#997](https://github.com/adobe/spacecat-shared/issues/997)) ([78e609a](https://github.com/adobe/spacecat-shared/commit/78e609a336cb2a2645199d94d4464c4843bd8c4a))
7
+
8
+ # [@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)
9
+
10
+
11
+ ### Features
12
+
13
+ * 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))
14
+
1
15
  # [@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
16
 
3
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-utils",
3
- "version": "1.52.0",
3
+ "version": "1.54.0",
4
4
  "description": "Shared modules of the Spacecat Services - utils",
5
5
  "type": "module",
6
6
  "engines": {
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)]) }),
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({
@@ -52,7 +52,6 @@ export const llmoConfig = z.object({
52
52
  aliases: z.array(nonEmptyString),
53
53
  category: z.uuid(),
54
54
  region: z.union([region, z.array(region)]),
55
- topic: z.uuid(),
56
55
  }),
57
56
  ),
58
57
  }),
@@ -72,11 +71,12 @@ export const llmoConfig = z.object({
72
71
 
73
72
  brands.aliases.forEach((alias, index) => {
74
73
  ensureEntityType(entities, ctx, alias.category, 'category', ['brands', 'aliases', index, 'category'], 'category');
75
- ensureEntityType(entities, ctx, alias.topic, 'topic', ['brands', 'aliases', index, 'topic'], 'topic');
74
+ ensureRegionCompatibility(entities, ctx, alias.category, alias.region, ['brands', 'aliases', index, 'region'], 'brand alias');
76
75
  });
77
76
 
78
77
  competitors.competitors.forEach((competitor, index) => {
79
78
  ensureEntityType(entities, ctx, competitor.category, 'category', ['competitors', 'competitors', index, 'category'], 'category');
79
+ ensureRegionCompatibility(entities, ctx, competitor.category, competitor.region, ['competitors', 'competitors', index, 'region'], 'competitor');
80
80
  });
81
81
  });
82
82
 
@@ -107,3 +107,37 @@ function ensureEntityType(entities, ctx, id, expectedType, path, refLabel) {
107
107
  });
108
108
  }
109
109
  }
110
+
111
+ /**
112
+ * @param {LLMOConfig['entities']} entities
113
+ * @param {z.RefinementCtx} ctx
114
+ * @param {string} categoryId
115
+ * @param {string | string[]} itemRegion
116
+ * @param {Array<number | string>} path
117
+ * @param {string} itemLabel
118
+ */
119
+ function ensureRegionCompatibility(entities, ctx, categoryId, itemRegion, path, itemLabel) {
120
+ const categoryEntity = entities[categoryId];
121
+ if (!categoryEntity) {
122
+ // Category validation is handled by ensureEntityType
123
+ return;
124
+ }
125
+
126
+ const categoryRegions = categoryEntity.region;
127
+
128
+ // Normalize regions to arrays for comparison
129
+ const categoryRegionArray = Array.isArray(categoryRegions) ? categoryRegions : [categoryRegions];
130
+ const itemRegionArray = Array.isArray(itemRegion) ? itemRegion : [itemRegion];
131
+
132
+ // Check if all item regions are contained in category regions
133
+ const invalidRegions = itemRegionArray.filter(
134
+ (regionItem) => !categoryRegionArray.includes(regionItem),
135
+ );
136
+ if (invalidRegions.length > 0) {
137
+ ctx.addIssue({
138
+ code: 'custom',
139
+ path,
140
+ message: `${itemLabel} regions [${invalidRegions.join(', ')}] are not allowed. Category only supports regions: [${categoryRegionArray.join(', ')}]`,
141
+ });
142
+ }
143
+ }