@platforma-sdk/model 1.65.9 → 1.66.2

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.
Files changed (121) hide show
  1. package/dist/block_model.cjs +8 -11
  2. package/dist/block_model.cjs.map +1 -1
  3. package/dist/block_model.d.ts.map +1 -1
  4. package/dist/block_model.js +8 -10
  5. package/dist/block_model.js.map +1 -1
  6. package/dist/columns/column_collection_builder.cjs +61 -74
  7. package/dist/columns/column_collection_builder.cjs.map +1 -1
  8. package/dist/columns/column_collection_builder.d.ts +16 -22
  9. package/dist/columns/column_collection_builder.d.ts.map +1 -1
  10. package/dist/columns/column_collection_builder.js +62 -75
  11. package/dist/columns/column_collection_builder.js.map +1 -1
  12. package/dist/columns/column_selector.cjs.map +1 -1
  13. package/dist/columns/column_selector.d.ts +1 -1
  14. package/dist/columns/column_selector.js.map +1 -1
  15. package/dist/columns/column_snapshot.cjs.map +1 -1
  16. package/dist/columns/column_snapshot.d.ts +4 -4
  17. package/dist/columns/column_snapshot.d.ts.map +1 -1
  18. package/dist/columns/column_snapshot.js.map +1 -1
  19. package/dist/columns/ctx_column_sources.cjs.map +1 -1
  20. package/dist/columns/ctx_column_sources.d.ts +1 -1
  21. package/dist/columns/ctx_column_sources.d.ts.map +1 -1
  22. package/dist/columns/ctx_column_sources.js.map +1 -1
  23. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.cjs +2 -2
  24. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.cjs.map +1 -1
  25. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.js +2 -2
  26. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.js.map +1 -1
  27. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.cjs +17 -18
  28. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.cjs.map +1 -1
  29. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.js +17 -18
  30. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.js.map +1 -1
  31. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs +99 -91
  32. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs.map +1 -1
  33. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts +16 -16
  34. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts.map +1 -1
  35. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js +102 -94
  36. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js.map +1 -1
  37. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs +32 -23
  38. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs.map +1 -1
  39. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.d.ts +5 -5
  40. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.d.ts.map +1 -1
  41. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js +33 -24
  42. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js.map +1 -1
  43. package/dist/components/PlDataTable/createPlDataTable/index.cjs.map +1 -1
  44. package/dist/components/PlDataTable/createPlDataTable/index.d.ts +2 -3
  45. package/dist/components/PlDataTable/createPlDataTable/index.d.ts.map +1 -1
  46. package/dist/components/PlDataTable/createPlDataTable/index.js.map +1 -1
  47. package/dist/components/PlDataTable/createPlDataTable/utils.cjs +133 -16
  48. package/dist/components/PlDataTable/createPlDataTable/utils.cjs.map +1 -1
  49. package/dist/components/PlDataTable/createPlDataTable/utils.d.ts +8 -6
  50. package/dist/components/PlDataTable/createPlDataTable/utils.d.ts.map +1 -1
  51. package/dist/components/PlDataTable/createPlDataTable/utils.js +130 -17
  52. package/dist/components/PlDataTable/createPlDataTable/utils.js.map +1 -1
  53. package/dist/components/PlDataTable/labels.cjs +1 -2
  54. package/dist/components/PlDataTable/labels.cjs.map +1 -1
  55. package/dist/components/PlDataTable/labels.js +1 -2
  56. package/dist/components/PlDataTable/labels.js.map +1 -1
  57. package/dist/filters/distill.cjs +73 -30
  58. package/dist/filters/distill.cjs.map +1 -1
  59. package/dist/filters/distill.d.ts.map +1 -1
  60. package/dist/filters/distill.js +73 -30
  61. package/dist/filters/distill.js.map +1 -1
  62. package/dist/index.cjs +19 -15
  63. package/dist/index.d.ts +4 -2
  64. package/dist/index.js +6 -4
  65. package/dist/labels/derive_distinct_tooltips.cjs +85 -0
  66. package/dist/labels/derive_distinct_tooltips.cjs.map +1 -0
  67. package/dist/labels/derive_distinct_tooltips.d.ts +17 -0
  68. package/dist/labels/derive_distinct_tooltips.d.ts.map +1 -0
  69. package/dist/labels/derive_distinct_tooltips.js +84 -0
  70. package/dist/labels/derive_distinct_tooltips.js.map +1 -0
  71. package/dist/labels/index.cjs +1 -0
  72. package/dist/labels/index.d.ts +2 -1
  73. package/dist/labels/index.js +1 -0
  74. package/dist/package.cjs +1 -1
  75. package/dist/package.js +1 -1
  76. package/dist/render/api.cjs +8 -13
  77. package/dist/render/api.cjs.map +1 -1
  78. package/dist/render/api.d.ts +8 -11
  79. package/dist/render/api.d.ts.map +1 -1
  80. package/dist/render/api.js +8 -13
  81. package/dist/render/api.js.map +1 -1
  82. package/dist/services/get_services.cjs +19 -0
  83. package/dist/services/get_services.cjs.map +1 -0
  84. package/dist/services/get_services.d.ts +7 -0
  85. package/dist/services/get_services.d.ts.map +1 -0
  86. package/dist/services/get_services.js +19 -0
  87. package/dist/services/get_services.js.map +1 -0
  88. package/dist/services/index.cjs +1 -0
  89. package/dist/services/index.d.ts +2 -1
  90. package/dist/services/index.js +1 -0
  91. package/dist/services/service_bridge.cjs +4 -4
  92. package/dist/services/service_bridge.cjs.map +1 -1
  93. package/dist/services/service_bridge.d.ts +4 -4
  94. package/dist/services/service_bridge.d.ts.map +1 -1
  95. package/dist/services/service_bridge.js +4 -4
  96. package/dist/services/service_bridge.js.map +1 -1
  97. package/package.json +6 -6
  98. package/src/block_model.ts +8 -11
  99. package/src/columns/column_collection_builder.test.ts +75 -30
  100. package/src/columns/column_collection_builder.ts +96 -133
  101. package/src/columns/column_selector.ts +1 -1
  102. package/src/columns/column_snapshot.ts +7 -4
  103. package/src/columns/ctx_column_sources.ts +1 -3
  104. package/src/components/PFrameForGraphs.test.ts +4 -4
  105. package/src/components/PlDataTable/createPlDataTable/createPTableDefV2.ts +2 -2
  106. package/src/components/PlDataTable/createPlDataTable/createPTableDefV3.ts +44 -21
  107. package/src/components/PlDataTable/createPlDataTable/createPlDataTableV3.ts +202 -218
  108. package/src/components/PlDataTable/createPlDataTable/discoverColumns.ts +69 -56
  109. package/src/components/PlDataTable/createPlDataTable/index.ts +6 -7
  110. package/src/components/PlDataTable/createPlDataTable/utils.test.ts +97 -1
  111. package/src/components/PlDataTable/createPlDataTable/utils.ts +190 -35
  112. package/src/components/PlDataTable/labels.ts +3 -7
  113. package/src/filters/distill.test.ts +91 -0
  114. package/src/filters/distill.ts +102 -46
  115. package/src/labels/derive_distinct_tooltips.test.ts +233 -0
  116. package/src/labels/derive_distinct_tooltips.ts +130 -0
  117. package/src/labels/index.ts +1 -0
  118. package/src/render/api.ts +15 -50
  119. package/src/services/get_services.ts +28 -0
  120. package/src/services/index.ts +1 -0
  121. package/src/services/service_bridge.ts +5 -5
@@ -1,4 +1,8 @@
1
1
  import { deriveDistinctLabels } from "../../../labels/derive_distinct_labels.js";
2
+ import { ArrayColumnProvider } from "../../../columns/column_snapshot_provider.js";
3
+ import { ColumnCollectionBuilder } from "../../../columns/column_collection_builder.js";
4
+ import "../../../columns/index.js";
5
+ import { deriveDistinctTooltips } from "../../../labels/derive_distinct_tooltips.js";
2
6
  import { Annotation, canonicalizeAxisId, canonicalizeJson, getAxisId, readAnnotation, readAnnotationJson } from "@milaboratories/pl-model-common";
3
7
  import { isNil } from "es-toolkit";
4
8
  //#region src/components/PlDataTable/createPlDataTable/utils.ts
