@milaboratories/milaboratories.pool-explorer.model 1.1.5 → 1.1.7

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.
@@ -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.5 build /home/runner/_work/platforma/platforma/etc/blocks/pool-explorer/model
3
+ > @milaboratories/milaboratories.pool-explorer.model@1.1.7 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
  
9
9
  ./src/index.ts → dist, dist...
10
- created dist, dist in 1.7s
10
+ created dist, dist in 2s
11
11
  
12
12
  ./src/index.ts → dist...
13
13
  (!) Circular dependency
14
14
  ../../../../sdk/model/dist/components/PFrameForGraphs.js -> ../../../../sdk/model/dist/pframe_utils/columns.js -> ../../../../sdk/model/dist/components/PFrameForGraphs.js
15
- created dist in 3.4s
15
+ created dist in 2.5s
16
16
  Build completed successfully
@@ -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.5 lint /home/runner/_work/platforma/platforma/etc/blocks/pool-explorer/model
3
+ > @milaboratories/milaboratories.pool-explorer.model@1.1.7 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.5 type-check /home/runner/_work/platforma/platforma/etc/blocks/pool-explorer/model
3
+ > @milaboratories/milaboratories.pool-explorer.model@1.1.7 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,19 @@
1
1
  # @milaboratories/milaboratories.pool-explorer.model
2
2
 
3
+ ## 1.1.7
4
+
5
+ ### Patch Changes
6
+
7
+ - @platforma-sdk/model@1.53.4
8
+
9
+ ## 1.1.6
10
+
11
+ ### Patch Changes
12
+
13
+ - f459e5a: Refined DataModel
14
+ - Updated dependencies [f459e5a]
15
+ - @platforma-sdk/model@1.53.3
16
+
3
17
  ## 1.1.5
4
18
 
5
19
  ### Patch Changes
package/dist/bundle.js CHANGED
@@ -7529,7 +7529,7 @@
7529
7529
  }
7530
7530
  }
7531
7531
 
7532
- var version = "1.53.2";
7532
+ var version = "1.53.4";
7533
7533
 
7534
7534
  const PlatformaSDKVersion = version;
7535
7535
 
@@ -7816,7 +7816,14 @@
7816
7816
  * Creates a new BlockModelV3 builder with the specified data model and options.
7817
7817
  *
7818
7818
  * @example
7819
- * const dataModel = DataModel.create<BlockData>(() => ({ numbers: [], labels: [] }));
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
+ *
7820
7827
  * BlockModelV3.create({ dataModel })
7821
7828
  * .args((data) => ({ numbers: data.numbers }))
7822
7829
  * .sections(() => [{ type: 'link', href: '/', label: 'Main' }])
@@ -7848,7 +7855,11 @@
7848
7855
  ...this.config,
7849
7856
  outputs: {
7850
7857
  ...this.config.outputs,
7851
- [key]: createAndRegisterRenderLambda({ handle: `output#${key}`, lambda: () => cfgOrRf(new RenderCtx()), ...flags }),
7858
+ [key]: createAndRegisterRenderLambda({
7859
+ handle: `output#${key}`,
7860
+ lambda: () => cfgOrRf(new RenderCtx()),
7861
+ ...flags,
7862
+ }),
7852
7863
  },
7853
7864
  });
7854
7865
  }
@@ -7902,7 +7913,10 @@
7902
7913
  prerunArgs(fn) {
7903
7914
  return new BlockModelV3({
7904
7915
  ...this.config,
7905
- prerunArgs: createAndRegisterRenderLambda({ handle: 'prerunArgs', lambda: fn }),
7916
+ prerunArgs: createAndRegisterRenderLambda({
7917
+ handle: 'prerunArgs',
7918
+ lambda: fn,
7919
+ }),
7906
7920
  });
7907
7921
  }
7908
7922
  /** Sets the lambda to generate list of sections in the left block overviews panel. */
