@milaboratories/milaboratories.pool-explorer.model 1.1.4 → 1.1.6
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 +15 -0
- package/dist/bundle.js +323 -69
- package/dist/bundle.js.map +1 -1
- package/dist/index.cjs +4 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/model.json +1 -1
- package/package.json +5 -5
- package/src/index.ts +15 -2
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
WARN Issue while reading "/home/runner/_work/platforma/platforma/.npmrc". Failed to replace env in config: ${NPMJS_TOKEN}
|
|
2
2
|
|
|
3
|
-
> @milaboratories/milaboratories.pool-explorer.model@1.1.
|
|
3
|
+
> @milaboratories/milaboratories.pool-explorer.model@1.1.6 build /home/runner/_work/platforma/platforma/etc/blocks/pool-explorer/model
|
|
4
4
|
> ts-builder build --target block-model && block-tools build-model
|
|
5
5
|
|
|
6
6
|
Building block-model project...
|
|
7
7
|
↳ rollup -c /configs/rollup.block-model.config.js
|
|
8
8
|
[36m
|
|
9
9
|
[1m./src/index.ts[22m → [1mdist, dist[22m...[39m
|
|
10
|
-
[32mcreated [1mdist, dist[22m in [1m2.
|
|
10
|
+
[32mcreated [1mdist, dist[22m in [1m2.8s[22m[39m
|
|
11
11
|
[36m
|
|
12
12
|
[1m./src/index.ts[22m → [1mdist[22m...[39m
|
|
13
13
|
[1m[33m(!) Circular dependency[39m[22m
|
|
14
14
|
../../../../sdk/model/dist/components/PFrameForGraphs.js -> ../../../../sdk/model/dist/pframe_utils/columns.js -> ../../../../sdk/model/dist/components/PFrameForGraphs.js
|
|
15
|
-
[32mcreated [1mdist[22m in [
|
|
15
|
+
[32mcreated [1mdist[22m in [1m4.8s[22m[39m
|
|
16
16
|
Build completed successfully
|
package/.turbo/turbo-lint.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
WARN Issue while reading "/home/runner/_work/platforma/platforma/.npmrc". Failed to replace env in config: ${NPMJS_TOKEN}
|
|
2
2
|
|
|
3
|
-
> @milaboratories/milaboratories.pool-explorer.model@1.1.
|
|
3
|
+
> @milaboratories/milaboratories.pool-explorer.model@1.1.6 lint /home/runner/_work/platforma/platforma/etc/blocks/pool-explorer/model
|
|
4
4
|
> eslint .
|
|
5
5
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
WARN Issue while reading "/home/runner/_work/platforma/platforma/.npmrc". Failed to replace env in config: ${NPMJS_TOKEN}
|
|
2
2
|
|
|
3
|
-
> @milaboratories/milaboratories.pool-explorer.model@1.1.
|
|
3
|
+
> @milaboratories/milaboratories.pool-explorer.model@1.1.6 type-check /home/runner/_work/platforma/platforma/etc/blocks/pool-explorer/model
|
|
4
4
|
> ts-builder types --target block-model
|
|
5
5
|
|
|
6
6
|
↳ tsc --noEmit --project ./tsconfig.json --customConditions ,
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @milaboratories/milaboratories.pool-explorer.model
|
|
2
2
|
|
|
3
|
+
## 1.1.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f459e5a: Refined DataModel
|
|
8
|
+
- Updated dependencies [f459e5a]
|
|
9
|
+
- @platforma-sdk/model@1.53.3
|
|
10
|
+
|
|
11
|
+
## 1.1.5
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [57799dd]
|
|
16
|
+
- @platforma-sdk/model@1.53.2
|
|
17
|
+
|
|
3
18
|
## 1.1.4
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/dist/bundle.js
CHANGED
|
@@ -72,7 +72,13 @@
|
|
|
72
72
|
function normalizeBlockStorage(raw) {
|
|
73
73
|
if (isBlockStorage(raw)) {
|
|
74
74
|
const storage = raw;
|
|
75
|
-
return {
|
|
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
|
+
};
|
|
76
82
|
}
|
|
77
83
|
// Legacy format: raw is the state directly
|
|
78
84
|
return createBlockStorage(raw);
|
|
@@ -7523,7 +7529,7 @@
|
|
|
7523
7529
|
}
|
|
7524
7530
|
}
|
|
7525
7531
|
|
|
7526
|
-
var version = "1.53.
|
|
7532
|
+
var version = "1.53.3";
|
|
7527
7533
|
|
|
7528
7534
|
const PlatformaSDKVersion = version;
|
|
7529
7535
|
|
|
@@ -7555,7 +7561,7 @@
|
|
|
7555
7561
|
*
|
|
7556
7562
|
* Callbacks registered by DataModel.registerCallbacks():
|
|
7557
7563
|
* - `__pl_data_initial`: () => initial data
|
|
7558
|
-
* - `
|
|
7564
|
+
* - `__pl_data_upgrade`: (versioned) => DataMigrationResult
|
|
7559
7565
|
* - `__pl_storage_initial`: () => initial BlockStorage as JSON string
|
|
7560
7566
|
*
|
|
7561
7567
|
* @module block_storage_vm
|
|
@@ -7660,7 +7666,7 @@
|
|
|
7660
7666
|
* Runs storage migration using the DataModel's migrate callback.
|
|
7661
7667
|
* This is the main entry point for the middle layer to trigger migrations.
|
|
7662
7668
|
*
|
|
7663
|
-
* Uses the '
|
|
7669
|
+
* Uses the '__pl_data_upgrade' callback registered by DataModel.registerCallbacks() which:
|
|
7664
7670
|
* - Handles all migration logic internally
|
|
7665
7671
|
* - Returns { version, data, warning? } - warning present if reset to initial data
|
|
7666
7672
|
*
|
|
@@ -7685,9 +7691,9 @@
|
|
|
7685
7691
|
});
|
|
7686
7692
|
};
|
|
7687
7693
|
// Get the migrate callback (registered by DataModel.registerCallbacks())
|
|
7688
|
-
const migrateCallback = ctx.callbackRegistry['
|
|
7694
|
+
const migrateCallback = ctx.callbackRegistry['__pl_data_upgrade'];
|
|
7689
7695
|
if (typeof migrateCallback !== 'function') {
|
|
7690
|
-
return { error: '
|
|
7696
|
+
return { error: '__pl_data_upgrade callback not found (DataModel not registered)' };
|
|
7691
7697
|
}
|
|
7692
7698
|
// Call the migrator's migrate function
|
|
7693
7699
|
let result;
|
|
@@ -7810,7 +7816,14 @@
|
|
|
7810
7816
|
* Creates a new BlockModelV3 builder with the specified data model and options.
|
|
7811
7817
|
*
|
|
7812
7818
|
* @example
|
|
7813
|
-
* const
|
|
7819
|
+
* const Version = defineDataVersions({ V1: DATA_MODEL_DEFAULT_VERSION });
|
|
7820
|
+
*
|
|
7821
|
+
* type VersionedData = { [Version.V1]: BlockData };
|
|
7822
|
+
*
|
|
7823
|
+
* const dataModel = new DataModelBuilder<VersionedData>()
|
|
7824
|
+
* .from(Version.V1)
|
|
7825
|
+
* .init(() => ({ numbers: [], labels: [] }));
|
|
7826
|
+
*
|
|
7814
7827
|
* BlockModelV3.create({ dataModel })
|
|
7815
7828
|
* .args((data) => ({ numbers: data.numbers }))
|
|
7816
7829
|
* .sections(() => [{ type: 'link', href: '/', label: 'Main' }])
|
|
@@ -7842,7 +7855,11 @@
|
|
|
7842
7855
|
...this.config,
|
|
7843
7856
|
outputs: {
|
|
7844
7857
|
...this.config.outputs,
|
|
7845
|
-
[key]: createAndRegisterRenderLambda({
|
|
7858
|
+
[key]: createAndRegisterRenderLambda({
|
|
7859
|
+
handle: `output#${key}`,
|
|
7860
|
+
lambda: () => cfgOrRf(new RenderCtx()),
|
|
7861
|
+
...flags,
|
|
7862
|
+
}),
|
|
7846
7863
|
},
|
|
7847
7864
|
});
|
|
7848
7865
|
}
|
|
@@ -7896,7 +7913,10 @@
|
|
|
7896
7913
|
prerunArgs(fn) {
|
|
7897
7914
|
return new BlockModelV3({
|
|
7898
7915
|
...this.config,
|
|
7899
|
-
prerunArgs: createAndRegisterRenderLambda({
|
|
7916
|
+
prerunArgs: createAndRegisterRenderLambda({
|
|
7917
|
+
handle: 'prerunArgs',
|
|
7918
|
+
lambda: fn,
|
|
7919
|
+
}),
|
|
7900
7920
|
});
|
|
7901
7921
|
}
|
|
7902
7922
|
/** Sets the lambda to generate list of sections in the left block overviews panel. */
|
|
@@ -7911,19 +7931,28 @@
|
|
|
7911
7931
|
title(rf) {
|
|
7912
7932
|
return new BlockModelV3({
|
|
7913
7933
|
...this.config,
|
|
7914
|
-
title: createAndRegisterRenderLambda({
|
|
7934
|
+
title: createAndRegisterRenderLambda({
|
|
7935
|
+
handle: 'title',
|
|
7936
|
+
lambda: () => rf(new RenderCtx()),
|
|
7937
|
+
}),
|
|
7915
7938
|
});
|
|
7916
7939
|
}
|
|
7917
7940
|
subtitle(rf) {
|
|
7918
7941
|
return new BlockModelV3({
|
|
7919
7942
|
...this.config,
|
|
7920
|
-
subtitle: createAndRegisterRenderLambda({
|
|
7943
|
+
subtitle: createAndRegisterRenderLambda({
|
|
7944
|
+
handle: 'subtitle',
|
|
7945
|
+
lambda: () => rf(new RenderCtx()),
|
|
7946
|
+
}),
|
|
7921
7947
|
});
|
|
7922
7948
|
}
|
|
7923
7949
|
tags(rf) {
|
|
7924
7950
|
return new BlockModelV3({
|
|
7925
7951
|
...this.config,
|
|
7926
|
-
tags: createAndRegisterRenderLambda({
|
|
7952
|
+
tags: createAndRegisterRenderLambda({
|
|
7953
|
+
handle: 'tags',
|
|
7954
|
+
lambda: () => rf(new RenderCtx()),
|
|
7955
|
+
}),
|
|
7927
7956
|
});
|
|
7928
7957
|
}
|
|
7929
7958
|
/** Sets or overrides feature flags for the block. */
|
|
@@ -7940,7 +7969,10 @@
|
|
|
7940
7969
|
enriches(lambda) {
|
|
7941
7970
|
return new BlockModelV3({
|
|
7942
7971
|
...this.config,
|
|
7943
|
-
enrichmentTargets: createAndRegisterRenderLambda({
|
|
7972
|
+
enrichmentTargets: createAndRegisterRenderLambda({
|
|
7973
|
+
handle: 'enrichmentTargets',
|
|
7974
|
+
lambda: lambda,
|
|
7975
|
+
}),
|
|
7944
7976
|
});
|
|
7945
7977
|
}
|
|
7946
7978
|
/** Renders all provided block settings into a pre-configured platforma API
|
|
@@ -7980,7 +8012,10 @@
|
|
|
7980
8012
|
sdkVersion: PlatformaSDKVersion,
|
|
7981
8013
|
renderingMode: this.config.renderingMode,
|
|
7982
8014
|
sections: this.config.sections,
|
|
7983
|
-
outputs: Object.fromEntries(Object.entries(this.config.outputs).map(([key, value]) => [
|
|
8015
|
+
outputs: Object.fromEntries(Object.entries(this.config.outputs).map(([key, value]) => [
|
|
8016
|
+
key,
|
|
8017
|
+
downgradeCfgOrLambda(value),
|
|
8018
|
+
])),
|
|
7984
8019
|
};
|
|
7985
8020
|
globalThis.platformaApiVersion = apiVersion;
|
|
7986
8021
|
if (!isInUI())
|
|
@@ -7989,17 +8024,55 @@
|
|
|
7989
8024
|
// normal operation inside the UI
|
|
7990
8025
|
else
|
|
7991
8026
|
return {
|
|
7992
|
-
...getPlatformaInstance({
|
|
8027
|
+
...getPlatformaInstance({
|
|
8028
|
+
sdkVersion: PlatformaSDKVersion,
|
|
8029
|
+
apiVersion,
|
|
8030
|
+
}),
|
|
7993
8031
|
blockModelInfo: {
|
|
7994
|
-
outputs: Object.fromEntries(Object.entries(this.config.outputs)
|
|
7995
|
-
|
|
8032
|
+
outputs: Object.fromEntries(Object.entries(this.config.outputs).map(([key, value]) => [
|
|
8033
|
+
key,
|
|
8034
|
+
{
|
|
7996
8035
|
withStatus: Boolean(isConfigLambda(value) && value.withStatus),
|
|
7997
|
-
}
|
|
8036
|
+
},
|
|
8037
|
+
])),
|
|
7998
8038
|
},
|
|
7999
8039
|
};
|
|
8000
8040
|
}
|
|
8001
8041
|
}
|
|
8002
8042
|
|
|
8043
|
+
/**
|
|
8044
|
+
* Helper to define version keys with literal type inference and runtime validation.
|
|
8045
|
+
* - Validates that all version values are unique
|
|
8046
|
+
* - Validates that no version value is empty
|
|
8047
|
+
* - Eliminates need for `as const` assertion
|
|
8048
|
+
*
|
|
8049
|
+
* @throws Error if duplicate or empty version values are found
|
|
8050
|
+
*
|
|
8051
|
+
* @example
|
|
8052
|
+
* const Version = defineDataVersions({
|
|
8053
|
+
* Initial: 'v1',
|
|
8054
|
+
* AddedLabels: 'v2',
|
|
8055
|
+
* });
|
|
8056
|
+
*
|
|
8057
|
+
* type VersionedData = {
|
|
8058
|
+
* [Version.Initial]: DataV1;
|
|
8059
|
+
* [Version.AddedLabels]: DataV2;
|
|
8060
|
+
* };
|
|
8061
|
+
*/
|
|
8062
|
+
function defineDataVersions(versions) {
|
|
8063
|
+
const values = Object.values(versions);
|
|
8064
|
+
const keys = Object.keys(versions);
|
|
8065
|
+
const emptyKeys = keys.filter((key) => versions[key] === '');
|
|
8066
|
+
if (emptyKeys.length > 0) {
|
|
8067
|
+
throw new Error(`Version values must be non-empty strings (empty: ${emptyKeys.join(', ')})`);
|
|
8068
|
+
}
|
|
8069
|
+
const unique = new Set(values);
|
|
8070
|
+
if (unique.size !== values.length) {
|
|
8071
|
+
const duplicates = values.filter((v, i) => values.indexOf(v) !== i);
|
|
8072
|
+
throw new Error(`Duplicate version values: ${[...new Set(duplicates)].join(', ')}`);
|
|
8073
|
+
}
|
|
8074
|
+
return versions;
|
|
8075
|
+
}
|
|
8003
8076
|
/** Create a DataVersioned wrapper with correct shape */
|
|
8004
8077
|
function makeDataVersioned(version, data) {
|
|
8005
8078
|
return { version, data };
|
|
@@ -8014,56 +8087,228 @@
|
|
|
8014
8087
|
function isDataUnrecoverableError(error) {
|
|
8015
8088
|
return error instanceof Error && error.name === 'DataUnrecoverableError';
|
|
8016
8089
|
}
|
|
8017
|
-
/**
|
|
8090
|
+
/**
|
|
8091
|
+
* Default recover function for unknown versions.
|
|
8092
|
+
* Use as fallback at the end of custom recover functions.
|
|
8093
|
+
*
|
|
8094
|
+
* @example
|
|
8095
|
+
* .recover((version, data) => {
|
|
8096
|
+
* if (version === 'legacy') {
|
|
8097
|
+
* return transformLegacyData(data);
|
|
8098
|
+
* }
|
|
8099
|
+
* return defaultRecover(version, data);
|
|
8100
|
+
* })
|
|
8101
|
+
*/
|
|
8018
8102
|
const defaultRecover = (version, _data) => {
|
|
8019
8103
|
throw new DataUnrecoverableError(version);
|
|
8020
8104
|
};
|
|
8021
|
-
/**
|
|
8022
|
-
|
|
8105
|
+
/** Symbol for internal builder creation method */
|
|
8106
|
+
const FROM_BUILDER = Symbol('fromBuilder');
|
|
8107
|
+
/**
|
|
8108
|
+
* Final builder state after recover() is called.
|
|
8109
|
+
* Only allows calling create() to finalize the DataModel.
|
|
8110
|
+
*
|
|
8111
|
+
* @typeParam VersionedData - Map of version keys to their data types
|
|
8112
|
+
* @typeParam CurrentVersion - The current (final) version in the chain
|
|
8113
|
+
* @internal
|
|
8114
|
+
*/
|
|
8115
|
+
class DataModelBuilderWithRecover {
|
|
8023
8116
|
versionChain;
|
|
8024
8117
|
migrationSteps;
|
|
8025
8118
|
recoverFn;
|
|
8026
|
-
|
|
8119
|
+
/** @internal */
|
|
8120
|
+
constructor({ versionChain, steps, recoverFn, }) {
|
|
8027
8121
|
this.versionChain = versionChain;
|
|
8028
8122
|
this.migrationSteps = steps;
|
|
8029
8123
|
this.recoverFn = recoverFn;
|
|
8030
8124
|
}
|
|
8031
|
-
/**
|
|
8032
|
-
|
|
8033
|
-
|
|
8125
|
+
/**
|
|
8126
|
+
* Finalize the DataModel with initial data factory.
|
|
8127
|
+
*
|
|
8128
|
+
* The initial data factory is called when creating new blocks or when
|
|
8129
|
+
* migration/recovery fails and data must be reset.
|
|
8130
|
+
*
|
|
8131
|
+
* @param initialData - Factory function returning initial state (must exactly match CurrentVersion's data type)
|
|
8132
|
+
* @returns Finalized DataModel instance
|
|
8133
|
+
*
|
|
8134
|
+
* @example
|
|
8135
|
+
* .init(() => ({ numbers: [], labels: [], description: '' }))
|
|
8136
|
+
*/
|
|
8137
|
+
init(initialData,
|
|
8138
|
+
// Compile-time check: S must have exactly the same keys as VersionedData[CurrentVersion]
|
|
8139
|
+
..._noExtraKeys) {
|
|
8140
|
+
return DataModel[FROM_BUILDER]({
|
|
8141
|
+
versionChain: this.versionChain,
|
|
8142
|
+
steps: this.migrationSteps,
|
|
8143
|
+
initialDataFn: initialData,
|
|
8144
|
+
recoverFn: this.recoverFn,
|
|
8145
|
+
});
|
|
8034
8146
|
}
|
|
8035
|
-
|
|
8147
|
+
}
|
|
8148
|
+
/**
|
|
8149
|
+
* Internal builder for constructing DataModel with type-safe migration chains.
|
|
8150
|
+
*
|
|
8151
|
+
* Tracks the current version through the generic type system, ensuring:
|
|
8152
|
+
* - Migration functions receive correctly typed input
|
|
8153
|
+
* - Migration functions must return the correct output type
|
|
8154
|
+
* - Version keys must exist in the VersionedData map
|
|
8155
|
+
* - All versions must be covered before calling init()
|
|
8156
|
+
*
|
|
8157
|
+
* @typeParam VersionedData - Map of version keys to their data types
|
|
8158
|
+
* @typeParam CurrentVersion - The current version in the migration chain
|
|
8159
|
+
* @typeParam RemainingVersions - Versions not yet covered by migrations
|
|
8160
|
+
* @internal
|
|
8161
|
+
*/
|
|
8162
|
+
class DataModelMigrationChain {
|
|
8163
|
+
versionChain;
|
|
8164
|
+
migrationSteps;
|
|
8165
|
+
/** @internal */
|
|
8166
|
+
constructor({ versionChain, steps = [], }) {
|
|
8167
|
+
this.versionChain = versionChain;
|
|
8168
|
+
this.migrationSteps = steps;
|
|
8169
|
+
}
|
|
8170
|
+
/**
|
|
8171
|
+
* Add a migration step to transform data from current version to next version.
|
|
8172
|
+
*
|
|
8173
|
+
* Migration functions:
|
|
8174
|
+
* - Receive data typed as the current version's data type (readonly)
|
|
8175
|
+
* - Must return data matching the target version's data type
|
|
8176
|
+
* - Should be pure functions (no side effects)
|
|
8177
|
+
* - May throw errors (will result in data reset with warning)
|
|
8178
|
+
*
|
|
8179
|
+
* @typeParam NextVersion - The target version key (must be in RemainingVersions)
|
|
8180
|
+
* @param nextVersion - The version key to migrate to
|
|
8181
|
+
* @param fn - Migration function transforming current data to next version
|
|
8182
|
+
* @returns Builder with updated current version
|
|
8183
|
+
*
|
|
8184
|
+
* @example
|
|
8185
|
+
* .migrate(Version.V2, (data) => ({ ...data, labels: [] }))
|
|
8186
|
+
*/
|
|
8036
8187
|
migrate(nextVersion, fn) {
|
|
8037
8188
|
if (this.versionChain.includes(nextVersion)) {
|
|
8038
8189
|
throw new Error(`Duplicate version '${nextVersion}' in migration chain`);
|
|
8039
8190
|
}
|
|
8040
8191
|
const fromVersion = this.versionChain[this.versionChain.length - 1];
|
|
8041
|
-
const step = {
|
|
8042
|
-
|
|
8192
|
+
const step = {
|
|
8193
|
+
fromVersion,
|
|
8194
|
+
toVersion: nextVersion,
|
|
8195
|
+
migrate: fn,
|
|
8196
|
+
};
|
|
8197
|
+
return new DataModelMigrationChain({
|
|
8198
|
+
versionChain: [...this.versionChain, nextVersion],
|
|
8199
|
+
steps: [...this.migrationSteps, step],
|
|
8200
|
+
});
|
|
8043
8201
|
}
|
|
8044
|
-
/**
|
|
8202
|
+
/**
|
|
8203
|
+
* Set a recovery handler for unknown or legacy versions.
|
|
8204
|
+
*
|
|
8205
|
+
* The recover function is called when data has a version not in the migration chain.
|
|
8206
|
+
* It should either:
|
|
8207
|
+
* - Transform the data to the current version's format and return it
|
|
8208
|
+
* - Call `defaultRecover(version, data)` to signal unrecoverable data
|
|
8209
|
+
*
|
|
8210
|
+
* Can only be called once. After calling, only `init()` is available.
|
|
8211
|
+
*
|
|
8212
|
+
* @param fn - Recovery function that transforms unknown data or throws
|
|
8213
|
+
* @returns Builder with only init() method available
|
|
8214
|
+
*
|
|
8215
|
+
* @example
|
|
8216
|
+
* .recover((version, data) => {
|
|
8217
|
+
* if (version === 'legacy' && isLegacyFormat(data)) {
|
|
8218
|
+
* return transformLegacy(data);
|
|
8219
|
+
* }
|
|
8220
|
+
* return defaultRecover(version, data);
|
|
8221
|
+
* })
|
|
8222
|
+
*/
|
|
8045
8223
|
recover(fn) {
|
|
8046
|
-
return new
|
|
8224
|
+
return new DataModelBuilderWithRecover({
|
|
8225
|
+
versionChain: [...this.versionChain],
|
|
8226
|
+
steps: [...this.migrationSteps],
|
|
8227
|
+
recoverFn: fn,
|
|
8228
|
+
});
|
|
8047
8229
|
}
|
|
8048
|
-
/**
|
|
8049
|
-
|
|
8050
|
-
|
|
8230
|
+
/**
|
|
8231
|
+
* Finalize the DataModel with initial data factory.
|
|
8232
|
+
*
|
|
8233
|
+
* Can only be called when all versions in VersionedData have been covered
|
|
8234
|
+
* by the migration chain (RemainingVersions is empty).
|
|
8235
|
+
*
|
|
8236
|
+
* The initial data factory is called when creating new blocks or when
|
|
8237
|
+
* migration/recovery fails and data must be reset.
|
|
8238
|
+
*
|
|
8239
|
+
* @param initialData - Factory function returning initial state (must exactly match CurrentVersion's data type)
|
|
8240
|
+
* @returns Finalized DataModel instance
|
|
8241
|
+
*
|
|
8242
|
+
* @example
|
|
8243
|
+
* .init(() => ({ numbers: [], labels: [], description: '' }))
|
|
8244
|
+
*/
|
|
8245
|
+
init(initialData,
|
|
8246
|
+
// Compile-time check: S must have exactly the same keys as VersionedData[CurrentVersion]
|
|
8247
|
+
..._noExtraKeys) {
|
|
8248
|
+
return DataModel[FROM_BUILDER]({
|
|
8249
|
+
versionChain: this.versionChain,
|
|
8250
|
+
steps: this.migrationSteps,
|
|
8251
|
+
initialDataFn: initialData,
|
|
8252
|
+
});
|
|
8253
|
+
}
|
|
8254
|
+
}
|
|
8255
|
+
/**
|
|
8256
|
+
* Builder entry point for creating DataModel with type-safe migrations.
|
|
8257
|
+
*
|
|
8258
|
+
* @typeParam VersionedData - Map of version keys to their data types
|
|
8259
|
+
*
|
|
8260
|
+
* @example
|
|
8261
|
+
* const Version = defineDataVersions({
|
|
8262
|
+
* V1: 'v1',
|
|
8263
|
+
* V2: 'v2',
|
|
8264
|
+
* });
|
|
8265
|
+
*
|
|
8266
|
+
* type VersionedData = {
|
|
8267
|
+
* [Version.V1]: { count: number };
|
|
8268
|
+
* [Version.V2]: { count: number; label: string };
|
|
8269
|
+
* };
|
|
8270
|
+
*
|
|
8271
|
+
* const dataModel = new DataModelBuilder<VersionedData>()
|
|
8272
|
+
* .from(Version.V1)
|
|
8273
|
+
* .migrate(Version.V2, (data) => ({ ...data, label: '' }))
|
|
8274
|
+
* .init(() => ({ count: 0, label: '' }));
|
|
8275
|
+
*/
|
|
8276
|
+
class DataModelBuilder {
|
|
8277
|
+
/**
|
|
8278
|
+
* Start a migration chain from an initial version.
|
|
8279
|
+
*
|
|
8280
|
+
* @typeParam InitialVersion - The starting version key (inferred from argument)
|
|
8281
|
+
* @param initialVersion - The version key to start from
|
|
8282
|
+
* @returns Migration chain builder for adding migrations
|
|
8283
|
+
*
|
|
8284
|
+
* @example
|
|
8285
|
+
* new DataModelBuilder<VersionedData>()
|
|
8286
|
+
* .from(Version.V1)
|
|
8287
|
+
* .migrate(Version.V2, (data) => ({ ...data, newField: '' }))
|
|
8288
|
+
*/
|
|
8289
|
+
from(initialVersion) {
|
|
8290
|
+
return new DataModelMigrationChain({ versionChain: [initialVersion] });
|
|
8051
8291
|
}
|
|
8052
8292
|
}
|
|
8053
8293
|
/**
|
|
8054
8294
|
* DataModel defines the block's data structure, initial values, and migrations.
|
|
8055
8295
|
* Used by BlockModelV3 to manage data state.
|
|
8056
8296
|
*
|
|
8297
|
+
* Use `new DataModelBuilder<VersionedData>()` to create a DataModel:
|
|
8298
|
+
*
|
|
8299
|
+
* **Simple (no migrations):**
|
|
8057
8300
|
* @example
|
|
8058
|
-
*
|
|
8059
|
-
*
|
|
8060
|
-
*
|
|
8061
|
-
*
|
|
8062
|
-
*
|
|
8301
|
+
* const Version = defineDataVersions({ V1: DATA_MODEL_DEFAULT_VERSION });
|
|
8302
|
+
* type VersionedData = { [Version.V1]: BlockData };
|
|
8303
|
+
*
|
|
8304
|
+
* const dataModel = new DataModelBuilder<VersionedData>()
|
|
8305
|
+
* .from(Version.V1)
|
|
8306
|
+
* .init(() => ({ numbers: [], labels: [] }));
|
|
8063
8307
|
*
|
|
8064
|
-
*
|
|
8308
|
+
* **With migrations:**
|
|
8309
|
+
* @example
|
|
8065
8310
|
* const Version = defineDataVersions({
|
|
8066
|
-
* V1:
|
|
8311
|
+
* V1: DATA_MODEL_DEFAULT_VERSION,
|
|
8067
8312
|
* V2: 'v2',
|
|
8068
8313
|
* V3: 'v3',
|
|
8069
8314
|
* });
|
|
@@ -8074,8 +8319,8 @@
|
|
|
8074
8319
|
* [Version.V3]: { numbers: number[]; labels: string[]; description: string };
|
|
8075
8320
|
* };
|
|
8076
8321
|
*
|
|
8077
|
-
* const dataModel =
|
|
8078
|
-
* .from
|
|
8322
|
+
* const dataModel = new DataModelBuilder<VersionedData>()
|
|
8323
|
+
* .from(Version.V1)
|
|
8079
8324
|
* .migrate(Version.V2, (data) => ({ ...data, labels: [] }))
|
|
8080
8325
|
* .migrate(Version.V3, (data) => ({ ...data, description: '' }))
|
|
8081
8326
|
* .recover((version, data) => {
|
|
@@ -8084,49 +8329,52 @@
|
|
|
8084
8329
|
* }
|
|
8085
8330
|
* return defaultRecover(version, data);
|
|
8086
8331
|
* })
|
|
8087
|
-
* .
|
|
8332
|
+
* .init(() => ({ numbers: [], labels: [], description: '' }));
|
|
8088
8333
|
*/
|
|
8089
8334
|
class DataModel {
|
|
8090
8335
|
versionChain;
|
|
8091
8336
|
steps;
|
|
8092
8337
|
initialDataFn;
|
|
8093
8338
|
recoverFn;
|
|
8094
|
-
constructor(versionChain, steps,
|
|
8339
|
+
constructor({ versionChain, steps, initialDataFn, recoverFn = defaultRecover, }) {
|
|
8095
8340
|
if (versionChain.length === 0) {
|
|
8096
8341
|
throw new Error('DataModel requires at least one version key');
|
|
8097
8342
|
}
|
|
8098
8343
|
this.versionChain = versionChain;
|
|
8099
8344
|
this.steps = steps;
|
|
8100
|
-
this.initialDataFn =
|
|
8101
|
-
this.recoverFn =
|
|
8102
|
-
}
|
|
8103
|
-
/** Start a migration chain from an initial type */
|
|
8104
|
-
static from(initialVersion) {
|
|
8105
|
-
return DataModelBuilder.from(initialVersion);
|
|
8106
|
-
}
|
|
8107
|
-
/** Create a data model with just initial data (no migrations) */
|
|
8108
|
-
static create(initialData, version = DATA_MODEL_DEFAULT_VERSION) {
|
|
8109
|
-
return new DataModel([version], [], initialData);
|
|
8345
|
+
this.initialDataFn = initialDataFn;
|
|
8346
|
+
this.recoverFn = recoverFn;
|
|
8110
8347
|
}
|
|
8111
|
-
/**
|
|
8112
|
-
|
|
8113
|
-
|
|
8348
|
+
/**
|
|
8349
|
+
* Internal method for creating DataModel from builder.
|
|
8350
|
+
* Uses Symbol key to prevent external access.
|
|
8351
|
+
* @internal
|
|
8352
|
+
*/
|
|
8353
|
+
static [FROM_BUILDER](state) {
|
|
8354
|
+
return new DataModel(state);
|
|
8114
8355
|
}
|
|
8115
8356
|
/**
|
|
8116
|
-
*
|
|
8357
|
+
* The latest (current) version key in the migration chain.
|
|
8117
8358
|
*/
|
|
8118
8359
|
get version() {
|
|
8119
8360
|
return this.versionChain[this.versionChain.length - 1];
|
|
8120
8361
|
}
|
|
8121
|
-
/**
|
|
8362
|
+
/**
|
|
8363
|
+
* Number of migration steps defined.
|
|
8364
|
+
*/
|
|
8122
8365
|
get migrationCount() {
|
|
8123
8366
|
return this.steps.length;
|
|
8124
8367
|
}
|
|
8125
|
-
/**
|
|
8368
|
+
/**
|
|
8369
|
+
* Get a fresh copy of the initial data.
|
|
8370
|
+
*/
|
|
8126
8371
|
initialData() {
|
|
8127
8372
|
return this.initialDataFn();
|
|
8128
8373
|
}
|
|
8129
|
-
/**
|
|
8374
|
+
/**
|
|
8375
|
+
* Get initial data wrapped with current version.
|
|
8376
|
+
* Used when creating new blocks or resetting to defaults.
|
|
8377
|
+
*/
|
|
8130
8378
|
getDefaultData() {
|
|
8131
8379
|
return makeDataVersioned(this.version, this.initialDataFn());
|
|
8132
8380
|
}
|
|
@@ -8147,8 +8395,14 @@
|
|
|
8147
8395
|
}
|
|
8148
8396
|
/**
|
|
8149
8397
|
* Migrate versioned data from any version to the latest.
|
|
8150
|
-
*
|
|
8151
|
-
* If
|
|
8398
|
+
*
|
|
8399
|
+
* - If data is already at latest version, returns as-is
|
|
8400
|
+
* - If version is in chain, applies needed migrations
|
|
8401
|
+
* - If version is unknown, calls recover function
|
|
8402
|
+
* - If migration/recovery fails, returns default data with warning
|
|
8403
|
+
*
|
|
8404
|
+
* @param versioned - Data with version tag
|
|
8405
|
+
* @returns Migration result with data at latest version
|
|
8152
8406
|
*/
|
|
8153
8407
|
migrate(versioned) {
|
|
8154
8408
|
const { version: fromVersion, data } = versioned;
|
|
@@ -8179,14 +8433,11 @@
|
|
|
8179
8433
|
* Register callbacks for use in the VM.
|
|
8180
8434
|
* Called by BlockModelV3.create() to set up internal callbacks.
|
|
8181
8435
|
*
|
|
8182
|
-
*
|
|
8183
|
-
* - `__pl_data_initial`: returns initial data for new blocks
|
|
8184
|
-
* - `__pl_data_migrate`: migrates versioned data from any version to latest
|
|
8185
|
-
* - `__pl_storage_initial`: returns initial BlockStorage as JSON string
|
|
8436
|
+
* @internal
|
|
8186
8437
|
*/
|
|
8187
8438
|
registerCallbacks() {
|
|
8188
8439
|
tryRegisterCallback('__pl_data_initial', () => this.initialDataFn());
|
|
8189
|
-
tryRegisterCallback('
|
|
8440
|
+
tryRegisterCallback('__pl_data_upgrade', (versioned) => this.migrate(versioned));
|
|
8190
8441
|
tryRegisterCallback('__pl_storage_initial', () => {
|
|
8191
8442
|
const { version, data } = this.getDefaultData();
|
|
8192
8443
|
const storage = createBlockStorage(data, version);
|
|
@@ -8275,7 +8526,10 @@
|
|
|
8275
8526
|
errors: z.lazy(() => ErrorShape.array()).optional(),
|
|
8276
8527
|
});
|
|
8277
8528
|
|
|
8278
|
-
const
|
|
8529
|
+
const Version = defineDataVersions({ V1: DATA_MODEL_DEFAULT_VERSION });
|
|
8530
|
+
const dataModel = new DataModelBuilder()
|
|
8531
|
+
.from(Version.V1)
|
|
8532
|
+
.init(() => ({ titleArgs: 'The title' }));
|
|
8279
8533
|
const platforma = BlockModelV3.create({ dataModel, renderingMode: 'Heavy' })
|
|
8280
8534
|
.args((data) => {
|
|
8281
8535
|
return { titleArgs: data.titleArgs };
|