@okf/ootils 1.27.0 → 1.28.1

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/dist/node.d.ts CHANGED
@@ -1027,6 +1027,51 @@ declare const _self_managed_buildAnnoHierarchyConfig: ({ annotationTagsCount, }:
1027
1027
  };
1028
1028
  } | null;
1029
1029
 
1030
+ /**
1031
+ * Builds a consolidated docHierarchyType config for self-managed tenants.
1032
+ *
1033
+ * Produces a single filter config that contains:
1034
+ * - commonFilters: filters present in ALL selected templates
1035
+ * - sharedAcrossMultiple: groups of filters shared by 2+ (but not all) templates
1036
+ * - perDataset: each template's complete filter list + rollup configs
1037
+ * - commonDocumentFilters: Author, Published Date, etc.
1038
+ *
1039
+ * This is consumed by DocHierarchyFilter on the frontend to render the
1040
+ * drilldown-based document filter UI.
1041
+ */
1042
+ declare const _self_managed_buildDocHierarchyConfig: ({ combinedDocumentBlocks, docRollupBlocks, selectedTpls, allTpls, commonDocumentFilters, flatDocFilters, }: {
1043
+ combinedDocumentBlocks: any[];
1044
+ docRollupBlocks: any[];
1045
+ selectedTpls: any[];
1046
+ allTpls: any[];
1047
+ commonDocumentFilters: any[];
1048
+ flatDocFilters?: boolean;
1049
+ }) => {
1050
+ filterId: string;
1051
+ display: string;
1052
+ filterKey: string;
1053
+ source: {
1054
+ filterType: string;
1055
+ flatDocFilters: boolean;
1056
+ commonFilters: any[];
1057
+ sharedAcrossMultiple: {
1058
+ contentTypes: any;
1059
+ display: any;
1060
+ filters: any[];
1061
+ }[];
1062
+ commonDocumentFilters: any[];
1063
+ perDataset: {
1064
+ contentType: any;
1065
+ display: any;
1066
+ filters: any[];
1067
+ rollups: any[];
1068
+ }[];
1069
+ };
1070
+ target: {
1071
+ filterType: string;
1072
+ };
1073
+ } | null;
1074
+
1030
1075
  /**
1031
1076
  * TEMP_removeDuplicateFilters - Removes duplicate rollup and tag-type filters
1032
1077
  *
@@ -1121,6 +1166,37 @@ declare const autoGenFilterConfigsFromTpl: ({ selectedTpls, allTpls, filterScope
1121
1166
  sectionId: string;
1122
1167
  configs: any[];
1123
1168
  }[];
1169
+ } | {
1170
+ sectionId: string;
1171
+ configs: {
1172
+ sectionId: string;
1173
+ configs: {
1174
+ filterId: string;
1175
+ display: string;
1176
+ filterKey: string;
1177
+ source: {
1178
+ filterType: string;
1179
+ flatDocFilters: boolean;
1180
+ commonFilters: any[];
1181
+ sharedAcrossMultiple: {
1182
+ contentTypes: any;
1183
+ display: any;
1184
+ filters: any[];
1185
+ }[];
1186
+ commonDocumentFilters: any[];
1187
+ perDataset: {
1188
+ contentType: any;
1189
+ display: any;
1190
+ filters: any[];
1191
+ rollups: any[];
1192
+ }[];
1193
+ };
1194
+ target: {
1195
+ filterType: string;
1196
+ };
1197
+ }[];
1198
+ }[];
1199
+ sectionTitle?: undefined;
1124
1200
  } | {
1125
1201
  sectionId: string;
1126
1202
  sectionTitle: string;
@@ -1191,11 +1267,30 @@ declare const UI_CONTENT: {
1191
1267
  };
1192
1268
  };
1193
1269
 
1194
- declare const genCleanCamelCaseId: (id: string) => string;
1270
+ interface GenCleanCamelCaseIdOptions {
1271
+ /** Strip all non-alphanumeric chars (A-Z, a-z, 0-9 only). Default: false */
1272
+ stripNonAlphanumeric?: boolean;
1273
+ /** Truncate result to this max length. Default: no limit */
1274
+ maxLength?: number;
1275
+ }
1276
+ declare const genCleanCamelCaseId: (id: string, options?: GenCleanCamelCaseIdOptions) => string;
1277
+
1278
+ /**
1279
+ * Generates a clean, deterministic contentType ID from text.
1280
+ * Wraps genCleanCamelCaseId with stripNonAlphanumeric + maxLength 40.
1281
+ *
1282
+ * Idempotent: running twice produces the same output.
1283
+ *
1284
+ * @example
1285
+ * genCleanContentTypeId("My Articles!") => "myArticles"
1286
+ * genCleanContentTypeId("myArticles") => "myArticles" (idempotent)
1287
+ * genCleanContentTypeId("123 Test") => "a123Test"
1288
+ */
1289
+ declare const genCleanContentTypeId: (text: string) => string;
1195
1290
 
