@platforma-sdk/model 1.63.12 → 1.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/columns/column_collection_builder.cjs +105 -92
- package/dist/columns/column_collection_builder.cjs.map +1 -1
- package/dist/columns/column_collection_builder.d.ts +13 -12
- package/dist/columns/column_collection_builder.d.ts.map +1 -1
- package/dist/columns/column_collection_builder.js +107 -94
- package/dist/columns/column_collection_builder.js.map +1 -1
- package/dist/columns/column_selector.cjs +8 -80
- package/dist/columns/column_selector.cjs.map +1 -1
- package/dist/columns/column_selector.d.ts +6 -14
- package/dist/columns/column_selector.d.ts.map +1 -1
- package/dist/columns/column_selector.js +6 -77
- package/dist/columns/column_selector.js.map +1 -1
- package/dist/columns/column_snapshot.cjs +3 -3
- package/dist/columns/column_snapshot.cjs.map +1 -1
- package/dist/columns/column_snapshot.d.ts +3 -3
- package/dist/columns/column_snapshot.d.ts.map +1 -1
- package/dist/columns/column_snapshot.js +3 -3
- package/dist/columns/column_snapshot.js.map +1 -1
- package/dist/columns/column_snapshot_provider.cjs +1 -1
- package/dist/columns/column_snapshot_provider.cjs.map +1 -1
- package/dist/columns/column_snapshot_provider.d.ts +8 -8
- package/dist/columns/column_snapshot_provider.d.ts.map +1 -1
- package/dist/columns/column_snapshot_provider.js +1 -1
- package/dist/columns/column_snapshot_provider.js.map +1 -1
- package/dist/columns/ctx_column_sources.cjs.map +1 -1
- package/dist/columns/ctx_column_sources.d.ts +2 -1
- package/dist/columns/ctx_column_sources.d.ts.map +1 -1
- package/dist/columns/ctx_column_sources.js.map +1 -1
- package/dist/columns/expand_by_partition.cjs +106 -0
- package/dist/columns/expand_by_partition.cjs.map +1 -0
- package/dist/columns/expand_by_partition.d.ts +33 -0
- package/dist/columns/expand_by_partition.d.ts.map +1 -0
- package/dist/columns/expand_by_partition.js +105 -0
- package/dist/columns/expand_by_partition.js.map +1 -0
- package/dist/columns/index.cjs +1 -0
- package/dist/columns/index.d.ts +4 -3
- package/dist/columns/index.js +1 -0
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.cjs +26 -0
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.cjs.map +1 -0
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.js +25 -0
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.js.map +1 -0
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.cjs +68 -0
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.cjs.map +1 -0
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.js +67 -0
- package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.js.map +1 -0
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.cjs +27 -17
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.d.ts +4 -0
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.d.ts.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.js +28 -18
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs +258 -175
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts +37 -21
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js +261 -175
- package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs +64 -0
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs.map +1 -0
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.d.ts +17 -0
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.d.ts.map +1 -0
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js +63 -0
- package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js.map +1 -0
- package/dist/components/PlDataTable/createPlDataTable/index.cjs +2 -1
- package/dist/components/PlDataTable/createPlDataTable/index.cjs.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/index.d.ts +2 -1
- package/dist/components/PlDataTable/createPlDataTable/index.d.ts.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/index.js +2 -1
- package/dist/components/PlDataTable/createPlDataTable/index.js.map +1 -1
- package/dist/components/PlDataTable/createPlDataTable/utils.cjs +109 -0
- package/dist/components/PlDataTable/createPlDataTable/utils.cjs.map +1 -0
- package/dist/components/PlDataTable/createPlDataTable/utils.d.ts +19 -0
- package/dist/components/PlDataTable/createPlDataTable/utils.d.ts.map +1 -0
- package/dist/components/PlDataTable/createPlDataTable/utils.js +102 -0
- package/dist/components/PlDataTable/createPlDataTable/utils.js.map +1 -0
- package/dist/components/PlDataTable/index.cjs +3 -1
- package/dist/components/PlDataTable/index.d.ts +5 -3
- package/dist/components/PlDataTable/index.js +3 -1
- package/dist/components/PlDataTable/labels.cjs +25 -11
- package/dist/components/PlDataTable/labels.cjs.map +1 -1
- package/dist/components/PlDataTable/labels.js +25 -11
- package/dist/components/PlDataTable/labels.js.map +1 -1
- package/dist/components/PlDataTable/state-migration.cjs +8 -2
- package/dist/components/PlDataTable/state-migration.cjs.map +1 -1
- package/dist/components/PlDataTable/state-migration.d.ts.map +1 -1
- package/dist/components/PlDataTable/state-migration.js +8 -2
- package/dist/components/PlDataTable/state-migration.js.map +1 -1
- package/dist/components/PlDataTable/typesV5.d.ts +23 -15
- package/dist/components/PlDataTable/typesV5.d.ts.map +1 -1
- package/dist/components/index.cjs +3 -1
- package/dist/components/index.d.ts +4 -2
- package/dist/components/index.js +3 -1
- package/dist/index.cjs +13 -9
- package/dist/index.d.ts +9 -7
- package/dist/index.js +6 -4
- package/dist/labels/derive_distinct_labels.cjs +39 -27
- package/dist/labels/derive_distinct_labels.cjs.map +1 -1
- package/dist/labels/derive_distinct_labels.d.ts +15 -15
- package/dist/labels/derive_distinct_labels.d.ts.map +1 -1
- package/dist/labels/derive_distinct_labels.js +39 -27
- package/dist/labels/derive_distinct_labels.js.map +1 -1
- package/dist/labels/index.cjs +0 -1
- package/dist/labels/index.d.ts +1 -2
- package/dist/labels/index.js +0 -1
- package/dist/package.cjs +1 -1
- package/dist/package.js +1 -1
- package/dist/render/api.cjs +10 -3
- package/dist/render/api.cjs.map +1 -1
- package/dist/render/api.d.ts +2 -2
- package/dist/render/api.d.ts.map +1 -1
- package/dist/render/api.js +10 -3
- package/dist/render/api.js.map +1 -1
- package/dist/render/util/column_collection.cjs +3 -3
- package/dist/render/util/column_collection.cjs.map +1 -1
- package/dist/render/util/column_collection.d.ts.map +1 -1
- package/dist/render/util/column_collection.js +3 -3
- package/dist/render/util/column_collection.js.map +1 -1
- package/dist/render/util/label.cjs +2 -2
- package/dist/render/util/label.cjs.map +1 -1
- package/dist/render/util/label.js +2 -2
- package/dist/render/util/label.js.map +1 -1
- package/dist/render/util/pcolumn_data.cjs.map +1 -1
- package/dist/render/util/pcolumn_data.d.ts +2 -2
- package/dist/render/util/pcolumn_data.d.ts.map +1 -1
- package/dist/render/util/pcolumn_data.js.map +1 -1
- package/package.json +7 -7
- package/src/columns/column_collection_builder.test.ts +40 -27
- package/src/columns/column_collection_builder.ts +176 -131
- package/src/columns/column_selector.test.ts +17 -399
- package/src/columns/column_selector.ts +14 -127
- package/src/columns/column_snapshot.ts +5 -5
- package/src/columns/column_snapshot_provider.ts +11 -10
- package/src/columns/ctx_column_sources.ts +2 -2
- package/src/columns/expand_by_partition.test.ts +4 -4
- package/src/columns/expand_by_partition.ts +4 -3
- package/src/columns/index.ts +1 -0
- package/src/components/PlDataTable/createPlDataTable/createPTableDefV2.ts +42 -0
- package/src/components/PlDataTable/createPlDataTable/createPTableDefV3.ts +89 -0
- package/src/components/PlDataTable/createPlDataTable/createPlDataTableV2.ts +51 -19
- package/src/components/PlDataTable/createPlDataTable/createPlDataTableV3.ts +500 -313
- package/src/components/PlDataTable/createPlDataTable/discoverColumns.ts +122 -0
- package/src/components/PlDataTable/createPlDataTable/index.ts +4 -2
- package/src/components/PlDataTable/createPlDataTable/utils.test.ts +257 -0
- package/src/components/PlDataTable/createPlDataTable/utils.ts +160 -0
- package/src/components/PlDataTable/index.ts +15 -2
- package/src/components/PlDataTable/labels.ts +29 -18
- package/src/components/PlDataTable/state-migration.ts +6 -1
- package/src/components/PlDataTable/typesV5.ts +25 -12
- package/src/labels/derive_distinct_labels.test.ts +143 -45
- package/src/labels/derive_distinct_labels.ts +102 -49
- package/src/labels/index.ts +0 -1
- package/src/render/api.ts +15 -5
- package/src/render/util/column_collection.ts +4 -3
- package/src/render/util/label.ts +2 -2
- package/src/render/util/pcolumn_data.ts +5 -3
- package/dist/labels/write_labels_to_specs.cjs +0 -14
- package/dist/labels/write_labels_to_specs.cjs.map +0 -1
- package/dist/labels/write_labels_to_specs.d.ts +0 -7
- package/dist/labels/write_labels_to_specs.d.ts.map +0 -1
- package/dist/labels/write_labels_to_specs.js +0 -13
- package/dist/labels/write_labels_to_specs.js.map +0 -1
- package/src/labels/write_labels_to_specs.ts +0 -12
|
@@ -3,7 +3,6 @@ import type {
|
|
|
3
3
|
AxisSpec,
|
|
4
4
|
CanonicalizedJson,
|
|
5
5
|
ListOptionBase,
|
|
6
|
-
PObjectId,
|
|
7
6
|
PTableColumnSpec,
|
|
8
7
|
PTableSorting,
|
|
9
8
|
PColumnIdAndSpec,
|
|
@@ -13,6 +12,7 @@ import type {
|
|
|
13
12
|
PFrameHandle,
|
|
14
13
|
} from "@milaboratories/pl-model-common";
|
|
15
14
|
import type { FilterSpecLeaf } from "../../filters";
|
|
15
|
+
import { Nil } from "@milaboratories/helpers";
|
|
16
16
|
|
|
17
17
|
export type PlTableColumnId = {
|
|
18
18
|
/** Original column spec */
|
|
@@ -63,10 +63,17 @@ export type PlDataTableSheetState = {
|
|
|
63
63
|
};
|
|
64
64
|
|
|
65
65
|
/** Tree-based filter state compatible with PlAdvancedFilter's RootFilter */
|
|
66
|
-
export type
|
|
66
|
+
export type PlDataTableFilterMeta = {
|
|
67
|
+
id: number;
|
|
68
|
+
source?: "table-filter" | "table-search";
|
|
69
|
+
isExpanded?: boolean;
|
|
70
|
+
isSuppressed?: boolean;
|
|
71
|
+
};
|
|
72
|
+
export type PlDataTableFilterSpecLeaf = FilterSpecLeaf<CanonicalizedJson<PTableColumnId>>;
|
|
73
|
+
export type PlDataTableFilters = RootFilterSpec<PlDataTableFilterSpecLeaf>;
|
|
67
74
|
export type PlDataTableFiltersWithMeta = RootFilterSpec<
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
PlDataTableFilterSpecLeaf,
|
|
76
|
+
PlDataTableFilterMeta
|
|
70
77
|
>;
|
|
71
78
|
|
|
72
79
|
export type PlDataTableStateV2CacheEntry = {
|
|
@@ -76,8 +83,10 @@ export type PlDataTableStateV2CacheEntry = {
|
|
|
76
83
|
gridState: PlDataTableGridStateCore;
|
|
77
84
|
/** Sheets state */
|
|
78
85
|
sheetsState: PlDataTableSheetState[];
|
|
79
|
-
/**
|
|
86
|
+
/** User filters state (tree-based, compatible with PlAdvancedFilter) */
|
|
80
87
|
filtersState: null | PlDataTableFiltersWithMeta;
|
|
88
|
+
/** Default filters state from model (snapshot of defaults) */
|
|
89
|
+
defaultFiltersState: null | PlDataTableFiltersWithMeta;
|
|
81
90
|
/** Fast search string */
|
|
82
91
|
searchString?: string;
|
|
83
92
|
};
|
|
@@ -86,14 +95,16 @@ export type PTableParamsV2 =
|
|
|
86
95
|
| {
|
|
87
96
|
sourceId: null;
|
|
88
97
|
hiddenColIds: null;
|
|
89
|
-
filters: null;
|
|
90
98
|
sorting: [];
|
|
99
|
+
filters: null;
|
|
100
|
+
defaultFilters: null;
|
|
91
101
|
}
|
|
92
102
|
| {
|
|
93
103
|
sourceId: string;
|
|
94
|
-
hiddenColIds: null |
|
|
95
|
-
filters: null | PlDataTableFilters;
|
|
104
|
+
hiddenColIds: null | PTableColumnId[];
|
|
96
105
|
sorting: PTableSorting[];
|
|
106
|
+
filters: null | PlDataTableFilters;
|
|
107
|
+
defaultFilters: null | PlDataTableFilters;
|
|
97
108
|
};
|
|
98
109
|
|
|
99
110
|
export type PlDataTableStateV2Normalized = {
|
|
@@ -108,13 +119,15 @@ export type PlDataTableStateV2Normalized = {
|
|
|
108
119
|
/** PlAgDataTable model */
|
|
109
120
|
export type PlDataTableModel = {
|
|
110
121
|
/** DataSource identifier for state management */
|
|
111
|
-
sourceId:
|
|
122
|
+
sourceId: null | string;
|
|
112
123
|
/** p-table including all columns, used to show the full specification of the table */
|
|
113
|
-
fullTableHandle
|
|
124
|
+
fullTableHandle?: PTableHandle;
|
|
114
125
|
/** p-frame handle */
|
|
115
|
-
fullPframeHandle
|
|
126
|
+
fullPframeHandle?: PFrameHandle;
|
|
116
127
|
/** p-table including only visible columns, used to get the data */
|
|
117
|
-
visibleTableHandle
|
|
128
|
+
visibleTableHandle?: PTableHandle;
|
|
129
|
+
/** Default filters from model options, surfaced for UI display */
|
|
130
|
+
defaultFilters?: Nil | PlDataTableFilters;
|
|
118
131
|
};
|
|
119
132
|
|
|
120
133
|
export type CreatePlDataTableOps = {
|
|
@@ -117,17 +117,14 @@ test.each<{ name: string; traces: Trace[]; labels: string[] }>([
|
|
|
117
117
|
labels: ["Unique entry 1", "Unique entry 2"],
|
|
118
118
|
},
|
|
119
119
|
])("test label derivation: $name", ({ traces, labels }) => {
|
|
120
|
-
expect(deriveDistinctLabels(tracesToSpecs(traces))
|
|
121
|
-
expect(
|
|
122
|
-
|
|
123
|
-
)
|
|
120
|
+
expect(deriveDistinctLabels(tracesToSpecs(traces))).toEqual(labels);
|
|
121
|
+
expect(deriveDistinctLabels(tracesToSpecs(traces), { includeNativeLabel: true })).toEqual(
|
|
122
|
+
labels.map((l) => "Label / " + l),
|
|
123
|
+
);
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
test("test fallback to native labels in label derivation", () => {
|
|
127
|
-
expect(deriveDistinctLabels(tracesToSpecs([[], []]))
|
|
128
|
-
"Label",
|
|
129
|
-
"Label",
|
|
130
|
-
]);
|
|
127
|
+
expect(deriveDistinctLabels(tracesToSpecs([[], []]))).toEqual(["Label", "Label"]);
|
|
131
128
|
});
|
|
132
129
|
|
|
133
130
|
test.each<{ name: string; traces: Trace[]; labels: string[] }>([
|
|
@@ -208,7 +205,7 @@ test.each<{ name: string; traces: Trace[]; labels: string[] }>([
|
|
|
208
205
|
labels: ["A", "A", "B"],
|
|
209
206
|
},
|
|
210
207
|
])("test label minimization: $name", ({ traces, labels }) => {
|
|
211
|
-
expect(deriveDistinctLabels(tracesToSpecs(traces))
|
|
208
|
+
expect(deriveDistinctLabels(tracesToSpecs(traces))).toEqual(labels);
|
|
212
209
|
});
|
|
213
210
|
|
|
214
211
|
test.each<{ name: string; traces: Trace[]; labels: string[]; forceTraceElements: string[] }>([
|
|
@@ -274,72 +271,173 @@ test.each<{ name: string; traces: Trace[]; labels: string[]; forceTraceElements:
|
|
|
274
271
|
])(
|
|
275
272
|
"test label derivation with forceTraceElements: $name",
|
|
276
273
|
({ name, traces, labels, forceTraceElements }) => {
|
|
277
|
-
expect(
|
|
278
|
-
deriveDistinctLabels(tracesToSpecs(traces), { forceTraceElements }).map((r) => r.label),
|
|
279
|
-
).toEqual(labels);
|
|
274
|
+
expect(deriveDistinctLabels(tracesToSpecs(traces), { forceTraceElements })).toEqual(labels);
|
|
280
275
|
|
|
281
276
|
if (name === "force element with includeNativeLabel") {
|
|
282
277
|
expect(
|
|
283
278
|
deriveDistinctLabels(tracesToSpecs(traces), {
|
|
284
279
|
forceTraceElements,
|
|
285
280
|
includeNativeLabel: true,
|
|
286
|
-
})
|
|
281
|
+
}),
|
|
287
282
|
).toEqual(labels.map((l) => "Label / " + l));
|
|
288
283
|
}
|
|
289
284
|
},
|
|
290
285
|
);
|
|
291
286
|
|
|
292
|
-
// --- Entry with { spec,
|
|
287
|
+
// --- Entry with { spec, extraTrace } ---
|
|
293
288
|
|
|
294
|
-
test("Entry with
|
|
289
|
+
test("Entry with extraTrace (suffix, default) appends to labels", () => {
|
|
295
290
|
const spec = createSpec({
|
|
296
291
|
annotations: {
|
|
297
292
|
[Annotation.Trace]: JSON.stringify([{ type: "t1", label: "Base" }]),
|
|
298
293
|
},
|
|
299
294
|
});
|
|
300
295
|
const entries: Entry[] = [
|
|
301
|
-
{ spec,
|
|
302
|
-
{ spec,
|
|
296
|
+
{ spec, extraTrace: [{ type: "suffix", label: "S1" }] },
|
|
297
|
+
{ spec, extraTrace: [{ type: "suffix", label: "S2" }] },
|
|
303
298
|
];
|
|
304
|
-
const labels = deriveDistinctLabels(entries)
|
|
305
|
-
expect(labels).toEqual(["
|
|
299
|
+
const labels = deriveDistinctLabels(entries);
|
|
300
|
+
expect(labels).toEqual(["S1", "S2"]);
|
|
306
301
|
});
|
|
307
302
|
|
|
308
|
-
test("Entry with
|
|
303
|
+
test("Entry with extraTrace position prefix prepends to labels", () => {
|
|
309
304
|
const spec = createSpec({
|
|
310
305
|
annotations: {
|
|
311
306
|
[Annotation.Trace]: JSON.stringify([{ type: "t1", label: "Base" }]),
|
|
312
307
|
},
|
|
313
308
|
});
|
|
314
309
|
const entries: Entry[] = [
|
|
315
|
-
{ spec,
|
|
316
|
-
{ spec,
|
|
310
|
+
{ spec, extraTrace: [{ type: "prefix", label: "P1", position: "prefix" }] },
|
|
311
|
+
{ spec, extraTrace: [{ type: "prefix", label: "P2", position: "prefix" }] },
|
|
317
312
|
];
|
|
318
|
-
const labels = deriveDistinctLabels(entries)
|
|
319
|
-
expect(labels).toEqual(["
|
|
313
|
+
const labels = deriveDistinctLabels(entries);
|
|
314
|
+
expect(labels).toEqual(["P1", "P2"]);
|
|
320
315
|
});
|
|
321
316
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
317
|
+
// --- linkerPath ---
|
|
318
|
+
|
|
319
|
+
test("linkerPath appends default 'via' suffix", () => {
|
|
320
|
+
const entries: Entry[] = [
|
|
321
|
+
{
|
|
322
|
+
spec: createSpec({
|
|
323
|
+
annotations: { [Annotation.Trace]: JSON.stringify([{ type: "t1", label: "Col1" }]) },
|
|
324
|
+
}),
|
|
325
|
+
linkerPath: [
|
|
326
|
+
{
|
|
327
|
+
spec: createSpec({
|
|
328
|
+
annotations: { [Annotation.LinkLabel]: "MyLinker" },
|
|
329
|
+
}),
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
spec: createSpec({
|
|
335
|
+
annotations: { [Annotation.Trace]: JSON.stringify([{ type: "t1", label: "Col2" }]) },
|
|
336
|
+
}),
|
|
326
337
|
},
|
|
338
|
+
];
|
|
339
|
+
const labels = deriveDistinctLabels(entries);
|
|
340
|
+
expect(labels).toEqual(["Col1 via MyLinker", "Col2"]);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test("linkerPath with multiple steps joins with ' > '", () => {
|
|
344
|
+
const entries: Entry[] = [
|
|
345
|
+
{
|
|
346
|
+
spec: createSpec({
|
|
347
|
+
annotations: { [Annotation.Trace]: JSON.stringify([{ type: "t1", label: "Col1" }]) },
|
|
348
|
+
}),
|
|
349
|
+
linkerPath: [
|
|
350
|
+
{ spec: createSpec({ annotations: { [Annotation.LinkLabel]: "L1" } }) },
|
|
351
|
+
{ spec: createSpec({ annotations: { [Annotation.LinkLabel]: "L2" } }) },
|
|
352
|
+
],
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
spec: createSpec({
|
|
356
|
+
annotations: { [Annotation.Trace]: JSON.stringify([{ type: "t1", label: "Col2" }]) },
|
|
357
|
+
}),
|
|
358
|
+
},
|
|
359
|
+
];
|
|
360
|
+
const labels = deriveDistinctLabels(entries);
|
|
361
|
+
expect(labels).toEqual(["Col1 via L1 > L2", "Col2"]);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
test("linkerPath skips steps without labels", () => {
|
|
365
|
+
const entries: Entry[] = [
|
|
366
|
+
{
|
|
367
|
+
spec: createSpec({
|
|
368
|
+
annotations: { [Annotation.Trace]: JSON.stringify([{ type: "t1", label: "Col1" }]) },
|
|
369
|
+
}),
|
|
370
|
+
linkerPath: [
|
|
371
|
+
{ spec: createSpec() },
|
|
372
|
+
{ spec: createSpec({ annotations: { [Annotation.LinkLabel]: "L2" } }) },
|
|
373
|
+
],
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
spec: createSpec({
|
|
377
|
+
annotations: { [Annotation.Trace]: JSON.stringify([{ type: "t1", label: "Col2" }]) },
|
|
378
|
+
}),
|
|
379
|
+
},
|
|
380
|
+
];
|
|
381
|
+
const labels = deriveDistinctLabels(entries);
|
|
382
|
+
expect(labels).toEqual(["Col1 via L2", "Col2"]);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
test("linkerPath with custom linkerLabelFormatter", () => {
|
|
386
|
+
const entries: Entry[] = [
|
|
387
|
+
{
|
|
388
|
+
spec: createSpec({
|
|
389
|
+
annotations: { [Annotation.Trace]: JSON.stringify([{ type: "t1", label: "Col1" }]) },
|
|
390
|
+
}),
|
|
391
|
+
linkerPath: [{ spec: createSpec({ annotations: { [Annotation.LinkLabel]: "L1" } }) }],
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
spec: createSpec({
|
|
395
|
+
annotations: { [Annotation.Trace]: JSON.stringify([{ type: "t1", label: "Col2" }]) },
|
|
396
|
+
}),
|
|
397
|
+
},
|
|
398
|
+
];
|
|
399
|
+
const labels = deriveDistinctLabels(entries, {
|
|
400
|
+
linkerLabelFormatter: (linkerLabels) => `[${linkerLabels.join(", ")}]`,
|
|
327
401
|
});
|
|
402
|
+
expect(labels).toEqual(["Col1 [L1]", "Col2"]);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
test("linkerPath with linkerLabelFormatter returning undefined suppresses suffix", () => {
|
|
328
406
|
const entries: Entry[] = [
|
|
329
407
|
{
|
|
330
|
-
spec:
|
|
331
|
-
|
|
332
|
-
|
|
408
|
+
spec: createSpec({
|
|
409
|
+
annotations: { [Annotation.Trace]: JSON.stringify([{ type: "t1", label: "Col1" }]) },
|
|
410
|
+
}),
|
|
411
|
+
linkerPath: [{ spec: createSpec({ annotations: { [Annotation.LinkLabel]: "L1" } }) }],
|
|
333
412
|
},
|
|
334
413
|
{
|
|
335
|
-
spec:
|
|
336
|
-
|
|
337
|
-
|
|
414
|
+
spec: createSpec({
|
|
415
|
+
annotations: { [Annotation.Trace]: JSON.stringify([{ type: "t1", label: "Col2" }]) },
|
|
416
|
+
}),
|
|
338
417
|
},
|
|
339
418
|
];
|
|
340
|
-
const labels = deriveDistinctLabels(entries
|
|
341
|
-
|
|
342
|
-
|
|
419
|
+
const labels = deriveDistinctLabels(entries, {
|
|
420
|
+
linkerLabelFormatter: () => undefined,
|
|
421
|
+
});
|
|
422
|
+
expect(labels).toEqual(["Col1", "Col2"]);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
test("linkerPath falls back to Label when LinkLabel is absent", () => {
|
|
426
|
+
const entries: Entry[] = [
|
|
427
|
+
{
|
|
428
|
+
spec: createSpec({
|
|
429
|
+
annotations: { [Annotation.Trace]: JSON.stringify([{ type: "t1", label: "Col1" }]) },
|
|
430
|
+
}),
|
|
431
|
+
linkerPath: [{ spec: createSpec({ annotations: { [Annotation.Label]: "FallbackLabel" } }) }],
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
spec: createSpec({
|
|
435
|
+
annotations: { [Annotation.Trace]: JSON.stringify([{ type: "t1", label: "Col2" }]) },
|
|
436
|
+
}),
|
|
437
|
+
},
|
|
438
|
+
];
|
|
439
|
+
const labels = deriveDistinctLabels(entries);
|
|
440
|
+
expect(labels).toEqual(["Col1 via FallbackLabel", "Col2"]);
|
|
343
441
|
});
|
|
344
442
|
|
|
345
443
|
// --- addLabelAsSuffix ---
|
|
@@ -349,7 +447,7 @@ test("addLabelAsSuffix places native label at the end", () => {
|
|
|
349
447
|
const labels = deriveDistinctLabels(specs, {
|
|
350
448
|
includeNativeLabel: true,
|
|
351
449
|
addLabelAsSuffix: true,
|
|
352
|
-
})
|
|
450
|
+
});
|
|
353
451
|
expect(labels).toEqual(["L1 / Label", "L2 / Label"]);
|
|
354
452
|
});
|
|
355
453
|
|
|
@@ -370,7 +468,7 @@ test("custom separator is used between label parts", () => {
|
|
|
370
468
|
{ type: "t2", label: "Y" },
|
|
371
469
|
],
|
|
372
470
|
]);
|
|
373
|
-
const labels = deriveDistinctLabels(specs, { separator: " - " })
|
|
471
|
+
const labels = deriveDistinctLabels(specs, { separator: " - " });
|
|
374
472
|
expect(labels).toEqual(["A - X", "A - Y", "B - Y"]);
|
|
375
473
|
});
|
|
376
474
|
|
|
@@ -378,7 +476,7 @@ test("custom separator is used between label parts", () => {
|
|
|
378
476
|
|
|
379
477
|
test("single value gets its trace label", () => {
|
|
380
478
|
const specs = tracesToSpecs([[{ type: "t1", label: "Only" }]]);
|
|
381
|
-
const labels = deriveDistinctLabels(specs)
|
|
479
|
+
const labels = deriveDistinctLabels(specs);
|
|
382
480
|
expect(labels).toEqual(["Only"]);
|
|
383
481
|
});
|
|
384
482
|
|
|
@@ -395,13 +493,13 @@ test("Unlabeled fallback when no trace entries match", () => {
|
|
|
395
493
|
delete spec.annotations![Annotation.Label];
|
|
396
494
|
|
|
397
495
|
const result = deriveDistinctLabels([spec, spec]);
|
|
398
|
-
expect(result.every((r) => r
|
|
496
|
+
expect(result.every((r) => r === "Same")).toBe(true);
|
|
399
497
|
});
|
|
400
498
|
|
|
401
499
|
test("Unlabeled when no traces and no label", () => {
|
|
402
500
|
const spec = createSpec();
|
|
403
501
|
const result = deriveDistinctLabels([spec, spec]);
|
|
404
|
-
expect(result.every((r) => r
|
|
502
|
+
expect(result.every((r) => r === "Unlabeled")).toBe(true);
|
|
405
503
|
});
|
|
406
504
|
|
|
407
505
|
// --- repeated type occurrences (secondaryTypes path) ---
|
|
@@ -418,7 +516,7 @@ test("repeated type occurrences are used as secondary types", () => {
|
|
|
418
516
|
{ type: "t1", label: "B" },
|
|
419
517
|
],
|
|
420
518
|
]);
|
|
421
|
-
const labels = deriveDistinctLabels(specs)
|
|
519
|
+
const labels = deriveDistinctLabels(specs);
|
|
422
520
|
// t1@1 has label "First" for both (same), t1@2 has "A" vs "B" (distinguishing)
|
|
423
521
|
// t1@2 is secondary since it only appears when there are 2 occurrences
|
|
424
522
|
expect(labels).toEqual(["A", "B"]);
|
|
@@ -439,7 +537,7 @@ test("spec without native label uses only trace entries", () => {
|
|
|
439
537
|
},
|
|
440
538
|
}),
|
|
441
539
|
];
|
|
442
|
-
const labels = deriveDistinctLabels(specs)
|
|
540
|
+
const labels = deriveDistinctLabels(specs);
|
|
443
541
|
expect(labels).toEqual(["X", "Y"]);
|
|
444
542
|
});
|
|
445
543
|
|
|
@@ -456,6 +554,6 @@ test("includeNativeLabel with no native label does not break", () => {
|
|
|
456
554
|
},
|
|
457
555
|
}),
|
|
458
556
|
];
|
|
459
|
-
const labels = deriveDistinctLabels(specs, { includeNativeLabel: true })
|
|
557
|
+
const labels = deriveDistinctLabels(specs, { includeNativeLabel: true });
|
|
460
558
|
expect(labels).toEqual(["X", "Y"]);
|
|
461
559
|
});
|
|
@@ -2,32 +2,34 @@ import {
|
|
|
2
2
|
Annotation,
|
|
3
3
|
parseJson,
|
|
4
4
|
readAnnotation,
|
|
5
|
-
type CanonicalizedJson,
|
|
6
5
|
type PObjectSpec,
|
|
6
|
+
type StringifiedJson,
|
|
7
|
+
type Trace,
|
|
7
8
|
} from "@milaboratories/pl-model-common";
|
|
8
9
|
import { throwError } from "@milaboratories/helpers";
|
|
10
|
+
import { isFunction, isNil } from "es-toolkit";
|
|
11
|
+
|
|
12
|
+
export type { Trace, TraceEntry } from "@milaboratories/pl-model-common";
|
|
9
13
|
|
|
10
14
|
const DISTANCE_PENALTY = 0.001;
|
|
11
15
|
const LABEL_TYPE = "__LABEL__";
|
|
12
16
|
const LABEL_TYPE_FULL = "__LABEL__@1";
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
label: string;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
type TraceEntry = {
|
|
20
|
-
id?: string;
|
|
21
|
-
type: string;
|
|
22
|
-
label: string;
|
|
18
|
+
/** SDK-internal trace shape — adds fields used by this algorithm only, not part of the on-disk contract. */
|
|
19
|
+
type ExtendedTraceEntry = Trace[number] & {
|
|
23
20
|
importance?: number;
|
|
21
|
+
position?: "prefix" | "suffix";
|
|
24
22
|
};
|
|
25
23
|
|
|
26
|
-
export type Trace = TraceEntry[];
|
|
27
|
-
|
|
28
24
|
export type Entry =
|
|
29
25
|
| PObjectSpec
|
|
30
|
-
| {
|
|
26
|
+
| {
|
|
27
|
+
spec: PObjectSpec;
|
|
28
|
+
/** Extra trace entries merged with the base trace from annotations. */
|
|
29
|
+
extraTrace?: ExtendedTraceEntry[];
|
|
30
|
+
/** Linker steps traversed to discover this column; used to append "via $linkLabel" to derived labels. */
|
|
31
|
+
linkerPath?: { spec: PObjectSpec }[];
|
|
32
|
+
};
|
|
31
33
|
|
|
32
34
|
export type DeriveLabelsOptions = {
|
|
33
35
|
/** Separator to use between label parts (" / " by default) */
|
|
@@ -38,18 +40,33 @@ export type DeriveLabelsOptions = {
|
|
|
38
40
|
includeNativeLabel?: boolean;
|
|
39
41
|
/** Trace elements list that will be forced to be included in the label. */
|
|
40
42
|
forceTraceElements?: string[];
|
|
43
|
+
/** Custom formatter for linker path suffix. Receives the array of linker labels from the full traversal chain,
|
|
44
|
+
* the column spec, and the column index.
|
|
45
|
+
* If returns undefined, no linker suffix is appended. By default labels are joined with " > " and prefixed with "via ". */
|
|
46
|
+
linkerLabelFormatter?: (
|
|
47
|
+
linkerLabels: string[],
|
|
48
|
+
spec: PObjectSpec,
|
|
49
|
+
index: number,
|
|
50
|
+
) => string | undefined;
|
|
41
51
|
};
|
|
42
52
|
|
|
43
|
-
export function deriveDistinctLabels
|
|
44
|
-
values: T[],
|
|
45
|
-
options: DeriveLabelsOptions = {},
|
|
46
|
-
): WithLabel<T>[] {
|
|
53
|
+
export function deriveDistinctLabels(values: Entry[], options: DeriveLabelsOptions = {}): string[] {
|
|
47
54
|
const forceTraceElements =
|
|
48
55
|
options.forceTraceElements !== undefined && options.forceTraceElements.length > 0
|
|
49
56
|
? new Set(options.forceTraceElements)
|
|
50
57
|
: undefined;
|
|
51
58
|
const separator = options.separator ?? " / ";
|
|
52
59
|
|
|
60
|
+
// Collect per-entry linker suffixes before disambiguation
|
|
61
|
+
const linkerSuffixes = values.map((v, i) => {
|
|
62
|
+
const spec = "spec" in v && typeof v.spec === "object" ? v.spec : (v as PObjectSpec);
|
|
63
|
+
const linkerLabels = extractLinkerLabels(v);
|
|
64
|
+
if (linkerLabels.length === 0) return undefined;
|
|
65
|
+
return isFunction(options.linkerLabelFormatter)
|
|
66
|
+
? options.linkerLabelFormatter(linkerLabels, spec, i)
|
|
67
|
+
: `via ${linkerLabels.join(" > ")}`;
|
|
68
|
+
});
|
|
69
|
+
|
|
53
70
|
// Phase 1: enrich each value with parsed trace
|
|
54
71
|
const records = values.map((v) => enrichRecord(v, options));
|
|
55
72
|
|
|
@@ -65,7 +82,12 @@ export function deriveDistinctLabels<T extends Entry>(
|
|
|
65
82
|
if (mainTypes.length === 0) {
|
|
66
83
|
if (secondaryTypes.length !== 0)
|
|
67
84
|
throw new Error("Non-empty secondary types list while main types list is empty.");
|
|
68
|
-
|
|
85
|
+
|
|
86
|
+
return applyLinkerSuffixes(
|
|
87
|
+
build(new Set(LABEL_TYPE_FULL), true) ??
|
|
88
|
+
throwError("Failed to derive labels using native column labels"),
|
|
89
|
+
linkerSuffixes,
|
|
90
|
+
);
|
|
69
91
|
}
|
|
70
92
|
|
|
71
93
|
// Phase 4: search for minimal type set that produces unique labels
|
|
@@ -96,7 +118,10 @@ export function deriveDistinctLabels<T extends Entry>(
|
|
|
96
118
|
options,
|
|
97
119
|
separator,
|
|
98
120
|
);
|
|
99
|
-
return
|
|
121
|
+
return applyLinkerSuffixes(
|
|
122
|
+
build(minimized, false) ?? throwError("Failed to derive unique labels"),
|
|
123
|
+
linkerSuffixes,
|
|
124
|
+
);
|
|
100
125
|
}
|
|
101
126
|
|
|
102
127
|
additionalType++;
|
|
@@ -116,29 +141,55 @@ export function deriveDistinctLabels<T extends Entry>(
|
|
|
116
141
|
options,
|
|
117
142
|
separator,
|
|
118
143
|
);
|
|
119
|
-
return
|
|
144
|
+
return applyLinkerSuffixes(
|
|
145
|
+
build(minimized, true) ?? throwError("Failed to derive unique labels"),
|
|
146
|
+
linkerSuffixes,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Apply pre-formatted linker suffixes to labels that have them. */
|
|
151
|
+
function applyLinkerSuffixes(labels: string[], suffixes: (string | undefined)[]): string[] {
|
|
152
|
+
return labels.map((label, i) => (isNil(suffixes[i]) ? label : `${label} ${suffixes[i]}`));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Extract linker labels from every step of the linkers path. */
|
|
156
|
+
function extractLinkerLabels(entry: Entry): string[] {
|
|
157
|
+
if (!("spec" in entry) || typeof entry.spec !== "object") return [];
|
|
158
|
+
const path = entry.linkerPath;
|
|
159
|
+
if (path === undefined || path.length === 0) return [];
|
|
160
|
+
const labels: string[] = [];
|
|
161
|
+
for (const step of path) {
|
|
162
|
+
const label = (
|
|
163
|
+
readAnnotation(step.spec, Annotation.LinkLabel) ?? readAnnotation(step.spec, Annotation.Label)
|
|
164
|
+
)?.trim();
|
|
165
|
+
if (label !== undefined && label.length > 0) {
|
|
166
|
+
labels.push(label);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return labels;
|
|
120
170
|
}
|
|
121
171
|
|
|
122
172
|
// --- Pure helpers ---
|
|
123
|
-
type FullTraceEntry =
|
|
173
|
+
type FullTraceEntry = ExtendedTraceEntry & { fullType: string; occurrenceIndex: number };
|
|
124
174
|
|
|
125
|
-
type EnrichedRecord
|
|
126
|
-
value: T;
|
|
175
|
+
type EnrichedRecord = {
|
|
127
176
|
fullTrace: FullTraceEntry[];
|
|
128
177
|
};
|
|
129
178
|
|
|
130
179
|
function extractSpecAndTrace(entry: Entry): {
|
|
131
180
|
spec: PObjectSpec;
|
|
132
|
-
|
|
133
|
-
|
|
181
|
+
extraTrace: ExtendedTraceEntry[] | undefined;
|
|
182
|
+
linkerPath: { spec: PObjectSpec }[] | undefined;
|
|
134
183
|
} {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
184
|
+
const isEnriched = "spec" in entry && typeof entry.spec === "object";
|
|
185
|
+
return {
|
|
186
|
+
spec: isEnriched ? entry.spec : (entry as PObjectSpec),
|
|
187
|
+
extraTrace: isEnriched ? entry.extraTrace : undefined,
|
|
188
|
+
linkerPath: isEnriched ? entry.linkerPath : undefined,
|
|
189
|
+
};
|
|
139
190
|
}
|
|
140
191
|
|
|
141
|
-
function buildFullTrace(trace:
|
|
192
|
+
function buildFullTrace(trace: ExtendedTraceEntry[]): FullTraceEntry[] {
|
|
142
193
|
const result: FullTraceEntry[] = [];
|
|
143
194
|
const occurrences = new Map<string, number>();
|
|
144
195
|
|
|
@@ -157,15 +208,17 @@ function buildFullTrace(trace: TraceEntry[]): FullTraceEntry[] {
|
|
|
157
208
|
return result;
|
|
158
209
|
}
|
|
159
210
|
|
|
160
|
-
function enrichRecord
|
|
161
|
-
const { spec,
|
|
211
|
+
function enrichRecord(value: Entry, options: DeriveLabelsOptions): EnrichedRecord {
|
|
212
|
+
const { spec, extraTrace } = extractSpecAndTrace(value);
|
|
162
213
|
|
|
163
214
|
const label = readAnnotation(spec, Annotation.Label);
|
|
164
|
-
const traceStr = readAnnotation(spec, Annotation.Trace)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
215
|
+
const traceStr = readAnnotation(spec, Annotation.Trace);
|
|
216
|
+
const baseTrace = traceStr
|
|
217
|
+
? (parseJson(traceStr as StringifiedJson<ExtendedTraceEntry[]>) ?? [])
|
|
218
|
+
: [];
|
|
219
|
+
const prefixExtra = extraTrace?.filter((e) => e.position === "prefix") ?? [];
|
|
220
|
+
const suffixExtra = extraTrace?.filter((e) => e.position !== "prefix") ?? [];
|
|
221
|
+
const trace = [...prefixExtra, ...baseTrace, ...suffixExtra];
|
|
169
222
|
|
|
170
223
|
if (label !== undefined) {
|
|
171
224
|
const labelEntry = { label, type: LABEL_TYPE, importance: -2 };
|
|
@@ -173,7 +226,7 @@ function enrichRecord<T extends Entry>(value: T, options: DeriveLabelsOptions):
|
|
|
173
226
|
else trace.splice(0, 0, labelEntry);
|
|
174
227
|
}
|
|
175
228
|
|
|
176
|
-
return {
|
|
229
|
+
return { fullTrace: buildFullTrace(trace) };
|
|
177
230
|
}
|
|
178
231
|
|
|
179
232
|
type TypeStats = {
|
|
@@ -181,7 +234,7 @@ type TypeStats = {
|
|
|
181
234
|
countByType: Map<string, number>;
|
|
182
235
|
};
|
|
183
236
|
|
|
184
|
-
function collectTypeStats
|
|
237
|
+
function collectTypeStats(records: EnrichedRecord[]): TypeStats {
|
|
185
238
|
const importances = new Map<string, number>();
|
|
186
239
|
const countByType = new Map<string, number>();
|
|
187
240
|
|
|
@@ -220,14 +273,14 @@ function classifyTypes(
|
|
|
220
273
|
return { mainTypes, secondaryTypes };
|
|
221
274
|
}
|
|
222
275
|
|
|
223
|
-
function buildLabels
|
|
224
|
-
records: EnrichedRecord
|
|
276
|
+
function buildLabels(
|
|
277
|
+
records: EnrichedRecord[],
|
|
225
278
|
includedTypes: Set<string>,
|
|
226
279
|
forceTraceElements: Set<string> | undefined,
|
|
227
280
|
separator: string,
|
|
228
281
|
force: boolean,
|
|
229
|
-
):
|
|
230
|
-
const result:
|
|
282
|
+
): string[] | undefined {
|
|
283
|
+
const result: string[] = [];
|
|
231
284
|
|
|
232
285
|
for (const r of records) {
|
|
233
286
|
const parts: string[] = [];
|
|
@@ -239,24 +292,24 @@ function buildLabels<T>(
|
|
|
239
292
|
|
|
240
293
|
if (parts.length === 0) {
|
|
241
294
|
if (!force) return undefined;
|
|
242
|
-
result.push(
|
|
295
|
+
result.push("Unlabeled");
|
|
243
296
|
continue;
|
|
244
297
|
}
|
|
245
298
|
|
|
246
|
-
result.push(
|
|
299
|
+
result.push(parts.join(separator));
|
|
247
300
|
}
|
|
248
301
|
|
|
249
302
|
return result;
|
|
250
303
|
}
|
|
251
304
|
|
|
252
|
-
function countUniqueLabels
|
|
305
|
+
function countUniqueLabels(result: string[] | undefined): number {
|
|
253
306
|
if (result === undefined) return 0;
|
|
254
|
-
return new Set(result
|
|
307
|
+
return new Set(result).size;
|
|
255
308
|
}
|
|
256
309
|
|
|
257
|
-
function minimizeTypeSet
|
|
310
|
+
function minimizeTypeSet(
|
|
258
311
|
typeSet: Set<string>,
|
|
259
|
-
records: EnrichedRecord
|
|
312
|
+
records: EnrichedRecord[],
|
|
260
313
|
stats: TypeStats,
|
|
261
314
|
forceTraceElements: Set<string> | undefined,
|
|
262
315
|
options: DeriveLabelsOptions,
|
package/src/labels/index.ts
CHANGED