@okf/ootils 1.39.3 → 1.41.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/dist/node.mjs CHANGED
@@ -4,6 +4,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __getProtoOf = Object.getPrototypeOf;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __typeError = (msg) => {
8
+ throw TypeError(msg);
9
+ };
7
10
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
11
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
9
12
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
@@ -39,6 +42,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
39
42
  ));
40
43
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
41
44
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
45
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
46
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
47
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
42
48
 
43
49
  // src/bullmq/GLOBAL_BULLMQ_CONFIG.js
44
50
  var GLOBAL_BULLMQ_CONFIG_exports = {};
@@ -1917,6 +1923,133 @@ var require_BaseProducer = __commonJS({
1917
1923
  }
1918
1924
  });
1919
1925
 
1926
+ // src/bullmq/BaseFlowProducer.js
1927
+ var require_BaseFlowProducer = __commonJS({
1928
+ "src/bullmq/BaseFlowProducer.js"(exports, module) {
1929
+ "use strict";
1930
+ var { FlowProducer, Queue } = __require("bullmq");
1931
+ var _BaseFlowProducer_instances, mergeOpts_fn, childConfig_fn;
1932
+ var BaseFlowProducer2 = class {
1933
+ constructor({ parentConfig, childConfigs = {} }) {
1934
+ __privateAdd(this, _BaseFlowProducer_instances);
1935
+ if (!parentConfig) {
1936
+ throw new Error("BaseFlowProducer requires a parentConfig");
1937
+ }
1938
+ this.parentConfig = parentConfig;
1939
+ this.childConfigs = childConfigs;
1940
+ this.flow = new FlowProducer({
1941
+ connection: parentConfig.queueConfig.connection
1942
+ });
1943
+ }
1944
+ /**
1945
+ * Build & enqueue a flow declaratively.
1946
+ *
1947
+ * @param {Object} params
1948
+ * @param {string} [params.name] Parent job name.
1949
+ * @param {Object} params.parentData Parent job data.
1950
+ * @param {Object} [params.parentOpts] Per-node opts for the parent, layered
1951
+ * over the parent queue's defaults.
1952
+ * @param {Array} params.children One entry per child node:
1953
+ * @param {string} children[].childKey Key into childConfigs (which queue).
1954
+ * @param {string} [children[].name] Child job name.
1955
+ * @param {Object} children[].data Child job data.
1956
+ * @param {Object} [children[].opts] Job-specific opts (e.g.
1957
+ * failParentOnFailure); layered over
1958
+ * that child queue's defaults.
1959
+ * @returns {Promise<{ parentJobId, childJobIds }>}
1960
+ */
1961
+ async addFlow({ name, parentData, parentOpts = {}, children = [] }) {
1962
+ const flowJob = await this.flow.add({
1963
+ name,
1964
+ queueName: this.parentConfig.id,
1965
+ data: parentData,
1966
+ opts: __privateMethod(this, _BaseFlowProducer_instances, mergeOpts_fn).call(this, this.parentConfig, parentOpts),
1967
+ children: children.map((child) => {
1968
+ const childCfg = __privateMethod(this, _BaseFlowProducer_instances, childConfig_fn).call(this, child.childKey);
1969
+ return {
1970
+ name: child.name,
1971
+ queueName: childCfg.id,
1972
+ data: child.data,
1973
+ opts: __privateMethod(this, _BaseFlowProducer_instances, mergeOpts_fn).call(this, childCfg, child.opts)
1974
+ };
1975
+ })
1976
+ });
1977
+ const parentJobId = flowJob?.job?.id;
1978
+ const childJobIds = (flowJob?.children || []).map((c) => c?.job?.id).filter(Boolean);
1979
+ return { parentJobId, childJobIds };
1980
+ }
1981
+ /**
1982
+ * Find the FIRST parent job in the given states whose data matches the
1983
+ * predicate — e.g. an in-flight lock check ("is a run already pending for
1984
+ * this tenant?"). Opens a short-lived Queue on the parent queue and closes
1985
+ * it before returning, so callers don't have to reach into config internals
1986
+ * to construct one themselves.
1987
+ *
1988
+ * Returns the first match or null. BullMQ can't filter by job.data
1989
+ * server-side, so this still fetches the in-flight set and scans in JS; the
1990
+ * contract is intentionally first-match (not a list) because that's the only
1991
+ * thing the lock-check use case needs.
1992
+ *
1993
+ * @param {Object} params
1994
+ * @param {string[]} params.states
1995
+ * Job states to scan, e.g. ["waiting", "active", "delayed"].
1996
+ * @param {(data: any, job: any) => boolean} params.matchData
1997
+ * Required function; receives job.data (and the job). Without it there's
1998
+ * no meaningful "match", so this throws — returning an arbitrary in-flight
1999
+ * job would be a silent caller bug.
2000
+ * @returns {Promise<Object|null>} The first matching parent job, or null.
2001
+ */
2002
+ async findParentJob({ states, matchData }) {
2003
+ if (typeof matchData !== "function") {
2004
+ throw new Error("BaseFlowProducer.findParentJob requires a matchData function");
2005
+ }
2006
+ const parentQueue = new Queue(
2007
+ this.parentConfig.id,
2008
+ this.parentConfig.queueConfig
2009
+ );
2010
+ try {
2011
+ const jobs = await parentQueue.getJobs(states, 0, -1);
2012
+ return jobs.find((job) => matchData(job?.data, job)) || null;
2013
+ } finally {
2014
+ await parentQueue.close();
2015
+ }
2016
+ }
2017
+ async stop() {
2018
+ if (this.flow) {
2019
+ try {
2020
+ await this.flow.close();
2021
+ } catch (err) {
2022
+ console.warn(`[${this.constructor.name}] flow close failed: ${err.message}`);
2023
+ }
2024
+ }
2025
+ }
2026
+ };
2027
+ _BaseFlowProducer_instances = new WeakSet();
2028
+ /**
2029
+ * Layer a queue config's defaultJobOptions under per-node opts — identical
2030
+ * to how BullMQ merges defaultJobOptions under per-`add` opts for a Queue.
2031
+ * Config is the base; anything the node explicitly sets wins.
2032
+ */
2033
+ mergeOpts_fn = function(queueConfig, nodeOpts = {}) {
2034
+ const defaults = queueConfig?.queueConfig?.defaultJobOptions || {};
2035
+ return { ...defaults, ...nodeOpts };
2036
+ };
2037
+ /**
2038
+ * Resolve the GLOBAL_BULLMQ_CONFIG entry for a declared child key.
2039
+ */
2040
+ childConfig_fn = function(childKey) {
2041
+ const cfg = this.childConfigs[childKey];
2042
+ if (!cfg) {
2043
+ throw new Error(
2044
+ `BaseFlowProducer: no child queue declared under key "${childKey}". Declared keys: [${Object.keys(this.childConfigs).join(", ")}]`
2045
+ );
2046
+ }
2047
+ return cfg;
2048
+ };
2049
+ module.exports = { BaseFlowProducer: BaseFlowProducer2 };
2050
+ }
2051
+ });
2052
+
1920
2053
  // src/bullmq/BaseWorker.js
