@platforma-open/milaboratories.repertoire-mutation-heatmap.block 1.2.3 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,15 @@
1
1
  import { r as __toESM, t as __commonJSMin } from "./rolldown-runtime-DC62tzP2.js";
2
2
  //#region ../model/dist/chart-state.js
3
3
  /**
4
+ * Opacity range, reversed for the error column the page binds: the smallest error draws solid and
5
+ * the largest faint. A column bound to opacity by hand fades the other way round. Dragging the
6
+ * range slider re-sorts the two ends, which undoes the reversal.
7
+ */
8
+ const ERROR_OPACITY_RANGE = {
9
+ min: 1,
10
+ max: .15
11
+ };
12
+ /**
4
13
  * Default state for a mutation-landscape chart. Used by `init` for the placeholder and by the UI
5
14
  * for each score a run produces, so the two look alike.
6
15
  *
@@ -14,7 +23,8 @@ function makeLandscapeChartState(title, currentTab) {
14
23
  layersSettings: { heatmap: {
15
24
  normalizationDirection: null,
16
25
  transform: null,
17
- NAValueAs: null
26
+ NAValueAs: null,
27
+ opacityRange: { ...ERROR_OPACITY_RANGE }
18
28
  } },
19
29
  axesSettings: {
20
30
  axisX: {
@@ -180,6 +190,22 @@ function withYAnnotationTitleAtBottom(state) {
180
190
  }
181
191
  };
182
192
  }
193
+ /**
194
+ * Seeds the reversed error opacity on a saved chart. Layer settings are seeded once, when a chart
195
+ * is created, so an older chart would otherwise fade its most certain cells.
196
+ */
197
+ function withErrorOpacityRange(state) {
198
+ return {
199
+ ...state,
200
+ layersSettings: {
201
+ ...state.layersSettings,
202
+ heatmap: {
203
+ ...state.layersSettings?.heatmap,
204
+ opacityRange: { ...ERROR_OPACITY_RANGE }
205
+ }
206
+ }
207
+ };
208
+ }
183
209
  function mapChartStates(data, rewrite) {
184
210
  return {
185
211
  compositionHeatmapState: rewrite(data.compositionHeatmapState),
@@ -188,14 +214,14 @@ function mapChartStates(data, rewrite) {
188
214
  };
189
215
  }
190
216
  //#endregion
191
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/plugin_handle.js
217
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/plugin_handle.js
192
218
  const PLUGIN_OUTPUT_PREFIX = "plugin-output#";
193
219
  /** Construct the output key for a plugin output in the block outputs map. */
194
220
  function pluginOutputKey(handle, outputKey) {
195
221
  return `${PLUGIN_OUTPUT_PREFIX}${handle}#${outputKey}`;
196
222
  }
197
223
  //#endregion
198
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/block_storage.js
224
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/block_storage.js
199
225
  /**
200
226
  * Discriminator key for BlockStorage format detection.
201
227
  * This unique hash-based key identifies data as BlockStorage vs legacy formats.
@@ -4007,20 +4033,6 @@ const coerce = {
4007
4033
  coerce: true
4008
4034
  }))
4009
4035
  };
4010
- objectType({
4011
- type: literalType("plain"),
4012
- content: stringType()
4013
- });
4014
- //#endregion
4015
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/bmodel/block_kind_ref.js
4016
- /**
4017
- * Compose a {@link BlockKindReference} from a kind's `name`/`version`.
4018
- *
4019
- * The single place that decides how the reference is assembled. If global
4020
- * uniqueness later requires the name segment to be org-qualified, this is the
4021
- * one line that changes.
4022
- */
4023
- const formatKindRef = (k) => `${k.name}@${k.version}`;
4024
4036
  objectType({
4025
4037
  /** Included left border. */
4026
4038
  from: numberType().min(0),
@@ -4028,7 +4040,7 @@ objectType({
4028
4040
  to: numberType().min(1)
4029
4041
  });
4030
4042
  //#endregion
4031
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/util.js
4043
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/util.js
4032
4044
  function assertNever(x) {
4033
4045
  throw new Error("Unexpected object: " + x);
4034
4046
  }
@@ -4040,7 +4052,7 @@ function uniqueBy(array, makeId) {
4040
4052
  return [...new Map(array.map((e) => [makeId(e), e])).values()];
4041
4053
  }
4042
4054
  //#endregion
4043
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/data_info.js
4055
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/data_info.js
4044
4056
  /**
4045
4057
  * Type guard function that checks if the given value is a valid DataInfo.
4046
4058
  *
@@ -4229,7 +4241,7 @@ function entriesToDataInfo(dataInfoEntries) {
4229
4241
  }
4230
4242
  }
4231
4243
  //#endregion
4232
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/errors.js
4244
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/errors.js
4233
4245
  var ServiceError = class extends Error {
4234
4246
  name = "ServiceError";
4235
4247
  };
@@ -4253,7 +4265,7 @@ function ensureError(value) {
4253
4265
  return new Error(stringifyValue(value));
4254
4266
  }
4255
4267
  //#endregion
4256
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/json.js
4268
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/json.js
4257
4269
  var import_canonicalize = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
4258
4270
  module.exports = function serialize(object) {
4259
4271
  if (typeof object === "number" && isNaN(object)) throw new Error("NaN is not allowed");
@@ -4286,7 +4298,7 @@ function parseJsonSafely(value, fallback) {
4286
4298
  }
4287
4299
  }
4288
4300
  //#endregion
4289
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/spec.js
4301
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/spec.js
4290
4302
  function readMetadata(metadata, key) {
4291
4303
  return metadata?.[key];
4292
4304
  }
@@ -4614,7 +4626,7 @@ function matchAxisId(query, target) {
4614
4626
  return query.name === target.name && matchDomain$1(query.domain, target.domain) && matchDomain$1(query.contextDomain, target.contextDomain);
4615
4627
  }
4616
4628
  //#endregion
4617
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/query/utils.js
4629
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/query/utils.js
4618
4630
  const BOOLEAN_TYPES = /* @__PURE__ */ new Set([
4619
4631
  "numericComparison",
4620
4632
  "stringEquals",
@@ -4719,7 +4731,7 @@ function collectSpecQueryColumns(query) {
4719
4731
  return result;
4720
4732
  }
4721
4733
  //#endregion
4722
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/table_calculate.js
4734
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/table_calculate.js
4723
4735
  function mapPTableDef(def, cb) {
4724
4736
  return {
4725
4737
  ...def,
@@ -4762,7 +4774,7 @@ function mapJoinEntry(entry, cb) {
4762
4774
  }
4763
4775
  }
4764
4776
  //#endregion
4765
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/table_common.js
4777
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/table_common.js
4766
4778
  function getPTableColumnId(spec) {
4767
4779
  switch (spec.type) {
4768
4780
  case "axis": return {
@@ -4776,7 +4788,7 @@ function getPTableColumnId(spec) {
4776
4788
  }
4777
4789
  }
4778
4790
  //#endregion
4779
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/filtered_column.js
4791
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/filtered_column.js
4780
4792
  function stringifyColumnFilteredId(key) {
4781
4793
  return canonicalizeJson(createColumnFilteredKey(key));
4782
4794
  }
@@ -4809,7 +4821,7 @@ function applyAxisFilters(spec, axisFilters) {
4809
4821
  };
4810
4822
  }
4811
4823
  //#endregion
4812
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/pool/spec.js
4824
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/pool/spec.js
4813
4825
  function isPObjectId(value) {
4814
4826
  if (typeof value !== "string") return false;
4815
4827
  return isPObjectKey(parseJsonSafely(value));
@@ -4844,6 +4856,10 @@ function isGlobalPObjectKey(value) {
4844
4856
  if (typeof value !== "object" || value === null) return false;
4845
4857
  return "__isRef" in value && "blockId" in value && "name" in value;
4846
4858
  }
4859
+ function isGlobalPObjectId(value) {
4860
+ if (typeof value !== "string") return false;
4861
+ return isGlobalPObjectKey(parseJsonSafely(value));
4862
+ }
4847
4863
  function isPObject(obj) {
4848
4864
  return typeof obj === "object" && obj !== null && "id" in obj && "spec" in obj && "data" in obj;
4849
4865
  }
@@ -4892,7 +4908,7 @@ function extractAllColumns(entry) {
4892
4908
  return [...columns.values()];
4893
4909
  }
4894
4910
  //#endregion
4895
- //#region ../node_modules/.pnpm/@milaboratories+helpers@1.14.5/node_modules/@milaboratories/helpers/dist/utils.js
4911
+ //#region ../node_modules/.pnpm/@milaboratories+helpers@1.14.6/node_modules/@milaboratories/helpers/dist/utils.js
4896
4912
  function isNil$1(v) {
4897
4913
  return v === null || v === void 0;
4898
4914
  }
@@ -4925,13 +4941,13 @@ function isPlainObject$1(value) {
4925
4941
  return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
4926
4942
  }
4927
4943
  //#endregion
4928
- //#region ../node_modules/.pnpm/@milaboratories+helpers@1.14.5/node_modules/@milaboratories/helpers/dist/error.js
4944
+ //#region ../node_modules/.pnpm/@milaboratories+helpers@1.14.6/node_modules/@milaboratories/helpers/dist/error.js
4929
4945
  function throwError(v) {
4930
4946
  if (typeof v === "string") throw new Error(v);
4931
4947
  else throw v;
4932
4948
  }
4933
4949
  //#endregion
4934
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/discovered_column.js
4950
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/discovered_column.js
4935
4951
  function isColumnDiscoveredKey(obj) {
4936
4952
  return typeof obj === "object" && obj !== null && "__isDiscovered" in obj;
4937
4953
  }
@@ -5211,7 +5227,12 @@ function isString(value) {
5211
5227
  return typeof value === "string";
5212
5228
  }
5213
5229
  //#endregion
5214
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/ids.js
5230
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/column_value.js
5231
+ function isColumnValueKey(obj) {
5232
+ return typeof obj === "object" && obj !== null && "__isColumnValue" in obj && obj.__isColumnValue === true && "column" in obj && isColumnUniversalId(obj.column) && "value" in obj && isString(obj.value);
5233
+ }
5234
+ //#endregion
5235
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/ids.js
5215
5236
  /**
5216
5237
  * Canonically serializes a column key to a branded string id. Accepts both
5217
5238
  * the new {@link ColumnUniversalKey} and the deprecated {@link UniversalPColumnId}
@@ -5231,6 +5252,10 @@ function parseColumnIdSafely(str, fallback = void 0) {
5231
5252
  function isColumnUniversalKey(value) {
5232
5253
  return isPObjectKey(value) || isColumnFilteredKey(value) || isColumnDiscoveredKey(value) || isColumnOverriddenKey(value);
5233
5254
  }
5255
+ function isColumnUniversalId(value) {
5256
+ const key = isString(value) ? parseJsonSafely(value, false) : false;
5257
+ return key === false ? false : isColumnUniversalKey(key);
5258
+ }
5234
5259
  /**
5235
5260
  * Take a value out of however many `JSON.stringify` passes wrapped it, or `undefined`
5236
5261
  * when `s` is not JSON at all.
@@ -5270,7 +5295,8 @@ function peelJsonLayers(s) {
5270
5295
  }
5271
5296
  }
5272
5297
  /**
5273
- * Rewrite every block id buried inside a column id.
5298
+ * Rewrite every block id buried inside a column id, or inside the column of a column value
5299
+ * id — a picked value names its producer through that column's leaf.
5274
5300
  *
5275
5301
  * A {@link GlobalPObjectKey} leaf names its upstream by block id, and the wrapper key forms
5276
5302
  * nest by *string* id rather than by object — so a block id can sit under several layers of
@@ -5300,17 +5326,43 @@ function remapColumnIdBlockIds(id, remapBlockId) {
5300
5326
  *
5301
5327
  * Escape padding is peeled and put back, so an id that reached params through an extra
5302
5328
  * `JSON.stringify` is rewritten in place and comes back wrapped as it was found. A string
5303
- * that does not peel to a column key is left alone: params hold ordinary strings too.
5329
+ * that peels to neither a column key nor a column value key is left alone: params hold
5330
+ * ordinary strings too.
5304
5331
  */
5305
5332
  function remapIdString(id, remapBlockId) {
5306
5333
  const peeled = peelJsonLayers(id);
5307
- if (peeled === void 0 || !isColumnUniversalKey(peeled.value)) return void 0;
5308
- const remappedKey = remapKey(peeled.value, remapBlockId);
5309
- if (remappedKey === void 0) return void 0;
5310
- let rebuilt = stringifyColumnId(remappedKey);
5334
+ if (peeled === void 0) return void 0;
5335
+ let rebuilt;
5336
+ if (isColumnValueKey(peeled.value)) {
5337
+ const remappedKey = remapValueKey(peeled.value, remapBlockId);
5338
+ if (remappedKey === void 0) return void 0;
5339
+ rebuilt = canonicalizeJson(remappedKey);
5340
+ } else if (isColumnUniversalKey(peeled.value)) {
5341
+ const remappedKey = remapKey(peeled.value, remapBlockId);
5342
+ if (remappedKey === void 0) return void 0;
5343
+ rebuilt = stringifyColumnId(remappedKey);
5344
+ } else return;
5311
5345
  for (let layer = 0; layer < peeled.layers; layer++) rebuilt = JSON.stringify(rebuilt);
5312
5346
  return rebuilt;
5313
5347
  }
5348
+ /**
5349
+ * {@link remapIdString} over a column id. Remapping keeps an id's shape, so a column id comes
5350
+ * back a column id.
5351
+ */
5352
+ function remapColumnIdString(id, remapBlockId) {
5353
+ return remapIdString(id, remapBlockId);
5354
+ }
5355
+ /**
5356
+ * The value-id half of {@link remapIdString}. `undefined` means "nothing to change". Only the
5357
+ * column carries block ids; the value is cell data and stays as it is.
5358
+ */
5359
+ function remapValueKey(key, remapBlockId) {
5360
+ const column = remapColumnIdString(key.column, remapBlockId);
5361
+ return column === void 0 ? void 0 : {
5362
+ ...key,
5363
+ column
5364
+ };
5365
+ }
5314
5366
  /** The key half of {@link remapColumnIdBlockIds}. `undefined` means "nothing to change". */
5315
5367
  function remapKey(key, remapBlockId) {
5316
5368
  if (isGlobalPObjectKey(key)) {
@@ -5322,7 +5374,7 @@ function remapKey(key, remapBlockId) {
5322
5374
  }
5323
5375
  if (isLocalPObjectKey(key)) return void 0;
5324
5376
  if (isColumnFilteredKey(key)) {
5325
- const source = remapIdString(key.source, remapBlockId);
5377
+ const source = remapColumnIdString(key.source, remapBlockId);
5326
5378
  return source === void 0 ? void 0 : {
5327
5379
  ...key,
5328
5380
  source
@@ -5344,10 +5396,10 @@ function remapKey(key, remapBlockId) {
5344
5396
  * id is the map key, not the value.
5345
5397
  */
5346
5398
  function remapDiscoveredKey(key, remapBlockId) {
5347
- const column = remapIdString(key.column, remapBlockId);
5399
+ const column = remapColumnIdString(key.column, remapBlockId);
5348
5400
  let pathChanged = false;
5349
5401
  const path = key.path?.map((item) => {
5350
- const itemColumn = remapIdString(item.column, remapBlockId);
5402
+ const itemColumn = remapColumnIdString(item.column, remapBlockId);
5351
5403
  if (itemColumn === void 0) return item;
5352
5404
  pathChanged = true;
5353
5405
  return {
@@ -5385,7 +5437,7 @@ function extractPObjectId(id) {
5385
5437
  throw new Error(`extractPObjectId: unrecognized column id structure: ${JSON.stringify(id)}`);
5386
5438
  }
5387
5439
  //#endregion
5388
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/overridden.js
5440
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/overridden.js
5389
5441
  function isColumnOverriddenKey(obj) {
5390
5442
  return typeof obj === "object" && obj !== null && "__isOverridden" in obj;
5391
5443
  }
@@ -5507,7 +5559,7 @@ function mergeRecord(a, b) {
5507
5559
  };
5508
5560
  }
5509
5561
  //#endregion
5510
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/anchored.js
5562
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/anchored.js
5511
5563
  function axisKey(axis) {
5512
5564
  return (0, import_canonicalize.default)(getAxisId(axis));
5513
5565
  }
@@ -5753,7 +5805,7 @@ function isAnchorAxisRef(value) {
5753
5805
  return typeof value === "object" && "anchor" in value;
5754
5806
  }
5755
5807
  //#endregion
5756
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/selectors.js
5808
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/selectors.js
5757
5809
  /**
5758
5810
  * Determines if an axis ID matches an axis selector.
5759
5811
  *
@@ -5833,8 +5885,53 @@ function legacyColumnSelectorsToPredicate(predicateOrSelectors) {
5833
5885
  if (Array.isArray(predicateOrSelectors)) return (spec) => predicateOrSelectors.some((selector) => isPColumnSpec(spec) && matchPColumn(spec, selector));
5834
5886
  else return (spec) => isPColumnSpec(spec) && matchPColumn(spec, predicateOrSelectors);
5835
5887
  }
5888
+ function matchString(matcher, value) {
5889
+ return matcher.type === "exact" ? value === matcher.value : new RegExp(matcher.value).test(value);
5890
+ }
5891
+ function matchMatcherMap(map, target) {
5892
+ const t = target ?? {};
5893
+ for (const [key, matchers] of Object.entries(map)) {
5894
+ const v = t[key];
5895
+ if (v === void 0) return false;
5896
+ if (!matchers.some((m) => matchString(m, v))) return false;
5897
+ }
5898
+ return true;
5899
+ }
5900
+ function matchMultiAxis(selector, axis) {
5901
+ if (selector.type !== void 0 && !selector.type.includes(axis.type)) return false;
5902
+ if (selector.name !== void 0 && !selector.name.some((m) => matchString(m, axis.name))) return false;
5903
+ if (selector.domain !== void 0 && !matchMatcherMap(selector.domain, axis.domain)) return false;
5904
+ if (selector.contextDomain !== void 0 && !matchMatcherMap(selector.contextDomain, axis.contextDomain)) return false;
5905
+ return true;
5906
+ }
5907
+ /** Match a {@link PColumnSpec} against a {@link MultiColumnSelector}. */
5908
+ function matchMultiColumnSelector(spec, selector) {
5909
+ if (selector.type !== void 0 && !selector.type.includes(spec.valueType)) return false;
5910
+ if (selector.name !== void 0 && !selector.name.some((m) => matchString(m, spec.name))) return false;
5911
+ if (selector.domain !== void 0 && !matchMatcherMap(selector.domain, spec.domain)) return false;
5912
+ if (selector.contextDomain !== void 0 && !matchMatcherMap(selector.contextDomain, spec.contextDomain)) return false;
5913
+ if (selector.annotations !== void 0 && !matchMatcherMap(selector.annotations, spec.annotations)) return false;
5914
+ if (selector.axes !== void 0) {
5915
+ const columnAxes = spec.axesSpec.map(getAxisId);
5916
+ if (selector.partialAxesMatch ?? true) {
5917
+ for (const axisSelector of selector.axes) if (!columnAxes.some((axis) => matchMultiAxis(axisSelector, axis))) return false;
5918
+ } else {
5919
+ if (columnAxes.length !== selector.axes.length) return false;
5920
+ for (let i = 0; i < selector.axes.length; i++) if (!matchMultiAxis(selector.axes[i], columnAxes[i])) return false;
5921
+ }
5922
+ }
5923
+ return true;
5924
+ }
5925
+ /**
5926
+ * Convert a {@link MultiColumnSelector} (or array; selectors OR-ed) to a predicate.
5927
+ * Non-PColumn specs always return false.
5928
+ */
5929
+ function multiColumnSelectorsToPredicate(selectors) {
5930
+ const arr = Array.isArray(selectors) ? selectors : [selectors];
5931
+ return (spec) => isPColumnSpec(spec) && arr.some((sel) => matchMultiColumnSelector(spec, sel));
5932
+ }
5836
5933
  //#endregion
5837
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/native_id.js
5934
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/spec/native_id.js
5838
5935
  function deriveNativeId(spec) {
5839
5936
  const result = {
5840
5937
  kind: spec.kind,
@@ -5846,7 +5943,7 @@ function deriveNativeId(spec) {
5846
5943
  return (0, import_canonicalize.default)(result);
5847
5944
  }
5848
5945
  //#endregion
5849
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/linker_columns.js
5946
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/drivers/pframe/linker_columns.js
5850
5947
  var LinkerMap = class LinkerMap {
5851
5948
  /** Graph of linkers connected by axes (single or grouped by parents) */
5852
5949
  data;
@@ -6023,58 +6120,7 @@ var LinkerMap = class LinkerMap {
6023
6120
  }
6024
6121
  };
6025
6122
  //#endregion
6026
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/flags/block_flags.js
6027
- /**
6028
- * Required PFrames version. Bump this in lockstep with the `@milaboratories/pframes-rs-*`
6029
- * version in `pnpm-workspace.yaml` so blocks built against the new SDK refuse to load on
6030
- * older desktop apps.
6031
- */
6032
- const REQUIRES_PFRAMES_VERSION = 1001031;
6033
- stringType().length(24).regex(/[ABCDEFGHIJKLMNOPQRSTUVWXYZ234567]/);
6034
- objectType({
6035
- __isRef: literalType(true).describe("Crucial marker for the block dependency tree reconstruction"),
6036
- blockId: stringType().describe("Upstream block id"),
6037
- name: stringType().describe("Name of the output provided to the upstream block's output context"),
6038
- requireEnrichments: literalType(true).optional().describe("True if current block that stores this reference in its args, may need enrichments for the references value originating from the blocks in between current and referenced block")
6039
- }).describe("Universal reference type, allowing to set block connections. It is crucial that {@link __isRef} is present and equal to true, internal logic relies on this marker to build block dependency trees.").readonly();
6040
- /**
6041
- * Type guard to check if a value is a PlRef.
6042
- *
6043
- * @param value - The value to check.
6044
- * @returns True if the value is a PlRef, false otherwise.
6045
- */
6046
- function isPlRef(value) {
6047
- return typeof value === "object" && value !== null && "__isRef" in value && value.__isRef === true && "blockId" in value && "name" in value;
6048
- }
6049
- /**
6050
- * Creates a new PlRef based on an existing one, explicitly setting (default) or removing the
6051
- * requireEnrichments property.
6052
- *
6053
- * @param ref - The original PlRef object.
6054
- * @param requireEnrichments - If true, the `requireEnrichments: true` property is added
6055
- * to the returned PlRef. If false, the `requireEnrichments` property is removed. Defaults to true.
6056
- * @returns A new PlRef object with the `requireEnrichments` property set or removed accordingly.
6057
- */
6058
- function withEnrichments(ref, requireEnrichments = true) {
6059
- if (requireEnrichments) return {
6060
- ...ref,
6061
- requireEnrichments: true
6062
- };
6063
- else {
6064
- const { requireEnrichments: _, ...rest } = ref;
6065
- return rest;
6066
- }
6067
- }
6068
- //#endregion
6069
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/value_or_error.js
6070
- function mapValueInVOE(voe, cb) {
6071
- return voe.ok ? {
6072
- ok: true,
6073
- value: cb(voe.value)
6074
- } : voe;
6075
- }
6076
- //#endregion
6077
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/resource_types.js
6123
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/resource_types.js
6078
6124
  /** Well-known resource type names used across the platform. */
6079
6125
  const ResourceTypeName = {
6080
6126
  StreamManager: "StreamManager",
@@ -6122,8 +6168,122 @@ const ResourceTypeName = {
6122
6168
  SharingState: "SharingState",
6123
6169
  SharedEnvelope: "SharedEnvelope"
6124
6170
  };
6171
+ objectType({
6172
+ type: literalType("plain"),
6173
+ content: stringType()
6174
+ });
6175
+ //#endregion
6176
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/bmodel/block_kind_ref.js
6177
+ /**
6178
+ * Compose a {@link BlockKindReference} from a kind's `name`/`version`.
6179
+ *
6180
+ * The single place that decides how the reference is assembled. If global
6181
+ * uniqueness later requires the name segment to be org-qualified, this is the
6182
+ * one line that changes.
6183
+ */
6184
+ const formatKindRef = (k) => `${k.name}@${k.version}`;
6185
+ //#endregion
6186
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/flags/block_flags.js
6187
+ /**
6188
+ * Required PFrames version. Bump this in lockstep with the `@milaboratories/pframes-rs-*`
6189
+ * version in `pnpm-workspace.yaml` so blocks built against the new SDK refuse to load on
6190
+ * older desktop apps.
6191
+ */
6192
+ const REQUIRES_PFRAMES_VERSION = 1001031;
6193
+ stringType().length(24).regex(/[ABCDEFGHIJKLMNOPQRSTUVWXYZ234567]/);
6194
+ //#endregion
6195
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/ref.js
6196
+ /** One output of one block: the block and the name of the output in its output context. */
6197
+ const BlockOutput = objectType({
6198
+ blockId: stringType().describe("Upstream block id"),
6199
+ name: stringType().describe("Name of the output provided to the upstream block's output context")
6200
+ });
6201
+ objectType({
6202
+ __isRef: literalType(true).describe("Crucial marker for the block dependency tree reconstruction"),
6203
+ ...BlockOutput.shape,
6204
+ requireEnrichments: literalType(true).optional().describe("True if current block that stores this reference in its args, may need enrichments for the references value originating from the blocks in between current and referenced block")
6205
+ }).describe("Universal reference type, allowing to set block connections. It is crucial that {@link __isRef} is present and equal to true, internal logic relies on this marker to build block dependency trees.").readonly();
6206
+ /**
6207
+ * Type guard to check if a value is a PlRef.
6208
+ *
6209
+ * @param value - The value to check.
6210
+ * @returns True if the value is a PlRef, false otherwise.
6211
+ */
6212
+ function isPlRef(value) {
6213
+ return typeof value === "object" && value !== null && "__isRef" in value && value.__isRef === true && "blockId" in value && "name" in value;
6214
+ }
6215
+ /**
6216
+ * Creates a new PlRef based on an existing one, explicitly setting (default) or removing the
6217
+ * requireEnrichments property.
6218
+ *
6219
+ * @param ref - The original PlRef object.
6220
+ * @param requireEnrichments - If true, the `requireEnrichments: true` property is added
6221
+ * to the returned PlRef. If false, the `requireEnrichments` property is removed. Defaults to true.
6222
+ * @returns A new PlRef object with the `requireEnrichments` property set or removed accordingly.
6223
+ */
6224
+ function withEnrichments(ref, requireEnrichments = true) {
6225
+ if (requireEnrichments) return {
6226
+ ...ref,
6227
+ requireEnrichments: true
6228
+ };
6229
+ else {
6230
+ const { requireEnrichments: _, ...rest } = ref;
6231
+ return rest;
6232
+ }
6233
+ }
6234
+ function createEnrichmentRef(hit, options = {}) {
6235
+ const { path, qualifications } = options;
6236
+ return {
6237
+ __isEnrichment: "v1",
6238
+ hit,
6239
+ ...path !== void 0 && path.length > 0 ? { path } : {},
6240
+ ...qualifications !== void 0 && qualifications.length > 0 ? { qualifications } : {}
6241
+ };
6242
+ }
6125
6243
  //#endregion
6126
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/services/service_types.js
6244
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/args_issue.js
6245
+ /**
6246
+ * Reads one {@link ArgsIssue} from a value of unknown origin.
6247
+ *
6248
+ * Any severity other than `"warning"` is read as `"error"`: an issue whose weight cannot be
6249
+ * told keeps the block from running rather than letting it run on a misread finding.
6250
+ *
6251
+ * @throws when `value` is not an object carrying a string `message`
6252
+ */
6253
+ function parseArgsIssue(value) {
6254
+ if (typeof value !== "object" || value === null) throw new Error(`An args issue must be an object, got ${describe$1(value)}`);
6255
+ const message = "message" in value ? value.message : void 0;
6256
+ if (!isString(message)) throw new Error(`An args issue must carry a string message, got ${describe$1(message)}`);
6257
+ return {
6258
+ severity: "severity" in value && value.severity === "warning" ? "warning" : "error",
6259
+ message
6260
+ };
6261
+ }
6262
+ /**
6263
+ * Reads a list of {@link ArgsIssue}s from a value of unknown origin, each entry as
6264
+ * {@link parseArgsIssue} reads it.
6265
+ *
6266
+ * @throws when `value` is not an array, or one of its entries is not an issue
6267
+ */
6268
+ function parseArgsIssues(value) {
6269
+ if (!Array.isArray(value)) throw new Error(`Args issues must be an array, got ${describe$1(value)}`);
6270
+ return value.map(parseArgsIssue);
6271
+ }
6272
+ function describe$1(value) {
6273
+ if (value === null) return "null";
6274
+ if (Array.isArray(value)) return "an array";
6275
+ return typeof value;
6276
+ }
6277
+ //#endregion
6278
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/value_or_error.js
6279
+ function mapValueInVOE(voe, cb) {
6280
+ return voe.ok ? {
6281
+ ok: true,
6282
+ value: cb(voe.value)
6283
+ } : voe;
6284
+ }
6285
+ //#endregion
6286
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/services/service_types.js
6127
6287
  const SERVICE_ID_PATTERN = /^[a-zA-Z][a-zA-Z0-9]*$/;
6128
6288
  const { service, isNodeService, isWasmService, isMainService, getServiceKind, getServiceModelMethods, getServiceUiMethods } = (() => {
6129
6289
  const typeMap = /* @__PURE__ */ new Map();
@@ -6162,7 +6322,7 @@ const { service, isNodeService, isWasmService, isMainService, getServiceKind, ge
6162
6322
  };
6163
6323
  })();
6164
6324
  //#endregion
6165
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/services/service_declarations.js
6325
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/services/service_declarations.js
6166
6326
  const Services = {
6167
6327
  PFrameSpec: service()({
6168
6328
  type: "wasm",
@@ -6256,7 +6416,7 @@ function resolveRequiredServices(flags) {
6256
6416
  }
6257
6417
  Object.fromEntries(Object.keys(Services).map((key) => [`requires${key}`, true]));
6258
6418
  //#endregion
6259
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/services/service_injectors.js
6419
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/services/service_injectors.js
6260
6420
  function buildMethodMap(pick) {
6261
6421
  const result = {};
6262
6422
  for (const id of Object.values(Services)) result[id] = [...pick(id)];
@@ -6265,7 +6425,7 @@ function buildMethodMap(pick) {
6265
6425
  buildMethodMap(getServiceUiMethods);
6266
6426
  buildMethodMap(getServiceModelMethods);
6267
6427
  //#endregion
6268
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/columns/accessor_traversal.js
6428
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/columns/accessor_traversal.js
6269
6429
  /** Resource types that hold column collections — DFS stops here and collects. */
6270
6430
  const COLLECT_TYPES = [ResourceTypeName.PFrame];
6271
6431
  /** Resource types DFS descends through when looking for PFrames. */
@@ -6373,7 +6533,7 @@ function indexPoolBlock(block) {
6373
6533
  return result;
6374
6534
  }
6375
6535
  //#endregion
6376
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/columns/column_registry.js
6536
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/columns/column_registry.js
6377
6537
  /**
6378
6538
  * Id-index over a set of {@link ColumnEntriesProvider}s. Sole job:
6379
6539
  * {@link PObjectId} → {@link LeafEntry}. Generic over the accessor flavour, so
@@ -6410,7 +6570,7 @@ var ColumnRegistry = class {
6410
6570
  }
6411
6571
  };
6412
6572
  //#endregion
6413
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/columns/dedup.js
6573
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/columns/dedup.js
6414
6574
  /**
6415
6575
  * Two-track dedup over an item stream keyed by `ColumnUniversalId`:
6416
6576
  *
@@ -6451,7 +6611,7 @@ function dedupColumns(items, getId, getSpec) {
6451
6611
  return out;
6452
6612
  }
6453
6613
  //#endregion
6454
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/columns/providers.js
6614
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/columns/providers.js
6455
6615
  /**
6456
6616
  * Generic entries provider over a single accessor root. Walks `<root>` once
6457
6617
  * from the supplied `rootPath`, builds an id → {@link LeafEntry} map and
@@ -6508,7 +6668,7 @@ var ResultPoolEntriesProvider = class {
6508
6668
  }
6509
6669
  };
6510
6670
  //#endregion
6511
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/template/template_ref_form.js
6671
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/template/template_ref_form.js
6512
6672
  /**
6513
6673
  * Whether `value` is a reference in the readable spelling.
6514
6674
  *
@@ -6577,7 +6737,7 @@ function expandTemplatePlRef(ref) {
6577
6737
  };
6578
6738
  }
6579
6739
  //#endregion
6580
- //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.52.0/node_modules/@milaboratories/pl-model-common/dist/template/template_relocate.js
6740
+ //#region ../node_modules/.pnpm/@milaboratories+pl-model-common@1.55.0/node_modules/@milaboratories/pl-model-common/dist/template/template_relocate.js
6581
6741
  /**
6582
6742
  * Point every column identifier in a block's params at the blocks of the project being built.
6583
6743
  *
@@ -6637,7 +6797,7 @@ function relocateBlockIds(params, blockIds) {
6637
6797
  return walk(params);
6638
6798
  }
6639
6799
  //#endregion
6640
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/block_migrations.js
6800
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/block_migrations.js
6641
6801
  /** Create a DataVersioned wrapper with correct shape */
6642
6802
  function makeVersionedData(version, data) {
6643
6803
  return {
@@ -6949,9 +7109,10 @@ var DataModelInitialChain = class extends DataModelMigrationChain {
6949
7109
  if (data !== null && typeof data === "object" && "args" in data) return fn(data);
6950
7110
  return data;
6951
7111
  };
7112
+ const initialVersion = this.versionChain[0];
6952
7113
  const step = {
6953
7114
  fromVersion: DATA_MODEL_LEGACY_VERSION,
6954
- toVersion: this.versionChain[0],
7115
+ toVersion: initialVersion,
6955
7116
  migrate: wrappedFn
6956
7117
  };
6957
7118
  return new DataModelMigrationChainWithRecover({
@@ -7215,7 +7376,7 @@ var DataModel = class DataModel {
7215
7376
  }
7216
7377
  };
7217
7378
  //#endregion
7218
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/internal.js
7379
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/internal.js
7219
7380
  /** Utility code helping to identify whether the code is running in actual UI environment */
7220
7381
  function isInUI() {
7221
7382
  return typeof globalThis.getPlatforma !== "undefined" || typeof globalThis.platforma !== "undefined";
@@ -7285,7 +7446,7 @@ function registerFutureAwait(handle, onResolve) {
7285
7446
  futureResolves.get(handle).push(onResolve);
7286
7447
  }
7287
7448
  //#endregion
7288
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/block_storage_facade.js
7449
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/block_storage_facade.js
7289
7450
  /**
7290
7451
  * All facade callback names as constants.
7291
7452
  * These are the source of truth - the interface is derived from these.
@@ -7323,7 +7484,7 @@ function registerFacadeCallbacks(callbacks) {
7323
7484
  for (const key of Object.values(BlockStorageFacadeCallbacks)) tryRegisterCallback(key, callbacks[key]);
7324
7485
  }
7325
7486
  //#endregion
7326
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/services/block_services.js
7487
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/services/block_services.js
7327
7488
  /**
7328
7489
  * Services required by all V3 blocks by default.
7329
7490
  * Edit this when a new service should be available to all blocks.
@@ -7339,7 +7500,7 @@ const BLOCK_SERVICE_FLAGS = {
7339
7500
  };
7340
7501
  resolveRequiredServices(BLOCK_SERVICE_FLAGS);
7341
7502
  //#endregion
7342
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/render/future.js
7503
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/render/future.js
7343
7504
  var FutureRef = class FutureRef {
7344
7505
  handle;
7345
7506
  postProcess;
@@ -7367,7 +7528,7 @@ var FutureRef = class FutureRef {
7367
7528
  }
7368
7529
  };
7369
7530
  //#endregion
7370
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/render/accessor.js
7531
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/render/accessor.js
7371
7532
  function ifDef(value, cb) {
7372
7533
  return value === void 0 ? void 0 : cb(value);
7373
7534
  }
@@ -7617,11 +7778,11 @@ var TreeNodeAccessor = class TreeNodeAccessor {
7617
7778
  }
7618
7779
  };
7619
7780
  //#endregion
7620
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/render/internal.js
7781
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/render/internal.js
7621
7782
  const MainAccessorName = "main";
7622
7783
  const StagingAccessorName = "staging";
7623
7784
  //#endregion
7624
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/render/util/axis_filtering.js
7785
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/render/util/axis_filtering.js
7625
7786
  function filterDataInfoEntries(dataInfoEntries, axisFilters) {
7626
7787
  const sortedFilters = [...axisFilters].sort((a, b) => b[0] - a[0]);
7627
7788
  const { type } = dataInfoEntries;
@@ -7685,7 +7846,7 @@ function filterDataInfoEntries(dataInfoEntries, axisFilters) {
7685
7846
  }
7686
7847
  }
7687
7848
  //#endregion
7688
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/labels/linked_column_postfix.js
7849
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/labels/linked_column_postfix.js
7689
7850
  /**
7690
7851
  * Structural postfix derivation for linked columns (phase 2 of `deriveDistinctLabels`).
7691
7852
  *
@@ -7857,7 +8018,10 @@ function resolveGroup(entries, indices, format) {
7857
8018
  * linked columns by the difference between their sources.
7858
8019
  */
7859
8020
  function derivePostfixes(entries, format = defaultLinkerFormatter) {
7860
- const postfix = [...entries.reduce((acc, e, idx) => acc.set(e.stem, [...acc.get(e.stem) ?? [], idx]), /* @__PURE__ */ new Map()).values()].reduce((acc, idxs) => {
8021
+ const postfix = [...entries.reduce((acc, e, idx) => {
8022
+ const key = `${e.stem}\u0000${e.marker ?? ""}`;
8023
+ return acc.set(key, [...acc.get(key) ?? [], idx]);
8024
+ }, /* @__PURE__ */ new Map()).values()].reduce((acc, idxs) => {
7861
8025
  if (idxs.length < 2) return acc;
7862
8026
  const resolved = resolveGroup(idxs.map((i) => entries[i]), idxs, format);
7863
8027
  return idxs.reduce((m, i, k) => m.set(i, resolved[k]), acc);
@@ -7868,7 +8032,7 @@ function derivePostfixes(entries, format = defaultLinkerFormatter) {
7868
8032
  });
7869
8033
  }
7870
8034
  //#endregion
7871
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/labels/derive_distinct_labels.js
8035
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/labels/derive_distinct_labels.js
7872
8036
  const DISTANCE_PENALTY = .001;
7873
8037
  const LABEL_TYPE = "__LABEL__";
7874
8038
  const LABEL_TYPE_FULL = "__LABEL__@1";
@@ -7890,9 +8054,10 @@ function isSyntheticType(t) {
7890
8054
  function deriveDistinctLabels(values, options = {}) {
7891
8055
  const stems = deriveStems(values, options);
7892
8056
  return derivePostfixes(values.map((v, i) => {
7893
- const { spec, linkerPath } = extractEntryParts(v);
8057
+ const { spec, linkerPath, marker } = extractEntryParts(v);
7894
8058
  return {
7895
8059
  stem: stems[i],
8060
+ marker,
7896
8061
  hit: spec,
7897
8062
  linkers: (linkerPath ?? []).map((s) => s.spec)
7898
8063
  };
@@ -7910,6 +8075,7 @@ function deriveStems(values, options) {
7910
8075
  if (labelForced) forcedSet.add(LABEL_TYPE_FULL);
7911
8076
  const { mainTypes, secondaryTypes } = classifyTypes(stats, values.length);
7912
8077
  const build = (typeSet, force) => buildLabels(records, typeSet, forceTraceElements, separator, force);
8078
+ const finalize = (minimized, rendered) => repairBareLabels(rendered, minimized, records, stats, forceTraceElements, separator);
7913
8079
  if (mainTypes.length === 0) {
7914
8080
  if (secondaryTypes.length !== 0) throw new Error("Non-empty secondary types list while main types list is empty.");
7915
8081
  return build(/* @__PURE__ */ new Set([LABEL_TYPE_FULL]), true) ?? throwError("Failed to derive labels using native column labels");
@@ -7923,7 +8089,7 @@ function deriveStems(values, options) {
7923
8089
  const candidateResult = build(currentSet, false);
7924
8090
  if (candidateResult !== void 0 && countUniqueLabels(candidateResult) === values.length) {
7925
8091
  const minimized = minimizeTypeSet(currentSet, records, stats, forceTraceElements, forcedSet, separator);
7926
- return repairBareByPresence(build(minimized, false) ?? throwError("Failed to derive unique labels"), minimized, records, stats, forcedSet, forceTraceElements, separator);
8092
+ return finalize(minimized, build(minimized, false) ?? throwError("Failed to derive unique labels"));
7927
8093
  }
7928
8094
  additionalType++;
7929
8095
  if (additionalType >= mainTypes.length) {
@@ -7936,20 +8102,22 @@ function deriveStems(values, options) {
7936
8102
  ...mainTypes,
7937
8103
  ...secondaryTypes
7938
8104
  ]), records, stats, forceTraceElements, forcedSet, separator);
7939
- return repairBareByPresence(build(minimized, true) ?? throwError("Failed to derive unique labels"), minimized, records, stats, forcedSet, forceTraceElements, separator);
8105
+ return finalize(minimized, build(minimized, true) ?? throwError("Failed to derive unique labels"));
7940
8106
  }
7941
8107
  function extractEntryParts(entry) {
7942
8108
  if (!("spec" in entry && typeof entry.spec === "object")) return {
7943
8109
  spec: entry,
7944
8110
  extraTrace: void 0,
7945
8111
  linkerPath: void 0,
7946
- qualifications: void 0
8112
+ qualifications: void 0,
8113
+ marker: void 0
7947
8114
  };
7948
8115
  return {
7949
8116
  spec: entry.spec,
7950
8117
  extraTrace: entry.extraTrace,
7951
8118
  linkerPath: entry.linkerPath,
7952
- qualifications: entry.qualifications
8119
+ qualifications: entry.qualifications,
8120
+ marker: entry.marker
7953
8121
  };
7954
8122
  }
7955
8123
  function formatQualification(q) {
@@ -8101,62 +8269,202 @@ function minimizeTypeSet(typeSet, records, stats, forceTraceElements, forcedSet,
8101
8269
  }
8102
8270
  return result;
8103
8271
  }
8104
- const ABSENT_VALUE = "\0absent";
8105
- /** Whether `fullType`'s rendered value differs across the group (absence counts as a value). */
8106
- function typeDistinguishes(records, group, fullType) {
8107
- const values = /* @__PURE__ */ new Set();
8108
- for (const i of group) {
8109
- const ft = records[i].fullTrace.find((e) => e.fullType === fullType);
8110
- values.add(ft?.label ?? ABSENT_VALUE);
8111
- if (values.size > 1) return true;
8112
- }
8113
- return false;
8114
- }
8115
- /** The highest-importance trace type this row HAS (outside `typeSet`) that sets it apart from its
8116
- * group peers. `undefined` when the row carries nothing distinguishing of its own. */
8117
- function bestDistinguishingType(records, group, row, typeSet, forcedSet, forceTraceElements, stats) {
8118
- return records[row].fullTrace.reduce((best, ft) => {
8119
- if (typeSet.has(ft.fullType) || forcedSet.has(ft.fullType) || forceTraceElements?.has(ft.type)) return best;
8120
- if (!typeDistinguishes(records, group, ft.fullType)) return best;
8121
- const imp = stats.importances.get(ft.fullType) ?? 0;
8122
- return best === void 0 || imp > best.imp ? {
8123
- type: ft.fullType,
8124
- imp
8125
- } : best;
8126
- }, void 0)?.type;
8127
- }
8128
- /**
8129
- * Un-bare columns distinguished only "by absence". After minimization a column can end up with a
8130
- * bare trace zone (only the forced native label) while a peer sharing that same base renders extra
8131
- * tokens — the column reads as unique purely because it LACKS what the peer has. For each such bare
8132
- * column this re-renders JUST that column's label with the highest-importance trace type it actually
8133
- * carries that tells it apart from its peers, so every colliding column is distinguished by a token
8134
- * it HAS rather than by omission.
8135
- *
8136
- * Patches individual labels rather than the shared type set on purpose: the set is global, so adding
8137
- * a type there would also decorate unrelated columns in other groups that happen to carry it. Only
8138
- * bare columns' labels grow (a superset of their previous value), so uniqueness is preserved. Groups
8139
- * are keyed by the base = the label rendered from the forced types alone.
8140
- */
8141
- function repairBareByPresence(labels, minimized, records, stats, forcedSet, forceTraceElements, separator) {
8142
- const base = records.map((r) => renderRecordLabel(r, forcedSet, forceTraceElements, separator));
8143
- const isBare = records.map((r, i) => renderRecordLabel(r, minimized, forceTraceElements, separator) === base[i]);
8144
- const groups = records.reduce((acc, _, i) => acc.set(base[i] ?? "", [...acc.get(base[i] ?? "") ?? [], i]), /* @__PURE__ */ new Map());
8272
+ /**
8273
+ * Repairs labels told apart only by what they lack: in a group with the same shown base, a
8274
+ * member is bare when its shown parts are a strict subset of a peer's. The group is re-labelled
8275
+ * from parts all members carry, or bare members add a hidden part (group-wide when labels stay
8276
+ * unique), whichever keeps the more important distinction; labels stay unique across the list.
8277
+ */
8278
+ function repairBareLabels(labels, minimized, records, stats, forceTraceElements, separator) {
8279
+ const traces = records.map((r) => new Map(r.fullTrace.map((ft) => [ft.fullType, ft.label])));
8280
+ const isQualification = (t) => isSyntheticType(t.split("@")[0]);
8281
+ const depths = records.map((r) => {
8282
+ const steps = r.fullTrace.filter((ft) => ft.fullType !== LABEL_TYPE_FULL && !isQualification(ft.fullType));
8283
+ return new Map(steps.map((ft, idx) => [ft.fullType, steps.length - idx]));
8284
+ });
8285
+ const isForced = (t) => forceTraceElements?.has(t.split("@")[0]) === true;
8286
+ const isDistinctionType = (t) => t !== LABEL_TYPE_FULL && !isQualification(t);
8287
+ const importance = (t) => stats.importances.get(t) ?? 0;
8288
+ const byImportance = (a, b) => importance(b) - importance(a);
8289
+ const render = (i, types) => renderRecordLabel(records[i], types, forceTraceElements, separator);
8290
+ const partKey = (t, l) => `${t}\u0000${l}`;
8291
+ const labelShown = minimized.has(LABEL_TYPE_FULL);
8292
+ const baseTypes = new Set(labelShown ? [LABEL_TYPE_FULL] : []);
8293
+ const qualTypes = [...minimized].filter(isQualification);
8294
+ const groups = /* @__PURE__ */ new Map();
8295
+ records.forEach((_, i) => {
8296
+ const key = render(i, baseTypes) ?? "";
8297
+ const group = groups.get(key);
8298
+ if (group) group.push(i);
8299
+ else groups.set(key, [i]);
8300
+ });
8145
8301
  const patched = [...labels];
8146
- for (const group of groups.values()) {
8147
- if (!group.some((i) => !isBare[i])) continue;
8148
- for (const i of group) {
8149
- if (!isBare[i]) continue;
8150
- const chosen = bestDistinguishingType(records, group, i, minimized, forcedSet, forceTraceElements, stats);
8151
- if (chosen === void 0) continue;
8152
- const withToken = /* @__PURE__ */ new Set([...minimized, chosen]);
8153
- patched[i] = renderRecordLabel(records[i], withToken, forceTraceElements, separator) ?? patched[i];
8154
- }
8302
+ const counts = /* @__PURE__ */ new Map();
8303
+ const addCount = (l, d) => counts.set(l, (counts.get(l) ?? 0) + d);
8304
+ for (const l of patched) addCount(l, 1);
8305
+ const tryApply = (members, next) => {
8306
+ if (new Set(next).size !== members.length) return false;
8307
+ for (const i of members) addCount(patched[i], -1);
8308
+ const clashes = next.some((l) => (counts.get(l) ?? 0) > 0);
8309
+ members.forEach((i, k) => {
8310
+ if (!clashes) patched[i] = next[k];
8311
+ addCount(patched[i], 1);
8312
+ });
8313
+ return !clashes;
8314
+ };
8315
+ for (const [key, group] of groups) {
8316
+ if (group.length < 2) continue;
8317
+ const shown = group.map((i) => [...traces[i]].filter(([t]) => t !== LABEL_TYPE_FULL && (minimized.has(t) || isForced(t))));
8318
+ const partKeys = shown.map((parts) => parts.map(([t, l]) => partKey(t, l)));
8319
+ const sigIndex = /* @__PURE__ */ new Map();
8320
+ const setKeys = [];
8321
+ const setMembers = [];
8322
+ const sigOf = partKeys.map((keys, k) => {
8323
+ const sig = [...keys].sort().join("");
8324
+ let s = sigIndex.get(sig);
8325
+ if (s === void 0) {
8326
+ s = setKeys.length;
8327
+ sigIndex.set(sig, s);
8328
+ setKeys.push(keys);
8329
+ setMembers.push([]);
8330
+ }
8331
+ setMembers[s].push(k);
8332
+ return s;
8333
+ });
8334
+ const setSets = setKeys.map((keys) => new Set(keys));
8335
+ const postings = /* @__PURE__ */ new Map();
8336
+ setKeys.forEach((keys, s) => keys.forEach((p) => {
8337
+ const list = postings.get(p);
8338
+ if (list) list.push(s);
8339
+ else postings.set(p, [s]);
8340
+ }));
8341
+ const withParts = setKeys.map((_, s) => s).filter((s) => setKeys[s].length > 0);
8342
+ const supersetSets = setKeys.map((keys, s) => {
8343
+ if (keys.length === 0) return withParts;
8344
+ const rarest = keys.reduce((a, b) => postings.get(a).length <= postings.get(b).length ? a : b);
8345
+ return postings.get(rarest).filter((t) => t !== s && setSets[t].size > keys.length && keys.every((x) => setSets[t].has(x)));
8346
+ });
8347
+ const bare = group.map((_, k) => supersetSets[sigOf[k]].length > 0);
8348
+ if (!bare.some(Boolean)) continue;
8349
+ const typeCount = /* @__PURE__ */ new Map();
8350
+ for (const i of group) for (const t of traces[i].keys()) typeCount.set(t, (typeCount.get(t) ?? 0) + 1);
8351
+ const isUneven = (t) => (typeCount.get(t) ?? 0) > 0 && typeCount.get(t) < group.length;
8352
+ const unevenMax = Math.max(...[...minimized].filter((t) => isDistinctionType(t) && !isForced(t) && isUneven(t)).map(importance));
8353
+ let sharedLabels;
8354
+ let sharedMax = Number.NEGATIVE_INFINITY;
8355
+ if (labelShown && key !== "") {
8356
+ const shared = [...typeCount].filter(([t, n]) => n === group.length && isDistinctionType(t)).map(([t]) => t).sort(byImportance);
8357
+ const chosen = new Set(baseTypes);
8358
+ const distinctCount = (types) => new Set(group.map((i) => render(i, types) ?? "")).size;
8359
+ let count = distinctCount(chosen);
8360
+ for (const t of shared) {
8361
+ if (count === group.length) break;
8362
+ const next = distinctCount(/* @__PURE__ */ new Set([...chosen, t]));
8363
+ if (next > count) {
8364
+ if (chosen.size === baseTypes.size) sharedMax = importance(t);
8365
+ chosen.add(t);
8366
+ count = next;
8367
+ }
8368
+ }
8369
+ const withTags = /* @__PURE__ */ new Set([...chosen, ...qualTypes]);
8370
+ const complete = group.map((i) => render(i, withTags)).filter((l) => l !== void 0);
8371
+ if (count === group.length && complete.length === group.length && new Set(complete).size === group.length) sharedLabels = complete;
8372
+ }
8373
+ const applyBareFix = () => {
8374
+ const bareMembers = group.filter((_, k) => bare[k]);
8375
+ const oldBare = /* @__PURE__ */ new Map();
8376
+ for (const i of bareMembers) oldBare.set(patched[i], (oldBare.get(patched[i]) ?? 0) + 1);
8377
+ const isFree = (l) => (counts.get(l) ?? 0) - (oldBare.get(l) ?? 0) === 0;
8378
+ const shownTypes = (k) => [
8379
+ ...baseTypes,
8380
+ ...shown[k].map(([t]) => t),
8381
+ ...qualTypes
8382
+ ];
8383
+ const setLabels = /* @__PURE__ */ new Map();
8384
+ const labelsIn = (s) => {
8385
+ let byType = setLabels.get(s);
8386
+ if (byType) return byType;
8387
+ byType = /* @__PURE__ */ new Map();
8388
+ for (const m of setMembers[s]) for (const [t, l] of traces[group[m]]) {
8389
+ const entry = byType.get(t) ?? {
8390
+ labels: /* @__PURE__ */ new Set(),
8391
+ n: 0
8392
+ };
8393
+ entry.labels.add(l);
8394
+ entry.n++;
8395
+ byType.set(t, entry);
8396
+ }
8397
+ setLabels.set(s, byType);
8398
+ return byType;
8399
+ };
8400
+ const someDiffers = (s, u, mine) => {
8401
+ const entry = labelsIn(s).get(u);
8402
+ if (entry === void 0) return mine !== void 0;
8403
+ if (entry.n < setMembers[s].length) return true;
8404
+ return entry.labels.size > 1 || !entry.labels.has(mine ?? "");
8405
+ };
8406
+ const extraDepthsOf = /* @__PURE__ */ new Map();
8407
+ const extraDepths = (s) => {
8408
+ let depthsSet = extraDepthsOf.get(s);
8409
+ if (depthsSet) return depthsSet;
8410
+ depthsSet = new Set(supersetSets[s].flatMap((t) => {
8411
+ const rep = setMembers[t][0];
8412
+ return shown[rep].filter(([u, l]) => !isQualification(u) && !setSets[s].has(partKey(u, l))).map(([u]) => depths[group[rep]].get(u));
8413
+ }));
8414
+ extraDepthsOf.set(s, depthsSet);
8415
+ return depthsSet;
8416
+ };
8417
+ const picks = /* @__PURE__ */ new Map();
8418
+ group.forEach((i, k) => {
8419
+ if (!bare[k]) return;
8420
+ const peers = supersetSets[sigOf[k]];
8421
+ const kindDepths = extraDepths(sigOf[k]);
8422
+ const sameKind = (u) => kindDepths.has(depths[i].get(u)) ? 1 : 0;
8423
+ const candidates = [...traces[i].keys()].filter((u) => !minimized.has(u) && !isForced(u)).filter((u) => peers.some((t) => someDiffers(t, u, traces[i].get(u)))).sort((a, b) => sameKind(b) - sameKind(a) || byImportance(a, b));
8424
+ for (const u of candidates) {
8425
+ const label = render(i, /* @__PURE__ */ new Set([...shownTypes(k), u]));
8426
+ if (label !== void 0 && isFree(label)) {
8427
+ picks.set(i, {
8428
+ type: u,
8429
+ label
8430
+ });
8431
+ break;
8432
+ }
8433
+ }
8434
+ });
8435
+ if (picks.size === 0) return false;
8436
+ if (labelShown && key !== "") {
8437
+ const picked = [...new Set([...picks.values()].map((p) => p.type))];
8438
+ const full = group.map((i, k) => render(i, /* @__PURE__ */ new Set([...shownTypes(k), ...picked]))).filter((l) => l !== void 0);
8439
+ if (full.length === group.length && tryApply(group, full)) return true;
8440
+ }
8441
+ const proposed = new Map([...picks].map(([i, p]) => [i, p.label]));
8442
+ for (let changed = true; changed;) {
8443
+ changed = false;
8444
+ const firstOld = /* @__PURE__ */ new Map();
8445
+ for (const [i, l] of proposed) {
8446
+ const old = firstOld.get(l);
8447
+ if (old === void 0) firstOld.set(l, patched[i]);
8448
+ else if (old !== patched[i]) changed = proposed.delete(i);
8449
+ }
8450
+ const kept = new Set(bareMembers.filter((i) => !proposed.has(i)).map((i) => patched[i]));
8451
+ for (const [i, l] of proposed) if (kept.has(l)) changed = proposed.delete(i);
8452
+ }
8453
+ for (const i of bareMembers) addCount(patched[i], -1);
8454
+ for (const i of bareMembers) {
8455
+ patched[i] = proposed.get(i) ?? patched[i];
8456
+ addCount(patched[i], 1);
8457
+ }
8458
+ return proposed.size > 0;
8459
+ };
8460
+ if (sharedLabels && sharedMax >= unevenMax && tryApply(group, sharedLabels)) continue;
8461
+ if (applyBareFix()) continue;
8462
+ if (sharedLabels && sharedMax < unevenMax) tryApply(group, sharedLabels);
8155
8463
  }
8156
8464
  return patched;
8157
8465
  }
8158
8466
  //#endregion
8159
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/render/util/label.js
8467
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/render/util/label.js
8160
8468
  /** @deprecated Use deriveDistinctLabels */
8161
8469
  function deriveLabels(values, getSpec, options = {}) {
8162
8470
  return deriveDistinctLabels(values.map(getSpec), options).map((label, i) => ({
@@ -8165,15 +8473,17 @@ function deriveLabels(values, getSpec, options = {}) {
8165
8473
  }));
8166
8474
  }
8167
8475
  //#endregion
8168
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/render/util/pcolumn_data.js
8169
- const RT_RESOURCE_MAP = "PColumnData/ResourceMap";
8170
- const RT_RESOURCE_MAP_PARTITIONED = "PColumnData/Partitioned/ResourceMap";
8171
- const RT_JSON_PARTITIONED = "PColumnData/JsonPartitioned";
8172
- const RT_BINARY_PARTITIONED = "PColumnData/BinaryPartitioned";
8173
- const RT_PARQUET_PARTITIONED = "PColumnData/ParquetPartitioned";
8174
- const RT_JSON_SUPER_PARTITIONED = "PColumnData/Partitioned/JsonPartitioned";
8175
- const RT_BINARY_SUPER_PARTITIONED = "PColumnData/Partitioned/BinaryPartitioned";
8176
- const RT_PARQUET_SUPER_PARTITIONED = "PColumnData/Partitioned/ParquetPartitioned";
8476
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/render/util/pcolumn_data.js
8477
+ const PCD_PREFIX = "PColumnData/";
8478
+ const RT_RESOURCE_MAP = PCD_PREFIX + "ResourceMap";
8479
+ const RT_RESOURCE_MAP_PARTITIONED = PCD_PREFIX + "Partitioned/ResourceMap";
8480
+ const RT_JSON_PARTITIONED = PCD_PREFIX + "JsonPartitioned";
8481
+ const RT_BINARY_PARTITIONED = PCD_PREFIX + "BinaryPartitioned";
8482
+ const RT_PARQUET_PARTITIONED = PCD_PREFIX + "ParquetPartitioned";
8483
+ const PCD_SUP_PREFIX = PCD_PREFIX + "Partitioned/";
8484
+ const RT_JSON_SUPER_PARTITIONED = PCD_SUP_PREFIX + "JsonPartitioned";
8485
+ const RT_BINARY_SUPER_PARTITIONED = PCD_SUP_PREFIX + "BinaryPartitioned";
8486
+ const RT_PARQUET_SUPER_PARTITIONED = PCD_SUP_PREFIX + "ParquetPartitioned";
8177
8487
  const removeIndexSuffix = (keyStr) => {
8178
8488
  if (keyStr.endsWith(".index")) return {
8179
8489
  baseKey: keyStr.substring(0, keyStr.length - 6),
@@ -8459,7 +8769,7 @@ function allPColumnsReady(columns) {
8459
8769
  return columns.every(isPColumnReady);
8460
8770
  }
8461
8771
  //#endregion
8462
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/render/util/column_collection.js
8772
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/render/util/column_collection.js
8463
8773
  function isPColumnValues(value) {
8464
8774
  if (!Array.isArray(value)) return false;
8465
8775
  if (value.length === 0) return true;
@@ -8493,9 +8803,10 @@ function splitFiltersToAxisFilter(splitFilters) {
8493
8803
  }
8494
8804
  function fallbackIdDeriver(originalId, axisFilters) {
8495
8805
  if (!axisFilters || axisFilters.length === 0) return originalId;
8806
+ const filtersToCanonicalize = [...axisFilters].sort((a, b) => a[0] - b[0]);
8496
8807
  return (0, import_canonicalize.default)({
8497
8808
  id: originalId,
8498
- axisFilters: [...axisFilters].sort((a, b) => a[0] - b[0])
8809
+ axisFilters: filtersToCanonicalize
8499
8810
  });
8500
8811
  }
8501
8812
  /** Checks if a selector object uses any anchor properties */
@@ -8737,7 +9048,7 @@ var PColumnCollection = class {
8737
9048
  }
8738
9049
  };
8739
9050
  //#endregion
8740
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/services/service_bridge.js
9051
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/services/service_bridge.js
8741
9052
  /**
8742
9053
  * Builds a ServiceProxy from a ServiceDispatch.
8743
9054
  * Each service method call is forwarded to dispatch.callServiceMethod.
@@ -8746,7 +9057,7 @@ function createServiceProxy(dispatch) {
8746
9057
  return ((serviceId) => Object.freeze(Object.fromEntries(dispatch.getServiceMethods(serviceId).map((method) => [method, (...args) => dispatch.callServiceMethod(serviceId, method, ...args)]))));
8747
9058
  }
8748
9059
  //#endregion
8749
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/services/get_services.js
9060
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/services/get_services.js
8750
9061
  const cachedServices = /* @__PURE__ */ new WeakMap();
8751
9062
  function getService(name, deps) {
8752
9063
  const ctx = deps?.ctx ?? getCfgRenderCtx();
@@ -8760,7 +9071,7 @@ function getService(name, deps) {
8760
9071
  })();
8761
9072
  }
8762
9073
  //#endregion
8763
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/render/util/pframe_upgraders.js
9074
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/render/util/pframe_upgraders.js
8764
9075
  function patchInSetFilters(filters) {
8765
9076
  const inSetToOrEqual = (predicate) => {
8766
9077
  if (predicate.operator !== "InSet") return predicate;
@@ -8798,7 +9109,7 @@ function patchInSetFilters(filters) {
8798
9109
  return filters.map((filter) => mapFilter(filter, inSetToOrEqual));
8799
9110
  }
8800
9111
  //#endregion
8801
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/render/api.js
9112
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/render/api.js
8802
9113
  /**
8803
9114
  * Helper function to match domain objects
8804
9115
  * @param query Optional domain to match against
@@ -8849,11 +9160,13 @@ var ResultPool = class {
8849
9160
  let refsWithEnrichments = false;
8850
9161
  if (typeof opts !== "undefined") {
8851
9162
  if (typeof opts === "function") labelOps = opts;
8852
- else if (typeof opts === "object") if ("includeNativeLabel" in opts || "separator" in opts || "addLabelAsSuffix" in opts) labelOps = opts;
8853
- else {
8854
- opts = opts;
8855
- labelOps = opts.label ?? {};
8856
- refsWithEnrichments = opts.refsWithEnrichments ?? false;
9163
+ else if (typeof opts === "object") {
9164
+ if ("includeNativeLabel" in opts || "separator" in opts || "addLabelAsSuffix" in opts) labelOps = opts;
9165
+ else {
9166
+ opts = opts;
9167
+ labelOps = opts.label ?? {};
9168
+ refsWithEnrichments = opts.refsWithEnrichments ?? false;
9169
+ }
8857
9170
  }
8858
9171
  }
8859
9172
  if (typeof labelOps === "object") return deriveLabels(filtered, (o) => o.obj, labelOps ?? {}).map(({ value: { ref }, label }) => ({
@@ -9247,27 +9560,27 @@ var PluginRenderCtx = class extends RenderCtxBase {
9247
9560
  }
9248
9561
  };
9249
9562
  //#endregion
9250
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/plugin_model.js
9563
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/plugin_model.js
9251
9564
  /** Internal plugin↔block handshake. `Symbol.for` (not `Symbol`) so it stays
9252
9565
  * identical across multiple `@platforma-sdk/model` copies in one process. */
9253
9566
  const CREATE_PLUGIN_MODEL = Symbol.for("@platforma-sdk/model:createPluginModel");
9254
9567
  //#endregion
9255
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/version.js
9256
- const PlatformaSDKVersion = "1.84.10";
9568
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/version.js
9569
+ const PlatformaSDKVersion = "1.88.0";
9257
9570
  //#endregion
9258
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/bconfig/types.js
9571
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/bconfig/types.js
9259
9572
  function isConfigLambda(cfgOrFh) {
9260
9573
  return cfgOrFh.__renderLambda === true;
9261
9574
  }
9262
9575
  //#endregion
9263
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/bconfig/normalization.js
9576
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/bconfig/normalization.js
9264
9577
  function downgradeCfgOrLambda(data) {
9265
9578
  if (data === void 0) return void 0;
9266
9579
  if (isConfigLambda(data)) return data.handle;
9267
9580
  return data;
9268
9581
  }
9269
9582
  //#endregion
9270
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/block_storage_callbacks.js
9583
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/block_storage_callbacks.js
9271
9584
  /**
9272
9585
  * BlockStorage Callback Implementations - wired to facade callbacks in BlockModelV3.done().
9273
9586
  *
@@ -9556,42 +9869,77 @@ function createInitialStorageFromParams(paramsJson, blockIdsJson, hooks) {
9556
9869
  }
9557
9870
  }
9558
9871
  /**
9559
- * Derives args from storage using the provided args function.
9560
- * This extracts data from storage and passes it to the block's args() function.
9872
+ * Derives args from storage using the block's args function, then finds the issues in them
9873
+ * with the block's `argsIssues()`.
9874
+ *
9875
+ * A throw from `argsIssues()`, or a result that is not a list of issues, is one error carrying
9876
+ * the thrown or parser message, so a broken check keeps the block from running instead of
9877
+ * letting it run unchecked.
9561
9878
  *
9562
9879
  * @param storageJson - Storage as JSON string
9563
- * @param deriveArgs - The block's args derivation function
9564
- * @returns ArgsDeriveResult with derived args or error
9880
+ * @param derivation - The block's args and args issues lambdas
9881
+ * @returns ArgsDeriveResult with derived args and args issues, or error
9565
9882
  */
9566
- function deriveArgsFromStorage(storageJson, deriveArgs) {
9883
+ function deriveArgsFromStorage(storageJson, derivation) {
9567
9884
  const { data } = normalizeStorage(storageJson);
9568
- try {
9569
- return { value: deriveArgs(data) };
9570
- } catch (e) {
9571
- return { error: `args() threw: ${e instanceof Error ? e.message : String(e)}` };
9572
- }
9885
+ const run = deriveArgsWithIssues(data, derivation);
9886
+ if (run.type === "threw") return { error: `args() threw: ${run.message}` };
9887
+ const { args, issues } = run;
9888
+ const errors = errorMessages(issues);
9889
+ if (errors.length > 0) return {
9890
+ error: errors.join("; "),
9891
+ args,
9892
+ issues
9893
+ };
9894
+ return {
9895
+ value: args,
9896
+ issues
9897
+ };
9573
9898
  }
9574
9899
  /**
9575
9900
  * Derives prerunArgs from storage.
9576
- * Uses derivePrerunArgs if provided, otherwise falls back to deriveArgs.
9901
+ *
9902
+ * A block with its own `prerunArgs()` is staged with what it returns, unless its
9903
+ * `prerunArgsIssues()` finds errors in it; `argsIssues()` is not consulted then. A `prerunArgs()`
9904
+ * that returns `undefined` skips staging, and `prerunArgsIssues()` is not called for it.
9905
+ *
9906
+ * A block without its own `prerunArgs()` is staged with its args, unless `argsIssues()` finds
9907
+ * errors in them: args that may not run are not staged either.
9908
+ *
9909
+ * A throw from either issues lambda, or a result that is not a list of issues, is one error
9910
+ * carrying the thrown or parser message.
9577
9911
  *
9578
9912
  * @param storageJson - Storage as JSON string
9579
- * @param deriveArgs - The block's args derivation function (fallback)
9580
- * @param derivePrerunArgs - Optional prerun args derivation function
9581
- * @returns ArgsDeriveResult with derived prerunArgs or error
9913
+ * @param derivation - The block's args, args issues and own staging lambdas
9914
+ * @returns PrerunArgsDeriveResult with derived prerunArgs and their issues, or error
9582
9915
  */
9583
- function derivePrerunArgsFromStorage(storageJson, deriveArgs, derivePrerunArgs) {
9916
+ function derivePrerunArgsFromStorage(storageJson, derivation) {
9584
9917
  const { data } = normalizeStorage(storageJson);
9585
- if (derivePrerunArgs) try {
9586
- return { value: derivePrerunArgs(data) };
9587
- } catch (e) {
9588
- return { error: `prerunArgs() threw: ${e instanceof Error ? e.message : String(e)}` };
9589
- }
9590
- try {
9591
- return { value: deriveArgs(data) };
9592
- } catch (e) {
9593
- return { error: `args() threw (fallback): ${e instanceof Error ? e.message : String(e)}` };
9918
+ const { ownPrerunArgs } = derivation;
9919
+ if (ownPrerunArgs !== void 0) {
9920
+ const run = derivePrerunArgsWithIssues(data, ownPrerunArgs);
9921
+ if (run.type === "threw") return { error: `prerunArgs() threw: ${run.message}` };
9922
+ const errors = errorMessages(run.issues);
9923
+ if (errors.length > 0) return {
9924
+ error: errors.join("; "),
9925
+ issues: run.issues
9926
+ };
9927
+ return {
9928
+ value: run.args,
9929
+ issues: run.issues
9930
+ };
9594
9931
  }
9932
+ const run = deriveArgsWithIssues(data, derivation);
9933
+ if (run.type === "threw") return { error: `args() threw (fallback): ${run.message}` };
9934
+ const errors = errorMessages(run.issues);
9935
+ if (errors.length > 0) return {
9936
+ error: `argsIssues() found errors (fallback): ${errors.join("; ")}`,
9937
+ issues: run.issues
9938
+ };
9939
+ return {
9940
+ value: run.args,
9941
+ issues: run.issues
9942
+ };
9595
9943
  }
9596
9944
  /**
9597
9945
  * Derives this block's template-entry params from storage.
@@ -9610,7 +9958,7 @@ function derivePrerunArgsFromStorage(storageJson, deriveArgs, derivePrerunArgs)
9610
9958
  *
9611
9959
  * @param storageJson - Storage as JSON string
9612
9960
  * @param deriveTemplateParams - The block's templateParams lambda
9613
- * @returns ArgsDeriveResult holding the params exactly as the block projected them
9961
+ * @returns DeriveResult holding the params exactly as the block projected them
9614
9962
  */
9615
9963
  function deriveTemplateParamsFromStorage(storageJson, deriveTemplateParams) {
9616
9964
  const { data } = normalizeStorage(storageJson);
@@ -9620,8 +9968,76 @@ function deriveTemplateParamsFromStorage(storageJson, deriveTemplateParams) {
9620
9968
  return { error: `templateParams() threw: ${e instanceof Error ? e.message : String(e)}` };
9621
9969
  }
9622
9970
  }
9971
+ function deriveArgsWithIssues(data, derivation) {
9972
+ const { deriveArgs, argsIssues } = derivation;
9973
+ let args;
9974
+ try {
9975
+ args = deriveArgs(data);
9976
+ } catch (e) {
9977
+ return {
9978
+ type: "threw",
9979
+ message: messageOf(e)
9980
+ };
9981
+ }
9982
+ if (argsIssues === void 0) return {
9983
+ type: "derived",
9984
+ args,
9985
+ issues: []
9986
+ };
9987
+ return {
9988
+ type: "derived",
9989
+ args,
9990
+ issues: collectIssues(() => argsIssues({
9991
+ args,
9992
+ data
9993
+ }))
9994
+ };
9995
+ }
9996
+ function derivePrerunArgsWithIssues(data, derivation) {
9997
+ const { derivePrerunArgs, prerunArgsIssues } = derivation;
9998
+ let prerunArgs;
9999
+ try {
10000
+ prerunArgs = derivePrerunArgs(data);
10001
+ } catch (e) {
10002
+ return {
10003
+ type: "threw",
10004
+ message: messageOf(e)
10005
+ };
10006
+ }
10007
+ if (prerunArgsIssues === void 0 || prerunArgs === void 0) return {
10008
+ type: "derived",
10009
+ args: prerunArgs,
10010
+ issues: []
10011
+ };
10012
+ return {
10013
+ type: "derived",
10014
+ args: prerunArgs,
10015
+ issues: collectIssues(() => prerunArgsIssues({
10016
+ prerunArgs,
10017
+ data
10018
+ }))
10019
+ };
10020
+ }
10021
+ /**
10022
+ * Calls an issues lambda of the block. Its result is parsed rather than trusted: a block's types
10023
+ * do not hold at run time, and an entry that cannot be read must count as an error, not slip
10024
+ * past. A throw, or a result that cannot be read, is one error carrying its message.
10025
+ */
10026
+ function collectIssues(issuesOf) {
10027
+ try {
10028
+ return parseArgsIssues(issuesOf());
10029
+ } catch (e) {
10030
+ return [{
10031
+ severity: "error",
10032
+ message: messageOf(e)
10033
+ }];
10034
+ }
10035
+ }
10036
+ function errorMessages(issues) {
10037
+ return issues.filter((issue) => issue.severity !== "warning").map((issue) => issue.message);
10038
+ }
9623
10039
  //#endregion
9624
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/block_model.js
10040
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/block_model.js
9625
10041
  /**
9626
10042
  * Merges two feature flag objects with type-aware logic:
9627
10043
  * - `supports*` (boolean): OR — `true` if either side is `true`
@@ -9649,10 +10065,12 @@ var BlockModelV3 = class BlockModelV3 {
9649
10065
  static FEATURE_FLAGS = {
9650
10066
  supportsLazyState: true,
9651
10067
  supportsPframeQueryRanking: true,
10068
+ supportsColumnLinks: true,
9652
10069
  requiresUIAPIVersion: 3,
9653
10070
  requiresModelAPIVersion: 2,
9654
10071
  requiresCreatePTable: 2,
9655
10072
  requiresPFramesVersion: REQUIRES_PFRAMES_VERSION,
10073
+ requiresArgsIssues: true,
9656
10074
  ...BLOCK_SERVICE_FLAGS
9657
10075
  };
9658
10076
  /** @deprecated Use FEATURE_FLAGS */
@@ -9696,7 +10114,9 @@ var BlockModelV3 = class BlockModelV3 {
9696
10114
  enrichmentTargets: void 0,
9697
10115
  featureFlags: { ...BlockModelV3.FEATURE_FLAGS },
9698
10116
  deriveArgs: void 0,
10117
+ argsIssues: void 0,
9699
10118
  derivePrerunArgs: void 0,
10119
+ prerunArgsIssues: void 0,
9700
10120
  deriveTemplateParams: void 0,
9701
10121
  plugins: {}
9702
10122
  });
@@ -9726,19 +10146,56 @@ var BlockModelV3 = class BlockModelV3 {
9726
10146
  * Sets a function to derive block args from data.
9727
10147
  * This is called during setData to compute the args that will be used for block execution.
9728
10148
  *
10149
+ * Build args even from incomplete data, and say what keeps the block from running in
10150
+ * {@link argsIssues}: args that exist let the platform see what the block references
10151
+ * before it is ready to run. Throwing here still works, and means there are no args at all.
10152
+ *
10153
+ * Setting args again drops a {@link argsIssues} set before, since it checked other args.
10154
+ *
9729
10155
  * @example
9730
10156
  * .args<BlockArgs>((data) => ({ numbers: data.numbers }))
9731
10157
  *
9732
10158
  * @example
9733
- * .args<BlockArgs>((data) => {
9734
- * if (data.numbers.length === 0) throw new Error('Numbers required'); // block not ready
9735
- * return { numbers: data.numbers };
9736
- * })
10159
+ * .args<BlockArgs>((data) => ({ numbers: data.numbers }))
10160
+ * .argsIssues(({ args }) =>
10161
+ * args.numbers.length === 0 ? [{ severity: 'error', message: 'Numbers required' }] : [],
10162
+ * )
9737
10163
  */
9738
10164
  args(lambda) {
9739
10165
  return new BlockModelV3({
9740
10166
  ...this.config,
9741
- deriveArgs: lambda
10167
+ deriveArgs: lambda,
10168
+ argsIssues: void 0
10169
+ });
10170
+ }
10171
+ /**
10172
+ * Sets a function that finds what keeps the block from running with the args {@link args}
10173
+ * derived (optional). It is called after `args()`, with its result and the data it was built
10174
+ * from.
10175
+ *
10176
+ * An issue of severity `"error"` keeps the block from running; a `"warning"` does not. A throw,
10177
+ * or a result that is not a list of issues, counts as one error carrying the thrown or parser
10178
+ * message. Without this function, args that `args()` returns are always runnable.
10179
+ *
10180
+ * A block without its own {@link prerunArgs} is staged with its args, and only while this
10181
+ * function finds no errors in them.
10182
+ *
10183
+ * Must be set after `.args()`, and is dropped when `.args()` is set again.
10184
+ *
10185
+ * @example
10186
+ * .args((data) => ({ dataset: data.dataset, threshold: data.threshold }))
10187
+ * .argsIssues(({ args }) => {
10188
+ * const issues: ArgsIssue[] = [];
10189
+ * if (args.dataset === undefined) issues.push({ severity: 'error', message: 'Select a dataset' });
10190
+ * if (args.threshold > 0.5) issues.push({ severity: 'warning', message: 'Threshold is high' });
10191
+ * return issues;
10192
+ * })
10193
+ */
10194
+ argsIssues(fn) {
10195
+ if (this.config.deriveArgs === void 0) throw new Error("argsIssues() must be set after args(): it checks the args they derive.");
10196
+ return new BlockModelV3({
10197
+ ...this.config,
10198
+ argsIssues: fn
9742
10199
  });
9743
10200
  }
9744
10201
  /**
@@ -9751,6 +10208,9 @@ var BlockModelV3 = class BlockModelV3 {
9751
10208
  * The staging / prerun phase runs only if currentPrerunArgs differs from the executed
9752
10209
  * version of prerunArgs (same comparison logic as currentArgs vs prodArgs).
9753
10210
  *
10211
+ * Setting prerun args again drops a {@link prerunArgsIssues} set before, since it checked other
10212
+ * args.
10213
+ *
9754
10214
  * @example
9755
10215
  * .prerunArgs((data) => ({ numbers: data.numbers }))
9756
10216
  *
@@ -9764,7 +10224,33 @@ var BlockModelV3 = class BlockModelV3 {
9764
10224
  prerunArgs(fn) {
9765
10225
  return new BlockModelV3({
9766
10226
  ...this.config,
9767
- derivePrerunArgs: fn
10227
+ derivePrerunArgs: fn,
10228
+ prerunArgsIssues: void 0
10229
+ });
10230
+ }
10231
+ /**
10232
+ * Sets a function that finds what keeps the block from being staged with the args
10233
+ * {@link prerunArgs} derived (optional). It is called after `prerunArgs()`, with its result
10234
+ * and the data it was built from; not when `prerunArgs()` returned `undefined`, which skips
10235
+ * staging anyway.
10236
+ *
10237
+ * An issue of severity `"error"` keeps the block from being staged; a `"warning"` does not. A
10238
+ * throw, or a result that is not a list of issues, counts as one error carrying the thrown or
10239
+ * parser message. {@link argsIssues} is not consulted for a block with its own prerun args.
10240
+ *
10241
+ * Must be set after `.prerunArgs()`, and is dropped when `.prerunArgs()` is set again.
10242
+ *
10243
+ * @example
10244
+ * .prerunArgs((data) => ({ dataset: data.dataset }))
10245
+ * .prerunArgsIssues(({ prerunArgs }) =>
10246
+ * prerunArgs.dataset === undefined ? [{ severity: 'error', message: 'Select a dataset' }] : [],
10247
+ * )
10248
+ */
10249
+ prerunArgsIssues(fn) {
10250
+ if (this.config.derivePrerunArgs === void 0) throw new Error("prerunArgsIssues() must be set after prerunArgs(): it checks the prerun args they derive.");
10251
+ return new BlockModelV3({
10252
+ ...this.config,
10253
+ prerunArgsIssues: fn
9768
10254
  });
9769
10255
  }
9770
10256
  /**
@@ -9897,7 +10383,7 @@ var BlockModelV3 = class BlockModelV3 {
9897
10383
  const pluginRegistry = {};
9898
10384
  const pluginHandles = Object.keys(plugins);
9899
10385
  for (const handle of pluginHandles) pluginRegistry[handle] = plugins[handle].model.name;
9900
- const { dataModel, deriveArgs, derivePrerunArgs, deriveTemplateParams, parseInitializationParams } = this.config;
10386
+ const { dataModel, deriveArgs, argsIssues, derivePrerunArgs, prerunArgsIssues, deriveTemplateParams, parseInitializationParams } = this.config;
9901
10387
  function getPlugin(handle) {
9902
10388
  const plugin = plugins[handle];
9903
10389
  if (!plugin) throw new Error(`Plugin model not found for '${handle}'`);
@@ -9920,8 +10406,18 @@ var BlockModelV3 = class BlockModelV3 {
9920
10406
  getPluginRegistry: () => pluginRegistry,
9921
10407
  createPluginData: (handle) => getPlugin(handle).model.getDefaultData()
9922
10408
  }),
9923
- [BlockStorageFacadeCallbacks.ArgsDerive]: (storageJson) => deriveArgsFromStorage(storageJson, deriveArgs),
9924
- [BlockStorageFacadeCallbacks.PrerunArgsDerive]: (storageJson) => derivePrerunArgsFromStorage(storageJson, deriveArgs, derivePrerunArgs),
10409
+ [BlockStorageFacadeCallbacks.ArgsDerive]: (storageJson) => deriveArgsFromStorage(storageJson, {
10410
+ deriveArgs,
10411
+ argsIssues
10412
+ }),
10413
+ [BlockStorageFacadeCallbacks.PrerunArgsDerive]: (storageJson) => derivePrerunArgsFromStorage(storageJson, {
10414
+ deriveArgs,
10415
+ argsIssues,
10416
+ ownPrerunArgs: derivePrerunArgs === void 0 ? void 0 : {
10417
+ derivePrerunArgs,
10418
+ prerunArgsIssues
10419
+ }
10420
+ }),
9925
10421
  [BlockStorageFacadeCallbacks.InitializationParamsDerive]: (storageJson) => deriveTemplateParamsFromStorage(storageJson, deriveTemplateParams),
9926
10422
  [BlockStorageFacadeCallbacks.StorageInitialFromParams]: (paramsJson, blockIdsJson) => createInitialStorageFromParams(paramsJson, blockIdsJson, {
9927
10423
  getBlockDataFromParams: (params) => dataModel.getDataFromParams(params),
@@ -9987,7 +10483,7 @@ var BlockModelV3 = class BlockModelV3 {
9987
10483
  }
9988
10484
  };
9989
10485
  //#endregion
9990
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/pframe_utils/axes.js
10486
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/pframe_utils/axes.js
9991
10487
  /** Create id for column copy with added keys in axes domains */
9992
10488
  const colId = (id, domains, contextDomains) => {
9993
10489
  let wid = id.toString();
@@ -10093,7 +10589,7 @@ function getAdditionalColumnsForColumn(blockAxes, column) {
10093
10589
  })];
10094
10590
  }
10095
10591
  //#endregion
10096
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/columns/column_recipes/leaf_rebrand.js
10592
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/columns/column_recipes/leaf_rebrand.js
10097
10593
  /**
10098
10594
  * Replace every column leaf whose id equals `fromId` with `toId`, leaving
10099
10595
  * other column refs (linkers, sub-anchors) intact. Wrapper recipes
@@ -10761,7 +11257,7 @@ var U = class d {
10761
11257
  }
10762
11258
  };
10763
11259
  //#endregion
10764
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/columns/column_providers/providers.js
11260
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/columns/column_providers/providers.js
10765
11261
  /**
10766
11262
  * Unified memoised factory dispatching to the right provider flavour by the
10767
11263
  * source shape:
@@ -10821,7 +11317,7 @@ var AccessorColumnsProviderImpl = class extends AccessorEntriesProvider {
10821
11317
  }
10822
11318
  getColumns() {
10823
11319
  if (this.cachedColumns !== void 0) return this.cachedColumns;
10824
- return this.cachedColumns = Array.from(this.entries.values()).map((e) => DataColumn.fromAccessor(e)).filter((v) => !isNil(v));
11320
+ return this.cachedColumns = Array.from(this.entries.values(), DataColumn.fromAccessor).filter((column) => column !== void 0);
10825
11321
  }
10826
11322
  };
10827
11323
  /**
@@ -10843,14 +11339,14 @@ var ResultPoolColumnsProviderImpl = class extends ResultPoolEntriesProvider {
10843
11339
  }
10844
11340
  getColumns() {
10845
11341
  if (this.cachedColumns !== void 0) return this.cachedColumns;
10846
- return this.cachedColumns = Array.from(this.getPObjectEntries().values()).map((e) => DataColumn.fromAccessor(e)).filter((v) => !isNil(v));
11342
+ return this.cachedColumns = Array.from(this.getPObjectEntries().values(), DataColumn.fromAccessor).filter((column) => column !== void 0);
10847
11343
  }
10848
11344
  };
10849
11345
  function hashRawPool(rawPool) {
10850
11346
  return rawPool.map((b) => `${b.blockId}\x1f${b.prodCtx ?? ""}\x1f${b.stagingCtx ?? ""}\x1f${b.prodIncomplete ? 1 : 0}\x1f${b.stagingIncomplete ? 1 : 0}`).join("");
10851
11347
  }
10852
11348
  //#endregion
10853
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/columns/column_providers/index.js
11349
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/columns/column_providers/index.js
10854
11350
  /**
10855
11351
  * Build the default set of ColumnsProviders for the ambient render ctx:
10856
11352
  * - `AccessorColumnsProvider` over `outputs` (if present)
@@ -10885,26 +11381,7 @@ function isColumnProvider(source) {
10885
11381
  return typeof p.getColumns === "function" && typeof p.isFinal === "function";
10886
11382
  }
10887
11383
  //#endregion
10888
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/columns/data_column.js
10889
- /**
10890
- * Thrown by leaf-recipe factories when the requested column is provably
10891
- * absent in the active render ctx — i.e. every relevant accessor reports
10892
- * `inputsLocked` and the column did not appear. Distinct from the
10893
- * `undefined` return that the factories still use for the "resolving" case
10894
- * (the column may still appear later).
10895
- *
10896
- * Catch at the boundary (resolver / filter+sort wiring / data-table assembly)
10897
- * when partial absence should be surfaced to the user rather than silently
10898
- * producing an empty result.
10899
- */
10900
- var ColumnAbsentError = class extends Error {
10901
- id;
10902
- constructor(id) {
10903
- super(`Column is absent in the active render ctx and will not appear: ${id}`);
10904
- this.id = id;
10905
- this.name = "ColumnAbsentError";
10906
- }
10907
- };
11384
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/columns/data_column.js
10908
11385
  /**
10909
11386
  * The leaf-recipe building block: a {@link DataColumnRecipe} whose `id` is a bare
10910
11387
  * {@link PObjectId} and whose readers are bound to a single tree-accessor
@@ -10957,29 +11434,15 @@ var DataColumnImpl = class DataColumnImpl {
10957
11434
  }
10958
11435
  /**
10959
11436
  * Resolve via the ambient {@link ColumnRegistry}. Spec is resolved eagerly:
10960
- * returns `undefined` if the leaf isn't reachable yet (resolving). Throws
10961
- * {@link ColumnAbsentError} when every relevant accessor is `inputsLocked`
10962
- * and the column did not appear — the column will not exist in this ctx.
11437
+ * returns `undefined` unless the id names a column whose spec is readable in
11438
+ * this ctx — while it is resolving, when it is absent, and when the id names
11439
+ * an export that is not a column (a `kind: "File"` object, for example).
10963
11440
  * Data and dataStatus stay lazy.
10964
11441
  */
10965
11442
  static fromId(id, { ctx } = {}) {
10966
- const registry = new ColumnRegistry(getCtxProviders({ ctx }));
10967
- const leaf = registry.resolve(id);
10968
- if (isNil(leaf)) {
10969
- if (registry.isFinal()) throw new ColumnAbsentError(id);
10970
- return;
10971
- }
10972
- const spec = readSpecAccessor(leaf);
10973
- if (isNil(spec)) {
10974
- if (leaf.accessor.getInputsLocked()) throw new ColumnAbsentError(id);
10975
- return;
10976
- }
10977
- if (!spec.hasData()) return void 0;
10978
- return new DataColumnImpl(id, {
10979
- getSpec: () => spec.getDataAsJson(),
10980
- getData: () => readDataAccessor(leaf),
10981
- getDataStatus: () => readDataStatus(leaf)
10982
- });
11443
+ const leaf = new ColumnRegistry(getCtxProviders({ ctx })).resolve(id);
11444
+ if (isNil(leaf)) return void 0;
11445
+ return DataColumnImpl.fromAccessor(leaf);
10983
11446
  }
10984
11447
  /** {@link PlRef} wrapper over {@link fromId}. */
10985
11448
  static fromPlRef(ref) {
@@ -10987,18 +11450,15 @@ var DataColumnImpl = class DataColumnImpl {
10987
11450
  }
10988
11451
  /**
10989
11452
  * Bind directly to an accessor-backed {@link LeafEntry} — no registry.
10990
- * Throws {@link ColumnAbsentError} if the leaf has no spec field and its
10991
- * accessor is `inputsLocked`. Returns `undefined` while still resolving.
11453
+ * Returns `undefined` unless the leaf's spec is readable and is a column
11454
+ * spec: while the spec is resolving, when it is absent, and for an export
11455
+ * that is not a column.
10992
11456
  */
10993
11457
  static fromAccessor(entry) {
10994
- const spec = readSpecAccessor(entry);
10995
- if (isNil(spec)) {
10996
- if (entry.accessor.getInputsLocked()) throw new ColumnAbsentError(entry.id);
10997
- return;
10998
- }
10999
- if (!spec.hasData()) return void 0;
11458
+ const spec = readSpec(entry);
11459
+ if (spec === void 0 || !isPColumnSpec(spec)) return void 0;
11000
11460
  return new DataColumnImpl(entry.id, {
11001
- getSpec: () => spec.getDataAsJson(),
11461
+ getSpec: () => spec,
11002
11462
  getData: () => readDataAccessor(entry),
11003
11463
  getDataStatus: () => readDataStatus(entry)
11004
11464
  });
@@ -11020,7 +11480,9 @@ var DataColumnImpl = class DataColumnImpl {
11020
11480
  * Distinguishes `present` / `resolving` / `absent` for a {@link PObjectId}
11021
11481
  * in the active render ctx. Falls back to the registry's `isFinal()`
11022
11482
  * when the id has no entry — only then we can say `absent` instead of
11023
- * `resolving`.
11483
+ * `resolving`. The status tells only whether the spec is readable: for an
11484
+ * export that is not a column it is `present`, and the factories return
11485
+ * `undefined`.
11024
11486
  */
11025
11487
  static getStatusById(id, { ctx } = {}) {
11026
11488
  const registry = new ColumnRegistry(getCtxProviders({ ctx }));
@@ -11089,6 +11551,12 @@ const readSpecAccessor = memoizeByEntry(({ accessor, name }) => accessor.travers
11089
11551
  assertFieldType: "Input",
11090
11552
  ignoreError: true
11091
11553
  }));
11554
+ /** The leaf's spec, or `undefined` until the spec resource has data. */
11555
+ const readSpec = memoizeByEntry((entry) => {
11556
+ const spec = readSpecAccessor(entry);
11557
+ if (isNil(spec) || !spec.hasData()) return void 0;
11558
+ return spec.getDataAsJson();
11559
+ });
11092
11560
  /**
11093
11561
  * Per-entry counterpart to {@link readDataStatus}: tells whether the leaf's
11094
11562
  * **spec** can be read in this ctx, and — for the negative cases —
@@ -11125,7 +11593,7 @@ function memoizeByEntry(fn) {
11125
11593
  };
11126
11594
  }
11127
11595
  //#endregion
11128
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/columns/column_recipes/column_overrided_recipe.js
11596
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/columns/column_recipes/column_overrided_recipe.js
11129
11597
  /**
11130
11598
  * Recipe wrapper that overlays {@link SpecOverrides} on top of any other
11131
11599
  * recipe. The id is `ColumnOverriddenKey { source: inner.id, specOverrides }`.
@@ -11263,7 +11731,7 @@ var DataColumnOverriddenRecipe = class extends ColumnOverriddenRecipe {
11263
11731
  }
11264
11732
  };
11265
11733
  //#endregion
11266
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/columns/column_recipes/column_filtered_recipe.js
11734
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/columns/column_recipes/column_filtered_recipe.js
11267
11735
  /**
11268
11736
  * Recipe wrapper that fixes the values of selected axes of an inner recipe
11269
11737
  * (axis slicing). The id is `FilteredPColumnId { source: inner.id,
@@ -11389,7 +11857,7 @@ function toAxisSelector(axis) {
11389
11857
  return selector;
11390
11858
  }
11391
11859
  //#endregion
11392
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/columns/column_recipes/index.js
11860
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/columns/column_recipes/index.js
11393
11861
  /**
11394
11862
  * Build a `ColumnRecipe` from a stringified id. Dispatches to the matching
11395
11863
  * concrete recipe variant based on the id's encoding and recurses on
@@ -11427,8 +11895,8 @@ function ColumnRecipeBuild(id, opts = {}) {
11427
11895
  * Resolution status counterpart to {@link ColumnRecipeBuild}. Dispatches on
11428
11896
  * the parsed id shape to the matching `getStatusBy*` static — never
11429
11897
  * constructs the recipe. Use this at boundaries (resolver, filter/sort
11430
- * targets) to throw early on `absent` instead of silently producing an empty
11431
- * recipe via {@link ColumnRecipeBuild}.
11898
+ * targets) to tell an `absent` column from a `resolving` one: the recipe
11899
+ * factory returns `undefined` for both.
11432
11900
  */
11433
11901
  function ColumnRecipeGetStatus(id, opts = {}) {
11434
11902
  const parsed = parseColumnIdSafely(id);
@@ -11440,7 +11908,7 @@ function ColumnRecipeGetStatus(id, opts = {}) {
11440
11908
  }
11441
11909
  const ColumnRecipe = Object.assign(ColumnRecipeBuild, { getStatus: ColumnRecipeGetStatus });
11442
11910
  //#endregion
11443
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/columns/column_recipes/column_discovered_recipe.js
11911
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/columns/column_recipes/column_discovered_recipe.js
11444
11912
  /**
11445
11913
  * Recipe for columns identified by a {@link ColumnDiscoveredKey}: a base
11446
11914
  * column + a `path` of linkers + qualifications.
@@ -11611,7 +12079,7 @@ function referencedUniIdsOf(key) {
11611
12079
  return /* @__PURE__ */ new Set([key.column, ...(key.path ?? []).map((item) => item.column)]);
11612
12080
  }
11613
12081
  //#endregion
11614
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/columns/column.js
12082
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/columns/column.js
11615
12083
  /**
11616
12084
  * Unified entry point — routes between the two top-level dispatchers:
11617
12085
  * - string id (`PObjectId` / `ColumnUniversalId`) → {@link ColumnRecipe}
@@ -11625,7 +12093,7 @@ function Column(source, opts) {
11625
12093
  return DataColumn(source, opts);
11626
12094
  }
11627
12095
  //#endregion
11628
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/columns/columns_collection.js
12096
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/columns/columns_collection.js
11629
12097
  /**
11630
12098
  * Build a {@link ColumnsCollection} from sandbox-side source descriptors.
11631
12099
  * Resolves the `columnsCollection` driver (either from `deps.driver` or via
@@ -11734,7 +12202,35 @@ function defaultCtxSources(ctx) {
11734
12202
  return [...currentBlockSources(ctx), { kind: "result_pool" }];
11735
12203
  }
11736
12204
  //#endregion
11737
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/columns/utils.js
12205
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/columns/peer_links.js
12206
+ /** Main axes of the datasets: the second axis of every column marked `pl7.app/isAnchor`. */
12207
+ function collectDatasetAxes(specs) {
12208
+ return new Set(specs.flatMap((spec) => {
12209
+ const main = spec.axesSpec[1];
12210
+ return readAnnotation(spec, Annotation.IsAnchor) === "true" && main !== void 0 ? [canonicalizeAxisId(main)] : [];
12211
+ }));
12212
+ }
12213
+ /** {@link collectDatasetAxes} over the result pool. */
12214
+ function getResultPoolDatasetAxes() {
12215
+ return collectDatasetAxes(ResultPoolColumnsProvider().getColumns().map((c) => c.getSpec()));
12216
+ }
12217
+ /**
12218
+ * Labels of the peer links on a linker path. A peer link joins two datasets' main axes, e.g. one
12219
+ * clonotype to another, so the columns behind it describe a different clonotype than the row.
12220
+ */
12221
+ function findPeerLinks(linkers, datasetAxes) {
12222
+ return uniq(linkers.flatMap((l) => {
12223
+ const peer = l.axesSpec.every((a) => datasetAxes.has(canonicalizeAxisId(a)));
12224
+ const label = readAnnotation(l, Annotation.LinkLabel) ?? readAnnotation(l, Annotation.Label);
12225
+ return peer && label ? [label] : [];
12226
+ }));
12227
+ }
12228
+ /** Hover text for the link icon. */
12229
+ function describePeerLinks(links) {
12230
+ return `Reached via ${links.join(", then ")}`;
12231
+ }
12232
+ //#endregion
12233
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/columns/utils.js
11738
12234
  /**
11739
12235
  * PObjectIds of every non-hit column referenced by `recipe.getQuery()`,
11740
12236
  * deduped in traversal order. The hit column is
@@ -11865,7 +12361,7 @@ function hasSingleDataColumn(recipe) {
11865
12361
  return recipe.getReferencedIds().length === 1;
11866
12362
  }
11867
12363
  //#endregion
11868
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/pframe_utils/columns.js
12364
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/pframe_utils/columns.js
11869
12365
  function getAllRelatedColumns(ctx, predicate) {
11870
12366
  const columns = new PColumnCollection();
11871
12367
  columns.addColumnProvider(ctx.resultPool);
@@ -11941,7 +12437,7 @@ function discoverIntegrableColumnIds(ctx, rootColumns) {
11941
12437
  }
11942
12438
  }
11943
12439
  //#endregion
11944
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/components/PFrameForGraphs.js
12440
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/components/PFrameForGraphs.js
11945
12441
  function isHiddenFromGraphColumn(column) {
11946
12442
  return !!readAnnotationJson(column, Annotation.HideDataFromGraphs);
11947
12443
  }
@@ -11970,7 +12466,169 @@ function createPFrameForGraphs(ctx, blockColumns) {
11970
12466
  }));
11971
12467
  }
11972
12468
  //#endregion
11973
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/filters/traverse.js
12469
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/components/PlDatasetSelector/filter_discovery.js
12470
+ /**
12471
+ * Matches columns annotated `pl7.app/isSubset: "true"` whose axes ⊆ anchor axes.
12472
+ *
12473
+ * Filter-intent: only the dataset's own direct subset columns, no linker
12474
+ * traversal — so this uses `.filter()` (pins `maxHops: 0`), not `.discover()`
12475
+ * (which would default to 4 hops and surface subset columns from other,
12476
+ * linker-reachable datasets). `.filter()` still applies the default
12477
+ * `enrichment` matching mode (`allowFloatingHitAxes: false`), which enforces
12478
+ * the axes-subset constraint. See `matchingModeToConstraints()`.
12479
+ */
12480
+ function findFilterColumns(options) {
12481
+ return ColumnsCollection([...options.sources], { driver: options.driver }).filter({
12482
+ anchors: { main: options.anchorSpec },
12483
+ include: { annotations: { [Annotation.IsSubset]: "true" } }
12484
+ }).getColumns();
12485
+ }
12486
+ /**
12487
+ * Derive labeled options from filter column variants, for use in DatasetOption.filters.
12488
+ *
12489
+ * Entries whose column id has no PlRef in `refsByObjectId` are silently
12490
+ * skipped — they cannot be exposed as user-selectable options.
12491
+ */
12492
+ function filterMatchesToOptions(variants, options) {
12493
+ if (variants.length === 0) return [];
12494
+ const { refsByObjectId, datasetSpec, labelOptions } = options;
12495
+ const entries = variants.flatMap((variant) => {
12496
+ const ref = refsByObjectId.get(extractPObjectId(variant.id));
12497
+ if (ref === void 0) return [];
12498
+ return [{
12499
+ ref,
12500
+ spec: variant.getSpec(),
12501
+ linkerPath: collectLinkerColumns(variant).map((linker) => ({ spec: linker.getSpec() }))
12502
+ }];
12503
+ });
12504
+ const labels = deriveDistinctLabels([...entries, { spec: datasetSpec }], {
12505
+ ...labelOptions,
12506
+ formatters: {
12507
+ ...labelOptions?.formatters,
12508
+ native: () => void 0
12509
+ }
12510
+ });
12511
+ return entries.map(({ ref }, i) => ({
12512
+ ref,
12513
+ label: labels[i]
12514
+ }));
12515
+ }
12516
+ /** Build the `refsByObjectId` map from `ctx.resultPool.getSpecs().entries`. */
12517
+ function buildRefMap(entries) {
12518
+ return new Map(entries.map((e) => [(0, import_canonicalize.default)(e.ref), e.ref]));
12519
+ }
12520
+ //#endregion
12521
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/components/PlDatasetSelector/enrichment_discovery.js
12522
+ /**
12523
+ * Linker-reached hits attached to the anchor primary. Drops zero-hop variants
12524
+ * (filters / the primary itself) and structural hits (subset, linker, label
12525
+ * columns). Narrow further with `include` selectors or a `predicate`.
12526
+ */
12527
+ function findEnrichmentColumns(options) {
12528
+ const include = options.include === void 0 ? void 0 : Array.isArray(options.include) ? options.include : [options.include];
12529
+ const columns = ColumnsCollection([...options.sources], { driver: options.driver }).discover({
12530
+ anchors: { main: options.anchorSpec },
12531
+ mode: "enrichment",
12532
+ maxHops: options.maxHops ?? 4,
12533
+ include,
12534
+ exclude: [
12535
+ { annotations: { [Annotation.IsSubset]: "true" } },
12536
+ { annotations: { [Annotation.IsLinkerColumn]: "true" } },
12537
+ { name: Annotation.Label }
12538
+ ]
12539
+ }).getColumns();
12540
+ const predicate = options.predicate;
12541
+ return columns.filter((v) => {
12542
+ if (hasSingleDataColumn(v)) return false;
12543
+ if (predicate !== void 0 && !predicate(v.getSpec())) return false;
12544
+ if (!isGlobalPObjectId(extractPObjectId(v.id))) return false;
12545
+ if (collectLinkerIds(v).some((id) => !isGlobalPObjectId(id))) return false;
12546
+ return true;
12547
+ });
12548
+ }
12549
+ /**
12550
+ * Pair each variant with a path-disambiguated label (so export headers stay
12551
+ * unique) and carry hit/linker `PObjectId`s through verbatim. Propagates
12552
+ * `qualifications.forHit`; `forQueries` is re-derived by the table builder.
12553
+ */
12554
+ function enrichmentVariantsToRefs(variants, labelOptions) {
12555
+ if (variants.length === 0) return [];
12556
+ const linkerIdsByVariant = variants.map((v) => collectLinkerIds(v));
12557
+ const hitQualsByVariant = variants.map((v) => [...hitQualifications(v)]);
12558
+ const labels = deriveDistinctLabels(variants.map((variant, i) => ({
12559
+ spec: variant.getSpec(),
12560
+ linkerPath: collectLinkerColumns(variant).map((linker) => ({ spec: linker.getSpec() })),
12561
+ qualifications: {
12562
+ forHit: hitQualsByVariant[i],
12563
+ forQueries: queriesQualifications(variant)
12564
+ }
12565
+ })), labelOptions);
12566
+ return variants.map((variant, i) => {
12567
+ const path = linkerIdsByVariant[i].length === 0 ? void 0 : linkerIdsByVariant[i].map((id) => ({
12568
+ type: "linker",
12569
+ linker: id
12570
+ }));
12571
+ return {
12572
+ ref: createEnrichmentRef(extractPObjectId(variant.id), {
12573
+ path,
12574
+ qualifications: hitQualsByVariant[i]
12575
+ }),
12576
+ label: labels[i]
12577
+ };
12578
+ });
12579
+ }
12580
+ //#endregion
12581
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/components/PlDatasetSelector/build_dataset_options.js
12582
+ function toPredicate(opt) {
12583
+ if (opt === void 0) return () => true;
12584
+ return typeof opt === "function" ? opt : multiColumnSelectorsToPredicate(opt);
12585
+ }
12586
+ /**
12587
+ * Usage:
12588
+ * ```ts
12589
+ * .output("datasetOptions", (ctx) => buildDatasetOptions(ctx))
12590
+ * ```
12591
+ */
12592
+ function buildDatasetOptions(ctx, opts) {
12593
+ const primaryPredicate = toPredicate(opts?.primary);
12594
+ const filterPredicate = toPredicate(opts?.filter);
12595
+ const options = ctx.resultPool.getOptions((spec) => isPColumnSpec(spec) && primaryPredicate(spec), { refsWithEnrichments: opts?.requireEnrichments ?? true });
12596
+ if (options.length === 0) return [];
12597
+ const refMap = buildRefMap(ctx.resultPool.getSpecs().entries);
12598
+ const withEnrichments = opts?.withEnrichments ?? false;
12599
+ const filterSource = ColumnsProvider(ctx.ctx.getUpstreamBlockCtx());
12600
+ const enrichmentSources = withEnrichments ? getCtxProviders(ctx) : void 0;
12601
+ return options.map((primary) => {
12602
+ const datasetSpec = ctx.resultPool.getPColumnSpecByRef(primary.ref);
12603
+ if (!datasetSpec) return { primary };
12604
+ const filterMatches = findFilterColumns({
12605
+ sources: [filterSource],
12606
+ anchorSpec: datasetSpec
12607
+ }).filter((m) => filterPredicate(m.getSpec()));
12608
+ const filters = filterMatches.length === 0 ? void 0 : filterMatchesToOptions(filterMatches, {
12609
+ refsByObjectId: refMap,
12610
+ datasetSpec,
12611
+ labelOptions: opts?.labelOptions
12612
+ });
12613
+ let enrichments;
12614
+ if (enrichmentSources && withEnrichments) {
12615
+ const enrichmentVariants = findEnrichmentColumns({
12616
+ sources: enrichmentSources,
12617
+ anchorSpec: datasetSpec,
12618
+ maxHops: opts?.enrichmentMaxHops,
12619
+ ...typeof withEnrichments === "function" ? { predicate: withEnrichments } : { include: withEnrichments }
12620
+ });
12621
+ if (enrichmentVariants.length > 0) enrichments = enrichmentVariantsToRefs(enrichmentVariants, opts?.labelOptions);
12622
+ }
12623
+ return {
12624
+ primary,
12625
+ ...filters !== void 0 && filters.length > 0 ? { filters } : {},
12626
+ ...enrichments !== void 0 && enrichments.length > 0 ? { enrichments } : {}
12627
+ };
12628
+ });
12629
+ }
12630
+ //#endregion
12631
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/filters/traverse.js
11974
12632
  /**
11975
12633
  * Recursively traverses a FilterSpec tree bottom-up, applying visitor callbacks.
11976
12634
  *
@@ -12009,7 +12667,7 @@ function collectFilterSpecColumns(filter) {
12009
12667
  });
12010
12668
  }
12011
12669
  //#endregion
12012
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/filters/converters/filterToQuery.js
12670
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/filters/converters/filterToQuery.js
12013
12671
  /** Converts a QueryColumnId object into a SpecQueryExpression reference. */
12014
12672
  function resolveColumnRef(col) {
12015
12673
  return col.type === "axis" ? {
@@ -12396,7 +13054,7 @@ function isEmpty(value) {
12396
13054
  return true;
12397
13055
  }
12398
13056
  //#endregion
12399
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/filters/distill.js
13057
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/filters/distill.js
12400
13058
  /**
12401
13059
  * Strips non-FilterSpec metadata (whitelist approach) and removes
12402
13060
  * unfilled leaves (type is undefined or any required field is undefined).
@@ -12501,7 +13159,7 @@ const REQUIRED_KEYS_BY_TYPE = {
12501
13159
  greaterThanColumnOrEqual: ["column", "rhs"]
12502
13160
  };
12503
13161
  //#endregion
12504
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/components/PlDataTable/state-migration.js
13162
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/components/PlDataTable/state-migration.js
12505
13163
  /** Upgrade PlDataTableStateV2 to the latest version */
12506
13164
  function upgradePlDataTableStateV2(state) {
12507
13165
  if (!state) return createPlDataTableStateV2();
@@ -12859,7 +13517,7 @@ function parseLegacyLeafColumn(s) {
12859
13517
  return parsed;
12860
13518
  }
12861
13519
  //#endregion
12862
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/components/PlDataTable/createPlDataTable/utils.js
13520
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/components/PlDataTable/createPlDataTable/utils.js
12863
13521
  /** Adapt a {@link ColumnRecipe} to the plain {@link RuleColumn} shape. */
12864
13522
  function toRuleColumn(col) {
12865
13523
  return {
@@ -12953,11 +13611,12 @@ function deriveAllLabels(options) {
12953
13611
  return deriveDistinctLabels(columns.map((c) => ({
12954
13612
  spec: c.spec,
12955
13613
  linkerPath: c.linkerPath?.map((step) => ({ spec: step.linker.spec })),
12956
- qualifications: c.qualifications
13614
+ qualifications: c.qualifications,
13615
+ marker: c.marker
12957
13616
  })), deriveLabelsOptions).reduce((acc, label, index) => (acc[columns[index].id] = label, acc), {});
12958
13617
  }
12959
13618
  //#endregion
12960
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.js
13619
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.js
12961
13620
  /**
12962
13621
  * Assemble a ptable def directly from recipes. Each secondary recipe is its own
12963
13622
  * outer-joined subtree: `getQuery()` encodes its linker chain, and its
@@ -13021,7 +13680,7 @@ function columnToJoinEntry(col, qualifications) {
13021
13680
  };
13022
13681
  }
13023
13682
  //#endregion
13024
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/components/PlDataTable/columnResolver.js
13683
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/components/PlDataTable/columnResolver.js
13025
13684
  function createColumnResolver(columns) {
13026
13685
  const axisIds = [];
13027
13686
  for (const c of columns) for (const ax of c.getSpec().axesSpec) axisIds.push(getAxisId(ax));
@@ -13043,7 +13702,7 @@ function createColumnResolver(columns) {
13043
13702
  };
13044
13703
  }
13045
13704
  //#endregion
13046
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/components/PlDataTable/createPlDataTable/discoverColumns.js
13705
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/components/PlDataTable/createPlDataTable/discoverColumns.js
13047
13706
  /**
13048
13707
  * Discover columns from sources/anchors and split them into primary
13049
13708
  * (direct anchor hits — zero-hop, query is a bare column) and secondary
@@ -13089,7 +13748,7 @@ function discoverLabelColumns(ctx, primary) {
13089
13748
  }).getColumns();
13090
13749
  }
13091
13750
  //#endregion
13092
- //#region ../node_modules/.pnpm/@platforma-sdk+model@1.84.10/node_modules/@platforma-sdk/model/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js
13751
+ //#region ../node_modules/.pnpm/@platforma-sdk+model@1.88.0/node_modules/@platforma-sdk/model/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js
13093
13752
  function createPlDataTableV3(ctx, options) {
13094
13753
  const state = upgradePlDataTableStateV2(options.tableState);
13095
13754
  const primaryJoinType = options.primaryJoinType ?? "full";
@@ -13098,8 +13757,13 @@ function createPlDataTableV3(ctx, options) {
13098
13757
  const { primary, secondary } = resolved;
13099
13758
  if (primary.length === 0) return void 0;
13100
13759
  const { direct, linked } = splitByTopology(secondary);
13760
+ const labelable = [...primary, ...secondary].map(toLabelableColumn);
13761
+ const linkHints = collectLinkHints(labelable);
13101
13762
  const derivedLabels = deriveAllLabels({
13102
- columns: [...primary, ...secondary].filter((c) => !isColumnHidden(c.getSpec())).map(toLabelableColumn),
13763
+ columns: labelable.filter((c) => !isColumnHidden(c.spec)).map((c) => ({
13764
+ ...c,
13765
+ marker: linkHints.get(c.id)
13766
+ })),
13103
13767
  deriveLabelsOptions: {
13104
13768
  includeNativeLabel: true,
13105
13769
  ...options.labelsOptions
@@ -13172,6 +13836,7 @@ function createPlDataTableV3(ctx, options) {
13172
13836
  ],
13173
13837
  primaryColumns: primary,
13174
13838
  derivedLabels,
13839
+ linkHints,
13175
13840
  visibilityByColId,
13176
13841
  orderByColId
13177
13842
  });
@@ -13213,6 +13878,15 @@ function splitByTopology(columns) {
13213
13878
  function collectLinkerSnapshots(linked) {
13214
13879
  return uniqueBy(linked.flatMap((c) => collectLinkerColumns(c)), (c) => c.id);
13215
13880
  }
13881
+ /** Link icon hover text per column, for columns reached through at least one peer link. */
13882
+ function collectLinkHints(columns) {
13883
+ if (!columns.some((c) => c.linkerPath?.length)) return /* @__PURE__ */ new Map();
13884
+ const datasetAxes = getResultPoolDatasetAxes();
13885
+ return new Map(columns.flatMap((c) => {
13886
+ const links = findPeerLinks(c.linkerPath?.map((step) => step.linker.spec) ?? [], datasetAxes);
13887
+ return links.length > 0 ? [[c.id, describePeerLinks(links)]] : [];
13888
+ }));
13889
+ }
13216
13890
  function toLabelableColumn(col) {
13217
13891
  return {
13218
13892
  id: col.id,
@@ -13233,7 +13907,7 @@ function toLabelableColumn(col) {
13233
13907
  * The UI overlays this onto the engine-emitted specs at render time.
13234
13908
  */
13235
13909
  function buildColumnsMeta(params) {
13236
- const { fullColumns, visibleColumns, primaryColumns, derivedLabels } = params;
13910
+ const { fullColumns, visibleColumns, primaryColumns, derivedLabels, linkHints } = params;
13237
13911
  const { visibilityByColId, orderByColId } = params;
13238
13912
  const visibleStatus = buildDataStatusMap(visibleColumns);
13239
13913
  const columns = fullColumns.reduce((acc, c) => {
@@ -13242,7 +13916,8 @@ function buildColumnsMeta(params) {
13242
13916
  label: derivedLabels[c.id],
13243
13917
  visibility: getEffectiveVisibility(rc, visibilityByColId),
13244
13918
  order: getOrderPriority(rc, orderByColId),
13245
- status: visibleStatus[c.id]
13919
+ status: visibleStatus[c.id],
13920
+ linkHint: linkHints.get(c.id)
13246
13921
  };
13247
13922
  return acc;
13248
13923
  }, {});
@@ -13514,19 +14189,15 @@ function poolSpecByRef(ctx, ref) {
13514
14189
  }
13515
14190
  //#endregion
13516
14191
  //#region ../model/dist/specs.js
13517
- /**
13518
- * Column and axis names this block shares with its workflow and with the profiler upstream.
13519
- *
13520
- * Every string here is a contract: change one and the matching literal in
13521
- * `workflow/src/main.tpl.tengo` — or in the profiler that emits it — has to change with it, or
13522
- * the column simply stops being found and an output goes quietly empty. They live in one file so
13523
- * that obligation is visible rather than buried among the render helpers.
13524
- */
13525
- const STATE_MATRIX = "pl7.app/repertoire/stateMatrix";
13526
14192
  const LANDSCAPE_SCORE_REF = "pl7.app/repertoire/landscapeScoreRef";
13527
14193
  const MUTATION_ID_AXIS = "pl7.app/repertoire/mutationId";
13528
14194
  const MUTATION_VARIANT_LINK = "pl7.app/repertoire/mutationVariantLink";
13529
14195
  const BROWSABLE_MUTATION = "pl7.app/repertoire/browsableMutation";
14196
+ const LANDSCAPE_ERROR = "pl7.app/repertoire/singleMutantError";
14197
+ const LANDSCAPE_NT_VARIANTS = "pl7.app/repertoire/singleMutantNtVariants";
14198
+ const LANDSCAPE_SCORE_INDEX_DOMAIN = "pl7.app/repertoire/landscapeScore";
14199
+ const LANDSCAPE_ERROR_COLUMN = "pl7.app/repertoire/landscapeScoreErrorColumn";
14200
+ const LANDSCAPE_NT_VARIANTS_COLUMN = "pl7.app/repertoire/landscapeScoreNtVariantsColumn";
13530
14201
  //#endregion
13531
14202
  //#region ../model/dist/drill-down-table.js
13532
14203
  /**
@@ -13581,13 +14252,18 @@ function drillDownTableModel(ctx) {
13581
14252
  visibility: "default"
13582
14253
  }
13583
14254
  ];
13584
- if (activeScoreSpec !== void 0) visibility.push({
13585
- match: {
13586
- name: exactMatch(activeScoreSpec.name),
13587
- domain: Object.fromEntries(Object.entries(activeScoreSpec.domain ?? {}).map(([k, v]) => [k, exactMatch(v)]))
13588
- },
13589
- visibility: "default"
13590
- });
14255
+ if (activeScoreSpec !== void 0) {
14256
+ const scoreDomain = Object.fromEntries(Object.entries(activeScoreSpec.domain ?? {}).map(([k, v]) => [k, exactMatch(v)]));
14257
+ const landscapeCol = outputPColumns(ctx, "singleMutantHeatmapPf")?.find((c) => c.spec.name === "pl7.app/repertoire/singleMutantValue" && c.spec.annotations?.["pl7.app/repertoire/landscapeScoreRef"] === scoreKey);
14258
+ const companionNames = [LANDSCAPE_ERROR_COLUMN, LANDSCAPE_NT_VARIANTS_COLUMN].map((key) => landscapeCol?.spec.annotations?.[key]).filter((name) => name !== void 0);
14259
+ for (const name of [activeScoreSpec.name, ...companionNames]) visibility.push({
14260
+ match: {
14261
+ name: exactMatch(name),
14262
+ domain: scoreDomain
14263
+ },
14264
+ visibility: "default"
14265
+ });
14266
+ }
13591
14267
  visibility.push({
13592
14268
  match: { name: ".*" },
13593
14269
  visibility: "optional"
@@ -13751,19 +14427,27 @@ function drillDownLabel(mutationId, scoreLabel) {
13751
14427
  * Shared by the `landscapePanels` output and by `.sections()`, which needs the same list to name
13752
14428
  * one page per score — two derivations of "which charts exist" would drift the moment one of
13753
14429
  * them changed.
14430
+ *
14431
+ * `labelByRef` maps a score's ref to its picker label. The workflow stamps each column with the
14432
+ * score's own native label, which two sibling blocks share. The picker already told them apart, so
14433
+ * its label is used when given, and the stamped one is the fallback.
13754
14434
  */
13755
- function landscapePanelsFrom(pCols) {
14435
+ function landscapePanelsFrom(pCols, labelByRef) {
13756
14436
  if (pCols === void 0) return void 0;
13757
14437
  const panels = [];
13758
14438
  for (const col of pCols) {
13759
14439
  if (col.spec.name !== "pl7.app/repertoire/singleMutantValue") continue;
13760
14440
  const key = col.spec.annotations?.[LANDSCAPE_SCORE_REF];
13761
14441
  if (key === void 0) continue;
14442
+ const scoreIndex = col.spec.domain?.[LANDSCAPE_SCORE_INDEX_DOMAIN];
14443
+ const companion = (name) => pCols.find((c) => c.spec.name === name && c.spec.domain?.["pl7.app/repertoire/landscapeScore"] === scoreIndex)?.spec;
13762
14444
  panels.push({
13763
14445
  key,
13764
- label: col.spec.annotations?.["pl7.app/label"] ?? "Score",
14446
+ label: labelByRef?.get(key) ?? col.spec.annotations?.["pl7.app/label"] ?? "Score",
13765
14447
  index: Number(col.spec.annotations?.["pl7.app/repertoire/landscapeScoreIndex"] ?? "0"),
13766
- spec: col.spec
14448
+ spec: col.spec,
14449
+ errorSpec: companion(LANDSCAPE_ERROR),
14450
+ ntVariantsSpec: companion(LANDSCAPE_NT_VARIANTS)
13767
14451
  });
13768
14452
  }
13769
14453
  panels.sort((a, b) => a.index - b.index);
@@ -13798,6 +14482,30 @@ function isLandscapeScoreColumn(spec, variantAxisName) {
13798
14482
  if (variantAxisName === void 0) return false;
13799
14483
  return spec.axesSpec.length === 1 && spec.axesSpec[0].name === variantAxisName;
13800
14484
  }
14485
+ /** The subset a column was computed on (Clonotype Enrichment's stamp), if any. */
14486
+ function subsetStampOf(spec) {
14487
+ return spec.domain?.["pl7.app/inputSubset"];
14488
+ }
14489
+ /**
14490
+ * Picker entries for score and round columns. Only columns that fit the run: full-data ones, or
14491
+ * ones computed on the run's own subset. Labels come from the trace, so a subset's copy is named.
14492
+ */
14493
+ function runColumnOptions(entries, inputFilter) {
14494
+ const fitting = entries.filter((e) => {
14495
+ const stamp = subsetStampOf(e.spec);
14496
+ return stamp === void 0 || stamp === inputFilter;
14497
+ });
14498
+ const labels = deriveDistinctLabels(fitting.map((e) => e.spec), { includeNativeLabel: true });
14499
+ return fitting.map((e, i) => ({
14500
+ label: labels[i] ?? e.id,
14501
+ value: e.id,
14502
+ ...subsetStampOf(e.spec) !== void 0 && { subset: subsetStampOf(e.spec) }
14503
+ }));
14504
+ }
14505
+ /** The run's subset as the workflow and the stamp write it. */
14506
+ function inputFilterOf(data) {
14507
+ return data.filterRef && createGlobalPObjectId(data.filterRef.blockId, data.filterRef.name);
14508
+ }
13801
14509
  /** The section href of one landscape page. */
13802
14510
  function landscapeHref(scoreKey) {
13803
14511
  return `/?score=${encodeURIComponent(scoreKey)}`;
@@ -13806,17 +14514,57 @@ function landscapeHref(scoreKey) {
13806
14514
  function landscapeLabel(scoreLabel) {
13807
14515
  return `Landscape · ${scoreLabel}`;
13808
14516
  }
13809
- /** score id -> display label, from the columns the last run produced. */
13810
- function scoreLabelsByKey(pCols) {
14517
+ /**
14518
+ * score id -> display label, from the columns the last run produced.
14519
+ *
14520
+ * `labelByRef` carries the picker labels, which tell sibling blocks' same-named scores apart. It is
14521
+ * preferred per score; the column's stamped label is the fallback. See {@link landscapePanelsFrom}.
14522
+ */
14523
+ function scoreLabelsByKey(pCols, labelByRef) {
13811
14524
  const labels = {};
13812
14525
  for (const col of pCols ?? []) {
13813
14526
  if (col.spec.name !== "pl7.app/repertoire/singleMutantValue") continue;
13814
14527
  const key = col.spec.annotations?.[LANDSCAPE_SCORE_REF];
13815
- const label = col.spec.annotations?.["pl7.app/label"];
14528
+ const label = labelByRef?.get(key ?? "") ?? col.spec.annotations?.["pl7.app/label"];
13816
14529
  if (key !== void 0 && label !== void 0) labels[key] = label;
13817
14530
  }
13818
14531
  return labels;
13819
14532
  }
14533
+ /**
14534
+ * Score picker options from the result pool.
14535
+ *
14536
+ * Shared by the `scoreOptions` output and by section / panel naming, so a score's page is named by
14537
+ * the same label the user picked it under, and the two cannot drift.
14538
+ *
14539
+ * Discovered via discover() (not getCanonicalOptions — see roundFrequencyOptions for why) anchored
14540
+ * on the state matrix. The anchor name `main` must match the workflow's `bb.addAnchor(...)`.
14541
+ *
14542
+ * Numeric only — a cell holds one variant's score. NOT restricted to `pl7.app/isScore`: no shipped
14543
+ * block emits a variant-keyed column carrying that annotation yet (repertoire-score is
14544
+ * clonotype-keyed), so filtering on it would leave the dropdown empty. Revisit once the bin-score
14545
+ * block lands.
14546
+ */
14547
+ function computeScoreOptions(ctx) {
14548
+ const stateMatrixRef = ctx.data.stateMatrixRef;
14549
+ if (stateMatrixRef === void 0) return void 0;
14550
+ const stateSpec = poolSpecByRef(ctx, stateMatrixRef);
14551
+ if (!stateSpec) return void 0;
14552
+ const columns = ColumnsCollection(["result_pool"]).discover({
14553
+ anchors: { main: stateSpec },
14554
+ mode: "enrichment",
14555
+ maxHops: 0,
14556
+ exclude: [{ annotations: { "pl7.app/isLinkerColumn": exactMatch("true") } }]
14557
+ }).getColumns();
14558
+ const variantAxisName = stateSpec.axesSpec[0]?.name;
14559
+ return runColumnOptions(dedupByLeafId(columns.filter((recipe) => isLandscapeScoreColumn(recipe.getSpec(), variantAxisName))).map((recipe) => ({
14560
+ id: recipe.id,
14561
+ spec: recipe.getSpec()
14562
+ })), inputFilterOf(ctx.data));
14563
+ }
14564
+ /** ref -> picker label for the discovered scores. See {@link computeScoreOptions}. */
14565
+ function scoreLabelByRefFrom(ctx) {
14566
+ return new Map((computeScoreOptions(ctx) ?? []).map((o) => [o.value, o.label]));
14567
+ }
13820
14568
  function drillDownHref(mutationId) {
13821
14569
  return `/drilldown?m=${encodeURIComponent(mutationId)}`;
13822
14570
  }
@@ -13841,6 +14589,11 @@ const dataModel = new DataModelBuilder({ kind }).from("v1").migrate("v2", (v1) =
13841
14589
  ...v5,
13842
14590
  ...mapChartStates(v5, withYAnnotationTitleAtBottom),
13843
14591
  drillDownChartState: withYAnnotationTitleAtBottom(v5.drillDownChartState)
14592
+ })).migrate("v7", (v6) => ({
14593
+ ...v6,
14594
+ singleMutantHeatmapState: withErrorOpacityRange(v6.singleMutantHeatmapState),
14595
+ singleMutantHeatmapStates: Object.fromEntries(Object.entries(v6.singleMutantHeatmapStates).map(([key, state]) => [key, withErrorOpacityRange(state)])),
14596
+ drillDownChartState: withErrorOpacityRange(v6.drillDownChartState)
13844
14597
  })).init(() => ({
13845
14598
  drillDowns: [],
13846
14599
  drillDownChartState: makeDrillDownChartState(),
@@ -13880,67 +14633,62 @@ const platforma$1 = BlockModelV3.create({
13880
14633
  if (data.selectedParentId === void 0) throw new Error("Select a parent");
13881
14634
  return {
13882
14635
  stateMatrixRef: data.stateMatrixRef,
14636
+ ...data.filterRef !== void 0 && { inputFilter: createGlobalPObjectId(data.filterRef.blockId, data.filterRef.name) },
13883
14637
  selectedParentId: data.selectedParentId,
13884
14638
  roundFrequencyRefs: data.roundFrequencyRefs ?? [],
13885
14639
  compositionEpsilon: data.compositionEpsilon ?? 1e-6,
13886
14640
  scoreRefs: data.scoreRefs ?? []
13887
14641
  };
13888
14642
  }).templateParams(() => ({})).output("stateMatrixOptions", (ctx) => {
13889
- const options = ctx.resultPool.getOptions([{ name: STATE_MATRIX }], { label: { includeNativeLabel: false } });
14643
+ const options = buildDatasetOptions(ctx, {
14644
+ primary: (spec) => isPColumnSpec(spec) && spec.name === "pl7.app/repertoire/stateMatrix",
14645
+ filter: (spec) => isPColumnSpec(spec) && spec.axesSpec.length === 1 && spec.axesSpec[0].name === "pl7.app/variantKey",
14646
+ requireEnrichments: false
14647
+ }) ?? [];
13890
14648
  const labelCounts = /* @__PURE__ */ new Map();
13891
- for (const o of options) labelCounts.set(o.label, (labelCounts.get(o.label) ?? 0) + 1);
14649
+ for (const { primary } of options) labelCounts.set(primary.label, (labelCounts.get(primary.label) ?? 0) + 1);
13892
14650
  return options.map((o) => {
13893
- if ((labelCounts.get(o.label) ?? 0) <= 1) return o;
13894
- const alphabet = ctx.resultPool.getPColumnSpecByRef(o.ref)?.axesSpec.find((a) => a.domain?.["pl7.app/alphabet"])?.domain?.["pl7.app/alphabet"];
14651
+ if ((labelCounts.get(o.primary.label) ?? 0) <= 1) return o;
14652
+ const alphabet = ctx.resultPool.getPColumnSpecByRef(o.primary.ref)?.axesSpec.find((a) => a.domain?.["pl7.app/alphabet"])?.domain?.["pl7.app/alphabet"];
13895
14653
  const suffix = alphabet === "aminoacid" ? " (aa)" : alphabet === "nucleotide" ? " (nt)" : "";
13896
14654
  return {
13897
14655
  ...o,
13898
- label: o.label + suffix
14656
+ primary: {
14657
+ ...o.primary,
14658
+ label: o.primary.label + suffix
14659
+ }
13899
14660
  };
13900
14661
  });
13901
- }).output("scoreOptions", (ctx) => {
14662
+ }).output("scoreOptions", (ctx) => computeScoreOptions(ctx)).output("roundFrequencyOptions", (ctx) => {
13902
14663
  const stateMatrixRef = ctx.data.stateMatrixRef;
13903
14664
  if (stateMatrixRef === void 0) return void 0;
13904
14665
  const stateSpec = poolSpecByRef(ctx, stateMatrixRef);
13905
14666
  if (!stateSpec) return void 0;
13906
- const anchors = { main: stateSpec };
13907
- const columns = ColumnsCollection(["result_pool"]).discover({
13908
- anchors,
13909
- mode: "enrichment",
13910
- maxHops: 0,
13911
- exclude: [{ annotations: { "pl7.app/isLinkerColumn": exactMatch("true") } }]
13912
- }).getColumns();
13913
- const variantAxisName = stateSpec.axesSpec[0]?.name;
13914
- const seen = /* @__PURE__ */ new Set();
13915
- const options = [];
13916
- for (const recipe of columns) {
13917
- const spec = recipe.getSpec();
13918
- if (!isLandscapeScoreColumn(spec, variantAxisName)) continue;
13919
- const dedupKey = spec.name + "|" + JSON.stringify(spec.domain ?? {});
13920
- if (seen.has(dedupKey)) continue;
13921
- seen.add(dedupKey);
13922
- options.push({
13923
- label: spec.annotations?.["pl7.app/label"] ?? recipe.id,
13924
- value: recipe.id
13925
- });
13926
- }
13927
- return options;
13928
- }).output("roundFrequencyOptions", (ctx) => {
13929
- const stateMatrixRef = ctx.data.stateMatrixRef;
13930
- if (stateMatrixRef === void 0) return void 0;
13931
- const stateSpec = poolSpecByRef(ctx, stateMatrixRef);
13932
- if (!stateSpec) return void 0;
13933
- return dedupByLeafId(ColumnsCollection(["result_pool"]).discover({
14667
+ return runColumnOptions(dedupByLeafId(ColumnsCollection(["result_pool"]).discover({
13934
14668
  include: { name: exactMatch("pl7.app/frequency") },
13935
14669
  anchors: { main: stateSpec },
13936
14670
  mode: "enrichment",
13937
14671
  maxHops: 0
13938
- }).getColumns()).map((recipe) => {
13939
- return {
13940
- label: recipe.getSpec().annotations?.["pl7.app/label"] ?? recipe.id,
13941
- value: recipe.id
13942
- };
13943
- });
14672
+ }).getColumns()).map((recipe) => ({
14673
+ id: recipe.id,
14674
+ spec: recipe.getSpec()
14675
+ })), inputFilterOf(ctx.data));
14676
+ }).output("subsetParentsFrame", (ctx) => {
14677
+ const { stateMatrixRef, filterRef } = ctx.data;
14678
+ if (stateMatrixRef === void 0 || filterRef === void 0) return void 0;
14679
+ const variantAxis = poolSpecByRef(ctx, stateMatrixRef)?.axesSpec[0];
14680
+ const filter = ctx.resultPool.getPColumnByRef(filterRef);
14681
+ if (variantAxis === void 0 || filter === void 0) return void 0;
14682
+ const linker = ctx.resultPool.selectColumns((spec) => spec.name === "pl7.app/repertoire/parentLink" && spec.axesSpec.some((a) => a.name === variantAxis.name && Object.entries(variantAxis.domain ?? {}).every(([k, v]) => a.domain?.[k] === v))).find((c) => c.data !== void 0);
14683
+ if (linker?.data === void 0) return void 0;
14684
+ return {
14685
+ frame: ctx.createPFrame([{
14686
+ ...linker,
14687
+ data: linker.data
14688
+ }, filter]),
14689
+ linkerId: linker.id,
14690
+ filterId: filter.id
14691
+ };
13944
14692
  }).output("stateMatrixPf", (ctx) => {
13945
14693
  const { stateMatrixRef } = ctx.data;
13946
14694
  if (stateMatrixRef === void 0) return void 0;
@@ -13957,7 +14705,7 @@ const platforma$1 = BlockModelV3.create({
13957
14705
  return createPFrameForGraphs(ctx, pCols);
13958
14706
  }).output("singleMutantHeatmapPCols", (ctx) => {
13959
14707
  return outputPColumns(ctx, "singleMutantHeatmapPf");
13960
- }).output("landscapePanels", (ctx) => landscapePanelsFrom(outputPColumns(ctx, "singleMutantHeatmapPf"))).output("singleMutantCount", (ctx) => {
14708
+ }).output("landscapePanels", (ctx) => landscapePanelsFrom(outputPColumns(ctx, "singleMutantHeatmapPf"), scoreLabelByRefFrom(ctx))).output("singleMutantCount", (ctx) => {
13961
14709
  return ctx.outputs?.resolve({
13962
14710
  field: "singleMutantCount",
13963
14711
  assertFieldType: "Input",
@@ -13983,7 +14731,8 @@ const platforma$1 = BlockModelV3.create({
13983
14731
  return outputPColumns(ctx, "compositionHeatmapPf");
13984
14732
  }).output("isRunning", (ctx) => ctx.outputs?.getIsReadyOrError() === false).title(() => "Mutation Explorer").subtitle((ctx) => ctx.data.customBlockLabel || ctx.data.defaultBlockLabel || NO_DATASET_LABEL).sections((ctx) => {
13985
14733
  const pCols = outputPColumns(ctx, "singleMutantHeatmapPf");
13986
- const panels = landscapePanelsFrom(pCols) ?? [];
14734
+ const labelByRef = scoreLabelByRefFrom(ctx);
14735
+ const panels = landscapePanelsFrom(pCols, labelByRef) ?? [];
13987
14736
  const sections = [];
13988
14737
  if (panels.length === 0) sections.push({
13989
14738
  type: "link",
@@ -13997,7 +14746,7 @@ const platforma$1 = BlockModelV3.create({
13997
14746
  });
13998
14747
  const drillDowns = ctx.data.drillDowns ?? [];
13999
14748
  if (drillDowns.length > 0) {
14000
- const scoreLabels = scoreLabelsByKey(pCols);
14749
+ const scoreLabels = scoreLabelsByKey(pCols, labelByRef);
14001
14750
  sections.push({ type: "delimiter" });
14002
14751
  for (const d of drillDowns) sections.push({
14003
14752
  type: "link",