@platforma-open/milaboratories.top-antibodies 4.2.2 → 4.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -13809,6 +13809,21 @@ function isProducedByLeadSelection(spec) {
13809
13809
  const trace = readAnnotationJson(spec, Annotation.Trace);
13810
13810
  return Array.isArray(trace) && trace.length > 0 && trace[trace.length - 1]?.type === LEAD_SELECTION_TRACE_TYPE;
13811
13811
  }
13812
+ /**
13813
+ * True when a column carries no readable value. Presence is the whole signal.
13814
+ *
13815
+ * Requires the annotation. Requires axes that are a subset of the anchor's. Axis identity
13816
+ * is name, type, domain and context-domain. An axis that differs in any of them is a
13817
+ * different axis. A column that carries one is not presence-only.
13818
+ *
13819
+ * `matchAxisId` accepts an extra domain key on the column axis. This check does not.
13820
+ * A column reached through a linker keeps its own axes and reads as not presence-only.
13821
+ */
13822
+ function isPresenceOnlyColumn(spec, anchorSpec) {
13823
+ if (spec.annotations?.[Annotation.IsSubset] !== "true") return false;
13824
+ const anchorAxes = new Set(anchorSpec.axesSpec.map(canonicalizeAxisId));
13825
+ return spec.axesSpec.every((axis) => anchorAxes.has(canonicalizeAxisId(axis)));
13826
+ }
13812
13827
  /** JS post-filter for the residual predicates that {@link discoveryExcludeSelectors}
13813
13828
  * can't express host-side: File value type (`File` is not a matchable `ValueType`)
13814
13829
  * and columns produced by a lead-selection block (last-trace-step check on parsed
@@ -13817,6 +13832,17 @@ function isSelectableMatch(c) {
13817
13832
  return !isProducedByLeadSelection(c.getSpec());
13818
13833
  }
13819
13834
  /**
13835
+ * Whether the ranking options include a discovered column.
13836
+ *
13837
+ * Presence-only columns carry nothing to order by. A column the saved ranking names stays
13838
+ * in the options. Dropping the last one resets the list to preset defaults.
13839
+ */
13840
+ function isRankableMatch(c, anchorSpec, rankedColumnIds) {
13841
+ if (!isSelectableMatch(c)) return false;
13842
+ if (rankedColumnIds.has(extractPObjectId(c.id))) return true;
13843
+ return !isPresenceOnlyColumn(c.getSpec(), anchorSpec);
13844
+ }
13845
+ /**
13820
13846
  * Collapses discovery results to one recipe per storage column, first hit wins.
13821
13847
  *
13822
13848
  * Restores the pre-migration shape of a discovery result. The old
@@ -14445,14 +14471,14 @@ const CLUSTER_ID_SELECTORS = [...CLUSTER_ID_AXIS_NAMES].map((name) => ({ name: e
14445
14471
  const OWN_PLUMBING_SELECTORS = [{ name: exactMatch("pl7.app/selectionStage") }, { name: exactMatch("pl7.app/lead-selection") }];
14446
14472
  /**
14447
14473
  * Adapt column recipes to `PColumn`s for the helpers that still take
14448
- * materialised `PColumn[]` and have no id form (`createPFrameForGraphs`,
14449
- * `PColumnIdAndSpec`).
14474
+ * materialised `PColumn[]` and have no id form (`getRelatedColumns`,
14475
+ * `createPFrameForGraphs`, `PColumnIdAndSpec`).
14450
14476
  *
14451
14477
  * Only bare leaves qualify: `PColumn.id` is typed `PObjectId`, which no wrapper
14452
14478
  * recipe carries — hence `isDataColumn` rather than `hasReachableData`, which
14453
- * also admits spec-overrides over a leaf. Deduped by id: the same leaf can be
14454
- * reachable via more than one path and `createPFrameForGraphs` rejects
14455
- * duplicates.
14479
+ * also admits spec-overrides over a leaf. Deduped by id: these lists travel as
14480
+ * block columns, and a repeated id aborts the collection scan inside
14481
+ * `getRelatedColumns`.
14456
14482
  */
