@milaboratories/milaboratories.pool-explorer.model 1.0.125 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +3 -3
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +18 -0
- package/dist/bundle.js +662 -181
- 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 +7 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/model.json +1 -1
- package/package.json +5 -5
- package/src/index.ts +11 -5
package/dist/bundle.js
CHANGED
|
@@ -4,14 +4,84 @@
|
|
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["block-model"] = {}));
|
|
5
5
|
})(this, (function (exports) { 'use strict';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
+
* Type guard to check if a value is a valid BlockStorage object.
|
|
32
|
+
* Checks for the discriminator key and valid schema version.
|
|
33
|
+
*/
|
|
34
|
+
function isBlockStorage(value) {
|
|
35
|
+
if (value === null || typeof value !== 'object')
|
|
36
|
+
return false;
|
|
37
|
+
const obj = value;
|
|
38
|
+
const schemaVersion = obj[BLOCK_STORAGE_KEY];
|
|
39
|
+
// Currently only 'v1' is valid, but this allows future versions
|
|
40
|
+
return schemaVersion === 'v1'; // Add more versions as schema evolves
|
|
41
|
+
}
|
|
42
|
+
// =============================================================================
|
|
43
|
+
// Factory Functions
|
|
44
|
+
// =============================================================================
|
|
45
|
+
/**
|
|
46
|
+
* Creates a BlockStorage with the given initial data
|
|
47
|
+
*
|
|
48
|
+
* @param initialData - The initial data value (defaults to empty object)
|
|
49
|
+
* @param version - The initial data version (defaults to 1)
|
|
50
|
+
* @returns A new BlockStorage instance with discriminator key
|
|
51
|
+
*/
|
|
52
|
+
function createBlockStorage(initialData = {}, version = 1) {
|
|
53
|
+
return {
|
|
54
|
+
[BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,
|
|
55
|
+
__dataVersion: version,
|
|
56
|
+
__data: initialData,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// =============================================================================
|
|
60
|
+
// Data Access & Update Functions
|
|
61
|
+
// =============================================================================
|
|
62
|
+
/**
|
|
63
|
+
* Gets the data from BlockStorage
|
|
64
|
+
*
|
|
65
|
+
* @param storage - The BlockStorage instance
|
|
66
|
+
* @returns The data value
|
|
67
|
+
*/
|
|
68
|
+
function getStorageData(storage) {
|
|
69
|
+
return storage.__data;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Updates the data in BlockStorage (immutable)
|
|
73
|
+
*
|
|
74
|
+
* @param storage - The current BlockStorage
|
|
75
|
+
* @param payload - The update payload with operation and value
|
|
76
|
+
* @returns A new BlockStorage with updated data
|
|
77
|
+
*/
|
|
78
|
+
function updateStorageData(storage, payload) {
|
|
79
|
+
switch (payload.operation) {
|
|
80
|
+
case 'update-data':
|
|
81
|
+
return { ...storage, __data: payload.value };
|
|
82
|
+
default:
|
|
83
|
+
throw new Error(`Unknown storage operation: ${payload.operation}`);
|
|
84
|
+
}
|
|
15
85
|
}
|
|
16
86
|
|
|
17
87
|
/** Utility code helping to identify whether the code is running in actual UI environment */
|
|
@@ -48,6 +118,41 @@
|
|
|
48
118
|
ctx.callbackRegistry[key] = callback;
|
|
49
119
|
return true;
|
|
50
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Registers a callback, replacing any existing callback with the same key.
|
|
123
|
+
* Use this for callbacks that have a default value but can be overridden.
|
|
124
|
+
*
|
|
125
|
+
* @param key - The callback registry key
|
|
126
|
+
* @param callback - The callback function to register
|
|
127
|
+
* @returns true if registered, false if not in render context
|
|
128
|
+
*/
|
|
129
|
+
function replaceCallback(key, callback) {
|
|
130
|
+
const ctx = tryGetCfgRenderCtx();
|
|
131
|
+
if (ctx === undefined)
|
|
132
|
+
return false;
|
|
133
|
+
ctx.callbackRegistry[key] = callback;
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
/** Creates a ConfigRenderLambda descriptor without registering a callback. */
|
|
137
|
+
function createRenderLambda(opts) {
|
|
138
|
+
const { handle, ...flags } = opts;
|
|
139
|
+
return {
|
|
140
|
+
__renderLambda: true,
|
|
141
|
+
handle,
|
|
142
|
+
...flags,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/** Registers a callback and returns a ConfigRenderLambda descriptor. */
|
|
146
|
+
function createAndRegisterRenderLambda(opts, replace) {
|
|
147
|
+
const { handle, lambda, ...flags } = opts;
|
|
148
|
+
if (replace) {
|
|
149
|
+
replaceCallback(handle, lambda);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
tryRegisterCallback(handle, lambda);
|
|
153
|
+
}
|
|
154
|
+
return createRenderLambda({ handle, ...flags });
|
|
155
|
+
}
|
|
51
156
|
const futureResolves = new Map();
|
|
52
157
|
function registerFutureAwait(handle, onResolve) {
|
|
53
158
|
if (!(handle in getCfgRenderCtx().callbackRegistry)) {
|
|
@@ -6088,10 +6193,15 @@
|
|
|
6088
6193
|
}
|
|
6089
6194
|
return result;
|
|
6090
6195
|
};
|
|
6091
|
-
|
|
6092
|
-
const hasUniqueLabels = (result) => result !== undefined && new Set(result.map((c) => c.label)).size === values.length;
|
|
6196
|
+
const countUniqueLabels = (result) => result === undefined ? 0 : new Set(result.map((c) => c.label)).size;
|
|
6093
6197
|
// Post-processing: try removing types one by one (lowest importance first) to minimize the label set
|
|
6198
|
+
// Accepts removal if it doesn't decrease the number of unique labels (cardinality)
|
|
6094
6199
|
const minimizeTypeSet = (typeSet) => {
|
|
6200
|
+
const initialResult = calculate(typeSet);
|
|
6201
|
+
if (initialResult === undefined) {
|
|
6202
|
+
return typeSet;
|
|
6203
|
+
}
|
|
6204
|
+
const currentCardinality = countUniqueLabels(initialResult);
|
|
6095
6205
|
// Get types sorted by importance ascending (lowest first), excluding forced elements
|
|
6096
6206
|
const removableSorted = [...typeSet]
|
|
6097
6207
|
.filter((t) => !forceTraceElements?.has(t.split('@')[0])
|
|
@@ -6101,7 +6211,7 @@
|
|
|
6101
6211
|
const reducedSet = new Set(typeSet);
|
|
6102
6212
|
reducedSet.delete(typeToRemove);
|
|
6103
6213
|
const candidateResult = calculate(reducedSet);
|
|
6104
|
-
if (
|
|
6214
|
+
if (candidateResult !== undefined && countUniqueLabels(candidateResult) >= currentCardinality) {
|
|
6105
6215
|
typeSet.delete(typeToRemove);
|
|
6106
6216
|
}
|
|
6107
6217
|
}
|
|
@@ -6132,7 +6242,7 @@
|
|
|
6132
6242
|
if (additionalType >= 0)
|
|
6133
6243
|
currentSet.add(mainTypes[additionalType]);
|
|
6134
6244
|
const candidateResult = calculate(currentSet);
|
|
6135
|
-
if (
|
|
6245
|
+
if (candidateResult !== undefined && countUniqueLabels(candidateResult) === values.length) {
|
|
6136
6246
|
minimizeTypeSet(currentSet);
|
|
6137
6247
|
return calculate(currentSet);
|
|
6138
6248
|
}
|
|
@@ -7248,28 +7358,19 @@
|
|
|
7248
7358
|
}
|
|
7249
7359
|
}
|
|
7250
7360
|
/** Main entry point to the API available within model lambdas (like outputs, sections, etc..) */
|
|
7251
|
-
class
|
|
7361
|
+
class RenderCtxBase {
|
|
7252
7362
|
ctx;
|
|
7253
7363
|
constructor() {
|
|
7254
7364
|
this.ctx = getCfgRenderCtx();
|
|
7255
7365
|
}
|
|
7256
|
-
|
|
7257
|
-
get
|
|
7258
|
-
if (this.
|
|
7259
|
-
const raw = this.ctx.
|
|
7260
|
-
const value = typeof raw === 'function' ? raw() : raw;
|
|
7261
|
-
this._argsCache = { v: JSON.parse(value) };
|
|
7262
|
-
}
|
|
7263
|
-
return this._argsCache.v;
|
|
7264
|
-
}
|
|
7265
|
-
_uiStateCache;
|
|
7266
|
-
get uiState() {
|
|
7267
|
-
if (this._uiStateCache === undefined) {
|
|
7268
|
-
const raw = this.ctx.uiState;
|
|
7366
|
+
_dataCache;
|
|
7367
|
+
get data() {
|
|
7368
|
+
if (this._dataCache === undefined) {
|
|
7369
|
+
const raw = this.ctx.data;
|
|
7269
7370
|
const value = typeof raw === 'function' ? raw() : raw;
|
|
7270
|
-
this.
|
|
7371
|
+
this._dataCache = { v: value ? JSON.parse(value) : {} };
|
|
7271
7372
|
}
|
|
7272
|
-
return this.
|
|
7373
|
+
return this._dataCache.v;
|
|
7273
7374
|
}
|
|
7274
7375
|
// lazy rendering because this feature is rarely used
|
|
7275
7376
|
_activeArgsCache;
|
|
@@ -7380,8 +7481,21 @@
|
|
|
7380
7481
|
this.ctx.logError(msg);
|
|
7381
7482
|
}
|
|
7382
7483
|
}
|
|
7484
|
+
/** Main entry point to the API available within model lambdas (like outputs, sections, etc..) for v3+ blocks */
|
|
7485
|
+
class RenderCtx extends RenderCtxBase {
|
|
7486
|
+
_argsCache;
|
|
7487
|
+
get args() {
|
|
7488
|
+
if (this._argsCache === undefined) {
|
|
7489
|
+
const raw = this.ctx.args;
|
|
7490
|
+
const value = typeof raw === 'function' ? raw() : raw;
|
|
7491
|
+
// args can be undefined when derivation fails (e.g., validation error in args())
|
|
7492
|
+
this._argsCache = { v: value === undefined ? undefined : JSON.parse(value) };
|
|
7493
|
+
}
|
|
7494
|
+
return this._argsCache.v;
|
|
7495
|
+
}
|
|
7496
|
+
}
|
|
7383
7497
|
|
|
7384
|
-
var version = "1.
|
|
7498
|
+
var version = "1.52.0";
|
|
7385
7499
|
|
|
7386
7500
|
const PlatformaSDKVersion = version;
|
|
7387
7501
|
|
|
@@ -7397,56 +7511,315 @@
|
|
|
7397
7511
|
return data;
|
|
7398
7512
|
}
|
|
7399
7513
|
|
|
7514
|
+
/**
|
|
7515
|
+
* BlockStorage VM Integration - Internal module for VM-based storage operations.
|
|
7516
|
+
*
|
|
7517
|
+
* This module auto-registers internal callbacks that the middle layer can invoke
|
|
7518
|
+
* to perform storage transformations. Block developers never interact with these
|
|
7519
|
+
* directly - they only see `state`.
|
|
7520
|
+
*
|
|
7521
|
+
* Registered callbacks (all prefixed with `__pl_` for internal SDK use):
|
|
7522
|
+
* - `__pl_storage_normalize`: (rawStorage) => { storage, data }
|
|
7523
|
+
* - `__pl_storage_applyUpdate`: (currentStorageJson, payload) => updatedStorageJson
|
|
7524
|
+
* - `__pl_storage_getInfo`: (rawStorage) => JSON string with storage info
|
|
7525
|
+
* - `__pl_storage_migrate`: (currentStorageJson) => MigrationResult
|
|
7526
|
+
* - `__pl_args_derive`: (storageJson) => ArgsDeriveResult
|
|
7527
|
+
* - `__pl_prerunArgs_derive`: (storageJson) => ArgsDeriveResult
|
|
7528
|
+
*
|
|
7529
|
+
* Callbacks registered by DataModel.registerCallbacks():
|
|
7530
|
+
* - `__pl_data_initial`: () => initial data
|
|
7531
|
+
* - `__pl_data_upgrade`: (versioned) => UpgradeResult
|
|
7532
|
+
* - `__pl_storage_initial`: () => initial BlockStorage as JSON string
|
|
7533
|
+
*
|
|
7534
|
+
* @module block_storage_vm
|
|
7535
|
+
* @internal
|
|
7536
|
+
*/
|
|
7537
|
+
/**
|
|
7538
|
+
* Normalizes raw storage data and extracts state.
|
|
7539
|
+
* Handles all formats:
|
|
7540
|
+
* - New BlockStorage format (has discriminator)
|
|
7541
|
+
* - Legacy V1/V2 format ({ args, uiState })
|
|
7542
|
+
* - Raw V3 state (any other format)
|
|
7543
|
+
*
|
|
7544
|
+
* @param rawStorage - Raw data from blockStorage field (may be JSON string or object)
|
|
7545
|
+
* @returns Object with normalized storage and extracted state
|
|
7546
|
+
*/
|
|
7547
|
+
function normalizeStorage(rawStorage) {
|
|
7548
|
+
// Handle undefined/null
|
|
7549
|
+
if (rawStorage === undefined || rawStorage === null) {
|
|
7550
|
+
const storage = createBlockStorage({});
|
|
7551
|
+
return { storage, data: {} };
|
|
7552
|
+
}
|
|
7553
|
+
// Parse JSON string if needed
|
|
7554
|
+
let parsed = rawStorage;
|
|
7555
|
+
if (typeof rawStorage === 'string') {
|
|
7556
|
+
try {
|
|
7557
|
+
parsed = JSON.parse(rawStorage);
|
|
7558
|
+
}
|
|
7559
|
+
catch {
|
|
7560
|
+
// If parsing fails, treat string as the data
|
|
7561
|
+
const storage = createBlockStorage(rawStorage);
|
|
7562
|
+
return { storage, data: rawStorage };
|
|
7563
|
+
}
|
|
7564
|
+
}
|
|
7565
|
+
// Check for BlockStorage format (has discriminator)
|
|
7566
|
+
if (isBlockStorage(parsed)) {
|
|
7567
|
+
return { storage: parsed, data: getStorageData(parsed) };
|
|
7568
|
+
}
|
|
7569
|
+
// Check for legacy V1/V2 format: { args, uiState }
|
|
7570
|
+
if (isLegacyModelV1ApiFormat(parsed)) {
|
|
7571
|
+
// For legacy format, the whole object IS the data
|
|
7572
|
+
const storage = createBlockStorage(parsed);
|
|
7573
|
+
return { storage, data: parsed };
|
|
7574
|
+
}
|
|
7575
|
+
// Raw V3 data - wrap it
|
|
7576
|
+
const storage = createBlockStorage(parsed);
|
|
7577
|
+
return { storage, data: parsed };
|
|
7578
|
+
}
|
|
7579
|
+
/**
|
|
7580
|
+
* Applies a state update to existing storage.
|
|
7581
|
+
* Used when setData is called from the frontend.
|
|
7582
|
+
*
|
|
7583
|
+
* @param currentStorageJson - Current storage as JSON string (must be defined)
|
|
7584
|
+
* @param newData - New data from application
|
|
7585
|
+
* @returns Updated storage as JSON string
|
|
7586
|
+
*/
|
|
7587
|
+
function applyStorageUpdate(currentStorageJson, payload) {
|
|
7588
|
+
const { storage: currentStorage } = normalizeStorage(currentStorageJson);
|
|
7589
|
+
// Update data while preserving other storage fields (version, plugins)
|
|
7590
|
+
const updatedStorage = updateStorageData(currentStorage, payload);
|
|
7591
|
+
return JSON.stringify(updatedStorage);
|
|
7592
|
+
}
|
|
7593
|
+
/**
|
|
7594
|
+
* Checks if data is in legacy Model API v1 format.
|
|
7595
|
+
* Legacy format has { args, uiState? } at top level without the BlockStorage discriminator.
|
|
7596
|
+
*/
|
|
7597
|
+
function isLegacyModelV1ApiFormat(data) {
|
|
7598
|
+
if (data === null || typeof data !== 'object')
|
|
7599
|
+
return false;
|
|
7600
|
+
if (isBlockStorage(data))
|
|
7601
|
+
return false;
|
|
7602
|
+
const obj = data;
|
|
7603
|
+
return 'args' in obj;
|
|
7604
|
+
}
|
|
7605
|
+
// =============================================================================
|
|
7606
|
+
// Auto-register internal callbacks when module is loaded in VM
|
|
7607
|
+
// =============================================================================
|
|
7608
|
+
// Register normalize callback
|
|
7609
|
+
tryRegisterCallback('__pl_storage_normalize', (rawStorage) => {
|
|
7610
|
+
return normalizeStorage(rawStorage);
|
|
7611
|
+
});
|
|
7612
|
+
// Register apply update callback (requires existing storage)
|
|
7613
|
+
tryRegisterCallback('__pl_storage_applyUpdate', (currentStorageJson, payload) => {
|
|
7614
|
+
return applyStorageUpdate(currentStorageJson, payload);
|
|
7615
|
+
});
|
|
7616
|
+
/**
|
|
7617
|
+
* Gets storage info from raw storage data.
|
|
7618
|
+
* Returns structured info about the storage state.
|
|
7619
|
+
*
|
|
7620
|
+
* @param rawStorage - Raw data from blockStorage field (may be JSON string or object)
|
|
7621
|
+
* @returns JSON string with storage info
|
|
7622
|
+
*/
|
|
7623
|
+
function getStorageInfo(rawStorage) {
|
|
7624
|
+
const { storage } = normalizeStorage(rawStorage);
|
|
7625
|
+
const info = {
|
|
7626
|
+
dataVersion: storage.__dataVersion,
|
|
7627
|
+
};
|
|
7628
|
+
return JSON.stringify(info);
|
|
7629
|
+
}
|
|
7630
|
+
// Register get info callback
|
|
7631
|
+
tryRegisterCallback('__pl_storage_getInfo', (rawStorage) => {
|
|
7632
|
+
return getStorageInfo(rawStorage);
|
|
7633
|
+
});
|
|
7634
|
+
/**
|
|
7635
|
+
* Runs storage migration using the DataModel's upgrade callback.
|
|
7636
|
+
* This is the main entry point for the middle layer to trigger migrations.
|
|
7637
|
+
*
|
|
7638
|
+
* Uses the '__pl_data_upgrade' callback registered by DataModel.registerCallbacks() which:
|
|
7639
|
+
* - Handles all migration logic internally
|
|
7640
|
+
* - Returns { version, data, warning? } - warning present if reset to initial data
|
|
7641
|
+
*
|
|
7642
|
+
* @param currentStorageJson - Current storage as JSON string (or undefined)
|
|
7643
|
+
* @returns MigrationResult
|
|
7644
|
+
*/
|
|
7645
|
+
function migrateStorage(currentStorageJson) {
|
|
7646
|
+
// Get the callback registry context
|
|
7647
|
+
const ctx = tryGetCfgRenderCtx();
|
|
7648
|
+
if (ctx === undefined) {
|
|
7649
|
+
return { error: 'Not in config rendering context' };
|
|
7650
|
+
}
|
|
7651
|
+
// Normalize storage to get current data and version
|
|
7652
|
+
const { storage: currentStorage, data: currentData } = normalizeStorage(currentStorageJson);
|
|
7653
|
+
const currentVersion = currentStorage.__dataVersion;
|
|
7654
|
+
// Helper to create storage with given data and version
|
|
7655
|
+
const createStorageJson = (data, version) => {
|
|
7656
|
+
return JSON.stringify({
|
|
7657
|
+
...currentStorage,
|
|
7658
|
+
__dataVersion: version,
|
|
7659
|
+
__data: data,
|
|
7660
|
+
});
|
|
7661
|
+
};
|
|
7662
|
+
// Get the upgrade callback (registered by DataModel.registerCallbacks())
|
|
7663
|
+
const upgradeCallback = ctx.callbackRegistry['__pl_data_upgrade'];
|
|
7664
|
+
if (typeof upgradeCallback !== 'function') {
|
|
7665
|
+
return { error: '__pl_data_upgrade callback not found (DataModel not registered)' };
|
|
7666
|
+
}
|
|
7667
|
+
// Call the migrator's upgrade function
|
|
7668
|
+
let result;
|
|
7669
|
+
try {
|
|
7670
|
+
result = upgradeCallback({ version: currentVersion, data: currentData });
|
|
7671
|
+
}
|
|
7672
|
+
catch (e) {
|
|
7673
|
+
const errorMsg = e instanceof Error ? e.message : String(e);
|
|
7674
|
+
return { error: `upgrade() threw: ${errorMsg}` };
|
|
7675
|
+
}
|
|
7676
|
+
// Build info message
|
|
7677
|
+
const info = result.version === currentVersion
|
|
7678
|
+
? `No migration needed (v${currentVersion})`
|
|
7679
|
+
: result.warning
|
|
7680
|
+
? `Reset to initial data (v${result.version})`
|
|
7681
|
+
: `Migrated v${currentVersion}→v${result.version}`;
|
|
7682
|
+
return {
|
|
7683
|
+
newStorageJson: createStorageJson(result.data, result.version),
|
|
7684
|
+
info,
|
|
7685
|
+
warn: result.warning,
|
|
7686
|
+
};
|
|
7687
|
+
}
|
|
7688
|
+
// Register migrate callback
|
|
7689
|
+
tryRegisterCallback('__pl_storage_migrate', (currentStorageJson) => {
|
|
7690
|
+
return migrateStorage(currentStorageJson);
|
|
7691
|
+
});
|
|
7692
|
+
/**
|
|
7693
|
+
* Derives args from storage using the registered 'args' callback.
|
|
7694
|
+
* This extracts data from storage and passes it to the block's args() function.
|
|
7695
|
+
*
|
|
7696
|
+
* @param storageJson - Storage as JSON string
|
|
7697
|
+
* @returns ArgsDeriveResult with derived args or error
|
|
7698
|
+
*/
|
|
7699
|
+
function deriveArgsFromStorage(storageJson) {
|
|
7700
|
+
const ctx = tryGetCfgRenderCtx();
|
|
7701
|
+
if (ctx === undefined) {
|
|
7702
|
+
return { error: 'Not in config rendering context' };
|
|
7703
|
+
}
|
|
7704
|
+
// Extract data from storage
|
|
7705
|
+
const { data } = normalizeStorage(storageJson);
|
|
7706
|
+
// Get the args callback (registered by BlockModelV3.args())
|
|
7707
|
+
const argsCallback = ctx.callbackRegistry['args'];
|
|
7708
|
+
if (typeof argsCallback !== 'function') {
|
|
7709
|
+
return { error: 'args callback not found' };
|
|
7710
|
+
}
|
|
7711
|
+
// Call the args callback with extracted data
|
|
7712
|
+
try {
|
|
7713
|
+
const result = argsCallback(data);
|
|
7714
|
+
return { value: result };
|
|
7715
|
+
}
|
|
7716
|
+
catch (e) {
|
|
7717
|
+
const errorMsg = e instanceof Error ? e.message : String(e);
|
|
7718
|
+
return { error: `args() threw: ${errorMsg}` };
|
|
7719
|
+
}
|
|
7720
|
+
}
|
|
7721
|
+
// Register args derivation callback
|
|
7722
|
+
tryRegisterCallback('__pl_args_derive', (storageJson) => {
|
|
7723
|
+
return deriveArgsFromStorage(storageJson);
|
|
7724
|
+
});
|
|
7725
|
+
/**
|
|
7726
|
+
* Derives prerunArgs from storage using the registered 'prerunArgs' callback.
|
|
7727
|
+
* Falls back to 'args' callback if 'prerunArgs' is not defined.
|
|
7728
|
+
*
|
|
7729
|
+
* @param storageJson - Storage as JSON string
|
|
7730
|
+
* @returns ArgsDeriveResult with derived prerunArgs or error
|
|
7731
|
+
*/
|
|
7732
|
+
function derivePrerunArgsFromStorage(storageJson) {
|
|
7733
|
+
const ctx = tryGetCfgRenderCtx();
|
|
7734
|
+
if (ctx === undefined) {
|
|
7735
|
+
return { error: 'Not in config rendering context' };
|
|
7736
|
+
}
|
|
7737
|
+
// Extract data from storage
|
|
7738
|
+
const { data } = normalizeStorage(storageJson);
|
|
7739
|
+
// Try prerunArgs callback first
|
|
7740
|
+
const prerunArgsCallback = ctx.callbackRegistry['prerunArgs'];
|
|
7741
|
+
if (typeof prerunArgsCallback === 'function') {
|
|
7742
|
+
try {
|
|
7743
|
+
const result = prerunArgsCallback(data);
|
|
7744
|
+
return { value: result };
|
|
7745
|
+
}
|
|
7746
|
+
catch (e) {
|
|
7747
|
+
const errorMsg = e instanceof Error ? e.message : String(e);
|
|
7748
|
+
return { error: `prerunArgs() threw: ${errorMsg}` };
|
|
7749
|
+
}
|
|
7750
|
+
}
|
|
7751
|
+
// Fall back to args callback
|
|
7752
|
+
const argsCallback = ctx.callbackRegistry['args'];
|
|
7753
|
+
if (typeof argsCallback !== 'function') {
|
|
7754
|
+
return { error: 'args callback not found (fallback from missing prerunArgs)' };
|
|
7755
|
+
}
|
|
7756
|
+
try {
|
|
7757
|
+
const result = argsCallback(data);
|
|
7758
|
+
return { value: result };
|
|
7759
|
+
}
|
|
7760
|
+
catch (e) {
|
|
7761
|
+
const errorMsg = e instanceof Error ? e.message : String(e);
|
|
7762
|
+
return { error: `args() threw (fallback): ${errorMsg}` };
|
|
7763
|
+
}
|
|
7764
|
+
}
|
|
7765
|
+
// Register prerunArgs derivation callback
|
|
7766
|
+
tryRegisterCallback('__pl_prerunArgs_derive', (storageJson) => {
|
|
7767
|
+
return derivePrerunArgsFromStorage(storageJson);
|
|
7768
|
+
});
|
|
7769
|
+
|
|
7400
7770
|
/** Main entry point that each block should use in it's "config" module. Don't forget
|
|
7401
7771
|
* to call {@link done()} at the end of configuration. Value returned by this builder must be
|
|
7402
|
-
* exported as constant with name "platforma" from the "config" module.
|
|
7403
|
-
|
|
7772
|
+
* exported as constant with name "platforma" from the "config" module.
|
|
7773
|
+
* API version is 3 (for UI) and 2 (for model) */
|
|
7774
|
+
class BlockModelV3 {
|
|
7404
7775
|
config;
|
|
7405
7776
|
constructor(config) {
|
|
7406
7777
|
this.config = config;
|
|
7407
7778
|
}
|
|
7408
|
-
static
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
|
|
7779
|
+
static INITIAL_BLOCK_FEATURE_FLAGS = {
|
|
7780
|
+
supportsLazyState: true,
|
|
7781
|
+
requiresUIAPIVersion: 3,
|
|
7782
|
+
requiresModelAPIVersion: 2,
|
|
7783
|
+
};
|
|
7784
|
+
/**
|
|
7785
|
+
* Creates a new BlockModelV3 builder with the specified data model and options.
|
|
7786
|
+
*
|
|
7787
|
+
* @example
|
|
7788
|
+
* const dataModel = DataModel.create<BlockData>(() => ({ numbers: [], labels: [] }));
|
|
7789
|
+
* BlockModelV3.create({ dataModel })
|
|
7790
|
+
* .args((data) => ({ numbers: data.numbers }))
|
|
7791
|
+
* .sections(() => [{ type: 'link', href: '/', label: 'Main' }])
|
|
7792
|
+
* .done();
|
|
7793
|
+
*
|
|
7794
|
+
* @param options Configuration options including required data model
|
|
7795
|
+
*/
|
|
7796
|
+
static create(options) {
|
|
7797
|
+
const { dataModel, renderingMode = 'Heavy' } = options;
|
|
7798
|
+
// Register data model callbacks for VM use (initialData and upgrade)
|
|
7799
|
+
dataModel.registerCallbacks();
|
|
7800
|
+
return new BlockModelV3({
|
|
7417
7801
|
renderingMode,
|
|
7418
|
-
|
|
7802
|
+
dataModel,
|
|
7419
7803
|
outputs: {},
|
|
7420
|
-
|
|
7421
|
-
sections:
|
|
7422
|
-
|
|
7804
|
+
// Register default sections callback (returns empty array)
|
|
7805
|
+
sections: createAndRegisterRenderLambda({ handle: 'sections', lambda: () => [] }, true),
|
|
7806
|
+
title: undefined,
|
|
7807
|
+
subtitle: undefined,
|
|
7808
|
+
tags: undefined,
|
|
7809
|
+
enrichmentTargets: undefined,
|
|
7810
|
+
featureFlags: { ...BlockModelV3.INITIAL_BLOCK_FEATURE_FLAGS },
|
|
7811
|
+
args: undefined,
|
|
7812
|
+
prerunArgs: undefined,
|
|
7423
7813
|
});
|
|
7424
7814
|
}
|
|
7425
7815
|
output(key, cfgOrRf, flags = {}) {
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
7430
|
-
...
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
[key]: {
|
|
7434
|
-
__renderLambda: true,
|
|
7435
|
-
handle,
|
|
7436
|
-
...flags,
|
|
7437
|
-
},
|
|
7438
|
-
},
|
|
7439
|
-
});
|
|
7440
|
-
}
|
|
7441
|
-
else {
|
|
7442
|
-
return new BlockModel({
|
|
7443
|
-
...this.config,
|
|
7444
|
-
outputs: {
|
|
7445
|
-
...this.config.outputs,
|
|
7446
|
-
[key]: cfgOrRf,
|
|
7447
|
-
},
|
|
7448
|
-
});
|
|
7449
|
-
}
|
|
7816
|
+
return new BlockModelV3({
|
|
7817
|
+
...this.config,
|
|
7818
|
+
outputs: {
|
|
7819
|
+
...this.config.outputs,
|
|
7820
|
+
[key]: createAndRegisterRenderLambda({ handle: `output#${key}`, lambda: () => cfgOrRf(new RenderCtx()), ...flags }),
|
|
7821
|
+
},
|
|
7822
|
+
});
|
|
7450
7823
|
}
|
|
7451
7824
|
/** Shortcut for {@link output} with retentive flag set to true. */
|
|
7452
7825
|
retentiveOutput(key, rf) {
|
|
@@ -7456,109 +7829,83 @@
|
|
|
7456
7829
|
outputWithStatus(key, rf) {
|
|
7457
7830
|
return this.output(key, rf, { withStatus: true });
|
|
7458
7831
|
}
|
|
7459
|
-
/**
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
else {
|
|
7475
|
-
return new BlockModel({
|
|
7476
|
-
...this.config,
|
|
7477
|
-
inputsValid: cfgOrRf,
|
|
7478
|
-
});
|
|
7479
|
-
}
|
|
7480
|
-
}
|
|
7481
|
-
sections(arrOrCfgOrRf) {
|
|
7482
|
-
if (Array.isArray(arrOrCfgOrRf)) {
|
|
7483
|
-
return this.sections(getImmediate(arrOrCfgOrRf));
|
|
7484
|
-
}
|
|
7485
|
-
else if (typeof arrOrCfgOrRf === 'function') {
|
|
7486
|
-
tryRegisterCallback('sections', () => arrOrCfgOrRf(new RenderCtx()));
|
|
7487
|
-
return new BlockModel({
|
|
7488
|
-
...this.config,
|
|
7489
|
-
sections: {
|
|
7490
|
-
__renderLambda: true,
|
|
7491
|
-
handle: 'sections',
|
|
7492
|
-
},
|
|
7493
|
-
});
|
|
7494
|
-
}
|
|
7495
|
-
else {
|
|
7496
|
-
return new BlockModel({
|
|
7497
|
-
...this.config,
|
|
7498
|
-
sections: arrOrCfgOrRf,
|
|
7499
|
-
});
|
|
7500
|
-
}
|
|
7501
|
-
}
|
|
7502
|
-
/** Sets a rendering function to derive block title, shown for the block in the left blocks-overview panel. */
|
|
7503
|
-
title(rf) {
|
|
7504
|
-
tryRegisterCallback('title', () => rf(new RenderCtx()));
|
|
7505
|
-
return new BlockModel({
|
|
7832
|
+
/**
|
|
7833
|
+
* Sets a function to derive block args from data.
|
|
7834
|
+
* This is called during setData to compute the args that will be used for block execution.
|
|
7835
|
+
*
|
|
7836
|
+
* @example
|
|
7837
|
+
* .args<BlockArgs>((data) => ({ numbers: data.numbers }))
|
|
7838
|
+
*
|
|
7839
|
+
* @example
|
|
7840
|
+
* .args<BlockArgs>((data) => {
|
|
7841
|
+
* if (data.numbers.length === 0) throw new Error('Numbers required'); // block not ready
|
|
7842
|
+
* return { numbers: data.numbers };
|
|
7843
|
+
* })
|
|
7844
|
+
*/
|
|
7845
|
+
args(lambda) {
|
|
7846
|
+
return new BlockModelV3({
|
|
7506
7847
|
...this.config,
|
|
7507
|
-
|
|
7508
|
-
__renderLambda: true,
|
|
7509
|
-
handle: 'title',
|
|
7510
|
-
},
|
|
7848
|
+
args: createAndRegisterRenderLambda({ handle: 'args', lambda }),
|
|
7511
7849
|
});
|
|
7512
7850
|
}
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7851
|
+
/**
|
|
7852
|
+
* Sets a function to derive pre-run args from data (optional).
|
|
7853
|
+
* This is called during setData to compute the args that will be used for staging/pre-run phase.
|
|
7854
|
+
*
|
|
7855
|
+
* If not defined, defaults to using the args() function result.
|
|
7856
|
+
* If defined, uses its return value for the staging / prerun phase.
|
|
7857
|
+
*
|
|
7858
|
+
* The staging / prerun phase runs only if currentPrerunArgs differs from the executed
|
|
7859
|
+
* version of prerunArgs (same comparison logic as currentArgs vs prodArgs).
|
|
7860
|
+
*
|
|
7861
|
+
* @example
|
|
7862
|
+
* .prerunArgs((data) => ({ numbers: data.numbers }))
|
|
7863
|
+
*
|
|
7864
|
+
* @example
|
|
7865
|
+
* .prerunArgs((data) => {
|
|
7866
|
+
* // Return undefined to skip staging for this block
|
|
7867
|
+
* if (!data.isReady) return undefined;
|
|
7868
|
+
* return { numbers: data.numbers };
|
|
7869
|
+
* })
|
|
7870
|
+
*/
|
|
7871
|
+
prerunArgs(fn) {
|
|
7872
|
+
return new BlockModelV3({
|
|
7516
7873
|
...this.config,
|
|
7517
|
-
|
|
7518
|
-
__renderLambda: true,
|
|
7519
|
-
handle: 'subtitle',
|
|
7520
|
-
},
|
|
7874
|
+
prerunArgs: createAndRegisterRenderLambda({ handle: 'prerunArgs', lambda: fn }),
|
|
7521
7875
|
});
|
|
7522
7876
|
}
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
return new
|
|
7877
|
+
/** Sets the lambda to generate list of sections in the left block overviews panel. */
|
|
7878
|
+
sections(rf) {
|
|
7879
|
+
return new BlockModelV3({
|
|
7526
7880
|
...this.config,
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
handle: 'tags',
|
|
7530
|
-
},
|
|
7881
|
+
// Replace the default sections callback with the user-provided one
|
|
7882
|
+
sections: createAndRegisterRenderLambda({ handle: 'sections', lambda: () => rf(new RenderCtx()) }, true),
|
|
7531
7883
|
});
|
|
7532
7884
|
}
|
|
7533
|
-
/**
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7885
|
+
/** Sets a rendering function to derive block title, shown for the block in the left blocks-overview panel. */
|
|
7886
|
+
title(rf) {
|
|
7887
|
+
return new BlockModelV3({
|
|
7888
|
+
...this.config,
|
|
7889
|
+
title: createAndRegisterRenderLambda({ handle: 'title', lambda: () => rf(new RenderCtx()) }),
|
|
7890
|
+
});
|
|
7539
7891
|
}
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
return new BlockModel({
|
|
7892
|
+
subtitle(rf) {
|
|
7893
|
+
return new BlockModelV3({
|
|
7543
7894
|
...this.config,
|
|
7544
|
-
|
|
7895
|
+
subtitle: createAndRegisterRenderLambda({ handle: 'subtitle', lambda: () => rf(new RenderCtx()) }),
|
|
7545
7896
|
});
|
|
7546
7897
|
}
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
return new BlockModel({
|
|
7898
|
+
tags(rf) {
|
|
7899
|
+
return new BlockModelV3({
|
|
7550
7900
|
...this.config,
|
|
7551
|
-
|
|
7901
|
+
tags: createAndRegisterRenderLambda({ handle: 'tags', lambda: () => rf(new RenderCtx()) }),
|
|
7552
7902
|
});
|
|
7553
7903
|
}
|
|
7554
7904
|
/** Sets or overrides feature flags for the block. */
|
|
7555
7905
|
withFeatureFlags(flags) {
|
|
7556
|
-
return new
|
|
7906
|
+
return new BlockModelV3({
|
|
7557
7907
|
...this.config,
|
|
7558
|
-
featureFlags: {
|
|
7559
|
-
...this.config.featureFlags,
|
|
7560
|
-
...flags,
|
|
7561
|
-
},
|
|
7908
|
+
featureFlags: { ...this.config.featureFlags, ...flags },
|
|
7562
7909
|
});
|
|
7563
7910
|
}
|
|
7564
7911
|
/**
|
|
@@ -7566,58 +7913,58 @@
|
|
|
7566
7913
|
* Influences dependency graph construction.
|
|
7567
7914
|
*/
|
|
7568
7915
|
enriches(lambda) {
|
|
7569
|
-
|
|
7570
|
-
return new BlockModel({
|
|
7916
|
+
return new BlockModelV3({
|
|
7571
7917
|
...this.config,
|
|
7572
|
-
enrichmentTargets: {
|
|
7573
|
-
__renderLambda: true,
|
|
7574
|
-
handle: 'enrichmentTargets',
|
|
7575
|
-
},
|
|
7918
|
+
enrichmentTargets: createAndRegisterRenderLambda({ handle: 'enrichmentTargets', lambda: lambda }),
|
|
7576
7919
|
});
|
|
7577
7920
|
}
|
|
7578
7921
|
/** Renders all provided block settings into a pre-configured platforma API
|
|
7579
|
-
* instance, that can be used in frontend to interact with block
|
|
7922
|
+
* instance, that can be used in frontend to interact with block data, and
|
|
7580
7923
|
* other features provided by the platforma to the block. */
|
|
7581
|
-
done(
|
|
7924
|
+
done() {
|
|
7582
7925
|
return this.withFeatureFlags({
|
|
7583
7926
|
...this.config.featureFlags,
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
const
|
|
7591
|
-
|
|
7927
|
+
})._done();
|
|
7928
|
+
}
|
|
7929
|
+
_done() {
|
|
7930
|
+
if (this.config.args === undefined)
|
|
7931
|
+
throw new Error('Args rendering function not set.');
|
|
7932
|
+
const apiVersion = 3;
|
|
7933
|
+
const migrationCount = this.config.dataModel.migrationCount;
|
|
7934
|
+
const blockConfig = {
|
|
7935
|
+
v4: {
|
|
7936
|
+
configVersion: 4,
|
|
7937
|
+
modelAPIVersion: 2,
|
|
7592
7938
|
sdkVersion: PlatformaSDKVersion,
|
|
7593
7939
|
renderingMode: this.config.renderingMode,
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7940
|
+
args: this.config.args,
|
|
7941
|
+
prerunArgs: this.config.prerunArgs,
|
|
7942
|
+
// Reference to __pl_data_initial callback registered by DataModel
|
|
7943
|
+
initialData: createRenderLambda({ handle: '__pl_data_initial' }),
|
|
7597
7944
|
sections: this.config.sections,
|
|
7598
7945
|
title: this.config.title,
|
|
7599
|
-
subtitle: this.config.subtitle,
|
|
7600
|
-
tags: this.config.tags,
|
|
7601
7946
|
outputs: this.config.outputs,
|
|
7602
7947
|
enrichmentTargets: this.config.enrichmentTargets,
|
|
7603
7948
|
featureFlags: this.config.featureFlags,
|
|
7949
|
+
// Generate migration descriptors (indices for metadata)
|
|
7950
|
+
migrations: migrationCount > 0
|
|
7951
|
+
? Array.from({ length: migrationCount }, (_, i) => ({ index: i }))
|
|
7952
|
+
: undefined,
|
|
7604
7953
|
},
|
|
7605
7954
|
// fields below are added to allow previous desktop versions read generated configs
|
|
7606
7955
|
sdkVersion: PlatformaSDKVersion,
|
|
7607
7956
|
renderingMode: this.config.renderingMode,
|
|
7608
|
-
|
|
7609
|
-
inputsValid: downgradeCfgOrLambda(this.config.inputsValid),
|
|
7610
|
-
sections: downgradeCfgOrLambda(this.config.sections),
|
|
7957
|
+
sections: this.config.sections,
|
|
7611
7958
|
outputs: Object.fromEntries(Object.entries(this.config.outputs).map(([key, value]) => [key, downgradeCfgOrLambda(value)])),
|
|
7612
7959
|
};
|
|
7613
|
-
globalThis.platformaApiVersion =
|
|
7960
|
+
globalThis.platformaApiVersion = apiVersion;
|
|
7614
7961
|
if (!isInUI())
|
|
7615
7962
|
// we are in the configuration rendering routine, not in actual UI
|
|
7616
|
-
return { config };
|
|
7963
|
+
return { config: blockConfig };
|
|
7617
7964
|
// normal operation inside the UI
|
|
7618
7965
|
else
|
|
7619
7966
|
return {
|
|
7620
|
-
...getPlatformaInstance({ sdkVersion: PlatformaSDKVersion, apiVersion
|
|
7967
|
+
...getPlatformaInstance({ sdkVersion: PlatformaSDKVersion, apiVersion }),
|
|
7621
7968
|
blockModelInfo: {
|
|
7622
7969
|
outputs: Object.fromEntries(Object.entries(this.config.outputs)
|
|
7623
7970
|
.map(([key, value]) => [key, {
|
|
@@ -7628,6 +7975,137 @@
|
|
|
7628
7975
|
}
|
|
7629
7976
|
}
|
|
7630
7977
|
|
|
7978
|
+
/** Internal builder for chaining migrations */
|
|
7979
|
+
class DataModelBuilder {
|
|
7980
|
+
migrationSteps;
|
|
7981
|
+
constructor(steps = []) {
|
|
7982
|
+
this.migrationSteps = steps;
|
|
7983
|
+
}
|
|
7984
|
+
/** Start a migration chain from an initial type */
|
|
7985
|
+
static from() {
|
|
7986
|
+
return new DataModelBuilder();
|
|
7987
|
+
}
|
|
7988
|
+
/** Add a migration step */
|
|
7989
|
+
migrate(fn) {
|
|
7990
|
+
return new DataModelBuilder([...this.migrationSteps, fn]);
|
|
7991
|
+
}
|
|
7992
|
+
/** Finalize with initial data, creating the DataModel */
|
|
7993
|
+
create(initialData, ..._) {
|
|
7994
|
+
return DataModel._fromBuilder(this.migrationSteps, initialData);
|
|
7995
|
+
}
|
|
7996
|
+
}
|
|
7997
|
+
/**
|
|
7998
|
+
* DataModel defines the block's data structure, initial values, and migrations.
|
|
7999
|
+
* Used by BlockModelV3 to manage data state.
|
|
8000
|
+
*
|
|
8001
|
+
* @example
|
|
8002
|
+
* // Simple data model (no migrations)
|
|
8003
|
+
* const dataModel = DataModel.create<BlockData>(() => ({
|
|
8004
|
+
* numbers: [],
|
|
8005
|
+
* labels: [],
|
|
8006
|
+
* }));
|
|
8007
|
+
*
|
|
8008
|
+
* // Data model with migrations
|
|
8009
|
+
* const dataModel = DataModel
|
|
8010
|
+
* .from<V1>()
|
|
8011
|
+
* .migrate((data) => ({ ...data, labels: [] })) // v1 → v2
|
|
8012
|
+
* .migrate((data) => ({ ...data, description: '' })) // v2 → v3
|
|
8013
|
+
* .create<BlockData>(() => ({ numbers: [], labels: [], description: '' }));
|
|
8014
|
+
*/
|
|
8015
|
+
class DataModel {
|
|
8016
|
+
steps;
|
|
8017
|
+
_initialData;
|
|
8018
|
+
constructor(steps, initialData) {
|
|
8019
|
+
this.steps = steps;
|
|
8020
|
+
this._initialData = initialData;
|
|
8021
|
+
}
|
|
8022
|
+
/** Start a migration chain from an initial type */
|
|
8023
|
+
static from() {
|
|
8024
|
+
return DataModelBuilder.from();
|
|
8025
|
+
}
|
|
8026
|
+
/** Create a data model with just initial data (no migrations) */
|
|
8027
|
+
static create(initialData) {
|
|
8028
|
+
return new DataModel([], initialData);
|
|
8029
|
+
}
|
|
8030
|
+
/** Create from builder (internal use) */
|
|
8031
|
+
static _fromBuilder(steps, initialData) {
|
|
8032
|
+
return new DataModel(steps, initialData);
|
|
8033
|
+
}
|
|
8034
|
+
/**
|
|
8035
|
+
* Latest version number.
|
|
8036
|
+
* Version 1 = initial state, each migration adds 1.
|
|
8037
|
+
*/
|
|
8038
|
+
get version() {
|
|
8039
|
+
return this.steps.length + 1;
|
|
8040
|
+
}
|
|
8041
|
+
/** Number of migration steps */
|
|
8042
|
+
get migrationCount() {
|
|
8043
|
+
return this.steps.length;
|
|
8044
|
+
}
|
|
8045
|
+
/** Get initial data */
|
|
8046
|
+
initialData() {
|
|
8047
|
+
return this._initialData();
|
|
8048
|
+
}
|
|
8049
|
+
/** Get default data wrapped with current version */
|
|
8050
|
+
getDefaultData() {
|
|
8051
|
+
return { version: this.version, data: this._initialData() };
|
|
8052
|
+
}
|
|
8053
|
+
/**
|
|
8054
|
+
* Upgrade versioned data from any version to the latest.
|
|
8055
|
+
* Applies only the migrations needed (skips already-applied ones).
|
|
8056
|
+
* If a migration fails, returns default data with a warning.
|
|
8057
|
+
*/
|
|
8058
|
+
upgrade(versioned) {
|
|
8059
|
+
const { version: fromVersion, data } = versioned;
|
|
8060
|
+
if (fromVersion > this.version) {
|
|
8061
|
+
throw new Error(`Cannot downgrade from version ${fromVersion} to ${this.version}`);
|
|
8062
|
+
}
|
|
8063
|
+
if (fromVersion === this.version) {
|
|
8064
|
+
return { version: this.version, data: data };
|
|
8065
|
+
}
|
|
8066
|
+
// Apply migrations starting from (fromVersion - 1) index
|
|
8067
|
+
// Version 1 -> no migrations applied yet -> start at index 0
|
|
8068
|
+
// Version 2 -> migration[0] already applied -> start at index 1
|
|
8069
|
+
const startIndex = fromVersion - 1;
|
|
8070
|
+
const migrationsToApply = this.steps.slice(startIndex);
|
|
8071
|
+
let currentData = data;
|
|
8072
|
+
for (let i = 0; i < migrationsToApply.length; i++) {
|
|
8073
|
+
const stepIndex = startIndex + i;
|
|
8074
|
+
const fromVer = stepIndex + 1;
|
|
8075
|
+
const toVer = stepIndex + 2;
|
|
8076
|
+
try {
|
|
8077
|
+
currentData = migrationsToApply[i](currentData);
|
|
8078
|
+
}
|
|
8079
|
+
catch (error) {
|
|
8080
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
8081
|
+
return {
|
|
8082
|
+
...this.getDefaultData(),
|
|
8083
|
+
warning: `Migration v${fromVer}→v${toVer} failed: ${errorMessage}`,
|
|
8084
|
+
};
|
|
8085
|
+
}
|
|
8086
|
+
}
|
|
8087
|
+
return { version: this.version, data: currentData };
|
|
8088
|
+
}
|
|
8089
|
+
/**
|
|
8090
|
+
* Register callbacks for use in the VM.
|
|
8091
|
+
* Called by BlockModelV3.create() to set up internal callbacks.
|
|
8092
|
+
*
|
|
8093
|
+
* All callbacks are prefixed with `__pl_` to indicate internal SDK use:
|
|
8094
|
+
* - `__pl_data_initial`: returns initial data for new blocks
|
|
8095
|
+
* - `__pl_data_upgrade`: upgrades versioned data from any version to latest
|
|
8096
|
+
* - `__pl_storage_initial`: returns initial BlockStorage as JSON string
|
|
8097
|
+
*/
|
|
8098
|
+
registerCallbacks() {
|
|
8099
|
+
tryRegisterCallback('__pl_data_initial', () => this._initialData());
|
|
8100
|
+
tryRegisterCallback('__pl_data_upgrade', (versioned) => this.upgrade(versioned));
|
|
8101
|
+
tryRegisterCallback('__pl_storage_initial', () => {
|
|
8102
|
+
const { version, data } = this.getDefaultData();
|
|
8103
|
+
const storage = createBlockStorage(data, version);
|
|
8104
|
+
return JSON.stringify(storage);
|
|
8105
|
+
});
|
|
8106
|
+
}
|
|
8107
|
+
}
|
|
8108
|
+
|
|
7631
8109
|
var stringify = {exports: {}};
|
|
7632
8110
|
|
|
7633
8111
|
var hasRequiredStringify;
|
|
@@ -7708,8 +8186,11 @@
|
|
|
7708
8186
|
errors: z.lazy(() => ErrorShape.array()).optional(),
|
|
7709
8187
|
});
|
|
7710
8188
|
|
|
7711
|
-
const
|
|
7712
|
-
|
|
8189
|
+
const dataModel = DataModel.create(() => ({ titleArgs: 'The title' }));
|
|
8190
|
+
const platforma = BlockModelV3.create({ dataModel, renderingMode: 'Heavy' })
|
|
8191
|
+
.args((data) => {
|
|
8192
|
+
return { titleArgs: data.titleArgs };
|
|
8193
|
+
})
|
|
7713
8194
|
.output('allSpecs', (ctx) => ctx.resultPool.getSpecs())
|
|
7714
8195
|
.sections((_ctx) => {
|
|
7715
8196
|
return [{ type: 'link', href: '/', label: 'Main' }];
|