@platforma-sdk/model 1.81.0 → 1.82.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.
Files changed (38) hide show
  1. package/dist/block_migrations.cjs +92 -8
  2. package/dist/block_migrations.cjs.map +1 -1
  3. package/dist/block_migrations.d.ts +121 -32
  4. package/dist/block_migrations.d.ts.map +1 -1
  5. package/dist/block_migrations.js +92 -8
  6. package/dist/block_migrations.js.map +1 -1
  7. package/dist/block_model.cjs +69 -15
  8. package/dist/block_model.cjs.map +1 -1
  9. package/dist/block_model.d.ts +58 -22
  10. package/dist/block_model.d.ts.map +1 -1
  11. package/dist/block_model.js +71 -17
  12. package/dist/block_model.js.map +1 -1
  13. package/dist/block_storage_callbacks.cjs +194 -13
  14. package/dist/block_storage_callbacks.cjs.map +1 -1
  15. package/dist/block_storage_callbacks.js +192 -15
  16. package/dist/block_storage_callbacks.js.map +1 -1
  17. package/dist/block_storage_facade.cjs +4 -1
  18. package/dist/block_storage_facade.cjs.map +1 -1
  19. package/dist/block_storage_facade.d.ts +102 -0
  20. package/dist/block_storage_facade.d.ts.map +1 -1
  21. package/dist/block_storage_facade.js +4 -1
  22. package/dist/block_storage_facade.js.map +1 -1
  23. package/dist/components/PlDataTable/createPlDataTable/utils.cjs +1 -1
  24. package/dist/components/PlDataTable/createPlDataTable/utils.cjs.map +1 -1
  25. package/dist/components/PlDataTable/createPlDataTable/utils.js +1 -1
  26. package/dist/components/PlDataTable/createPlDataTable/utils.js.map +1 -1
  27. package/dist/package.cjs +1 -1
  28. package/dist/package.js +1 -1
  29. package/package.json +9 -8
  30. package/src/block_migrations.ts +205 -55
  31. package/src/block_model.ts +190 -59
  32. package/src/block_storage_callbacks.ts +294 -15
  33. package/src/block_storage_facade.ts +95 -0
  34. package/src/components/PlDataTable/createPlDataTable/utils.test.ts +31 -0
  35. package/src/components/PlDataTable/createPlDataTable/utils.ts +4 -1
  36. package/src/kind_reference.test.ts +134 -0
  37. package/src/template_init.test.ts +413 -0
  38. package/src/template_params.test.ts +135 -0
@@ -1,10 +1,29 @@
1
+ import type { BlockKindReference } from "@milaboratories/pl-model-common";
2
+ import { formatKindRef } from "@milaboratories/pl-model-common";
3
+ import type { CompiledBlockKind } from "@platforma-sdk/block-kind";
1
4
  import { DATA_MODEL_LEGACY_VERSION } from "./block_storage";
2
5
 
3
6
  export type DataVersionKey = string;
4
7
  export type DataMigrateFn<From, To> = (prev: Readonly<From>) => To;
5
- export type DataCreateFn<T> = () => T;
8
+ /**
9
+ * Initial-data factory. Object-arg so future inputs (services, resolved refs)
10
+ * can extend it without a signature break. `Params` carries the block's
11
+ * kind-declared params type; `never` means the block reads no params yet.
12
+ */
13
+ export type DataCreateFn<T, Params = never> = (args: { params?: Params }) => T;
6
14
  export type DataRecoverFn<T> = (version: DataVersionKey, data: unknown) => T;
7
15
 