@@ -7917,19 +7931,28 @@
7917
7931
  title(rf) {
7918
7932
  return new BlockModelV3({
7919
7933
  ...this.config,
7920
- title: createAndRegisterRenderLambda({ handle: 'title', lambda: () => rf(new RenderCtx()) }),
7934
+ title: createAndRegisterRenderLambda({
7935
+ handle: 'title',
7936
+ lambda: () => rf(new RenderCtx()),
7937
+ }),
7921
7938
  });
7922
7939
  }
7923
7940
  subtitle(rf) {
7924
7941
  return new BlockModelV3({
7925
7942
  ...this.config,
7926
- subtitle: createAndRegisterRenderLambda({ handle: 'subtitle', lambda: () => rf(new RenderCtx()) }),
7943
+ subtitle: createAndRegisterRenderLambda({
7944
+ handle: 'subtitle',
7945
+ lambda: () => rf(new RenderCtx()),
7946
+ }),
7927
7947
  });
7928
7948
  }
7929
7949
  tags(rf) {
7930
7950
  return new BlockModelV3({
7931
7951
  ...this.config,
7932
- tags: createAndRegisterRenderLambda({ handle: 'tags', lambda: () => rf(new RenderCtx()) }),
7952
+ tags: createAndRegisterRenderLambda({
7953
+ handle: 'tags',
7954
+ lambda: () => rf(new RenderCtx()),
7955
+ }),
7933
7956
  });
7934
7957
  }
7935
7958
  /** Sets or overrides feature flags for the block. */
@@ -7946,7 +7969,10 @@
7946
7969
  enriches(lambda) {
7947
7970
  return new BlockModelV3({
7948
7971
  ...this.config,
7949
- enrichmentTargets: createAndRegisterRenderLambda({ handle: 'enrichmentTargets', lambda: lambda }),
7972
+ enrichmentTargets: createAndRegisterRenderLambda({
7973
+ handle: 'enrichmentTargets',
7974
+ lambda: lambda,
7975
+ }),
7950
7976
  });
7951
7977
  }
7952
7978
  /** Renders all provided block settings into a pre-configured platforma API
@@ -7986,7 +8012,10 @@
7986
8012
  sdkVersion: PlatformaSDKVersion,
7987
8013
  renderingMode: this.config.renderingMode,
7988
8014
  sections: this.config.sections,
7989
- outputs: Object.fromEntries(Object.entries(this.config.outputs).map(([key, value]) => [key, downgradeCfgOrLambda(value)])),
8015
+ outputs: Object.fromEntries(Object.entries(this.config.outputs).map(([key, value]) => [
8016
+ key,
8017
+ downgradeCfgOrLambda(value),
8018
+ ])),
7990
8019
  };
7991
8020
  globalThis.platformaApiVersion = apiVersion;
7992
8021
  if (!isInUI())
@@ -7995,17 +8024,55 @@
7995
8024
  // normal operation inside the UI
7996
8025
  else
7997
8026
  return {
7998
- ...getPlatformaInstance({ sdkVersion: PlatformaSDKVersion, apiVersion }),
8027
+ ...getPlatformaInstance({
8028
+ sdkVersion: PlatformaSDKVersion,
8029
+ apiVersion,
8030
+ }),
7999
8031
  blockModelInfo: {
8000
- outputs: Object.fromEntries(Object.entries(this.config.outputs)
8001
- .map(([key, value]) => [key, {
8032
+ outputs: Object.fromEntries(Object.entries(this.config.outputs).map(([key, value]) => [
8033
+ key,
8034
+ {
8002
8035
  withStatus: Boolean(isConfigLambda(value) && value.withStatus),
8003
- }])),
8036
+ },
8037
+ ])),
8004
8038
  },
8005
8039
  };
8006
8040
  }
8007
8041
  }
8008
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
+ }
8009
8076
  /** Create a DataVersioned wrapper with correct shape */
8010
8077
  function makeDataVersioned(version, data) {
8011
8078
  return { version, data };
@@ -8037,23 +8104,211 @@
8037
8104
  };
8038
8105
  /** Symbol for internal builder creation method */
