@platforma-sdk/model 1.68.5 → 1.68.6

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 (52) hide show
  1. package/dist/columns/column_collection_builder.cjs +8 -2
  2. package/dist/columns/column_collection_builder.cjs.map +1 -1
  3. package/dist/columns/column_collection_builder.d.ts +14 -3
  4. package/dist/columns/column_collection_builder.d.ts.map +1 -1
  5. package/dist/columns/column_collection_builder.js +8 -2
  6. package/dist/columns/column_collection_builder.js.map +1 -1
  7. package/dist/columns/ctx_column_sources.d.ts +1 -1
  8. package/dist/columns/index.d.ts +1 -1
  9. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs +48 -49
  10. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs.map +1 -1
  11. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts +5 -10
  12. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts.map +1 -1
  13. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js +48 -49
  14. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js.map +1 -1
  15. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs +16 -17
  16. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs.map +1 -1
  17. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.d.ts +4 -4
  18. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.d.ts.map +1 -1
  19. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js +16 -17
  20. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js.map +1 -1
  21. package/dist/components/PlDataTable/createPlDataTable/utils.cjs +8 -2
  22. package/dist/components/PlDataTable/createPlDataTable/utils.cjs.map +1 -1
  23. package/dist/components/PlDataTable/createPlDataTable/utils.js +8 -2
  24. package/dist/components/PlDataTable/createPlDataTable/utils.js.map +1 -1
  25. package/dist/components/PlDatasetSelector/filter_discovery.d.ts +1 -1
  26. package/dist/index.d.ts +6 -6
  27. package/dist/labels/derive_distinct_labels.cjs +121 -50
  28. package/dist/labels/derive_distinct_labels.cjs.map +1 -1
  29. package/dist/labels/derive_distinct_labels.d.ts +30 -14
  30. package/dist/labels/derive_distinct_labels.d.ts.map +1 -1
  31. package/dist/labels/derive_distinct_labels.js +121 -50
  32. package/dist/labels/derive_distinct_labels.js.map +1 -1
  33. package/dist/labels/derive_distinct_tooltips.cjs +0 -10
  34. package/dist/labels/derive_distinct_tooltips.cjs.map +1 -1
  35. package/dist/labels/derive_distinct_tooltips.d.ts +2 -3
  36. package/dist/labels/derive_distinct_tooltips.d.ts.map +1 -1
  37. package/dist/labels/derive_distinct_tooltips.js +0 -10
  38. package/dist/labels/derive_distinct_tooltips.js.map +1 -1
  39. package/dist/labels/index.d.ts +1 -1
  40. package/dist/package.cjs +1 -1
  41. package/dist/package.js +1 -1
  42. package/package.json +5 -5
  43. package/src/columns/column_collection_builder.test.ts +0 -2
  44. package/src/columns/column_collection_builder.ts +26 -3
  45. package/src/components/PlDataTable/createPlDataTable/createPlDataTableV3.ts +85 -75
  46. package/src/components/PlDataTable/createPlDataTable/discoverColumns.ts +31 -34
  47. package/src/components/PlDataTable/createPlDataTable/utils.test.ts +1 -1
  48. package/src/components/PlDataTable/createPlDataTable/utils.ts +11 -4
  49. package/src/labels/derive_distinct_labels.test.ts +396 -52
  50. package/src/labels/derive_distinct_labels.ts +205 -103
  51. package/src/labels/derive_distinct_tooltips.test.ts +1 -22
  52. package/src/labels/derive_distinct_tooltips.ts +1 -18
@@ -20,13 +20,7 @@ import { isEmpty } from "es-toolkit/compat";
20
20
  import type { PlDataTableFilters, PlDataTableFilterSpecLeaf, PlDataTableModel } from "../typesV5";
21
21
  import { upgradePlDataTableStateV2 } from "../state-migration";
22
22
  import type { PlDataTableStateV2 } from "../state-migration";
23
- import type {
24
- ColumnSelector,
25
- ColumnSnapshot,
26
- MatchingMode,
27
- MatchQualifications,
28
- MatchVariant,
29
- } from "../../../columns";
23
+ import type { ColumnSelector, ColumnSnapshot, ColumnVariant, MatchingMode } from "../../../columns";
30
24
  import { getAllLabelColumns, getMatchingLabelColumns } from "../labels";
