@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/.turbo/turbo-build.log +5 -5
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +12 -0
- package/dist/bundle.js +453 -31
- package/dist/bundle.js.map +1 -1
- package/dist/index.cjs +5 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +17 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/label.cjs +12 -0
- package/dist/label.cjs.map +1 -0
- package/dist/label.d.ts +4 -0
- package/dist/label.d.ts.map +1 -0
- package/dist/label.js +10 -0
- package/dist/label.js.map +1 -0
- package/dist/model.json +1 -1
- package/package.json +7 -6
- package/src/index.ts +6 -2
- package/src/label.ts +10 -0
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
|
-
|
|
6132
|
-
|
|
6133
|
-
return
|
|
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
|
-
|
|
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
|
|
7382
|
+
class RenderCtxBase {
|
|
7244
7383
|
ctx;
|
|
7245
7384
|
constructor() {
|
|
7246
7385
|
this.ctx = getCfgRenderCtx();
|
|
7247
7386
|
}
|
|
7248
|
-
|
|
7249
|
-
get
|
|
7250
|
-
if (this.
|
|
7251
|
-
const raw = this.ctx.
|
|
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.
|
|
7392
|
+
this._dataCache = { v: value ? JSON.parse(value) : {} };
|
|
7254
7393
|
}
|
|
7255
|
-
return this.
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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([...
|
|
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([...
|
|
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:
|
|
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
|
}));
|