1921
2054
  var require_BaseWorker = __commonJS({
1922
2055
  "src/bullmq/BaseWorker.js"(exports, module) {
@@ -2754,7 +2887,8 @@ var collectMarkIdsInOrder = (editorState) => {
2754
2887
  var MAX_DEPTH_ROLLUP_POSSIBILITIES = 10;
2755
2888
  function getRollupPossibilities({
2756
2889
  tagType,
2757
- allTpls
2890
+ allTpls,
2891
+ includeSelfHead = false
2758
2892
  }) {
2759
2893
  const thisTagTpl = allTpls.find((d) => d.kp_content_type === tagType);
2760
2894
  if (!thisTagTpl) return [];
@@ -2786,7 +2920,8 @@ function getRollupPossibilities({
2786
2920
  chains.push({
2787
2921
  rollupType: "tagType",
2788
2922
  tagTypeCollectionToRollup: tagType,
2789
- rollupPath: [...visited, startTagType, nextTagType]
2923
+ rollupPath: [...visited, startTagType, nextTagType],
2924
+ filterDisplay: block.props?.shortLabel || block.props?.label
2790
2925
  });
2791
2926
  if (visited.length < maxDepth - 1) {
2792
2927
  const nextChains = findTagChains(
@@ -2832,7 +2967,8 @@ function getRollupPossibilities({
2832
2967
  const directChain = {
2833
2968
  rollupType: "tagType",
2834
2969
  tagTypeCollectionToRollup: tagType,
2835
- rollupPath: [tagType, block.props.tagType]
2970
+ rollupPath: [tagType, block.props.tagType],
2971
+ filterDisplay: block.props?.shortLabel || block.props?.label
2836
2972
  };
2837
2973
  const deeperChains = findTagChains(
2838
2974
  block.props?.tagType,
@@ -2841,7 +2977,14 @@ function getRollupPossibilities({
2841
2977
  );
2842
2978
  return [directChain, ...deeperChains];
2843
2979
  });
2844
- return [...singleLevelValuePathRollups, ...nestedTagRollups];
2980
+ const selfHeadRollup = includeSelfHead && nestedTagRollups.length > 0 && singleLevelValuePathRollups.length === 0 ? [
2981
+ {
2982
+ rollupType: "tagType",
2983
+ tagTypeCollectionToRollup: tagType,
2984
+ rollupPath: [tagType]
2985
+ }
2986
+ ] : [];
2987
+ return [...singleLevelValuePathRollups, ...selfHeadRollup, ...nestedTagRollups];
2845
2988
  }
2846
2989
 
2847
2990
  // src/universal.ts
@@ -3249,6 +3392,7 @@ var buildFilterConfigurations = ({ groups, type, selectedTpls, allTpls, isRollup
3249
3392
  if (isTerminalValuePath) {
3250
3393
  return element.filterDisplay || lastItemInPath;
3251
3394
  }
3395
+ if (element.filterDisplay) return element.filterDisplay;
3252
3396
  const lastTag = rollupPath[rollupPath.length - 1];
3253
3397
  const lastTagTpl = allTpls.find(
3254
3398
  (tpl) => tpl.kp_content_type === lastTag
@@ -3515,7 +3659,7 @@ var extractAndOrganizeBlocks = (selectedTpls, allTpls, { smTagTypesConfig } = {}
3515
3659
  return {
3516
3660
  contentType: tpl.kp_content_type,
3517
3661
  blocks: allBlocks.filter((block) => block.valuePath.startsWith("tags.") && ["TagsInputSingle", "TagsInputMulti"].includes(block.comp)).flatMap((block) => {
3518
- const possibilities = getRollupPossibilities({ tagType: block.props.tagType, allTpls });
3662
+ const possibilities = getRollupPossibilities({ tagType: block.props.tagType, allTpls, includeSelfHead: true });
3519
3663
  return possibilities.map((p) => ({
3520
3664
  ...p,
3521
3665
  filterType: p.rollupPath ? "nestedRollupTagType" : "rollupValuePathType"
@@ -3848,13 +3992,16 @@ var buildTagTypeParentMap = (tagTypesInGroup, allTpls) => {
3848
3992
  const tagBlocks = blocks.filter(
3849
3993
  (block) => block.valuePath?.startsWith("tags.") && block.props?.tagType
3850
3994
  );
3995
+ const referencedTagTypes = /* @__PURE__ */ new Set();
3851
3996
  for (const block of tagBlocks) {
3852
3997
  const referencedTagType = block.props.tagType;
3853
3998
  if (tagTypeSet.has(referencedTagType) && referencedTagType !== tagType) {
3854
- parentMap.set(tagType, referencedTagType);
3855
- break;
3999
+ referencedTagTypes.add(referencedTagType);
3856
4000
  }
3857
4001
  }
4002
+ if (referencedTagTypes.size === 1) {
4003
+ parentMap.set(tagType, [...referencedTagTypes][0]);
4004
+ }
3858
4005
  }
3859
4006
  for (const child of parentMap.keys()) {
3860
4007
  const visited = /* @__PURE__ */ new Set();
@@ -3928,6 +4075,34 @@ var sortFiltersHierarchically = (filters) => {
3928
4075
  return sorted;
3929
4076
  };
3930
4077
 
4078
+ // src/utils/autoGenFilterConfigsFromTpl/utils/deduplicateRollupTagFilters.ts
4079
+ var deduplicateRollupTagFilters = (rollups) => {
4080
+ const tagTerminals = [];
4081
+ const nonTag = [];
4082
+ for (const r of rollups) {
4083
+ const rp = r.target?.rollupPath;
4084
+ if (rp?.length && !rp[rp.length - 1].startsWith("main.")) {
4085
+ tagTerminals.push(r);
4086
+ } else {
4087
+ nonTag.push({ ...r, isTagRollup: false });
4088
+ }
4089
+ }
4090
+ if (tagTerminals.length === 0) return nonTag;
4091
+ const byTerminal = /* @__PURE__ */ new Map();
4092
+ for (const r of tagTerminals) {
4093
+ const terminal = r.target.rollupPath[r.target.rollupPath.length - 1];
4094
+ const existing = byTerminal.get(terminal);
4095
+ if (!existing || r.target.rollupPath.length < existing.target.rollupPath.length) {
4096
+ byTerminal.set(terminal, r);
4097
+ }
4098
+ }
4099
+ const dedupedTags = [...byTerminal.values()].map((r) => ({
4100
+ ...r,
4101
+ isTagRollup: true
4102
+ }));
4103
+ return [...nonTag, ...dedupedTags];
4104
+ };
4105
+
3931
4106
  // src/utils/autoGenFilterConfigsFromTpl/utils/_self_managed_buildDocHierarchyConfig.ts
3932
4107
  var injectFilterOptionsBy = (filters) => filters.map(
3933
4108
  (f) => f.parentFilterId ? { ...f, source: { ...f.source, filterOptionsBy: { filterId: f.parentFilterId } } } : f
@@ -4034,24 +4209,34 @@ var _self_managed_buildDocHierarchyConfig = ({
4034
4209
  const dedupedFilters = filtersWithCommonFlag.filter(
4035
4210
  (f) => !rollupSourceKeys.has(JSON.stringify(f.source))
4036
4211
  );
4037
- const combined = [...dedupedFilters, ...rollupConfigs];
4212
+ const dedupedRollupConfigs = deduplicateRollupTagFilters(rollupConfigs);
4213
+ const combined = [...dedupedFilters, ...dedupedRollupConfigs];
4038
4214
  const combinedWithParents = injectFilterOptionsBy(
4039
4215
  sortFiltersHierarchically(
4040
4216
  attachParentFields(combined, allTpls)
4041
4217
  )
4042
4218
  );
4043
- const rollupFilterIds = new Set(rollupConfigs.map((r) => r.filterId));
4219
+ const rollupFilterIds = new Set(dedupedRollupConfigs.map((r) => r.filterId));
4044
4220
  const filtersWithParents = combinedWithParents.filter(
4045
4221
  (f) => !rollupFilterIds.has(f.filterId)
4046
4222
  );
4047
4223
  const rollupsWithParents = combinedWithParents.filter(
4048
4224
  (f) => rollupFilterIds.has(f.filterId)
4049
4225
  );
4226
+ const rollupContentTypes = new Set(
4227
+ rollupsWithParents.filter((r) => r.target?.rollupPath).flatMap((r) => r.target.rollupPath)
4228
+ );
4229
+ const rollupDisplayMap = {};
4230
+ for (const ct of rollupContentTypes) {
4231
+ const t = allTpls.find((tp) => tp.kp_content_type === ct);
4232
+ if (t?.general?.content?.title) rollupDisplayMap[ct] = t.general.content.title;
4233
+ }
4050
4234
  return {
4051
4235
  contentType: tpl.kp_content_type,
4052
4236
  display: tplData?.general?.content?.title || tpl.kp_content_type,
4053
4237
  filters: filtersWithParents,
4054
- rollups: rollupsWithParents
4238
+ rollups: rollupsWithParents,
4239
+ rollupDisplayMap
4055
4240
  };
4056
4241
  });
4057
4242
  const filteredPerDataset = perDataset.filter(
@@ -4696,11 +4881,13 @@ init_models();
4696
4881
  var import_WorkerManager = __toESM(require_WorkerManager());
4697
4882
  var import_ProducerManager = __toESM(require_ProducerManager());
4698
4883
  var import_BaseProducer = __toESM(require_BaseProducer());
4884
+ var import_BaseFlowProducer = __toESM(require_BaseFlowProducer());
4699
4885
  var import_BaseWorker = __toESM(require_BaseWorker());
4700
4886
  var import_ChunksElasticSyncProducer = __toESM(require_ChunksElasticSyncProducer());
4701
4887
  var import_AnnosElasticSyncProducer = __toESM(require_AnnosElasticSyncProducer());
4702
4888
  var import_GET_GLOBAL_BULLMQ_CONFIG = __toESM(require_GET_GLOBAL_BULLMQ_CONFIG());
4703
4889
  var export_AnnosElasticSyncProducer = import_AnnosElasticSyncProducer.AnnosElasticSyncProducer;
4890
+ var export_BaseFlowProducer = import_BaseFlowProducer.BaseFlowProducer;
4704
4891
  var export_BaseProducer = import_BaseProducer.BaseProducer;
4705
4892
  var export_BaseWorker = import_BaseWorker.BaseWorker;
4706
4893
  var export_ChunksElasticSyncProducer = import_ChunksElasticSyncProducer.ChunksElasticSyncProducer;
@@ -4721,6 +4908,7 @@ export {
4721
4908
  export_AnnosElasticSyncProducer as AnnosElasticSyncProducer,
4722
4909
  Annotations_default as AnnotationSchema,
4723
4910
  BASE_BULLMQ_CONFIG,
4911
+ export_BaseFlowProducer as BaseFlowProducer,
4724
4912
  export_BaseProducer as BaseProducer,
4725
4913
  export_BaseWorker as BaseWorker,
4726
4914
  BlockRegistry,
@@ -285,9 +285,10 @@ declare const collectMarkIdsInOrder: (editorState: any) => string[];
285
285
  * }
286
286
  * ]
287
287
  */
288
- declare function getRollupPossibilities({ tagType, allTpls }: {
288
+ declare function getRollupPossibilities({ tagType, allTpls, includeSelfHead, }: {
289
289
  tagType: string;
290
290
  allTpls: any[];
291
+ includeSelfHead?: boolean;
291
292
  }): any[];
292
293
 
293
294
  declare namespace BASE_BULLMQ_CONFIG {
@@ -1241,6 +1242,27 @@ declare const processAuthorAndCommonFilters: (allTpls: any[], filterScopes: stri
1241
1242
 
1242
1243
  declare const _self_managed_getFixedAnnoTagBlock: (selectedTpls: any[], annotationTagsCount: any) => any[];
1243
1244
 
1245
+ /**
1246
+ * FOLLOWUP (consolidate the count->rollupPath rule into one source of truth):
1247
+ *
1248
+ * The rule "given annotationTagsCount, which self-managed anno rollupPaths exist"
1249
+ * (subThemes>0 -> ['tags','subThemes']; subThemes>0 && themes>0 -> ['tags','subThemes','themes'])
1250
+ * is currently duplicated in THREE places and will drift:
1251
+ * 1. here (_self_managed_getFixedAnnoRollupBlocks) -> filter blocks
1252
+ * 2. _self_managed_buildAnnoHierarchyConfig -> anno hierarchy filter UI
1253
+ * 3. okf-fe _extractAllAnnoBasedDimensionsAsOptions.js -> reports dimension dropdown
1254
+ *
1255
+ * (#3 was added to fix a crash: the reports widget previously discovered these rollups
1256
+ * from publishing-segment tpls via getRollupPossibilities, but self-managed taxonomy lives
1257
+ * on `collections`-segment tpls which isTplAnnotationEnabled excludes -> empty list ->
1258
+ * unguarded .find('subThemes') returned undefined -> crash. The FE now builds them from the
1259
+ * count, mirroring this function.)
1260
+ *
1261
+ * Extract a pure helper here in ootils:
1262
+ * export const getSelfManagedAnnoRollupPaths = (annotationTagsCount): string[][] => [...]
1263
+ * then have all three call sites consume it (each maps paths -> its own shape:
1264
+ * filter-blocks / hierarchy levels / dropdown options). One rule, three presentations.
1265
+ */
1244
1266
  declare const _self_managed_getFixedAnnoRollupBlocks: ({ selectedTpls, allTpls, annotationTagsCount }: {
1245
1267
  selectedTpls: any[];
1246
1268
  allTpls: any[];
@@ -1342,6 +1364,7 @@ declare const _self_managed_buildDocHierarchyConfig: ({ combinedDocumentBlocks,
1342
1364
  display: any;
1343
1365
  filters: any[];
1344
1366
  rollups: any[];
1367
+ rollupDisplayMap: Record<string, string>;
1345
1368
  }[];
1346
1369
  };
1347
1370
  target: {
@@ -1501,6 +1524,7 @@ declare const autoGenFilterConfigsFromTpl: ({ selectedTpls, allTpls, filterScope
1501
1524
  display: any;
1502
1525
  filters: any[];
1503
1526
  rollups: any[];
1527
+ rollupDisplayMap: Record<string, string>;
1504
1528
  }[];
1505
1529
  };
1506
1530
  target: {
@@ -285,9 +285,10 @@ declare const collectMarkIdsInOrder: (editorState: any) => string[];
285
285
  * }
286
286
  * ]
287
287
  */
288
- declare function getRollupPossibilities({ tagType, allTpls }: {
288
+ declare function getRollupPossibilities({ tagType, allTpls, includeSelfHead, }: {
289
289
  tagType: string;
290
290
  allTpls: any[];
291
+ includeSelfHead?: boolean;
291
292
  }): any[];
292
293
 
293
294
  declare namespace BASE_BULLMQ_CONFIG {
@@ -1241,6 +1242,27 @@ declare const processAuthorAndCommonFilters: (allTpls: any[], filterScopes: stri
1241
1242
 
1242
1243
  declare const _self_managed_getFixedAnnoTagBlock: (selectedTpls: any[], annotationTagsCount: any) => any[];
1243
1244
 
1245
+ /**
1246
+ * FOLLOWUP (consolidate the count->rollupPath rule into one source of truth):
1247
+ *
1248
+ * The rule "given annotationTagsCount, which self-managed anno rollupPaths exist"
1249
+ * (subThemes>0 -> ['tags','subThemes']; subThemes>0 && themes>0 -> ['tags','subThemes','themes'])
1250
+ * is currently duplicated in THREE places and will drift:
1251
+ * 1. here (_self_managed_getFixedAnnoRollupBlocks) -> filter blocks
1252
+ * 2. _self_managed_buildAnnoHierarchyConfig -> anno hierarchy filter UI
1253
+ * 3. okf-fe _extractAllAnnoBasedDimensionsAsOptions.js -> reports dimension dropdown
1254
+ *
1255
+ * (#3 was added to fix a crash: the reports widget previously discovered these rollups
1256
+ * from publishing-segment tpls via getRollupPossibilities, but self-managed taxonomy lives
1257
+ * on `collections`-segment tpls which isTplAnnotationEnabled excludes -> empty list ->
1258
+ * unguarded .find('subThemes') returned undefined -> crash. The FE now builds them from the
1259
+ * count, mirroring this function.)
1260
+ *
1261
+ * Extract a pure helper here in ootils:
1262
+ * export const getSelfManagedAnnoRollupPaths = (annotationTagsCount): string[][] => [...]
1263
+ * then have all three call sites consume it (each maps paths -> its own shape:
1264
+ * filter-blocks / hierarchy levels / dropdown options). One rule, three presentations.
1265
+ */
1244
1266
  declare const _self_managed_getFixedAnnoRollupBlocks: ({ selectedTpls, allTpls, annotationTagsCount }: {
1245
1267
  selectedTpls: any[];
1246
1268
  allTpls: any[];
@@ -1342,6 +1364,7 @@ declare const _self_managed_buildDocHierarchyConfig: ({ combinedDocumentBlocks,
1342
1364
  display: any;
1343
1365
  filters: any[];
1344
1366
  rollups: any[];
1367
+ rollupDisplayMap: Record<string, string>;
1345
1368
  }[];
1346
1369
  };
1347
1370
  target: {
@@ -1501,6 +1524,7 @@ declare const autoGenFilterConfigsFromTpl: ({ selectedTpls, allTpls, filterScope
1501
1524
  display: any;
1502
1525
  filters: any[];
1503
1526
  rollups: any[];
1527
+ rollupDisplayMap: Record<string, string>;
1504
1528
  }[];
1505
1529
  };
1506
1530
  target: {
package/dist/universal.js CHANGED
@@ -722,7 +722,8 @@ var collectMarkIdsInOrder = (editorState) => {
722
722
  var MAX_DEPTH_ROLLUP_POSSIBILITIES = 10;
723
723
  function getRollupPossibilities({
724
724
  tagType,
725
- allTpls
725
+ allTpls,
726
+ includeSelfHead = false
726
727
  }) {
727
728
  const thisTagTpl = allTpls.find((d) => d.kp_content_type === tagType);
728
729
  if (!thisTagTpl) return [];
@@ -754,7 +755,8 @@ function getRollupPossibilities({
754
755
  chains.push({
755
756
  rollupType: "tagType",
756
757
  tagTypeCollectionToRollup: tagType,
757
- rollupPath: [...visited, startTagType, nextTagType]
758
+ rollupPath: [...visited, startTagType, nextTagType],
759
+ filterDisplay: block.props?.shortLabel || block.props?.label
758
760
  });
759
761
  if (visited.length < maxDepth - 1) {
760
762
  const nextChains = findTagChains(
@@ -800,7 +802,8 @@ function getRollupPossibilities({
800
802
  const directChain = {
801
803
  rollupType: "tagType",
802
804
  tagTypeCollectionToRollup: tagType,
803
- rollupPath: [tagType, block.props.tagType]
805
+ rollupPath: [tagType, block.props.tagType],
806
+ filterDisplay: block.props?.shortLabel || block.props?.label
804
807
  };
805
808
  const deeperChains = findTagChains(
806
809
  block.props?.tagType,
@@ -809,7 +812,14 @@ function getRollupPossibilities({
809
812
  );
810
813
  return [directChain, ...deeperChains];
811
814
  });
812
- return [...singleLevelValuePathRollups, ...nestedTagRollups];
815
+ const selfHeadRollup = includeSelfHead && nestedTagRollups.length > 0 && singleLevelValuePathRollups.length === 0 ? [
816
+ {
817
+ rollupType: "tagType",
818
+ tagTypeCollectionToRollup: tagType,
819
+ rollupPath: [tagType]
820
+ }
821
+ ] : [];
822
+ return [...singleLevelValuePathRollups, ...selfHeadRollup, ...nestedTagRollups];
813
823
  }
814
824
 
815
825
  // src/bullmq/GLOBAL_BULLMQ_CONFIG.js
@@ -1714,6 +1724,7 @@ var buildFilterConfigurations = ({ groups, type, selectedTpls, allTpls, isRollup
1714
1724
  if (isTerminalValuePath) {
1715
1725
  return element.filterDisplay || lastItemInPath;
1716
1726
  }
1727
+ if (element.filterDisplay) return element.filterDisplay;
1717
1728
  const lastTag = rollupPath[rollupPath.length - 1];
1718
1729
  const lastTagTpl = allTpls.find(
1719
1730
  (tpl) => tpl.kp_content_type === lastTag
@@ -1980,7 +1991,7 @@ var extractAndOrganizeBlocks = (selectedTpls, allTpls, { smTagTypesConfig } = {}
1980
1991
  return {
1981
1992
  contentType: tpl.kp_content_type,
1982
1993
  blocks: allBlocks.filter((block) => block.valuePath.startsWith("tags.") && ["TagsInputSingle", "TagsInputMulti"].includes(block.comp)).flatMap((block) => {
1983
- const possibilities = getRollupPossibilities({ tagType: block.props.tagType, allTpls });
1994
+ const possibilities = getRollupPossibilities({ tagType: block.props.tagType, allTpls, includeSelfHead: true });
1984
1995
  return possibilities.map((p) => ({
1985
1996
  ...p,
1986
1997
  filterType: p.rollupPath ? "nestedRollupTagType" : "rollupValuePathType"
@@ -2313,13 +2324,16 @@ var buildTagTypeParentMap = (tagTypesInGroup, allTpls) => {
2313
2324
  const tagBlocks = blocks.filter(
2314
2325
  (block) => block.valuePath?.startsWith("tags.") && block.props?.tagType
2315
2326
  );
2327
+ const referencedTagTypes = /* @__PURE__ */ new Set();
2316
2328
  for (const block of tagBlocks) {
2317
2329
  const referencedTagType = block.props.tagType;
2318
2330
  if (tagTypeSet.has(referencedTagType) && referencedTagType !== tagType) {
2319
- parentMap.set(tagType, referencedTagType);
2320
- break;
2331
+ referencedTagTypes.add(referencedTagType);
2321
2332
  }
2322
2333
  }
2334
+ if (referencedTagTypes.size === 1) {
2335
+ parentMap.set(tagType, [...referencedTagTypes][0]);
2336
+ }
2323
2337
  }
2324
2338
  for (const child of parentMap.keys()) {
2325
2339
  const visited = /* @__PURE__ */ new Set();
@@ -2393,6 +2407,34 @@ var sortFiltersHierarchically = (filters) => {
2393
2407
  return sorted;
2394
2408
  };
2395
2409
 
2410
+ // src/utils/autoGenFilterConfigsFromTpl/utils/deduplicateRollupTagFilters.ts
2411
+ var deduplicateRollupTagFilters = (rollups) => {
2412
+ const tagTerminals = [];
2413
+ const nonTag = [];
2414
+ for (const r of rollups) {
2415
+ const rp = r.target?.rollupPath;
2416
+ if (rp?.length && !rp[rp.length - 1].startsWith("main.")) {
2417
+ tagTerminals.push(r);
2418
+ } else {
2419
+ nonTag.push({ ...r, isTagRollup: false });
2420
+ }
2421
+ }
2422
+ if (tagTerminals.length === 0) return nonTag;
2423
+ const byTerminal = /* @__PURE__ */ new Map();
2424
+ for (const r of tagTerminals) {
2425
+ const terminal = r.target.rollupPath[r.target.rollupPath.length - 1];
2426
+ const existing = byTerminal.get(terminal);
2427
+ if (!existing || r.target.rollupPath.length < existing.target.rollupPath.length) {
2428
+ byTerminal.set(terminal, r);
2429
+ }
2430
+ }
2431
+ const dedupedTags = [...byTerminal.values()].map((r) => ({
2432
+ ...r,
2433
+ isTagRollup: true
2434
+ }));
2435
+ return [...nonTag, ...dedupedTags];
2436
+ };
2437
+
2396
2438
  // src/utils/autoGenFilterConfigsFromTpl/utils/_self_managed_buildDocHierarchyConfig.ts
2397
2439
  var injectFilterOptionsBy = (filters) => filters.map(
2398
2440
  (f) => f.parentFilterId ? { ...f, source: { ...f.source, filterOptionsBy: { filterId: f.parentFilterId } } } : f
@@ -2499,24 +2541,34 @@ var _self_managed_buildDocHierarchyConfig = ({
2499
2541
  const dedupedFilters = filtersWithCommonFlag.filter(
2500
2542
  (f) => !rollupSourceKeys.has(JSON.stringify(f.source))
2501
2543
  );
2502
- const combined = [...dedupedFilters, ...rollupConfigs];
2544
+ const dedupedRollupConfigs = deduplicateRollupTagFilters(rollupConfigs);
2545
+ const combined = [...dedupedFilters, ...dedupedRollupConfigs];
2503
2546
  const combinedWithParents = injectFilterOptionsBy(
2504
2547
  sortFiltersHierarchically(
2505
2548
  attachParentFields(combined, allTpls)
2506
2549
  )
2507
2550
  );
2508
- const rollupFilterIds = new Set(rollupConfigs.map((r) => r.filterId));
2551
+ const rollupFilterIds = new Set(dedupedRollupConfigs.map((r) => r.filterId));
2509
2552
  const filtersWithParents = combinedWithParents.filter(
2510
2553
  (f) => !rollupFilterIds.has(f.filterId)
2511
2554
  );
2512
2555
  const rollupsWithParents = combinedWithParents.filter(
2513
2556
  (f) => rollupFilterIds.has(f.filterId)
2514
2557
  );
2558
+ const rollupContentTypes = new Set(
2559
+ rollupsWithParents.filter((r) => r.target?.rollupPath).flatMap((r) => r.target.rollupPath)
2560
+ );
2561
+ const rollupDisplayMap = {};
2562
+ for (const ct of rollupContentTypes) {
2563
+ const t = allTpls.find((tp) => tp.kp_content_type === ct);
2564
+ if (t?.general?.content?.title) rollupDisplayMap[ct] = t.general.content.title;
2565
+ }
2515
2566
  return {
2516
2567
  contentType: tpl.kp_content_type,
2517
2568
  display: tplData?.general?.content?.title || tpl.kp_content_type,
2518
2569
  filters: filtersWithParents,
2519
- rollups: rollupsWithParents
2570
+ rollups: rollupsWithParents,
2571
+ rollupDisplayMap
2520
2572
  };
2521
2573
  });
2522
2574
  const filteredPerDataset = perDataset.filter(