@platforma-open/milaboratories.repertoire-diversity-2.model 1.5.6 → 1.5.8

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/bundle.js CHANGED
@@ -4,6 +4,113 @@
4
4
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["block-model"] = {}));
5
5
  })(this, (function (exports) { 'use strict';
6
6
 
7
+ /**
8
+ * BlockStorage - Typed storage abstraction for block persistent data.
9
+ *
10
+ * This module provides:
11
+ * - A typed structure for block storage with versioning and plugin support
12
+ * - Utility functions for manipulating storage
13
+ * - Handler interfaces for model-level customization
14
+ *
15
+ * @module block_storage
16
+ */
17
+ // =============================================================================
18
+ // Core Types
19
+ // =============================================================================
20
+ /**
21
+ * Discriminator key for BlockStorage format detection.
22
+ * This unique hash-based key identifies data as BlockStorage vs legacy formats.
23
+ */
24
+ const BLOCK_STORAGE_KEY = '__pl_a7f3e2b9__';
25
+ /**
26
+ * Current BlockStorage schema version.
27
+ * Increment this when the storage structure itself changes (not block state migrations).
28
+ */
29
+ const BLOCK_STORAGE_SCHEMA_VERSION = 'v1';
30
+ /**
31
+ * Default data version for new blocks without migrations.
32
+ * Unique identifier ensures blocks are created via DataModel API.
33
+ */
34
+ const DATA_MODEL_DEFAULT_VERSION = '__pl_v1_d4e8f2a1__';
35
+ /**
36
+ * Type guard to check if a value is a valid BlockStorage object.
37
+ * Checks for the discriminator key and valid schema version.
38
+ */
39
+ function isBlockStorage(value) {
40
+ if (value === null || typeof value !== 'object')
41
+ return false;
42
+ const obj = value;
43
+ const schemaVersion = obj[BLOCK_STORAGE_KEY];
44
+ // Currently only 'v1' is valid, but this allows future versions
45
+ return schemaVersion === 'v1'; // Add more versions as schema evolves
46
+ }
47
+ // =============================================================================
48
+ // Factory Functions
49
+ // =============================================================================
50
+ /**
51
+ * Creates a BlockStorage with the given initial data
52
+ *
53
+ * @param initialData - The initial data value (defaults to empty object)
54
+ * @param version - The initial data version key (defaults to DATA_MODEL_DEFAULT_VERSION)
55
+ * @returns A new BlockStorage instance with discriminator key
56
+ */
57
+ function createBlockStorage(initialData = {}, version = DATA_MODEL_DEFAULT_VERSION) {
58
+ return {
59
+ [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,
60
+ __dataVersion: version,
61
+ __data: initialData,
62
+ };
63
+ }
64
+ /**
65
+ * Normalizes raw storage data to BlockStorage format.
66
+ * If the input is already a BlockStorage, returns it as-is.
67
+ * If the input is legacy format (raw state), wraps it in BlockStorage structure.
68
+ *
69
+ * @param raw - Raw storage data (may be legacy format or BlockStorage)
70
+ * @returns Normalized BlockStorage
71
+ */
72
+ function normalizeBlockStorage(raw) {
73
+ if (isBlockStorage(raw)) {
74
+ const storage = raw;
75
+ return {
76
+ ...storage,
77
+ // Fix for early released version where __dataVersion was a number
78
+ __dataVersion: typeof storage.__dataVersion === 'number'
79
+ ? DATA_MODEL_DEFAULT_VERSION
80
+ : storage.__dataVersion,
81
+ };
82
+ }
83
+ // Legacy format: raw is the state directly
84
+ return createBlockStorage(raw);
85
+ }
86
+ // =============================================================================
87
+ // Data Access & Update Functions
88
+ // =============================================================================
89
+ /**
90
+ * Gets the data from BlockStorage
91
+ *
92
+ * @param storage - The BlockStorage instance
93
+ * @returns The data value
94
+ */
95
+ function getStorageData(storage) {
96
+ return storage.__data;
97
+ }
98
+ /**
99
+ * Updates the data in BlockStorage (immutable)
100
+ *
101
+ * @param storage - The current BlockStorage
102
+ * @param payload - The update payload with operation and value
103
+ * @returns A new BlockStorage with updated data
104
+ */
105
+ function updateStorageData(storage, payload) {
106
+ switch (payload.operation) {
107
+ case 'update-data':
108
+ return { ...storage, __data: payload.value };
109
+ default:
110
+ throw new Error(`Unknown storage operation: ${payload.operation}`);
111
+ }
112
+ }
113
+
7
114
  //
8
115
  // Helpers
9
116
  //
@@ -4699,6 +4806,9 @@
4699
4806
  Visibility: 'pl7.app/table/visibility',
4700
4807
  },
4701
4808
  Trace: 'pl7.app/trace',
4809
+ VDJ: {
4810
+ IsAssemblingFeature: 'pl7.app/vdj/isAssemblingFeature',
4811
+ },
4702
4812
  };
4703
4813
  const ValueTypeSchema = z.enum(['Int', 'Long', 'Float', 'Double', 'String']);
4704
4814
  const AnnotationJson = {
@@ -4728,6 +4838,7 @@
4728
4838
  [Annotation.Sequence.IsAnnotation]: z.boolean(),
4729
4839
  [Annotation.Table.OrderPriority]: z.number(),
4730
4840
  [Annotation.Trace]: z.record(z.string(), z.unknown()),
4841
+ [Annotation.VDJ.IsAssemblingFeature]: z.boolean(),
4731
4842
  };
4732
4843
  /// Helper function for reading plain annotation values
4733
4844
  function readAnnotation(spec, key) {
@@ -6103,6 +6214,30 @@
6103
6214
  }
6104
6215
  return result;
6105
6216
  };
6217
+ const countUniqueLabels = (result) => result === undefined ? 0 : new Set(result.map((c) => c.label)).size;
6218
+ // Post-processing: try removing types one by one (lowest importance first) to minimize the label set
6219
+ // Accepts removal if it doesn't decrease the number of unique labels (cardinality)
6220
+ const minimizeTypeSet = (typeSet) => {
6221
+ const initialResult = calculate(typeSet);
6222
+ if (initialResult === undefined) {
6223
+ return typeSet;
6224
+ }
6225
+ const currentCardinality = countUniqueLabels(initialResult);
6226
+ // Get types sorted by importance ascending (lowest first), excluding forced elements
6227
+ const removableSorted = [...typeSet]
6228
+ .filter((t) => !forceTraceElements?.has(t.split('@')[0])
6229
+ && !(ops.includeNativeLabel && t === LabelTypeFull))
6230
+ .sort((a, b) => (importances.get(a) ?? 0) - (importances.get(b) ?? 0));
6231
+ for (const typeToRemove of removableSorted) {
6232
+ const reducedSet = new Set(typeSet);
6233
+ reducedSet.delete(typeToRemove);
6234
+ const candidateResult = calculate(reducedSet);
6235
+ if (candidateResult !== undefined && countUniqueLabels(candidateResult) >= currentCardinality) {
6236
+ typeSet.delete(typeToRemove);
6237
+ }
6238
+ }
6239
+ return typeSet;
6240
+ };
6106
6241
  if (mainTypes.length === 0) {
6107
6242
  if (secondaryTypes.length !== 0)
6108
6243
  throw new Error('Non-empty secondary types list while main types list is empty.');
@@ -6128,16 +6263,20 @@
6128
6263
  if (additionalType >= 0)
6129
6264
  currentSet.add(mainTypes[additionalType]);
6130
6265
  const candidateResult = calculate(currentSet);
6131
- // checking if labels uniquely separate our records
6132
- if (candidateResult !== undefined && new Set(candidateResult.map((c) => c.label)).size === values.length)
6133
- return candidateResult;
6266
+ if (candidateResult !== undefined && countUniqueLabels(candidateResult) === values.length) {
6267
+ minimizeTypeSet(currentSet);
6268
+ return calculate(currentSet);
6269
+ }
6134
6270
  additionalType++;
6135
6271
  if (additionalType >= mainTypes.length) {
6136
6272
  includedTypes++;
6137
6273
  additionalType = includedTypes;
6138
6274
  }
6139
6275
  }
6140
- return calculate(new Set([...mainTypes, ...secondaryTypes]), true);
6276
+ // Fallback: include all types, then try to minimize
6277
+ const fallbackSet = new Set([...mainTypes, ...secondaryTypes]);
6278
+ minimizeTypeSet(fallbackSet);
6279
+ return calculate(fallbackSet, true);
6141
6280
  }
