@platforma-sdk/model 1.70.0 → 1.72.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.
Files changed (42) hide show
  1. package/dist/block_model.cjs +3 -0
  2. package/dist/block_model.cjs.map +1 -1
  3. package/dist/block_model.d.ts +2 -0
  4. package/dist/block_model.d.ts.map +1 -1
  5. package/dist/block_model.js +2 -0
  6. package/dist/block_model.js.map +1 -1
  7. package/dist/block_model_legacy.cjs +4 -1
  8. package/dist/block_model_legacy.cjs.map +1 -1
  9. package/dist/block_model_legacy.d.ts.map +1 -1
  10. package/dist/block_model_legacy.js +3 -1
  11. package/dist/block_model_legacy.js.map +1 -1
  12. package/dist/columns/column_snapshot_provider.cjs +26 -2
  13. package/dist/columns/column_snapshot_provider.cjs.map +1 -1
  14. package/dist/columns/column_snapshot_provider.d.ts +2 -1
  15. package/dist/columns/column_snapshot_provider.d.ts.map +1 -1
  16. package/dist/columns/column_snapshot_provider.js +25 -2
  17. package/dist/columns/column_snapshot_provider.js.map +1 -1
  18. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs +40 -20
  19. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs.map +1 -1
  20. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts.map +1 -1
  21. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js +41 -21
  22. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js.map +1 -1
  23. package/dist/components/PlDataTable/createPlDataTable/utils.cjs +15 -0
  24. package/dist/components/PlDataTable/createPlDataTable/utils.cjs.map +1 -1
  25. package/dist/components/PlDataTable/createPlDataTable/utils.js +15 -1
  26. package/dist/components/PlDataTable/createPlDataTable/utils.js.map +1 -1
  27. package/dist/package.cjs +1 -1
  28. package/dist/package.js +1 -1
  29. package/dist/render/api.cjs +3 -14
  30. package/dist/render/api.cjs.map +1 -1
  31. package/dist/render/api.d.ts.map +1 -1
  32. package/dist/render/api.js +4 -15
  33. package/dist/render/api.js.map +1 -1
  34. package/dist/render/index.cjs +1 -1
  35. package/dist/render/index.js +1 -1
  36. package/package.json +8 -8
  37. package/src/block_model.ts +2 -0
  38. package/src/block_model_legacy.ts +2 -0
  39. package/src/columns/column_snapshot_provider.ts +30 -14
  40. package/src/components/PlDataTable/createPlDataTable/createPlDataTableV3.ts +58 -43
  41. package/src/components/PlDataTable/createPlDataTable/utils.ts +25 -2
  42. package/src/render/api.ts +5 -17
@@ -1,11 +1,9 @@
1
1
  import type { PObjectId } from "@milaboratories/pl-model-common";
2
- import { PColumn } from "@milaboratories/pl-model-common";
2
+ import { isDataInfo, PColumn, visitDataInfo } from "@milaboratories/pl-model-common";
3
3
  import { TreeNodeAccessor } from "../render/accessor";
4
4
  import type { PColumnDataUniversal } from "../render/internal";
5
5
  import type { ColumnDataStatus, ColumnSnapshot } from "./column_snapshot";
6
6
 