@@ -10,20 +14,65 @@ function isColumnHidden(spec) {
10
14
  function isColumnOptional(spec) {
11
15
  return readAnnotation(spec, Annotation.Table.Visibility) === "optional";
12
16
  }
13
- /** Get effective visibility for a column, considering display config rules first, then annotations. */
14
- function getEffectiveVisibility(spec, displayConfig) {
15
- if (displayConfig?.visibility) {
16
- for (const rule of displayConfig.visibility) if (rule.match(spec)) return rule.visibility;
17
+ /** Get effective visibility for a column. Rule map lookup first, then annotation fallback. */
18
+ function getEffectiveVisibility(col, visibilityByColId) {
19
+ const rule = visibilityByColId?.get(col.id);
20
+ if (rule !== void 0) return rule.visibility;
21
+ if (isColumnHidden(col.spec)) return "hidden";
22
+ if (isColumnOptional(col.spec)) return "optional";
23
+ }
24
+ /** Get ordering priority for a column. Rule map lookup first, then annotation fallback. */
25
+ function getOrderPriority(col, orderByColId) {
26
+ const rule = orderByColId?.get(col.id);
27
+ if (rule !== void 0) return rule.priority;
28
+ return readAnnotationJson(col.spec, Annotation.Table.OrderPriority);
29
+ }
30
+ /**
31
+ * Evaluate display rules against a set of columns and return a map of `colId → winning rule`
32
+ * (first-match-wins, preserving original rule order).
33
+ *
34
+ * Predicate-based rules (`ColumnMatcher`) are evaluated directly on the spec.
35
+ * Selector-based rules (`ColumnSelector`) are matched via `PFrameSpecDriver.discoverColumns`
36
+ * using the same engine as `ColumnCollection.findColumns` — no client-side matcher.
37
+ */
38
+ function evaluateRules(rules, columns, pframeSpec) {
39
+ const result = /* @__PURE__ */ new Map();
40
+ if (rules.length === 0 || columns.length === 0) return result;
41
+ const hasSelectorRules = rules.some((rule) => typeof rule.match !== "function");
42
+ const selectorHitsByRule = /* @__PURE__ */ new Map();
43
+ if (hasSelectorRules) {
44
+ const pColumns = dedupeById(columns).map((c) => ({
45
+ id: c.id,
46
+ spec: c.spec,
47
+ data: void 0
48
+ }));
49
+ const collection = new ColumnCollectionBuilder(pframeSpec).addSource(new ArrayColumnProvider(pColumns)).build();
50
+ if (collection === void 0) return result;
51
+ try {
52
+ for (const rule of rules) {
53
+ if (typeof rule.match === "function") continue;
54
+ const hits = collection.findColumns({ include: rule.match });
55
+ selectorHitsByRule.set(rule, new Set(hits.map((h) => h.id)));
56
+ }
57
+ } finally {
58
+ collection.dispose();
59
+ }
60
+ }
61
+ for (const col of columns) for (const rule of rules) if (typeof rule.match === "function" ? rule.match(col.spec) : selectorHitsByRule.get(rule)?.has(col.id) ?? false) {
62
+ result.set(col.id, rule);
63
+ break;
17
64
  }
18
- if (isColumnHidden(spec)) return "hidden";
19
- if (isColumnOptional(spec)) return "optional";
65
+ return result;
20
66
  }
21
- /** Get ordering priority for a column. Display config rules first, then annotation fallback. */
22
- function getOrderPriority(spec, displayConfig) {
23
- if (displayConfig?.ordering) {
24
- for (const rule of displayConfig.ordering) if (rule.match(spec)) return rule.priority;
67
+ function dedupeById(columns) {
68
+ const seen = /* @__PURE__ */ new Set();
69
+ const result = [];
70
+ for (const col of columns) {
71
+ if (seen.has(col.id)) continue;
72
+ seen.add(col.id);
73
+ result.push(col);
25
74
  }
26
- return readAnnotationJson(spec, Annotation.Table.OrderPriority);
75
+ return result;
27
76
  }
28
77
  /**
29
78
  * Writes derived labels into column and axis annotations.
@@ -59,16 +108,16 @@ function withLabelAnnotations(derivedLabels, columns) {
59
108
  });
60
109
  }
61
110
  /**
62
- * Writes effective display properties (OrderPriority, Visibility) from ColumnDisplayOptions
111
+ * Writes effective display properties (OrderPriority, Visibility) from precomputed rule maps
63
112
  * into column annotations. Returns new column objects — originals are not mutated.
64
113
  */
65
- function withTableVisualAnnotations(displayOptions, columns) {
66
- if (displayOptions === void 0) return columns;
114
+ function withTableVisualAnnotations(visibilityByColId, orderByColId, columns) {
115
+ if (visibilityByColId === void 0 && orderByColId === void 0) return columns;
67
116
  return columns.map((col) => {
68
117
  const annotations = { ...col.spec.annotations };
69
- const visibility = getEffectiveVisibility(col.spec, displayOptions);
118
+ const visibility = getEffectiveVisibility(col, visibilityByColId);
70
119
  if (!isNil(visibility)) annotations[Annotation.Table.Visibility] = visibility;
71
- const orderPriority = getOrderPriority(col.spec, displayOptions);
120
+ const orderPriority = getOrderPriority(col, orderByColId);
72
121
  if (!isNil(orderPriority)) annotations[Annotation.Table.OrderPriority] = String(orderPriority);
73
122
  return {
74
123
  ...col,
@@ -79,6 +128,42 @@ function withTableVisualAnnotations(displayOptions, columns) {
79
128
  };
80
129
  });
81
130
  }
131
+ /**
132
+ * Writes derived info annotations into column annotations.
133
+ * Columns without an info entry are passed through unchanged.
134
+ */
135
+ function withInfoAnnotations(infoById, columns) {
136
+ if (isNil(infoById)) return columns;
137
+ return columns.map((col) => {
138
+ const info = infoById[col.id];
139
+ if (isNil(info)) return col;
140
+ return {
141
+ ...col,
142
+ spec: {
143
+ ...col.spec,
144
+ annotations: {
145
+ ...col.spec.annotations,
146
+ [Annotation.Table.Info]: info
147
+ }
148
+ }
149
+ };
150
+ });
151
+ }
152
+ function withHidenAxesAnnotations(columns) {
153
+ return columns.map((col) => ({
154
+ ...col,
155
+ spec: {
156
+ ...col.spec,
157
+ axesSpec: col.spec.axesSpec.map((axis) => ({
158
+ ...axis,
159
+ annotations: {
160
+ ...axis.annotations,
161
+ [Annotation.Table.Visibility]: "hidden"
162
+ }
163
+ }))
164
+ }
165
+ }));
166
+ }
82
167
  /** Derive labels for all table elements: columns via deriveDistinctLabels, axes from label columns. */
83
168
  function deriveAllLabels(options) {
84
169
  const { columns, labelColumns, deriveLabelsOptions } = options;
@@ -96,7 +181,35 @@ function deriveAxisLabels(columns, labelColumns) {
96
181
  for (const axisKey of axisSpecMap.keys()) result[axisKey] = readAnnotation(labelColumns.find((lc) => canonicalizeJson(getAxisId(lc.spec.axesSpec[0])) === axisKey)?.spec ?? axisSpecMap.get(axisKey) ?? {}, Annotation.Label)?.trim() ?? "Unlabeled";
97
182
  return result;
98
183
  }
184
+ /** Derive origin tooltips for columns whose qualifications or linker path carry info. */
185
+ function deriveAllTooltips(options) {
186
+ const { columns } = options;
187
+ const variantCountByOriginal = columns.reduce((acc, c) => {
188
+ return acc.set(c.originalId, (acc.get(c.originalId) ?? 0) + 1);
189
+ }, /* @__PURE__ */ new Map());
190
+ const { entries } = columns.reduce(({ entries, variantSeen }, c) => {
191
+ const variantCount = variantCountByOriginal.get(c.originalId);
192
+ const variantIndex = (variantSeen.set(c.originalId, (variantSeen.get(c.originalId) ?? 0) + 1), variantSeen.get(c.originalId));
193
+ entries.push({
194
+ spec: c.spec,
195
+ linkerPath: c.linkerPath,
196
+ qualifications: c.qualifications,
197
+ distinctiveQualifications: c.distinctiveQualifications,
198
+ variantIndex,
199
+ variantCount
200
+ });
201
+ return {
202
+ entries,
203
+ variantSeen
204
+ };
205
+ }, {
206
+ entries: [],
207
+ variantSeen: /* @__PURE__ */ new Map()
208
+ });
209
+ const tooltips = deriveDistinctTooltips(entries);
210
+ return Object.fromEntries(tooltips.flatMap((t, i) => isNil(t) ? [] : [[columns[i].id, t]]));
211
+ }
99
212
  //#endregion
100
- export { deriveAllLabels, getEffectiveVisibility, getOrderPriority, isColumnHidden, isColumnOptional, withLabelAnnotations, withTableVisualAnnotations };
213
+ export { deriveAllLabels, deriveAllTooltips, evaluateRules, getEffectiveVisibility, getOrderPriority, isColumnHidden, isColumnOptional, withHidenAxesAnnotations, withInfoAnnotations, withLabelAnnotations, withTableVisualAnnotations };
101
214
 
102
215
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../../../../src/components/PlDataTable/createPlDataTable/utils.ts"],"sourcesContent":["import {\n type PColumn,\n type PColumnSpec,\n type PObjectId,\n Annotation,\n canonicalizeAxisId,\n canonicalizeJson,\n getAxisId,\n readAnnotation,\n readAnnotationJson,\n} from \"@milaboratories/pl-model-common\";\nimport {\n deriveDistinctLabels,\n type DeriveLabelsOptions,\n} from \"../../../labels/derive_distinct_labels\";\nimport type { ColumnsDisplayOptions } from \"./createPlDataTableV3\";\nimport { isNil } from \"es-toolkit\";\n\n/** Check if column should be omitted from the table */\nexport function isColumnHidden(spec: { annotations?: Annotation }): boolean {\n return readAnnotation(spec, Annotation.Table.Visibility) === \"hidden\";\n}\n\n/** Check if column is hidden by default */\nexport function isColumnOptional(spec: { annotations?: Annotation }): boolean {\n return readAnnotation(spec, Annotation.Table.Visibility) === \"optional\";\n}\n\n/** Get effective visibility for a column, considering display config rules first, then annotations. */\nexport function getEffectiveVisibility(\n spec: PColumnSpec,\n displayConfig?: ColumnsDisplayOptions,\n): undefined | \"default\" | \"optional\" | \"hidden\" {\n if (displayConfig?.visibility) {\n for (const rule of displayConfig.visibility) {\n if (rule.match(spec)) {\n return rule.visibility;\n }\n }\n }\n if (isColumnHidden(spec)) return \"hidden\";\n if (isColumnOptional(spec)) return \"optional\";\n return undefined;\n}\n\n/** Get ordering priority for a column. Display config rules first, then annotation fallback. */\nexport function getOrderPriority(\n spec: PColumnSpec,\n displayConfig?: ColumnsDisplayOptions,\n): undefined | number {\n if (displayConfig?.ordering) {\n for (const rule of displayConfig.ordering) {\n if (rule.match(spec)) {\n return rule.priority;\n }\n }\n }\n return readAnnotationJson(spec, Annotation.Table.OrderPriority);\n}\n\n/**\n * Writes derived labels into column and axis annotations.\n * Returns new column objects with modified specs — original columns are not mutated.\n *\n * For each column: writes derived label into Annotation.Label (if present in derivedLabels).\n * For each axis in column specs: writes derived axis label into AxisSpec annotations.\n */\nexport function withLabelAnnotations<Data>(\n derivedLabels: undefined | Record<string, string>,\n columns: PColumn<Data>[],\n): PColumn<Data>[] {\n if (derivedLabels === undefined) return columns;\n return columns.map((col) => {\n const colLabel = derivedLabels[col.id];\n return {\n ...col,\n spec: {\n ...col.spec,\n ...(isNil(colLabel)\n ? {}\n : { annotations: { ...col.spec.annotations, [Annotation.Label]: colLabel } }),\n axesSpec: col.spec.axesSpec.map((axis) => {\n const label = derivedLabels[canonicalizeAxisId(axis)];\n return isNil(label)\n ? axis\n : { ...axis, annotations: { ...axis.annotations, [Annotation.Label]: label } };\n }),\n },\n };\n });\n}\n\n/**\n * Writes effective display properties (OrderPriority, Visibility) from ColumnDisplayOptions\n * into column annotations. Returns new column objects — originals are not mutated.\n */\nexport function withTableVisualAnnotations<Data>(\n displayOptions: undefined | ColumnsDisplayOptions,\n columns: PColumn<Data>[],\n): PColumn<Data>[] {\n if (displayOptions === undefined) return columns;\n return columns.map((col) => {\n const annotations = { ...col.spec.annotations };\n\n const visibility = getEffectiveVisibility(col.spec, displayOptions);\n if (!isNil(visibility)) annotations[Annotation.Table.Visibility] = visibility;\n\n const orderPriority = getOrderPriority(col.spec, displayOptions);\n if (!isNil(orderPriority)) annotations[Annotation.Table.OrderPriority] = String(orderPriority);\n\n return {\n ...col,\n spec: {\n ...col.spec,\n annotations: annotations,\n },\n };\n });\n}\n\n/** Column shape required by label derivation. */\nexport type LabelableColumn = {\n readonly id: PObjectId;\n readonly spec: PColumnSpec;\n readonly linkerPath?: { spec: PColumnSpec }[];\n};\n\n/** Derive labels for all table elements: columns via deriveDistinctLabels, axes from label columns. */\nexport function deriveAllLabels(options: {\n columns: LabelableColumn[];\n labelColumns: { readonly spec: PColumnSpec }[];\n deriveLabelsOptions?: DeriveLabelsOptions;\n}): Record<string, string> {\n const { columns, labelColumns, deriveLabelsOptions } = options;\n const axisLabels = deriveAxisLabels(columns, labelColumns);\n const columnLabels = deriveDistinctLabels(columns, deriveLabelsOptions).reduce(\n (acc, label, index) => ((acc[columns[index].id] = label), acc),\n {} as Record<string, string>,\n );\n return { ...axisLabels, ...columnLabels };\n}\n\n/** Derive axis labels from matching label columns, falling back to axis spec annotations. */\nfunction deriveAxisLabels(\n columns: { readonly spec: PColumnSpec }[],\n labelColumns: { readonly spec: PColumnSpec }[],\n): Record<string, string> {\n const axisSpecMap = new Map(\n columns.flatMap((c) => c.spec.axesSpec).map((a) => [canonicalizeJson(getAxisId(a)), a]),\n );\n const result: Record<string, string> = {};\n for (const axisKey of axisSpecMap.keys()) {\n const labelCol = labelColumns.find(\n (lc) => canonicalizeJson(getAxisId(lc.spec.axesSpec[0])) === axisKey,\n );\n const source = labelCol?.spec ?? axisSpecMap.get(axisKey);\n result[axisKey] = readAnnotation(source ?? {}, Annotation.Label)?.trim() ?? \"Unlabeled\";\n }\n return result;\n}\n"],"mappings":";;;;;AAmBA,SAAgB,eAAe,MAA6C;AAC1E,QAAO,eAAe,MAAM,WAAW,MAAM,WAAW,KAAK;;;AAI/D,SAAgB,iBAAiB,MAA6C;AAC5E,QAAO,eAAe,MAAM,WAAW,MAAM,WAAW,KAAK;;;AAI/D,SAAgB,uBACd,MACA,eAC+C;AAC/C,KAAI,eAAe;OACZ,MAAM,QAAQ,cAAc,WAC/B,KAAI,KAAK,MAAM,KAAK,CAClB,QAAO,KAAK;;AAIlB,KAAI,eAAe,KAAK,CAAE,QAAO;AACjC,KAAI,iBAAiB,KAAK,CAAE,QAAO;;;AAKrC,SAAgB,iBACd,MACA,eACoB;AACpB,KAAI,eAAe;OACZ,MAAM,QAAQ,cAAc,SAC/B,KAAI,KAAK,MAAM,KAAK,CAClB,QAAO,KAAK;;AAIlB,QAAO,mBAAmB,MAAM,WAAW,MAAM,cAAc;;;;;;;;;AAUjE,SAAgB,qBACd,eACA,SACiB;AACjB,KAAI,kBAAkB,KAAA,EAAW,QAAO;AACxC,QAAO,QAAQ,KAAK,QAAQ;EAC1B,MAAM,WAAW,cAAc,IAAI;AACnC,SAAO;GACL,GAAG;GACH,MAAM;IACJ,GAAG,IAAI;IACP,GAAI,MAAM,SAAS,GACf,EAAE,GACF,EAAE,aAAa;KAAE,GAAG,IAAI,KAAK;MAAc,WAAW,QAAQ;KAAU,EAAE;IAC9E,UAAU,IAAI,KAAK,SAAS,KAAK,SAAS;KACxC,MAAM,QAAQ,cAAc,mBAAmB,KAAK;AACpD,YAAO,MAAM,MAAM,GACf,OACA;MAAE,GAAG;MAAM,aAAa;OAAE,GAAG,KAAK;QAAc,WAAW,QAAQ;OAAO;MAAE;MAChF;IACH;GACF;GACD;;;;;;AAOJ,SAAgB,2BACd,gBACA,SACiB;AACjB,KAAI,mBAAmB,KAAA,EAAW,QAAO;AACzC,QAAO,QAAQ,KAAK,QAAQ;EAC1B,MAAM,cAAc,EAAE,GAAG,IAAI,KAAK,aAAa;EAE/C,MAAM,aAAa,uBAAuB,IAAI,MAAM,eAAe;AACnE,MAAI,CAAC,MAAM,WAAW,CAAE,aAAY,WAAW,MAAM,cAAc;EAEnE,MAAM,gBAAgB,iBAAiB,IAAI,MAAM,eAAe;AAChE,MAAI,CAAC,MAAM,cAAc,CAAE,aAAY,WAAW,MAAM,iBAAiB,OAAO,cAAc;AAE9F,SAAO;GACL,GAAG;GACH,MAAM;IACJ,GAAG,IAAI;IACM;IACd;GACF;GACD;;;AAWJ,SAAgB,gBAAgB,SAIL;CACzB,MAAM,EAAE,SAAS,cAAc,wBAAwB;CACvD,MAAM,aAAa,iBAAiB,SAAS,aAAa;CAC1D,MAAM,eAAe,qBAAqB,SAAS,oBAAoB,CAAC,QACrE,KAAK,OAAO,WAAY,IAAI,QAAQ,OAAO,MAAM,OAAQ,MAC1D,EAAE,CACH;AACD,QAAO;EAAE,GAAG;EAAY,GAAG;EAAc;;;AAI3C,SAAS,iBACP,SACA,cACwB;CACxB,MAAM,cAAc,IAAI,IACtB,QAAQ,SAAS,MAAM,EAAE,KAAK,SAAS,CAAC,KAAK,MAAM,CAAC,iBAAiB,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC,CACxF;CACD,MAAM,SAAiC,EAAE;AACzC,MAAK,MAAM,WAAW,YAAY,MAAM,CAKtC,QAAO,WAAW,eAJD,aAAa,MAC3B,OAAO,iBAAiB,UAAU,GAAG,KAAK,SAAS,GAAG,CAAC,KAAK,QAC9D,EACwB,QAAQ,YAAY,IAAI,QAAQ,IACd,EAAE,EAAE,WAAW,MAAM,EAAE,MAAM,IAAI;AAE9E,QAAO"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../../../src/components/PlDataTable/createPlDataTable/utils.ts"],"sourcesContent":["import {\n type PColumn,\n type PColumnSpec,\n type PFrameSpecDriver,\n type PObjectId,\n Annotation,\n canonicalizeAxisId,\n canonicalizeJson,\n DiscoveredPColumnId,\n getAxisId,\n readAnnotation,\n readAnnotationJson,\n} from \"@milaboratories/pl-model-common\";\nimport {\n deriveDistinctLabels,\n type DeriveLabelsOptions,\n} from \"../../../labels/derive_distinct_labels\";\nimport {\n deriveDistinctTooltips,\n type TooltipEntry,\n} from \"../../../labels/derive_distinct_tooltips\";\nimport type { MatchQualifications, MatchVariant } from \"../../../columns\";\nimport type { ColumnMatcher, ColumnOrderRule, ColumnVisibilityRule } from \"./createPlDataTableV3\";\nimport type { ColumnSelector } from \"../../../columns\";\nimport { ArrayColumnProvider, ColumnCollectionBuilder } from \"../../../columns\";\nimport { isNil } from \"es-toolkit\";\n\n/** Check if column should be omitted from the table */\nexport function isColumnHidden(spec: { annotations?: Annotation }): boolean {\n return readAnnotation(spec, Annotation.Table.Visibility) === \"hidden\";\n}\n\n/** Check if column is hidden by default */\nexport function isColumnOptional(spec: { annotations?: Annotation }): boolean {\n return readAnnotation(spec, Annotation.Table.Visibility) === \"optional\";\n}\n\n/** Column shape consumed by rule evaluation. */\nexport type RuleColumn = Pick<PColumn<PObjectId>, \"id\" | \"spec\">;\n\n/** Get effective visibility for a column. Rule map lookup first, then annotation fallback. */\nexport function getEffectiveVisibility(\n col: RuleColumn,\n visibilityByColId?: Map<PObjectId, ColumnVisibilityRule>,\n): undefined | \"default\" | \"optional\" | \"hidden\" {\n const rule = visibilityByColId?.get(col.id);\n if (rule !== undefined) return rule.visibility;\n if (isColumnHidden(col.spec)) return \"hidden\";\n if (isColumnOptional(col.spec)) return \"optional\";\n return undefined;\n}\n\n/** Get ordering priority for a column. Rule map lookup first, then annotation fallback. */\nexport function getOrderPriority(\n col: RuleColumn,\n orderByColId?: Map<PObjectId, ColumnOrderRule>,\n): undefined | number {\n const rule = orderByColId?.get(col.id);\n if (rule !== undefined) return rule.priority;\n return readAnnotationJson(col.spec, Annotation.Table.OrderPriority);\n}\n\n/**\n * Evaluate display rules against a set of columns and return a map of `colId → winning rule`\n * (first-match-wins, preserving original rule order).\n *\n * Predicate-based rules (`ColumnMatcher`) are evaluated directly on the spec.\n * Selector-based rules (`ColumnSelector`) are matched via `PFrameSpecDriver.discoverColumns`\n * using the same engine as `ColumnCollection.findColumns` — no client-side matcher.\n */\nexport function evaluateRules<R extends { match: ColumnMatcher | ColumnSelector }>(\n rules: R[],\n columns: RuleColumn[],\n pframeSpec: PFrameSpecDriver,\n): Map<PObjectId, R> {\n const result = new Map<PObjectId, R>();\n if (rules.length === 0 || columns.length === 0) return result;\n\n const hasSelectorRules = rules.some((rule) => typeof rule.match !== \"function\");\n const selectorHitsByRule = new Map<R, Set<PObjectId>>();\n\n if (hasSelectorRules) {\n const dedupedColumns = dedupeById(columns);\n const pColumns = dedupedColumns.map((c) => ({ id: c.id, spec: c.spec, data: undefined }));\n const collection = new ColumnCollectionBuilder(pframeSpec)\n .addSource(new ArrayColumnProvider(pColumns))\n .build();\n if (collection === undefined) return result;\n\n try {\n for (const rule of rules) {\n if (typeof rule.match === \"function\") continue;\n const hits = collection.findColumns({ include: rule.match });\n selectorHitsByRule.set(rule, new Set(hits.map((h) => h.id)));\n }\n } finally {\n collection.dispose();\n }\n }\n\n for (const col of columns) {\n for (const rule of rules) {\n const matches =\n typeof rule.match === \"function\"\n ? rule.match(col.spec)\n : (selectorHitsByRule.get(rule)?.has(col.id) ?? false);\n if (matches) {\n result.set(col.id, rule);\n break;\n }\n }\n }\n return result;\n}\n\nfunction dedupeById(columns: RuleColumn[]): RuleColumn[] {\n const seen = new Set<PObjectId>();\n const result: RuleColumn[] = [];\n for (const col of columns) {\n if (seen.has(col.id)) continue;\n seen.add(col.id);\n result.push(col);\n }\n return result;\n}\n\n/**\n * Writes derived labels into column and axis annotations.\n * Returns new column objects with modified specs — original columns are not mutated.\n *\n * For each column: writes derived label into Annotation.Label (if present in derivedLabels).\n * For each axis in column specs: writes derived axis label into AxisSpec annotations.\n */\nexport function withLabelAnnotations<\n T extends { readonly id: PObjectId; readonly spec: PColumnSpec },\n>(derivedLabels: undefined | Record<string, string>, columns: T[]): T[] {\n if (derivedLabels === undefined) return columns;\n return columns.map((col) => {\n const colLabel = derivedLabels[col.id];\n return {\n ...col,\n spec: {\n ...col.spec,\n ...(isNil(colLabel)\n ? {}\n : { annotations: { ...col.spec.annotations, [Annotation.Label]: colLabel } }),\n axesSpec: col.spec.axesSpec.map((axis) => {\n const label = derivedLabels[canonicalizeAxisId(axis)];\n return isNil(label)\n ? axis\n : { ...axis, annotations: { ...axis.annotations, [Annotation.Label]: label } };\n }),\n },\n } as T;\n });\n}\n\n/**\n * Writes effective display properties (OrderPriority, Visibility) from precomputed rule maps\n * into column annotations. Returns new column objects — originals are not mutated.\n */\nexport function withTableVisualAnnotations<\n T extends { readonly id: PObjectId; readonly spec: PColumnSpec },\n>(\n visibilityByColId: undefined | Map<PObjectId, ColumnVisibilityRule>,\n orderByColId: undefined | Map<PObjectId, ColumnOrderRule>,\n columns: T[],\n): T[] {\n if (visibilityByColId === undefined && orderByColId === undefined) return columns;\n return columns.map((col) => {\n const annotations = { ...col.spec.annotations };\n\n const visibility = getEffectiveVisibility(col, visibilityByColId);\n if (!isNil(visibility)) annotations[Annotation.Table.Visibility] = visibility;\n\n const orderPriority = getOrderPriority(col, orderByColId);\n if (!isNil(orderPriority)) annotations[Annotation.Table.OrderPriority] = String(orderPriority);\n\n return {\n ...col,\n spec: {\n ...col.spec,\n annotations: annotations,\n },\n } as T;\n });\n}\n\n/**\n * Writes derived info annotations into column annotations.\n * Columns without an info entry are passed through unchanged.\n */\nexport function withInfoAnnotations<\n T extends { readonly id: PObjectId; readonly spec: PColumnSpec },\n>(infoById: undefined | Record<string, string>, columns: T[]): T[] {\n if (isNil(infoById)) return columns;\n return columns.map((col) => {\n const info = infoById[col.id];\n if (isNil(info)) return col;\n return {\n ...col,\n spec: {\n ...col.spec,\n annotations: { ...col.spec.annotations, [Annotation.Table.Info]: info },\n },\n } as T;\n });\n}\n\nexport function withHidenAxesAnnotations<T extends { readonly spec: PColumnSpec }>(\n columns: T[],\n): T[] {\n return columns.map(\n (col) =>\n ({\n ...col,\n spec: {\n ...col.spec,\n axesSpec: col.spec.axesSpec.map((axis) => ({\n ...axis,\n annotations: { ...axis.annotations, [Annotation.Table.Visibility]: \"hidden\" },\n })),\n },\n }) as T,\n );\n}\n\n/** Column shape required by label derivation. */\nexport type LabelableColumn = {\n readonly id: PObjectId;\n readonly spec: PColumnSpec;\n readonly linkerPath?: { spec: PColumnSpec }[];\n};\n\n/** Derive labels for all table elements: columns via deriveDistinctLabels, axes from label columns. */\nexport function deriveAllLabels(options: {\n columns: LabelableColumn[];\n labelColumns: { readonly spec: PColumnSpec }[];\n deriveLabelsOptions?: DeriveLabelsOptions;\n}): Record<string, string> {\n const { columns, labelColumns, deriveLabelsOptions } = options;\n const axisLabels = deriveAxisLabels(columns, labelColumns);\n const columnLabels = deriveDistinctLabels(columns, deriveLabelsOptions).reduce(\n (acc, label, index) => ((acc[columns[index].id] = label), acc),\n {} as Record<string, string>,\n );\n return { ...axisLabels, ...columnLabels };\n}\n\n/** Derive axis labels from matching label columns, falling back to axis spec annotations. */\nfunction deriveAxisLabels(\n columns: { readonly spec: PColumnSpec }[],\n labelColumns: { readonly spec: PColumnSpec }[],\n): Record<string, string> {\n const axisSpecMap = new Map(\n columns.flatMap((c) => c.spec.axesSpec).map((a) => [canonicalizeJson(getAxisId(a)), a]),\n );\n const result: Record<string, string> = {};\n for (const axisKey of axisSpecMap.keys()) {\n const labelCol = labelColumns.find(\n (lc) => canonicalizeJson(getAxisId(lc.spec.axesSpec[0])) === axisKey,\n );\n const source = labelCol?.spec ?? axisSpecMap.get(axisKey);\n result[axisKey] = readAnnotation(source ?? {}, Annotation.Label)?.trim() ?? \"Unlabeled\";\n }\n return result;\n}\n\n/** Column shape required by tooltip derivation. */\nexport type TooltipableColumn = {\n readonly id: DiscoveredPColumnId;\n readonly spec: PColumnSpec;\n readonly originalId: PObjectId;\n readonly linkerPath?: MatchVariant[\"path\"];\n readonly qualifications?: MatchQualifications;\n readonly distinctiveQualifications?: MatchQualifications;\n};\n\n/** Derive origin tooltips for columns whose qualifications or linker path carry info. */\nexport function deriveAllTooltips(options: {\n columns: TooltipableColumn[];\n}): Record<DiscoveredPColumnId, string> {\n const { columns } = options;\n\n const variantCountByOriginal = columns.reduce<Map<PObjectId, number>>((acc, c) => {\n return acc.set(c.originalId, (acc.get(c.originalId) ?? 0) + 1);\n }, new Map());\n\n const { entries } = columns.reduce(\n ({ entries, variantSeen }, c) => {\n const variantCount = variantCountByOriginal.get(c.originalId);\n const variantIndex =\n (variantSeen.set(c.originalId, (variantSeen.get(c.originalId) ?? 0) + 1),\n variantSeen.get(c.originalId));\n\n entries.push({\n spec: c.spec,\n linkerPath: c.linkerPath,\n qualifications: c.qualifications,\n distinctiveQualifications: c.distinctiveQualifications,\n variantIndex,\n variantCount,\n });\n\n return { entries, variantSeen };\n },\n { entries: [] as TooltipEntry[], variantSeen: new Map<PObjectId, number>() },\n );\n\n const tooltips = deriveDistinctTooltips(entries);\n\n return Object.fromEntries(\n tooltips.flatMap((t, i) => (isNil(t) ? [] : [[columns[i].id, t] as const])),\n );\n}\n"],"mappings":"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"}
@@ -3,14 +3,13 @@ const require_column_collection_builder = require("../../columns/column_collecti
3
3
  const require_ctx_column_sources = require("../../columns/ctx_column_sources.cjs");
4
4
  require("../../columns/index.cjs");
5
5
  let _milaboratories_pl_model_common = require("@milaboratories/pl-model-common");
6
- let _milaboratories_helpers = require("@milaboratories/helpers");
7
6
  //#region src/components/PlDataTable/labels.ts
8
7
  /**
9
8
  * Get all label columns visible in the current render context
10
9
  * (result pool + block outputs + prerun).
11
10
  */
12
11
  function getAllLabelColumns(ctx) {
13
- const collection = new require_column_collection_builder.ColumnCollectionBuilder(ctx.services.pframeSpec ?? (0, _milaboratories_helpers.throwError)("PFrameSpec service is required for label discovery.")).addSources(require_ctx_column_sources.collectCtxColumnSnapshotProviders(ctx)).build({ allowPartialColumnList: true });
12
+ const collection = new require_column_collection_builder.ColumnCollectionBuilder(ctx.getService("pframeSpec")).addSources(require_ctx_column_sources.collectCtxColumnSnapshotProviders(ctx)).build({ allowPartialColumnList: true });
14
13
  try {
15
14
  return collection.findColumns({ include: {
16
15
  name: _milaboratories_pl_model_common.PColumnName.Label,
@@ -1 +1 @@
1
- {"version":3,"file":"labels.cjs","names":["ColumnCollectionBuilder","collectCtxColumnSnapshotProviders","PColumnName"],"sources":["../../../src/components/PlDataTable/labels.ts"],"sourcesContent":["import type { AxisId, PColumn, PColumnSpec, PObjectId } from \"@milaboratories/pl-model-common\";\nimport {\n getAxisId,\n isLabelColumn,\n matchAxisId,\n PColumnName,\n Services,\n type RequireServices,\n} from \"@milaboratories/pl-model-common\";\nimport type { PColumnDataUniversal, RenderCtxBase } from \"../../render\";\nimport { ColumnCollectionBuilder, collectCtxColumnSnapshotProviders } from \"../../columns\";\nimport { throwError } from \"@milaboratories/helpers\";\n\n/**\n * Get all label columns visible in the current render context\n * (result pool + block outputs + prerun).\n */\nexport function getAllLabelColumns<A, U, S extends RequireServices<typeof Services.PFrameSpec>>(\n ctx: RenderCtxBase<A, U, S>,\n): PColumn<PColumnDataUniversal>[] {\n const pframeSpec =\n ctx.services.pframeSpec ?? throwError(\"PFrameSpec service is required for label discovery.\");\n const collection = new ColumnCollectionBuilder(pframeSpec)\n .addSources(collectCtxColumnSnapshotProviders(ctx))\n .build({ allowPartialColumnList: true });\n try {\n return collection\n .findColumns({ include: { name: PColumnName.Label, axes: [] } })\n .reduce<PColumn<PColumnDataUniversal>[]>((acc, hit) => {\n const data = hit.data?.get();\n return data === undefined ? acc : [...acc, { id: hit.id, spec: hit.spec, data }];\n }, []);\n } finally {\n collection.dispose();\n }\n}\n\n/** Get label columns matching the provided columns from the result pool */\nexport function getMatchingLabelColumns(\n columns: { spec: PColumnSpec }[],\n allLabelColumns: PColumn<PColumnDataUniversal>[],\n): PColumn<PColumnDataUniversal>[] {\n // split input columns into label and value columns\n const inputLabelColumns: typeof columns = [];\n const inputValueColumns: typeof columns = [];\n for (const column of columns) {\n if (isLabelColumn(column.spec)) {\n inputLabelColumns.push(column);\n } else {\n inputValueColumns.push(column);\n }\n }\n\n // collect distinct axes of value columns\n const unlabeledAxes: AxisId[] = [];\n for (const column of inputValueColumns) {\n for (const axis of column.spec.axesSpec) {\n const axisId = getAxisId(axis);\n if (!unlabeledAxes.some((id) => matchAxisId(id, axisId))) {\n unlabeledAxes.push(axisId);\n }\n }\n }\n\n // remove axes matched by input label columns\n for (const labelColumn of inputLabelColumns) {\n const labelAxisId = getAxisId(labelColumn.spec.axesSpec[0]);\n const labelMatch = unlabeledAxes.findIndex((axisId) => matchAxisId(axisId, labelAxisId));\n if (labelMatch !== -1) {\n unlabeledAxes.splice(labelMatch, 1);\n }\n }\n\n // warning: changing this id will break backward compatibility\n const colId = (\n id: PObjectId,\n domain?: Record<string, string>,\n contextDomain?: Record<string, string>,\n ): PObjectId => {\n let wid = id.toString();\n if (domain) {\n for (const k in domain) {\n wid += k;\n wid += domain[k];\n }\n }\n if (contextDomain) {\n for (const k in contextDomain) {\n wid += k;\n wid += contextDomain[k];\n }\n }\n return wid as PObjectId;\n };\n\n // search label columns for unmatched axes\n const labelColumns: typeof allLabelColumns = [];\n for (const labelColumn of allLabelColumns) {\n const labelAxis = labelColumn.spec.axesSpec[0];\n const labelAxisId = getAxisId(labelAxis);\n const labelMatch = unlabeledAxes.findIndex((axisId) => matchAxisId(axisId, labelAxisId));\n if (labelMatch !== -1) {\n const axisId = unlabeledAxes[labelMatch];\n const dataDomainLen =\n Object.keys(axisId.domain ?? {}).length + Object.keys(axisId.contextDomain ?? {}).length;\n const labelDomainLen =\n Object.keys(labelAxis.domain ?? {}).length +\n Object.keys(labelAxis.contextDomain ?? {}).length;\n if (dataDomainLen > labelDomainLen) {\n labelColumns.push({\n id: colId(labelColumn.id, axisId.domain, axisId.contextDomain),\n spec: {\n ...labelColumn.spec,\n axesSpec: [{ ...axisId, annotations: labelAxis.annotations }],\n },\n data: labelColumn.data,\n });\n } else {\n labelColumns.push(labelColumn);\n }\n unlabeledAxes.splice(labelMatch, 1);\n }\n }\n return labelColumns;\n}\n"],"mappings":";;;;;;;;;;;AAiBA,SAAgB,mBACd,KACiC;CAGjC,MAAM,aAAa,IAAIA,kCAAAA,wBADrB,IAAI,SAAS,eAAA,GAAA,wBAAA,YAAyB,sDAAsD,CACpC,CACvD,WAAWC,2BAAAA,kCAAkC,IAAI,CAAC,CAClD,MAAM,EAAE,wBAAwB,MAAM,CAAC;AAC1C,KAAI;AACF,SAAO,WACJ,YAAY,EAAE,SAAS;GAAE,MAAMC,gCAAAA,YAAY;GAAO,MAAM,EAAE;GAAE,EAAE,CAAC,CAC/D,QAAyC,KAAK,QAAQ;GACrD,MAAM,OAAO,IAAI,MAAM,KAAK;AAC5B,UAAO,SAAS,KAAA,IAAY,MAAM,CAAC,GAAG,KAAK;IAAE,IAAI,IAAI;IAAI,MAAM,IAAI;IAAM;IAAM,CAAC;KAC/E,EAAE,CAAC;WACA;AACR,aAAW,SAAS;;;;AAKxB,SAAgB,wBACd,SACA,iBACiC;CAEjC,MAAM,oBAAoC,EAAE;CAC5C,MAAM,oBAAoC,EAAE;AAC5C,MAAK,MAAM,UAAU,QACnB,MAAA,GAAA,gCAAA,eAAkB,OAAO,KAAK,CAC5B,mBAAkB,KAAK,OAAO;KAE9B,mBAAkB,KAAK,OAAO;CAKlC,MAAM,gBAA0B,EAAE;AAClC,MAAK,MAAM,UAAU,kBACnB,MAAK,MAAM,QAAQ,OAAO,KAAK,UAAU;EACvC,MAAM,UAAA,GAAA,gCAAA,WAAmB,KAAK;AAC9B,MAAI,CAAC,cAAc,MAAM,QAAA,GAAA,gCAAA,aAAmB,IAAI,OAAO,CAAC,CACtD,eAAc,KAAK,OAAO;;AAMhC,MAAK,MAAM,eAAe,mBAAmB;EAC3C,MAAM,eAAA,GAAA,gCAAA,WAAwB,YAAY,KAAK,SAAS,GAAG;EAC3D,MAAM,aAAa,cAAc,WAAW,YAAA,GAAA,gCAAA,aAAuB,QAAQ,YAAY,CAAC;AACxF,MAAI,eAAe,GACjB,eAAc,OAAO,YAAY,EAAE;;CAKvC,MAAM,SACJ,IACA,QACA,kBACc;EACd,IAAI,MAAM,GAAG,UAAU;AACvB,MAAI,OACF,MAAK,MAAM,KAAK,QAAQ;AACtB,UAAO;AACP,UAAO,OAAO;;AAGlB,MAAI,cACF,MAAK,MAAM,KAAK,eAAe;AAC7B,UAAO;AACP,UAAO,cAAc;;AAGzB,SAAO;;CAIT,MAAM,eAAuC,EAAE;AAC/C,MAAK,MAAM,eAAe,iBAAiB;EACzC,MAAM,YAAY,YAAY,KAAK,SAAS;EAC5C,MAAM,eAAA,GAAA,gCAAA,WAAwB,UAAU;EACxC,MAAM,aAAa,cAAc,WAAW,YAAA,GAAA,gCAAA,aAAuB,QAAQ,YAAY,CAAC;AACxF,MAAI,eAAe,IAAI;GACrB,MAAM,SAAS,cAAc;AAM7B,OAJE,OAAO,KAAK,OAAO,UAAU,EAAE,CAAC,CAAC,SAAS,OAAO,KAAK,OAAO,iBAAiB,EAAE,CAAC,CAAC,SAElF,OAAO,KAAK,UAAU,UAAU,EAAE,CAAC,CAAC,SACpC,OAAO,KAAK,UAAU,iBAAiB,EAAE,CAAC,CAAC,OAE3C,cAAa,KAAK;IAChB,IAAI,MAAM,YAAY,IAAI,OAAO,QAAQ,OAAO,cAAc;IAC9D,MAAM;KACJ,GAAG,YAAY;KACf,UAAU,CAAC;MAAE,GAAG;MAAQ,aAAa,UAAU;MAAa,CAAC;KAC9D;IACD,MAAM,YAAY;IACnB,CAAC;OAEF,cAAa,KAAK,YAAY;AAEhC,iBAAc,OAAO,YAAY,EAAE;;;AAGvC,QAAO"}
1
+ {"version":3,"file":"labels.cjs","names":["ColumnCollectionBuilder","collectCtxColumnSnapshotProviders","PColumnName"],"sources":["../../../src/components/PlDataTable/labels.ts"],"sourcesContent":["import type { AxisId, PColumn, PColumnSpec, PObjectId } from \"@milaboratories/pl-model-common\";\nimport {\n getAxisId,\n isLabelColumn,\n matchAxisId,\n PColumnName,\n} from \"@milaboratories/pl-model-common\";\nimport type { PColumnDataUniversal, RenderCtxBase } from \"../../render\";\nimport { ColumnCollectionBuilder, collectCtxColumnSnapshotProviders } from \"../../columns\";\n\n/**\n * Get all label columns visible in the current render context\n * (result pool + block outputs + prerun).\n */\nexport function getAllLabelColumns<A, U>(\n ctx: RenderCtxBase<A, U>,\n): PColumn<PColumnDataUniversal>[] {\n const pframeSpec = ctx.getService(\"pframeSpec\");\n const collection = new ColumnCollectionBuilder(pframeSpec)\n .addSources(collectCtxColumnSnapshotProviders(ctx))\n .build({ allowPartialColumnList: true });\n try {\n return collection\n .findColumns({ include: { name: PColumnName.Label, axes: [] } })\n .reduce<PColumn<PColumnDataUniversal>[]>((acc, hit) => {\n const data = hit.data?.get();\n return data === undefined ? acc : [...acc, { id: hit.id, spec: hit.spec, data }];\n }, []);\n } finally {\n collection.dispose();\n }\n}\n\n/** Get label columns matching the provided columns from the result pool */\nexport function getMatchingLabelColumns(\n columns: { spec: PColumnSpec }[],\n allLabelColumns: PColumn<PColumnDataUniversal>[],\n): PColumn<PColumnDataUniversal>[] {\n // split input columns into label and value columns\n const inputLabelColumns: typeof columns = [];\n const inputValueColumns: typeof columns = [];\n for (const column of columns) {\n if (isLabelColumn(column.spec)) {\n inputLabelColumns.push(column);\n } else {\n inputValueColumns.push(column);\n }\n }\n\n // collect distinct axes of value columns\n const unlabeledAxes: AxisId[] = [];\n for (const column of inputValueColumns) {\n for (const axis of column.spec.axesSpec) {\n const axisId = getAxisId(axis);\n if (!unlabeledAxes.some((id) => matchAxisId(id, axisId))) {\n unlabeledAxes.push(axisId);\n }\n }\n }\n\n // remove axes matched by input label columns\n for (const labelColumn of inputLabelColumns) {\n const labelAxisId = getAxisId(labelColumn.spec.axesSpec[0]);\n const labelMatch = unlabeledAxes.findIndex((axisId) => matchAxisId(axisId, labelAxisId));\n if (labelMatch !== -1) {\n unlabeledAxes.splice(labelMatch, 1);\n }\n }\n\n // warning: changing this id will break backward compatibility\n const colId = (\n id: PObjectId,\n domain?: Record<string, string>,\n contextDomain?: Record<string, string>,\n ): PObjectId => {\n let wid = id.toString();\n if (domain) {\n for (const k in domain) {\n wid += k;\n wid += domain[k];\n }\n }\n if (contextDomain) {\n for (const k in contextDomain) {\n wid += k;\n wid += contextDomain[k];\n }\n }\n return wid as PObjectId;\n };\n\n // search label columns for unmatched axes\n const labelColumns: typeof allLabelColumns = [];\n for (const labelColumn of allLabelColumns) {\n const labelAxis = labelColumn.spec.axesSpec[0];\n const labelAxisId = getAxisId(labelAxis);\n const labelMatch = unlabeledAxes.findIndex((axisId) => matchAxisId(axisId, labelAxisId));\n if (labelMatch !== -1) {\n const axisId = unlabeledAxes[labelMatch];\n const dataDomainLen =\n Object.keys(axisId.domain ?? {}).length + Object.keys(axisId.contextDomain ?? {}).length;\n const labelDomainLen =\n Object.keys(labelAxis.domain ?? {}).length +\n Object.keys(labelAxis.contextDomain ?? {}).length;\n if (dataDomainLen > labelDomainLen) {\n labelColumns.push({\n id: colId(labelColumn.id, axisId.domain, axisId.contextDomain),\n spec: {\n ...labelColumn.spec,\n axesSpec: [{ ...axisId, annotations: labelAxis.annotations }],\n },\n data: labelColumn.data,\n });\n } else {\n labelColumns.push(labelColumn);\n }\n unlabeledAxes.splice(labelMatch, 1);\n }\n }\n return labelColumns;\n}\n"],"mappings":";;;;;;;;;;AAcA,SAAgB,mBACd,KACiC;CAEjC,MAAM,aAAa,IAAIA,kCAAAA,wBADJ,IAAI,WAAW,aAAa,CACW,CACvD,WAAWC,2BAAAA,kCAAkC,IAAI,CAAC,CAClD,MAAM,EAAE,wBAAwB,MAAM,CAAC;AAC1C,KAAI;AACF,SAAO,WACJ,YAAY,EAAE,SAAS;GAAE,MAAMC,gCAAAA,YAAY;GAAO,MAAM,EAAE;GAAE,EAAE,CAAC,CAC/D,QAAyC,KAAK,QAAQ;GACrD,MAAM,OAAO,IAAI,MAAM,KAAK;AAC5B,UAAO,SAAS,KAAA,IAAY,MAAM,CAAC,GAAG,KAAK;IAAE,IAAI,IAAI;IAAI,MAAM,IAAI;IAAM;IAAM,CAAC;KAC/E,EAAE,CAAC;WACA;AACR,aAAW,SAAS;;;;AAKxB,SAAgB,wBACd,SACA,iBACiC;CAEjC,MAAM,oBAAoC,EAAE;CAC5C,MAAM,oBAAoC,EAAE;AAC5C,MAAK,MAAM,UAAU,QACnB,MAAA,GAAA,gCAAA,eAAkB,OAAO,KAAK,CAC5B,mBAAkB,KAAK,OAAO;KAE9B,mBAAkB,KAAK,OAAO;CAKlC,MAAM,gBAA0B,EAAE;AAClC,MAAK,MAAM,UAAU,kBACnB,MAAK,MAAM,QAAQ,OAAO,KAAK,UAAU;EACvC,MAAM,UAAA,GAAA,gCAAA,WAAmB,KAAK;AAC9B,MAAI,CAAC,cAAc,MAAM,QAAA,GAAA,gCAAA,aAAmB,IAAI,OAAO,CAAC,CACtD,eAAc,KAAK,OAAO;;AAMhC,MAAK,MAAM,eAAe,mBAAmB;EAC3C,MAAM,eAAA,GAAA,gCAAA,WAAwB,YAAY,KAAK,SAAS,GAAG;EAC3D,MAAM,aAAa,cAAc,WAAW,YAAA,GAAA,gCAAA,aAAuB,QAAQ,YAAY,CAAC;AACxF,MAAI,eAAe,GACjB,eAAc,OAAO,YAAY,EAAE;;CAKvC,MAAM,SACJ,IACA,QACA,kBACc;EACd,IAAI,MAAM,GAAG,UAAU;AACvB,MAAI,OACF,MAAK,MAAM,KAAK,QAAQ;AACtB,UAAO;AACP,UAAO,OAAO;;AAGlB,MAAI,cACF,MAAK,MAAM,KAAK,eAAe;AAC7B,UAAO;AACP,UAAO,cAAc;;AAGzB,SAAO;;CAIT,MAAM,eAAuC,EAAE;AAC/C,MAAK,MAAM,eAAe,iBAAiB;EACzC,MAAM,YAAY,YAAY,KAAK,SAAS;EAC5C,MAAM,eAAA,GAAA,gCAAA,WAAwB,UAAU;EACxC,MAAM,aAAa,cAAc,WAAW,YAAA,GAAA,gCAAA,aAAuB,QAAQ,YAAY,CAAC;AACxF,MAAI,eAAe,IAAI;GACrB,MAAM,SAAS,cAAc;AAM7B,OAJE,OAAO,KAAK,OAAO,UAAU,EAAE,CAAC,CAAC,SAAS,OAAO,KAAK,OAAO,iBAAiB,EAAE,CAAC,CAAC,SAElF,OAAO,KAAK,UAAU,UAAU,EAAE,CAAC,CAAC,SACpC,OAAO,KAAK,UAAU,iBAAiB,EAAE,CAAC,CAAC,OAE3C,cAAa,KAAK;IAChB,IAAI,MAAM,YAAY,IAAI,OAAO,QAAQ,OAAO,cAAc;IAC9D,MAAM;KACJ,GAAG,YAAY;KACf,UAAU,CAAC;MAAE,GAAG;MAAQ,aAAa,UAAU;MAAa,CAAC;KAC9D;IACD,MAAM,YAAY;IACnB,CAAC;OAEF,cAAa,KAAK,YAAY;AAEhC,iBAAc,OAAO,YAAY,EAAE;;;AAGvC,QAAO"}
@@ -2,14 +2,13 @@ import { ColumnCollectionBuilder } from "../../columns/column_collection_builder
2
2
  import { collectCtxColumnSnapshotProviders } from "../../columns/ctx_column_sources.js";
3
3
  import "../../columns/index.js";
4
4
  import { PColumnName, getAxisId, isLabelColumn, matchAxisId } from "@milaboratories/pl-model-common";
5
- import { throwError } from "@milaboratories/helpers";
6
5
  //#region src/components/PlDataTable/labels.ts
7
6
  /**
8
7
  * Get all label columns visible in the current render context
9
8
  * (result pool + block outputs + prerun).
10
9
  */
11
10
  function getAllLabelColumns(ctx) {
12
- const collection = new ColumnCollectionBuilder(ctx.services.pframeSpec ?? throwError("PFrameSpec service is required for label discovery.")).addSources(collectCtxColumnSnapshotProviders(ctx)).build({ allowPartialColumnList: true });
11
+ const collection = new ColumnCollectionBuilder(ctx.getService("pframeSpec")).addSources(collectCtxColumnSnapshotProviders(ctx)).build({ allowPartialColumnList: true });
13
12
  try {
14
13
  return collection.findColumns({ include: {
15
14
  name: PColumnName.Label,
@@ -1 +1 @@
1
- {"version":3,"file":"labels.js","names":[],"sources":["../../../src/components/PlDataTable/labels.ts"],"sourcesContent":["import type { AxisId, PColumn, PColumnSpec, PObjectId } from \"@milaboratories/pl-model-common\";\nimport {\n getAxisId,\n isLabelColumn,\n matchAxisId,\n PColumnName,\n Services,\n type RequireServices,\n} from \"@milaboratories/pl-model-common\";\nimport type { PColumnDataUniversal, RenderCtxBase } from \"../../render\";\nimport { ColumnCollectionBuilder, collectCtxColumnSnapshotProviders } from \"../../columns\";\nimport { throwError } from \"@milaboratories/helpers\";\n\n/**\n * Get all label columns visible in the current render context\n * (result pool + block outputs + prerun).\n */\nexport function getAllLabelColumns<A, U, S extends RequireServices<typeof Services.PFrameSpec>>(\n ctx: RenderCtxBase<A, U, S>,\n): PColumn<PColumnDataUniversal>[] {\n const pframeSpec =\n ctx.services.pframeSpec ?? throwError(\"PFrameSpec service is required for label discovery.\");\n const collection = new ColumnCollectionBuilder(pframeSpec)\n .addSources(collectCtxColumnSnapshotProviders(ctx))\n .build({ allowPartialColumnList: true });\n try {\n return collection\n .findColumns({ include: { name: PColumnName.Label, axes: [] } })\n .reduce<PColumn<PColumnDataUniversal>[]>((acc, hit) => {\n const data = hit.data?.get();\n return data === undefined ? acc : [...acc, { id: hit.id, spec: hit.spec, data }];\n }, []);\n } finally {\n collection.dispose();\n }\n}\n\n/** Get label columns matching the provided columns from the result pool */\nexport function getMatchingLabelColumns(\n columns: { spec: PColumnSpec }[],\n allLabelColumns: PColumn<PColumnDataUniversal>[],\n): PColumn<PColumnDataUniversal>[] {\n // split input columns into label and value columns\n const inputLabelColumns: typeof columns = [];\n const inputValueColumns: typeof columns = [];\n for (const column of columns) {\n if (isLabelColumn(column.spec)) {\n inputLabelColumns.push(column);\n } else {\n inputValueColumns.push(column);\n }\n }\n\n // collect distinct axes of value columns\n const unlabeledAxes: AxisId[] = [];\n for (const column of inputValueColumns) {\n for (const axis of column.spec.axesSpec) {\n const axisId = getAxisId(axis);\n if (!unlabeledAxes.some((id) => matchAxisId(id, axisId))) {\n unlabeledAxes.push(axisId);\n }\n }\n }\n\n // remove axes matched by input label columns\n for (const labelColumn of inputLabelColumns) {\n const labelAxisId = getAxisId(labelColumn.spec.axesSpec[0]);\n const labelMatch = unlabeledAxes.findIndex((axisId) => matchAxisId(axisId, labelAxisId));\n if (labelMatch !== -1) {\n unlabeledAxes.splice(labelMatch, 1);\n }\n }\n\n // warning: changing this id will break backward compatibility\n const colId = (\n id: PObjectId,\n domain?: Record<string, string>,\n contextDomain?: Record<string, string>,\n ): PObjectId => {\n let wid = id.toString();\n if (domain) {\n for (const k in domain) {\n wid += k;\n wid += domain[k];\n }\n }\n if (contextDomain) {\n for (const k in contextDomain) {\n wid += k;\n wid += contextDomain[k];\n }\n }\n return wid as PObjectId;\n };\n\n // search label columns for unmatched axes\n const labelColumns: typeof allLabelColumns = [];\n for (const labelColumn of allLabelColumns) {\n const labelAxis = labelColumn.spec.axesSpec[0];\n const labelAxisId = getAxisId(labelAxis);\n const labelMatch = unlabeledAxes.findIndex((axisId) => matchAxisId(axisId, labelAxisId));\n if (labelMatch !== -1) {\n const axisId = unlabeledAxes[labelMatch];\n const dataDomainLen =\n Object.keys(axisId.domain ?? {}).length + Object.keys(axisId.contextDomain ?? {}).length;\n const labelDomainLen =\n Object.keys(labelAxis.domain ?? {}).length +\n Object.keys(labelAxis.contextDomain ?? {}).length;\n if (dataDomainLen > labelDomainLen) {\n labelColumns.push({\n id: colId(labelColumn.id, axisId.domain, axisId.contextDomain),\n spec: {\n ...labelColumn.spec,\n axesSpec: [{ ...axisId, annotations: labelAxis.annotations }],\n },\n data: labelColumn.data,\n });\n } else {\n labelColumns.push(labelColumn);\n }\n unlabeledAxes.splice(labelMatch, 1);\n }\n }\n return labelColumns;\n}\n"],"mappings":";;;;;;;;;;AAiBA,SAAgB,mBACd,KACiC;CAGjC,MAAM,aAAa,IAAI,wBADrB,IAAI,SAAS,cAAc,WAAW,sDAAsD,CACpC,CACvD,WAAW,kCAAkC,IAAI,CAAC,CAClD,MAAM,EAAE,wBAAwB,MAAM,CAAC;AAC1C,KAAI;AACF,SAAO,WACJ,YAAY,EAAE,SAAS;GAAE,MAAM,YAAY;GAAO,MAAM,EAAE;GAAE,EAAE,CAAC,CAC/D,QAAyC,KAAK,QAAQ;GACrD,MAAM,OAAO,IAAI,MAAM,KAAK;AAC5B,UAAO,SAAS,KAAA,IAAY,MAAM,CAAC,GAAG,KAAK;IAAE,IAAI,IAAI;IAAI,MAAM,IAAI;IAAM;IAAM,CAAC;KAC/E,EAAE,CAAC;WACA;AACR,aAAW,SAAS;;;;AAKxB,SAAgB,wBACd,SACA,iBACiC;CAEjC,MAAM,oBAAoC,EAAE;CAC5C,MAAM,oBAAoC,EAAE;AAC5C,MAAK,MAAM,UAAU,QACnB,KAAI,cAAc,OAAO,KAAK,CAC5B,mBAAkB,KAAK,OAAO;KAE9B,mBAAkB,KAAK,OAAO;CAKlC,MAAM,gBAA0B,EAAE;AAClC,MAAK,MAAM,UAAU,kBACnB,MAAK,MAAM,QAAQ,OAAO,KAAK,UAAU;EACvC,MAAM,SAAS,UAAU,KAAK;AAC9B,MAAI,CAAC,cAAc,MAAM,OAAO,YAAY,IAAI,OAAO,CAAC,CACtD,eAAc,KAAK,OAAO;;AAMhC,MAAK,MAAM,eAAe,mBAAmB;EAC3C,MAAM,cAAc,UAAU,YAAY,KAAK,SAAS,GAAG;EAC3D,MAAM,aAAa,cAAc,WAAW,WAAW,YAAY,QAAQ,YAAY,CAAC;AACxF,MAAI,eAAe,GACjB,eAAc,OAAO,YAAY,EAAE;;CAKvC,MAAM,SACJ,IACA,QACA,kBACc;EACd,IAAI,MAAM,GAAG,UAAU;AACvB,MAAI,OACF,MAAK,MAAM,KAAK,QAAQ;AACtB,UAAO;AACP,UAAO,OAAO;;AAGlB,MAAI,cACF,MAAK,MAAM,KAAK,eAAe;AAC7B,UAAO;AACP,UAAO,cAAc;;AAGzB,SAAO;;CAIT,MAAM,eAAuC,EAAE;AAC/C,MAAK,MAAM,eAAe,iBAAiB;EACzC,MAAM,YAAY,YAAY,KAAK,SAAS;EAC5C,MAAM,cAAc,UAAU,UAAU;EACxC,MAAM,aAAa,cAAc,WAAW,WAAW,YAAY,QAAQ,YAAY,CAAC;AACxF,MAAI,eAAe,IAAI;GACrB,MAAM,SAAS,cAAc;AAM7B,OAJE,OAAO,KAAK,OAAO,UAAU,EAAE,CAAC,CAAC,SAAS,OAAO,KAAK,OAAO,iBAAiB,EAAE,CAAC,CAAC,SAElF,OAAO,KAAK,UAAU,UAAU,EAAE,CAAC,CAAC,SACpC,OAAO,KAAK,UAAU,iBAAiB,EAAE,CAAC,CAAC,OAE3C,cAAa,KAAK;IAChB,IAAI,MAAM,YAAY,IAAI,OAAO,QAAQ,OAAO,cAAc;IAC9D,MAAM;KACJ,GAAG,YAAY;KACf,UAAU,CAAC;MAAE,GAAG;MAAQ,aAAa,UAAU;MAAa,CAAC;KAC9D;IACD,MAAM,YAAY;IACnB,CAAC;OAEF,cAAa,KAAK,YAAY;AAEhC,iBAAc,OAAO,YAAY,EAAE;;;AAGvC,QAAO"}
1
+ {"version":3,"file":"labels.js","names":[],"sources":["../../../src/components/PlDataTable/labels.ts"],"sourcesContent":["import type { AxisId, PColumn, PColumnSpec, PObjectId } from \"@milaboratories/pl-model-common\";\nimport {\n getAxisId,\n isLabelColumn,\n matchAxisId,\n PColumnName,\n} from \"@milaboratories/pl-model-common\";\nimport type { PColumnDataUniversal, RenderCtxBase } from \"../../render\";\nimport { ColumnCollectionBuilder, collectCtxColumnSnapshotProviders } from \"../../columns\";\n\n/**\n * Get all label columns visible in the current render context\n * (result pool + block outputs + prerun).\n */\nexport function getAllLabelColumns<A, U>(\n ctx: RenderCtxBase<A, U>,\n): PColumn<PColumnDataUniversal>[] {\n const pframeSpec = ctx.getService(\"pframeSpec\");\n const collection = new ColumnCollectionBuilder(pframeSpec)\n .addSources(collectCtxColumnSnapshotProviders(ctx))\n .build({ allowPartialColumnList: true });\n try {\n return collection\n .findColumns({ include: { name: PColumnName.Label, axes: [] } })\n .reduce<PColumn<PColumnDataUniversal>[]>((acc, hit) => {\n const data = hit.data?.get();\n return data === undefined ? acc : [...acc, { id: hit.id, spec: hit.spec, data }];\n }, []);\n } finally {\n collection.dispose();\n }\n}\n\n/** Get label columns matching the provided columns from the result pool */\nexport function getMatchingLabelColumns(\n columns: { spec: PColumnSpec }[],\n allLabelColumns: PColumn<PColumnDataUniversal>[],\n): PColumn<PColumnDataUniversal>[] {\n // split input columns into label and value columns\n const inputLabelColumns: typeof columns = [];\n const inputValueColumns: typeof columns = [];\n for (const column of columns) {\n if (isLabelColumn(column.spec)) {\n inputLabelColumns.push(column);\n } else {\n inputValueColumns.push(column);\n }\n }\n\n // collect distinct axes of value columns\n const unlabeledAxes: AxisId[] = [];\n for (const column of inputValueColumns) {\n for (const axis of column.spec.axesSpec) {\n const axisId = getAxisId(axis);\n if (!unlabeledAxes.some((id) => matchAxisId(id, axisId))) {\n unlabeledAxes.push(axisId);\n }\n }\n }\n\n // remove axes matched by input label columns\n for (const labelColumn of inputLabelColumns) {\n const labelAxisId = getAxisId(labelColumn.spec.axesSpec[0]);\n const labelMatch = unlabeledAxes.findIndex((axisId) => matchAxisId(axisId, labelAxisId));\n if (labelMatch !== -1) {\n unlabeledAxes.splice(labelMatch, 1);\n }\n }\n\n // warning: changing this id will break backward compatibility\n const colId = (\n id: PObjectId,\n domain?: Record<string, string>,\n contextDomain?: Record<string, string>,\n ): PObjectId => {\n let wid = id.toString();\n if (domain) {\n for (const k in domain) {\n wid += k;\n wid += domain[k];\n }\n }\n if (contextDomain) {\n for (const k in contextDomain) {\n wid += k;\n wid += contextDomain[k];\n }\n }\n return wid as PObjectId;\n };\n\n // search label columns for unmatched axes\n const labelColumns: typeof allLabelColumns = [];\n for (const labelColumn of allLabelColumns) {\n const labelAxis = labelColumn.spec.axesSpec[0];\n const labelAxisId = getAxisId(labelAxis);\n const labelMatch = unlabeledAxes.findIndex((axisId) => matchAxisId(axisId, labelAxisId));\n if (labelMatch !== -1) {\n const axisId = unlabeledAxes[labelMatch];\n const dataDomainLen =\n Object.keys(axisId.domain ?? {}).length + Object.keys(axisId.contextDomain ?? {}).length;\n const labelDomainLen =\n Object.keys(labelAxis.domain ?? {}).length +\n Object.keys(labelAxis.contextDomain ?? {}).length;\n if (dataDomainLen > labelDomainLen) {\n labelColumns.push({\n id: colId(labelColumn.id, axisId.domain, axisId.contextDomain),\n spec: {\n ...labelColumn.spec,\n axesSpec: [{ ...axisId, annotations: labelAxis.annotations }],\n },\n data: labelColumn.data,\n });\n } else {\n labelColumns.push(labelColumn);\n }\n unlabeledAxes.splice(labelMatch, 1);\n }\n }\n return labelColumns;\n}\n"],"mappings":";;;;;;;;;AAcA,SAAgB,mBACd,KACiC;CAEjC,MAAM,aAAa,IAAI,wBADJ,IAAI,WAAW,aAAa,CACW,CACvD,WAAW,kCAAkC,IAAI,CAAC,CAClD,MAAM,EAAE,wBAAwB,MAAM,CAAC;AAC1C,KAAI;AACF,SAAO,WACJ,YAAY,EAAE,SAAS;GAAE,MAAM,YAAY;GAAO,MAAM,EAAE;GAAE,EAAE,CAAC,CAC/D,QAAyC,KAAK,QAAQ;GACrD,MAAM,OAAO,IAAI,MAAM,KAAK;AAC5B,UAAO,SAAS,KAAA,IAAY,MAAM,CAAC,GAAG,KAAK;IAAE,IAAI,IAAI;IAAI,MAAM,IAAI;IAAM;IAAM,CAAC;KAC/E,EAAE,CAAC;WACA;AACR,aAAW,SAAS;;;;AAKxB,SAAgB,wBACd,SACA,iBACiC;CAEjC,MAAM,oBAAoC,EAAE;CAC5C,MAAM,oBAAoC,EAAE;AAC5C,MAAK,MAAM,UAAU,QACnB,KAAI,cAAc,OAAO,KAAK,CAC5B,mBAAkB,KAAK,OAAO;KAE9B,mBAAkB,KAAK,OAAO;CAKlC,MAAM,gBAA0B,EAAE;AAClC,MAAK,MAAM,UAAU,kBACnB,MAAK,MAAM,QAAQ,OAAO,KAAK,UAAU;EACvC,MAAM,SAAS,UAAU,KAAK;AAC9B,MAAI,CAAC,cAAc,MAAM,OAAO,YAAY,IAAI,OAAO,CAAC,CACtD,eAAc,KAAK,OAAO;;AAMhC,MAAK,MAAM,eAAe,mBAAmB;EAC3C,MAAM,cAAc,UAAU,YAAY,KAAK,SAAS,GAAG;EAC3D,MAAM,aAAa,cAAc,WAAW,WAAW,YAAY,QAAQ,YAAY,CAAC;AACxF,MAAI,eAAe,GACjB,eAAc,OAAO,YAAY,EAAE;;CAKvC,MAAM,SACJ,IACA,QACA,kBACc;EACd,IAAI,MAAM,GAAG,UAAU;AACvB,MAAI,OACF,MAAK,MAAM,KAAK,QAAQ;AACtB,UAAO;AACP,UAAO,OAAO;;AAGlB,MAAI,cACF,MAAK,MAAM,KAAK,eAAe;AAC7B,UAAO;AACP,UAAO,cAAc;;AAGzB,SAAO;;CAIT,MAAM,eAAuC,EAAE;AAC/C,MAAK,MAAM,eAAe,iBAAiB;EACzC,MAAM,YAAY,YAAY,KAAK,SAAS;EAC5C,MAAM,cAAc,UAAU,UAAU;EACxC,MAAM,aAAa,cAAc,WAAW,WAAW,YAAY,QAAQ,YAAY,CAAC;AACxF,MAAI,eAAe,IAAI;GACrB,MAAM,SAAS,cAAc;AAM7B,OAJE,OAAO,KAAK,OAAO,UAAU,EAAE,CAAC,CAAC,SAAS,OAAO,KAAK,OAAO,iBAAiB,EAAE,CAAC,CAAC,SAElF,OAAO,KAAK,UAAU,UAAU,EAAE,CAAC,CAAC,SACpC,OAAO,KAAK,UAAU,iBAAiB,EAAE,CAAC,CAAC,OAE3C,cAAa,KAAK;IAChB,IAAI,MAAM,YAAY,IAAI,OAAO,QAAQ,OAAO,cAAc;IAC9D,MAAM;KACJ,GAAG,YAAY;KACf,UAAU,CAAC;MAAE,GAAG;MAAQ,aAAa,UAAU;MAAa,CAAC;KAC9D;IACD,MAAM,YAAY;IACnB,CAAC;OAEF,cAAa,KAAK,YAAY;AAEhC,iBAAc,OAAO,YAAY,EAAE;;;AAGvC,QAAO"}
@@ -1,37 +1,8 @@
1
1
  require("../_virtual/_rolldown/runtime.cjs");
2
2
  const require_traverse = require("./traverse.cjs");
3
+ let _milaboratories_helpers = require("@milaboratories/helpers");
3
4
  let es_toolkit_compat = require("es-toolkit/compat");
4
5
  //#region src/filters/distill.ts
5
- const KNOWN_LEAF_KEYS = new Set([
6
- "n",
7
- "x",
8
- "rhs",
9
- "type",
10
- "value",
11
- "column",
12
- "minDiff",
13
- "maxEdits",
14
- "wildcard",
15
- "replacement",
16
- "substitutionsOnly"
17
- ]);
18
- /** Returns true if the leaf is filled — type is defined and no required fields are undefined. */
19
- function isFilledLeaf(node) {
20
- if (node.type == null) return false;
21
- return !Object.values(node).some((value) => {
22
- switch (typeof value) {
23
- case "number":
24
- case "boolean": return false;
25
- case "string": return value.trim() === "";
26
- default: return (0, es_toolkit_compat.isEmpty)(value);
27
- }
28
- });
29
- }
30
- function distillLeaf(node) {
31
- const result = {};
32
- for (const [key, value] of Object.entries(node)) if (KNOWN_LEAF_KEYS.has(key)) result[key] = value;
33
- return result;
34
- }
35
6
  /**
36
7
  * Strips non-FilterSpec metadata (whitelist approach) and removes
37
8
  * unfilled leaves (type is undefined or any required field is undefined).
@@ -63,6 +34,78 @@ function distillFilterSpec(filter) {
63
34
  }
64
35
  });
65
36
  }
37
+ function distillLeaf(node) {
38
+ const result = {};
39
+ for (const [key, value] of Object.entries(node)) if (KNOWN_LEAF_KEYS.has(key)) result[key] = value;
40
+ return result;
41
+ }
42
+ /** Returns true if the leaf is filled — type is defined and every required field per-type is filled. */
43
+ function isFilledLeaf(node) {
44
+ if ((0, _milaboratories_helpers.isNil)(node.type)) return false;
45
+ const required = REQUIRED_KEYS_BY_TYPE[node.type];
46
+ const record = node;
47
+ return required.every((key) => isFilledValue(record[key]));
48
+ }
49
+ /**
50
+ * Returns true if the value is considered "filled":
51
+ * - primitives (number, boolean): always true
52
+ * - string: non-empty after trim
53
+ * - array: non-empty AND every item is filled
54
+ * - plain object: non-empty AND every field value is filled
55
+ * - null/undefined: false
56
+ */
57
+ function isFilledValue(value) {
58
+ if ((0, _milaboratories_helpers.isNil)(value)) return false;
59
+ switch (typeof value) {
60
+ case "number":
61
+ case "boolean": return true;
62
+ case "string": return value.trim() !== "";
63
+ default:
64
+ if ((0, es_toolkit_compat.isEmpty)(value)) return false;
65
+ if (Array.isArray(value)) return value.every(isFilledValue);
66
+ return Object.values(value).every(isFilledValue);
67
+ }
68
+ }
69
+ const KNOWN_LEAF_KEYS = new Set([
70
+ "n",
71
+ "x",
72
+ "rhs",
73
+ "type",
74
+ "value",
75
+ "column",
76
+ "minDiff",
77
+ "maxEdits",
78
+ "wildcard",
79
+ "replacement",
80
+ "substitutionsOnly"
81
+ ]);
82
+ /** Required fields per leaf type. Optional fields (e.g. minDiff, maxEdits) excluded. */
83
+ const REQUIRED_KEYS_BY_TYPE = {
84
+ isNA: ["column"],
85
+ isNotNA: ["column"],
86
+ ifNa: ["column", "replacement"],
87
+ patternEquals: ["column", "value"],
88
+ patternNotEquals: ["column", "value"],
89
+ patternContainSubsequence: ["column", "value"],
90
+ patternNotContainSubsequence: ["column", "value"],
91
+ patternMatchesRegularExpression: ["column", "value"],
92
+ patternFuzzyContainSubsequence: ["column", "value"],
93
+ inSet: ["column", "value"],
94
+ notInSet: ["column", "value"],
95
+ topN: ["column", "n"],
96
+ bottomN: ["column", "n"],
97
+ equal: ["column", "x"],
98
+ notEqual: ["column", "x"],
99
+ lessThan: ["column", "x"],
100
+ greaterThan: ["column", "x"],
101
+ lessThanOrEqual: ["column", "x"],
102
+ greaterThanOrEqual: ["column", "x"],
103
+ equalToColumn: ["column", "rhs"],
104
+ lessThanColumn: ["column", "rhs"],
105
+ greaterThanColumn: ["column", "rhs"],
106
+ lessThanColumnOrEqual: ["column", "rhs"],
107
+ greaterThanColumnOrEqual: ["column", "rhs"]
108
+ };
66
109
  //#endregion
67
110
  exports.distillFilterSpec = distillFilterSpec;
68
111
 
@@ -1 +1 @@
1
- {"version":3,"file":"distill.cjs","names":["traverseFilterSpec"],"sources":["../../src/filters/distill.ts"],"sourcesContent":["import { DistributiveKeys, UnionToTuples } from \"@milaboratories/helpers\";\nimport {\n RootFilterSpec,\n type FilterSpec,\n type FilterSpecLeaf,\n} from \"@milaboratories/pl-model-common\";\nimport { traverseFilterSpec } from \"./traverse\";\nimport { InferFilterSpecLeaf } from \"@milaboratories/pl-model-common\";\nimport { isEmpty } from \"es-toolkit/compat\";\n\n/** All possible field names that can appear in any FilterSpecLeaf variant. */\ntype FilterSpecLeafKey = DistributiveKeys<FilterSpecLeaf<string>>;\n\n/** Compile-time check: every key in the tuple is a valid leaf key (via satisfies). */\nconst KNOWN_LEAF_KEYS_TUPLE: UnionToTuples<FilterSpecLeafKey> = [\n \"n\",\n \"x\",\n \"rhs\",\n \"type\",\n \"value\",\n \"column\",\n \"minDiff\",\n \"maxEdits\",\n \"wildcard\",\n \"replacement\",\n \"substitutionsOnly\",\n];\nconst KNOWN_LEAF_KEYS: Set<FilterSpecLeafKey> = new Set(KNOWN_LEAF_KEYS_TUPLE);\n\n/** Returns true if the leaf is filled type is defined and no required fields are undefined. */\nfunction isFilledLeaf<T>(node: FilterSpecLeaf<T>): boolean {\n if (node.type == null) return false;\n return !Object.values(node).some((value) => {\n switch (typeof value) {\n case \"number\":\n case \"boolean\":\n return false;\n case \"string\":\n return value.trim() === \"\";\n default: // undefined, null, empty objects/arrays\n return isEmpty(value);\n }\n });\n}\n\nfunction distillLeaf<T>(node: FilterSpecLeaf<T>): FilterSpecLeaf<T> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(node)) {\n if (KNOWN_LEAF_KEYS.has(key as FilterSpecLeafKey)) {\n result[key] = value;\n }\n }\n return result as FilterSpecLeaf<T>;\n}\n\n/**\n * Strips non-FilterSpec metadata (whitelist approach) and removes\n * unfilled leaves (type is undefined or any required field is undefined).\n */\nexport function distillFilterSpec<\n FS extends FilterSpec<FilterSpecLeaf<unknown>, unknown, unknown>,\n R extends FS extends RootFilterSpec<FilterSpecLeaf<unknown>, unknown, unknown>\n ? RootFilterSpec<InferFilterSpecLeaf<FS>>\n : FilterSpec<InferFilterSpecLeaf<FS>>,\n>(filter: null | undefined | FS): null | R {\n if (filter == null) return null;\n return traverseFilterSpec<FS, null | R>(filter, {\n leaf: (leaf) => {\n const distilled = distillLeaf(leaf);\n return isFilledLeaf(distilled) ? (distilled as R) : null;\n },\n and: (results) => {\n const filtered = results.filter((f): f is NonNullable<typeof f> => f !== null);\n return filtered.length === 0 ? null : ({ type: \"and\", filters: filtered } as R);\n },\n or: (results) => {\n const filtered = results.filter((f): f is NonNullable<typeof f> => f !== null);\n return filtered.length === 0 ? null : ({ type: \"or\", filters: filtered } as R);\n },\n not: (result) => (result === null ? null : ({ type: \"not\", filter: result } as R)),\n });\n}\n"],"mappings":";;;;AA2BA,MAAM,kBAA0C,IAAI,IAbY;CAC9D;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAC6E;;AAG9E,SAAS,aAAgB,MAAkC;AACzD,KAAI,KAAK,QAAQ,KAAM,QAAO;AAC9B,QAAO,CAAC,OAAO,OAAO,KAAK,CAAC,MAAM,UAAU;AAC1C,UAAQ,OAAO,OAAf;GACE,KAAK;GACL,KAAK,UACH,QAAO;GACT,KAAK,SACH,QAAO,MAAM,MAAM,KAAK;GAC1B,QACE,SAAA,GAAA,kBAAA,SAAe,MAAM;;GAEzB;;AAGJ,SAAS,YAAe,MAA4C;CAClE,MAAM,SAAkC,EAAE;AAC1C,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,CAC7C,KAAI,gBAAgB,IAAI,IAAyB,CAC/C,QAAO,OAAO;AAGlB,QAAO;;;;;;AAOT,SAAgB,kBAKd,QAAyC;AACzC,KAAI,UAAU,KAAM,QAAO;AAC3B,QAAOA,iBAAAA,mBAAiC,QAAQ;EAC9C,OAAO,SAAS;GACd,MAAM,YAAY,YAAY,KAAK;AACnC,UAAO,aAAa,UAAU,GAAI,YAAkB;;EAEtD,MAAM,YAAY;GAChB,MAAM,WAAW,QAAQ,QAAQ,MAAkC,MAAM,KAAK;AAC9E,UAAO,SAAS,WAAW,IAAI,OAAQ;IAAE,MAAM;IAAO,SAAS;IAAU;;EAE3E,KAAK,YAAY;GACf,MAAM,WAAW,QAAQ,QAAQ,MAAkC,MAAM,KAAK;AAC9E,UAAO,SAAS,WAAW,IAAI,OAAQ;IAAE,MAAM;IAAM,SAAS;IAAU;;EAE1E,MAAM,WAAY,WAAW,OAAO,OAAQ;GAAE,MAAM;GAAO,QAAQ;GAAQ;EAC5E,CAAC"}
1
+ {"version":3,"file":"distill.cjs","names":["traverseFilterSpec"],"sources":["../../src/filters/distill.ts"],"sourcesContent":["import { DistributiveKeys, isNil, UnionToTuples } from \"@milaboratories/helpers\";\nimport {\n RootFilterSpec,\n type FilterSpec,\n type FilterSpecLeaf,\n} from \"@milaboratories/pl-model-common\";\nimport { traverseFilterSpec } from \"./traverse\";\nimport { InferFilterSpecLeaf } from \"@milaboratories/pl-model-common\";\nimport { isEmpty } from \"es-toolkit/compat\";\n\n/**\n * Strips non-FilterSpec metadata (whitelist approach) and removes\n * unfilled leaves (type is undefined or any required field is undefined).\n */\nexport function distillFilterSpec<\n FS extends FilterSpec<FilterSpecLeaf<unknown>, unknown, unknown>,\n R extends FS extends RootFilterSpec<FilterSpecLeaf<unknown>, unknown, unknown>\n ? RootFilterSpec<InferFilterSpecLeaf<FS>>\n : FilterSpec<InferFilterSpecLeaf<FS>>,\n>(filter: null | undefined | FS): null | R {\n if (filter == null) return null;\n return traverseFilterSpec<FS, null | R>(filter, {\n leaf: (leaf) => {\n const distilled = distillLeaf(leaf);\n return isFilledLeaf(distilled) ? (distilled as R) : null;\n },\n and: (results) => {\n const filtered = results.filter((f): f is NonNullable<typeof f> => f !== null);\n return filtered.length === 0 ? null : ({ type: \"and\", filters: filtered } as R);\n },\n or: (results) => {\n const filtered = results.filter((f): f is NonNullable<typeof f> => f !== null);\n return filtered.length === 0 ? null : ({ type: \"or\", filters: filtered } as R);\n },\n not: (result) => (result === null ? null : ({ type: \"not\", filter: result } as R)),\n });\n}\n\nfunction distillLeaf<T>(node: FilterSpecLeaf<T>): FilterSpecLeaf<T> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(node)) {\n if (KNOWN_LEAF_KEYS.has(key as FilterSpecLeafKey)) {\n result[key] = value;\n }\n }\n return result as FilterSpecLeaf<T>;\n}\n\n/** Returns true if the leaf is filled type is defined and every required field per-type is filled. */\nfunction isFilledLeaf<T>(node: FilterSpecLeaf<T>): boolean {\n if (isNil(node.type)) return false;\n const required = REQUIRED_KEYS_BY_TYPE[node.type];\n const record = node as Record<string, unknown>;\n return required.every((key) => isFilledValue(record[key]));\n}\n\n/**\n * Returns true if the value is considered \"filled\":\n * - primitives (number, boolean): always true\n * - string: non-empty after trim\n * - array: non-empty AND every item is filled\n * - plain object: non-empty AND every field value is filled\n * - null/undefined: false\n */\nfunction isFilledValue(value: unknown): boolean {\n if (isNil(value)) return false;\n switch (typeof value) {\n case \"number\":\n case \"boolean\":\n return true;\n case \"string\":\n return value.trim() !== \"\";\n default:\n if (isEmpty(value)) return false;\n if (Array.isArray(value)) return value.every(isFilledValue);\n return Object.values(value as Record<string, unknown>).every(isFilledValue);\n }\n}\n\n/** All possible field names that can appear in any FilterSpecLeaf variant. */\ntype FilterSpecLeafKey = DistributiveKeys<FilterSpecLeaf<string>>;\n\n/** Leaf type discriminators (excludes the placeholder `undefined` variant). */\ntype FilterSpecLeafType = Exclude<FilterSpecLeaf<unknown>, { type: undefined }>[\"type\"];\n\ntype LeafOfType<T extends FilterSpecLeafType> = Extract<FilterSpecLeaf<unknown>, { type: T }>;\n\ntype RequiredKeys<O> = { [K in keyof O]-?: {} extends Pick<O, K> ? never : K }[keyof O];\n\n/** Required field keys of a given leaf variant (excluding the `type` discriminator). */\ntype RequiredLeafKeys<T extends FilterSpecLeafType> = Exclude<RequiredKeys<LeafOfType<T>>, \"type\">;\n\n/** Exact per-type shape — adding a key not required by that variant becomes a type error. */\ntype RequiredKeysByType = { readonly [T in FilterSpecLeafType]: readonly RequiredLeafKeys<T>[] };\n\n/** Compile-time check: every key in the tuple is a valid leaf key (via satisfies). */\nconst KNOWN_LEAF_KEYS_TUPLE: UnionToTuples<FilterSpecLeafKey> = [\n \"n\",\n \"x\",\n \"rhs\",\n \"type\",\n \"value\",\n \"column\",\n \"minDiff\",\n \"maxEdits\",\n \"wildcard\",\n \"replacement\",\n \"substitutionsOnly\",\n];\nconst KNOWN_LEAF_KEYS: Set<FilterSpecLeafKey> = new Set(KNOWN_LEAF_KEYS_TUPLE);\n\n/** Required fields per leaf type. Optional fields (e.g. minDiff, maxEdits) excluded. */\nconst REQUIRED_KEYS_BY_TYPE = {\n isNA: [\"column\"],\n isNotNA: [\"column\"],\n ifNa: [\"column\", \"replacement\"],\n patternEquals: [\"column\", \"value\"],\n patternNotEquals: [\"column\", \"value\"],\n patternContainSubsequence: [\"column\", \"value\"],\n patternNotContainSubsequence: [\"column\", \"value\"],\n patternMatchesRegularExpression: [\"column\", \"value\"],\n patternFuzzyContainSubsequence: [\"column\", \"value\"],\n inSet: [\"column\", \"value\"],\n notInSet: [\"column\", \"value\"],\n topN: [\"column\", \"n\"],\n bottomN: [\"column\", \"n\"],\n equal: [\"column\", \"x\"],\n notEqual: [\"column\", \"x\"],\n lessThan: [\"column\", \"x\"],\n greaterThan: [\"column\", \"x\"],\n lessThanOrEqual: [\"column\", \"x\"],\n greaterThanOrEqual: [\"column\", \"x\"],\n equalToColumn: [\"column\", \"rhs\"],\n lessThanColumn: [\"column\", \"rhs\"],\n greaterThanColumn: [\"column\", \"rhs\"],\n lessThanColumnOrEqual: [\"column\", \"rhs\"],\n greaterThanColumnOrEqual: [\"column\", \"rhs\"],\n} as const satisfies RequiredKeysByType;\n"],"mappings":";;;;;;;;;AAcA,SAAgB,kBAKd,QAAyC;AACzC,KAAI,UAAU,KAAM,QAAO;AAC3B,QAAOA,iBAAAA,mBAAiC,QAAQ;EAC9C,OAAO,SAAS;GACd,MAAM,YAAY,YAAY,KAAK;AACnC,UAAO,aAAa,UAAU,GAAI,YAAkB;;EAEtD,MAAM,YAAY;GAChB,MAAM,WAAW,QAAQ,QAAQ,MAAkC,MAAM,KAAK;AAC9E,UAAO,SAAS,WAAW,IAAI,OAAQ;IAAE,MAAM;IAAO,SAAS;IAAU;;EAE3E,KAAK,YAAY;GACf,MAAM,WAAW,QAAQ,QAAQ,MAAkC,MAAM,KAAK;AAC9E,UAAO,SAAS,WAAW,IAAI,OAAQ;IAAE,MAAM;IAAM,SAAS;IAAU;;EAE1E,MAAM,WAAY,WAAW,OAAO,OAAQ;GAAE,MAAM;GAAO,QAAQ;GAAQ;EAC5E,CAAC;;AAGJ,SAAS,YAAe,MAA4C;CAClE,MAAM,SAAkC,EAAE;AAC1C,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,CAC7C,KAAI,gBAAgB,IAAI,IAAyB,CAC/C,QAAO,OAAO;AAGlB,QAAO;;;AAIT,SAAS,aAAgB,MAAkC;AACzD,MAAA,GAAA,wBAAA,OAAU,KAAK,KAAK,CAAE,QAAO;CAC7B,MAAM,WAAW,sBAAsB,KAAK;CAC5C,MAAM,SAAS;AACf,QAAO,SAAS,OAAO,QAAQ,cAAc,OAAO,KAAK,CAAC;;;;;;;;;;AAW5D,SAAS,cAAc,OAAyB;AAC9C,MAAA,GAAA,wBAAA,OAAU,MAAM,CAAE,QAAO;AACzB,SAAQ,OAAO,OAAf;EACE,KAAK;EACL,KAAK,UACH,QAAO;EACT,KAAK,SACH,QAAO,MAAM,MAAM,KAAK;EAC1B;AACE,QAAA,GAAA,kBAAA,SAAY,MAAM,CAAE,QAAO;AAC3B,OAAI,MAAM,QAAQ,MAAM,CAAE,QAAO,MAAM,MAAM,cAAc;AAC3D,UAAO,OAAO,OAAO,MAAiC,CAAC,MAAM,cAAc;;;AAkCjF,MAAM,kBAA0C,IAAI,IAbY;CAC9D;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAC6E;;AAG9E,MAAM,wBAAwB;CAC5B,MAAM,CAAC,SAAS;CAChB,SAAS,CAAC,SAAS;CACnB,MAAM,CAAC,UAAU,cAAc;CAC/B,eAAe,CAAC,UAAU,QAAQ;CAClC,kBAAkB,CAAC,UAAU,QAAQ;CACrC,2BAA2B,CAAC,UAAU,QAAQ;CAC9C,8BAA8B,CAAC,UAAU,QAAQ;CACjD,iCAAiC,CAAC,UAAU,QAAQ;CACpD,gCAAgC,CAAC,UAAU,QAAQ;CACnD,OAAO,CAAC,UAAU,QAAQ;CAC1B,UAAU,CAAC,UAAU,QAAQ;CAC7B,MAAM,CAAC,UAAU,IAAI;CACrB,SAAS,CAAC,UAAU,IAAI;CACxB,OAAO,CAAC,UAAU,IAAI;CACtB,UAAU,CAAC,UAAU,IAAI;CACzB,UAAU,CAAC,UAAU,IAAI;CACzB,aAAa,CAAC,UAAU,IAAI;CAC5B,iBAAiB,CAAC,UAAU,IAAI;CAChC,oBAAoB,CAAC,UAAU,IAAI;CACnC,eAAe,CAAC,UAAU,MAAM;CAChC,gBAAgB,CAAC,UAAU,MAAM;CACjC,mBAAmB,CAAC,UAAU,MAAM;CACpC,uBAAuB,CAAC,UAAU,MAAM;CACxC,0BAA0B,CAAC,UAAU,MAAM;CAC5C"}
@@ -1 +1 @@
1
- {"version":3,"file":"distill.d.ts","names":[],"sources":["../../src/filters/distill.ts"],"mappings":";;;;AA2DA;;;iBAAgB,iBAAA,YACH,UAAA,CAAW,cAAA,yCACZ,EAAA,SAAW,cAAA,CAAe,cAAA,+BAChC,cAAA,CAAe,mBAAA,CAAoB,EAAA,KACnC,UAAA,CAAW,mBAAA,CAAoB,EAAA,IAAA,CACnC,MAAA,qBAA2B,EAAA,UAAY,CAAA"}
1
+ {"version":3,"file":"distill.d.ts","names":[],"sources":["../../src/filters/distill.ts"],"mappings":";;;;AAcA;;;iBAAgB,iBAAA,YACH,UAAA,CAAW,cAAA,yCACZ,EAAA,SAAW,cAAA,CAAe,cAAA,+BAChC,cAAA,CAAe,mBAAA,CAAoB,EAAA,KACnC,UAAA,CAAW,mBAAA,CAAoB,EAAA,IAAA,CACnC,MAAA,qBAA2B,EAAA,UAAY,CAAA"}
@@ -1,36 +1,7 @@
1
1
  import { traverseFilterSpec } from "./traverse.js";
2
+ import { isNil } from "@milaboratories/helpers";
2
3
  import { isEmpty } from "es-toolkit/compat";
3
4
  //#region src/filters/distill.ts
4
- const KNOWN_LEAF_KEYS = new Set([
5
- "n",
6
- "x",
7
- "rhs",
8
- "type",
9
- "value",
10
- "column",
11
- "minDiff",
12
- "maxEdits",
13
- "wildcard",
14
- "replacement",
15
- "substitutionsOnly"
16
- ]);
17
- /** Returns true if the leaf is filled — type is defined and no required fields are undefined. */
18
- function isFilledLeaf(node) {
19
- if (node.type == null) return false;
20
- return !Object.values(node).some((value) => {
21
- switch (typeof value) {
22
- case "number":
23
- case "boolean": return false;
24
- case "string": return value.trim() === "";
25
- default: return isEmpty(value);
26
- }
27
- });
28
- }
29
- function distillLeaf(node) {
30
- const result = {};
31
- for (const [key, value] of Object.entries(node)) if (KNOWN_LEAF_KEYS.has(key)) result[key] = value;
32
- return result;
33
- }
34
5
  /**
35
6
  * Strips non-FilterSpec metadata (whitelist approach) and removes
36
7
  * unfilled leaves (type is undefined or any required field is undefined).
@@ -62,6 +33,78 @@ function distillFilterSpec(filter) {
62
33
  }
63
34
  });
64
35
  }
36
+ function distillLeaf(node) {
37
+ const result = {};
38
+ for (const [key, value] of Object.entries(node)) if (KNOWN_LEAF_KEYS.has(key)) result[key] = value;
39
+ return result;
40
+ }
41
+ /** Returns true if the leaf is filled — type is defined and every required field per-type is filled. */
42
+ function isFilledLeaf(node) {
43
+ if (isNil(node.type)) return false;
44
+ const required = REQUIRED_KEYS_BY_TYPE[node.type];
45
+ const record = node;
46
+ return required.every((key) => isFilledValue(record[key]));
47
+ }
48
+ /**
49
+ * Returns true if the value is considered "filled":
50
+ * - primitives (number, boolean): always true
51
+ * - string: non-empty after trim
52
+ * - array: non-empty AND every item is filled
53
+ * - plain object: non-empty AND every field value is filled
54
+ * - null/undefined: false
55
+ */
56
+ function isFilledValue(value) {
57
+ if (isNil(value)) return false;
58
+ switch (typeof value) {
59
+ case "number":
60
+ case "boolean": return true;
61
+ case "string": return value.trim() !== "";
62
+ default:
63
+ if (isEmpty(value)) return false;
64
+ if (Array.isArray(value)) return value.every(isFilledValue);
65
+ return Object.values(value).every(isFilledValue);
66
+ }
67
+ }
68
+ const KNOWN_LEAF_KEYS = new Set([
69
+ "n",
70
+ "x",
71
+ "rhs",
72
+ "type",
73
+ "value",
74
+ "column",
75
+ "minDiff",
76
+ "maxEdits",
77
+ "wildcard",
78
+ "replacement",
79
+ "substitutionsOnly"
80
+ ]);
81
+ /** Required fields per leaf type. Optional fields (e.g. minDiff, maxEdits) excluded. */
82
+ const REQUIRED_KEYS_BY_TYPE = {
83
+ isNA: ["column"],
84
+ isNotNA: ["column"],
85
+ ifNa: ["column", "replacement"],
86
+ patternEquals: ["column", "value"],
87
+ patternNotEquals: ["column", "value"],
88
+ patternContainSubsequence: ["column", "value"],
89
+ patternNotContainSubsequence: ["column", "value"],
90
+ patternMatchesRegularExpression: ["column", "value"],
91
+ patternFuzzyContainSubsequence: ["column", "value"],
92
+ inSet: ["column", "value"],
93
+ notInSet: ["column", "value"],
94
+ topN: ["column", "n"],
95
+ bottomN: ["column", "n"],
96
+ equal: ["column", "x"],
97
+ notEqual: ["column", "x"],
98
+ lessThan: ["column", "x"],
99
+ greaterThan: ["column", "x"],
100
+ lessThanOrEqual: ["column", "x"],
101
+ greaterThanOrEqual: ["column", "x"],
102
+ equalToColumn: ["column", "rhs"],
103
+ lessThanColumn: ["column", "rhs"],
104
+ greaterThanColumn: ["column", "rhs"],
105
+ lessThanColumnOrEqual: ["column", "rhs"],
106
+ greaterThanColumnOrEqual: ["column", "rhs"]
107
+ };
65
108
  //#endregion
66
109
  export { distillFilterSpec };
67
110