@platforma-open/milaboratories.immune-assay-data.model 1.5.1 → 1.5.3
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 +14 -0
- package/dist/bundle.js +703 -28
- package/dist/bundle.js.map +1 -1
- package/dist/index.cjs +62 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +46 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +63 -3
- package/dist/index.js.map +1 -1
- package/dist/label.cjs +22 -0
- package/dist/label.cjs.map +1 -0
- package/dist/label.d.ts +7 -0
- package/dist/label.d.ts.map +1 -0
- package/dist/label.js +20 -0
- package/dist/label.js.map +1 -0
- package/dist/model.json +1 -1
- package/package.json +5 -5
- package/src/index.ts +90 -1
- package/src/label.ts +27 -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
|
//
|
|
@@ -4625,6 +4732,9 @@
|
|
|
4625
4732
|
var canonicalizeExports = requireCanonicalize();
|
|
4626
4733
|
var canonicalize = /*@__PURE__*/getDefaultExportFromCjs(canonicalizeExports);
|
|
4627
4734
|
|
|
4735
|
+
function stringifyJson(value) {
|
|
4736
|
+
return JSON.stringify(value);
|
|
4737
|
+
}
|
|
4628
4738
|
function canonicalizeJson(value) {
|
|
4629
4739
|
return canonicalize(value);
|
|
4630
4740
|
}
|
|
@@ -4696,6 +4806,9 @@
|
|
|
4696
4806
|
Visibility: 'pl7.app/table/visibility',
|
|
4697
4807
|
},
|
|
4698
4808
|
Trace: 'pl7.app/trace',
|
|
4809
|
+
VDJ: {
|
|
4810
|
+
IsAssemblingFeature: 'pl7.app/vdj/isAssemblingFeature',
|
|
4811
|
+
},
|
|
4699
4812
|
};
|
|
4700
4813
|
const ValueTypeSchema = z.enum(['Int', 'Long', 'Float', 'Double', 'String']);
|
|
4701
4814
|
const AnnotationJson = {
|
|
@@ -4725,6 +4838,7 @@
|
|
|
4725
4838
|
[Annotation.Sequence.IsAnnotation]: z.boolean(),
|
|
4726
4839
|
[Annotation.Table.OrderPriority]: z.number(),
|
|
4727
4840
|
[Annotation.Trace]: z.record(z.string(), z.unknown()),
|
|
4841
|
+
[Annotation.VDJ.IsAssemblingFeature]: z.boolean(),
|
|
4728
4842
|
};
|
|
4729
4843
|
/// Helper function for reading plain annotation values
|
|
4730
4844
|
function readAnnotation(spec, key) {
|
|
@@ -6100,10 +6214,15 @@
|
|
|
6100
6214
|
}
|
|
6101
6215
|
return result;
|
|
6102
6216
|
};
|
|
6103
|
-
|
|
6104
|
-
const hasUniqueLabels = (result) => result !== undefined && new Set(result.map((c) => c.label)).size === values.length;
|
|
6217
|
+
const countUniqueLabels = (result) => result === undefined ? 0 : new Set(result.map((c) => c.label)).size;
|
|
6105
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)
|
|
6106
6220
|
const minimizeTypeSet = (typeSet) => {
|
|
6221
|
+
const initialResult = calculate(typeSet);
|
|
6222
|
+
if (initialResult === undefined) {
|
|
6223
|
+
return typeSet;
|
|
6224
|
+
}
|
|
6225
|
+
const currentCardinality = countUniqueLabels(initialResult);
|
|
6107
6226
|
// Get types sorted by importance ascending (lowest first), excluding forced elements
|
|
6108
6227
|
const removableSorted = [...typeSet]
|
|
6109
6228
|
.filter((t) => !forceTraceElements?.has(t.split('@')[0])
|
|
@@ -6113,7 +6232,7 @@
|
|
|
6113
6232
|
const reducedSet = new Set(typeSet);
|
|
6114
6233
|
reducedSet.delete(typeToRemove);
|
|
6115
6234
|
const candidateResult = calculate(reducedSet);
|
|
6116
|
-
if (
|
|
6235
|
+
if (candidateResult !== undefined && countUniqueLabels(candidateResult) >= currentCardinality) {
|
|
6117
6236
|
typeSet.delete(typeToRemove);
|
|
6118
6237
|
}
|
|
6119
6238
|
}
|
|
@@ -6144,7 +6263,7 @@
|
|
|
6144
6263
|
if (additionalType >= 0)
|
|
6145
6264
|
currentSet.add(mainTypes[additionalType]);
|
|
6146
6265
|
const candidateResult = calculate(currentSet);
|
|
6147
|
-
if (
|
|
6266
|
+
if (candidateResult !== undefined && countUniqueLabels(candidateResult) === values.length) {
|
|
6148
6267
|
minimizeTypeSet(currentSet);
|
|
6149
6268
|
return calculate(currentSet);
|
|
6150
6269
|
}
|
|
@@ -7260,28 +7379,19 @@
|
|
|
7260
7379
|
}
|
|
7261
7380
|
}
|
|
7262
7381
|
/** Main entry point to the API available within model lambdas (like outputs, sections, etc..) */
|
|
7263
|
-
class
|
|
7382
|
+
class RenderCtxBase {
|
|
7264
7383
|
ctx;
|
|
7265
7384
|
constructor() {
|
|
7266
7385
|
this.ctx = getCfgRenderCtx();
|
|
7267
7386
|
}
|
|
7268
|
-
|
|
7269
|
-
get
|
|
7270
|
-
if (this.
|
|
7271
|
-
const raw = this.ctx.
|
|
7272
|
-
const value = typeof raw === 'function' ? raw() : raw;
|
|
7273
|
-
this._argsCache = { v: JSON.parse(value) };
|
|
7274
|
-
}
|
|
7275
|
-
return this._argsCache.v;
|
|
7276
|
-
}
|
|
7277
|
-
_uiStateCache;
|
|
7278
|
-
get uiState() {
|
|
7279
|
-
if (this._uiStateCache === undefined) {
|
|
7280
|
-
const raw = this.ctx.uiState;
|
|
7387
|
+
_dataCache;
|
|
7388
|
+
get data() {
|
|
7389
|
+
if (this._dataCache === undefined) {
|
|
7390
|
+
const raw = this.ctx.data;
|
|
7281
7391
|
const value = typeof raw === 'function' ? raw() : raw;
|
|
7282
|
-
this.
|
|
7392
|
+
this._dataCache = { v: value ? JSON.parse(value) : {} };
|
|
7283
7393
|
}
|
|
7284
|
-
return this.
|
|
7394
|
+
return this._dataCache.v;
|
|
7285
7395
|
}
|
|
7286
7396
|
// lazy rendering because this feature is rarely used
|
|
7287
7397
|
_activeArgsCache;
|
|
@@ -7392,8 +7502,29 @@
|
|
|
7392
7502
|
this.ctx.logError(msg);
|
|
7393
7503
|
}
|
|
7394
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
|
+
}
|
|
7395
7526
|
|
|
7396
|
-
var version = "1.
|
|
7527
|
+
var version = "1.53.3";
|
|
7397
7528
|
|
|
7398
7529
|
const PlatformaSDKVersion = version;
|
|
7399
7530
|
|
|
@@ -7437,7 +7568,7 @@
|
|
|
7437
7568
|
output(key, cfgOrRf, flags = {}) {
|
|
7438
7569
|
if (typeof cfgOrRf === 'function') {
|
|
7439
7570
|
const handle = `output#${key}`;
|
|
7440
|
-
tryRegisterCallback(handle, () => cfgOrRf(new
|
|
7571
|
+
tryRegisterCallback(handle, () => cfgOrRf(new RenderCtxLegacy()));
|
|
7441
7572
|
return new BlockModel({
|
|
7442
7573
|
...this.config,
|
|
7443
7574
|
outputs: {
|
|
@@ -7474,7 +7605,7 @@
|
|
|
7474
7605
|
}
|
|
7475
7606
|
argsValid(cfgOrRf) {
|
|
7476
7607
|
if (typeof cfgOrRf === 'function') {
|
|
7477
|
-
tryRegisterCallback('inputsValid', () => cfgOrRf(new
|
|
7608
|
+
tryRegisterCallback('inputsValid', () => cfgOrRf(new RenderCtxLegacy()));
|
|
7478
7609
|
return new BlockModel({
|
|
7479
7610
|
...this.config,
|
|
7480
7611
|
inputsValid: {
|
|
@@ -7495,7 +7626,7 @@
|
|
|
7495
7626
|
return this.sections(getImmediate(arrOrCfgOrRf));
|
|
7496
7627
|
}
|
|
7497
7628
|
else if (typeof arrOrCfgOrRf === 'function') {
|
|
7498
|
-
tryRegisterCallback('sections', () => arrOrCfgOrRf(new
|
|
7629
|
+
tryRegisterCallback('sections', () => arrOrCfgOrRf(new RenderCtxLegacy()));
|
|
7499
7630
|
return new BlockModel({
|
|
7500
7631
|
...this.config,
|
|
7501
7632
|
sections: {
|
|
@@ -7513,7 +7644,7 @@
|
|
|
7513
7644
|
}
|
|
7514
7645
|
/** Sets a rendering function to derive block title, shown for the block in the left blocks-overview panel. */
|
|
7515
7646
|
title(rf) {
|
|
7516
|
-
tryRegisterCallback('title', () => rf(new
|
|
7647
|
+
tryRegisterCallback('title', () => rf(new RenderCtxLegacy()));
|
|
7517
7648
|
return new BlockModel({
|
|
7518
7649
|
...this.config,
|
|
7519
7650
|
title: {
|
|
@@ -7523,7 +7654,7 @@
|
|
|
7523
7654
|
});
|
|
7524
7655
|
}
|
|
7525
7656
|
subtitle(rf) {
|
|
7526
|
-
tryRegisterCallback('subtitle', () => rf(new
|
|
7657
|
+
tryRegisterCallback('subtitle', () => rf(new RenderCtxLegacy()));
|
|
7527
7658
|
return new BlockModel({
|
|
7528
7659
|
...this.config,
|
|
7529
7660
|
subtitle: {
|
|
@@ -7533,7 +7664,7 @@
|
|
|
7533
7664
|
});
|
|
7534
7665
|
}
|
|
7535
7666
|
tags(rf) {
|
|
7536
|
-
tryRegisterCallback('tags', () => rf(new
|
|
7667
|
+
tryRegisterCallback('tags', () => rf(new RenderCtxLegacy()));
|
|
7537
7668
|
return new BlockModel({
|
|
7538
7669
|
...this.config,
|
|
7539
7670
|
tags: {
|
|
@@ -7600,7 +7731,10 @@
|
|
|
7600
7731
|
if (this.config.initialArgs === undefined)
|
|
7601
7732
|
throw new Error('Initial arguments not set.');
|
|
7602
7733
|
const config = {
|
|
7734
|
+
v4: undefined,
|
|
7603
7735
|
v3: {
|
|
7736
|
+
configVersion: 3,
|
|
7737
|
+
modelAPIVersion: 1,
|
|
7604
7738
|
sdkVersion: PlatformaSDKVersion,
|
|
7605
7739
|
renderingMode: this.config.renderingMode,
|
|
7606
7740
|
initialArgs: this.config.initialArgs,
|
|
@@ -7640,10 +7774,473 @@
|
|
|
7640
7774
|
}
|
|
7641
7775
|
}
|
|
7642
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
|
+
|
|
7643
8030
|
function identity(x) {
|
|
7644
8031
|
return x;
|
|
7645
8032
|
}
|
|
7646
8033
|
|
|
8034
|
+
function getAllRelatedColumns(ctx, predicate) {
|
|
8035
|
+
// if current block doesn't produce own columns then use all columns from result pool
|
|
8036
|
+
const columns = new PColumnCollection();
|
|
8037
|
+
columns.addColumnProvider(ctx.resultPool);
|
|
8038
|
+
const allColumns = columns.getUniversalEntries(predicate, { dontWaitAllData: true, overrideLabelAnnotation: false }) ?? [];
|
|
8039
|
+
const allAxes = new Map(allColumns
|
|
8040
|
+
.flatMap((column) => getNormalizedAxesList(column.spec.axesSpec))
|
|
8041
|
+
.map((axisSpec) => {
|
|
8042
|
+
const axisId = getAxisId(axisSpec);
|
|
8043
|
+
return [canonicalizeJson(axisId), axisSpec];
|
|
8044
|
+
}));
|
|
8045
|
+
// additional columns are duplicates with extra fields in domains for compatibility if there are ones with partial match
|
|
8046
|
+
const extendedColumns = enrichCompatible(allAxes, allColumns);
|
|
8047
|
+
return extendedColumns;
|
|
8048
|
+
}
|
|
8049
|
+
function getRelatedColumns(ctx, { columns: rootColumns, predicate }) {
|
|
8050
|
+
// if current block has its own columns then take from result pool only compatible with them
|
|
8051
|
+
const columns = new PColumnCollection();
|
|
8052
|
+
columns.addColumnProvider(ctx.resultPool);
|
|
8053
|
+
columns.addColumns(rootColumns);
|
|
8054
|
+
// all possible axes from block columns
|
|
8055
|
+
const blockAxes = new Map();
|
|
8056
|
+
// axes from block columns and compatible result pool columns
|
|
8057
|
+
const allAxes = new Map();
|
|
8058
|
+
for (const c of rootColumns) {
|
|
8059
|
+
for (const spec of getNormalizedAxesList(c.spec.axesSpec)) {
|
|
8060
|
+
const aid = getAxisId(spec);
|
|
8061
|
+
blockAxes.set(canonicalizeJson(aid), spec);
|
|
8062
|
+
allAxes.set(canonicalizeJson(aid), spec);
|
|
8063
|
+
}
|
|
8064
|
+
}
|
|
8065
|
+
// all linker columns always go to pFrame - even it's impossible to use some of them they all are hidden
|
|
8066
|
+
const linkerColumns = columns.getUniversalEntries((spec) => predicate(spec) && isLinkerColumn(spec)) ?? [];
|
|
8067
|
+
const availableWithLinkersAxes = getAvailableWithLinkersAxes(linkerColumns, blockAxes);
|
|
8068
|
+
// all possible axes from connected linkers
|
|
8069
|
+
for (const item of availableWithLinkersAxes) {
|
|
8070
|
+
blockAxes.set(...item);
|
|
8071
|
+
allAxes.set(...item);
|
|
8072
|
+
}
|
|
8073
|
+
const blockAxesArr = Array.from(blockAxes.values());
|
|
8074
|
+
// all compatible with block columns but without label columns
|
|
8075
|
+
let compatibleWithoutLabels = (columns.getUniversalEntries((spec) => predicate(spec) && spec.axesSpec.some((axisSpec) => {
|
|
8076
|
+
const axisId = getAxisId(axisSpec);
|
|
8077
|
+
return blockAxesArr.some((selectorAxisSpec) => matchAxisId(getAxisId(selectorAxisSpec), axisId));
|
|
8078
|
+
}), { dontWaitAllData: true, overrideLabelAnnotation: false }) ?? []).filter((column) => !isLabelColumn(column.spec));
|
|
8079
|
+
// extend axes set for label columns request
|
|
8080
|
+
for (const c of compatibleWithoutLabels) {
|
|
8081
|
+
for (const spec of getNormalizedAxesList(c.spec.axesSpec)) {
|
|
8082
|
+
const aid = getAxisId(spec);
|
|
8083
|
+
allAxes.set(canonicalizeJson(aid), spec);
|
|
8084
|
+
}
|
|
8085
|
+
}
|
|
8086
|
+
const allAxesArr = Array.from(allAxes.values());
|
|
8087
|
+
// extend allowed columns - add columns thad doesn't have axes from block, but have all axes in 'allAxes' list (that means all axes from linkers or from 'hanging' of other selected columns)
|
|
8088
|
+
compatibleWithoutLabels = (columns.getUniversalEntries((spec) => predicate(spec) && spec.axesSpec.every((axisSpec) => {
|
|
8089
|
+
const axisId = getAxisId(axisSpec);
|
|
8090
|
+
return allAxesArr.some((selectorAxisSpec) => matchAxisId(getAxisId(selectorAxisSpec), axisId));
|
|
8091
|
+
}), { dontWaitAllData: true, overrideLabelAnnotation: false }) ?? []).filter((column) => !isLabelColumn(column.spec));
|
|
8092
|
+
// label columns must be compatible with full set of axes - block axes and axes from compatible columns from result pool
|
|
8093
|
+
const compatibleLabels = (columns.getUniversalEntries((spec) => predicate(spec) && spec.axesSpec.some((axisSpec) => {
|
|
8094
|
+
const axisId = getAxisId(axisSpec);
|
|
8095
|
+
return allAxesArr.some((selectorAxisSpec) => matchAxisId(getAxisId(selectorAxisSpec), axisId));
|
|
8096
|
+
}), { dontWaitAllData: true, overrideLabelAnnotation: false }) ?? []).filter((column) => isLabelColumn(column.spec));
|
|
8097
|
+
const compatible = [...compatibleWithoutLabels, ...compatibleLabels];
|
|
8098
|
+
// additional columns are duplicates with extra fields in domains for compatibility if there are ones with partial match
|
|
8099
|
+
const extendedColumns = enrichCompatible(blockAxes, compatible);
|
|
8100
|
+
return extendedColumns;
|
|
8101
|
+
}
|
|
8102
|
+
|
|
8103
|
+
/** Create id for column copy with added keys in axes domains */
|
|
8104
|
+
const colId = (id, domains) => {
|
|
8105
|
+
let wid = id.toString();
|
|
8106
|
+
domains?.forEach((domain) => {
|
|
8107
|
+
if (domain) {
|
|
8108
|
+
for (const [k, v] of Object.entries(domain)) {
|
|
8109
|
+
wid += k;
|
|
8110
|
+
wid += v;
|
|
8111
|
+
}
|
|
8112
|
+
}
|
|
8113
|
+
});
|
|
8114
|
+
return wid;
|
|
8115
|
+
};
|
|
8116
|
+
/** All combinations with 1 key from each list */
|
|
8117
|
+
function getKeysCombinations(idsLists) {
|
|
8118
|
+
if (!idsLists.length) {
|
|
8119
|
+
return [];
|
|
8120
|
+
}
|
|
8121
|
+
let result = [[]];
|
|
8122
|
+
idsLists.forEach((list) => {
|
|
8123
|
+
const nextResult = [];
|
|
8124
|
+
list.forEach((key) => {
|
|
8125
|
+
nextResult.push(...result.map((resultItem) => [...resultItem, key]));
|
|
8126
|
+
});
|
|
8127
|
+
result = nextResult;
|
|
8128
|
+
});
|
|
8129
|
+
return result;
|
|
8130
|
+
}
|
|
8131
|
+
function isHiddenFromGraphColumn(column) {
|
|
8132
|
+
return !!readAnnotationJson(column, Annotation.HideDataFromGraphs);
|
|
8133
|
+
}
|
|
8134
|
+
function isHiddenFromUIColumn(column) {
|
|
8135
|
+
return !!readAnnotationJson(column, Annotation.HideDataFromUi);
|
|
8136
|
+
}
|
|
8137
|
+
function getAvailableWithLinkersAxes(linkerColumns, blockAxes) {
|
|
8138
|
+
const linkerMap = LinkerMap.fromColumns(linkerColumns.map(getColumnIdAndSpec));
|
|
8139
|
+
const availableAxes = linkerMap.getReachableByLinkersAxesFromAxesNormalized([...blockAxes.values()], (linkerKeyId, sourceAxisId) => matchAxisId(sourceAxisId, linkerKeyId));
|
|
8140
|
+
return new Map(availableAxes.map((axisSpec) => {
|
|
8141
|
+
const id = getAxisId(axisSpec);
|
|
8142
|
+
return [canonicalizeJson(id), axisSpec];
|
|
8143
|
+
}));
|
|
8144
|
+
}
|
|
8145
|
+
/** Add columns with fully compatible axes created from partial compatible ones */
|
|
8146
|
+
function enrichCompatible(blockAxes, columns) {
|
|
8147
|
+
return columns.flatMap((column) => getAdditionalColumnsForColumn(blockAxes, column));
|
|
8148
|
+
}
|
|
8149
|
+
function getAdditionalColumnsForColumn(blockAxes, column) {
|
|
8150
|
+
const columnAxesIds = column.spec.axesSpec.map(getAxisId);
|
|
8151
|
+
if (columnAxesIds.every((id) => blockAxes.has(canonicalizeJson(id)))) {
|
|
8152
|
+
return [column]; // the column is compatible with its own domains without modifications
|
|
8153
|
+
}
|
|
8154
|
+
// options with different possible domains for every axis of secondary column
|
|
8155
|
+
const secondaryIdsOptions = columnAxesIds.map((id) => {
|
|
8156
|
+
const result = [];
|
|
8157
|
+
for (const [_, mainId] of blockAxes) {
|
|
8158
|
+
if (matchAxisId(mainId, id) && !matchAxisId(id, mainId)) {
|
|
8159
|
+
result.push(mainId);
|
|
8160
|
+
}
|
|
8161
|
+
}
|
|
8162
|
+
return result;
|
|
8163
|
+
});
|
|
8164
|
+
// all possible combinations of axes with added domains
|
|
8165
|
+
const secondaryIdsVariants = getKeysCombinations(secondaryIdsOptions);
|
|
8166
|
+
// sets of added to column domain fields
|
|
8167
|
+
const allAddedDomainValues = new Set();
|
|
8168
|
+
const addedNotToAllVariantsDomainValues = new Set();
|
|
8169
|
+
const addedByVariantsDomainValues = secondaryIdsVariants.map((idsList) => {
|
|
8170
|
+
const addedSet = new Set();
|
|
8171
|
+
idsList.map((axisId, idx) => {
|
|
8172
|
+
const d1 = column.spec.axesSpec[idx].domain;
|
|
8173
|
+
const d2 = axisId.domain;
|
|
8174
|
+
Object.entries(d2 ?? {}).forEach(([key, value]) => {
|
|
8175
|
+
if (d1?.[key] === undefined) {
|
|
8176
|
+
const item = JSON.stringify([key, value]);
|
|
8177
|
+
addedSet.add(item);
|
|
8178
|
+
allAddedDomainValues.add(item);
|
|
8179
|
+
}
|
|
8180
|
+
});
|
|
8181
|
+
return ({
|
|
8182
|
+
...axisId,
|
|
8183
|
+
annotations: column.spec.axesSpec[idx].annotations,
|
|
8184
|
+
});
|
|
8185
|
+
});
|
|
8186
|
+
return addedSet;
|
|
8187
|
+
});
|
|
8188
|
+
[...allAddedDomainValues].forEach((addedPart) => {
|
|
8189
|
+
if (addedByVariantsDomainValues.some((s) => !s.has(addedPart))) {
|
|
8190
|
+
addedNotToAllVariantsDomainValues.add(addedPart);
|
|
8191
|
+
}
|
|
8192
|
+
});
|
|
8193
|
+
const additionalColumns = secondaryIdsVariants.map((idsList, idx) => {
|
|
8194
|
+
const id = colId(column.id, idsList.map((id) => id.domain));
|
|
8195
|
+
const label = readAnnotation(column.spec, Annotation.Label) ?? '';
|
|
8196
|
+
const labelDomainPart = ([...addedByVariantsDomainValues[idx]])
|
|
8197
|
+
.filter((str) => addedNotToAllVariantsDomainValues.has(str))
|
|
8198
|
+
.sort()
|
|
8199
|
+
.map((v) => JSON.parse(v)?.[1]) // use in labels only domain values, but sort them by key to save the same order in all column variants
|
|
8200
|
+
.join(' / ');
|
|
8201
|
+
const annotations = {
|
|
8202
|
+
...column.spec.annotations,
|
|
8203
|
+
[Annotation.Graph.IsVirtual]: stringifyJson(true),
|
|
8204
|
+
};
|
|
8205
|
+
if (label || labelDomainPart) {
|
|
8206
|
+
annotations[Annotation.Label] = label && labelDomainPart ? label + ' / ' + labelDomainPart : label + labelDomainPart;
|
|
8207
|
+
}
|
|
8208
|
+
return {
|
|
8209
|
+
...column,
|
|
8210
|
+
id: id,
|
|
8211
|
+
spec: {
|
|
8212
|
+
...column.spec,
|
|
8213
|
+
axesSpec: idsList.map((axisId, idx) => ({
|
|
8214
|
+
...axisId,
|
|
8215
|
+
annotations: column.spec.axesSpec[idx].annotations,
|
|
8216
|
+
})),
|
|
8217
|
+
annotations,
|
|
8218
|
+
},
|
|
8219
|
+
};
|
|
8220
|
+
});
|
|
8221
|
+
return [column, ...additionalColumns];
|
|
8222
|
+
}
|
|
8223
|
+
/**
|
|
8224
|
+
The aim of createPFrameForGraphs: to create pframe with block’s columns and all compatible columns from result pool
|
|
8225
|
+
(including linker columns and all label columns).
|
|
8226
|
+
Block’s columns are added to pframe as is.
|
|
8227
|
+
Other columns are added basing on set of axes of block’s columns, considering available with linker columns.
|
|
8228
|
+
Compatible columns must have at least one axis from block’s axes set. This axis of the compatible column from
|
|
8229
|
+
result pool must satisfy matchAxisId (it can have less domain keys than in block’s axis, but without conflicting values
|
|
8230
|
+
among existing ones).
|
|
8231
|
+
In requests to pframe (calculateTableData) columns must have strictly the same axes. For compatibility in case
|
|
8232
|
+
of partially matched axis we add to pframe a copy of this column with modified axis (with filled missed domains)
|
|
8233
|
+
and modified label (with added domain values in case if more than one copy with different domains exist).
|
|
8234
|
+
*/
|
|
8235
|
+
function createPFrameForGraphs(ctx, blockColumns) {
|
|
8236
|
+
const suitableSpec = (spec) => !isHiddenFromUIColumn(spec) && !isHiddenFromGraphColumn(spec);
|
|
8237
|
+
// if current block doesn't produce own columns then use all columns from result pool
|
|
8238
|
+
if (!blockColumns) {
|
|
8239
|
+
return ctx.createPFrame(getAllRelatedColumns(ctx, suitableSpec));
|
|
8240
|
+
}
|
|
8241
|
+
return ctx.createPFrame(getRelatedColumns(ctx, { columns: blockColumns, predicate: suitableSpec }));
|
|
8242
|
+
}
|
|
8243
|
+
|
|
7647
8244
|
function makeDefaultPTableParams() {
|
|
7648
8245
|
return {
|
|
7649
8246
|
sourceId: null,
|
|
@@ -7990,9 +8587,49 @@
|
|
|
7990
8587
|
errors: z.lazy(() => ErrorShape.array()).optional(),
|
|
7991
8588
|
});
|
|
7992
8589
|
|
|
8590
|
+
function getDefaultBlockLabel(data) {
|
|
8591
|
+
const parts = [];
|
|
8592
|
+
// Add file name if available
|
|
8593
|
+
if (data.fileName) {
|
|
8594
|
+
parts.push(data.fileName);
|
|
8595
|
+
}
|
|
8596
|
+
// Add similarity type label
|
|
8597
|
+
const similarityLabel = data.similarityType === 'alignment-score' ? 'BLOSUM' : 'Exact Match';
|
|
8598
|
+
if (similarityLabel) {
|
|
8599
|
+
parts.push(similarityLabel);
|
|
8600
|
+
}
|
|
8601
|
+
// Add identity threshold
|
|
8602
|
+
parts.push(`ident:${data.identity}`);
|
|
8603
|
+
// Add coverage threshold
|
|
8604
|
+
parts.push(`cov:${data.coverageThreshold}`);
|
|
8605
|
+
return parts.filter(Boolean).join(', ');
|
|
8606
|
+
}
|
|
8607
|
+
|
|
8608
|
+
function getColumns(ctx) {
|
|
8609
|
+
const anchor = ctx.args.datasetRef;
|
|
8610
|
+
if (anchor === undefined)
|
|
8611
|
+
return undefined;
|
|
8612
|
+
const anchorSpec = ctx.resultPool.getPColumnSpecByRef(anchor);
|
|
8613
|
+
if (anchorSpec === undefined)
|
|
8614
|
+
return undefined;
|
|
8615
|
+
// all clone properties
|
|
8616
|
+
const props = (ctx.resultPool.getAnchoredPColumns({ main: anchor }, [
|
|
8617
|
+
{
|
|
8618
|
+
axes: [{ anchor: 'main', idx: 1 }],
|
|
8619
|
+
},
|
|
8620
|
+
]) ?? [])
|
|
8621
|
+
.filter((p) => p.spec.annotations?.['pl7.app/sequence/isAnnotation'] !== 'true');
|
|
8622
|
+
return {
|
|
8623
|
+
props: props,
|
|
8624
|
+
};
|
|
8625
|
+
}
|
|
7993
8626
|
const model = BlockModel.create()
|
|
7994
8627
|
.withArgs({
|
|
7995
|
-
defaultBlockLabel:
|
|
8628
|
+
defaultBlockLabel: getDefaultBlockLabel({
|
|
8629
|
+
similarityType: 'alignment-score',
|
|
8630
|
+
identity: 0.9,
|
|
8631
|
+
coverageThreshold: 0.95,
|
|
8632
|
+
}),
|
|
7996
8633
|
customBlockLabel: '',
|
|
7997
8634
|
settings: {
|
|
7998
8635
|
coverageThreshold: 0.95, // default value matching MMseqs2 default
|
|
@@ -8003,6 +8640,7 @@
|
|
|
8003
8640
|
})
|
|
8004
8641
|
.withUiState({
|
|
8005
8642
|
tableState: createPlDataTableStateV2(),
|
|
8643
|
+
alignmentModel: {},
|
|
8006
8644
|
})
|
|
8007
8645
|
.argsValid((ctx) => ctx.args.datasetRef !== undefined
|
|
8008
8646
|
&& ctx.args.fileHandle !== undefined
|
|
@@ -8067,6 +8705,42 @@
|
|
|
8067
8705
|
if (cols === undefined)
|
|
8068
8706
|
return undefined;
|
|
8069
8707
|
return createPlDataTableV2(ctx, cols, ctx.uiState.tableState);
|
|
8708
|
+
})
|
|
8709
|
+
.output('pf', (ctx) => {
|
|
8710
|
+
if (ctx.outputs?.resolve('emptyResults')?.getDataAsJson()) {
|
|
8711
|
+
return undefined;
|
|
8712
|
+
}
|
|
8713
|
+
const cols = ctx.outputs?.resolve('table')?.getPColumns();
|
|
8714
|
+
if (cols === undefined)
|
|
8715
|
+
return undefined;
|
|
8716
|
+
return createPFrameForGraphs(ctx, cols);
|
|
8717
|
+
})
|
|
8718
|
+
.output('assaySequenceSpec', (ctx) => {
|
|
8719
|
+
if (ctx.outputs?.resolve('emptyResults')?.getDataAsJson()) {
|
|
8720
|
+
return undefined;
|
|
8721
|
+
}
|
|
8722
|
+
const cols = ctx.outputs?.resolve('table')?.getPColumns();
|
|
8723
|
+
if (cols === undefined)
|
|
8724
|
+
return undefined;
|
|
8725
|
+
// Return only sequence column
|
|
8726
|
+
return cols.find((c) => c.spec.name === 'pl7.app/vdj/sequence'
|
|
8727
|
+
&& c.spec.axesSpec[0].name === 'pl7.app/vdj/assay/sequenceId')?.spec;
|
|
8728
|
+
})
|
|
8729
|
+
.output('msaPf', (ctx) => {
|
|
8730
|
+
if (ctx.outputs?.resolve('emptyResults')?.getDataAsJson()) {
|
|
8731
|
+
return undefined;
|
|
8732
|
+
}
|
|
8733
|
+
const cols = ctx.outputs?.resolve('table')?.getPColumns();
|
|
8734
|
+
if (cols === undefined)
|
|
8735
|
+
return undefined;
|
|
8736
|
+
const msaCols = ctx.outputs?.resolve('assayLinkerPframe')?.getPColumns();
|
|
8737
|
+
if (!msaCols)
|
|
8738
|
+
return undefined;
|
|
8739
|
+
const columns = getColumns(ctx);
|
|
8740
|
+
if (columns === undefined) {
|
|
8741
|
+
return undefined;
|
|
8742
|
+
}
|
|
8743
|
+
return createPFrameForGraphs(ctx, [...msaCols, ...cols, ...columns.props]);
|
|
8070
8744
|
})
|
|
8071
8745
|
.output('isRunning', (ctx) => ctx.outputs?.getIsReadyOrError() === false)
|
|
8072
8746
|
.title(() => 'Immune Assay Data')
|
|
@@ -8076,6 +8750,7 @@
|
|
|
8076
8750
|
]))
|
|
8077
8751
|
.done(2);
|
|
8078
8752
|
|
|
8753
|
+
exports.getDefaultBlockLabel = getDefaultBlockLabel;
|
|
8079
8754
|
exports.model = model;
|
|
8080
8755
|
|
|
8081
8756
|
}));
|