7
- // --- ColumnProvider ---
8
-
9
7
  /**
10
8
  * Data source interface for column enumeration.
11
9
  *
@@ -22,8 +20,6 @@ export interface ColumnSnapshotProvider {
22
20
  isColumnListComplete(): boolean;
23
21
  }
24
22
 
25
- // --- ColumnSource ---
26
-
27
23
  /**
28
24
  * Union of types that can serve as column sources for helpers and builders.
29
25
  * Does NOT include TreeNodeAccessor — call `.toColumnSource()` on it first.
@@ -33,8 +29,6 @@ export type ColumnSource =
33
29
  | ColumnSnapshot<PObjectId>[]
34
30
  | PColumn<PColumnDataUniversal | undefined>[];
35
31
 
36
- // --- ArrayColumnProvider ---
37
-
38
32
  /**
39
33
  * Simple provider wrapping an array of PColumns.
40
34
  * Always complete, data status always 'ready'.
@@ -46,8 +40,8 @@ export class ArrayColumnProvider implements ColumnSnapshotProvider {
46
40
  this.columns = columns.map((col) => ({
47
41
  id: col.id,
48
42
  spec: col.spec,
49
- dataStatus: "ready" as const,
50
43
  data: { get: () => col.data },
44
+ dataStatus: this.getStatus(col.data),
51
45
  }));
52
46
  }
53
47
 
@@ -58,9 +52,35 @@ export class ArrayColumnProvider implements ColumnSnapshotProvider {
58
52
  isColumnListComplete(): boolean {
59
53
  return true;
60
54
  }
61
- }
62
55
 
63
- // --- SnapshotColumnProvider ---
56
+ protected getStatus(
57
+ d: undefined | PColumnDataUniversal | (() => undefined | PColumnDataUniversal),
58
+ ): ColumnDataStatus {
59
+ if (d == null) {
60
+ return "absent";
61
+ }
62
+ if (typeof d === "function") {
63
+ return this.getStatus(d());
64
+ }
65
+ if (d instanceof TreeNodeAccessor) {
66
+ if (d.getIsReadyOrError()) return "ready";
67
+ if (d.getIsFinal()) return "absent";
68
+ return "computing";
69
+ }
70
+ if (isDataInfo(d)) {
71
+ let ready = true;
72
+ let final = true;
73
+ visitDataInfo(d, (v) => {
74
+ ready &&= v.getIsReadyOrError();
75
+ final &&= v.getIsFinal();
76
+ });
77
+ if (ready) return "ready";
78
+ if (final) return "absent";
79
+ return "computing";
80
+ }
81
+ return "ready";
82
+ }
83
+ }
64
84
 
65
85
  /**
66
86
  * Provider wrapping an array of ColumnSnapshots.
@@ -78,8 +98,6 @@ export class SnapshotColumnProvider implements ColumnSnapshotProvider {
78
98
  }
79
99
  }
80
100
 
81
- // --- OutputColumnProvider ---
82
-
83
101
  export interface OutputColumnProviderOpts {
84
102
  /** When true and the accessor is final, columns with no ready data get status 'absent'. */
85
103
  allowPermanentAbsence?: boolean;
@@ -133,8 +151,6 @@ export class OutputColumnProvider implements ColumnSnapshotProvider {
133
151
  }
134
152
  }
135
153
 
136
- // --- Source normalization ---
137
-
138
154
  /** Checks if a value is a ColumnSnapshotProvider (duck-typing). */