1196
1291
  /**
1197
1292
  * Generates a clean, short, unique ID from text input.
1198
- * Used for both contentType IDs and block valuePaths.
1293
+ * Used for block valuePaths in templates.
1199
1294
  *
1200
1295
  * 1. Strips all characters except A-Z, a-z, 0-9
1201
1296
  * 2. Lowercases the result
@@ -1203,16 +1298,19 @@ declare const genCleanCamelCaseId: (id: string) => string;
1203
1298
  * 4. Truncates to maxLength (default 10)
1204
1299
  * 5. Appends _{random id} (default 4 chars)
1205
1300
  *
1301
+ * Idempotent: if input already matches the output pattern
1302
+ * (lowercase alphanumeric + _hash suffix), returns as-is.
1303
+ *
1206
1304
  * @param text - The input text to convert
1207
- * @param maxLength - Max characters before the random suffix (default 10, use 20 for contentType)
1305
+ * @param maxLength - Max characters before the random suffix (default 10)
1208
1306
  * @param shortIdLength - Length of the random suffix (default 4)
1209
1307
  *
1210
1308
  * @example
1211
- * genCleanIdForContentTypeAndValuePaths("Author Name") => "authorname_x7k2"
1212
- * genCleanIdForContentTypeAndValuePaths("My Articles!", 20) => "myarticles_m3p1"
1213
- * genCleanIdForContentTypeAndValuePaths("123 Test") => "a123test_ab4d"
1309
+ * genCleanValuePath("Author Name") => "authorname_x7k2"
1310
+ * genCleanValuePath("authorname_x7k2") => "authorname_x7k2" (idempotent)
1311
+ * genCleanValuePath("123 Test") => "a123test_ab4d"
1214
1312
  */
1215
- declare const genCleanIdForContentTypeAndValuePaths: (text: string, maxLength?: number, shortIdLength?: number) => string;
1313
+ declare const genCleanValuePath: (text: string) => string;
1216
1314
 
1217
1315
  declare class MongoConnector {
1218
1316
  static getInstance(): any;
@@ -1944,4 +2042,4 @@ declare function GET_GLOBAL_BULLMQ_CONFIG({ env, redisCredentials }: {
1944
2042
  };
1945
2043
  }): Object;
1946
2044
 