16
+ /**
17
+ * The compiled block-kind object a block declares — exactly the output of
18
+ * `defineBlockKind` from `@platforma-sdk/block-kind`, consumed type-only.
19
+ *
20
+ * The kind object carries no reference field: the `{name}@{version}` reference
21
+ * is derived from it internally (via `formatKindRef`) at the point the builder
22
+ * and `BlockModelV3.create` need it. Aliased here so `DataModelBuilder` /
23
+ * `create` signatures directly accept `defineBlockKind`'s output.
24
+ */
25
+ export type BlockKind<Params = never> = CompiledBlockKind<Params>;
26
+
8
27
  /**
9
28
  * Minimal interface that .transfer() accepts. PluginInstance implements this.
10
29
  * Defined here to avoid circular dependency with plugin_model.ts.
@@ -89,23 +108,42 @@ const FROM_BUILDER = Symbol("fromBuilder");
89
108
  /** Legacy V1 model state shape: { args, uiState } */
90
109
  export type LegacyV1State<Args, UiState> = { args: Args; uiState: UiState };
91
110
 
92
- /** Internal state passed from builder to DataModel */
93
- type BuilderState<S> = {
94
- versionChain: DataVersionKey[];
95
- steps: MigrationStep[];
96
- transferSteps: TransferStep[];
97
- initialDataFn: () => S;
98
- recoverFn?: (version: DataVersionKey, data: unknown) => unknown;
99
- /** Index of the first step to run after recovery. Equals the number of steps
100
- * present at the time recover() was called. */
101
- recoverFromIndex?: number;
111
+ /**
112
+ * The kind a chain belongs to, threaded unchanged from the builder into `DataModel`.
113
+ *
114
+ * Named because every constructor in this file carries exactly this pair, and they travel
115
+ * together by construction: a reference with no parser could not check the params it names,
116
+ * and a parser with no reference would have nothing to check them against.
117
+ */
118
+ type KindWiring = {
119
+ /** Reference to the block kind this data model belongs to, if declared. */
120
+ kindRef?: BlockKindReference;
121
+ /** The kind's runtime params check, threaded with {@link KindWiring.kindRef}. */
122
+ parseInitializationParams?: (value: unknown) => unknown;
102
123
  };
103
124
 
104
125
  type RecoverState = {
105
126
  recoverFn?: (version: DataVersionKey, data: unknown) => unknown;
127
+ /** Index of the first step to run after recovery. Equals the number of steps
128
+ * present at the time recover() was called. */
106
129
  recoverFromIndex?: number;
107
130
  };
108
131
 
132
+ /** Internal state passed from builder to DataModel */
133
+ type BuilderState<S> = KindWiring &
134
+ RecoverState & {
135
+ versionChain: DataVersionKey[];
136
+ steps: MigrationStep[];
137
+ transferSteps: TransferStep[];
138
+ /**
139
+ * The params type is erased to `unknown` here: the builder checks the block's
140
+ * factory against its kind's params, but a caller that supplies params carries them as
141
+ * data it did not type — parsed file content, today — and cannot state the type. The
142
+ * kind's own parser is what recovers it.
143
+ */
144
+ initialDataFn: DataCreateFn<S, unknown>;
145
+ };
146
+
109
147
  /**
110
148
  * Abstract base for both migration chain types.
111
149
  * Holds shared state, buildStep() helper, and init().
@@ -115,19 +153,31 @@ type RecoverState = {
115
153
  *
116
154
  * @internal
117
155
  */
118
- abstract class MigrationChainBase<Current, Transfers extends Record<string, unknown> = {}> {
156
+ abstract class MigrationChainBase<
157
+ Current,
158
+ Transfers extends Record<string, unknown> = {},
159
+ Params = never,
160
+ > {
119
161
  protected readonly versionChain: DataVersionKey[];
120
162
  protected readonly migrationSteps: MigrationStep[];
121
163
  protected readonly transferSteps: TransferStep[];
122
-
123
- protected constructor(state: {
124
- versionChain: DataVersionKey[];
125
- steps: MigrationStep[];
126
- transferSteps?: TransferStep[];
127
- }) {
164
+ /** Kind reference seeded by the builder, threaded through the chain into init(). */
165
+ protected readonly kindRef?: BlockKindReference;
166
+ /** The kind's runtime params check, carried for `init()` to hand to the DataModel. */
167
+ protected readonly parseInitializationParams?: (value: unknown) => unknown;
168
+
169
+ protected constructor(
170
+ state: KindWiring & {
171
+ versionChain: DataVersionKey[];
172
+ steps: MigrationStep[];
173
+ transferSteps?: TransferStep[];
174
+ },
175
+ ) {
128
176
  this.versionChain = state.versionChain;
129
177
  this.migrationSteps = state.steps;
130
178
  this.transferSteps = state.transferSteps ?? [];
179
+ this.kindRef = state.kindRef;
180
+ this.parseInitializationParams = state.parseInitializationParams;
131
181
  }
132
182
 
133
183
  /** Appends a migration step and returns the new versionChain and steps arrays. */
@@ -178,12 +228,14 @@ abstract class MigrationChainBase<Current, Transfers extends Record<string, unkn
178
228
  * @param initialData - Factory function returning the initial state
179
229
  * @returns Finalized DataModel instance
180
230
  */
181
- init(initialData: DataCreateFn<Current>): DataModel<Current, Transfers> {
182
- return DataModel[FROM_BUILDER]<Current, Transfers>({
231
+ init(initialData: DataCreateFn<Current, Params>): DataModel<Current, Params, Transfers> {
232
+ return DataModel[FROM_BUILDER]<Current, Params, Transfers>({
183
233
  versionChain: this.versionChain,
184
234
  steps: this.migrationSteps,
185
235
  transferSteps: this.transferSteps,
186
- initialDataFn: initialData,
236
+ initialDataFn: initialData as DataCreateFn<Current, unknown>,
237
+ kindRef: this.kindRef,
238
+ parseInitializationParams: this.parseInitializationParams,
187
239
  ...this.recoverState(),
188
240
  });
189
241
  }
@@ -201,18 +253,20 @@ abstract class MigrationChainBase<Current, Transfers extends Record<string, unkn
201
253
  class DataModelMigrationChainWithRecover<
202
254
  Current,
203
255
  Transfers extends Record<string, unknown> = {},
204
- > extends MigrationChainBase<Current, Transfers> {
256
+ Params = never,
257
+ > extends MigrationChainBase<Current, Transfers, Params> {
205
258
  private readonly recoverFn?: (version: DataVersionKey, data: unknown) => unknown;
206
259
  private readonly recoverFromIndex?: number;
207
260
 
208
261
  /** @internal */
209
- constructor(state: {
210
- versionChain: DataVersionKey[];
211
- steps: MigrationStep[];
212
- transferSteps?: TransferStep[];
213
- recoverFn?: (version: DataVersionKey, data: unknown) => unknown;
214
- recoverFromIndex?: number;
215
- }) {
262
+ constructor(
263
+ state: KindWiring &
264
+ RecoverState & {
265
+ versionChain: DataVersionKey[];
266
+ steps: MigrationStep[];
267
+ transferSteps?: TransferStep[];
268
+ },
269
+ ) {
216
270
  super(state);
217
271
  this.recoverFn = state.recoverFn;
218
272
  this.recoverFromIndex = state.recoverFromIndex;
@@ -232,12 +286,14 @@ class DataModelMigrationChainWithRecover<
232
286
  migrate<Next>(
233
287
  nextVersion: string,
234
288
  fn: DataMigrateFn<Current, Next>,
235
- ): DataModelMigrationChainWithRecover<Next, Transfers> {
289
+ ): DataModelMigrationChainWithRecover<Next, Transfers, Params> {
236
290
  const { versionChain, steps } = this.buildStep(nextVersion, fn);
237
- return new DataModelMigrationChainWithRecover<Next, Transfers>({
291
+ return new DataModelMigrationChainWithRecover<Next, Transfers, Params>({
238
292
  versionChain,
239
293
  steps,
240
294
  transferSteps: this.transferSteps,
295
+ kindRef: this.kindRef,
296
+ parseInitializationParams: this.parseInitializationParams,
241
297
  recoverFn: this.recoverFn,
242
298
  recoverFromIndex: this.recoverFromIndex,
243
299
  });
@@ -251,12 +307,14 @@ class DataModelMigrationChainWithRecover<
251
307
  transfer<Id extends string, L>(
252
308
  target: TransferTarget<Id & (Id extends keyof Transfers ? never : string), L>,
253
309
  extract: (data: Current) => L,
254
- ): DataModelMigrationChainWithRecover<Current, Transfers & Record<Id, L>> {
310
+ ): DataModelMigrationChainWithRecover<Current, Transfers & Record<Id, L>, Params> {
255
311
  const { transferSteps } = this.buildTransfer(target, extract);
256
- return new DataModelMigrationChainWithRecover<Current, Transfers & Record<Id, L>>({
312
+ return new DataModelMigrationChainWithRecover<Current, Transfers & Record<Id, L>, Params>({
257
313
  versionChain: this.versionChain,
258
314
  steps: this.migrationSteps,
259
315
  transferSteps,
316
+ kindRef: this.kindRef,
317
+ parseInitializationParams: this.parseInitializationParams,
260
318
  recoverFn: this.recoverFn,
261
319
  recoverFromIndex: this.recoverFromIndex,
262
320
  });
@@ -276,18 +334,21 @@ class DataModelMigrationChainWithRecover<
276
334
  class DataModelMigrationChain<
277
335
  Current,
278
336
  Transfers extends Record<string, unknown> = {},
279
- > extends MigrationChainBase<Current, Transfers> {
337
+ Params = never,
338
+ > extends MigrationChainBase<Current, Transfers, Params> {
280
339
  /** @internal */
281
340
  constructor({
282
341
  versionChain,
283
342
  steps = [],
284
343
  transferSteps = [],
285
- }: {
344
+ kindRef,
345
+ parseInitializationParams,
346
+ }: KindWiring & {
286
347
  versionChain: DataVersionKey[];
287
348
  steps?: MigrationStep[];
288
349
  transferSteps?: TransferStep[];
289
350
  }) {
290
- super({ versionChain, steps, transferSteps });
351
+ super({ versionChain, steps, transferSteps, kindRef, parseInitializationParams });
291
352
  }
292
353
 
293
354
  /**
@@ -304,12 +365,14 @@ class DataModelMigrationChain<
304
365
  migrate<Next>(
305
366
  nextVersion: string,
306
367
  fn: DataMigrateFn<Current, Next>,
307
- ): DataModelMigrationChain<Next, Transfers> {
368
+ ): DataModelMigrationChain<Next, Transfers, Params> {
308
369
  const { versionChain, steps } = this.buildStep(nextVersion, fn);
309
- return new DataModelMigrationChain<Next, Transfers>({
370
+ return new DataModelMigrationChain<Next, Transfers, Params>({
310
371
  versionChain,
311
372
  steps,
312
373
  transferSteps: this.transferSteps,
374
+ kindRef: this.kindRef,
375
+ parseInitializationParams: this.parseInitializationParams,
313
376
  });
314
377
  }
315
378
 
@@ -329,12 +392,14 @@ class DataModelMigrationChain<
329
392
  transfer<Id extends string, L>(
330
393
  target: TransferTarget<Id & (Id extends keyof Transfers ? never : string), L>,
331
394
  extract: (data: Current) => L,
332
- ): DataModelMigrationChain<Current, Transfers & Record<Id, L>> {
395
+ ): DataModelMigrationChain<Current, Transfers & Record<Id, L>, Params> {
333
396
  const { transferSteps } = this.buildTransfer(target, extract);
334
- return new DataModelMigrationChain<Current, Transfers & Record<Id, L>>({
397
+ return new DataModelMigrationChain<Current, Transfers & Record<Id, L>, Params>({
335
398
  versionChain: this.versionChain,
336
399
  steps: this.migrationSteps,
337
400
  transferSteps,
401
+ kindRef: this.kindRef,
402
+ parseInitializationParams: this.parseInitializationParams,
338
403
  });
339
404
  }
340
405
 
@@ -361,11 +426,15 @@ class DataModelMigrationChain<
361
426
  * .migrate<V3>("v3", (v2) => ({ ...v2, description: "" }))
362
427
  * .init(() => ({ count: 0, label: "", description: "" }));
363
428
  */
364
- recover(fn: DataRecoverFn<Current>): DataModelMigrationChainWithRecover<Current, Transfers> {
365
- return new DataModelMigrationChainWithRecover<Current, Transfers>({
429
+ recover(
430
+ fn: DataRecoverFn<Current>,
431
+ ): DataModelMigrationChainWithRecover<Current, Transfers, Params> {
432
+ return new DataModelMigrationChainWithRecover<Current, Transfers, Params>({
366
433
  versionChain: this.versionChain,
367
434
  steps: this.migrationSteps,
368
435
  transferSteps: this.transferSteps,
436
+ kindRef: this.kindRef,
437
+ parseInitializationParams: this.parseInitializationParams,
369
438
  recoverFn: fn as (version: DataVersionKey, data: unknown) => unknown,
370
439
  recoverFromIndex: this.migrationSteps.length,
371
440
  });
@@ -384,7 +453,8 @@ class DataModelMigrationChain<
384
453
  class DataModelInitialChain<
385
454
  Current,
386
455
  Transfers extends Record<string, unknown> = {},
387
- > extends DataModelMigrationChain<Current, Transfers> {
456
+ Params = never,
457
+ > extends DataModelMigrationChain<Current, Transfers, Params> {
388
458
  /**
389
459
  * Handle legacy V1 model state ({ args, uiState }) when upgrading a block from
390
460
  * BlockModel V1 to BlockModelV3.
@@ -421,7 +491,7 @@ class DataModelInitialChain<
421
491
  */
422
492
  upgradeLegacy<Args, UiState = unknown>(
423
493
  fn: (legacy: LegacyV1State<Args, UiState>) => Current,
424
- ): DataModelMigrationChainWithRecover<Current, Transfers> {
494
+ ): DataModelMigrationChainWithRecover<Current, Transfers, Params> {
425
495
  const wrappedFn = (data: unknown): unknown => {
426
496
  if (data !== null && typeof data === "object" && "args" in data) {
427
497
  return fn(data as LegacyV1State<Args, UiState>);
@@ -437,9 +507,11 @@ class DataModelInitialChain<
437
507
  toVersion: initialVersion,
438
508
  migrate: wrappedFn,
439
509
  };
440
- return new DataModelMigrationChainWithRecover<Current, Transfers>({
510
+ return new DataModelMigrationChainWithRecover<Current, Transfers, Params>({
441
511
  versionChain: [DATA_MODEL_LEGACY_VERSION, ...this.versionChain],
442
512
  steps: [step, ...this.migrationSteps],
513
+ kindRef: this.kindRef,
514
+ parseInitializationParams: this.parseInitializationParams,
443
515
  // Shift transfer indices to account for the prepended legacy step
444
516
  transferSteps: this.transferSteps.map((t) => ({
445
517
  ...t,
@@ -495,7 +567,29 @@ class DataModelInitialChain<
495
567
  * }))
496
568
  * .init(() => ({ inputFile: '', selectedTab: 'main' }));
497
569
  */
498
- export class DataModelBuilder {
570
+ export class DataModelBuilder<Params = never> {
571
+ readonly #kindRef?: BlockKindReference;
572
+ readonly #parseInitializationParams?: (value: unknown) => Params;
573
+
574
+ /**
575
+ * @param opts.kind - The block kind this data model implements. Its reference
576
+ * is captured and baked into the config so the manifest can advertise which
577
+ * kind the block satisfies, and its `Params` type flows into `.init()`.
578
+ * Optional during the transition window while existing V3 blocks are
579
+ * migrated to kind-carrying builders; a kind-less builder simply carries no
580
+ * reference and the reconciler can't project it yet. Object form mirrors
581
+ * `BlockModelV3.create({ dataModel, kind })`.
582
+ */
583
+ constructor(opts?: { kind?: BlockKind<Params> }) {
584
+ // Derive the on-wire `{name}@{version}` reference from the compiled kind;
585
+ // the kind object itself has no reference field.
586
+ this.#kindRef = opts?.kind ? formatKindRef(opts.kind) : undefined;
587
+ // Carried alongside the reference, and for the same reason: the kind object is
588
+ // not kept, but two things off it are needed later — how to name the kind, and
589
+ // how to check params claimed to be of it.
590
+ this.#parseInitializationParams = opts?.kind?.parseInitializationParams;
591
+ }
592
+
499
593
  /**
500
594
  * Start the migration chain with the given initial data type and version key.
501
595
  *
@@ -503,8 +597,14 @@ export class DataModelBuilder {
503
597
  * @param initialVersion - Version key string (e.g. "v1")
504
598
  * @returns Migration chain builder
505
599
  */
506
- from<T>(initialVersion: string): DataModelInitialChain<T> {
507
- return new DataModelInitialChain<T>({ versionChain: [initialVersion] });
600
+ from<T>(initialVersion: string): DataModelInitialChain<T, {}, Params> {
601
+ return new DataModelInitialChain<T, {}, Params>({
602
+ versionChain: [initialVersion],
603
+ kindRef: this.#kindRef,
604
+ parseInitializationParams: this.#parseInitializationParams as
605
+ | ((value: unknown) => unknown)
606
+ | undefined,
607
+ });
508
608
  }
509
609
  }
510
610
 
@@ -527,9 +627,12 @@ export class DataModelBuilder {
527
627
  * .migrate<V3>("v3", (v2) => ({ ...v2, description: "" }))
528
628
  * .init(() => ({ count: 0, label: "", description: "" }));
529
629
  */
530
- export class DataModel<State, Transfers extends Record<string, unknown> = {}> {
630
+ export class DataModel<State, Params = never, Transfers extends Record<string, unknown> = {}> {
531
631
  /** @internal Phantom field to anchor the Transfers type parameter. */
532
632
  declare readonly __transfers?: Transfers;
633
+ /** @internal Phantom field to anchor the Params type parameter (drives the
634
+ * compile-time kind cross-check in `BlockModelV3.create`). */
635
+ declare readonly __params?: Params;
533
636
 
534
637
  /** Latest version key — O(1) access for the common "already current" check. */
535
638
  private readonly latestVersion: DataVersionKey;
@@ -537,15 +640,21 @@ export class DataModel<State, Transfers extends Record<string, unknown> = {}> {
537
640
  private readonly stepsByFromVersion: ReadonlyMap<DataVersionKey, number>;
538
641
  private readonly steps: MigrationStep[];
539
642
  private readonly transferSteps: TransferStep[];
540
- private readonly initialDataFn: () => State;
643
+ private readonly initialDataFn: DataCreateFn<State, unknown>;
541
644
  private readonly recoverFn: (version: DataVersionKey, data: unknown) => unknown;
542
645
  private readonly recoverFromIndex: number;
646
+ /** Reference to the block kind this data model was built for, if any. */
647
+ private readonly _kindRef?: BlockKindReference;
648
+ /** The kind's runtime params check, if it declares one. */
649
+ private readonly _parseInitializationParams?: (value: unknown) => unknown;
543
650
 
544
651
  private constructor({
545
652
  versionChain,
546
653
  steps,
547
654
  transferSteps = [],
548
655
  initialDataFn,
656
+ kindRef,
657
+ parseInitializationParams,
549
658
  recoverFn = defaultRecover,
550
659
  recoverFromIndex,
551
660
  }: BuilderState<State>) {
@@ -557,6 +666,8 @@ export class DataModel<State, Transfers extends Record<string, unknown> = {}> {
557
666
  this.steps = steps;
558
667
  this.transferSteps = transferSteps;
559
668
  this.initialDataFn = initialDataFn;
669
+ this._kindRef = kindRef;
670
+ this._parseInitializationParams = parseInitializationParams;
560
671
  this.recoverFn = recoverFn;
561
672
  this.recoverFromIndex = recoverFromIndex ?? steps.length;
562
673
  }
@@ -566,10 +677,31 @@ export class DataModel<State, Transfers extends Record<string, unknown> = {}> {
566
677
  * Uses Symbol key to prevent external access.
567
678
  * @internal
568
679
  */
569
- static [FROM_BUILDER]<S, T extends Record<string, unknown> = {}>(
680
+ static [FROM_BUILDER]<S, P = never, T extends Record<string, unknown> = {}>(
570
681
  state: BuilderState<S>,
571
- ): DataModel<S, T> {
572
- return new DataModel<S, T>(state);
682
+ ): DataModel<S, P, T> {
683
+ return new DataModel<S, P, T>(state);
684
+ }
685
+
686
+ /**
687
+ * Reference to the block kind this data model was built for, or `undefined`
688
+ * for a kind-less builder. Used by `BlockModelV3.create` to cross-check that
689
+ * the kind handed to the builder matches the kind handed to `create`.
690
+ * @internal
691
+ */
692
+ get kindRef(): BlockKindReference | undefined {
693
+ return this._kindRef;
694
+ }
695
+
696
+ /**
697
+ * The kind's runtime params check, or `undefined` if the kind declares none (or
698
+ * there is no kind). Read by `BlockModelV3.done()` to register the check, and
699
+ * carried here rather than only on the model because `init` — the one place params
700
+ * are consumed — lives on this side.
701
+ * @internal
702
+ */
703
+ get templateParamsParser(): ((value: unknown) => unknown) | undefined {
704
+ return this._parseInitializationParams;
573
705
  }
574
706
 
575
707
  /**
@@ -583,7 +715,7 @@ export class DataModel<State, Transfers extends Record<string, unknown> = {}> {
583
715
  * Get a fresh copy of the initial data.
584
716
  */
585
717
  initialData(): State {
586
- return this.initialDataFn();
718
+ return this.initialDataFn({});
587
719
  }
588
720
 
589
721
  /**
@@ -591,7 +723,25 @@ export class DataModel<State, Transfers extends Record<string, unknown> = {}> {
591
723
  * Used when creating new blocks or resetting to defaults.
592
724
  */
593
725
  getDefaultData(): DataVersioned<State> {
594
- return makeVersionedData(this.latestVersion, this.initialDataFn());
726
+ return makeVersionedData(this.latestVersion, this.initialDataFn({}));
727
+ }
728
+
729
+ /**
730
+ * Get initial data built from params, wrapped with current version.
731
+ *
732
+ * The counterpart of {@link getDefaultData} for a block created from a template
733
+ * entry: the factory receives the entry's params instead of nothing. A factory
734
+ * that ignores its argument produces the same result as `getDefaultData`, which
735
+ * is why the two are separate methods rather than one optional argument — the
736
+ * caller decides which contract it is asking for, and a block that cannot honour
737
+ * params must not silently look like one that can.
738
+ *
739
+ * References inside `params` are already resolved to the target project's
740
+ * concrete ids by the time they get here; the factory never sees a
741
+ * template-local one.
742
+ */
743
+ getDataFromParams(params: unknown): DataVersioned<State> {
744
+ return makeVersionedData(this.latestVersion, this.initialDataFn({ params }));
595
745
  }
596
746
 
597
747
  private recoverFrom(data: unknown, version: DataVersionKey): DataVersioned<State> {