31
25
  import type { DeriveLabelsOptions } from "../../../labels/derive_distinct_labels";
32
26
  import {
@@ -48,7 +42,7 @@ import { isNil, isPlainObject, throwError, type Nil } from "@milaboratories/help
48
42
  export type createPlDataTableOptionsV3 = {
49
43
  tableState?: PlDataTableStateV2;
50
44
 
51
- columns: Nil | DiscoverTableColumnOptions | TableColumnSnapshot[];
45
+ columns: Nil | DiscoverTableColumnOptions | TableColumnVariant[];
52
46
  filters?: PlDataTableFilters;
53
47
  sorting?: PTableSorting[];
54
48
  primaryJoinType?: "inner" | "full";
@@ -103,15 +97,16 @@ export function createPlDataTableV3<A, U>(
103
97
  const splited = splitDiscoveredColumns(discovered);
104
98
 
105
99
  const labelColumns = getMatchingLabelColumns(
106
- [...splited.direct, ...splited.linked],
100
+ [...splited.direct, ...splited.linked].map((v) => v.column),
107
101
  getAllLabelColumns(ctx),
108
102
  );
109
103
 
110
104
  const derivedLabels = deriveAllLabels({
111
105
  columns: discovered.map((dc) => ({
112
- id: dc.id,
113
- spec: dc.spec,
114
- linkerPath: dc.linkerPath?.map((lp) => ({ spec: lp.linker.spec })),
106
+ id: dc.column.id,
107
+ spec: dc.column.spec,
108
+ linkerPath: dc.path,
109
+ qualifications: dc.qualifications,
115
110
  })),
116
111
  labelColumns,
117
112
  deriveLabelsOptions: {
@@ -122,12 +117,11 @@ export function createPlDataTableV3<A, U>(
122
117
 
123
118
  const derivedTooltips = deriveAllTooltips({
124
119
  columns: discovered.map((dc) => ({
125
- id: dc.id,
120
+ id: dc.column.id,
126
121
  originalId: dc.originalId,
127
- spec: dc.spec,
128
- linkerPath: dc.linkerPath,
122
+ spec: dc.column.spec,
123
+ linkerPath: dc.path,
129
124
  qualifications: dc.qualifications,
130
- distinctiveQualifications: dc.distinctiveQualifications,
131
125
  })),
132
126
  });
133
127
 
@@ -146,8 +140,8 @@ export function createPlDataTableV3<A, U>(
146
140
  if (primarySnapshots.length === 0) return undefined;
147
141
 
148
142
  const columnIsAvailable = createColumnValidationById([
149
- ...annotated.direct,
150
- ...annotated.linked.flatMap((lc) => [...(lc.linkerPath ?? []).map((s) => s.linker), lc]),
143
+ ...annotated.direct.map((v) => v.column),
144
+ ...annotated.linked.flatMap((lc) => [...lc.path.map((s) => s.linker), lc.column]),
151
145
  ...annotated.labels,
152
146
  ]);
153
147
 
@@ -165,7 +159,7 @@ export function createPlDataTableV3<A, U>(
165
159
  validateSorting(sorting, columnIsAvailable);
166
160
 
167
161
  const primaryEntries: PrimaryEntry<undefined | PColumnDataUniversal>[] = primarySnapshots.map(
168
- (snap) => ({ column: resolveSnapshot(snap) }),
162
+ (v) => ({ column: resolveSnapshot(v.column) }),
169
163
  );
170
164
  const secondaryGroups: SecondaryGroup<undefined | PColumnDataUniversal>[] = buildSecondaryGroups(
171
165
  secondarySnapshots,
@@ -184,15 +178,15 @@ export function createPlDataTableV3<A, U>(
184
178
  // TODO: is workaround for dropdown suggestions.
185
179
  // Pframe have not equivalent data for columns relativly to Ptable
186
180
  const pframeHandle = ctx.createPFrame([
187
- ...annotated.direct.map(resolveSnapshot),
188
- ...annotated.linked.map(resolveSnapshot),
181
+ ...annotated.direct.map((v) => resolveSnapshot(v.column)),
182
+ ...annotated.linked.map((v) => resolveSnapshot(v.column)),
189
183
  ...annotated.labels,
190
184
  ...collectLinkerSnapshots(annotated.linked).map(resolveSnapshot),
191
185
  ]);
192
186
 
193
187
  const hiddenSpecs = state.pTableParams.hiddenColIds;
194
188
  const hiddenColumnIds = computeHiddenColumns(
195
- [...annotated.direct, ...annotated.linked],
189
+ [...annotated.direct, ...annotated.linked].map((v) => v.column),
196
190
  sorting,
197
191
  filters,
198
192
  hiddenSpecs,
@@ -221,43 +215,39 @@ export function createPlDataTableV3<A, U>(
221
215
  } satisfies PlDataTableModel;
222
216
  }
223
217
 
224
- /** A single column discovered from sources — normalized from raw ColumnSnapshot/ColumnMatch. */
225
- export type TableColumnSnapshot = ColumnSnapshot<DiscoveredPColumnId> & {
218
+ export type TableColumnVariant = ColumnVariant<DiscoveredPColumnId> & {
226
219
  readonly originalId: PObjectId;
227
220
  readonly isPrimary?: boolean;
228
- readonly linkerPath?: MatchVariant["path"];
229
- readonly qualifications?: MatchQualifications;
230
- readonly distinctiveQualifications?: MatchQualifications;
231
221
  };
232
222
 
233
223
  type SplitDiscoveredColumns = {
234
- readonly direct: TableColumnSnapshot[];
235
- readonly linked: TableColumnSnapshot[];
224
+ readonly direct: TableColumnVariant[];
225
+ readonly linked: TableColumnVariant[];
236
226
  };
237
227
 
238
228
  type AnnotatedColumnGroups = {
239
- readonly direct: TableColumnSnapshot[];
240
- readonly linked: TableColumnSnapshot[];
229
+ readonly direct: TableColumnVariant[];
230
+ readonly linked: TableColumnVariant[];
241
231
  readonly labels: PColumn<PColumnDataUniversal>[];
242
232
  };
243
233
 
244
234
  type VisibleColumns = {
245
- readonly direct: TableColumnSnapshot[];
246
- readonly linked: TableColumnSnapshot[];
235
+ readonly direct: TableColumnVariant[];
236
+ readonly linked: TableColumnVariant[];
247
237
  readonly labels: PColumn<PColumnDataUniversal>[];
248
238
  };
249
239
 
250
240
  /** Split discovered columns into direct (no linker path) and linked (with linker path). */
251
- function splitDiscoveredColumns(columns: TableColumnSnapshot[]): SplitDiscoveredColumns {
252
- const direct = columns.filter((dc) => isNil(dc.linkerPath) || dc.linkerPath.length === 0);
253
- const linked = columns.filter((dc) => !isNil(dc.linkerPath) && dc.linkerPath.length > 0);
241
+ function splitDiscoveredColumns(columns: TableColumnVariant[]): SplitDiscoveredColumns {
242
+ const direct = columns.filter((dc) => dc.path.length === 0);
243
+ const linked = columns.filter((dc) => dc.path.length > 0);
254
244
  return { direct, linked };
255
245
  }
256
246
 
257
247
  /** All linker snapshots across the given linked columns, deduped by id. */
258
- function collectLinkerSnapshots(linked: TableColumnSnapshot[]): ColumnSnapshot<PObjectId>[] {
248
+ function collectLinkerSnapshots(linked: TableColumnVariant[]): ColumnSnapshot<PObjectId>[] {
259
249
  return uniqueBy(
260
- linked.flatMap((lc) => (lc.linkerPath ?? []).map((s) => s.linker)),
250
+ linked.flatMap((lc) => lc.path.map((s) => s.linker)),
261
251
  (c) => c.id,
262
252
  );
263
253
  }
@@ -269,8 +259,8 @@ function collectLinkerSnapshots(linked: TableColumnSnapshot[]): ColumnSnapshot<P
269
259
  * column annotations via `withTableVisualAnnotations`.
270
260
  */
271
261
  function annotateColumnGroups(params: {
272
- direct: TableColumnSnapshot[];
273
- linked: TableColumnSnapshot[];
262
+ direct: TableColumnVariant[];
263
+ linked: TableColumnVariant[];
274
264
  labelColumns: PColumn<PColumnDataUniversal>[];
275
265
  derivedLabels: Record<string, string>;
276
266
  derivedTooltips: Record<string, string>;
@@ -288,8 +278,8 @@ function annotateColumnGroups(params: {
288
278
  } = params;
289
279
 
290
280
  const allColumnsForRules = [
291
- ...direct,
292
- ...linked,
281
+ ...direct.map((v) => v.column),
282
+ ...linked.map((v) => v.column),
293
283
  ...labelColumns,
294
284
  ...collectLinkerSnapshots(linked),
295
285
  ];
@@ -304,20 +294,23 @@ function annotateColumnGroups(params: {
304
294
  pframeSpec,
305
295
  );
306
296
 
307
- const directAnnotated = [
308
- withLabelAnnotations.bind(null, derivedLabels),
309
- withInfoAnnotations.bind(null, derivedTooltips),
310
- withTableVisualAnnotations.bind(null, visibilityByColId, orderByColId),
311
- ].reduce((cols, fn) => fn(cols) as TableColumnSnapshot[], direct);
312
-
313
- const linkedAnnotated = [
314
- withLabelAnnotations.bind(null, derivedLabels),
315
- withInfoAnnotations.bind(null, derivedTooltips),
316
- withHidenAxesAnnotations.bind(null),
317
- withTableVisualAnnotations.bind(null, visibilityByColId, orderByColId),
318
- (cols: TableColumnSnapshot[]) =>
319
- cols.map((lc) => ({ ...lc, linkerPath: annotateLinkerPath(derivedLabels, lc.linkerPath) })),
320
- ].reduce((cols, fn) => fn(cols) as TableColumnSnapshot[], linked);
297
+ const directAnnotated = withVariantColumns(direct, (cols) =>
298
+ withTableVisualAnnotations(
299
+ visibilityByColId,
300
+ orderByColId,
301
+ withInfoAnnotations(derivedTooltips, withLabelAnnotations(derivedLabels, cols)),
302
+ ),
303
+ );
304
+
305
+ const linkedAnnotated = withVariantColumns(linked, (cols) =>
306
+ withTableVisualAnnotations(
307
+ visibilityByColId,
308
+ orderByColId,
309
+ withHidenAxesAnnotations(
310
+ withInfoAnnotations(derivedTooltips, withLabelAnnotations(derivedLabels, cols)),
311
+ ),
312
+ ),
313
+ ).map((lc) => ({ ...lc, path: annotateLinkerPath(derivedLabels, lc.path) }));
321
314
 
322
315
  const labelColumnsAnnotated = withLabelAnnotations(derivedLabels, labelColumns);
323
316
 
@@ -327,11 +320,25 @@ function annotateColumnGroups(params: {
327
320
  labels: labelColumnsAnnotated,
328
321
  };
329
322
  }
323
+
324
+ /** Apply a snapshot-array transformation to the inner `column` of each variant. */
325
+ function withVariantColumns<V extends { readonly column: ColumnSnapshot<DiscoveredPColumnId> }>(
326
+ variants: V[],
327
+ fn: (cols: ColumnSnapshot<DiscoveredPColumnId>[]) => ColumnSnapshot<DiscoveredPColumnId>[],
328
+ ): V[] {
329
+ const cols = fn(variants.map((v) => v.column));
330
+ if (cols.length !== variants.length)
331
+ throw new Error(
332
+ `withVariantColumns: fn must preserve array length (got ${cols.length}, expected ${variants.length})`,
333
+ );
334
+ return variants.map((v, i) => ({ ...v, column: cols[i] }));
335
+ }
336
+
330
337
  function annotateLinkerPath(
331
338
  derivedLabels: Record<string, string>,
332
- path: TableColumnSnapshot["linkerPath"],
333
- ): TableColumnSnapshot["linkerPath"] {
334
- if (isNil(path) || path.length === 0) return path;
339
+ path: TableColumnVariant["path"],
340
+ ): TableColumnVariant["path"] {
341
+ if (path.length === 0) return path;
335
342
  const annotatedLinkers = withHidenAxesAnnotations(
336
343
  withLabelAnnotations(
337
344
  derivedLabels,
@@ -408,27 +415,27 @@ function validateSorting(sorting: PTableSorting[], isValidColumnId: (id: string)
408
415
  }
409
416
 
410
417
  function buildSecondaryGroups(
411
- direct: TableColumnSnapshot[],
412
- linked: TableColumnSnapshot[],
418
+ direct: TableColumnVariant[],
419
+ linked: TableColumnVariant[],
413
420
  labels: PColumn<PColumnDataUniversal>[],
414
421
  ): SecondaryGroup<undefined | PColumnDataUniversal>[] {
415
422
  return [
416
423
  ...direct.map(
417
424
  (c): SecondaryGroup<undefined | PColumnDataUniversal> => ({
418
- entries: [{ column: resolveSnapshot(c), qualifications: c.qualifications?.forHit }],
419
- primaryQualifications: c.qualifications?.forQueries,
425
+ entries: [{ column: resolveSnapshot(c.column), qualifications: c.qualifications.forHit }],
426
+ primaryQualifications: c.qualifications.forQueries,
420
427
  }),
421
428
  ),
422
429
  ...linked.map(
423
430
  (lc): SecondaryGroup<undefined | PColumnDataUniversal> => ({
424
431
  entries: [
425
- ...(lc.linkerPath ?? []).map((s) => ({
432
+ ...lc.path.map((s) => ({
426
433
  column: resolveSnapshot(s.linker),
427
434
  qualifications: s.qualifications,
428
435
  })),
429
- { column: resolveSnapshot(lc), qualifications: lc.qualifications?.forHit },
436
+ { column: resolveSnapshot(lc.column), qualifications: lc.qualifications.forHit },
430
437
  ],
431
- primaryQualifications: lc.qualifications?.forQueries,
438
+ primaryQualifications: lc.qualifications.forQueries,
432
439
  }),
433
440
  ),
434
441
  ...labels.map(
@@ -480,9 +487,12 @@ function buildVisibleColumns(
480
487
  hiddenColumns: Set<PObjectId>,
481
488
  originalLabelColumns: PColumn<PColumnDataUniversal>[],
482
489
  ): VisibleColumns {
483
- const direct = annotated.direct.filter((c) => !hiddenColumns.has(c.id));
484
- const linked = annotated.linked.filter((c) => !hiddenColumns.has(c.id));
485
- const labels = getMatchingLabelColumns([...direct, ...linked], originalLabelColumns);
490
+ const direct = annotated.direct.filter((c) => !hiddenColumns.has(c.column.id));
491
+ const linked = annotated.linked.filter((c) => !hiddenColumns.has(c.column.id));
492
+ const labels = getMatchingLabelColumns(
493
+ [...direct, ...linked].map((v) => v.column),
494
+ originalLabelColumns,
495
+ );
486
496
  return { direct, linked, labels };
487
497
  }
488
498
 
@@ -496,21 +506,21 @@ function resolveSnapshot(
496
506
  /** Remap column references in sorting entries. */
497
507
  function remapSortingColumnIds(
498
508
  sorting: Nil | PTableSorting[],
499
- columns: TableColumnSnapshot[],
509
+ columns: TableColumnVariant[],
500
510
  ): Nil | PTableSorting[] {
501
511
  return sorting?.map((s) => {
502
512
  if (s.column.type === "axis") return s; // Axis references are unaffected by column ID remapping
503
513
 
504
514
  const id = s.column.id;
505
515
  const column =
506
- columns.find((c) => (c.originalId ?? c.id) === id) ??
516
+ columns.find((c) => (c.originalId ?? c.column.id) === id) ??
507
517
  throwError(`Column ID "${id}" in sorting does not match any discovered column`);
508
518
 
509
519
  return {
510
520
  ...s,
511
521
  column: {
512
522
  type: "column",
513
- id: column.id,
523
+ id: column.column.id,
514
524
  },
515
525
  };
516
526
  });
@@ -521,7 +531,7 @@ type PlDataTableFilterNode = FilterSpecNode<PlDataTableFilterSpecLeaf>;
521
531
  /** Remap column references in a filter tree. */
522
532
  function remapFilterColumnIds(
523
533
  filters: Nil | PlDataTableFilters,
524
- columns: TableColumnSnapshot[],
534
+ columns: TableColumnVariant[],
525
535
  ): Nil | PlDataTableFilters {
526
536
  if (isNil(filters)) return filters;
527
537
 
@@ -533,12 +543,12 @@ function remapFilterColumnIds(
533
543
 
534
544
  const originalId = parsed.id;
535
545
  const column =
536
- columns.find((c) => (c.originalId ?? c.id) === originalId) ??
546
+ columns.find((c) => (c.originalId ?? c.column.id) === originalId) ??
537
547
  throwError(`Column ID "${parsed.id}" in filters does not match any discovered column`);
538
548
 
539
549
  return canonicalizeJson<PTableColumnId>({
540
550
  type: "column",
541
- id: column.id,
551
+ id: column.column.id,
542
552
  });
543
553
  };
544
554
 
@@ -3,7 +3,7 @@ import { createDiscoveredPColumnId, isPlRef } from "@milaboratories/pl-model-com
3
3
  import type { RenderCtxBase } from "../../../render";
4
4
  import type {
5
5
  ColumnSource,
6
- ColumnMatch,
6
+ ColumnVariant,
7
7
  RelaxedColumnSelector,
8
8
  ColumnSnapshotProvider,
9
9
  ColumnSnapshot,
@@ -12,7 +12,7 @@ import { ColumnCollectionBuilder } from "../../../columns";
12
12
  import { toColumnSnapshotProvider } from "../../../columns/column_snapshot_provider";
13
13
  import { collectCtxColumnSnapshotProviders } from "../../../columns/ctx_column_sources";
14
14
  import { throwError } from "@milaboratories/helpers";
15
- import type { ColumnsSelectorConfig, TableColumnSnapshot } from "./createPlDataTableV3";
15
+ import type { ColumnsSelectorConfig, TableColumnVariant } from "./createPlDataTableV3";
16
16
  import { isNil } from "es-toolkit";
17
17
 
18
18
  export type DiscoverTableColumnOptions = {
@@ -21,11 +21,11 @@ export type DiscoverTableColumnOptions = {
21
21
  selector: ColumnsSelectorConfig;
22
22
  };
23
23
 
24
- /** Discover columns from sources/anchors and normalize into a flat DiscoveredColumn list. */
24
+ /** Discover columns from sources/anchors and normalize into a flat TableColumnVariant list. */
25
25
  export function discoverTableColumnSnaphots(
26
26
  ctx: RenderCtxBase,
27
27
  options: DiscoverTableColumnOptions,
28
- ): TableColumnSnapshot[] | undefined {
28
+ ): TableColumnVariant[] | undefined {
29
29
  // Resolve PlRef anchors to PColumnSpec
30
30
  const resolvedOptions = {
31
31
  ...options,
@@ -43,7 +43,7 @@ export function discoverTableColumnSnaphots(
43
43
  if (collection === undefined) return undefined;
44
44
 
45
45
  try {
46
- const columns = collection.findColumns({
46
+ const variants = collection.findColumnVariants({
47
47
  ...(resolvedOptions.selector ?? {}),
48
48
  exclude: [
49
49
  ...(Array.isArray(resolvedOptions.selector?.exclude)
@@ -55,7 +55,7 @@ export function discoverTableColumnSnaphots(
55
55
  ],
56
56
  });
57
57
  const anchors = collection.getAnchors();
58
- return mapToDiscoveredColumns(columns, anchors);
58
+ return mapToTableColumnVariants(variants, anchors);
59
59
  } finally {
60
60
  collection.dispose();
61
61
  }
@@ -93,43 +93,40 @@ function resolveProviders(
93
93
  : collectCtxColumnSnapshotProviders(ctx);
94
94
  }
95
95
 
96
- /** Map matched columns into a flat DiscoveredColumn list with deduped IDs. */
97
- function mapToDiscoveredColumns(
98
- matched: readonly ColumnMatch[],
96
+ /** Map column variants into TableColumnVariant list with anchor-derived isPrimary flag. */
97
+ function mapToTableColumnVariants(
98
+ variants: readonly ColumnVariant[],
99
99
  anchors: Map<string, ColumnSnapshot<PObjectId>>,
100
- ): TableColumnSnapshot[] {
100
+ ): TableColumnVariant[] {
101
101
  const columnIdToAnchorName = new Map<PObjectId, string>(
102
102
  Array.from(anchors.entries(), ([key, { id }]) => [id, key] as const),
103
103
  );
104
104
 
105
- return matched.flatMap((match) => {
106
- const snap = match.column;
107
- const isPrimary = columnIdToAnchorName.get(match.column.id) !== undefined;
105
+ return variants.map((variant): TableColumnVariant => {
106
+ const snap = variant.column;
107
+ const isPrimary = columnIdToAnchorName.get(snap.id) !== undefined;
108
108
 
109
- return match.variants.map((variant): TableColumnSnapshot => {
110
- const discoveredId = createDiscoveredPColumnId({
111
- column: snap.id,
112
- path: variant.path.map((p) => ({
113
- type: "linker",
114
- column: p.linker.id,
115
- qualifications: p.qualifications,
116
- })),
117
- columnQualifications: variant.qualifications.forHit,
118
- queriesQualifications: variant.qualifications.forQueries,
119
- });
120
- return {
109
+ const discoveredId = createDiscoveredPColumnId({
110
+ column: snap.id,
111
+ path: variant.path.map((p) => ({
112
+ type: "linker",
113
+ column: p.linker.id,
114
+ qualifications: p.qualifications,
115
+ })),
116
+ columnQualifications: variant.qualifications.forHit,
117
+ queriesQualifications: variant.qualifications.forQueries,
118
+ });
119
+ return {
120
+ column: {
121
121
  id: discoveredId,
122
- isPrimary,
123
-
124
- originalId: snap.id,
125
122
  spec: snap.spec,
126
123
  data: snap.data,
127
124
  dataStatus: snap.dataStatus,
128
-
129
- linkerPath: variant.path,
130
- qualifications: variant.qualifications,
131
- distinctiveQualifications: variant.distinctiveQualifications,
132
- };
133
- });
125
+ },
126
+ path: variant.path,
127
+ qualifications: variant.qualifications,
128
+ originalId: snap.id,
129
+ isPrimary,
130
+ };
134
131
  });
135
132
  }
@@ -239,7 +239,7 @@ describe("deriveAxisLabels via deriveAllLabels", () => {
239
239
  makeLabelableColumn(
240
240
  "c1",
241
241
  { name: "shared", annotations: { [Annotation.Label]: "Cluster size" } },
242
- [{ spec: linkerSpec }],
242
+ [{ linker: { id: "lk" as PObjectId, spec: linkerSpec } as never, qualifications: [] }],
243
243
  ),
244
244
  makeLabelableColumn("c2", {
245
245
  name: "shared",
@@ -229,7 +229,8 @@ export function withHidenAxesAnnotations<T extends { readonly spec: PColumnSpec
229
229
  export type LabelableColumn = {
230
230
  readonly id: PObjectId;
231
231
  readonly spec: PColumnSpec;
232
- readonly linkerPath?: { spec: PColumnSpec }[];
232
+ readonly linkerPath?: MatchVariant["path"];
233
+ readonly qualifications?: MatchQualifications;
233
234
  };
234
235
 
235
236
  /** Derive labels for all table elements: columns via deriveDistinctLabels, axes from label columns. */
@@ -240,7 +241,15 @@ export function deriveAllLabels(options: {
240
241
  }): Record<string, string> {
241
242
  const { columns, labelColumns, deriveLabelsOptions } = options;
242
243
  const axisLabels = deriveAxisLabels(columns, labelColumns);
243
- const columnLabels = deriveDistinctLabels(columns, deriveLabelsOptions).reduce(
244
+ const entries = columns.map((c) => ({
245
+ spec: c.spec,
246
+ linkerPath: c.linkerPath?.map((step) => ({
247
+ spec: step.linker.spec,
248
+ qualifications: step.qualifications,
249
+ })),
250
+ qualifications: c.qualifications,
251
+ }));
252
+ const columnLabels = deriveDistinctLabels(entries, deriveLabelsOptions).reduce(
244
253
  (acc, label, index) => ((acc[columns[index].id] = label), acc),
245
254
  {} as Record<string, string>,
246
255
  );
@@ -273,7 +282,6 @@ export type TooltipableColumn = {
273
282
  readonly originalId: PObjectId;
274
283
  readonly linkerPath?: MatchVariant["path"];
275
284
  readonly qualifications?: MatchQualifications;
276
- readonly distinctiveQualifications?: MatchQualifications;
277
285
  };
278
286
 
279
287
  /** Derive origin tooltips for columns whose qualifications or linker path carry info. */
@@ -297,7 +305,6 @@ export function deriveAllTooltips(options: {
297
305
  spec: c.spec,
298
306
  linkerPath: c.linkerPath,
299
307
  qualifications: c.qualifications,
300
- distinctiveQualifications: c.distinctiveQualifications,
301
308
  variantIndex,
302
309
  variantCount,
303
310
  });