8039
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 {
8116
+ versionChain;
8117
+ migrationSteps;
8118
+ recoverFn;
8119
+ /** @internal */
8120
+ constructor({ versionChain, steps, recoverFn, }) {
8121
+ this.versionChain = versionChain;
8122
+ this.migrationSteps = steps;
8123
+ this.recoverFn = recoverFn;
8124
+ }
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
+ });
8146
+ }
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
+ */
8187
+ migrate(nextVersion, fn) {
8188
+ if (this.versionChain.includes(nextVersion)) {
8189
+ throw new Error(`Duplicate version '${nextVersion}' in migration chain`);
8190
+ }
8191
+ const fromVersion = this.versionChain[this.versionChain.length - 1];
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
+ });
8201
+ }
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
+ */
8223
+ recover(fn) {
8224
+ return new DataModelBuilderWithRecover({
8225
+ versionChain: [...this.versionChain],
8226
+ steps: [...this.migrationSteps],
8227
+ recoverFn: fn,
8228
+ });
8229
+ }
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] });
8291
+ }
8292
+ }
8040
8293
  /**
8041
8294
  * DataModel defines the block's data structure, initial values, and migrations.
8042
8295
  * Used by BlockModelV3 to manage data state.
8043
8296
  *
8044
- * Two ways to create a DataModel:
8297
+ * Use `new DataModelBuilder<VersionedData>()` to create a DataModel:
8045
8298
  *
8046
- * 1. **Simple (no migrations)** - Use `DataModel.create()`:
8299
+ * **Simple (no migrations):**
8047
8300
  * @example
8048
- * const dataModel = DataModel.create<BlockData>(() => ({
8049
- * numbers: [],
8050
- * labels: [],
8051
- * }));
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: [] }));
8052
8307
  *
8053
- * 2. **With migrations** - Use `new DataModelBuilder<VersionedData>()`:
8308
+ * **With migrations:**
8054
8309
  * @example
8055
8310
  * const Version = defineDataVersions({
8056
- * V1: 'v1',
8311
+ * V1: DATA_MODEL_DEFAULT_VERSION,
8057
8312
  * V2: 'v2',
8058
8313
  * V3: 'v3',
8059
8314
  * });
@@ -8090,30 +8345,6 @@
8090
8345
  this.initialDataFn = initialDataFn;
8091
8346
  this.recoverFn = recoverFn;
8092
8347
  }
8093
- /**
8094
- * Create a DataModel with just initial data (no migrations).
8095
- *
8096
- * Use this for simple blocks that don't need version migrations.
8097
- * The version will be set to an internal default value.
8098
- *
8099
- * @typeParam S - The state type
8100
- * @param initialData - Factory function returning initial state
8101
- * @param version - Optional custom version key (defaults to internal version)
8102
- * @returns Finalized DataModel instance
8103
- *
8104
- * @example
8105
- * const dataModel = DataModel.create<BlockData>(() => ({
8106
- * numbers: [],
8107
- * labels: [],
8108
- * }));
8109
- */
8110
- static create(initialData, version = DATA_MODEL_DEFAULT_VERSION) {
8111
- return new DataModel({
8112
- versionChain: [version],
8113
- steps: [],
8114
- initialDataFn: initialData,
8115
- });
8116
- }
8117
8348
  /**
8118
8349
  * Internal method for creating DataModel from builder.
8119
8350
  * Uses Symbol key to prevent external access.
@@ -8295,7 +8526,10 @@
8295
8526
  errors: z.lazy(() => ErrorShape.array()).optional(),
8296
8527
  });
8297
8528
 
8298
- const dataModel = DataModel.create(() => ({ titleArgs: 'The title' }));
8529
+ const Version = defineDataVersions({ V1: DATA_MODEL_DEFAULT_VERSION });
8530
+ const dataModel = new DataModelBuilder()
8531
+ .from(Version.V1)
8532
+ .init(() => ({ titleArgs: 'The title' }));
8299
8533
  const platforma = BlockModelV3.create({ dataModel, renderingMode: 'Heavy' })
8300
8534
  .args((data) => {
8301
8535
  return { titleArgs: data.titleArgs };