139
155
  export function isColumnSnapshotProvider(source: unknown): source is ColumnSnapshotProvider {
140
156
  return (
@@ -32,6 +32,7 @@ import {
32
32
  withLabelAnnotations,
33
33
  withTableVisualAnnotations,
34
34
  withInfoAnnotations,
35
+ withDataStatusAnnotations,
35
36
  } from "./utils";
36
37
  import type { PrimaryEntry, SecondaryGroup } from "./createPTableDefV3";
37
38
  import { createPTableDefV3 } from "./createPTableDefV3";
@@ -79,8 +80,6 @@ export type ColumnVisibilityRule = {
79
80
 
80
81
  export type ColumnMatcher = (spec: PColumnSpec) => boolean;
81
82
 
82
- // Main Function
83
-
84
83
  export function createPlDataTableV3<A, U>(
85
84
  ctx: RenderCtxBase<A, U>,
86
85
  options: createPlDataTableOptionsV3,
@@ -138,17 +137,18 @@ export function createPlDataTableV3<A, U>(
138
137
  ]);
139
138
 
140
139
  const remapedDefaultFilters = remapFilterColumnIds(options.filters, discovered);
141
- const filters = concatFilters(
142
- state.pTableParams.filters,
143
- state.pTableParams.defaultFilters ?? remapedDefaultFilters,
140
+ const filters = filterFilters(
141
+ concatFilters(
142
+ state.pTableParams.filters,
143
+ state.pTableParams.defaultFilters ?? remapedDefaultFilters,
144
+ ),
145
+ columnIsAvailable,
144
146
  );
145
- validateFilters(filters, columnIsAvailable);
146
147
 
147
- const sorting = resolveSorting(
148
- state.pTableParams.sorting,
149
- remapSortingColumnIds(options.sorting, discovered),
148
+ const sorting = filterSorting(
149
+ resolveSorting(state.pTableParams.sorting, remapSortingColumnIds(options.sorting, discovered)),
150
+ columnIsAvailable,
150
151
  );
151
- validateSorting(sorting, columnIsAvailable);
152
152
 
153
153
  const primaryEntries: PrimaryEntry<undefined | PColumnDataUniversal>[] = primarySnapshots.map(
154
154
  (v) => ({ column: resolveSnapshot(v.column) }),
@@ -274,6 +274,7 @@ function annotateColumnGroups(params: {
274
274
  const directAnnotated = liftToVariantColumns(
275
275
  direct,
276
276
  flow(
277
+ (cols) => withDataStatusAnnotations(cols),
277
278
  (cols) => withLabelAnnotations(derivedLabels, cols),
278
279
  (cols) => withInfoAnnotations(derivedTooltips, cols),
279
280
  (cols) => withTableVisualAnnotations(visibilityByColId, orderByColId, cols),
@@ -283,6 +284,7 @@ function annotateColumnGroups(params: {
283
284
  const linkedAnnotated = liftToVariantColumns(
284
285
  linked,
285
286
  flow(
287
+ (cols) => withDataStatusAnnotations(cols),
286
288
  (cols) => withHidenAxesAnnotations(cols),
287
289
  (cols) => withLabelAnnotations(derivedLabels, cols),
288
290
  (cols) => withInfoAnnotations(derivedTooltips, cols),
@@ -344,19 +346,35 @@ function createColumnValidationById(
344
346
  };
345
347
  }
346
348
 
347
- /** Validate that all column references in filters exist in the table. */
348
- function validateFilters(
349
+ /** Drop filter leaves whose column references are not available in the table. */
350
+ function filterFilters(
349
351
  filters: Nil | PlDataTableFilters,
350
352
  isValidColumnId: (id: string) => boolean,
351
- ): void {
352
- if (filters == null) return;
353
- const filterColumns = collectFilterSpecColumns(filters);
354
- const firstInvalid = filterColumns.find((col) => !isValidColumnId(col));
355
- if (firstInvalid !== undefined) {
356
- throw new Error(
357
- `Invalid filter column ${firstInvalid}: column reference does not match the table columns`,
358
- );
359
- }
353
+ ): Nil | PlDataTableFilters {
354
+ if (isNil(filters)) return filters;
355
+
356
+ const isLeafValid = (leaf: PlDataTableFilterSpecLeaf): boolean => {
357
+ if (leaf.type === undefined) return true;
358
+ if ("column" in leaf && !isValidColumnId(leaf.column)) return false;
359
+ if ("rhs" in leaf && !isValidColumnId(leaf.rhs)) return false;
360
+ return true;
361
+ };
362
+
363
+ const prune = (node: PlDataTableFilterNode): Nil | PlDataTableFilterNode => {
364
+ if (node.type === "and" || node.type === "or") {
365
+ const kept = node.filters
366
+ .map((f) => prune(f))
367
+ .filter((f): f is PlDataTableFilterNode => !isNil(f));
368
+ return { type: node.type, filters: kept };
369
+ }
370
+ if (node.type === "not") {
371
+ const inner = prune(node.filter);
372
+ return isNil(inner) ? undefined : { type: "not", filter: inner };
373
+ }
374
+ return isLeafValid(node) ? node : undefined;
375
+ };
376
+
377
+ return prune(filters) as Nil | PlDataTableFilters;
360
378
  }
361
379
 
362
380
  /** Merge two filter trees into one AND-combined tree. Returns the non-nil one if the other is nil. */
@@ -377,16 +395,12 @@ function resolveSorting(
377
395
  return (isEmpty(userSorting) ? defaultSorting : userSorting) ?? [];
378
396
  }
379
397
 
380
- /** Validate that all column references in sorting exist in the table. */
381
- function validateSorting(sorting: PTableSorting[], isValidColumnId: (id: string) => boolean): void {
382
- const firstInvalid = sorting.find(
383
- (s) => !isValidColumnId(canonicalizeJson<PTableColumnId>(s.column)),
384
- );
385
- if (firstInvalid !== undefined) {
386
- throw new Error(
387
- `Invalid sorting column ${JSON.stringify(firstInvalid.column)}: column reference does not match the table columns`,
388
- );
389
- }
398
+ /** Drop sorting entries whose column is not available in the table. */
399
+ function filterSorting(
400
+ sorting: PTableSorting[],
401
+ isValidColumnId: (id: string) => boolean,
402
+ ): PTableSorting[] {
403
+ return sorting.filter((s) => isValidColumnId(canonicalizeJson<PTableColumnId>(s.column)));
390
404
  }
391
405
 
392
406
  function buildSecondaryGroups(
@@ -473,21 +487,22 @@ function remapSortingColumnIds(
473
487
  sorting: Nil | PTableSorting[],
474
488
  columns: TableColumnVariant[],
475
489
  ): Nil | PTableSorting[] {
476
- return sorting?.map((s) => {
477
- if (s.column.type === "axis") return s; // Axis references are unaffected by column ID remapping
490
+ return sorting?.flatMap((s) => {
491
+ if (s.column.type === "axis") return [s]; // Axis references are unaffected by column ID remapping
478
492
 
479
493
  const id = s.column.id;
480
- const column =
481
- columns.find((c) => (c.originalId ?? c.column.id) === id) ??
482
- throwError(`Column ID "${id}" in sorting does not match any discovered column`);
483
-
484
- return {
485
- ...s,
486
- column: {
487
- type: "column",
488
- id: column.column.id,
494
+ const column = columns.find((c) => (c.originalId ?? c.column.id) === id);
495
+ if (column === undefined) return [];
496
+
497
+ return [
498
+ {
499
+ ...s,
500
+ column: {
501
+ type: "column" as const,
502
+ id: column.column.id,
503
+ },
489
504
  },
490
- };
505
+ ];
491
506
  });
492
507
  }
493
508
 
@@ -17,7 +17,7 @@ import {
17
17
  deriveDistinctTooltips,
18
18
  type TooltipEntry,
19
19
  } from "../../../labels/derive_distinct_tooltips";
20
- import type { MatchQualifications, MatchVariant } from "../../../columns";
20
+ import type { ColumnDataStatus, MatchQualifications, MatchVariant } from "../../../columns";
21
21
  import type { ColumnMatcher, ColumnOrderRule, ColumnVisibilityRule } from "./createPlDataTableV3";
22
22
  import type { ColumnSelector } from "../../../columns";
23
23
  import { ArrayColumnProvider, ColumnCollectionBuilder } from "../../../columns";
@@ -130,7 +130,10 @@ function dedupeById(columns: RuleColumn[]): RuleColumn[] {
130
130
  * For each axis in column specs: writes derived axis label into AxisSpec annotations.
131
131
  */
132
132
  export function withLabelAnnotations<
133
- T extends { readonly id: PObjectId; readonly spec: PColumnSpec },
133
+ T extends {
134
+ readonly id: PObjectId;
135
+ readonly spec: PColumnSpec;
136
+ },
134
137
  >(derivedLabels: undefined | Record<string, string>, columns: T[]): T[] {
135
138
  if (derivedLabels === undefined) return columns;
136
139
  return columns.map((col) => {
@@ -153,6 +156,26 @@ export function withLabelAnnotations<
153
156
  });
154
157
  }
155
158
 
159
+ export function withDataStatusAnnotations<
160
+ T extends {
161
+ readonly spec: PColumnSpec;
162
+ readonly dataStatus: ColumnDataStatus;
163
+ },
164
+ >(columns: T[]): T[] {
165
+ return columns.map((col) => {
166
+ return {
167
+ ...col,
168
+ spec: {
169
+ ...col.spec,
170
+ annotations: {
171
+ ...col.spec.annotations,
172
+ [Annotation.DataStatus]: col.dataStatus,
173
+ },
174
+ },
175
+ } as T;
176
+ });
177
+ }
178
+
156
179
  /**
157
180
  * Writes effective display properties (OrderPriority, Visibility) from precomputed rule maps
158
181
  * into column annotations. Returns new column objects — originals are not mutated.
package/src/render/api.ts CHANGED
@@ -29,10 +29,8 @@ import type {
29
29
  } from "@milaboratories/pl-model-common";
30
30
  import {
31
31
  AnchoredIdDeriver,
32
- collectSpecQueryColumns,
33
32
  ensurePColumn,
34
33
  parseJson,
35
- extractAllColumns,
36
34
  isDataInfo,
37
35
  isPColumn,
38
36
  isPColumnSpec,
@@ -46,6 +44,7 @@ import {
46
44
  readAnnotation,
47
45
  withEnrichments,
48
46
  legacyColumnSelectorsToPredicate,
47
+ extractAllColumns,
49
48
  } from "@milaboratories/pl-model-common";
50
49
  import canonicalize from "canonicalize";
51
50
  import type { Optional } from "utility-types";
@@ -70,9 +69,9 @@ import type { LabelDerivationOps } from "./util/label";
70
69
  import { deriveLabels } from "./util/label";
71
70
  import type { APColumnSelectorWithSplit } from "./util/split_selectors";
72
71
  import { patchInSetFilters } from "./util/pframe_upgraders";
73
- import { allPColumnsReady } from "./util/pcolumn_data";
74
72
  import type { PColumnDataUniversal } from "./internal";
75
73
  import { getService } from "../services";
74
+ import { allPColumnsReady } from "./util";
76
75
 
77
76
  /**
78
77
  * Helper function to match domain objects
@@ -97,7 +96,7 @@ export type UniversalColumnOption = { label: string; value: SUniversalPColumnId
97
96
  * @returns Transformed data compatible with platform API
98
97
  */
99
98
  function transformPColumnData(
100
- data: PColumn<PColumnDataUniversal> | PColumnLazy<PColumnDataUniversal>,
99
+ data: PColumn<undefined | PColumnDataUniversal> | PColumnLazy<undefined | PColumnDataUniversal>,
101
100
  ): PColumn<PColumnValues | AccessorHandle | DataInfo<AccessorHandle>> {
102
101
  return mapPObjectData(data, (d) => {
103
102
  if (d instanceof TreeNodeAccessor) {
@@ -105,7 +104,7 @@ function transformPColumnData(
105
104
  } else if (isDataInfo(d)) {
106
105
  return mapDataInfo(d, (accessor) => accessor.handle);
107
106
  } else {
108
- return d;
107
+ return d ?? [];
109
108
  }
110
109
  });
111
110
  }
@@ -663,8 +662,6 @@ export abstract class RenderCtxBase<Args = unknown, Data = unknown> {
663
662
  PColumn<undefined | PColumnDataUniversal> | PColumnLazy<undefined | PColumnDataUniversal>
664
663
  >,
665
664
  ): PFrameHandle | undefined {
666
- if (!allPColumnsReady(def)) return undefined;
667
- this.verifyInlineAndExplicitColumnsSupport(def);
668
665
  return this.ctx.createPFrame(def.map((c) => transformPColumnData(c)));
669
666
  }
670
667
 
@@ -709,16 +706,7 @@ export abstract class RenderCtxBase<Args = unknown, Data = unknown> {
709
706
  public createPTableV2(
710
707
  def: PTableDefV2<PColumn<undefined | PColumnDataUniversal>>,
711
708
  ): PTableHandle | undefined {
712
- const columns = collectSpecQueryColumns(def.query);
713
- if (!allPColumnsReady(columns)) return undefined;
714
- this.verifyInlineAndExplicitColumnsSupport(columns);
715
- return this.ctx.createPTableV2(
716
- mapPTableDefV2(def, (po) => {
717
- if (po.data === undefined)
718
- throw new Error("unreachable: column data undefined after readiness check");
719
- return transformPColumnData({ id: po.id, spec: po.spec, data: po.data });
720
- }),
721
- );
709
+ return this.ctx.createPTableV2(mapPTableDefV2(def, (po) => transformPColumnData(po)));
722
710
  }
723
711
 
724
712
  /** @deprecated scheduled for removal from SDK */