@platforma-sdk/model 1.75.10 → 1.76.5

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 (27) hide show
  1. package/dist/components/PlDatasetSelector/build_dataset_options.cjs +5 -1
  2. package/dist/components/PlDatasetSelector/build_dataset_options.cjs.map +1 -1
  3. package/dist/components/PlDatasetSelector/build_dataset_options.d.ts +6 -1
  4. package/dist/components/PlDatasetSelector/build_dataset_options.d.ts.map +1 -1
  5. package/dist/components/PlDatasetSelector/build_dataset_options.js +5 -1
  6. package/dist/components/PlDatasetSelector/build_dataset_options.js.map +1 -1
  7. package/dist/components/PlDatasetSelector/filter_discovery.cjs +21 -30
  8. package/dist/components/PlDatasetSelector/filter_discovery.cjs.map +1 -1
  9. package/dist/components/PlDatasetSelector/filter_discovery.d.ts +20 -20
  10. package/dist/components/PlDatasetSelector/filter_discovery.d.ts.map +1 -1
  11. package/dist/components/PlDatasetSelector/filter_discovery.js +21 -30
  12. package/dist/components/PlDatasetSelector/filter_discovery.js.map +1 -1
  13. package/dist/components/PlDatasetSelector/index.d.ts +1 -1
  14. package/dist/components/index.d.ts +1 -1
  15. package/dist/filters/converters/filterToQuery.cjs +7 -8
  16. package/dist/filters/converters/filterToQuery.cjs.map +1 -1
  17. package/dist/filters/converters/filterToQuery.js +7 -8
  18. package/dist/filters/converters/filterToQuery.js.map +1 -1
  19. package/dist/index.d.ts +2 -2
  20. package/dist/package.cjs +1 -1
  21. package/dist/package.js +1 -1
  22. package/package.json +8 -8
  23. package/src/components/PlDatasetSelector/build_dataset_options.ts +10 -2
  24. package/src/components/PlDatasetSelector/filter_discovery.test.ts +116 -19
  25. package/src/components/PlDatasetSelector/filter_discovery.ts +46 -39
  26. package/src/filters/converters/filterToQuery.test.ts +5 -6
  27. package/src/filters/converters/filterToQuery.ts +6 -7
@@ -37,7 +37,11 @@ function buildDatasetOptions(ctx, opts) {
37
37
  });
38
38
  enrichmentCollection = enrichmentSources !== void 0 ? new require_column_collection_builder.ColumnCollectionBuilder(pframeSpec).addSources(enrichmentSources).build({ anchors: { main: datasetSpec } }) : void 0;
39
39
  const filterMatches = require_filter_discovery.findFilterColumns(filterCollection).filter((m) => filterPredicate(m.column.spec));
40
- const filters = filterMatches.length === 0 ? void 0 : require_filter_discovery.filterMatchesToOptions(filterMatches, refMap, opts?.labelOptions);
40
+ const filters = filterMatches.length === 0 ? void 0 : require_filter_discovery.filterMatchesToOptions(filterMatches, {
41
+ refsByObjectId: refMap,
42
+ datasetSpec,
43
+ labelOptions: opts?.labelOptions
44
+ });
41
45
  let enrichments;