6142
6281
 
6143
6282
  const PCD_PREFIX = 'PColumnData/';
@@ -7240,28 +7379,19 @@
7240
7379
  }
7241
7380
  }
7242
7381
  /** Main entry point to the API available within model lambdas (like outputs, sections, etc..) */
7243
- class RenderCtx {
7382
+ class RenderCtxBase {
7244
7383
  ctx;
7245
7384
  constructor() {
7246
7385
  this.ctx = getCfgRenderCtx();
7247
7386
  }
7248
- _argsCache;
7249
- get args() {
7250
- if (this._argsCache === undefined) {
7251
- const raw = this.ctx.args;
7387
+ _dataCache;
7388
+ get data() {
7389
+ if (this._dataCache === undefined) {
7390
+ const raw = this.ctx.data;
7252
7391
  const value = typeof raw === 'function' ? raw() : raw;
7253
- this._argsCache = { v: JSON.parse(value) };
7392
+ this._dataCache = { v: value ? JSON.parse(value) : {} };
7254
7393
  }
7255
- return this._argsCache.v;
7256
- }
7257
- _uiStateCache;
7258
- get uiState() {
7259
- if (this._uiStateCache === undefined) {
7260
- const raw = this.ctx.uiState;
7261
- const value = typeof raw === 'function' ? raw() : raw;
7262
- this._uiStateCache = { v: value ? JSON.parse(value) : {} };
7263
- }
7264
- return this._uiStateCache.v;
7394
+ return this._dataCache.v;
7265
7395
  }
7266
7396
  // lazy rendering because this feature is rarely used
7267
7397
  _activeArgsCache;
@@ -7372,8 +7502,29 @@
7372
7502
  this.ctx.logError(msg);
7373
7503
  }
7374
7504
  }
7505
+ /** Render context for legacy v1/v2 blocks - provides backward compatibility */
7506
+ class RenderCtxLegacy extends RenderCtxBase {
7507
+ _argsCache;
7508
+ get args() {
7509
+ if (this._argsCache === undefined) {
7510
+ const raw = this.ctx.args;
7511
+ const value = typeof raw === 'function' ? raw() : raw;
7512
+ this._argsCache = { v: JSON.parse(value) };
7513
+ }
7514
+ return this._argsCache.v;
7515
+ }
7516
+ _uiStateCache;
7517
+ get uiState() {
7518
+ if (this._uiStateCache === undefined) {
7519
+ const raw = this.ctx.uiState;
7520
+ const value = typeof raw === 'function' ? raw() : raw;
7521
+ this._uiStateCache = { v: value ? JSON.parse(value) : {} };
7522
+ }
7523
+ return this._uiStateCache.v;
7524
+ }
7525
+ }
7375
7526
 
7376
- var version = "1.51.2";
7527
+ var version = "1.53.3";
7377
7528
 
7378
7529
  const PlatformaSDKVersion = version;
7379
7530
 
@@ -7417,7 +7568,7 @@
7417
7568
  output(key, cfgOrRf, flags = {}) {
7418
7569
  if (typeof cfgOrRf === 'function') {
7419
7570
  const handle = `output#${key}`;
7420
- tryRegisterCallback(handle, () => cfgOrRf(new RenderCtx()));
7571
+ tryRegisterCallback(handle, () => cfgOrRf(new RenderCtxLegacy()));
7421
7572
  return new BlockModel({
7422
7573
  ...this.config,
7423
7574
  outputs: {
@@ -7454,7 +7605,7 @@
7454
7605
  }
7455
7606
  argsValid(cfgOrRf) {
7456
7607
  if (typeof cfgOrRf === 'function') {
7457
- tryRegisterCallback('inputsValid', () => cfgOrRf(new RenderCtx()));
7608
+ tryRegisterCallback('inputsValid', () => cfgOrRf(new RenderCtxLegacy()));
7458
7609
  return new BlockModel({
7459
7610
  ...this.config,
7460
7611
  inputsValid: {
@@ -7475,7 +7626,7 @@
7475
7626
  return this.sections(getImmediate(arrOrCfgOrRf));
7476
7627
  }
7477
7628
  else if (typeof arrOrCfgOrRf === 'function') {
7478
- tryRegisterCallback('sections', () => arrOrCfgOrRf(new RenderCtx()));
7629
+ tryRegisterCallback('sections', () => arrOrCfgOrRf(new RenderCtxLegacy()));
7479
7630
  return new BlockModel({
7480
7631
  ...this.config,
7481
7632
  sections: {
@@ -7493,7 +7644,7 @@
7493
7644
  }
7494
7645
  /** Sets a rendering function to derive block title, shown for the block in the left blocks-overview panel. */
7495
7646
  title(rf) {
7496
- tryRegisterCallback('title', () => rf(new RenderCtx()));
7647
+ tryRegisterCallback('title', () => rf(new RenderCtxLegacy()));
7497
7648
  return new BlockModel({
7498
7649
  ...this.config,
7499
7650
  title: {
@@ -7503,7 +7654,7 @@
7503
7654
  });
7504
7655
  }
7505
7656
  subtitle(rf) {
7506
- tryRegisterCallback('subtitle', () => rf(new RenderCtx()));
7657
+ tryRegisterCallback('subtitle', () => rf(new RenderCtxLegacy()));
7507
7658
  return new BlockModel({
7508
7659
  ...this.config,
7509
7660
  subtitle: {
@@ -7513,7 +7664,7 @@
7513
7664
  });
7514
7665
  }
7515
7666
  tags(rf) {
7516
- tryRegisterCallback('tags', () => rf(new RenderCtx()));
7667
+ tryRegisterCallback('tags', () => rf(new RenderCtxLegacy()));
7517
7668
  return new BlockModel({
7518
7669
  ...this.config,
7519
7670
  tags: {
@@ -7580,7 +7731,10 @@
7580
7731
  if (this.config.initialArgs === undefined)
7581
7732
  throw new Error('Initial arguments not set.');
7582
7733
  const config = {
7734
+ v4: undefined,
7583
7735
  v3: {
7736
+ configVersion: 3,
7737
+ modelAPIVersion: 1,
7584
7738
  sdkVersion: PlatformaSDKVersion,
7585
7739
  renderingMode: this.config.renderingMode,
7586
7740
  initialArgs: this.config.initialArgs,
@@ -7620,6 +7774,259 @@
7620
7774
  }
7621
7775
  }
7622
7776
 
7777
+ /**
7778
+ * BlockStorage VM Integration - Internal module for VM-based storage operations.
7779
+ *
7780
+ * This module auto-registers internal callbacks that the middle layer can invoke
7781
+ * to perform storage transformations. Block developers never interact with these
7782
+ * directly - they only see `state`.
7783
+ *
7784
+ * Registered callbacks (all prefixed with `__pl_` for internal SDK use):
7785
+ * - `__pl_storage_applyUpdate`: (currentStorageJson, payload) => updatedStorageJson
7786
+ * - `__pl_storage_debugView`: (rawStorage) => JSON string with storage debug view
7787
+ * - `__pl_storage_migrate`: (currentStorageJson) => MigrationResult
7788
+ * - `__pl_args_derive`: (storageJson) => ArgsDeriveResult
7789
+ * - `__pl_prerunArgs_derive`: (storageJson) => ArgsDeriveResult
7790
+ *
7791
+ * Callbacks registered by DataModel.registerCallbacks():
7792
+ * - `__pl_data_initial`: () => initial data
7793
+ * - `__pl_data_upgrade`: (versioned) => DataMigrationResult
7794
+ * - `__pl_storage_initial`: () => initial BlockStorage as JSON string
7795
+ *
7796
+ * @module block_storage_vm
7797
+ * @internal
7798
+ */
7799
+ /**
7800
+ * Normalizes raw storage data and extracts state.
7801
+ * Handles all formats:
7802
+ * - New BlockStorage format (has discriminator)
7803
+ * - Legacy V1/V2 format ({ args, uiState })
7804
+ * - Raw V3 state (any other format)
7805
+ *
7806
+ * @param rawStorage - Raw data from blockStorage field (may be JSON string or object)
7807
+ * @returns Object with normalized storage and extracted state
7808
+ */
7809
+ function normalizeStorage(rawStorage) {
7810
+ // Handle undefined/null
7811
+ if (rawStorage === undefined || rawStorage === null) {
7812
+ const storage = createBlockStorage({});
7813
+ return { storage, data: {} };
7814
+ }
7815
+ // Parse JSON string if needed
7816
+ let parsed = rawStorage;
7817
+ if (typeof rawStorage === 'string') {
7818
+ try {
7819
+ parsed = JSON.parse(rawStorage);
7820
+ }
7821
+ catch {
7822
+ // If parsing fails, treat string as the data
7823
+ const storage = createBlockStorage(rawStorage);
7824
+ return { storage, data: rawStorage };
7825
+ }
7826
+ }
7827
+ // Check for BlockStorage format (has discriminator)
7828
+ if (isBlockStorage(parsed)) {
7829
+ const storage = normalizeBlockStorage(parsed);
7830
+ return { storage, data: getStorageData(storage) };
7831
+ }
7832
+ // Check for legacy V1/V2 format: { args, uiState }
7833
+ if (isLegacyModelV1ApiFormat(parsed)) {
7834
+ // For legacy format, the whole object IS the data
7835
+ const storage = createBlockStorage(parsed);
7836
+ return { storage, data: parsed };
7837
+ }
7838
+ // Raw V3 data - wrap it
7839
+ const storage = createBlockStorage(parsed);
7840
+ return { storage, data: parsed };
7841
+ }
7842
+ /**
7843
+ * Applies a state update to existing storage.
7844
+ * Used when setData is called from the frontend.
7845
+ *
7846
+ * @param currentStorageJson - Current storage as JSON string (must be defined)
7847
+ * @param newData - New data from application
7848
+ * @returns Updated storage as JSON string
7849
+ */
7850
+ function applyStorageUpdate(currentStorageJson, payload) {
7851
+ const { storage: currentStorage } = normalizeStorage(currentStorageJson);
7852
+ // Update data while preserving other storage fields (version, plugins)
7853
+ const updatedStorage = updateStorageData(currentStorage, payload);
7854
+ return JSON.stringify(updatedStorage);
7855
+ }
7856
+ /**
7857
+ * Checks if data is in legacy Model API v1 format.
7858
+ * Legacy format has { args, uiState? } at top level without the BlockStorage discriminator.
7859
+ */
7860
+ function isLegacyModelV1ApiFormat(data) {
7861
+ if (data === null || typeof data !== 'object')
7862
+ return false;
7863
+ if (isBlockStorage(data))
7864
+ return false;
7865
+ const obj = data;
7866
+ return 'args' in obj;
7867
+ }
7868
+ // =============================================================================
7869
+ // Auto-register internal callbacks when module is loaded in VM
7870
+ // =============================================================================
7871
+ // Register apply update callback (requires existing storage)
7872
+ tryRegisterCallback('__pl_storage_applyUpdate', (currentStorageJson, payload) => {
7873
+ return applyStorageUpdate(currentStorageJson, payload);
7874
+ });
7875
+ /**
7876
+ * Gets storage debug view from raw storage data.
7877
+ * Returns structured debug info about the storage state.
7878
+ *
7879
+ * @param rawStorage - Raw data from blockStorage field (may be JSON string or object)
7880
+ * @returns JSON string with storage debug view
7881
+ */
7882
+ function getStorageDebugView(rawStorage) {
7883
+ const { storage } = normalizeStorage(rawStorage);
7884
+ const debugView = {
7885
+ dataVersion: storage.__dataVersion,
7886
+ data: storage.__data,
7887
+ };
7888
+ return stringifyJson(debugView);
7889
+ }
7890
+ // Register debug view callback
7891
+ tryRegisterCallback('__pl_storage_debugView', (rawStorage) => {
7892
+ return getStorageDebugView(rawStorage);
7893
+ });
7894
+ /**
7895
+ * Runs storage migration using the DataModel's migrate callback.
7896
+ * This is the main entry point for the middle layer to trigger migrations.
7897
+ *
7898
+ * Uses the '__pl_data_upgrade' callback registered by DataModel.registerCallbacks() which:
7899
+ * - Handles all migration logic internally
7900
+ * - Returns { version, data, warning? } - warning present if reset to initial data
7901
+ *
7902
+ * @param currentStorageJson - Current storage as JSON string (or undefined)
7903
+ * @returns MigrationResult
7904
+ */
7905
+ function migrateStorage(currentStorageJson) {
7906
+ // Get the callback registry context
7907
+ const ctx = tryGetCfgRenderCtx();
7908
+ if (ctx === undefined) {
7909
+ return { error: 'Not in config rendering context' };
7910
+ }
7911
+ // Normalize storage to get current data and version
7912
+ const { storage: currentStorage, data: currentData } = normalizeStorage(currentStorageJson);
7913
+ const currentVersion = currentStorage.__dataVersion;
7914
+ // Helper to create storage with given data and version
7915
+ const createStorageJson = (data, version) => {
7916
+ return JSON.stringify({
7917
+ ...currentStorage,
7918
+ __dataVersion: version,
7919
+ __data: data,
7920
+ });
7921
+ };
7922
+ // Get the migrate callback (registered by DataModel.registerCallbacks())
7923
+ const migrateCallback = ctx.callbackRegistry['__pl_data_upgrade'];
7924
+ if (typeof migrateCallback !== 'function') {
7925
+ return { error: '__pl_data_upgrade callback not found (DataModel not registered)' };
7926
+ }
7927
+ // Call the migrator's migrate function
7928
+ let result;
7929
+ try {
7930
+ result = migrateCallback({ version: currentVersion, data: currentData });
7931
+ }
7932
+ catch (e) {
7933
+ const errorMsg = e instanceof Error ? e.message : String(e);
7934
+ return { error: `migrate() threw: ${errorMsg}` };
7935
+ }
7936
+ // Build info message
7937
+ const info = result.version === currentVersion
7938
+ ? `No migration needed (${currentVersion})`
7939
+ : result.warning
7940
+ ? `Reset to initial data (${result.version})`
7941
+ : `Migrated ${currentVersion}→${result.version}`;
7942
+ return {
7943
+ newStorageJson: createStorageJson(result.data, result.version),
7944
+ info,
7945
+ warn: result.warning,
7946
+ };
7947
+ }
7948
+ // Register migrate callback
7949
+ tryRegisterCallback('__pl_storage_migrate', (currentStorageJson) => {
7950
+ return migrateStorage(currentStorageJson);
7951
+ });
7952
+ /**
7953
+ * Derives args from storage using the registered 'args' callback.
7954
+ * This extracts data from storage and passes it to the block's args() function.
7955
+ *
7956
+ * @param storageJson - Storage as JSON string
7957
+ * @returns ArgsDeriveResult with derived args or error
7958
+ */
7959
+ function deriveArgsFromStorage(storageJson) {
7960
+ const ctx = tryGetCfgRenderCtx();
7961
+ if (ctx === undefined) {
7962
+ return { error: 'Not in config rendering context' };
7963
+ }
7964
+ // Extract data from storage
7965
+ const { data } = normalizeStorage(storageJson);
7966
+ // Get the args callback (registered by BlockModelV3.args())
7967
+ const argsCallback = ctx.callbackRegistry['args'];
7968
+ if (typeof argsCallback !== 'function') {
7969
+ return { error: 'args callback not found' };
7970
+ }
7971
+ // Call the args callback with extracted data
7972
+ try {
7973
+ const result = argsCallback(data);
7974
+ return { value: result };
7975
+ }
7976
+ catch (e) {
7977
+ const errorMsg = e instanceof Error ? e.message : String(e);
7978
+ return { error: `args() threw: ${errorMsg}` };
7979
+ }
7980
+ }
7981
+ // Register args derivation callback
7982
+ tryRegisterCallback('__pl_args_derive', (storageJson) => {
7983
+ return deriveArgsFromStorage(storageJson);
7984
+ });
7985
+ /**
7986
+ * Derives prerunArgs from storage using the registered 'prerunArgs' callback.
7987
+ * Falls back to 'args' callback if 'prerunArgs' is not defined.
7988
+ *
7989
+ * @param storageJson - Storage as JSON string
7990
+ * @returns ArgsDeriveResult with derived prerunArgs or error
7991
+ */
7992
+ function derivePrerunArgsFromStorage(storageJson) {
7993
+ const ctx = tryGetCfgRenderCtx();
7994
+ if (ctx === undefined) {
7995
+ return { error: 'Not in config rendering context' };
7996
+ }
7997
+ // Extract data from storage
7998
+ const { data } = normalizeStorage(storageJson);
7999
+ // Try prerunArgs callback first
8000
+ const prerunArgsCallback = ctx.callbackRegistry['prerunArgs'];
8001
+ if (typeof prerunArgsCallback === 'function') {
8002
+ try {
8003
+ const result = prerunArgsCallback(data);
8004
+ return { value: result };
8005
+ }
8006
+ catch (e) {
8007
+ const errorMsg = e instanceof Error ? e.message : String(e);
8008
+ return { error: `prerunArgs() threw: ${errorMsg}` };
8009
+ }
8010
+ }
8011
+ // Fall back to args callback
8012
+ const argsCallback = ctx.callbackRegistry['args'];
8013
+ if (typeof argsCallback !== 'function') {
8014
+ return { error: 'args callback not found (fallback from missing prerunArgs)' };
8015
+ }
8016
+ try {
8017
+ const result = argsCallback(data);
8018
+ return { value: result };
8019
+ }
8020
+ catch (e) {
8021
+ const errorMsg = e instanceof Error ? e.message : String(e);
8022
+ return { error: `args() threw (fallback): ${errorMsg}` };
8023
+ }
8024
+ }
8025
+ // Register prerunArgs derivation callback
8026
+ tryRegisterCallback('__pl_prerunArgs_derive', (storageJson) => {
8027
+ return derivePrerunArgsFromStorage(storageJson);
8028
+ });
8029
+
7623
8030
  function identity(x) {
7624
8031
  return x;
7625
8032
  }
@@ -8034,13 +8441,13 @@
8034
8441
  ctx.logWarn(`Partition filter ${JSON.stringify(f)} does not match provided columns, skipping`);
8035
8442
  return valid;
8036
8443
  });
8037
- const filters = uniqueBy([...([]), ...tableStateNormalized.pTableParams.filters], (f) => canonicalizeJson(f.column)).filter((f) => {
8444
+ const filters = uniqueBy([...tableStateNormalized.pTableParams.filters, ...([])], (f) => canonicalizeJson(f.column)).filter((f) => {
8038
8445
  const valid = isValidColumnId(f.column);
8039
8446
  if (!valid)
8040
8447
  ctx.logWarn(`Filter ${JSON.stringify(f)} does not match provided columns, skipping`);
8041
8448
  return valid;
8042
8449
  });
8043
- const sorting = uniqueBy([...([]), ...tableStateNormalized.pTableParams.sorting], (s) => canonicalizeJson(s.column)).filter((s) => {
8450
+ const sorting = uniqueBy([...tableStateNormalized.pTableParams.sorting, ...([])], (s) => canonicalizeJson(s.column)).filter((s) => {
8044
8451
  const valid = isValidColumnId(s.column);
8045
8452
  if (!valid)
8046
8453
  ctx.logWarn(`Sorting ${JSON.stringify(s)} does not match provided columns, skipping`);
@@ -8180,6 +8587,20 @@
8180
8587
  errors: z.lazy(() => ErrorShape.array()).optional(),
8181
8588
  });
8182
8589
 
8590
+ var titles = {
8591
+ main: "Main"};
8592
+ var strings = {
8593
+ titles: titles
8594
+ };
8595
+
8596
+ function getDefaultBlockLabel(data) {
8597
+ // Return abundance label if available, otherwise default message
8598
+ if (data.abundanceLabel) {
8599
+ return data.abundanceLabel;
8600
+ }
8601
+ return 'Select Abundance and Metrics';
8602
+ }
8603
+
8183
8604
  const convertMetricsUiToArgs = (metrics) => {
8184
8605
  return metrics.map((metric) => {
8185
8606
  return {
@@ -8192,7 +8613,7 @@
8192
8613
  const model = BlockModel.create()
8193
8614
  .withArgs({
8194
8615
  metrics: [],
8195
- defaultBlockLabel: '',
8616
+ defaultBlockLabel: getDefaultBlockLabel({}),
8196
8617
  customBlockLabel: '',
8197
8618
  })
8198
8619
  .withUiState({
@@ -8293,12 +8714,13 @@
8293
8714
  .title(() => 'Repertoire Diversity')
8294
8715
  .subtitle((ctx) => ctx.args.customBlockLabel || ctx.args.defaultBlockLabel)
8295
8716
  .sections((_) => [
8296
- { type: 'link', href: '/', label: 'Main' },
8717
+ { type: 'link', href: '/', label: strings.titles.main },
8297
8718
  { type: 'link', href: '/diversityGraph', label: 'Diversity Graph' },
8298
8719
  ])
8299
8720
  .done(2);
8300
8721
 
8301
8722
  exports.convertMetricsUiToArgs = convertMetricsUiToArgs;
8723
+ exports.getDefaultBlockLabel = getDefaultBlockLabel;
8302
8724
  exports.model = model;
8303
8725
 
8304
8726
  }));