14457
14483
  function toGraphColumns(recipes) {
14458
14484
  return dedupColumns(recipes.filter(isDataColumn).map((c) => ({
@@ -14466,32 +14492,38 @@ function accessorGraphColumns(accessor) {
14466
14492
  if (!accessor) return void 0;
14467
14493
  return toGraphColumns(ColumnsCollection([accessor]).getColumns());
14468
14494
  }
14495
+ function umapPoolColumns(ctx) {
14496
+ const clonotypeAxisName = getSpecByRef(getInputAnchorRef(ctx.data))?.axesSpec[1]?.name;
14497
+ if (clonotypeAxisName === void 0) return void 0;
14498
+ const umap = ColumnsCollection(["result_pool"]).filter({ include: {
14499
+ name: "^pl7\\.app/umap[12]$",
14500
+ axes: [{ name: exactMatch(clonotypeAxisName) }]
14501
+ } }).getColumns();
14502
+ if (umap.length === 0) return void 0;
14503
+ return toGraphColumns(umap);
14504
+ }
14469
14505
  /**
14470
- * UMAP columns from the sequence-space block plus this block's own sampled-rows
14471
- * markers, materialised for `createPFrameForGraphs` / `PColumnIdAndSpec`.
14506
+ * Ids of the columns that join the UMAP projection, discovered once per UMAP
14507
+ * column and unioned, so a second clonotype-space block keyed on its own axis
14508
+ * still contributes.
14472
14509
  *
14473
- * The legacy `{ anchor: "main", idx: 1 }` axis binding has no selector form, so
14474
- * the clonotype axis name is read off the resolved anchor spec and matched by
14475
- * name. Omitting `partialAxesMatch` keeps the old exact-axis-set semantics.
14510
+ * Anchors are specs, never ids, so the pool's own copy of a UMAP column cannot
14511
+ * collide with one handed in here.
14476
14512
  */
14477
- function umapGraphColumns(ctx) {
14478
- const anchorSpec = getSpecByRef(getInputAnchorRef(ctx.data));
14479
- const clonotypeAxisName = anchorSpec?.axesSpec[1]?.name;
14480
- if (anchorSpec === void 0 || clonotypeAxisName === void 0) return void 0;
14481
- const umap = ColumnsCollection(["result_pool"]).discover({
14482
- anchors: { main: anchorSpec },
14483
- include: {
14484
- name: "^pl7\\.app/umap[12]$",
14485
- axes: [{ name: exactMatch(clonotypeAxisName) }]
14486
- }
14487
- }).getColumns();
14488
- if (umap.length === 0) return void 0;
14489
- const sampledRows = ColumnsCollection([ctx.outputs?.resolve({
14513
+ function umapRelatedIds(umap) {
14514
+ return [...new Set(umap.flatMap((c) => ColumnsCollection().discover({
14515
+ mode: "enrichment",
14516
+ maxHops: 4,
14517
+ anchors: { main: c.spec },
14518
+ exclude: [{ annotations: { [Annotation.HideDataFromUi]: exactMatch("true") } }, { annotations: { [Annotation.HideDataFromGraphs]: exactMatch("true") } }]
14519
+ }).getColumnIds()))];
14520
+ }
14521
+ function sampledRowsGraphColumns(ctx) {
14522
+ return accessorGraphColumns(ctx.outputs?.resolve({
14490
14523
  field: "sampledRows",
14491
14524
  assertFieldType: "Input",
14492
14525
  allowPermanentAbsence: true
14493
- })].filter((a) => a !== void 0)).getColumns();
14494
- return toGraphColumns([...umap, ...sampledRows]);
14526
+ }));
14495
14527
  }
14496
14528
  const platforma$1 = BlockModelV3.create({
14497
14529
  dataModel: blockDataModel,
@@ -14566,14 +14598,18 @@ const platforma$1 = BlockModelV3.create({
14566
14598
  }).getColumns()).filter(isSelectableMatch);
14567
14599
  const labels = deriveDistinctLabels(filterableMatches.map((c) => c.getSpec()), { includeNativeLabel: true });
14568
14600
  return {
14569
- options: filterableMatches.map((c, i) => ({
14570
- label: labels[i],
14571
- value: matchToColumnId(c, inputAnchor),
14572
- column: {
14573
- id: c.id,
14574
- spec: c.getSpec()
14575
- }
14576
- })),
14601
+ options: filterableMatches.map((c, i) => {
14602
+ const spec = c.getSpec();
14603
+ return {
14604
+ label: labels[i],
14605
+ value: matchToColumnId(c, inputAnchor),
14606
+ column: {
14607
+ id: c.id,
14608
+ spec
14609
+ },
14610
+ presenceOnly: isPresenceOnlyColumn(spec, result.anchorSpec)
14611
+ };
14612
+ }),
14577
14613
  defaults: result.meta.defaultFilters,
14578
14614
  inVivoDefaults: result.meta.inVivoDefaults.filters,
14579
14615
  inVitroDefaults: result.meta.inVitroDefaults.filters,
@@ -14583,10 +14619,11 @@ const platforma$1 = BlockModelV3.create({
14583
14619
  const inputAnchor = getInputAnchorRef(ctx.data);
14584
14620
  const result = buildCollection(inputAnchor);
14585
14621
  if (!result) return void 0;
14622
+ const rankedColumnIds = new Set(ctx.data.rankingOrder.flatMap((r) => r.value ? [r.value.column] : []));
14586
14623
  const rankableMatches = dedupByLeafId(result.collection.discover({
14587
14624
  anchors: { main: result.anchorSpec },
14588
14625
  exclude: [...discoveryExcludeSelectors(result.sampleAxisName), { type: "String" }]
14589
- }).getColumns()).filter(isSelectableMatch);
14626
+ }).getColumns()).filter((c) => isRankableMatch(c, result.anchorSpec, rankedColumnIds));
14590
14627
  const labels = deriveDistinctLabels(rankableMatches.map((c) => c.getSpec()), { includeNativeLabel: true });
14591
14628
  return {
14592
14629
  options: rankableMatches.map((c, i) => ({
@@ -14775,13 +14812,14 @@ const platforma$1 = BlockModelV3.create({
14775
14812
  if (ctx.outputs.getIsReadyOrError() === false) return true;
14776
14813
  return false;
14777
14814
  }).outputWithStatus("umapPf", (ctx) => {
14778
- const pCols = umapGraphColumns(ctx);
14779
- if (pCols === void 0) return void 0;
14780
- return createPFrameForGraphs(ctx, pCols);
14815
+ const umap = umapPoolColumns(ctx);
14816
+ if (umap === void 0) return void 0;
14817
+ if (umap.some((c) => c.data === void 0)) return void 0;
14818
+ return ctx.createPFrame([...umapRelatedIds(umap), ...umap.map((c) => c.id)]);
14781
14819
  }).outputWithStatus("umapPcols", (ctx) => {
14782
- const pCols = umapGraphColumns(ctx);
14783
- if (pCols === void 0) return void 0;
14784
- return pCols.map((c) => ({
14820
+ const umap = umapPoolColumns(ctx);
14821
+ if (umap === void 0) return void 0;
14822
+ return [...umap, ...sampledRowsGraphColumns(ctx) ?? []].map((c) => ({
14785
14823
  columnId: c.id,
14786
14824
  spec: c.spec
14787
14825
  }));