42
46
  if (enrichmentCollection && withEnrichments) {
43
47
  const enrichmentVariants = require_enrichment_discovery.findEnrichmentColumns(enrichmentCollection, {
@@ -1 +1 @@
1
- {"version":3,"file":"build_dataset_options.cjs","names":["buildRefMap","ResultPoolColumnSnapshotProvider","collectCtxColumnSnapshotProviders","ColumnCollectionBuilder","findFilterColumns","filterMatchesToOptions","findEnrichmentColumns","enrichmentVariantsToRefs"],"sources":["../../../src/components/PlDatasetSelector/build_dataset_options.ts"],"sourcesContent":["import type { MultiColumnSelector, Option, PObjectSpec } from \"@milaboratories/pl-model-common\";\nimport { multiColumnSelectorsToPredicate } from \"@milaboratories/pl-model-common\";\nimport type { DeriveLabelsOptions } from \"../../labels/derive_distinct_labels\";\nimport type { RenderCtxBase } from \"../../render\";\nimport type { AnchoredColumnCollection } from \"../../columns/column_collection_builder\";\nimport { ColumnCollectionBuilder } from \"../../columns/column_collection_builder\";\nimport {\n ResultPoolColumnSnapshotProvider,\n collectCtxColumnSnapshotProviders,\n} from \"../../columns/ctx_column_sources\";\nimport type { DatasetOption } from \"./dataset_selection\";\nimport { buildRefMap, filterMatchesToOptions, findFilterColumns } from \"./filter_discovery\";\nimport { enrichmentVariantsToRefs, findEnrichmentColumns } from \"./enrichment_discovery\";\n\ntype SpecPredicateOption =\n | MultiColumnSelector\n | MultiColumnSelector[]\n | ((spec: PObjectSpec) => boolean);\n\nfunction toPredicate(opt: SpecPredicateOption | undefined): (spec: PObjectSpec) => boolean {\n if (opt === undefined) return () => true;\n return typeof opt === \"function\" ? opt : multiColumnSelectorsToPredicate(opt);\n}\n\nexport type BuildDatasetOptions = {\n /** Which result pool columns qualify as datasets. Defaults to all. */\n primary?: SpecPredicateOption;\n /**\n * Restricts which result pool columns are considered as filters. Intersected\n * with the built-in `pl7.app/isSubset: \"true\"` constraint. Defaults to\n * accept-all.\n */\n filter?: SpecPredicateOption;\n /** Formatting options for filter labels. */\n labelOptions?: DeriveLabelsOptions;\n /**\n * Enables enrichment discovery and filters hits attached to\n * `DatasetOption.enrichments`. Use `() => true` to accept all; omit to disable.\n */\n withEnrichments?: SpecPredicateOption;\n /** Maximum linker hops considered. Only used when `withEnrichments` is set. */\n enrichmentMaxHops?: number;\n};\n\n/**\n * Usage:\n * ```ts\n * .output(\"datasetOptions\", (ctx) => buildDatasetOptions(ctx))\n * ```\n */\nexport function buildDatasetOptions(\n ctx: RenderCtxBase,\n opts?: BuildDatasetOptions,\n): DatasetOption[] | undefined {\n const primaryPredicate = toPredicate(opts?.primary);\n const filterPredicate = toPredicate(opts?.filter);\n\n const options = ctx.resultPool.getOptions(primaryPredicate, { refsWithEnrichments: true });\n if (options.length === 0) return [];\n\n const refMap = buildRefMap(ctx.resultPool.getSpecs().entries);\n const pframeSpec = ctx.getService(\"pframeSpec\");\n\n const withEnrichments = opts?.withEnrichments ?? false;\n const filterSource = new ResultPoolColumnSnapshotProvider(ctx.resultPool);\n // Hoisted out of the per-option loop: collectCtxColumnSnapshotProviders\n // walks the entire output tree, so calling it once per dataset option would\n // be O(N × tree).\n const enrichmentSources = withEnrichments ? collectCtxColumnSnapshotProviders(ctx) : undefined;\n\n return options.map((primary: Option): DatasetOption => {\n const datasetSpec = ctx.resultPool.getPColumnSpecByRef(primary.ref);\n if (!datasetSpec) return { primary };\n\n // Allocations happen inside try so a throw on the second build()\n // still disposes the first collection.\n let filterCollection: AnchoredColumnCollection | undefined;\n let enrichmentCollection: AnchoredColumnCollection | undefined;\n try {\n // ResultPoolColumnSnapshotProvider is always complete;\n // allowPartialColumnList narrows the return type to non-undefined.\n filterCollection = new ColumnCollectionBuilder(pframeSpec)\n .addSource(filterSource)\n .build({ anchors: { main: datasetSpec }, allowPartialColumnList: true });\n\n enrichmentCollection =\n enrichmentSources !== undefined\n ? new ColumnCollectionBuilder(pframeSpec)\n .addSources(enrichmentSources)\n .build({ anchors: { main: datasetSpec } })\n : undefined;\n\n const filterMatches = findFilterColumns(filterCollection).filter((m) =>\n filterPredicate(m.column.spec),\n );\n const filters =\n filterMatches.length === 0\n ? undefined\n : filterMatchesToOptions(filterMatches, refMap, opts?.labelOptions);\n\n let enrichments;\n if (enrichmentCollection && withEnrichments) {\n const enrichmentVariants = findEnrichmentColumns(enrichmentCollection, {\n maxHops: opts?.enrichmentMaxHops,\n ...(typeof withEnrichments === \"function\"\n ? { predicate: withEnrichments }\n : { include: withEnrichments }),\n });\n if (enrichmentVariants.length > 0) {\n enrichments = enrichmentVariantsToRefs(enrichmentVariants, opts?.labelOptions);\n }\n }\n\n return {\n primary,\n ...(filters !== undefined && filters.length > 0 ? { filters } : {}),\n ...(enrichments !== undefined && enrichments.length > 0 ? { enrichments } : {}),\n };\n } finally {\n filterCollection?.dispose();\n enrichmentCollection?.dispose();\n }\n });\n}\n"],"mappings":";;;;;;;AAmBA,SAAS,YAAY,KAAsE;AACzF,KAAI,QAAQ,KAAA,EAAW,cAAa;AACpC,QAAO,OAAO,QAAQ,aAAa,OAAA,GAAA,gCAAA,iCAAsC,IAAI;;;;;;;;AA6B/E,SAAgB,oBACd,KACA,MAC6B;CAC7B,MAAM,mBAAmB,YAAY,MAAM,QAAQ;CACnD,MAAM,kBAAkB,YAAY,MAAM,OAAO;CAEjD,MAAM,UAAU,IAAI,WAAW,WAAW,kBAAkB,EAAE,qBAAqB,MAAM,CAAC;AAC1F,KAAI,QAAQ,WAAW,EAAG,QAAO,EAAE;CAEnC,MAAM,SAASA,yBAAAA,YAAY,IAAI,WAAW,UAAU,CAAC,QAAQ;CAC7D,MAAM,aAAa,IAAI,WAAW,aAAa;CAE/C,MAAM,kBAAkB,MAAM,mBAAmB;CACjD,MAAM,eAAe,IAAIC,2BAAAA,iCAAiC,IAAI,WAAW;CAIzE,MAAM,oBAAoB,kBAAkBC,2BAAAA,kCAAkC,IAAI,GAAG,KAAA;AAErF,QAAO,QAAQ,KAAK,YAAmC;EACrD,MAAM,cAAc,IAAI,WAAW,oBAAoB,QAAQ,IAAI;AACnE,MAAI,CAAC,YAAa,QAAO,EAAE,SAAS;EAIpC,IAAI;EACJ,IAAI;AACJ,MAAI;AAGF,sBAAmB,IAAIC,kCAAAA,wBAAwB,WAAW,CACvD,UAAU,aAAa,CACvB,MAAM;IAAE,SAAS,EAAE,MAAM,aAAa;IAAE,wBAAwB;IAAM,CAAC;AAE1E,0BACE,sBAAsB,KAAA,IAClB,IAAIA,kCAAAA,wBAAwB,WAAW,CACpC,WAAW,kBAAkB,CAC7B,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,EAAE,CAAC,GAC5C,KAAA;GAEN,MAAM,gBAAgBC,yBAAAA,kBAAkB,iBAAiB,CAAC,QAAQ,MAChE,gBAAgB,EAAE,OAAO,KAAK,CAC/B;GACD,MAAM,UACJ,cAAc,WAAW,IACrB,KAAA,IACAC,yBAAAA,uBAAuB,eAAe,QAAQ,MAAM,aAAa;GAEvE,IAAI;AACJ,OAAI,wBAAwB,iBAAiB;IAC3C,MAAM,qBAAqBC,6BAAAA,sBAAsB,sBAAsB;KACrE,SAAS,MAAM;KACf,GAAI,OAAO,oBAAoB,aAC3B,EAAE,WAAW,iBAAiB,GAC9B,EAAE,SAAS,iBAAiB;KACjC,CAAC;AACF,QAAI,mBAAmB,SAAS,EAC9B,eAAcC,6BAAAA,yBAAyB,oBAAoB,MAAM,aAAa;;AAIlF,UAAO;IACL;IACA,GAAI,YAAY,KAAA,KAAa,QAAQ,SAAS,IAAI,EAAE,SAAS,GAAG,EAAE;IAClE,GAAI,gBAAgB,KAAA,KAAa,YAAY,SAAS,IAAI,EAAE,aAAa,GAAG,EAAE;IAC/E;YACO;AACR,qBAAkB,SAAS;AAC3B,yBAAsB,SAAS;;GAEjC"}
1
+ {"version":3,"file":"build_dataset_options.cjs","names":["buildRefMap","ResultPoolColumnSnapshotProvider","collectCtxColumnSnapshotProviders","ColumnCollectionBuilder","findFilterColumns","filterMatchesToOptions","findEnrichmentColumns","enrichmentVariantsToRefs"],"sources":["../../../src/components/PlDatasetSelector/build_dataset_options.ts"],"sourcesContent":["import type { MultiColumnSelector, Option, PObjectSpec } from \"@milaboratories/pl-model-common\";\nimport { multiColumnSelectorsToPredicate } from \"@milaboratories/pl-model-common\";\nimport type { DeriveLabelsOptions } from \"../../labels/derive_distinct_labels\";\nimport type { RenderCtxBase } from \"../../render\";\nimport type { AnchoredColumnCollection } from \"../../columns/column_collection_builder\";\nimport { ColumnCollectionBuilder } from \"../../columns/column_collection_builder\";\nimport {\n ResultPoolColumnSnapshotProvider,\n collectCtxColumnSnapshotProviders,\n} from \"../../columns/ctx_column_sources\";\nimport type { DatasetOption } from \"./dataset_selection\";\nimport { buildRefMap, filterMatchesToOptions, findFilterColumns } from \"./filter_discovery\";\nimport { enrichmentVariantsToRefs, findEnrichmentColumns } from \"./enrichment_discovery\";\n\ntype SpecPredicateOption =\n | MultiColumnSelector\n | MultiColumnSelector[]\n | ((spec: PObjectSpec) => boolean);\n\nfunction toPredicate(opt: SpecPredicateOption | undefined): (spec: PObjectSpec) => boolean {\n if (opt === undefined) return () => true;\n return typeof opt === \"function\" ? opt : multiColumnSelectorsToPredicate(opt);\n}\n\nexport type BuildDatasetOptions = {\n /** Which result pool columns qualify as datasets. Defaults to all. */\n primary?: SpecPredicateOption;\n /**\n * Restricts which result pool columns are considered as filters. Intersected\n * with the built-in `pl7.app/isSubset: \"true\"` constraint. Defaults to\n * accept-all.\n */\n filter?: SpecPredicateOption;\n /**\n * Formatting options forwarded to label derivation for both filter and\n * enrichment rows. `formatters.native` on the filter path is overridden\n * — see `FilterMatchOptions.labelOptions`.\n */\n labelOptions?: DeriveLabelsOptions;\n /**\n * Enables enrichment discovery and filters hits attached to\n * `DatasetOption.enrichments`. Use `() => true` to accept all; omit to disable.\n */\n withEnrichments?: SpecPredicateOption;\n /** Maximum linker hops considered. Only used when `withEnrichments` is set. */\n enrichmentMaxHops?: number;\n};\n\n/**\n * Usage:\n * ```ts\n * .output(\"datasetOptions\", (ctx) => buildDatasetOptions(ctx))\n * ```\n */\nexport function buildDatasetOptions(\n ctx: RenderCtxBase,\n opts?: BuildDatasetOptions,\n): DatasetOption[] | undefined {\n const primaryPredicate = toPredicate(opts?.primary);\n const filterPredicate = toPredicate(opts?.filter);\n\n const options = ctx.resultPool.getOptions(primaryPredicate, { refsWithEnrichments: true });\n if (options.length === 0) return [];\n\n const refMap = buildRefMap(ctx.resultPool.getSpecs().entries);\n const pframeSpec = ctx.getService(\"pframeSpec\");\n\n const withEnrichments = opts?.withEnrichments ?? false;\n const filterSource = new ResultPoolColumnSnapshotProvider(ctx.resultPool);\n // Hoisted out of the per-option loop: collectCtxColumnSnapshotProviders\n // walks the entire output tree, so calling it once per dataset option would\n // be O(N × tree).\n const enrichmentSources = withEnrichments ? collectCtxColumnSnapshotProviders(ctx) : undefined;\n\n return options.map((primary: Option): DatasetOption => {\n const datasetSpec = ctx.resultPool.getPColumnSpecByRef(primary.ref);\n if (!datasetSpec) return { primary };\n\n // Allocations happen inside try so a throw on the second build()\n // still disposes the first collection.\n let filterCollection: AnchoredColumnCollection | undefined;\n let enrichmentCollection: AnchoredColumnCollection | undefined;\n try {\n // ResultPoolColumnSnapshotProvider is always complete;\n // allowPartialColumnList narrows the return type to non-undefined.\n filterCollection = new ColumnCollectionBuilder(pframeSpec)\n .addSource(filterSource)\n .build({ anchors: { main: datasetSpec }, allowPartialColumnList: true });\n\n enrichmentCollection =\n enrichmentSources !== undefined\n ? new ColumnCollectionBuilder(pframeSpec)\n .addSources(enrichmentSources)\n .build({ anchors: { main: datasetSpec } })\n : undefined;\n\n const filterMatches = findFilterColumns(filterCollection).filter((m) =>\n filterPredicate(m.column.spec),\n );\n const filters =\n filterMatches.length === 0\n ? undefined\n : filterMatchesToOptions(filterMatches, {\n refsByObjectId: refMap,\n datasetSpec,\n labelOptions: opts?.labelOptions,\n });\n\n let enrichments;\n if (enrichmentCollection && withEnrichments) {\n const enrichmentVariants = findEnrichmentColumns(enrichmentCollection, {\n maxHops: opts?.enrichmentMaxHops,\n ...(typeof withEnrichments === \"function\"\n ? { predicate: withEnrichments }\n : { include: withEnrichments }),\n });\n if (enrichmentVariants.length > 0) {\n enrichments = enrichmentVariantsToRefs(enrichmentVariants, opts?.labelOptions);\n }\n }\n\n return {\n primary,\n ...(filters !== undefined && filters.length > 0 ? { filters } : {}),\n ...(enrichments !== undefined && enrichments.length > 0 ? { enrichments } : {}),\n };\n } finally {\n filterCollection?.dispose();\n enrichmentCollection?.dispose();\n }\n });\n}\n"],"mappings":";;;;;;;AAmBA,SAAS,YAAY,KAAsE;AACzF,KAAI,QAAQ,KAAA,EAAW,cAAa;AACpC,QAAO,OAAO,QAAQ,aAAa,OAAA,GAAA,gCAAA,iCAAsC,IAAI;;;;;;;;AAiC/E,SAAgB,oBACd,KACA,MAC6B;CAC7B,MAAM,mBAAmB,YAAY,MAAM,QAAQ;CACnD,MAAM,kBAAkB,YAAY,MAAM,OAAO;CAEjD,MAAM,UAAU,IAAI,WAAW,WAAW,kBAAkB,EAAE,qBAAqB,MAAM,CAAC;AAC1F,KAAI,QAAQ,WAAW,EAAG,QAAO,EAAE;CAEnC,MAAM,SAASA,yBAAAA,YAAY,IAAI,WAAW,UAAU,CAAC,QAAQ;CAC7D,MAAM,aAAa,IAAI,WAAW,aAAa;CAE/C,MAAM,kBAAkB,MAAM,mBAAmB;CACjD,MAAM,eAAe,IAAIC,2BAAAA,iCAAiC,IAAI,WAAW;CAIzE,MAAM,oBAAoB,kBAAkBC,2BAAAA,kCAAkC,IAAI,GAAG,KAAA;AAErF,QAAO,QAAQ,KAAK,YAAmC;EACrD,MAAM,cAAc,IAAI,WAAW,oBAAoB,QAAQ,IAAI;AACnE,MAAI,CAAC,YAAa,QAAO,EAAE,SAAS;EAIpC,IAAI;EACJ,IAAI;AACJ,MAAI;AAGF,sBAAmB,IAAIC,kCAAAA,wBAAwB,WAAW,CACvD,UAAU,aAAa,CACvB,MAAM;IAAE,SAAS,EAAE,MAAM,aAAa;IAAE,wBAAwB;IAAM,CAAC;AAE1E,0BACE,sBAAsB,KAAA,IAClB,IAAIA,kCAAAA,wBAAwB,WAAW,CACpC,WAAW,kBAAkB,CAC7B,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,EAAE,CAAC,GAC5C,KAAA;GAEN,MAAM,gBAAgBC,yBAAAA,kBAAkB,iBAAiB,CAAC,QAAQ,MAChE,gBAAgB,EAAE,OAAO,KAAK,CAC/B;GACD,MAAM,UACJ,cAAc,WAAW,IACrB,KAAA,IACAC,yBAAAA,uBAAuB,eAAe;IACpC,gBAAgB;IAChB;IACA,cAAc,MAAM;IACrB,CAAC;GAER,IAAI;AACJ,OAAI,wBAAwB,iBAAiB;IAC3C,MAAM,qBAAqBC,6BAAAA,sBAAsB,sBAAsB;KACrE,SAAS,MAAM;KACf,GAAI,OAAO,oBAAoB,aAC3B,EAAE,WAAW,iBAAiB,GAC9B,EAAE,SAAS,iBAAiB;KACjC,CAAC;AACF,QAAI,mBAAmB,SAAS,EAC9B,eAAcC,6BAAAA,yBAAyB,oBAAoB,MAAM,aAAa;;AAIlF,UAAO;IACL;IACA,GAAI,YAAY,KAAA,KAAa,QAAQ,SAAS,IAAI,EAAE,SAAS,GAAG,EAAE;IAClE,GAAI,gBAAgB,KAAA,KAAa,YAAY,SAAS,IAAI,EAAE,aAAa,GAAG,EAAE;IAC/E;YACO;AACR,qBAAkB,SAAS;AAC3B,yBAAsB,SAAS;;GAEjC"}
@@ -12,7 +12,12 @@ type BuildDatasetOptions = {
12
12
  * with the built-in `pl7.app/isSubset: "true"` constraint. Defaults to
13
13
  * accept-all.
14
14
  */
15
- filter?: SpecPredicateOption; /** Formatting options for filter labels. */
15
+ filter?: SpecPredicateOption;
16
+ /**
17
+ * Formatting options forwarded to label derivation for both filter and
18
+ * enrichment rows. `formatters.native` on the filter path is overridden
19
+ * — see `FilterMatchOptions.labelOptions`.
20
+ */
16
21
  labelOptions?: DeriveLabelsOptions;
17
22
  /**
18
23
  * Enables enrichment discovery and filters hits attached to
@@ -1 +1 @@
1
- {"version":3,"file":"build_dataset_options.d.ts","names":[],"sources":["../../../src/components/PlDatasetSelector/build_dataset_options.ts"],"mappings":";;;;;;KAcK,mBAAA,GACD,mBAAA,GACA,mBAAA,OACE,IAAA,EAAM,WAAA;AAAA,KAOA,mBAAA;wEAEV,OAAA,GAAU,mBAAA;EAZY;;;;;EAkBtB,MAAA,GAAS,mBAAA,EAfY;EAiBrB,YAAA,GAAe,mBAAA;EAlBb;;;;EAuBF,eAAA,GAAkB,mBAAA,EAfR;EAiBV,iBAAA;AAAA;;;;;;;iBASc,mBAAA,CACd,GAAA,EAAK,aAAA,EACL,IAAA,GAAO,mBAAA,GACN,aAAA"}
1
+ {"version":3,"file":"build_dataset_options.d.ts","names":[],"sources":["../../../src/components/PlDatasetSelector/build_dataset_options.ts"],"mappings":";;;;;;KAcK,mBAAA,GACD,mBAAA,GACA,mBAAA,OACE,IAAA,EAAM,WAAA;AAAA,KAOA,mBAAA;wEAEV,OAAA,GAAU,mBAAA;EAZY;;;;;EAkBtB,MAAA,GAAS,mBAAA;EAfY;;;;;EAqBrB,YAAA,GAAe,mBAAA;EArBM;AAOvB;;;EAmBE,eAAA,GAAkB,mBAAA,EAXT;EAaT,iBAAA;AAAA;;;;;;;iBASc,mBAAA,CACd,GAAA,EAAK,aAAA,EACL,IAAA,GAAO,mBAAA,GACN,aAAA"}
@@ -36,7 +36,11 @@ function buildDatasetOptions(ctx, opts) {
36
36
  });
37
37
  enrichmentCollection = enrichmentSources !== void 0 ? new ColumnCollectionBuilder(pframeSpec).addSources(enrichmentSources).build({ anchors: { main: datasetSpec } }) : void 0;
38
38
  const filterMatches = findFilterColumns(filterCollection).filter((m) => filterPredicate(m.column.spec));
39
- const filters = filterMatches.length === 0 ? void 0 : filterMatchesToOptions(filterMatches, refMap, opts?.labelOptions);
39
+ const filters = filterMatches.length === 0 ? void 0 : filterMatchesToOptions(filterMatches, {
40
+ refsByObjectId: refMap,
41
+ datasetSpec,
42
+ labelOptions: opts?.labelOptions
43
+ });
40
44
  let enrichments;
41
45
  if (enrichmentCollection && withEnrichments) {
42
46
  const enrichmentVariants = findEnrichmentColumns(enrichmentCollection, {
@@ -1 +1 @@
1
- {"version":3,"file":"build_dataset_options.js","names":[],"sources":["../../../src/components/PlDatasetSelector/build_dataset_options.ts"],"sourcesContent":["import type { MultiColumnSelector, Option, PObjectSpec } from \"@milaboratories/pl-model-common\";\nimport { multiColumnSelectorsToPredicate } from \"@milaboratories/pl-model-common\";\nimport type { DeriveLabelsOptions } from \"../../labels/derive_distinct_labels\";\nimport type { RenderCtxBase } from \"../../render\";\nimport type { AnchoredColumnCollection } from \"../../columns/column_collection_builder\";\nimport { ColumnCollectionBuilder } from \"../../columns/column_collection_builder\";\nimport {\n ResultPoolColumnSnapshotProvider,\n collectCtxColumnSnapshotProviders,\n} from \"../../columns/ctx_column_sources\";\nimport type { DatasetOption } from \"./dataset_selection\";\nimport { buildRefMap, filterMatchesToOptions, findFilterColumns } from \"./filter_discovery\";\nimport { enrichmentVariantsToRefs, findEnrichmentColumns } from \"./enrichment_discovery\";\n\ntype SpecPredicateOption =\n | MultiColumnSelector\n | MultiColumnSelector[]\n | ((spec: PObjectSpec) => boolean);\n\nfunction toPredicate(opt: SpecPredicateOption | undefined): (spec: PObjectSpec) => boolean {\n if (opt === undefined) return () => true;\n return typeof opt === \"function\" ? opt : multiColumnSelectorsToPredicate(opt);\n}\n\nexport type BuildDatasetOptions = {\n /** Which result pool columns qualify as datasets. Defaults to all. */\n primary?: SpecPredicateOption;\n /**\n * Restricts which result pool columns are considered as filters. Intersected\n * with the built-in `pl7.app/isSubset: \"true\"` constraint. Defaults to\n * accept-all.\n */\n filter?: SpecPredicateOption;\n /** Formatting options for filter labels. */\n labelOptions?: DeriveLabelsOptions;\n /**\n * Enables enrichment discovery and filters hits attached to\n * `DatasetOption.enrichments`. Use `() => true` to accept all; omit to disable.\n */\n withEnrichments?: SpecPredicateOption;\n /** Maximum linker hops considered. Only used when `withEnrichments` is set. */\n enrichmentMaxHops?: number;\n};\n\n/**\n * Usage:\n * ```ts\n * .output(\"datasetOptions\", (ctx) => buildDatasetOptions(ctx))\n * ```\n */\nexport function buildDatasetOptions(\n ctx: RenderCtxBase,\n opts?: BuildDatasetOptions,\n): DatasetOption[] | undefined {\n const primaryPredicate = toPredicate(opts?.primary);\n const filterPredicate = toPredicate(opts?.filter);\n\n const options = ctx.resultPool.getOptions(primaryPredicate, { refsWithEnrichments: true });\n if (options.length === 0) return [];\n\n const refMap = buildRefMap(ctx.resultPool.getSpecs().entries);\n const pframeSpec = ctx.getService(\"pframeSpec\");\n\n const withEnrichments = opts?.withEnrichments ?? false;\n const filterSource = new ResultPoolColumnSnapshotProvider(ctx.resultPool);\n // Hoisted out of the per-option loop: collectCtxColumnSnapshotProviders\n // walks the entire output tree, so calling it once per dataset option would\n // be O(N × tree).\n const enrichmentSources = withEnrichments ? collectCtxColumnSnapshotProviders(ctx) : undefined;\n\n return options.map((primary: Option): DatasetOption => {\n const datasetSpec = ctx.resultPool.getPColumnSpecByRef(primary.ref);\n if (!datasetSpec) return { primary };\n\n // Allocations happen inside try so a throw on the second build()\n // still disposes the first collection.\n let filterCollection: AnchoredColumnCollection | undefined;\n let enrichmentCollection: AnchoredColumnCollection | undefined;\n try {\n // ResultPoolColumnSnapshotProvider is always complete;\n // allowPartialColumnList narrows the return type to non-undefined.\n filterCollection = new ColumnCollectionBuilder(pframeSpec)\n .addSource(filterSource)\n .build({ anchors: { main: datasetSpec }, allowPartialColumnList: true });\n\n enrichmentCollection =\n enrichmentSources !== undefined\n ? new ColumnCollectionBuilder(pframeSpec)\n .addSources(enrichmentSources)\n .build({ anchors: { main: datasetSpec } })\n : undefined;\n\n const filterMatches = findFilterColumns(filterCollection).filter((m) =>\n filterPredicate(m.column.spec),\n );\n const filters =\n filterMatches.length === 0\n ? undefined\n : filterMatchesToOptions(filterMatches, refMap, opts?.labelOptions);\n\n let enrichments;\n if (enrichmentCollection && withEnrichments) {\n const enrichmentVariants = findEnrichmentColumns(enrichmentCollection, {\n maxHops: opts?.enrichmentMaxHops,\n ...(typeof withEnrichments === \"function\"\n ? { predicate: withEnrichments }\n : { include: withEnrichments }),\n });\n if (enrichmentVariants.length > 0) {\n enrichments = enrichmentVariantsToRefs(enrichmentVariants, opts?.labelOptions);\n }\n }\n\n return {\n primary,\n ...(filters !== undefined && filters.length > 0 ? { filters } : {}),\n ...(enrichments !== undefined && enrichments.length > 0 ? { enrichments } : {}),\n };\n } finally {\n filterCollection?.dispose();\n enrichmentCollection?.dispose();\n }\n });\n}\n"],"mappings":";;;;;;AAmBA,SAAS,YAAY,KAAsE;AACzF,KAAI,QAAQ,KAAA,EAAW,cAAa;AACpC,QAAO,OAAO,QAAQ,aAAa,MAAM,gCAAgC,IAAI;;;;;;;;AA6B/E,SAAgB,oBACd,KACA,MAC6B;CAC7B,MAAM,mBAAmB,YAAY,MAAM,QAAQ;CACnD,MAAM,kBAAkB,YAAY,MAAM,OAAO;CAEjD,MAAM,UAAU,IAAI,WAAW,WAAW,kBAAkB,EAAE,qBAAqB,MAAM,CAAC;AAC1F,KAAI,QAAQ,WAAW,EAAG,QAAO,EAAE;CAEnC,MAAM,SAAS,YAAY,IAAI,WAAW,UAAU,CAAC,QAAQ;CAC7D,MAAM,aAAa,IAAI,WAAW,aAAa;CAE/C,MAAM,kBAAkB,MAAM,mBAAmB;CACjD,MAAM,eAAe,IAAI,iCAAiC,IAAI,WAAW;CAIzE,MAAM,oBAAoB,kBAAkB,kCAAkC,IAAI,GAAG,KAAA;AAErF,QAAO,QAAQ,KAAK,YAAmC;EACrD,MAAM,cAAc,IAAI,WAAW,oBAAoB,QAAQ,IAAI;AACnE,MAAI,CAAC,YAAa,QAAO,EAAE,SAAS;EAIpC,IAAI;EACJ,IAAI;AACJ,MAAI;AAGF,sBAAmB,IAAI,wBAAwB,WAAW,CACvD,UAAU,aAAa,CACvB,MAAM;IAAE,SAAS,EAAE,MAAM,aAAa;IAAE,wBAAwB;IAAM,CAAC;AAE1E,0BACE,sBAAsB,KAAA,IAClB,IAAI,wBAAwB,WAAW,CACpC,WAAW,kBAAkB,CAC7B,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,EAAE,CAAC,GAC5C,KAAA;GAEN,MAAM,gBAAgB,kBAAkB,iBAAiB,CAAC,QAAQ,MAChE,gBAAgB,EAAE,OAAO,KAAK,CAC/B;GACD,MAAM,UACJ,cAAc,WAAW,IACrB,KAAA,IACA,uBAAuB,eAAe,QAAQ,MAAM,aAAa;GAEvE,IAAI;AACJ,OAAI,wBAAwB,iBAAiB;IAC3C,MAAM,qBAAqB,sBAAsB,sBAAsB;KACrE,SAAS,MAAM;KACf,GAAI,OAAO,oBAAoB,aAC3B,EAAE,WAAW,iBAAiB,GAC9B,EAAE,SAAS,iBAAiB;KACjC,CAAC;AACF,QAAI,mBAAmB,SAAS,EAC9B,eAAc,yBAAyB,oBAAoB,MAAM,aAAa;;AAIlF,UAAO;IACL;IACA,GAAI,YAAY,KAAA,KAAa,QAAQ,SAAS,IAAI,EAAE,SAAS,GAAG,EAAE;IAClE,GAAI,gBAAgB,KAAA,KAAa,YAAY,SAAS,IAAI,EAAE,aAAa,GAAG,EAAE;IAC/E;YACO;AACR,qBAAkB,SAAS;AAC3B,yBAAsB,SAAS;;GAEjC"}
1
+ {"version":3,"file":"build_dataset_options.js","names":[],"sources":["../../../src/components/PlDatasetSelector/build_dataset_options.ts"],"sourcesContent":["import type { MultiColumnSelector, Option, PObjectSpec } from \"@milaboratories/pl-model-common\";\nimport { multiColumnSelectorsToPredicate } from \"@milaboratories/pl-model-common\";\nimport type { DeriveLabelsOptions } from \"../../labels/derive_distinct_labels\";\nimport type { RenderCtxBase } from \"../../render\";\nimport type { AnchoredColumnCollection } from \"../../columns/column_collection_builder\";\nimport { ColumnCollectionBuilder } from \"../../columns/column_collection_builder\";\nimport {\n ResultPoolColumnSnapshotProvider,\n collectCtxColumnSnapshotProviders,\n} from \"../../columns/ctx_column_sources\";\nimport type { DatasetOption } from \"./dataset_selection\";\nimport { buildRefMap, filterMatchesToOptions, findFilterColumns } from \"./filter_discovery\";\nimport { enrichmentVariantsToRefs, findEnrichmentColumns } from \"./enrichment_discovery\";\n\ntype SpecPredicateOption =\n | MultiColumnSelector\n | MultiColumnSelector[]\n | ((spec: PObjectSpec) => boolean);\n\nfunction toPredicate(opt: SpecPredicateOption | undefined): (spec: PObjectSpec) => boolean {\n if (opt === undefined) return () => true;\n return typeof opt === \"function\" ? opt : multiColumnSelectorsToPredicate(opt);\n}\n\nexport type BuildDatasetOptions = {\n /** Which result pool columns qualify as datasets. Defaults to all. */\n primary?: SpecPredicateOption;\n /**\n * Restricts which result pool columns are considered as filters. Intersected\n * with the built-in `pl7.app/isSubset: \"true\"` constraint. Defaults to\n * accept-all.\n */\n filter?: SpecPredicateOption;\n /**\n * Formatting options forwarded to label derivation for both filter and\n * enrichment rows. `formatters.native` on the filter path is overridden\n * — see `FilterMatchOptions.labelOptions`.\n */\n labelOptions?: DeriveLabelsOptions;\n /**\n * Enables enrichment discovery and filters hits attached to\n * `DatasetOption.enrichments`. Use `() => true` to accept all; omit to disable.\n */\n withEnrichments?: SpecPredicateOption;\n /** Maximum linker hops considered. Only used when `withEnrichments` is set. */\n enrichmentMaxHops?: number;\n};\n\n/**\n * Usage:\n * ```ts\n * .output(\"datasetOptions\", (ctx) => buildDatasetOptions(ctx))\n * ```\n */\nexport function buildDatasetOptions(\n ctx: RenderCtxBase,\n opts?: BuildDatasetOptions,\n): DatasetOption[] | undefined {\n const primaryPredicate = toPredicate(opts?.primary);\n const filterPredicate = toPredicate(opts?.filter);\n\n const options = ctx.resultPool.getOptions(primaryPredicate, { refsWithEnrichments: true });\n if (options.length === 0) return [];\n\n const refMap = buildRefMap(ctx.resultPool.getSpecs().entries);\n const pframeSpec = ctx.getService(\"pframeSpec\");\n\n const withEnrichments = opts?.withEnrichments ?? false;\n const filterSource = new ResultPoolColumnSnapshotProvider(ctx.resultPool);\n // Hoisted out of the per-option loop: collectCtxColumnSnapshotProviders\n // walks the entire output tree, so calling it once per dataset option would\n // be O(N × tree).\n const enrichmentSources = withEnrichments ? collectCtxColumnSnapshotProviders(ctx) : undefined;\n\n return options.map((primary: Option): DatasetOption => {\n const datasetSpec = ctx.resultPool.getPColumnSpecByRef(primary.ref);\n if (!datasetSpec) return { primary };\n\n // Allocations happen inside try so a throw on the second build()\n // still disposes the first collection.\n let filterCollection: AnchoredColumnCollection | undefined;\n let enrichmentCollection: AnchoredColumnCollection | undefined;\n try {\n // ResultPoolColumnSnapshotProvider is always complete;\n // allowPartialColumnList narrows the return type to non-undefined.\n filterCollection = new ColumnCollectionBuilder(pframeSpec)\n .addSource(filterSource)\n .build({ anchors: { main: datasetSpec }, allowPartialColumnList: true });\n\n enrichmentCollection =\n enrichmentSources !== undefined\n ? new ColumnCollectionBuilder(pframeSpec)\n .addSources(enrichmentSources)\n .build({ anchors: { main: datasetSpec } })\n : undefined;\n\n const filterMatches = findFilterColumns(filterCollection).filter((m) =>\n filterPredicate(m.column.spec),\n );\n const filters =\n filterMatches.length === 0\n ? undefined\n : filterMatchesToOptions(filterMatches, {\n refsByObjectId: refMap,\n datasetSpec,\n labelOptions: opts?.labelOptions,\n });\n\n let enrichments;\n if (enrichmentCollection && withEnrichments) {\n const enrichmentVariants = findEnrichmentColumns(enrichmentCollection, {\n maxHops: opts?.enrichmentMaxHops,\n ...(typeof withEnrichments === \"function\"\n ? { predicate: withEnrichments }\n : { include: withEnrichments }),\n });\n if (enrichmentVariants.length > 0) {\n enrichments = enrichmentVariantsToRefs(enrichmentVariants, opts?.labelOptions);\n }\n }\n\n return {\n primary,\n ...(filters !== undefined && filters.length > 0 ? { filters } : {}),\n ...(enrichments !== undefined && enrichments.length > 0 ? { enrichments } : {}),\n };\n } finally {\n filterCollection?.dispose();\n enrichmentCollection?.dispose();\n }\n });\n}\n"],"mappings":";;;;;;AAmBA,SAAS,YAAY,KAAsE;AACzF,KAAI,QAAQ,KAAA,EAAW,cAAa;AACpC,QAAO,OAAO,QAAQ,aAAa,MAAM,gCAAgC,IAAI;;;;;;;;AAiC/E,SAAgB,oBACd,KACA,MAC6B;CAC7B,MAAM,mBAAmB,YAAY,MAAM,QAAQ;CACnD,MAAM,kBAAkB,YAAY,MAAM,OAAO;CAEjD,MAAM,UAAU,IAAI,WAAW,WAAW,kBAAkB,EAAE,qBAAqB,MAAM,CAAC;AAC1F,KAAI,QAAQ,WAAW,EAAG,QAAO,EAAE;CAEnC,MAAM,SAAS,YAAY,IAAI,WAAW,UAAU,CAAC,QAAQ;CAC7D,MAAM,aAAa,IAAI,WAAW,aAAa;CAE/C,MAAM,kBAAkB,MAAM,mBAAmB;CACjD,MAAM,eAAe,IAAI,iCAAiC,IAAI,WAAW;CAIzE,MAAM,oBAAoB,kBAAkB,kCAAkC,IAAI,GAAG,KAAA;AAErF,QAAO,QAAQ,KAAK,YAAmC;EACrD,MAAM,cAAc,IAAI,WAAW,oBAAoB,QAAQ,IAAI;AACnE,MAAI,CAAC,YAAa,QAAO,EAAE,SAAS;EAIpC,IAAI;EACJ,IAAI;AACJ,MAAI;AAGF,sBAAmB,IAAI,wBAAwB,WAAW,CACvD,UAAU,aAAa,CACvB,MAAM;IAAE,SAAS,EAAE,MAAM,aAAa;IAAE,wBAAwB;IAAM,CAAC;AAE1E,0BACE,sBAAsB,KAAA,IAClB,IAAI,wBAAwB,WAAW,CACpC,WAAW,kBAAkB,CAC7B,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,EAAE,CAAC,GAC5C,KAAA;GAEN,MAAM,gBAAgB,kBAAkB,iBAAiB,CAAC,QAAQ,MAChE,gBAAgB,EAAE,OAAO,KAAK,CAC/B;GACD,MAAM,UACJ,cAAc,WAAW,IACrB,KAAA,IACA,uBAAuB,eAAe;IACpC,gBAAgB;IAChB;IACA,cAAc,MAAM;IACrB,CAAC;GAER,IAAI;AACJ,OAAI,wBAAwB,iBAAiB;IAC3C,MAAM,qBAAqB,sBAAsB,sBAAsB;KACrE,SAAS,MAAM;KACf,GAAI,OAAO,oBAAoB,aAC3B,EAAE,WAAW,iBAAiB,GAC9B,EAAE,SAAS,iBAAiB;KACjC,CAAC;AACF,QAAI,mBAAmB,SAAS,EAC9B,eAAc,yBAAyB,oBAAoB,MAAM,aAAa;;AAIlF,UAAO;IACL;IACA,GAAI,YAAY,KAAA,KAAa,QAAQ,SAAS,IAAI,EAAE,SAAS,GAAG,EAAE;IAClE,GAAI,gBAAgB,KAAA,KAAa,YAAY,SAAS,IAAI,EAAE,aAAa,GAAG,EAAE;IAC/E;YACO;AACR,qBAAkB,SAAS;AAC3B,yBAAsB,SAAS;;GAEjC"}
@@ -5,12 +5,8 @@ let canonicalize = require("canonicalize");
5
5
  canonicalize = require_runtime.__toESM(canonicalize);
6
6
  //#region src/components/PlDatasetSelector/filter_discovery.ts
7
7
  /**
8
- * Matches columns annotated `pl7.app/isSubset: "true"` whose axes ⊆ anchor axes.
9
- *
10
- * The axes-subset constraint is enforced by `mode: "enrichment"`, which sets
11
- * `allowFloatingHitAxes: false` — every axis of the matched column must be
12
- * present in the anchor's axes. See `matchingModeToConstraints()` in
13
- * `column_collection_builder.ts`.
8
+ * Columns annotated `pl7.app/isSubset: "true"` whose axes ⊆ anchor axes.
9
+ * The axes-subset constraint comes from `mode: "enrichment"`.
14
10
  */
15
11
  function findFilterColumns(collection) {
16
12
  return collection.findColumns({
@@ -19,42 +15,37 @@ function findFilterColumns(collection) {
19
15
  });
20
16
  }
21
17
  /**
22
- * Derive labeled options from filter column matches, for use in DatasetOption.filters.
23
- *
24
- * Entries whose column id has no PlRef in `refsByObjectId` are silently
25
- * skipped — they cannot be exposed as user-selectable options.
26
- *
27
- * @param matches - from findFilterColumns()
28
- * @param refsByObjectId - from {@link buildRefMap}
29
- * @param labelOptions - forwarded to deriveDistinctLabels()
18
+ * Derive labels for filter column matches (for `DatasetOption.filters`).
19
+ * Matches whose column id is missing from `refsByObjectId` are silently
20
+ * dropped they cannot be exposed as selectable options.
30
21
  */
31
- function filterMatchesToOptions(matches, refsByObjectId, labelOptions) {
22
+ function filterMatchesToOptions(matches, options) {
32
23
  if (matches.length === 0) return [];
33
- const flattened = matches.flatMap((match) => {
24
+ const { refsByObjectId, datasetSpec, labelOptions } = options;
25
+ const entries = matches.flatMap((match) => {
34
26
  const ref = refsByObjectId.get(match.column.id);
35
27
  if (ref === void 0) return [];
36
28
  return match.variants.map((variant) => ({
37
- match,
38
- variant,
39
- ref
29
+ ref,
30
+ spec: match.column.spec,
31
+ linkerPath: variant.path.map((p) => ({ spec: p.linker.spec }))
40
32
  }));
41
33
  });
42
- const labels = require_derive_distinct_labels.deriveDistinctLabels(flattened.map(({ match, variant }) => ({
43
- spec: match.column.spec,
44
- linkerPath: variant.path.map((p) => ({ spec: p.linker.spec }))
45
- })), labelOptions);
46
- return flattened.map(({ ref }, i) => ({
34
+ const labels = require_derive_distinct_labels.deriveDistinctLabels([...entries, { spec: datasetSpec }], {
35
+ ...labelOptions,
36
+ formatters: {
37
+ ...labelOptions?.formatters,
38
+ native: () => void 0
39
+ }
40
+ });
41
+ return entries.map(({ ref }, i) => ({
47
42
  ref,
48
43
  label: labels[i]
49
44
  }));
50
45
  }
51
- /**
52
- * Usage: `buildRefMap(ctx.resultPool.getSpecs().entries)`
53
- */
46
+ /** Build the `refsByObjectId` map from `ctx.resultPool.getSpecs().entries`. */
54
47
  function buildRefMap(entries) {
55
- const map = /* @__PURE__ */ new Map();
56
- for (const entry of entries) map.set((0, canonicalize.default)(entry.ref), entry.ref);
57
- return map;
48
+ return new Map(entries.map((e) => [(0, canonicalize.default)(e.ref), e.ref]));
58
49
  }
59
50
  //#endregion
60
51
  exports.buildRefMap = buildRefMap;
@@ -1 +1 @@
1
- {"version":3,"file":"filter_discovery.cjs","names":["Annotation","deriveDistinctLabels"],"sources":["../../../src/components/PlDatasetSelector/filter_discovery.ts"],"sourcesContent":["import { Annotation } from \"@milaboratories/pl-model-common\";\nimport type { Option, PlRef, PObjectId } from \"@milaboratories/pl-model-common\";\nimport canonicalize from \"canonicalize\";\nimport type {\n AnchoredColumnCollection,\n ColumnMatch,\n} from \"../../columns/column_collection_builder\";\nimport {\n deriveDistinctLabels,\n type DeriveLabelsOptions,\n type Entry,\n} from \"../../labels/derive_distinct_labels\";\n\n/**\n * Matches columns annotated `pl7.app/isSubset: \"true\"` whose axes ⊆ anchor axes.\n *\n * The axes-subset constraint is enforced by `mode: \"enrichment\"`, which sets\n * `allowFloatingHitAxes: false` — every axis of the matched column must be\n * present in the anchor's axes. See `matchingModeToConstraints()` in\n * `column_collection_builder.ts`.\n */\nexport function findFilterColumns(collection: AnchoredColumnCollection): ColumnMatch[] {\n return collection.findColumns({\n mode: \"enrichment\",\n include: {\n annotations: { [Annotation.IsSubset]: \"true\" },\n },\n });\n}\n\n/**\n * Derive labeled options from filter column matches, for use in DatasetOption.filters.\n *\n * Entries whose column id has no PlRef in `refsByObjectId` are silently\n * skippedthey cannot be exposed as user-selectable options.\n *\n * @param matches - from findFilterColumns()\n * @param refsByObjectId - from {@link buildRefMap}\n * @param labelOptions - forwarded to deriveDistinctLabels()\n */\nexport function filterMatchesToOptions(\n matches: ColumnMatch[],\n refsByObjectId: ReadonlyMap<PObjectId, PlRef>,\n labelOptions?: DeriveLabelsOptions,\n): Option[] {\n if (matches.length === 0) return [];\n\n // Each ColumnMatch can be reached via multiple variants (different linker\n // paths / qualifications). We emit one Option per variant so the user can\n // pick a specific path `deriveDistinctLabels` disambiguates labels by\n // path. All variants of a match share a column id, so the ref lookup\n // happens once per match.\n const flattened = matches.flatMap((match) => {\n const ref = refsByObjectId.get(match.column.id);\n if (ref === undefined) return [];\n return match.variants.map((variant) => ({ match, variant, ref }));\n });\n\n const entries: Entry[] = flattened.map(({ match, variant }) => ({\n spec: match.column.spec,\n linkerPath: variant.path.map((p) => ({ spec: p.linker.spec })),\n }));\n\n const labels = deriveDistinctLabels(entries, labelOptions);\n\n return flattened.map(({ ref }, i) => ({ ref, label: labels[i] }));\n}\n\n/**\n * Usage: `buildRefMap(ctx.resultPool.getSpecs().entries)`\n */\nexport function buildRefMap(entries: readonly { readonly ref: PlRef }[]): Map<PObjectId, PlRef> {\n const map = new Map<PObjectId, PlRef>();\n for (const entry of entries) {\n map.set(canonicalize(entry.ref)! as PObjectId, entry.ref);\n }\n return map;\n}\n"],"mappings":";;;;;;;;;;;;;;AAqBA,SAAgB,kBAAkB,YAAqD;AACrF,QAAO,WAAW,YAAY;EAC5B,MAAM;EACN,SAAS,EACP,aAAa,GAAGA,gCAAAA,WAAW,WAAW,QAAQ,EAC/C;EACF,CAAC;;;;;;;;;;;;AAaJ,SAAgB,uBACd,SACA,gBACA,cACU;AACV,KAAI,QAAQ,WAAW,EAAG,QAAO,EAAE;CAOnC,MAAM,YAAY,QAAQ,SAAS,UAAU;EAC3C,MAAM,MAAM,eAAe,IAAI,MAAM,OAAO,GAAG;AAC/C,MAAI,QAAQ,KAAA,EAAW,QAAO,EAAE;AAChC,SAAO,MAAM,SAAS,KAAK,aAAa;GAAE;GAAO;GAAS;GAAK,EAAE;GACjE;CAOF,MAAM,SAASC,+BAAAA,qBALU,UAAU,KAAK,EAAE,OAAO,eAAe;EAC9D,MAAM,MAAM,OAAO;EACnB,YAAY,QAAQ,KAAK,KAAK,OAAO,EAAE,MAAM,EAAE,OAAO,MAAM,EAAE;EAC/D,EAAE,EAE0C,aAAa;AAE1D,QAAO,UAAU,KAAK,EAAE,OAAO,OAAO;EAAE;EAAK,OAAO,OAAO;EAAI,EAAE;;;;;AAMnE,SAAgB,YAAY,SAAoE;CAC9F,MAAM,sBAAM,IAAI,KAAuB;AACvC,MAAK,MAAM,SAAS,QAClB,KAAI,KAAA,GAAA,aAAA,SAAiB,MAAM,IAAI,EAAgB,MAAM,IAAI;AAE3D,QAAO"}
1
+ {"version":3,"file":"filter_discovery.cjs","names":["Annotation","deriveDistinctLabels"],"sources":["../../../src/components/PlDatasetSelector/filter_discovery.ts"],"sourcesContent":["import { Annotation } from \"@milaboratories/pl-model-common\";\nimport type { Option, PlRef, PObjectId, PObjectSpec } from \"@milaboratories/pl-model-common\";\nimport canonicalize from \"canonicalize\";\nimport type {\n AnchoredColumnCollection,\n ColumnMatch,\n} from \"../../columns/column_collection_builder\";\nimport {\n deriveDistinctLabels,\n type DeriveLabelsOptions,\n type Entry,\n} from \"../../labels/derive_distinct_labels\";\n\n/**\n * Columns annotated `pl7.app/isSubset: \"true\"` whose axes ⊆ anchor axes.\n * The axes-subset constraint comes from `mode: \"enrichment\"`.\n */\nexport function findFilterColumns(collection: AnchoredColumnCollection): ColumnMatch[] {\n return collection.findColumns({\n mode: \"enrichment\",\n include: {\n annotations: { [Annotation.IsSubset]: \"true\" },\n },\n });\n}\n\nexport type FilterMatchOptions = {\n /** Maps result-pool column id back to its source PlRef (see {@link buildRefMap}). */\n refsByObjectId: ReadonlyMap<PObjectId, PlRef>;\n /** Spec of the dataset the filters are subsets of. */\n datasetSpec: PObjectSpec;\n /**\n * Forwarded to `deriveDistinctLabels`. Any `formatters.native` caller\n * sets is silently overridden the function relies on a no-op native\n * formatter to keep the algorithm from short-circuiting on filters'\n * inherited `pl7.app/label`.\n */\n labelOptions?: DeriveLabelsOptions;\n};\n\n/**\n * Derive labels for filter column matches (for `DatasetOption.filters`).\n * Matches whose column id is missing from `refsByObjectId` are silently\n * dropped they cannot be exposed as selectable options.\n */\nexport function filterMatchesToOptions(\n matches: ColumnMatch[],\n options: FilterMatchOptions,\n): Option[] {\n if (matches.length === 0) return [];\n\n const { refsByObjectId, datasetSpec, labelOptions } = options;\n\n // One entry per match-variant (different paths to the same column are\n // exposed as separate Options). The `ref` field rides along on the\n // Entry-shaped objects via structural typing; `deriveDistinctLabels`\n // ignores extra fields.\n const entries = matches.flatMap((match): (Entry & { ref: PlRef })[] => {\n const ref = refsByObjectId.get(match.column.id);\n if (ref === undefined) return [];\n return match.variants.map((variant) => ({\n ref,\n spec: match.column.spec,\n linkerPath: variant.path.map((p) => ({ spec: p.linker.spec })),\n }));\n });\n\n // Appending the dataset forces a discriminating trace step into every\n // filter label (yielding e.g. \"Bulk / Top 10\"); the dataset's own label\n // is dropped because we only zip `entries`. Native label is force-\n // suppressed (the override sits after caller's spread) — filters\n // inherit the dataset's `pl7.app/label` and would otherwise satisfy\n // uniqueness before any trace step is consulted.\n const labels = deriveDistinctLabels([...entries, { spec: datasetSpec }], {\n ...labelOptions,\n formatters: { ...labelOptions?.formatters, native: () => undefined },\n });\n\n return entries.map(({ ref }, i) => ({ ref, label: labels[i] }));\n}\n\n/** Build the `refsByObjectId` map from `ctx.resultPool.getSpecs().entries`. */\nexport function buildRefMap(entries: readonly { readonly ref: PlRef }[]): Map<PObjectId, PlRef> {\n return new Map(entries.map((e) => [canonicalize(e.ref)! as PObjectId, e.ref]));\n}\n"],"mappings":";;;;;;;;;;AAiBA,SAAgB,kBAAkB,YAAqD;AACrF,QAAO,WAAW,YAAY;EAC5B,MAAM;EACN,SAAS,EACP,aAAa,GAAGA,gCAAAA,WAAW,WAAW,QAAQ,EAC/C;EACF,CAAC;;;;;;;AAsBJ,SAAgB,uBACd,SACA,SACU;AACV,KAAI,QAAQ,WAAW,EAAG,QAAO,EAAE;CAEnC,MAAM,EAAE,gBAAgB,aAAa,iBAAiB;CAMtD,MAAM,UAAU,QAAQ,SAAS,UAAsC;EACrE,MAAM,MAAM,eAAe,IAAI,MAAM,OAAO,GAAG;AAC/C,MAAI,QAAQ,KAAA,EAAW,QAAO,EAAE;AAChC,SAAO,MAAM,SAAS,KAAK,aAAa;GACtC;GACA,MAAM,MAAM,OAAO;GACnB,YAAY,QAAQ,KAAK,KAAK,OAAO,EAAE,MAAM,EAAE,OAAO,MAAM,EAAE;GAC/D,EAAE;GACH;CAQF,MAAM,SAASC,+BAAAA,qBAAqB,CAAC,GAAG,SAAS,EAAE,MAAM,aAAa,CAAC,EAAE;EACvE,GAAG;EACH,YAAY;GAAE,GAAG,cAAc;GAAY,cAAc,KAAA;GAAW;EACrE,CAAC;AAEF,QAAO,QAAQ,KAAK,EAAE,OAAO,OAAO;EAAE;EAAK,OAAO,OAAO;EAAI,EAAE;;;AAIjE,SAAgB,YAAY,SAAoE;AAC9F,QAAO,IAAI,IAAI,QAAQ,KAAK,MAAM,EAAA,GAAA,aAAA,SAAc,EAAE,IAAI,EAAgB,EAAE,IAAI,CAAC,CAAC"}
@@ -1,34 +1,34 @@
1
1
  import { AnchoredColumnCollection, ColumnMatch } from "../../columns/column_collection_builder.js";
2
2
  import { DeriveLabelsOptions } from "../../labels/derive_distinct_labels.js";
3
- import { Option, PObjectId, PlRef } from "@milaboratories/pl-model-common";
3
+ import { Option, PObjectId, PObjectSpec, PlRef } from "@milaboratories/pl-model-common";
4
4
 
5
5
  //#region src/components/PlDatasetSelector/filter_discovery.d.ts
6
6
  /**
7
- * Matches columns annotated `pl7.app/isSubset: "true"` whose axes ⊆ anchor axes.
8
- *
9
- * The axes-subset constraint is enforced by `mode: "enrichment"`, which sets
10
- * `allowFloatingHitAxes: false` — every axis of the matched column must be
11
- * present in the anchor's axes. See `matchingModeToConstraints()` in
12
- * `column_collection_builder.ts`.
7
+ * Columns annotated `pl7.app/isSubset: "true"` whose axes ⊆ anchor axes.
8
+ * The axes-subset constraint comes from `mode: "enrichment"`.
13
9
  */
14
10
  declare function findFilterColumns(collection: AnchoredColumnCollection): ColumnMatch[];
11
+ type FilterMatchOptions = {
12
+ /** Maps result-pool column id back to its source PlRef (see {@link buildRefMap}). */refsByObjectId: ReadonlyMap<PObjectId, PlRef>; /** Spec of the dataset the filters are subsets of. */
13
+ datasetSpec: PObjectSpec;
14
+ /**
15
+ * Forwarded to `deriveDistinctLabels`. Any `formatters.native` caller
16
+ * sets is silently overridden — the function relies on a no-op native
17
+ * formatter to keep the algorithm from short-circuiting on filters'
18
+ * inherited `pl7.app/label`.
19
+ */
20
+ labelOptions?: DeriveLabelsOptions;
21
+ };
15
22
  /**
16
- * Derive labeled options from filter column matches, for use in DatasetOption.filters.
17
- *
18
- * Entries whose column id has no PlRef in `refsByObjectId` are silently
19
- * skipped — they cannot be exposed as user-selectable options.
20
- *
21
- * @param matches - from findFilterColumns()
22
- * @param refsByObjectId - from {@link buildRefMap}
23
- * @param labelOptions - forwarded to deriveDistinctLabels()
24
- */
25
- declare function filterMatchesToOptions(matches: ColumnMatch[], refsByObjectId: ReadonlyMap<PObjectId, PlRef>, labelOptions?: DeriveLabelsOptions): Option[];
26
- /**
27
- * Usage: `buildRefMap(ctx.resultPool.getSpecs().entries)`
23
+ * Derive labels for filter column matches (for `DatasetOption.filters`).
24
+ * Matches whose column id is missing from `refsByObjectId` are silently
25
+ * dropped they cannot be exposed as selectable options.
28
26
  */
27
+ declare function filterMatchesToOptions(matches: ColumnMatch[], options: FilterMatchOptions): Option[];
28
+ /** Build the `refsByObjectId` map from `ctx.resultPool.getSpecs().entries`. */
29
29
  declare function buildRefMap(entries: readonly {
30
30
  readonly ref: PlRef;
31
31
  }[]): Map<PObjectId, PlRef>;
32
32
  //#endregion
33
- export { buildRefMap, filterMatchesToOptions, findFilterColumns };
33
+ export { FilterMatchOptions, buildRefMap, filterMatchesToOptions, findFilterColumns };
34
34
  //# sourceMappingURL=filter_discovery.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"filter_discovery.d.ts","names":[],"sources":["../../../src/components/PlDatasetSelector/filter_discovery.ts"],"mappings":";;;;;;;AAqBA;;;;;;iBAAgB,iBAAA,CAAkB,UAAA,EAAY,wBAAA,GAA2B,WAAA;;;AAmBzE;;;;;;;;iBAAgB,sBAAA,CACd,OAAA,EAAS,WAAA,IACT,cAAA,EAAgB,WAAA,CAAY,SAAA,EAAW,KAAA,GACvC,YAAA,GAAe,mBAAA,GACd,MAAA;;;;iBA2Ba,WAAA,CAAY,OAAA;EAAA,SAA6B,GAAA,EAAK,KAAA;AAAA,MAAY,GAAA,CAAI,SAAA,EAAW,KAAA"}
1
+ {"version":3,"file":"filter_discovery.d.ts","names":[],"sources":["../../../src/components/PlDatasetSelector/filter_discovery.ts"],"mappings":";;;;;;;AAiBA;;iBAAgB,iBAAA,CAAkB,UAAA,EAAY,wBAAA,GAA2B,WAAA;AAAA,KAS7D,kBAAA;EATkC,qFAW5C,cAAA,EAAgB,WAAA,CAAY,SAAA,EAAW,KAAA,GAXgC;EAavE,WAAA,EAAa,WAAA;EAbqE;AASpF;;;;;EAWE,YAAA,GAAe,mBAAA;AAAA;;;;;;iBAQD,sBAAA,CACd,OAAA,EAAS,WAAA,IACT,OAAA,EAAS,kBAAA,GACR,MAAA;;iBAkCa,WAAA,CAAY,OAAA;EAAA,SAA6B,GAAA,EAAK,KAAA;AAAA,MAAY,GAAA,CAAI,SAAA,EAAW,KAAA"}
@@ -3,12 +3,8 @@ import { Annotation } from "@milaboratories/pl-model-common";
3
3
  import canonicalize from "canonicalize";
4
4
  //#region src/components/PlDatasetSelector/filter_discovery.ts
5
5
  /**
6
- * Matches columns annotated `pl7.app/isSubset: "true"` whose axes ⊆ anchor axes.
7
- *
8
- * The axes-subset constraint is enforced by `mode: "enrichment"`, which sets
9
- * `allowFloatingHitAxes: false` — every axis of the matched column must be
10
- * present in the anchor's axes. See `matchingModeToConstraints()` in
11
- * `column_collection_builder.ts`.
6
+ * Columns annotated `pl7.app/isSubset: "true"` whose axes ⊆ anchor axes.
7
+ * The axes-subset constraint comes from `mode: "enrichment"`.
12
8
  */
13
9
  function findFilterColumns(collection) {
14
10
  return collection.findColumns({
@@ -17,42 +13,37 @@ function findFilterColumns(collection) {
17
13
  });
18
14
  }
19
15
  /**
20
- * Derive labeled options from filter column matches, for use in DatasetOption.filters.
21
- *
22
- * Entries whose column id has no PlRef in `refsByObjectId` are silently
23
- * skipped — they cannot be exposed as user-selectable options.
24
- *
25
- * @param matches - from findFilterColumns()
26
- * @param refsByObjectId - from {@link buildRefMap}
27
- * @param labelOptions - forwarded to deriveDistinctLabels()
16
+ * Derive labels for filter column matches (for `DatasetOption.filters`).
17
+ * Matches whose column id is missing from `refsByObjectId` are silently
18
+ * dropped they cannot be exposed as selectable options.
28
19
  */
29
- function filterMatchesToOptions(matches, refsByObjectId, labelOptions) {
20
+ function filterMatchesToOptions(matches, options) {
30
21
  if (matches.length === 0) return [];
31
- const flattened = matches.flatMap((match) => {
22
+ const { refsByObjectId, datasetSpec, labelOptions } = options;
23
+ const entries = matches.flatMap((match) => {
32
24
  const ref = refsByObjectId.get(match.column.id);
33
25
  if (ref === void 0) return [];
34
26
  return match.variants.map((variant) => ({
35
- match,
36
- variant,
37
- ref
27
+ ref,
28
+ spec: match.column.spec,
29
+ linkerPath: variant.path.map((p) => ({ spec: p.linker.spec }))
38
30
  }));
39
31
  });
40
- const labels = deriveDistinctLabels(flattened.map(({ match, variant }) => ({
41
- spec: match.column.spec,
42
- linkerPath: variant.path.map((p) => ({ spec: p.linker.spec }))
43
- })), labelOptions);
44
- return flattened.map(({ ref }, i) => ({
32
+ const labels = deriveDistinctLabels([...entries, { spec: datasetSpec }], {
33
+ ...labelOptions,
34
+ formatters: {
35
+ ...labelOptions?.formatters,
36
+ native: () => void 0
37
+ }
38
+ });
39
+ return entries.map(({ ref }, i) => ({
45
40
  ref,
46
41
  label: labels[i]
47
42
  }));
48
43
  }
49
- /**
50
- * Usage: `buildRefMap(ctx.resultPool.getSpecs().entries)`
51
- */
44
+ /** Build the `refsByObjectId` map from `ctx.resultPool.getSpecs().entries`. */
52
45
  function buildRefMap(entries) {
53
- const map = /* @__PURE__ */ new Map();
54
- for (const entry of entries) map.set(canonicalize(entry.ref), entry.ref);
55
- return map;
46
+ return new Map(entries.map((e) => [canonicalize(e.ref), e.ref]));
56
47
  }
57
48
  //#endregion
58
49
  export { buildRefMap, filterMatchesToOptions, findFilterColumns };
@@ -1 +1 @@
1
- {"version":3,"file":"filter_discovery.js","names":[],"sources":["../../../src/components/PlDatasetSelector/filter_discovery.ts"],"sourcesContent":["import { Annotation } from \"@milaboratories/pl-model-common\";\nimport type { Option, PlRef, PObjectId } from \"@milaboratories/pl-model-common\";\nimport canonicalize from \"canonicalize\";\nimport type {\n AnchoredColumnCollection,\n ColumnMatch,\n} from \"../../columns/column_collection_builder\";\nimport {\n deriveDistinctLabels,\n type DeriveLabelsOptions,\n type Entry,\n} from \"../../labels/derive_distinct_labels\";\n\n/**\n * Matches columns annotated `pl7.app/isSubset: \"true\"` whose axes ⊆ anchor axes.\n *\n * The axes-subset constraint is enforced by `mode: \"enrichment\"`, which sets\n * `allowFloatingHitAxes: false` — every axis of the matched column must be\n * present in the anchor's axes. See `matchingModeToConstraints()` in\n * `column_collection_builder.ts`.\n */\nexport function findFilterColumns(collection: AnchoredColumnCollection): ColumnMatch[] {\n return collection.findColumns({\n mode: \"enrichment\",\n include: {\n annotations: { [Annotation.IsSubset]: \"true\" },\n },\n });\n}\n\n/**\n * Derive labeled options from filter column matches, for use in DatasetOption.filters.\n *\n * Entries whose column id has no PlRef in `refsByObjectId` are silently\n * skippedthey cannot be exposed as user-selectable options.\n *\n * @param matches - from findFilterColumns()\n * @param refsByObjectId - from {@link buildRefMap}\n * @param labelOptions - forwarded to deriveDistinctLabels()\n */\nexport function filterMatchesToOptions(\n matches: ColumnMatch[],\n refsByObjectId: ReadonlyMap<PObjectId, PlRef>,\n labelOptions?: DeriveLabelsOptions,\n): Option[] {\n if (matches.length === 0) return [];\n\n // Each ColumnMatch can be reached via multiple variants (different linker\n // paths / qualifications). We emit one Option per variant so the user can\n // pick a specific path `deriveDistinctLabels` disambiguates labels by\n // path. All variants of a match share a column id, so the ref lookup\n // happens once per match.\n const flattened = matches.flatMap((match) => {\n const ref = refsByObjectId.get(match.column.id);\n if (ref === undefined) return [];\n return match.variants.map((variant) => ({ match, variant, ref }));\n });\n\n const entries: Entry[] = flattened.map(({ match, variant }) => ({\n spec: match.column.spec,\n linkerPath: variant.path.map((p) => ({ spec: p.linker.spec })),\n }));\n\n const labels = deriveDistinctLabels(entries, labelOptions);\n\n return flattened.map(({ ref }, i) => ({ ref, label: labels[i] }));\n}\n\n/**\n * Usage: `buildRefMap(ctx.resultPool.getSpecs().entries)`\n */\nexport function buildRefMap(entries: readonly { readonly ref: PlRef }[]): Map<PObjectId, PlRef> {\n const map = new Map<PObjectId, PlRef>();\n for (const entry of entries) {\n map.set(canonicalize(entry.ref)! as PObjectId, entry.ref);\n }\n return map;\n}\n"],"mappings":";;;;;;;;;;;;AAqBA,SAAgB,kBAAkB,YAAqD;AACrF,QAAO,WAAW,YAAY;EAC5B,MAAM;EACN,SAAS,EACP,aAAa,GAAG,WAAW,WAAW,QAAQ,EAC/C;EACF,CAAC;;;;;;;;;;;;AAaJ,SAAgB,uBACd,SACA,gBACA,cACU;AACV,KAAI,QAAQ,WAAW,EAAG,QAAO,EAAE;CAOnC,MAAM,YAAY,QAAQ,SAAS,UAAU;EAC3C,MAAM,MAAM,eAAe,IAAI,MAAM,OAAO,GAAG;AAC/C,MAAI,QAAQ,KAAA,EAAW,QAAO,EAAE;AAChC,SAAO,MAAM,SAAS,KAAK,aAAa;GAAE;GAAO;GAAS;GAAK,EAAE;GACjE;CAOF,MAAM,SAAS,qBALU,UAAU,KAAK,EAAE,OAAO,eAAe;EAC9D,MAAM,MAAM,OAAO;EACnB,YAAY,QAAQ,KAAK,KAAK,OAAO,EAAE,MAAM,EAAE,OAAO,MAAM,EAAE;EAC/D,EAAE,EAE0C,aAAa;AAE1D,QAAO,UAAU,KAAK,EAAE,OAAO,OAAO;EAAE;EAAK,OAAO,OAAO;EAAI,EAAE;;;;;AAMnE,SAAgB,YAAY,SAAoE;CAC9F,MAAM,sBAAM,IAAI,KAAuB;AACvC,MAAK,MAAM,SAAS,QAClB,KAAI,IAAI,aAAa,MAAM,IAAI,EAAgB,MAAM,IAAI;AAE3D,QAAO"}
1
+ {"version":3,"file":"filter_discovery.js","names":[],"sources":["../../../src/components/PlDatasetSelector/filter_discovery.ts"],"sourcesContent":["import { Annotation } from \"@milaboratories/pl-model-common\";\nimport type { Option, PlRef, PObjectId, PObjectSpec } from \"@milaboratories/pl-model-common\";\nimport canonicalize from \"canonicalize\";\nimport type {\n AnchoredColumnCollection,\n ColumnMatch,\n} from \"../../columns/column_collection_builder\";\nimport {\n deriveDistinctLabels,\n type DeriveLabelsOptions,\n type Entry,\n} from \"../../labels/derive_distinct_labels\";\n\n/**\n * Columns annotated `pl7.app/isSubset: \"true\"` whose axes ⊆ anchor axes.\n * The axes-subset constraint comes from `mode: \"enrichment\"`.\n */\nexport function findFilterColumns(collection: AnchoredColumnCollection): ColumnMatch[] {\n return collection.findColumns({\n mode: \"enrichment\",\n include: {\n annotations: { [Annotation.IsSubset]: \"true\" },\n },\n });\n}\n\nexport type FilterMatchOptions = {\n /** Maps result-pool column id back to its source PlRef (see {@link buildRefMap}). */\n refsByObjectId: ReadonlyMap<PObjectId, PlRef>;\n /** Spec of the dataset the filters are subsets of. */\n datasetSpec: PObjectSpec;\n /**\n * Forwarded to `deriveDistinctLabels`. Any `formatters.native` caller\n * sets is silently overridden the function relies on a no-op native\n * formatter to keep the algorithm from short-circuiting on filters'\n * inherited `pl7.app/label`.\n */\n labelOptions?: DeriveLabelsOptions;\n};\n\n/**\n * Derive labels for filter column matches (for `DatasetOption.filters`).\n * Matches whose column id is missing from `refsByObjectId` are silently\n * dropped they cannot be exposed as selectable options.\n */\nexport function filterMatchesToOptions(\n matches: ColumnMatch[],\n options: FilterMatchOptions,\n): Option[] {\n if (matches.length === 0) return [];\n\n const { refsByObjectId, datasetSpec, labelOptions } = options;\n\n // One entry per match-variant (different paths to the same column are\n // exposed as separate Options). The `ref` field rides along on the\n // Entry-shaped objects via structural typing; `deriveDistinctLabels`\n // ignores extra fields.\n const entries = matches.flatMap((match): (Entry & { ref: PlRef })[] => {\n const ref = refsByObjectId.get(match.column.id);\n if (ref === undefined) return [];\n return match.variants.map((variant) => ({\n ref,\n spec: match.column.spec,\n linkerPath: variant.path.map((p) => ({ spec: p.linker.spec })),\n }));\n });\n\n // Appending the dataset forces a discriminating trace step into every\n // filter label (yielding e.g. \"Bulk / Top 10\"); the dataset's own label\n // is dropped because we only zip `entries`. Native label is force-\n // suppressed (the override sits after caller's spread) — filters\n // inherit the dataset's `pl7.app/label` and would otherwise satisfy\n // uniqueness before any trace step is consulted.\n const labels = deriveDistinctLabels([...entries, { spec: datasetSpec }], {\n ...labelOptions,\n formatters: { ...labelOptions?.formatters, native: () => undefined },\n });\n\n return entries.map(({ ref }, i) => ({ ref, label: labels[i] }));\n}\n\n/** Build the `refsByObjectId` map from `ctx.resultPool.getSpecs().entries`. */\nexport function buildRefMap(entries: readonly { readonly ref: PlRef }[]): Map<PObjectId, PlRef> {\n return new Map(entries.map((e) => [canonicalize(e.ref)! as PObjectId, e.ref]));\n}\n"],"mappings":";;;;;;;;AAiBA,SAAgB,kBAAkB,YAAqD;AACrF,QAAO,WAAW,YAAY;EAC5B,MAAM;EACN,SAAS,EACP,aAAa,GAAG,WAAW,WAAW,QAAQ,EAC/C;EACF,CAAC;;;;;;;AAsBJ,SAAgB,uBACd,SACA,SACU;AACV,KAAI,QAAQ,WAAW,EAAG,QAAO,EAAE;CAEnC,MAAM,EAAE,gBAAgB,aAAa,iBAAiB;CAMtD,MAAM,UAAU,QAAQ,SAAS,UAAsC;EACrE,MAAM,MAAM,eAAe,IAAI,MAAM,OAAO,GAAG;AAC/C,MAAI,QAAQ,KAAA,EAAW,QAAO,EAAE;AAChC,SAAO,MAAM,SAAS,KAAK,aAAa;GACtC;GACA,MAAM,MAAM,OAAO;GACnB,YAAY,QAAQ,KAAK,KAAK,OAAO,EAAE,MAAM,EAAE,OAAO,MAAM,EAAE;GAC/D,EAAE;GACH;CAQF,MAAM,SAAS,qBAAqB,CAAC,GAAG,SAAS,EAAE,MAAM,aAAa,CAAC,EAAE;EACvE,GAAG;EACH,YAAY;GAAE,GAAG,cAAc;GAAY,cAAc,KAAA;GAAW;EACrE,CAAC;AAEF,QAAO,QAAQ,KAAK,EAAE,OAAO,OAAO;EAAE;EAAK,OAAO,OAAO;EAAI,EAAE;;;AAIjE,SAAgB,YAAY,SAAoE;AAC9F,QAAO,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC,aAAa,EAAE,IAAI,EAAgB,EAAE,IAAI,CAAC,CAAC"}
@@ -1,3 +1,3 @@
1
1
  import { DatasetOption, DatasetSelection, createDatasetSelection, isDatasetSelection } from "./dataset_selection.js";
2
- import { buildRefMap, filterMatchesToOptions, findFilterColumns } from "./filter_discovery.js";
2
+ import { FilterMatchOptions, buildRefMap, filterMatchesToOptions, findFilterColumns } from "./filter_discovery.js";
3
3
  import { BuildDatasetOptions, buildDatasetOptions } from "./build_dataset_options.js";
@@ -3,7 +3,7 @@ import { createPFrameForGraphs, isHiddenFromGraphColumn, isHiddenFromUIColumn }
3
3
  import { AndFilter, AnnotationFilter, AnnotationMode, AnnotationScript, AnnotationScript2, AnnotationStep, IsNA, Log10, NotFilter, NumericalComparisonFilter, OrFilter, PatternFilter, PatternPredicate, PatternPredicateContainSubsequence, PatternPredicateEquals, SortedCumulativeSum, TransformedColumn, ValueRank } from "./PlAnnotations/filter.js";
4
4
  import { AnnotationScriptUi, AnnotationStepUi, AnyForm, FilterUi, FilterUiOfType, FilterUiType, FormField, TypeField, TypeFieldRecord, TypeForm, TypeToLiteral, compileAnnotationScript, compileFilter, compileFilters, unreachable } from "./PlAnnotations/filters_ui.js";
5
5
  import { DatasetOption, DatasetSelection, createDatasetSelection, isDatasetSelection } from "./PlDatasetSelector/dataset_selection.js";
6
- import { buildRefMap, filterMatchesToOptions, findFilterColumns } from "./PlDatasetSelector/filter_discovery.js";
6
+ import { FilterMatchOptions, buildRefMap, filterMatchesToOptions, findFilterColumns } from "./PlDatasetSelector/filter_discovery.js";
7
7
  import { BuildDatasetOptions, buildDatasetOptions } from "./PlDatasetSelector/build_dataset_options.js";
8
8
  import { PTableParamsV2, PlDataTableFilterMeta, PlDataTableFilterSpecLeaf, PlDataTableFilters, PlDataTableFiltersWithMeta, PlDataTableGridStateCore, PlDataTableModel, PlDataTableSheet, PlDataTableSheetState, PlDataTableStateV2CacheEntry, PlDataTableStateV2Normalized, PlTableColumnIdJson } from "./PlDataTable/typesV6.js";
9
9
  import { PlDataTableStateV2, createDefaultPTableParams, createPlDataTableStateV2, upgradePlDataTableStateV2 } from "./PlDataTable/state-migration.js";
@@ -242,15 +242,14 @@ function leafToSpecQueryExpr(filter) {
242
242
  case "inSet": return {
243
243
  type: "isIn",
244
244
  input: resolveColumnRef(filter.column),
245
- set: filter.value
245
+ set: filter.value,
246
+ negate: false
246
247
  };
247
248
  case "notInSet": return {
248
- type: "not",
249
- input: {
250
- type: "isIn",
251
- input: resolveColumnRef(filter.column),
252
- set: filter.value
253
- }
249
+ type: "isIn",
250
+ input: resolveColumnRef(filter.column),
251
+ set: filter.value,
252
+ negate: true
254
253
  };
255
254
  case "isNA": return {
256
255
  type: "isNull",
@@ -264,7 +263,7 @@ function leafToSpecQueryExpr(filter) {
264
263
  }
265
264
  };
266
265
  case "ifNa": return {
267
- type: "ifNull",
266
+ type: "fillNull",
268
267
  input: resolveColumnRef(filter.column),
269
268
  replacement: {
270
269
  type: "constant",
@@ -1 +1 @@
1
- {"version":3,"file":"filterToQuery.cjs","names":["traverseFilterSpec"],"sources":["../../../src/filters/converters/filterToQuery.ts"],"sourcesContent":["import { assertNever } from \"@milaboratories/pl-model-common\";\nimport type {\n FilterSpec,\n FilterSpecLeaf,\n PTableColumnId,\n SingleAxisSelector,\n SpecQueryExpression,\n} from \"@milaboratories/pl-model-common\";\nimport { traverseFilterSpec } from \"../traverse\";\n\n/** Parses a CanonicalizedJson<PTableColumnId> string into a SpecQueryExpression reference. */\nfunction resolveColumnRef(columnStr: string): SpecQueryExpression {\n const parsed = JSON.parse(columnStr) as PTableColumnId;\n return parsed.type === \"axis\"\n ? { type: \"axisRef\", value: parsed.id as SingleAxisSelector }\n : { type: \"columnRef\", value: parsed.id };\n}\n\n/** Converts a FilterSpec tree into a SpecQueryExpression. */\nexport function filterSpecToSpecQueryExpr<Leaf extends FilterSpecLeaf<string>>(\n filter: FilterSpec<Leaf>,\n): SpecQueryExpression {\n return traverseFilterSpec(filter, {\n leaf: leafToSpecQueryExpr,\n and: (inputs) => {\n if (inputs.length === 0) {\n throw new Error(\"AND filter requires at least one operand\");\n }\n return { type: \"and\", input: inputs };\n },\n or: (inputs) => {\n if (inputs.length === 0) {\n throw new Error(\"OR filter requires at least one operand\");\n }\n return { type: \"or\", input: inputs };\n },\n not: (input) => ({ type: \"not\", input }),\n });\n}\n\nfunction leafToSpecQueryExpr<Leaf extends FilterSpecLeaf<string>>(\n filter: Leaf,\n): SpecQueryExpression {\n switch (filter.type) {\n case \"patternEquals\":\n return {\n type: \"stringEquals\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n caseInsensitive: false,\n };\n case \"patternNotEquals\":\n return {\n type: \"not\",\n input: {\n type: \"stringEquals\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n caseInsensitive: false,\n },\n };\n case \"patternContainSubsequence\":\n return {\n type: \"stringContains\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n caseInsensitive: false,\n };\n case \"patternNotContainSubsequence\":\n return {\n type: \"not\",\n input: {\n type: \"stringContains\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n caseInsensitive: false,\n },\n };\n case \"patternMatchesRegularExpression\":\n return {\n type: \"stringRegex\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n };\n case \"patternFuzzyContainSubsequence\":\n return {\n type: \"stringContainsFuzzy\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n maxEdits: filter.maxEdits ?? 1,\n caseInsensitive: false,\n substitutionsOnly: filter.substitutionsOnly ?? false,\n wildcard: filter.wildcard ?? null,\n };\n\n case \"equal\":\n return {\n type: \"numericComparison\",\n operand: \"eq\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"notEqual\":\n return {\n type: \"numericComparison\",\n operand: \"ne\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"lessThan\":\n return {\n type: \"numericComparison\",\n operand: \"lt\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"greaterThan\":\n return {\n type: \"numericComparison\",\n operand: \"gt\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"lessThanOrEqual\":\n return {\n type: \"numericComparison\",\n operand: \"le\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"greaterThanOrEqual\":\n return {\n type: \"numericComparison\",\n operand: \"ge\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n\n case \"equalToColumn\":\n return {\n type: \"numericComparison\",\n operand: \"eq\",\n left: resolveColumnRef(filter.column),\n right: resolveColumnRef(filter.rhs),\n };\n case \"lessThanColumn\": {\n const left = resolveColumnRef(filter.column);\n const right = resolveColumnRef(filter.rhs);\n if (filter.minDiff !== undefined && filter.minDiff !== 0) {\n return {\n type: \"numericComparison\",\n operand: \"lt\",\n left: {\n type: \"numericBinary\",\n operand: \"add\",\n left,\n right: { type: \"constant\", value: filter.minDiff },\n },\n right,\n };\n }\n return { type: \"numericComparison\", operand: \"lt\", left, right };\n }\n case \"greaterThanColumn\": {\n const left = resolveColumnRef(filter.column);\n const right = resolveColumnRef(filter.rhs);\n if (filter.minDiff !== undefined && filter.minDiff !== 0) {\n return {\n type: \"numericComparison\",\n operand: \"gt\",\n left: {\n type: \"numericBinary\",\n operand: \"add\",\n left,\n right: { type: \"constant\", value: filter.minDiff },\n },\n right,\n };\n }\n return { type: \"numericComparison\", operand: \"gt\", left, right };\n }\n case \"lessThanColumnOrEqual\": {\n const left = resolveColumnRef(filter.column);\n const right = resolveColumnRef(filter.rhs);\n if (filter.minDiff !== undefined && filter.minDiff !== 0) {\n return {\n type: \"numericComparison\",\n operand: \"le\",\n left: {\n type: \"numericBinary\",\n operand: \"add\",\n left,\n right: { type: \"constant\", value: filter.minDiff },\n },\n right,\n };\n }\n return { type: \"numericComparison\", operand: \"le\", left, right };\n }\n case \"greaterThanColumnOrEqual\": {\n const left = resolveColumnRef(filter.column);\n const right = resolveColumnRef(filter.rhs);\n if (filter.minDiff !== undefined && filter.minDiff !== 0) {\n return {\n type: \"numericComparison\",\n operand: \"ge\",\n left: {\n type: \"numericBinary\",\n operand: \"add\",\n left,\n right: { type: \"constant\", value: filter.minDiff },\n },\n right,\n };\n }\n return { type: \"numericComparison\", operand: \"ge\", left, right };\n }\n\n case \"inSet\":\n return {\n type: \"isIn\",\n input: resolveColumnRef(filter.column),\n set: filter.value,\n };\n case \"notInSet\":\n return {\n type: \"not\",\n input: {\n type: \"isIn\",\n input: resolveColumnRef(filter.column),\n set: filter.value,\n },\n };\n\n case \"isNA\":\n return {\n type: \"isNull\",\n input: resolveColumnRef(filter.column),\n };\n case \"isNotNA\":\n return {\n type: \"not\",\n input: {\n type: \"isNull\",\n input: resolveColumnRef(filter.column),\n },\n };\n\n case \"ifNa\":\n return {\n type: \"ifNull\",\n input: resolveColumnRef(filter.column),\n replacement: { type: \"constant\", value: filter.replacement },\n };\n\n case \"topN\":\n case \"bottomN\":\n throw new Error(`Filter type \"${filter.type}\" is not supported in query expressions`);\n\n case undefined:\n throw new Error(\"Filter type is undefined\");\n\n default:\n assertNever(filter);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"filterToQuery.cjs","names":["traverseFilterSpec"],"sources":["../../../src/filters/converters/filterToQuery.ts"],"sourcesContent":["import { assertNever } from \"@milaboratories/pl-model-common\";\nimport type {\n FilterSpec,\n FilterSpecLeaf,\n PTableColumnId,\n SingleAxisSelector,\n SpecQueryExpression,\n} from \"@milaboratories/pl-model-common\";\nimport { traverseFilterSpec } from \"../traverse\";\n\n/** Parses a CanonicalizedJson<PTableColumnId> string into a SpecQueryExpression reference. */\nfunction resolveColumnRef(columnStr: string): SpecQueryExpression {\n const parsed = JSON.parse(columnStr) as PTableColumnId;\n return parsed.type === \"axis\"\n ? { type: \"axisRef\", value: parsed.id as SingleAxisSelector }\n : { type: \"columnRef\", value: parsed.id };\n}\n\n/** Converts a FilterSpec tree into a SpecQueryExpression. */\nexport function filterSpecToSpecQueryExpr<Leaf extends FilterSpecLeaf<string>>(\n filter: FilterSpec<Leaf>,\n): SpecQueryExpression {\n return traverseFilterSpec(filter, {\n leaf: leafToSpecQueryExpr,\n and: (inputs) => {\n if (inputs.length === 0) {\n throw new Error(\"AND filter requires at least one operand\");\n }\n return { type: \"and\", input: inputs };\n },\n or: (inputs) => {\n if (inputs.length === 0) {\n throw new Error(\"OR filter requires at least one operand\");\n }\n return { type: \"or\", input: inputs };\n },\n not: (input) => ({ type: \"not\", input }),\n });\n}\n\nfunction leafToSpecQueryExpr<Leaf extends FilterSpecLeaf<string>>(\n filter: Leaf,\n): SpecQueryExpression {\n switch (filter.type) {\n case \"patternEquals\":\n return {\n type: \"stringEquals\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n caseInsensitive: false,\n };\n case \"patternNotEquals\":\n return {\n type: \"not\",\n input: {\n type: \"stringEquals\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n caseInsensitive: false,\n },\n };\n case \"patternContainSubsequence\":\n return {\n type: \"stringContains\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n caseInsensitive: false,\n };\n case \"patternNotContainSubsequence\":\n return {\n type: \"not\",\n input: {\n type: \"stringContains\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n caseInsensitive: false,\n },\n };\n case \"patternMatchesRegularExpression\":\n return {\n type: \"stringRegex\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n };\n case \"patternFuzzyContainSubsequence\":\n return {\n type: \"stringContainsFuzzy\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n maxEdits: filter.maxEdits ?? 1,\n caseInsensitive: false,\n substitutionsOnly: filter.substitutionsOnly ?? false,\n wildcard: filter.wildcard ?? null,\n };\n\n case \"equal\":\n return {\n type: \"numericComparison\",\n operand: \"eq\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"notEqual\":\n return {\n type: \"numericComparison\",\n operand: \"ne\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"lessThan\":\n return {\n type: \"numericComparison\",\n operand: \"lt\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"greaterThan\":\n return {\n type: \"numericComparison\",\n operand: \"gt\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"lessThanOrEqual\":\n return {\n type: \"numericComparison\",\n operand: \"le\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"greaterThanOrEqual\":\n return {\n type: \"numericComparison\",\n operand: \"ge\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n\n case \"equalToColumn\":\n return {\n type: \"numericComparison\",\n operand: \"eq\",\n left: resolveColumnRef(filter.column),\n right: resolveColumnRef(filter.rhs),\n };\n case \"lessThanColumn\": {\n const left = resolveColumnRef(filter.column);\n const right = resolveColumnRef(filter.rhs);\n if (filter.minDiff !== undefined && filter.minDiff !== 0) {\n return {\n type: \"numericComparison\",\n operand: \"lt\",\n left: {\n type: \"numericBinary\",\n operand: \"add\",\n left,\n right: { type: \"constant\", value: filter.minDiff },\n },\n right,\n };\n }\n return { type: \"numericComparison\", operand: \"lt\", left, right };\n }\n case \"greaterThanColumn\": {\n const left = resolveColumnRef(filter.column);\n const right = resolveColumnRef(filter.rhs);\n if (filter.minDiff !== undefined && filter.minDiff !== 0) {\n return {\n type: \"numericComparison\",\n operand: \"gt\",\n left: {\n type: \"numericBinary\",\n operand: \"add\",\n left,\n right: { type: \"constant\", value: filter.minDiff },\n },\n right,\n };\n }\n return { type: \"numericComparison\", operand: \"gt\", left, right };\n }\n case \"lessThanColumnOrEqual\": {\n const left = resolveColumnRef(filter.column);\n const right = resolveColumnRef(filter.rhs);\n if (filter.minDiff !== undefined && filter.minDiff !== 0) {\n return {\n type: \"numericComparison\",\n operand: \"le\",\n left: {\n type: \"numericBinary\",\n operand: \"add\",\n left,\n right: { type: \"constant\", value: filter.minDiff },\n },\n right,\n };\n }\n return { type: \"numericComparison\", operand: \"le\", left, right };\n }\n case \"greaterThanColumnOrEqual\": {\n const left = resolveColumnRef(filter.column);\n const right = resolveColumnRef(filter.rhs);\n if (filter.minDiff !== undefined && filter.minDiff !== 0) {\n return {\n type: \"numericComparison\",\n operand: \"ge\",\n left: {\n type: \"numericBinary\",\n operand: \"add\",\n left,\n right: { type: \"constant\", value: filter.minDiff },\n },\n right,\n };\n }\n return { type: \"numericComparison\", operand: \"ge\", left, right };\n }\n\n case \"inSet\":\n return {\n type: \"isIn\",\n input: resolveColumnRef(filter.column),\n set: filter.value,\n negate: false,\n };\n case \"notInSet\":\n return {\n type: \"isIn\",\n input: resolveColumnRef(filter.column),\n set: filter.value,\n negate: true,\n };\n\n case \"isNA\":\n return {\n type: \"isNull\",\n input: resolveColumnRef(filter.column),\n };\n case \"isNotNA\":\n return {\n type: \"not\",\n input: {\n type: \"isNull\",\n input: resolveColumnRef(filter.column),\n },\n };\n\n case \"ifNa\":\n return {\n type: \"fillNull\",\n input: resolveColumnRef(filter.column),\n replacement: { type: \"constant\", value: filter.replacement },\n };\n\n case \"topN\":\n case \"bottomN\":\n throw new Error(`Filter type \"${filter.type}\" is not supported in query expressions`);\n\n case undefined:\n throw new Error(\"Filter type is undefined\");\n\n default:\n assertNever(filter);\n }\n}\n"],"mappings":"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"}
@@ -241,15 +241,14 @@ function leafToSpecQueryExpr(filter) {
241
241
  case "inSet": return {
242
242
  type: "isIn",
243
243
  input: resolveColumnRef(filter.column),
244
- set: filter.value
244
+ set: filter.value,
245
+ negate: false
245
246
  };
246
247
  case "notInSet": return {
247
- type: "not",
248
- input: {
249
- type: "isIn",
250
- input: resolveColumnRef(filter.column),
251
- set: filter.value
252
- }
248
+ type: "isIn",
249
+ input: resolveColumnRef(filter.column),
250
+ set: filter.value,
251
+ negate: true
253
252
  };
254
253
  case "isNA": return {
255
254
  type: "isNull",
@@ -263,7 +262,7 @@ function leafToSpecQueryExpr(filter) {
263
262
  }
264
263
  };
265
264
  case "ifNa": return {
266
- type: "ifNull",
265
+ type: "fillNull",
267
266
  input: resolveColumnRef(filter.column),
268
267
  replacement: {
269
268
  type: "constant",
@@ -1 +1 @@
1
- {"version":3,"file":"filterToQuery.js","names":[],"sources":["../../../src/filters/converters/filterToQuery.ts"],"sourcesContent":["import { assertNever } from \"@milaboratories/pl-model-common\";\nimport type {\n FilterSpec,\n FilterSpecLeaf,\n PTableColumnId,\n SingleAxisSelector,\n SpecQueryExpression,\n} from \"@milaboratories/pl-model-common\";\nimport { traverseFilterSpec } from \"../traverse\";\n\n/** Parses a CanonicalizedJson<PTableColumnId> string into a SpecQueryExpression reference. */\nfunction resolveColumnRef(columnStr: string): SpecQueryExpression {\n const parsed = JSON.parse(columnStr) as PTableColumnId;\n return parsed.type === \"axis\"\n ? { type: \"axisRef\", value: parsed.id as SingleAxisSelector }\n : { type: \"columnRef\", value: parsed.id };\n}\n\n/** Converts a FilterSpec tree into a SpecQueryExpression. */\nexport function filterSpecToSpecQueryExpr<Leaf extends FilterSpecLeaf<string>>(\n filter: FilterSpec<Leaf>,\n): SpecQueryExpression {\n return traverseFilterSpec(filter, {\n leaf: leafToSpecQueryExpr,\n and: (inputs) => {\n if (inputs.length === 0) {\n throw new Error(\"AND filter requires at least one operand\");\n }\n return { type: \"and\", input: inputs };\n },\n or: (inputs) => {\n if (inputs.length === 0) {\n throw new Error(\"OR filter requires at least one operand\");\n }\n return { type: \"or\", input: inputs };\n },\n not: (input) => ({ type: \"not\", input }),\n });\n}\n\nfunction leafToSpecQueryExpr<Leaf extends FilterSpecLeaf<string>>(\n filter: Leaf,\n): SpecQueryExpression {\n switch (filter.type) {\n case \"patternEquals\":\n return {\n type: \"stringEquals\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n caseInsensitive: false,\n };\n case \"patternNotEquals\":\n return {\n type: \"not\",\n input: {\n type: \"stringEquals\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n caseInsensitive: false,\n },\n };\n case \"patternContainSubsequence\":\n return {\n type: \"stringContains\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n caseInsensitive: false,\n };\n case \"patternNotContainSubsequence\":\n return {\n type: \"not\",\n input: {\n type: \"stringContains\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n caseInsensitive: false,\n },\n };\n case \"patternMatchesRegularExpression\":\n return {\n type: \"stringRegex\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n };\n case \"patternFuzzyContainSubsequence\":\n return {\n type: \"stringContainsFuzzy\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n maxEdits: filter.maxEdits ?? 1,\n caseInsensitive: false,\n substitutionsOnly: filter.substitutionsOnly ?? false,\n wildcard: filter.wildcard ?? null,\n };\n\n case \"equal\":\n return {\n type: \"numericComparison\",\n operand: \"eq\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"notEqual\":\n return {\n type: \"numericComparison\",\n operand: \"ne\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"lessThan\":\n return {\n type: \"numericComparison\",\n operand: \"lt\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"greaterThan\":\n return {\n type: \"numericComparison\",\n operand: \"gt\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"lessThanOrEqual\":\n return {\n type: \"numericComparison\",\n operand: \"le\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"greaterThanOrEqual\":\n return {\n type: \"numericComparison\",\n operand: \"ge\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n\n case \"equalToColumn\":\n return {\n type: \"numericComparison\",\n operand: \"eq\",\n left: resolveColumnRef(filter.column),\n right: resolveColumnRef(filter.rhs),\n };\n case \"lessThanColumn\": {\n const left = resolveColumnRef(filter.column);\n const right = resolveColumnRef(filter.rhs);\n if (filter.minDiff !== undefined && filter.minDiff !== 0) {\n return {\n type: \"numericComparison\",\n operand: \"lt\",\n left: {\n type: \"numericBinary\",\n operand: \"add\",\n left,\n right: { type: \"constant\", value: filter.minDiff },\n },\n right,\n };\n }\n return { type: \"numericComparison\", operand: \"lt\", left, right };\n }\n case \"greaterThanColumn\": {\n const left = resolveColumnRef(filter.column);\n const right = resolveColumnRef(filter.rhs);\n if (filter.minDiff !== undefined && filter.minDiff !== 0) {\n return {\n type: \"numericComparison\",\n operand: \"gt\",\n left: {\n type: \"numericBinary\",\n operand: \"add\",\n left,\n right: { type: \"constant\", value: filter.minDiff },\n },\n right,\n };\n }\n return { type: \"numericComparison\", operand: \"gt\", left, right };\n }\n case \"lessThanColumnOrEqual\": {\n const left = resolveColumnRef(filter.column);\n const right = resolveColumnRef(filter.rhs);\n if (filter.minDiff !== undefined && filter.minDiff !== 0) {\n return {\n type: \"numericComparison\",\n operand: \"le\",\n left: {\n type: \"numericBinary\",\n operand: \"add\",\n left,\n right: { type: \"constant\", value: filter.minDiff },\n },\n right,\n };\n }\n return { type: \"numericComparison\", operand: \"le\", left, right };\n }\n case \"greaterThanColumnOrEqual\": {\n const left = resolveColumnRef(filter.column);\n const right = resolveColumnRef(filter.rhs);\n if (filter.minDiff !== undefined && filter.minDiff !== 0) {\n return {\n type: \"numericComparison\",\n operand: \"ge\",\n left: {\n type: \"numericBinary\",\n operand: \"add\",\n left,\n right: { type: \"constant\", value: filter.minDiff },\n },\n right,\n };\n }\n return { type: \"numericComparison\", operand: \"ge\", left, right };\n }\n\n case \"inSet\":\n return {\n type: \"isIn\",\n input: resolveColumnRef(filter.column),\n set: filter.value,\n };\n case \"notInSet\":\n return {\n type: \"not\",\n input: {\n type: \"isIn\",\n input: resolveColumnRef(filter.column),\n set: filter.value,\n },\n };\n\n case \"isNA\":\n return {\n type: \"isNull\",\n input: resolveColumnRef(filter.column),\n };\n case \"isNotNA\":\n return {\n type: \"not\",\n input: {\n type: \"isNull\",\n input: resolveColumnRef(filter.column),\n },\n };\n\n case \"ifNa\":\n return {\n type: \"ifNull\",\n input: resolveColumnRef(filter.column),\n replacement: { type: \"constant\", value: filter.replacement },\n };\n\n case \"topN\":\n case \"bottomN\":\n throw new Error(`Filter type \"${filter.type}\" is not supported in query expressions`);\n\n case undefined:\n throw new Error(\"Filter type is undefined\");\n\n default:\n assertNever(filter);\n }\n}\n"],"mappings":";;;;AAWA,SAAS,iBAAiB,WAAwC;CAChE,MAAM,SAAS,KAAK,MAAM,UAAU;AACpC,QAAO,OAAO,SAAS,SACnB;EAAE,MAAM;EAAW,OAAO,OAAO;EAA0B,GAC3D;EAAE,MAAM;EAAa,OAAO,OAAO;EAAI;;;AAI7C,SAAgB,0BACd,QACqB;AACrB,QAAO,mBAAmB,QAAQ;EAChC,MAAM;EACN,MAAM,WAAW;AACf,OAAI,OAAO,WAAW,EACpB,OAAM,IAAI,MAAM,2CAA2C;AAE7D,UAAO;IAAE,MAAM;IAAO,OAAO;IAAQ;;EAEvC,KAAK,WAAW;AACd,OAAI,OAAO,WAAW,EACpB,OAAM,IAAI,MAAM,0CAA0C;AAE5D,UAAO;IAAE,MAAM;IAAM,OAAO;IAAQ;;EAEtC,MAAM,WAAW;GAAE,MAAM;GAAO;GAAO;EACxC,CAAC;;AAGJ,SAAS,oBACP,QACqB;AACrB,SAAQ,OAAO,MAAf;EACE,KAAK,gBACH,QAAO;GACL,MAAM;GACN,OAAO,iBAAiB,OAAO,OAAO;GACtC,OAAO,OAAO;GACd,iBAAiB;GAClB;EACH,KAAK,mBACH,QAAO;GACL,MAAM;GACN,OAAO;IACL,MAAM;IACN,OAAO,iBAAiB,OAAO,OAAO;IACtC,OAAO,OAAO;IACd,iBAAiB;IAClB;GACF;EACH,KAAK,4BACH,QAAO;GACL,MAAM;GACN,OAAO,iBAAiB,OAAO,OAAO;GACtC,OAAO,OAAO;GACd,iBAAiB;GAClB;EACH,KAAK,+BACH,QAAO;GACL,MAAM;GACN,OAAO;IACL,MAAM;IACN,OAAO,iBAAiB,OAAO,OAAO;IACtC,OAAO,OAAO;IACd,iBAAiB;IAClB;GACF;EACH,KAAK,kCACH,QAAO;GACL,MAAM;GACN,OAAO,iBAAiB,OAAO,OAAO;GACtC,OAAO,OAAO;GACf;EACH,KAAK,iCACH,QAAO;GACL,MAAM;GACN,OAAO,iBAAiB,OAAO,OAAO;GACtC,OAAO,OAAO;GACd,UAAU,OAAO,YAAY;GAC7B,iBAAiB;GACjB,mBAAmB,OAAO,qBAAqB;GAC/C,UAAU,OAAO,YAAY;GAC9B;EAEH,KAAK,QACH,QAAO;GACL,MAAM;GACN,SAAS;GACT,MAAM,iBAAiB,OAAO,OAAO;GACrC,OAAO;IAAE,MAAM;IAAY,OAAO,OAAO;IAAG;GAC7C;EACH,KAAK,WACH,QAAO;GACL,MAAM;GACN,SAAS;GACT,MAAM,iBAAiB,OAAO,OAAO;GACrC,OAAO;IAAE,MAAM;IAAY,OAAO,OAAO;IAAG;GAC7C;EACH,KAAK,WACH,QAAO;GACL,MAAM;GACN,SAAS;GACT,MAAM,iBAAiB,OAAO,OAAO;GACrC,OAAO;IAAE,MAAM;IAAY,OAAO,OAAO;IAAG;GAC7C;EACH,KAAK,cACH,QAAO;GACL,MAAM;GACN,SAAS;GACT,MAAM,iBAAiB,OAAO,OAAO;GACrC,OAAO;IAAE,MAAM;IAAY,OAAO,OAAO;IAAG;GAC7C;EACH,KAAK,kBACH,QAAO;GACL,MAAM;GACN,SAAS;GACT,MAAM,iBAAiB,OAAO,OAAO;GACrC,OAAO;IAAE,MAAM;IAAY,OAAO,OAAO;IAAG;GAC7C;EACH,KAAK,qBACH,QAAO;GACL,MAAM;GACN,SAAS;GACT,MAAM,iBAAiB,OAAO,OAAO;GACrC,OAAO;IAAE,MAAM;IAAY,OAAO,OAAO;IAAG;GAC7C;EAEH,KAAK,gBACH,QAAO;GACL,MAAM;GACN,SAAS;GACT,MAAM,iBAAiB,OAAO,OAAO;GACrC,OAAO,iBAAiB,OAAO,IAAI;GACpC;EACH,KAAK,kBAAkB;GACrB,MAAM,OAAO,iBAAiB,OAAO,OAAO;GAC5C,MAAM,QAAQ,iBAAiB,OAAO,IAAI;AAC1C,OAAI,OAAO,YAAY,KAAA,KAAa,OAAO,YAAY,EACrD,QAAO;IACL,MAAM;IACN,SAAS;IACT,MAAM;KACJ,MAAM;KACN,SAAS;KACT;KACA,OAAO;MAAE,MAAM;MAAY,OAAO,OAAO;MAAS;KACnD;IACD;IACD;AAEH,UAAO;IAAE,MAAM;IAAqB,SAAS;IAAM;IAAM;IAAO;;EAElE,KAAK,qBAAqB;GACxB,MAAM,OAAO,iBAAiB,OAAO,OAAO;GAC5C,MAAM,QAAQ,iBAAiB,OAAO,IAAI;AAC1C,OAAI,OAAO,YAAY,KAAA,KAAa,OAAO,YAAY,EACrD,QAAO;IACL,MAAM;IACN,SAAS;IACT,MAAM;KACJ,MAAM;KACN,SAAS;KACT;KACA,OAAO;MAAE,MAAM;MAAY,OAAO,OAAO;MAAS;KACnD;IACD;IACD;AAEH,UAAO;IAAE,MAAM;IAAqB,SAAS;IAAM;IAAM;IAAO;;EAElE,KAAK,yBAAyB;GAC5B,MAAM,OAAO,iBAAiB,OAAO,OAAO;GAC5C,MAAM,QAAQ,iBAAiB,OAAO,IAAI;AAC1C,OAAI,OAAO,YAAY,KAAA,KAAa,OAAO,YAAY,EACrD,QAAO;IACL,MAAM;IACN,SAAS;IACT,MAAM;KACJ,MAAM;KACN,SAAS;KACT;KACA,OAAO;MAAE,MAAM;MAAY,OAAO,OAAO;MAAS;KACnD;IACD;IACD;AAEH,UAAO;IAAE,MAAM;IAAqB,SAAS;IAAM;IAAM;IAAO;;EAElE,KAAK,4BAA4B;GAC/B,MAAM,OAAO,iBAAiB,OAAO,OAAO;GAC5C,MAAM,QAAQ,iBAAiB,OAAO,IAAI;AAC1C,OAAI,OAAO,YAAY,KAAA,KAAa,OAAO,YAAY,EACrD,QAAO;IACL,MAAM;IACN,SAAS;IACT,MAAM;KACJ,MAAM;KACN,SAAS;KACT;KACA,OAAO;MAAE,MAAM;MAAY,OAAO,OAAO;MAAS;KACnD;IACD;IACD;AAEH,UAAO;IAAE,MAAM;IAAqB,SAAS;IAAM;IAAM;IAAO;;EAGlE,KAAK,QACH,QAAO;GACL,MAAM;GACN,OAAO,iBAAiB,OAAO,OAAO;GACtC,KAAK,OAAO;GACb;EACH,KAAK,WACH,QAAO;GACL,MAAM;GACN,OAAO;IACL,MAAM;IACN,OAAO,iBAAiB,OAAO,OAAO;IACtC,KAAK,OAAO;IACb;GACF;EAEH,KAAK,OACH,QAAO;GACL,MAAM;GACN,OAAO,iBAAiB,OAAO,OAAO;GACvC;EACH,KAAK,UACH,QAAO;GACL,MAAM;GACN,OAAO;IACL,MAAM;IACN,OAAO,iBAAiB,OAAO,OAAO;IACvC;GACF;EAEH,KAAK,OACH,QAAO;GACL,MAAM;GACN,OAAO,iBAAiB,OAAO,OAAO;GACtC,aAAa;IAAE,MAAM;IAAY,OAAO,OAAO;IAAa;GAC7D;EAEH,KAAK;EACL,KAAK,UACH,OAAM,IAAI,MAAM,gBAAgB,OAAO,KAAK,yCAAyC;EAEvF,KAAK,KAAA,EACH,OAAM,IAAI,MAAM,2BAA2B;EAE7C,QACE,aAAY,OAAO"}
1
+ {"version":3,"file":"filterToQuery.js","names":[],"sources":["../../../src/filters/converters/filterToQuery.ts"],"sourcesContent":["import { assertNever } from \"@milaboratories/pl-model-common\";\nimport type {\n FilterSpec,\n FilterSpecLeaf,\n PTableColumnId,\n SingleAxisSelector,\n SpecQueryExpression,\n} from \"@milaboratories/pl-model-common\";\nimport { traverseFilterSpec } from \"../traverse\";\n\n/** Parses a CanonicalizedJson<PTableColumnId> string into a SpecQueryExpression reference. */\nfunction resolveColumnRef(columnStr: string): SpecQueryExpression {\n const parsed = JSON.parse(columnStr) as PTableColumnId;\n return parsed.type === \"axis\"\n ? { type: \"axisRef\", value: parsed.id as SingleAxisSelector }\n : { type: \"columnRef\", value: parsed.id };\n}\n\n/** Converts a FilterSpec tree into a SpecQueryExpression. */\nexport function filterSpecToSpecQueryExpr<Leaf extends FilterSpecLeaf<string>>(\n filter: FilterSpec<Leaf>,\n): SpecQueryExpression {\n return traverseFilterSpec(filter, {\n leaf: leafToSpecQueryExpr,\n and: (inputs) => {\n if (inputs.length === 0) {\n throw new Error(\"AND filter requires at least one operand\");\n }\n return { type: \"and\", input: inputs };\n },\n or: (inputs) => {\n if (inputs.length === 0) {\n throw new Error(\"OR filter requires at least one operand\");\n }\n return { type: \"or\", input: inputs };\n },\n not: (input) => ({ type: \"not\", input }),\n });\n}\n\nfunction leafToSpecQueryExpr<Leaf extends FilterSpecLeaf<string>>(\n filter: Leaf,\n): SpecQueryExpression {\n switch (filter.type) {\n case \"patternEquals\":\n return {\n type: \"stringEquals\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n caseInsensitive: false,\n };\n case \"patternNotEquals\":\n return {\n type: \"not\",\n input: {\n type: \"stringEquals\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n caseInsensitive: false,\n },\n };\n case \"patternContainSubsequence\":\n return {\n type: \"stringContains\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n caseInsensitive: false,\n };\n case \"patternNotContainSubsequence\":\n return {\n type: \"not\",\n input: {\n type: \"stringContains\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n caseInsensitive: false,\n },\n };\n case \"patternMatchesRegularExpression\":\n return {\n type: \"stringRegex\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n };\n case \"patternFuzzyContainSubsequence\":\n return {\n type: \"stringContainsFuzzy\",\n input: resolveColumnRef(filter.column),\n value: filter.value,\n maxEdits: filter.maxEdits ?? 1,\n caseInsensitive: false,\n substitutionsOnly: filter.substitutionsOnly ?? false,\n wildcard: filter.wildcard ?? null,\n };\n\n case \"equal\":\n return {\n type: \"numericComparison\",\n operand: \"eq\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"notEqual\":\n return {\n type: \"numericComparison\",\n operand: \"ne\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"lessThan\":\n return {\n type: \"numericComparison\",\n operand: \"lt\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"greaterThan\":\n return {\n type: \"numericComparison\",\n operand: \"gt\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"lessThanOrEqual\":\n return {\n type: \"numericComparison\",\n operand: \"le\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n case \"greaterThanOrEqual\":\n return {\n type: \"numericComparison\",\n operand: \"ge\",\n left: resolveColumnRef(filter.column),\n right: { type: \"constant\", value: filter.x },\n };\n\n case \"equalToColumn\":\n return {\n type: \"numericComparison\",\n operand: \"eq\",\n left: resolveColumnRef(filter.column),\n right: resolveColumnRef(filter.rhs),\n };\n case \"lessThanColumn\": {\n const left = resolveColumnRef(filter.column);\n const right = resolveColumnRef(filter.rhs);\n if (filter.minDiff !== undefined && filter.minDiff !== 0) {\n return {\n type: \"numericComparison\",\n operand: \"lt\",\n left: {\n type: \"numericBinary\",\n operand: \"add\",\n left,\n right: { type: \"constant\", value: filter.minDiff },\n },\n right,\n };\n }\n return { type: \"numericComparison\", operand: \"lt\", left, right };\n }\n case \"greaterThanColumn\": {\n const left = resolveColumnRef(filter.column);\n const right = resolveColumnRef(filter.rhs);\n if (filter.minDiff !== undefined && filter.minDiff !== 0) {\n return {\n type: \"numericComparison\",\n operand: \"gt\",\n left: {\n type: \"numericBinary\",\n operand: \"add\",\n left,\n right: { type: \"constant\", value: filter.minDiff },\n },\n right,\n };\n }\n return { type: \"numericComparison\", operand: \"gt\", left, right };\n }\n case \"lessThanColumnOrEqual\": {\n const left = resolveColumnRef(filter.column);\n const right = resolveColumnRef(filter.rhs);\n if (filter.minDiff !== undefined && filter.minDiff !== 0) {\n return {\n type: \"numericComparison\",\n operand: \"le\",\n left: {\n type: \"numericBinary\",\n operand: \"add\",\n left,\n right: { type: \"constant\", value: filter.minDiff },\n },\n right,\n };\n }\n return { type: \"numericComparison\", operand: \"le\", left, right };\n }\n case \"greaterThanColumnOrEqual\": {\n const left = resolveColumnRef(filter.column);\n const right = resolveColumnRef(filter.rhs);\n if (filter.minDiff !== undefined && filter.minDiff !== 0) {\n return {\n type: \"numericComparison\",\n operand: \"ge\",\n left: {\n type: \"numericBinary\",\n operand: \"add\",\n left,\n right: { type: \"constant\", value: filter.minDiff },\n },\n right,\n };\n }\n return { type: \"numericComparison\", operand: \"ge\", left, right };\n }\n\n case \"inSet\":\n return {\n type: \"isIn\",\n input: resolveColumnRef(filter.column),\n set: filter.value,\n negate: false,\n };\n case \"notInSet\":\n return {\n type: \"isIn\",\n input: resolveColumnRef(filter.column),\n set: filter.value,\n negate: true,\n };\n\n case \"isNA\":\n return {\n type: \"isNull\",\n input: resolveColumnRef(filter.column),\n };\n case \"isNotNA\":\n return {\n type: \"not\",\n input: {\n type: \"isNull\",\n input: resolveColumnRef(filter.column),\n },\n };\n\n case \"ifNa\":\n return {\n type: \"fillNull\",\n input: resolveColumnRef(filter.column),\n replacement: { type: \"constant\", value: filter.replacement },\n };\n\n case \"topN\":\n case \"bottomN\":\n throw new Error(`Filter type \"${filter.type}\" is not supported in query expressions`);\n\n case undefined:\n throw new Error(\"Filter type is undefined\");\n\n default:\n assertNever(filter);\n }\n}\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -49,7 +49,7 @@ import { AnnotationSpec, AnnotationSpecUi, ExpressionSpec, FilterSpecUi } from "
49
49
  import { convertFilterSpecsToExpressionSpecs } from "./annotations/converter.js";
50
50
  import { AnnotationScriptUi, AnnotationStepUi, AnyForm, FilterUi, FilterUiOfType, FilterUiType, FormField, TypeField, TypeFieldRecord, TypeForm, TypeToLiteral, compileAnnotationScript, compileFilter, compileFilters, unreachable } from "./components/PlAnnotations/filters_ui.js";
51
51
  import { DatasetOption, DatasetSelection, createDatasetSelection, isDatasetSelection } from "./components/PlDatasetSelector/dataset_selection.js";
52
- import { buildRefMap, filterMatchesToOptions, findFilterColumns } from "./components/PlDatasetSelector/filter_discovery.js";
52
+ import { FilterMatchOptions, buildRefMap, filterMatchesToOptions, findFilterColumns } from "./components/PlDatasetSelector/filter_discovery.js";
53
53
  import { BuildDatasetOptions, buildDatasetOptions } from "./components/PlDatasetSelector/build_dataset_options.js";
54
54
  import { PTableParamsV2, PlDataTableFilterMeta, PlDataTableFilterSpecLeaf, PlDataTableFilters, PlDataTableFiltersWithMeta, PlDataTableGridStateCore, PlDataTableModel, PlDataTableSheet, PlDataTableSheetState, PlDataTableStateV2CacheEntry, PlDataTableStateV2Normalized, PlTableColumnIdJson } from "./components/PlDataTable/typesV6.js";
55
55
  import { PlDataTableStateV2, createDefaultPTableParams, createPlDataTableStateV2, upgradePlDataTableStateV2 } from "./components/PlDataTable/state-migration.js";
@@ -74,4 +74,4 @@ import { getService } from "./services/get_services.js";
74
74
  import { getEnvironmentValue } from "./env_value.js";
75
75
  export * from "@milaboratories/pl-model-common";
76
76
  export * from "@milaboratories/pl-error-like";
77
- export { ActAnd, ActExtractArchiveAndGetURL, ActFlatten, ActGetBlobContent, ActGetBlobContentAsJson, ActGetBlobContentAsString, ActGetDownloadedBlobContent, ActGetField, ActGetFromCtx, ActGetImmediate, ActGetLastLogs, ActGetLogHandle, ActGetOnDemandBlobContent, ActGetProgressLog, ActGetProgressLogWithInfo, ActGetResourceField, ActGetResourceValueAsJson, ActImportProgress, ActIsEmpty, ActIsolate, ActMakeArray, ActMakeObject, ActMapArrayValues, ActMapRecordValues, ActMapResourceFields, ActNot, ActOr, ActionResult, AnchorEntry, AnchoredBuildOptions, AnchoredColumnCollection, AnchoredFindColumnsOptions, AndFilter, AnnotationFilter, AnnotationMode, AnnotationScript, AnnotationScript2, AnnotationScriptUi, AnnotationSpec, AnnotationSpecUi, AnnotationStep, AnnotationStepUi, AnyForm, Args, ArrayColumnProvider, AxesVault, AxisLabelProvider, BLOCK_SERVICE_FLAGS, BLOCK_STORAGE_FACADE_VERSION, BlockApiV1, BlockApiV2, BlockConfig, BlockConfigV3, BlockConfigV4, BlockDefaultModelServices, BlockDefaultUiServices, BlockModel, BlockModelInfo, BlockModelV3, BlockRenderCtx, BlockServiceFlags, BlockStatePatch, type BlockStorage, BlockStorageFacade, BlockStorageFacadeCallbacks, BlockStorageFacadeHandles, type BlockStorageSchemaVersion, BuildDatasetOptions, BuildOptions, CancelSubscription, Cfg, CfgAnd, CfgBlobContent, CfgBlobContentAsJson, CfgBlobContentAsString, CfgDownloadedBlobContent, CfgExtractArchiveAndGetURL, CfgFlatten, CfgGetFromCtx, CfgGetJsonField, CfgGetResourceField, CfgImmediate, CfgImportProgress, CfgIsEmpty, CfgIsolate, CfgLastLogs, CfgLogHandle, CfgMakeArray, CfgMakeObject, CfgMapArrayValues, CfgMapRecordValues, CfgMapResourceFields, CfgNot, CfgOnDemandBlobContent, CfgOr, CfgProgressLog, CfgProgressLogWithInfo, CfgRenderingMode, CfgResourceValueAsJson, Checked, ColumnCollection, ColumnCollectionBuilder, ColumnData, ColumnDataStatus, ColumnMatch, ColumnMatcher, ColumnOrderRule, ColumnProvider, ColumnSelector, ColumnSnapshot, ColumnSnapshotProvider, ColumnSource, ColumnVariant, ColumnVisibilityRule, ColumnsDisplayOptions, ColumnsSelectorConfig, CommonFieldTraverseOps, CommonTraversalOps, ConfAction, ConfigRenderLambda, ConfigRenderLambdaFlags, ConfigResult, CurrentSdkInfo, DataModel, DataModelBuilder, DatasetOption, DatasetSelection, DeriveHref, DeriveLabelsFormatters, DeriveLabelsOptions, Entry, ExpandByPartitionOpts, ExpandByPartitionResult, ExpressionSpec, ExtractAction, ExtractFunctionHandleReturn, ExtractFutureRefType, FieldTraversalStep, FieldType, FilterSpec, FilterSpecLeaf, FilterSpecNode, FilterSpecOfType, FilterSpecType, FilterSpecUi, FilterUi, FilterUiOfType, FilterUiType, FindColumnsOptions, FormField, FutureRef, GetFieldStep, InferArgsType, InferBlockState, InferBlockStatePatch, InferDataType, InferFactoryData, InferFactoryModelServices, InferFactoryOutputs, InferFactoryParams, InferFactoryUiServices, InferHrefType, InferOutputType, InferOutputsFromConfigs, InferOutputsFromLambdas, InferOutputsType, InferPluginData, InferPluginHandle, InferPluginNames, InferPluginUiEntries, InferRenderFunctionReturn, InferUiState, InferVarTypeSafe, IsNA, It, internal_d_exports as JsRenderInternal, DeriveLabelsOptions as LabelDerivationOps, LinkerStep, Log10, MainOutputs, MatchQualifications, MatchVariant, MatchingMode, type MigrateBlockStorageConfig, type MigrationFailure, type MigrationResult, type MigrationSuccess, ModelServices, type MutateStoragePayload, NotFilter, NumericalComparisonFilter, OptionalPlResourceEntry, OrFilter, OutputColumnProvider, OutputColumnProviderOpts, OutputError, PColumnCollection, PColumnDataUniversal, PColumnEntryUniversal, PColumnEntryWithLabel, PColumnKeyList, PColumnLazyUniversal, PColumnLazyWithLabel, PColumnPredicate, PColumnResourceMapData, PColumnResourceMapEntry, PFrameImpl, POCExtractAction, PTableKey, PTableParamsV2, type ParamsInput, Patch, PatternFilter, PatternPredicate, PatternPredicateContainSubsequence, PatternPredicateEquals, PlDataTableFilterMeta, PlDataTableFilterSpecLeaf, PlDataTableFilters, PlDataTableFiltersWithMeta, PlDataTableGridStateCore, PlDataTableModel, PlDataTableSheet, PlDataTableSheetState, PlDataTableStateV2, PlDataTableStateV2CacheEntry, PlDataTableStateV2Normalized, PlMultiSequenceAlignmentColorSchemeOption, PlMultiSequenceAlignmentModel, PlMultiSequenceAlignmentSettings, PlMultiSequenceAlignmentWidget, PlResourceEntry, PlSelectionModel, PlTableColumnIdJson, Platforma, PlatformaApiVersion, PlatformaExtended, PlatformaFactory, PlatformaSDKVersion, PlatformaV1, PlatformaV2, PlatformaV3, type PluginConfig, type PluginData, PluginDataModel, PluginDataModelBuilder, type PluginDataModelVersions, type PluginFactory, PluginFactoryLike, PluginHandle, PluginInstance, PluginModel, type PluginName, type PluginOutputs, type PluginParams, type PluginPublicOutputs, type PluginRecord, type PluginRegistry, PluginRenderCtx, PrimitiveOrConfig, PrimitiveToCfg, type PublicOutputFieldDef, RT_BINARY_PARTITIONED, RT_BINARY_SUPER_PARTITIONED, RT_JSON_PARTITIONED, RT_JSON_SUPER_PARTITIONED, RT_PARQUET_PARTITIONED, RT_PARQUET_SUPER_PARTITIONED, RT_RESOURCE_MAP, RT_RESOURCE_MAP_PARTITIONED, RelaxedAxisSelector, RelaxedColumnSelector, RelaxedRecord, RelaxedStringMatchers, RenderCtx, RenderCtxBase, RenderCtxLegacy, RenderFunction, RenderFunctionLegacy, ResolveCfgType, ResolveModelServices, ResolveUiServices, ResourceTraversalOps, ResourceType, ResultPool, ResultPoolColumnSnapshotProvider, SdkInfo, ServiceProxy, SimplifiedPColumnSpec, SimplifiedUniversalPColumnEntry, SnapshotColumnProvider, SortedCumulativeSum, Entry as SpecExtractorResult, SplitAxis, StagingOutputs, StdCtx, StdCtxArgsOnly, StringMatcher, TooltipEntry, Trace, TraceEntry, TransformedColumn, TreeNodeAccessor, TypeField, TypeFieldRecord, TypeForm, TypeToLiteral, TypedConfig, TypedConfigOrConfigLambda, TypedConfigOrString, UiServices, UiState, Unionize, UniversalColumnOption, UnwrapFutureRef, ValueRank, type VersionedData, allPColumnsReady, and, blockServiceNames, buildDatasetOptions, buildRefMap, buildServices, collectCtxColumnSnapshotProviders, compileAnnotationScript, compileFilter, compileFilters, convertColumnSelectorToMultiColumnSelector, convertFilterSpecsToExpressionSpecs, convertFilterUiToExpressionImpl, convertFilterUiToExpressions, convertOrParsePColumnData, convertRelaxedAxisSelectorToMultiAxisSelector, convertRelaxedColumnSelectorToMultiColumnSelector, createBlockStorage, createColumnSnapshot, createDatasetSelection, createDefaultPTableParams, createPFrameForGraphs, createPlDataTable, createPlDataTableOptionsV3, createPlDataTableSheet, createPlDataTableStateV2, createPlDataTableV2, createPlDataTableV3, createPlSelectionModel, createReadyColumnData, createRowSelectionColumn, createServiceProxy, deriveDataFromStorage, deriveDistinctLabels, deriveDistinctTooltips, deriveLabels, discoverTableColumnSnaphots, distillFilterSpec, downgradeCfgOrLambda, enrichCompatible, expandByPartition, extractArchiveAndGetURL, extractConfig, filterDataInfoEntries, filterMatchesToOptions, filterSpecToSpecQueryExpr, findFilterColumns, flatten, fromPlOption, fromPlRef, getAllRelatedColumns, getAvailableWithLinkersAxes, getAxisUniqueValues, getBlobContent, getBlobContentAsJson, getBlobContentAsString, getColumnOrAxisValueLabelsId, getColumnSpecById, getColumnUniqueValues, getColumnsFull, getDownloadedBlobContent, getEffectiveVisibility, getEnvironmentValue, getFromCfg, getImmediate, getImportProgress, getJsonField, getLastLogs, getLogHandle, getOnDemandBlobContent, getOrderPriority, getPartitionKeysList, getPlatformaApiVersion, getPluginData, getProgressLog, getProgressLogWithInfo, getRawPlatformaInstance, getRelatedColumns, getRequestColumnsFromSelectedSources, getResourceField, getResourceValueAsJson, getService, getSingleColumnData, getStorageData, getUniquePartitionKeys, getUniqueSourceValuesWithLabels, ifDef, isBlockStorage, isColumnHidden, isColumnOptional, isColumnSnapshotProvider, isConfigLambda, isDatasetSelection, isEmpty, isHiddenFromGraphColumn, isHiddenFromUIColumn, isPColumnReady, isPluginOutputKey, isolate, makeArray, makeObject, mapArrayValues, mapRecordValues, mapResourceFields, migrateBlockStorage, normalizeBlockStorage, not, or, parsePColumnData, parseResourceMap, pluginOutputKey, pluginOutputPrefix, readOutput, registerFacadeCallbacks, toColumnSnapshotProvider, unreachable, updateStorageData, upgradePlDataTableStateV2, wrapOutputs };
77
+ export { ActAnd, ActExtractArchiveAndGetURL, ActFlatten, ActGetBlobContent, ActGetBlobContentAsJson, ActGetBlobContentAsString, ActGetDownloadedBlobContent, ActGetField, ActGetFromCtx, ActGetImmediate, ActGetLastLogs, ActGetLogHandle, ActGetOnDemandBlobContent, ActGetProgressLog, ActGetProgressLogWithInfo, ActGetResourceField, ActGetResourceValueAsJson, ActImportProgress, ActIsEmpty, ActIsolate, ActMakeArray, ActMakeObject, ActMapArrayValues, ActMapRecordValues, ActMapResourceFields, ActNot, ActOr, ActionResult, AnchorEntry, AnchoredBuildOptions, AnchoredColumnCollection, AnchoredFindColumnsOptions, AndFilter, AnnotationFilter, AnnotationMode, AnnotationScript, AnnotationScript2, AnnotationScriptUi, AnnotationSpec, AnnotationSpecUi, AnnotationStep, AnnotationStepUi, AnyForm, Args, ArrayColumnProvider, AxesVault, AxisLabelProvider, BLOCK_SERVICE_FLAGS, BLOCK_STORAGE_FACADE_VERSION, BlockApiV1, BlockApiV2, BlockConfig, BlockConfigV3, BlockConfigV4, BlockDefaultModelServices, BlockDefaultUiServices, BlockModel, BlockModelInfo, BlockModelV3, BlockRenderCtx, BlockServiceFlags, BlockStatePatch, type BlockStorage, BlockStorageFacade, BlockStorageFacadeCallbacks, BlockStorageFacadeHandles, type BlockStorageSchemaVersion, BuildDatasetOptions, BuildOptions, CancelSubscription, Cfg, CfgAnd, CfgBlobContent, CfgBlobContentAsJson, CfgBlobContentAsString, CfgDownloadedBlobContent, CfgExtractArchiveAndGetURL, CfgFlatten, CfgGetFromCtx, CfgGetJsonField, CfgGetResourceField, CfgImmediate, CfgImportProgress, CfgIsEmpty, CfgIsolate, CfgLastLogs, CfgLogHandle, CfgMakeArray, CfgMakeObject, CfgMapArrayValues, CfgMapRecordValues, CfgMapResourceFields, CfgNot, CfgOnDemandBlobContent, CfgOr, CfgProgressLog, CfgProgressLogWithInfo, CfgRenderingMode, CfgResourceValueAsJson, Checked, ColumnCollection, ColumnCollectionBuilder, ColumnData, ColumnDataStatus, ColumnMatch, ColumnMatcher, ColumnOrderRule, ColumnProvider, ColumnSelector, ColumnSnapshot, ColumnSnapshotProvider, ColumnSource, ColumnVariant, ColumnVisibilityRule, ColumnsDisplayOptions, ColumnsSelectorConfig, CommonFieldTraverseOps, CommonTraversalOps, ConfAction, ConfigRenderLambda, ConfigRenderLambdaFlags, ConfigResult, CurrentSdkInfo, DataModel, DataModelBuilder, DatasetOption, DatasetSelection, DeriveHref, DeriveLabelsFormatters, DeriveLabelsOptions, Entry, ExpandByPartitionOpts, ExpandByPartitionResult, ExpressionSpec, ExtractAction, ExtractFunctionHandleReturn, ExtractFutureRefType, FieldTraversalStep, FieldType, FilterMatchOptions, FilterSpec, FilterSpecLeaf, FilterSpecNode, FilterSpecOfType, FilterSpecType, FilterSpecUi, FilterUi, FilterUiOfType, FilterUiType, FindColumnsOptions, FormField, FutureRef, GetFieldStep, InferArgsType, InferBlockState, InferBlockStatePatch, InferDataType, InferFactoryData, InferFactoryModelServices, InferFactoryOutputs, InferFactoryParams, InferFactoryUiServices, InferHrefType, InferOutputType, InferOutputsFromConfigs, InferOutputsFromLambdas, InferOutputsType, InferPluginData, InferPluginHandle, InferPluginNames, InferPluginUiEntries, InferRenderFunctionReturn, InferUiState, InferVarTypeSafe, IsNA, It, internal_d_exports as JsRenderInternal, DeriveLabelsOptions as LabelDerivationOps, LinkerStep, Log10, MainOutputs, MatchQualifications, MatchVariant, MatchingMode, type MigrateBlockStorageConfig, type MigrationFailure, type MigrationResult, type MigrationSuccess, ModelServices, type MutateStoragePayload, NotFilter, NumericalComparisonFilter, OptionalPlResourceEntry, OrFilter, OutputColumnProvider, OutputColumnProviderOpts, OutputError, PColumnCollection, PColumnDataUniversal, PColumnEntryUniversal, PColumnEntryWithLabel, PColumnKeyList, PColumnLazyUniversal, PColumnLazyWithLabel, PColumnPredicate, PColumnResourceMapData, PColumnResourceMapEntry, PFrameImpl, POCExtractAction, PTableKey, PTableParamsV2, type ParamsInput, Patch, PatternFilter, PatternPredicate, PatternPredicateContainSubsequence, PatternPredicateEquals, PlDataTableFilterMeta, PlDataTableFilterSpecLeaf, PlDataTableFilters, PlDataTableFiltersWithMeta, PlDataTableGridStateCore, PlDataTableModel, PlDataTableSheet, PlDataTableSheetState, PlDataTableStateV2, PlDataTableStateV2CacheEntry, PlDataTableStateV2Normalized, PlMultiSequenceAlignmentColorSchemeOption, PlMultiSequenceAlignmentModel, PlMultiSequenceAlignmentSettings, PlMultiSequenceAlignmentWidget, PlResourceEntry, PlSelectionModel, PlTableColumnIdJson, Platforma, PlatformaApiVersion, PlatformaExtended, PlatformaFactory, PlatformaSDKVersion, PlatformaV1, PlatformaV2, PlatformaV3, type PluginConfig, type PluginData, PluginDataModel, PluginDataModelBuilder, type PluginDataModelVersions, type PluginFactory, PluginFactoryLike, PluginHandle, PluginInstance, PluginModel, type PluginName, type PluginOutputs, type PluginParams, type PluginPublicOutputs, type PluginRecord, type PluginRegistry, PluginRenderCtx, PrimitiveOrConfig, PrimitiveToCfg, type PublicOutputFieldDef, RT_BINARY_PARTITIONED, RT_BINARY_SUPER_PARTITIONED, RT_JSON_PARTITIONED, RT_JSON_SUPER_PARTITIONED, RT_PARQUET_PARTITIONED, RT_PARQUET_SUPER_PARTITIONED, RT_RESOURCE_MAP, RT_RESOURCE_MAP_PARTITIONED, RelaxedAxisSelector, RelaxedColumnSelector, RelaxedRecord, RelaxedStringMatchers, RenderCtx, RenderCtxBase, RenderCtxLegacy, RenderFunction, RenderFunctionLegacy, ResolveCfgType, ResolveModelServices, ResolveUiServices, ResourceTraversalOps, ResourceType, ResultPool, ResultPoolColumnSnapshotProvider, SdkInfo, ServiceProxy, SimplifiedPColumnSpec, SimplifiedUniversalPColumnEntry, SnapshotColumnProvider, SortedCumulativeSum, Entry as SpecExtractorResult, SplitAxis, StagingOutputs, StdCtx, StdCtxArgsOnly, StringMatcher, TooltipEntry, Trace, TraceEntry, TransformedColumn, TreeNodeAccessor, TypeField, TypeFieldRecord, TypeForm, TypeToLiteral, TypedConfig, TypedConfigOrConfigLambda, TypedConfigOrString, UiServices, UiState, Unionize, UniversalColumnOption, UnwrapFutureRef, ValueRank, type VersionedData, allPColumnsReady, and, blockServiceNames, buildDatasetOptions, buildRefMap, buildServices, collectCtxColumnSnapshotProviders, compileAnnotationScript, compileFilter, compileFilters, convertColumnSelectorToMultiColumnSelector, convertFilterSpecsToExpressionSpecs, convertFilterUiToExpressionImpl, convertFilterUiToExpressions, convertOrParsePColumnData, convertRelaxedAxisSelectorToMultiAxisSelector, convertRelaxedColumnSelectorToMultiColumnSelector, createBlockStorage, createColumnSnapshot, createDatasetSelection, createDefaultPTableParams, createPFrameForGraphs, createPlDataTable, createPlDataTableOptionsV3, createPlDataTableSheet, createPlDataTableStateV2, createPlDataTableV2, createPlDataTableV3, createPlSelectionModel, createReadyColumnData, createRowSelectionColumn, createServiceProxy, deriveDataFromStorage, deriveDistinctLabels, deriveDistinctTooltips, deriveLabels, discoverTableColumnSnaphots, distillFilterSpec, downgradeCfgOrLambda, enrichCompatible, expandByPartition, extractArchiveAndGetURL, extractConfig, filterDataInfoEntries, filterMatchesToOptions, filterSpecToSpecQueryExpr, findFilterColumns, flatten, fromPlOption, fromPlRef, getAllRelatedColumns, getAvailableWithLinkersAxes, getAxisUniqueValues, getBlobContent, getBlobContentAsJson, getBlobContentAsString, getColumnOrAxisValueLabelsId, getColumnSpecById, getColumnUniqueValues, getColumnsFull, getDownloadedBlobContent, getEffectiveVisibility, getEnvironmentValue, getFromCfg, getImmediate, getImportProgress, getJsonField, getLastLogs, getLogHandle, getOnDemandBlobContent, getOrderPriority, getPartitionKeysList, getPlatformaApiVersion, getPluginData, getProgressLog, getProgressLogWithInfo, getRawPlatformaInstance, getRelatedColumns, getRequestColumnsFromSelectedSources, getResourceField, getResourceValueAsJson, getService, getSingleColumnData, getStorageData, getUniquePartitionKeys, getUniqueSourceValuesWithLabels, ifDef, isBlockStorage, isColumnHidden, isColumnOptional, isColumnSnapshotProvider, isConfigLambda, isDatasetSelection, isEmpty, isHiddenFromGraphColumn, isHiddenFromUIColumn, isPColumnReady, isPluginOutputKey, isolate, makeArray, makeObject, mapArrayValues, mapRecordValues, mapResourceFields, migrateBlockStorage, normalizeBlockStorage, not, or, parsePColumnData, parseResourceMap, pluginOutputKey, pluginOutputPrefix, readOutput, registerFacadeCallbacks, toColumnSnapshotProvider, unreachable, updateStorageData, upgradePlDataTableStateV2, wrapOutputs };
package/dist/package.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "1.75.10";
2
+ var version = "1.76.5";
3
3
  //#endregion
4
4
  Object.defineProperty(exports, "version", {
5
5
  enumerable: true,
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "1.75.10";
2
+ var version = "1.76.5";
3
3
  //#endregion
4
4
  export { version };
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/model",
3
- "version": "1.75.10",
3
+ "version": "1.76.5",
4
4
  "description": "Platforma.bio SDK / Block Model",
5
5
  "files": [
6
6
  "./dist/**/*",
@@ -32,20 +32,20 @@
32
32
  "zod": "~3.25.76",
33
33
  "@milaboratories/helpers": "1.14.2",
34
34
  "@milaboratories/pl-error-like": "1.12.10",
35
- "@milaboratories/pl-model-middle-layer": "1.19.3",
36
- "@milaboratories/pl-model-common": "1.41.2",
37
- "@milaboratories/ptabler-expression-js": "1.2.24"
35
+ "@milaboratories/pl-model-common": "1.42.0",
36
+ "@milaboratories/pl-model-middle-layer": "1.19.4",
37
+ "@milaboratories/ptabler-expression-js": "1.2.25"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@vitest/coverage-istanbul": "^4.1.3",
41
41
  "fast-json-patch": "^3.1.1",
42
42
  "typescript": "~5.9.3",
43
43
  "vitest": "^4.1.3",
44
- "@milaboratories/ts-builder": "1.4.0",
45
- "@milaboratories/build-configs": "2.0.0",
46
- "@milaboratories/pf-spec-driver": "1.3.15",
47
44
  "@milaboratories/ts-configs": "1.2.3",
48
- "@milaboratories/pf-driver": "1.4.10"
45
+ "@milaboratories/build-configs": "2.0.0",
46
+ "@milaboratories/pf-driver": "1.4.11",
47
+ "@milaboratories/pf-spec-driver": "1.3.16",
48
+ "@milaboratories/ts-builder": "1.4.0"
49
49
  },
50
50
  "scripts": {
51
51
  "build": "ts-builder build --target node",
@@ -31,7 +31,11 @@ export type BuildDatasetOptions = {
31
31
  * accept-all.
32
32
  */
33
33
  filter?: SpecPredicateOption;
34
- /** Formatting options for filter labels. */
34
+ /**
35
+ * Formatting options forwarded to label derivation for both filter and
36
+ * enrichment rows. `formatters.native` on the filter path is overridden
37
+ * — see `FilterMatchOptions.labelOptions`.
38
+ */
35
39
  labelOptions?: DeriveLabelsOptions;
36
40
  /**
37
41
  * Enables enrichment discovery and filters hits attached to
@@ -96,7 +100,11 @@ export function buildDatasetOptions(
96
100
  const filters =
97
101
  filterMatches.length === 0
98
102
  ? undefined
99
- : filterMatchesToOptions(filterMatches, refMap, opts?.labelOptions);
103
+ : filterMatchesToOptions(filterMatches, {
104
+ refsByObjectId: refMap,
105
+ datasetSpec,
106
+ labelOptions: opts?.labelOptions,
107
+ });
100
108
 
101
109
  let enrichments;
102
110
  if (enrichmentCollection && withEnrichments) {
@@ -1,5 +1,5 @@
1
1
  import { Annotation, createPlRef } from "@milaboratories/pl-model-common";
2
- import type { AxisSpec, PColumnSpec, PlRef, PObjectId } from "@milaboratories/pl-model-common";
2
+ import type { AxisSpec, PColumnSpec, PObjectId } from "@milaboratories/pl-model-common";
3
3
  import { SpecDriver } from "@milaboratories/pf-spec-driver";
4
4
  import canonicalize from "canonicalize";
5
5
  import { afterEach, describe, expect, test } from "vitest";
@@ -85,10 +85,7 @@ describe("buildRefMap", () => {
85
85
  test("maps canonicalized PlRef to original ref", () => {
86
86
  const ref1 = createPlRef("b1", "out1");
87
87
  const ref2 = createPlRef("b2", "out2", true);
88
- const entries = [{ ref: ref1 }, { ref: ref2 }];
89
-
90
- const map = buildRefMap(entries);
91
-
88
+ const map = buildRefMap([{ ref: ref1 }, { ref: ref2 }]);
92
89
  expect(map.get(canonicalize(ref1)! as PObjectId)).toBe(ref1);
93
90
  expect(map.get(canonicalize(ref2)! as PObjectId)).toBe(ref2);
94
91
  expect(map.size).toBe(2);
@@ -104,18 +101,10 @@ describe("filterMatchesToOptions", () => {
104
101
  const filterRef1 = createPlRef("b1", "filter-top1000");
105
102
  const filterRef2 = createPlRef("b1", "filter-highconf");
106
103
 
107
- // Build ref map from entries (simulating result pool)
108
- const refMap = buildRefMap([
109
- { ref: anchorSnap.id as unknown as PlRef }, // anchor — won't be looked up
110
- { ref: filterRef1 },
111
- { ref: filterRef2 },
112
- ]);
113
-
114
- // Build filter specs with isSubset annotation
104
+ const refMap = buildRefMap([{ ref: filterRef1 }, { ref: filterRef2 }]);
115
105
  const filterSpec1 = spec("filter1", [axis("sample")], { [Annotation.IsSubset]: "true" });
116
106
  const filterSpec2 = spec("filter2", [axis("sample")], { [Annotation.IsSubset]: "true" });
117
107
 
118
- // Use the canonical PlRef as the PObjectId (matches how result pool works)
119
108
  const f1Snap = snap(canonicalize(filterRef1)! as string, filterSpec1);
120
109
  const f2Snap = snap(canonicalize(filterRef2)! as string, filterSpec2);
121
110
 
@@ -126,7 +115,10 @@ describe("filterMatchesToOptions", () => {
126
115
  const matches = findFilterColumns(collection);
127
116
  expect(matches.length).toBe(2);
128
117
 
129
- const options = filterMatchesToOptions(matches, refMap);
118
+ const options = filterMatchesToOptions(matches, {
119
+ refsByObjectId: refMap,
120
+ datasetSpec: anchorSpec,
121
+ });
130
122
  expect(options).toHaveLength(2);
131
123
  // Each option has a ref and label
132
124
  for (const opt of options) {
@@ -136,11 +128,114 @@ describe("filterMatchesToOptions", () => {
136
128
  }
137
129
  });
138
130
 
131
+ test("single filter: dataset name prefixes the discriminating trace step", () => {
132
+ // Regression: without the dataset in the input, deriveDistinctLabels
133
+ // picks the highest-importance step it sees — the dataset's own
134
+ // `samples-and-data/dataset` step (importance 100) — and the filter
135
+ // shows "Bulk" instead of "Top 10". Appending the dataset forces the
136
+ // algorithm to include the lower-importance lead-selection step too.
137
+ const datasetSpec = spec("dataset", [axis("sample"), axis("gene")], {
138
+ [Annotation.Label]: "Bulk",
139
+ [Annotation.Trace]: JSON.stringify([
140
+ { type: "milaboratories.samples-and-data/dataset", label: "Bulk", importance: 100 },
141
+ { type: "milaboratories.mixcr-clonotyping", label: "MiXCR", importance: 20 },
142
+ ]),
143
+ });
144
+
145
+ const filterRef = createPlRef("b1", "filter");
146
+ const refMap = buildRefMap([{ ref: filterRef }]);
147
+ const filterSpec = spec("filter", [axis("sample")], {
148
+ [Annotation.IsSubset]: "true",
149
+ [Annotation.Label]: "Selected Leads",
150
+ [Annotation.Trace]: JSON.stringify([
151
+ { type: "milaboratories.samples-and-data/dataset", label: "Bulk", importance: 100 },
152
+ { type: "milaboratories.mixcr-clonotyping", label: "MiXCR", importance: 20 },
153
+ { type: "milaboratories.antibody-tcr-lead-selection", label: "Top 10", importance: 30 },
154
+ ]),
155
+ });
156
+ const fSnap = snap(canonicalize(filterRef)! as string, filterSpec);
157
+
158
+ const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
159
+ builder.addSource([fSnap, anchorSnap]);
160
+ const collection = builder.build({ anchors: { main: anchorSpec } })!;
161
+
162
+ const matches = findFilterColumns(collection);
163
+ expect(matches).toHaveLength(1);
164
+
165
+ const options = filterMatchesToOptions(matches, { refsByObjectId: refMap, datasetSpec });
166
+ expect(options).toHaveLength(1);
167
+ expect(options[0].label).toBe("Bulk / Top 10");
168
+
169
+ // Caller's `formatters.native` must NOT override the internal
170
+ // suppression — otherwise the algorithm short-circuits on the
171
+ // inherited "Selected Leads" label.
172
+ const withCustomNative = filterMatchesToOptions(matches, {
173
+ refsByObjectId: refMap,
174
+ datasetSpec,
175
+ labelOptions: { formatters: { native: (l) => `<<${l}>>` } },
176
+ });
177
+ expect(withCustomNative[0].label).toBe("Bulk / Top 10");
178
+
179
+ // Non-native label options (here `separator`) flow through.
180
+ const withSeparator = filterMatchesToOptions(matches, {
181
+ refsByObjectId: refMap,
182
+ datasetSpec,
183
+ labelOptions: { separator: " :: " },
184
+ });
185
+ expect(withSeparator[0].label).toBe("Bulk :: Top 10");
186
+ });
187
+
188
+ test("multiple filters with shared dataset trace disambiguate by filter-specific steps", () => {
189
+ const datasetSpec = spec("dataset", [axis("sample"), axis("gene")], {
190
+ [Annotation.Label]: "Bulk",
191
+ [Annotation.Trace]: JSON.stringify([
192
+ { type: "milaboratories.samples-and-data/dataset", label: "Bulk", importance: 100 },
193
+ { type: "milaboratories.mixcr-clonotyping", label: "MiXCR", importance: 20 },
194
+ ]),
195
+ });
196
+
197
+ const ref1 = createPlRef("b1", "f1");
198
+ const ref2 = createPlRef("b2", "f2");
199
+ const refMap = buildRefMap([{ ref: ref1 }, { ref: ref2 }]);
200
+ const filterSpec1 = spec("filter1", [axis("sample")], {
201
+ [Annotation.IsSubset]: "true",
202
+ [Annotation.Label]: "Selected Leads",
203
+ [Annotation.Trace]: JSON.stringify([
204
+ { type: "milaboratories.samples-and-data/dataset", label: "Bulk", importance: 100 },
205
+ { type: "milaboratories.mixcr-clonotyping", label: "MiXCR", importance: 20 },
206
+ { type: "milaboratories.antibody-tcr-lead-selection", label: "Top 10", importance: 30 },
207
+ ]),
208
+ });
209
+ const filterSpec2 = spec("filter2", [axis("sample")], {
210
+ [Annotation.IsSubset]: "true",
211
+ [Annotation.Label]: "Selected Leads",
212
+ [Annotation.Trace]: JSON.stringify([
213
+ { type: "milaboratories.samples-and-data/dataset", label: "Bulk", importance: 100 },
214
+ { type: "milaboratories.mixcr-clonotyping", label: "MiXCR", importance: 20 },
215
+ { type: "milaboratories.antibody-tcr-lead-selection", label: "Top 11", importance: 30 },
216
+ ]),
217
+ });
218
+ const f1Snap = snap(canonicalize(ref1)! as string, filterSpec1);
219
+ const f2Snap = snap(canonicalize(ref2)! as string, filterSpec2);
220
+
221
+ const builder = new ColumnCollectionBuilder(createSpecFrameCtx());
222
+ builder.addSource([f1Snap, f2Snap, anchorSnap]);
223
+ const collection = builder.build({ anchors: { main: anchorSpec } })!;
224
+
225
+ const matches = findFilterColumns(collection);
226
+ expect(matches).toHaveLength(2);
227
+
228
+ const options = filterMatchesToOptions(matches, { refsByObjectId: refMap, datasetSpec });
229
+ expect(options.map((o) => o.label).sort()).toEqual(["Bulk / Top 10", "Bulk / Top 11"]);
230
+ });
231
+
139
232
  test("returns empty array for empty matches", () => {
140
- expect(filterMatchesToOptions([], new Map())).toEqual([]);
233
+ expect(
234
+ filterMatchesToOptions([], { refsByObjectId: new Map(), datasetSpec: anchorSpec }),
235
+ ).toEqual([]);
141
236
  });
142
237
 
143
- test("skips entries whose ref is not found in map", () => {
238
+ test("skips entries whose id is not in refsByObjectId", () => {
144
239
  const knownRef = createPlRef("b1", "known");
145
240
  const knownSpec = spec("known", [axis("sample")], { [Annotation.IsSubset]: "true" });
146
241
  const orphanSpec = spec("orphan", [axis("sample")], { [Annotation.IsSubset]: "true" });
@@ -153,9 +248,11 @@ describe("filterMatchesToOptions", () => {
153
248
 
154
249
  const matches = findFilterColumns(collection);
155
250
  expect(matches.length).toBe(2);
156
-
157
251
  const refMap = buildRefMap([{ ref: knownRef }]);
158
- const options = filterMatchesToOptions(matches, refMap);
252
+ const options = filterMatchesToOptions(matches, {
253
+ refsByObjectId: refMap,
254
+ datasetSpec: anchorSpec,
255
+ });
159
256
  expect(options).toHaveLength(1);
160
257
  expect(options[0].ref).toBe(knownRef);
161
258
  });
@@ -1,5 +1,5 @@
1
1
  import { Annotation } from "@milaboratories/pl-model-common";
2
- import type { Option, PlRef, PObjectId } from "@milaboratories/pl-model-common";
2
+ import type { Option, PlRef, PObjectId, PObjectSpec } from "@milaboratories/pl-model-common";
3
3
  import canonicalize from "canonicalize";
4
4
  import type {
5
5
  AnchoredColumnCollection,
@@ -12,12 +12,8 @@ import {
12
12
  } from "../../labels/derive_distinct_labels";
13
13
 
14
14
  /**
15
- * Matches columns annotated `pl7.app/isSubset: "true"` whose axes ⊆ anchor axes.
16
- *
17
- * The axes-subset constraint is enforced by `mode: "enrichment"`, which sets
18
- * `allowFloatingHitAxes: false` — every axis of the matched column must be
19
- * present in the anchor's axes. See `matchingModeToConstraints()` in
20
- * `column_collection_builder.ts`.
15
+ * Columns annotated `pl7.app/isSubset: "true"` whose axes ⊆ anchor axes.
16
+ * The axes-subset constraint comes from `mode: "enrichment"`.
21
17
  */
22
18
  export function findFilterColumns(collection: AnchoredColumnCollection): ColumnMatch[] {
23
19
  return collection.findColumns({
@@ -28,51 +24,62 @@ export function findFilterColumns(collection: AnchoredColumnCollection): ColumnM
28
24
  });
29
25
  }
30
26
 
27
+ export type FilterMatchOptions = {
28
+ /** Maps result-pool column id back to its source PlRef (see {@link buildRefMap}). */
29
+ refsByObjectId: ReadonlyMap<PObjectId, PlRef>;
30
+ /** Spec of the dataset the filters are subsets of. */
31
+ datasetSpec: PObjectSpec;
32
+ /**
33
+ * Forwarded to `deriveDistinctLabels`. Any `formatters.native` caller
34
+ * sets is silently overridden — the function relies on a no-op native
35
+ * formatter to keep the algorithm from short-circuiting on filters'
36
+ * inherited `pl7.app/label`.
37
+ */
38
+ labelOptions?: DeriveLabelsOptions;
39
+ };
40
+
31
41
  /**
32
- * Derive labeled options from filter column matches, for use in DatasetOption.filters.
33
- *
34
- * Entries whose column id has no PlRef in `refsByObjectId` are silently
35
- * skipped — they cannot be exposed as user-selectable options.
36
- *
37
- * @param matches - from findFilterColumns()
38
- * @param refsByObjectId - from {@link buildRefMap}
39
- * @param labelOptions - forwarded to deriveDistinctLabels()
42
+ * Derive labels for filter column matches (for `DatasetOption.filters`).
43
+ * Matches whose column id is missing from `refsByObjectId` are silently
44
+ * dropped they cannot be exposed as selectable options.
40
45
  */
41
46
  export function filterMatchesToOptions(
42
47
  matches: ColumnMatch[],
43
- refsByObjectId: ReadonlyMap<PObjectId, PlRef>,
44
- labelOptions?: DeriveLabelsOptions,
48
+ options: FilterMatchOptions,
45
49
  ): Option[] {
46
50
  if (matches.length === 0) return [];
47
51
 
48
- // Each ColumnMatch can be reached via multiple variants (different linker
49
- // paths / qualifications). We emit one Option per variant so the user can
50
- // pick a specific path `deriveDistinctLabels` disambiguates labels by
51
- // path. All variants of a match share a column id, so the ref lookup
52
- // happens once per match.
53
- const flattened = matches.flatMap((match) => {
52
+ const { refsByObjectId, datasetSpec, labelOptions } = options;
53
+
54
+ // One entry per match-variant (different paths to the same column are
55
+ // exposed as separate Options). The `ref` field rides along on the
56
+ // Entry-shaped objects via structural typing; `deriveDistinctLabels`
57
+ // ignores extra fields.
58
+ const entries = matches.flatMap((match): (Entry & { ref: PlRef })[] => {
54
59
  const ref = refsByObjectId.get(match.column.id);
55
60
  if (ref === undefined) return [];
56
- return match.variants.map((variant) => ({ match, variant, ref }));
61
+ return match.variants.map((variant) => ({
62
+ ref,
63
+ spec: match.column.spec,
64
+ linkerPath: variant.path.map((p) => ({ spec: p.linker.spec })),
65
+ }));
57
66
  });
58
67
 
59
- const entries: Entry[] = flattened.map(({ match, variant }) => ({
60
- spec: match.column.spec,
61
- linkerPath: variant.path.map((p) => ({ spec: p.linker.spec })),
62
- }));
63
-
64
- const labels = deriveDistinctLabels(entries, labelOptions);
68
+ // Appending the dataset forces a discriminating trace step into every
69
+ // filter label (yielding e.g. "Bulk / Top 10"); the dataset's own label
70
+ // is dropped because we only zip `entries`. Native label is force-
71
+ // suppressed (the override sits after caller's spread) — filters
72
+ // inherit the dataset's `pl7.app/label` and would otherwise satisfy
73
+ // uniqueness before any trace step is consulted.
74
+ const labels = deriveDistinctLabels([...entries, { spec: datasetSpec }], {
75
+ ...labelOptions,
76
+ formatters: { ...labelOptions?.formatters, native: () => undefined },
77
+ });
65
78
 
66
- return flattened.map(({ ref }, i) => ({ ref, label: labels[i] }));
79
+ return entries.map(({ ref }, i) => ({ ref, label: labels[i] }));
67
80
  }
68
81
 
69
- /**
70
- * Usage: `buildRefMap(ctx.resultPool.getSpecs().entries)`
71
- */
82
+ /** Build the `refsByObjectId` map from `ctx.resultPool.getSpecs().entries`. */
72
83
  export function buildRefMap(entries: readonly { readonly ref: PlRef }[]): Map<PObjectId, PlRef> {
73
- const map = new Map<PObjectId, PlRef>();
74
- for (const entry of entries) {
75
- map.set(canonicalize(entry.ref)! as PObjectId, entry.ref);
76
- }
77
- return map;
84
+ return new Map(entries.map((e) => [canonicalize(e.ref)! as PObjectId, e.ref]));
78
85
  }
@@ -312,6 +312,7 @@ describe("filterSpecToSpecQueryExpr", () => {
312
312
  type: "isIn",
313
313
  input: { type: "columnRef", value: "col1" },
314
314
  set: ["a", "b", "c"],
315
+ negate: false,
315
316
  });
316
317
  });
317
318
 
@@ -322,12 +323,10 @@ describe("filterSpecToSpecQueryExpr", () => {
322
323
  value: ["x"],
323
324
  };
324
325
  expect(filterSpecToSpecQueryExpr(filter)).toEqual({
325
- type: "not",
326
- input: {
327
- type: "isIn",
328
- input: { type: "columnRef", value: "col1" },
329
- set: ["x"],
330
- },
326
+ type: "isIn",
327
+ input: { type: "columnRef", value: "col1" },
328
+ set: ["x"],
329
+ negate: true,
331
330
  });
332
331
  });
333
332
 
@@ -221,15 +221,14 @@ function leafToSpecQueryExpr<Leaf extends FilterSpecLeaf<string>>(
221
221
  type: "isIn",
222
222
  input: resolveColumnRef(filter.column),
223
223
  set: filter.value,
224
+ negate: false,
224
225
  };
225
226
  case "notInSet":
226
227
  return {
227
- type: "not",
228
- input: {
229
- type: "isIn",
230
- input: resolveColumnRef(filter.column),
231
- set: filter.value,
232
- },
228
+ type: "isIn",
229
+ input: resolveColumnRef(filter.column),
230
+ set: filter.value,
231
+ negate: true,
233
232
  };
234
233
 
235
234
  case "isNA":
@@ -248,7 +247,7 @@ function leafToSpecQueryExpr<Leaf extends FilterSpecLeaf<string>>(
248
247
 
249
248
  case "ifNa":
250
249
  return {
251
- type: "ifNull",
250
+ type: "fillNull",
252
251
  input: resolveColumnRef(filter.column),
253
252
  replacement: { type: "constant", value: filter.replacement },
254
253
  };