1947
- export { AIChatSchema, AnnosElasticSyncProducer, AnnotationSchema, BASE_BULLMQ_CONFIG, BaseProducer, BaseWorker, ChunksElasticSyncProducer, ElasticSearchConnector, FILTER_IDS, GET_GLOBAL_BULLMQ_CONFIG, GeneratedEntitiesSchema, GeneratedTopicsSchema, MongoConnector, PlatformConfigsSchema, ProducerManager, RedisCacheConnector, SecretManagerConnector, TEMP_removeDuplicateFilters, TplSchema, UI_CONTENT, WorkerManager, _self_managed_buildAnnoHierarchyConfig, _self_managed_getFixedAnnoRollupBlocks, _self_managed_getFixedAnnoTagBlock, autoGenFilterConfigsFromTpl, buildFilterConfigurations, compareAndGroupBlocks, deleteVal, extractAllBlocksFromTpl, extractAndOrganizeBlocks, genCleanCamelCaseId, genCleanIdForContentTypeAndValuePaths, genTagId, generateFilterKey, getAIChatModelByTenant, getAnnotationsModelByTenant, getDbByTenant, getFilterKeyForBlock, getGeneratedEntitiesModelByTenant, getGeneratedTopicsModelByTenant, getModelByTenant, getPlatformConfigsModelByTenant, getPlatformContextContent, getRollupPossibilities, getRoutePathToEditContent, getRoutePathToModerateContent, getRoutePathToPublishedContent, getTplModelByTenant, getVal, mergeAnnoDataIntoAnnotationsTags, parseSpecialConfigSyntax, processAuthorAndCommonFilters, _recursExtractBlocks as recursivelyExtractBlocks, segrigateDocs, setVal, toArray };
2045
+ export { AIChatSchema, AnnosElasticSyncProducer, AnnotationSchema, BASE_BULLMQ_CONFIG, BaseProducer, BaseWorker, ChunksElasticSyncProducer, ElasticSearchConnector, FILTER_IDS, GET_GLOBAL_BULLMQ_CONFIG, GeneratedEntitiesSchema, GeneratedTopicsSchema, MongoConnector, PlatformConfigsSchema, ProducerManager, RedisCacheConnector, SecretManagerConnector, TEMP_removeDuplicateFilters, TplSchema, UI_CONTENT, WorkerManager, _self_managed_buildAnnoHierarchyConfig, _self_managed_buildDocHierarchyConfig, _self_managed_getFixedAnnoRollupBlocks, _self_managed_getFixedAnnoTagBlock, autoGenFilterConfigsFromTpl, buildFilterConfigurations, compareAndGroupBlocks, deleteVal, extractAllBlocksFromTpl, extractAndOrganizeBlocks, genCleanCamelCaseId, genCleanContentTypeId, genCleanValuePath, genTagId, generateFilterKey, getAIChatModelByTenant, getAnnotationsModelByTenant, getDbByTenant, getFilterKeyForBlock, getGeneratedEntitiesModelByTenant, getGeneratedTopicsModelByTenant, getModelByTenant, getPlatformConfigsModelByTenant, getPlatformContextContent, getRollupPossibilities, getRoutePathToEditContent, getRoutePathToModerateContent, getRoutePathToPublishedContent, getTplModelByTenant, getVal, mergeAnnoDataIntoAnnotationsTags, parseSpecialConfigSyntax, processAuthorAndCommonFilters, _recursExtractBlocks as recursivelyExtractBlocks, segrigateDocs, setVal, toArray };
package/dist/node.js CHANGED
@@ -1939,6 +1939,7 @@ __export(node_exports, {
1939
1939
  UI_CONTENT: () => UI_CONTENT,
1940
1940
  WorkerManager: () => import_WorkerManager.WorkerManager,
1941
1941
  _self_managed_buildAnnoHierarchyConfig: () => _self_managed_buildAnnoHierarchyConfig,
1942
+ _self_managed_buildDocHierarchyConfig: () => _self_managed_buildDocHierarchyConfig,
1942
1943
  _self_managed_getFixedAnnoRollupBlocks: () => _self_managed_getFixedAnnoRollupBlocks,
1943
1944
  _self_managed_getFixedAnnoTagBlock: () => _self_managed_getFixedAnnoTagBlock,
1944
1945
  autoGenFilterConfigsFromTpl: () => autoGenFilterConfigsFromTpl,
@@ -1948,7 +1949,8 @@ __export(node_exports, {
1948
1949
  extractAllBlocksFromTpl: () => extractAllBlocksFromTpl,
1949
1950
  extractAndOrganizeBlocks: () => extractAndOrganizeBlocks,
1950
1951
  genCleanCamelCaseId: () => genCleanCamelCaseId,
1951
- genCleanIdForContentTypeAndValuePaths: () => genCleanIdForContentTypeAndValuePaths,
1952
+ genCleanContentTypeId: () => genCleanContentTypeId,
1953
+ genCleanValuePath: () => genCleanValuePath,
1952
1954
  genTagId: () => genTagId,
1953
1955
  generateFilterKey: () => generateFilterKey,
1954
1956
  getAIChatModelByTenant: () => import_getModelByTenant2.getAIChatModelByTenant,
@@ -2664,6 +2666,7 @@ var buildFilterConfigurations = ({ groups, type, selectedTpls, allTpls, isRollup
2664
2666
  filterKey,
2665
2667
  contentType: element.contentType,
2666
2668
  path: element.valuePath,
2669
+ ...element.comp && { comp: element.comp },
2667
2670
  source: {
2668
2671
  filterType: "tplType",
2669
2672
  contentType: element.contentType,
@@ -2689,6 +2692,7 @@ var buildFilterConfigurations = ({ groups, type, selectedTpls, allTpls, isRollup
2689
2692
  filterKey: valuePathFilterKey,
2690
2693
  contentType: element.contentType,
2691
2694
  path: element.valuePath,
2695
+ ...element.comp && { comp: element.comp },
2692
2696
  source: {
2693
2697
  filterType: "tplType",
2694
2698
  contentType: element.contentType,
@@ -2714,6 +2718,7 @@ var buildFilterConfigurations = ({ groups, type, selectedTpls, allTpls, isRollup
2714
2718
  filterKey: dateFilterKey,
2715
2719
  contentType: element.contentType,
2716
2720
  path: element.valuePath,
2721
+ ...element.comp && { comp: element.comp },
2717
2722
  source: { filterType: "dateRangeType" },
2718
2723
  target: { filterType: "dateRangeType", valuePath: element.valuePath }
2719
2724
  };
@@ -2731,6 +2736,7 @@ var buildFilterConfigurations = ({ groups, type, selectedTpls, allTpls, isRollup
2731
2736
  filterKey: numberFilterKey,
2732
2737
  contentType: element.contentType,
2733
2738
  path: element.valuePath,
2739
+ ...element.comp && { comp: element.comp },
2734
2740
  source: { filterType: "numberRangeType", valuePath: element.valuePath },
2735
2741
  target: { filterType: "numberRangeType", valuePath: element.valuePath }
2736
2742
  };
@@ -2851,6 +2857,7 @@ var buildFilterConfigurations = ({ groups, type, selectedTpls, allTpls, isRollup
2851
2857
  filterKey: tagFilterKey,
2852
2858
  contentType: element.tagType,
2853
2859
  path: `tags.${element.tagType}`,
2860
+ ...element.comp && { comp: element.comp },
2854
2861
  source: allProfileTpls.some(
2855
2862
  (tpl) => tpl.kp_content_type === element.tagType
2856
2863
  ) ? {
@@ -2992,6 +2999,7 @@ var extractAndOrganizeBlocks = (selectedTpls, allTpls) => {
2992
2999
  contentType: tpl.kp_content_type,
2993
3000
  blocks: allBlocks.filter((block) => block.valuePath.startsWith("tags.")).map((block) => ({
2994
3001
  tagType: block.props.tagType,
3002
+ comp: block.comp,
2995
3003
  filterType: "tagType"
2996
3004
  }))
2997
3005
  };
@@ -3019,6 +3027,7 @@ var extractAndOrganizeBlocks = (selectedTpls, allTpls) => {
3019
3027
  value: block.valuePath,
3020
3028
  saveValueAsString: block.props?.saveValueAsString,
3021
3029
  contentType: tpl.kp_content_type,
3030
+ comp: block.comp,
3022
3031
  filterType: "valuePathType"
3023
3032
  }))
3024
3033
  };
@@ -3033,6 +3042,7 @@ var extractAndOrganizeBlocks = (selectedTpls, allTpls) => {
3033
3042
  value: block.valuePath,
3034
3043
  saveValueAsString: block.props?.saveValueAsString,
3035
3044
  contentType: tpl.kp_content_type,
3045
+ comp: block.comp,
3036
3046
  block,
3037
3047
  filterType: "split_valuePathType"
3038
3048
  }))
@@ -3047,6 +3057,7 @@ var extractAndOrganizeBlocks = (selectedTpls, allTpls) => {
3047
3057
  display: block.props?.shortLabel || block.props?.label || block.valuePath,
3048
3058
  value: block.valuePath,
3049
3059
  contentType: tpl.kp_content_type,
3060
+ comp: block.comp,
3050
3061
  filterType: "dateRangeType"
3051
3062
  }))
3052
3063
  };
@@ -3060,6 +3071,7 @@ var extractAndOrganizeBlocks = (selectedTpls, allTpls) => {
3060
3071
  display: block.props?.shortLabel || block.props?.label || block.valuePath,
3061
3072
  value: block.valuePath,
3062
3073
  contentType: tpl.kp_content_type,
3074
+ comp: block.comp,
3063
3075
  filterType: "numberRangeType"
3064
3076
  }))
3065
3077
  };
@@ -3306,6 +3318,240 @@ var _self_managed_buildAnnoHierarchyConfig = ({
3306
3318
  };
3307
3319
  };
3308
3320
 
3321
+ // src/utils/autoGenFilterConfigsFromTpl/utils/buildTagTypeParentMap.ts
3322
+ var buildTagTypeParentMap = (tagTypesInGroup, allTpls) => {
3323
+ const tagTypeSet = new Set(tagTypesInGroup);
3324
+ const parentMap = /* @__PURE__ */ new Map();
3325
+ for (const tagType of tagTypesInGroup) {
3326
+ const tpl = allTpls.find((t) => t.kp_content_type === tagType);
3327
+ if (!tpl) continue;
3328
+ const blocks = extractAllBlocksFromTpl({ tpl });
3329
+ const tagBlocks = blocks.filter(
3330
+ (block) => block.valuePath?.startsWith("tags.") && block.props?.tagType
3331
+ );
3332
+ for (const block of tagBlocks) {
3333
+ const referencedTagType = block.props.tagType;
3334
+ if (tagTypeSet.has(referencedTagType) && referencedTagType !== tagType) {
3335
+ parentMap.set(tagType, referencedTagType);
3336
+ break;
3337
+ }
3338
+ }
3339
+ }
3340
+ for (const child of parentMap.keys()) {
3341
+ const visited = /* @__PURE__ */ new Set();
3342
+ let current = child;
3343
+ while (current && parentMap.has(current)) {
3344
+ if (visited.has(current)) {
3345
+ parentMap.delete(current);
3346
+ break;
3347
+ }
3348
+ visited.add(current);
3349
+ current = parentMap.get(current);
3350
+ }
3351
+ }
3352
+ return parentMap;
3353
+ };
3354
+ var attachParentFields = (filters, allTpls) => {
3355
+ const tagTypeFilters = filters.filter(
3356
+ (f) => f.source?.filterType === "tagType" && f.source?.tagType
3357
+ );
3358
+ if (tagTypeFilters.length === 0) return filters;
3359
+ const tagTypesInGroup = tagTypeFilters.map((f) => f.source.tagType);
3360
+ const parentMap = buildTagTypeParentMap(tagTypesInGroup, allTpls);
3361
+ if (parentMap.size === 0) return filters;
3362
+ const tagTypeToFilterId = /* @__PURE__ */ new Map();
3363
+ for (const f of tagTypeFilters) {
3364
+ tagTypeToFilterId.set(f.source.tagType, f.filterId);
3365
+ }
3366
+ return filters.map((f) => {
3367
+ if (f.source?.filterType !== "tagType" || !f.source?.tagType) return f;
3368
+ const parentTagType = parentMap.get(f.source.tagType);
3369
+ if (!parentTagType) return f;
3370
+ const parentFilterId = tagTypeToFilterId.get(parentTagType);
3371
+ if (!parentFilterId) return f;
3372
+ return { ...f, parentFilterId };
3373
+ });
3374
+ };
3375
+ var sortFiltersHierarchically = (filters) => {
3376
+ if (filters.length <= 1) return filters;
3377
+ const hasHierarchy = filters.some((f) => f.parentFilterId);
3378
+ if (!hasHierarchy) return filters;
3379
+ const childrenOf = /* @__PURE__ */ new Map();
3380
+ const roots = [];
3381
+ for (const f of filters) {
3382
+ if (!f.parentFilterId) {
3383
+ roots.push(f);
3384
+ } else {
3385
+ const siblings = childrenOf.get(f.parentFilterId) || [];
3386
+ siblings.push(f);
3387
+ childrenOf.set(f.parentFilterId, siblings);
3388
+ }
3389
+ }
3390
+ const sorted = [];
3391
+ const visited = /* @__PURE__ */ new Set();
3392
+ const visit = (filter) => {
3393
+ if (visited.has(filter.filterId)) return;
3394
+ visited.add(filter.filterId);
3395
+ sorted.push(filter);
3396
+ const children = childrenOf.get(filter.filterId) || [];
3397
+ for (const child of children) {
3398
+ visit(child);
3399
+ }
3400
+ };
3401
+ for (const root of roots) {
3402
+ visit(root);
3403
+ }
3404
+ for (const f of filters) {
3405
+ if (!visited.has(f.filterId)) {
3406
+ sorted.push(f);
3407
+ }
3408
+ }
3409
+ return sorted;
3410
+ };
3411
+
3412
+ // src/utils/autoGenFilterConfigsFromTpl/utils/_self_managed_buildDocHierarchyConfig.ts
3413
+ var _self_managed_buildDocHierarchyConfig = ({
3414
+ combinedDocumentBlocks,
3415
+ docRollupBlocks,
3416
+ selectedTpls,
3417
+ allTpls,
3418
+ commonDocumentFilters,
3419
+ flatDocFilters = false
3420
+ }) => {
3421
+ const allGroups = compareAndGroupBlocks(combinedDocumentBlocks);
3422
+ const commonGroup = allGroups.find(
3423
+ (g) => g.contentTypes.length === selectedTpls.length
3424
+ );
3425
+ const commonFilterConfigs = commonGroup ? sortFiltersHierarchically(
3426
+ attachParentFields(
3427
+ buildFilterConfigurations({
3428
+ groups: [commonGroup],
3429
+ type: "tags",
3430
+ selectedTpls,
3431
+ allTpls,
3432
+ isRollup: false,
3433
+ isAnno: false
3434
+ }).flatMap((s) => s.configs),
3435
+ allTpls
3436
+ )
3437
+ ) : [];
3438
+ const multiDatasetGroups = selectedTpls.length >= 2 ? allGroups.filter(
3439
+ (g) => g.contentTypes.length > 1 && g.contentTypes.length < selectedTpls.length
3440
+ ) : [];
3441
+ const sharedAcrossMultiple = multiDatasetGroups.map((group) => {
3442
+ const groupFilterConfigs = sortFiltersHierarchically(
3443
+ attachParentFields(
3444
+ buildFilterConfigurations({
3445
+ groups: [group],
3446
+ type: "tags",
3447
+ selectedTpls,
3448
+ allTpls,
3449
+ isRollup: false,
3450
+ isAnno: false
3451
+ }).flatMap((s) => s.configs),
3452
+ allTpls
3453
+ )
3454
+ );
3455
+ const display = group.contentTypes.map((ct) => {
3456
+ const tplData = allTpls.find(
3457
+ (t) => t.kp_content_type === ct
3458
+ );
3459
+ return tplData?.general?.content?.title || ct;
3460
+ }).join(" + ");
3461
+ return {
3462
+ contentTypes: group.contentTypes,
3463
+ display,
3464
+ filters: groupFilterConfigs
3465
+ };
3466
+ }).filter((g) => g.filters.length > 0);
3467
+ const commonFilterIds = new Set(commonFilterConfigs.map((f) => f.filterId));
3468
+ const perDataset = selectedTpls.map((tpl) => {
3469
+ const tplBlocks = combinedDocumentBlocks.find(
3470
+ (b) => b.contentType === tpl.kp_content_type
3471
+ );
3472
+ const singleTplGroup = [
3473
+ {
3474
+ contentTypes: [tpl.kp_content_type],
3475
+ elements: tplBlocks?.blocks || []
3476
+ }
3477
+ ];
3478
+ const filters = buildFilterConfigurations({
3479
+ groups: singleTplGroup,
3480
+ type: "tags",
3481
+ selectedTpls: [tpl],
3482
+ allTpls,
3483
+ isRollup: false,
3484
+ isAnno: false
3485
+ }).flatMap((s) => s.configs);
3486
+ const filtersWithCommonFlag = filters.map((f) => ({
3487
+ ...f,
3488
+ isSharedFilter: commonFilterIds.has(f.filterId)
3489
+ }));
3490
+ const rollupData = docRollupBlocks?.find(
3491
+ (b) => b.contentType === tpl.kp_content_type
3492
+ );
3493
+ const rollupGroups = rollupData?.blocks?.length ? compareAndGroupBlocks([rollupData]) : [];
3494
+ const rollupConfigs = rollupGroups.length ? buildFilterConfigurations({
3495
+ groups: rollupGroups,
3496
+ type: "tags",
3497
+ selectedTpls: [tpl],
3498
+ allTpls,
3499
+ isRollup: true,
3500
+ isAnno: false
3501
+ }).flatMap((s) => s.configs) : [];
3502
+ const tplData = allTpls.find(
3503
+ (t) => t.kp_content_type === tpl.kp_content_type
3504
+ );
3505
+ const rollupSourceKeys = new Set(
3506
+ rollupConfigs.map((r) => JSON.stringify(r.source))
3507
+ );
3508
+ const dedupedFilters = filtersWithCommonFlag.filter(
3509
+ (f) => !rollupSourceKeys.has(JSON.stringify(f.source))
3510
+ );
3511
+ const combined = [...dedupedFilters, ...rollupConfigs];
3512
+ const combinedWithParents = sortFiltersHierarchically(
3513
+ attachParentFields(combined, allTpls)
3514
+ );
3515
+ const rollupFilterIds = new Set(rollupConfigs.map((r) => r.filterId));
3516
+ const filtersWithParents = combinedWithParents.filter(
3517
+ (f) => !rollupFilterIds.has(f.filterId)
3518
+ );
3519
+ const rollupsWithParents = combinedWithParents.filter(
3520
+ (f) => rollupFilterIds.has(f.filterId)
3521
+ );
3522
+ return {
3523
+ contentType: tpl.kp_content_type,
3524
+ display: tplData?.general?.content?.title || tpl.kp_content_type,
3525
+ filters: filtersWithParents,
3526
+ rollups: rollupsWithParents
3527
+ };
3528
+ });
3529
+ const filteredPerDataset = perDataset.filter(
3530
+ (d) => d.filters.length > 0 || d.rollups.length > 0
3531
+ );
3532
+ if (commonFilterConfigs.length === 0 && sharedAcrossMultiple.length === 0 && filteredPerDataset.every((d) => d.filters.length === 0)) {
3533
+ return null;
3534
+ }
3535
+ const flatCommonDocFilters = commonDocumentFilters.flatMap(
3536
+ (s) => s.configs || [s]
3537
+ );
3538
+ const shouldFlatten = flatDocFilters && selectedTpls.length === 1;
3539
+ return {
3540
+ filterId: "docHierarchy_document_filters",
3541
+ display: "Combined Content Filters",
3542
+ filterKey: "docHierarchyType::doc",
3543
+ source: {
3544
+ filterType: "docHierarchyType",
3545
+ flatDocFilters: shouldFlatten,
3546
+ commonFilters: commonFilterConfigs,
3547
+ sharedAcrossMultiple,
3548
+ commonDocumentFilters: flatCommonDocFilters,
3549
+ perDataset: filteredPerDataset
3550
+ },
3551
+ target: { filterType: "docHierarchyType" }
3552
+ };
3553
+ };
3554
+
3309
3555
  // src/utils/autoGenFilterConfigsFromTpl/utils/TEMP_removeDuplicateFilters.ts
3310
3556
  var TEMP_removeDuplicateFilters = (filterGroups) => {
3311
3557
  return filterGroups.map((group) => {
@@ -3442,7 +3688,25 @@ var autoGenFilterConfigsFromTpl = ({
3442
3688
  ...annotationRollupSections
3443
3689
  ].filter((section) => Array.isArray(section.configs) ? section.configs.length > 0 : true)
3444
3690
  };
3445
- const final_documentTagsFilterConfigs = {
3691
+ const isSMTDocFilterUI = isSelfManagedTenant && filterScopes.includes("doc");
3692
+ const final_documentTagsFilterConfigs = isSMTDocFilterUI ? (() => {
3693
+ const docHierarchyConfig = _self_managed_buildDocHierarchyConfig({
3694
+ combinedDocumentBlocks: extractedBlocks.combinedDocumentBlocks,
3695
+ docRollupBlocks: extractedBlocks.docRollupBlocks,
3696
+ selectedTpls,
3697
+ allTpls,
3698
+ commonDocumentFilters
3699
+ });
3700
+ if (!docHierarchyConfig) return { sectionId: "documentTagsSection", sectionTitle: "Document Filters", configs: [] };
3701
+ return {
3702
+ sectionId: "documentTagsSection",
3703
+ // sectionTitle: 'Document Filters',
3704
+ configs: [{
3705
+ sectionId: "self_managed_consolidated_doc_filters",
3706
+ configs: [docHierarchyConfig]
3707
+ }]
3708
+ };
3709
+ })() : {
3446
3710
  sectionId: "documentTagsSection",
3447
3711
  sectionTitle: "Document Filters",
3448
3712
  configs: [
@@ -3458,20 +3722,33 @@ var autoGenFilterConfigsFromTpl = ({
3458
3722
  };
3459
3723
 
3460
3724
  // src/utils/genCleanCamelCaseId.ts
3461
- var genCleanCamelCaseId = (id) => {
3725
+ var genCleanCamelCaseId = (id, options) => {
3462
3726
  if (!id || typeof id !== "string") return id;
3463
- const isAlreadyClean = /^\p{Ll}[\p{L}\p{N}]*$/u.test(id) || /^a\d[\p{L}\p{N}]*$/u.test(id);
3464
- if (isAlreadyClean) return id;
3727
+ const { stripNonAlphanumeric = false, maxLength } = options || {};
3728
+ const baseClean = /^\p{Ll}[\p{L}\p{N}]*$/u.test(id) || /^a\d[\p{L}\p{N}]*$/u.test(id);
3729
+ const withinMaxLength = maxLength ? id.length <= maxLength : true;
3730
+ const hasNoNonAlpha = stripNonAlphanumeric ? /^[a-zA-Z0-9]+$/.test(id) : true;
3731
+ if (baseClean && withinMaxLength && hasNoNonAlpha) return id;
3465
3732
  const words = id.split(/[^\p{L}\p{N}]+/u).filter(Boolean);
3466
3733
  if (words.length === 0) return "a";
3467
- const result = words.map((word, i) => {
3734
+ let result = words.map((word, i) => {
3468
3735
  const lower = word.toLowerCase();
3469
3736
  return i === 0 ? lower : lower.charAt(0).toUpperCase() + lower.slice(1);
3470
3737
  }).join("");
3471
- return /^\d/.test(result) ? "a" + result : result;
3738
+ if (/^\d/.test(result)) result = "a" + result;
3739
+ if (stripNonAlphanumeric) {
3740
+ result = result.replace(/[^a-zA-Z0-9]/g, "");
3741
+ }
3742
+ if (maxLength) {
3743
+ result = result.slice(0, maxLength);
3744
+ }
3745
+ return result;
3472
3746
  };
3473
3747
 
3474
- // src/utils/genCleanIdForContentTypeAndValuePaths.ts
3748
+ // src/utils/genCleanContentTypeId.ts
3749
+ var genCleanContentTypeId = (text) => genCleanCamelCaseId(text, { stripNonAlphanumeric: true, maxLength: 40 });
3750
+
3751
+ // src/utils/genCleanValuePath.ts
3475
3752
  var CHARS = "abcdefghijklmnopqrstuvwxyz0123456789";
3476
3753
  function genShortId(length = 4) {
3477
3754
  let result = "";
@@ -3480,11 +3757,15 @@ function genShortId(length = 4) {
3480
3757
  }
3481
3758
  return result;
3482
3759
  }
3483
- var genCleanIdForContentTypeAndValuePaths = (text, maxLength = 10, shortIdLength = 4) => {
3760
+ var genCleanValuePath = (text) => {
3761
+ const maxLength = 10;
3762
+ const shortIdLength = 4;
3484
3763
  if (!text || typeof text !== "string") return text;
3764
+ const idempotencyPattern = new RegExp(`^[a-z][a-z0-9]*_[a-z0-9]{${shortIdLength}}$`);
3765
+ if (idempotencyPattern.test(text)) return text;
3485
3766
  let cleaned = text.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
3486
3767
  if (!cleaned) {
3487
- throw new Error(`[genCleanIdForContentTypeAndValuePaths] Input "${text}" contains no alphanumeric characters`);
3768
+ throw new Error(`[genCleanValuePath] Input "${text}" contains no alphanumeric characters`);
3488
3769
  }
3489
3770
  if (/^\d/.test(cleaned)) cleaned = "a" + cleaned;
3490
3771
  const truncated = cleaned.slice(0, maxLength);
@@ -3898,6 +4179,7 @@ var import_GET_GLOBAL_BULLMQ_CONFIG = __toESM(require_GET_GLOBAL_BULLMQ_CONFIG()
3898
4179
  UI_CONTENT,
3899
4180
  WorkerManager,
3900
4181
  _self_managed_buildAnnoHierarchyConfig,
4182
+ _self_managed_buildDocHierarchyConfig,
3901
4183
  _self_managed_getFixedAnnoRollupBlocks,
3902
4184
  _self_managed_getFixedAnnoTagBlock,
3903
4185
  autoGenFilterConfigsFromTpl,
@@ -3907,7 +4189,8 @@ var import_GET_GLOBAL_BULLMQ_CONFIG = __toESM(require_GET_GLOBAL_BULLMQ_CONFIG()
3907
4189
  extractAllBlocksFromTpl,
3908
4190
  extractAndOrganizeBlocks,
3909
4191
  genCleanCamelCaseId,
3910
- genCleanIdForContentTypeAndValuePaths,
4192
+ genCleanContentTypeId,
4193
+ genCleanValuePath,
3911
4194
  genTagId,
3912
4195
  generateFilterKey,
3913
4196
  getAIChatModelByTenant,