@platforma-sdk/model 1.54.13 → 1.56.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 (120) hide show
  1. package/dist/bconfig/normalization.cjs +8 -1
  2. package/dist/bconfig/normalization.cjs.map +1 -1
  3. package/dist/bconfig/normalization.d.ts.map +1 -1
  4. package/dist/bconfig/normalization.js +8 -1
  5. package/dist/bconfig/normalization.js.map +1 -1
  6. package/dist/block_api_v3.d.ts +2 -2
  7. package/dist/block_api_v3.d.ts.map +1 -1
  8. package/dist/block_migrations.cjs +246 -214
  9. package/dist/block_migrations.cjs.map +1 -1
  10. package/dist/block_migrations.d.ts +180 -158
  11. package/dist/block_migrations.d.ts.map +1 -1
  12. package/dist/block_migrations.js +247 -214
  13. package/dist/block_migrations.js.map +1 -1
  14. package/dist/block_model.cjs +85 -35
  15. package/dist/block_model.cjs.map +1 -1
  16. package/dist/block_model.d.ts +66 -38
  17. package/dist/block_model.d.ts.map +1 -1
  18. package/dist/block_model.js +86 -36
  19. package/dist/block_model.js.map +1 -1
  20. package/dist/{builder.cjs → block_model_legacy.cjs} +2 -2
  21. package/dist/block_model_legacy.cjs.map +1 -0
  22. package/dist/{builder.d.ts → block_model_legacy.d.ts} +1 -1
  23. package/dist/block_model_legacy.d.ts.map +1 -0
  24. package/dist/{builder.js → block_model_legacy.js} +2 -2
  25. package/dist/block_model_legacy.js.map +1 -0
  26. package/dist/block_state_patch.d.ts +11 -1
  27. package/dist/block_state_patch.d.ts.map +1 -1
  28. package/dist/block_storage.cjs +126 -109
  29. package/dist/block_storage.cjs.map +1 -1
  30. package/dist/block_storage.d.ts +109 -112
  31. package/dist/block_storage.d.ts.map +1 -1
  32. package/dist/block_storage.js +126 -101
  33. package/dist/block_storage.js.map +1 -1
  34. package/dist/block_storage_callbacks.cjs +227 -0
  35. package/dist/block_storage_callbacks.cjs.map +1 -0
  36. package/dist/block_storage_callbacks.d.ts +113 -0
  37. package/dist/block_storage_callbacks.d.ts.map +1 -0
  38. package/dist/block_storage_callbacks.js +218 -0
  39. package/dist/block_storage_callbacks.js.map +1 -0
  40. package/dist/block_storage_facade.cjs +104 -0
  41. package/dist/block_storage_facade.cjs.map +1 -0
  42. package/dist/block_storage_facade.d.ts +168 -0
  43. package/dist/block_storage_facade.d.ts.map +1 -0
  44. package/dist/block_storage_facade.js +99 -0
  45. package/dist/block_storage_facade.js.map +1 -0
  46. package/dist/index.cjs +13 -14
  47. package/dist/index.cjs.map +1 -1
  48. package/dist/index.d.ts +8 -3
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +6 -4
  51. package/dist/index.js.map +1 -1
  52. package/dist/package.json.cjs +1 -1
  53. package/dist/package.json.js +1 -1
  54. package/dist/platforma.d.ts +11 -4
  55. package/dist/platforma.d.ts.map +1 -1
  56. package/dist/plugin_model.cjs +171 -0
  57. package/dist/plugin_model.cjs.map +1 -0
  58. package/dist/plugin_model.d.ts +162 -0
  59. package/dist/plugin_model.d.ts.map +1 -0
  60. package/dist/plugin_model.js +169 -0
  61. package/dist/plugin_model.js.map +1 -0
  62. package/dist/render/api.cjs +20 -21
  63. package/dist/render/api.cjs.map +1 -1
  64. package/dist/render/api.d.ts +8 -8
  65. package/dist/render/api.d.ts.map +1 -1
  66. package/dist/render/api.js +20 -21
  67. package/dist/render/api.js.map +1 -1
  68. package/dist/render/internal.cjs.map +1 -1
  69. package/dist/render/internal.d.ts +2 -1
  70. package/dist/render/internal.d.ts.map +1 -1
  71. package/dist/render/internal.js.map +1 -1
  72. package/dist/version.cjs +4 -0
  73. package/dist/version.cjs.map +1 -1
  74. package/dist/version.d.ts +4 -0
  75. package/dist/version.d.ts.map +1 -1
  76. package/dist/version.js +4 -1
  77. package/dist/version.js.map +1 -1
  78. package/package.json +5 -5
  79. package/src/bconfig/normalization.ts +8 -1
  80. package/src/block_api_v3.ts +2 -2
  81. package/src/block_migrations.test.ts +141 -171
  82. package/src/block_migrations.ts +300 -285
  83. package/src/block_model.ts +205 -95
  84. package/src/{builder.ts → block_model_legacy.ts} +1 -1
  85. package/src/block_state_patch.ts +13 -1
  86. package/src/block_storage.test.ts +283 -95
  87. package/src/block_storage.ts +199 -188
  88. package/src/block_storage_callbacks.ts +326 -0
  89. package/src/block_storage_facade.ts +199 -0
  90. package/src/index.ts +7 -3
  91. package/src/platforma.ts +26 -7
  92. package/src/plugin_model.test.ts +168 -0
  93. package/src/plugin_model.ts +242 -0
  94. package/src/render/api.ts +26 -24
  95. package/src/render/internal.ts +3 -1
  96. package/src/typing.test.ts +1 -1
  97. package/src/version.ts +8 -0
  98. package/dist/block_storage_vm.cjs +0 -262
  99. package/dist/block_storage_vm.cjs.map +0 -1
  100. package/dist/block_storage_vm.d.ts +0 -59
  101. package/dist/block_storage_vm.d.ts.map +0 -1
  102. package/dist/block_storage_vm.js +0 -258
  103. package/dist/block_storage_vm.js.map +0 -1
  104. package/dist/branding.d.ts +0 -7
  105. package/dist/branding.d.ts.map +0 -1
  106. package/dist/builder.cjs.map +0 -1
  107. package/dist/builder.d.ts.map +0 -1
  108. package/dist/builder.js.map +0 -1
  109. package/dist/sdk_info.cjs +0 -10
  110. package/dist/sdk_info.cjs.map +0 -1
  111. package/dist/sdk_info.d.ts +0 -5
  112. package/dist/sdk_info.d.ts.map +0 -1
  113. package/dist/sdk_info.js +0 -8
  114. package/dist/sdk_info.js.map +0 -1
  115. package/dist/unionize.d.ts +0 -12
  116. package/dist/unionize.d.ts.map +0 -1
  117. package/src/block_storage_vm.ts +0 -346
  118. package/src/branding.ts +0 -4
  119. package/src/sdk_info.ts +0 -9
  120. package/src/unionize.ts +0 -12
@@ -9,6 +9,9 @@
9
9
  * @module block_storage
10
10
  */
11
11
 
12
+ import type { Branded } from "@milaboratories/pl-model-common";
13
+ import type { DataMigrationResult, DataVersioned } from "./block_migrations";
14
+
12
15
  // =============================================================================
13
16
  // Core Types
14
17
  // =============================================================================
@@ -37,28 +40,44 @@ export const DATA_MODEL_DEFAULT_VERSION = "__pl_v1_d4e8f2a1__";
37
40
  export type BlockStorageSchemaVersion = "v1"; // Add 'v2', 'v3', etc. as schema evolves
38
41
 
39
42
  /**
40
- * Plugin key type - keys starting with `@plugin/` are reserved for plugin data
43
+ * Branded type for plugin names - globally unique plugin type identifiers.
44
+ * Using a branded type enforces explicit casting (`as PluginName`) which makes
45
+ * it easy to find all plugin name definitions in the codebase and verify uniqueness.
46
+ */
47
+ export type PluginName = Branded<string, "PluginName">;
48
+
49
+ /**
50
+ * Plugin registry - maps pluginId (unique within a block) to pluginName (globally unique plugin type).
51
+ * Using a Record highlights that pluginIds must be unique within a block.
52
+ */
53
+ export type PluginRegistry = Record<string, PluginName>;
54
+
55
+ /**
56
+ * Versioned data - used for both block data and plugin data
41
57
  */
42
- export type PluginKey = `@plugin/${string}`;
58
+ export interface VersionedData<TData = unknown> {
59
+ /** Version of the data, used for migrations */
60
+ __dataVersion: string;
61
+ /** The persistent data */
62
+ __data: TData;
63
+ }
43
64
 
44
65
  /**
45
66
  * Core BlockStorage type that holds:
46
67
  * - __pl_a7f3e2b9__: Schema version (discriminator key identifies BlockStorage format)
47
68
  * - __dataVersion: Version key for block data migrations
48
69
  * - __data: The block's user-facing data (state)
49
- * - @plugin/*: Optional plugin-specific data
70
+ * - __pluginRegistry: Map from pluginId to pluginName (optional)
71
+ * - __plugins: Plugin-specific data keyed by pluginId (optional)
50
72
  */
51
73
  export type BlockStorage<TState = unknown> = {
52
74
  /** Schema version - the key itself is the discriminator */
53
75
  readonly [BLOCK_STORAGE_KEY]: BlockStorageSchemaVersion;
54
- /** Version of the block data, used for migrations */
55
- __dataVersion: string;
56
- /** The block's user-facing data (state) */
57
- __data: TState;
58
- } & {
59
- /** Plugin-specific data, keyed by `@plugin/<pluginName>` */
60
- [K in PluginKey]?: unknown;
61
- };
76
+ /** Registry of plugins: pluginId -> pluginName */
77
+ __pluginRegistry?: PluginRegistry;
78
+ /** Plugin-specific data, keyed by pluginId */
79
+ __plugins?: Record<string, VersionedData<unknown>>;
80
+ } & VersionedData<TState>;
62
81
 
63
82
  /**
64
83
  * Type guard to check if a value is a valid BlockStorage object.
@@ -91,12 +110,14 @@ export function createBlockStorage<TState = unknown>(
91
110
  [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,
92
111
  __dataVersion: version,
93
112
  __data: initialData,
113
+ __pluginRegistry: {},
114
+ __plugins: {},
94
115
  };
95
116
  }
96
117
 
97
118
  /**
98
119
  * Normalizes raw storage data to BlockStorage format.
99
- * If the input is already a BlockStorage, returns it as-is.
120
+ * If the input is already a BlockStorage, returns it as-is (with defaults for missing fields).
100
121
  * If the input is legacy format (raw state), wraps it in BlockStorage structure.
101
122
  *
102
123
  * @param raw - Raw storage data (may be legacy format or BlockStorage)
@@ -112,6 +133,9 @@ export function normalizeBlockStorage<TState = unknown>(raw: unknown): BlockStor
112
133
  typeof storage.__dataVersion === "number"
113
134
  ? DATA_MODEL_DEFAULT_VERSION
114
135
  : storage.__dataVersion,
136
+ // Ensure plugin fields have defaults
137
+ __pluginRegistry: storage.__pluginRegistry ?? {},
138
+ __plugins: storage.__plugins ?? {},
115
139
  };
116
140
  }
117
141
  // Legacy format: raw is the state directly
@@ -150,7 +174,9 @@ export function deriveDataFromStorage<TData = unknown>(rawStorage: unknown): TDa
150
174
  }
151
175
 
152
176
  /** Payload for storage mutation operations. SDK defines specific operations. */
153
- export type MutateStoragePayload<T = unknown> = { operation: "update-data"; value: T };
177
+ export type MutateStoragePayload<T = unknown> =
178
+ | { operation: "update-block-data"; value: T }
179
+ | { operation: "update-plugin-data"; pluginId: string; value: unknown };
154
180
 
155
181
  /**
156
182
  * Updates the data in BlockStorage (immutable)
@@ -164,37 +190,29 @@ export function updateStorageData<TValue = unknown>(
164
190
  payload: MutateStoragePayload<TValue>,
165
191
  ): BlockStorage<TValue> {
166
192
  switch (payload.operation) {
167
- case "update-data":
193
+ case "update-block-data":
168
194
  return { ...storage, __data: payload.value };
195
+ case "update-plugin-data": {
196
+ const { pluginId, value } = payload;
197
+ const currentPlugins = storage.__plugins ?? {};
198
+ const existingEntry = currentPlugins[pluginId];
199
+ const version = existingEntry?.__dataVersion ?? DATA_MODEL_DEFAULT_VERSION;
200
+ return {
201
+ ...storage,
202
+ __plugins: {
203
+ ...currentPlugins,
204
+ [pluginId]: {
205
+ __dataVersion: version,
206
+ __data: value,
207
+ },
208
+ },
209
+ };
210
+ }
169
211
  default:
170
212
  throw new Error(`Unknown storage operation: ${(payload as { operation: string }).operation}`);
171
213
  }
172
214
  }
173
215
 
174
- /**
175
- * Gets the data version from BlockStorage
176
- *
177
- * @param storage - The BlockStorage instance
178
- * @returns The data version key
179
- */
180
- export function getStorageDataVersion(storage: BlockStorage): string {
181
- return storage.__dataVersion;
182
- }
183
-
184
- /**
185
- * Updates the data version in BlockStorage (immutable)
186
- *
187
- * @param storage - The current BlockStorage
188
- * @param version - The new version key
189
- * @returns A new BlockStorage with updated version
190
- */
191
- export function updateStorageDataVersion<TState>(
192
- storage: BlockStorage<TState>,
193
- version: string,
194
- ): BlockStorage<TState> {
195
- return { ...storage, __dataVersion: version };
196
- }
197
-
198
216
  /**
199
217
  * Storage debug view returned by __pl_storage_debugView callback.
200
218
  * Used by developer tools to display block storage info.
@@ -207,180 +225,173 @@ export interface StorageDebugView {
207
225
  }
208
226
 
209
227
  // =============================================================================
210
- // Plugin Data Functions
228
+ // Atomic Migration
211
229
  // =============================================================================
212
230
 
213
231
  /**
214
- * Gets plugin-specific data from BlockStorage
215
- *
216
- * @param storage - The BlockStorage instance
217
- * @param pluginName - The plugin name (without `@plugin/` prefix)
218
- * @returns The plugin data or undefined if not set
232
+ * Result of a successful atomic migration.
219
233
  */
220
- export function getPluginData<TData = unknown>(
221
- storage: BlockStorage,
222
- pluginName: string,
223
- ): TData | undefined {
224
- const key: PluginKey = `@plugin/${pluginName}`;
225
- return storage[key] as TData | undefined;
234
+ export interface MigrationSuccess<TState> {
235
+ success: true;
236
+ /** The fully migrated storage - commit this to persist */
237
+ storage: BlockStorage<TState>;
226
238
  }
227
239
 
228
240
  /**
229
- * Sets plugin-specific data in BlockStorage (immutable)
230
- *
231
- * @param storage - The current BlockStorage
232
- * @param pluginName - The plugin name (without `@plugin/` prefix)
233
- * @param data - The plugin data to store
234
- * @returns A new BlockStorage with updated plugin data
241
+ * Result of a failed atomic migration.
242
+ * The original storage is untouched - user must choose to abort or reset.
235
243
  */
236
- export function setPluginData<TState>(
237
- storage: BlockStorage<TState>,
238
- pluginName: string,
239
- data: unknown,
240
- ): BlockStorage<TState> {
241
- const key: PluginKey = `@plugin/${pluginName}`;
242
- return { ...storage, [key]: data };
244
+ export interface MigrationFailure {
245
+ success: false;
246
+ /** Description of what failed */
247
+ error: string;
248
+ /** Which step failed: 'block' or pluginId */
249
+ failedAt: string;
243
250
  }
244
251
 
245
- /**
246
- * Removes plugin-specific data from BlockStorage (immutable)
247
- *
248
- * @param storage - The current BlockStorage
249
- * @param pluginName - The plugin name (without `@plugin/` prefix)
250
- * @returns A new BlockStorage with the plugin data removed
251
- */
252
- export function removePluginData<TState>(
253
- storage: BlockStorage<TState>,
254
- pluginName: string,
255
- ): BlockStorage<TState> {
256
- const key: PluginKey = `@plugin/${pluginName}`;
257
- const { [key]: _, ...rest } = storage;
258
- return rest as BlockStorage<TState>;
259
- }
252
+ export type MigrationResult<TState> = MigrationSuccess<TState> | MigrationFailure;
260
253
 
261
254
  /**
262
- * Gets all plugin names that have data stored
263
- *
264
- * @param storage - The BlockStorage instance
265
- * @returns Array of plugin names (without `@plugin/` prefix)
255
+ * Configuration for atomic block storage migration.
256
+ * Callbacks use DataVersioned format (the DataModel API format).
257
+ * Conversion to internal VersionedData format is handled by migrateBlockStorage().
266
258
  */
267
- export function getPluginNames(storage: BlockStorage): string[] {
268
- return Object.keys(storage)
269
- .filter((key): key is PluginKey => key.startsWith("@plugin/"))
270
- .map((key) => key.slice("@plugin/".length));
259
+ export interface MigrateBlockStorageConfig {
260
+ /** Migrate block data from any version to latest */
261
+ migrateBlockData: (versioned: DataVersioned<unknown>) => DataMigrationResult<unknown>;
262
+ /** Migrate each plugin's data. Return undefined to remove the plugin. */
263
+ migratePluginData: (
264
+ pluginId: string,
265
+ versioned: DataVersioned<unknown>,
266
+ ) => DataMigrationResult<unknown> | undefined;
267
+ /** The new plugin registry after migration (pluginId -> pluginName) */
268
+ newPluginRegistry: PluginRegistry;
269
+ /** Factory to create initial data for new plugins */
270
+ createPluginData: (pluginId: string) => DataVersioned<unknown>;
271
271
  }
272
272
 
273
- // =============================================================================
274
- // Generic Storage Access
275
- // =============================================================================
276
-
277
273
  /**
278
- * Gets a value from BlockStorage by key
274
+ * Performs atomic migration of block storage including block data and all plugins.
279
275
  *
280
- * @param storage - The BlockStorage instance
281
- * @param key - The key to retrieve
282
- * @returns The value at the given key
283
- */
284
- export function getFromStorage<TState, K extends keyof BlockStorage<TState>>(
285
- storage: BlockStorage<TState>,
286
- key: K,
287
- ): BlockStorage<TState>[K] {
288
- return storage[key];
289
- }
290
-
291
- /**
292
- * Updates a value in BlockStorage by key (immutable)
276
+ * Migration is atomic: either everything succeeds and a new storage is returned,
277
+ * or an error is returned and the original storage is completely untouched.
293
278
  *
294
- * @param storage - The current BlockStorage
295
- * @param key - The key to update
296
- * @param value - The new value
297
- * @returns A new BlockStorage with the updated value
279
+ * Migration steps:
280
+ * 1. Migrate block data
281
+ * 2. For each plugin in newPluginRegistry:
282
+ * - If plugin exists with same name: migrate its data
283
+ * - Otherwise (new or type changed): create with initial data
284
+ * Plugins not in newPluginRegistry are dropped.
285
+ *
286
+ * If any step throws, migration fails and original storage is preserved.
287
+ * User can then choose to:
288
+ * - Abort: keep original storage, don't update block
289
+ * - Reset: call createBlockStorage() to start fresh
290
+ *
291
+ * @param storage - The original storage (will not be modified)
292
+ * @param config - Migration configuration
293
+ * @returns Migration result - either success with new storage, or failure with error info
294
+ *
295
+ * @example
296
+ * const result = migrateBlockStorage(storage, {
297
+ * migrateBlockData: (versioned) => blockDataModel.migrate(versioned),
298
+ * migratePluginData: (pluginId, versioned) => getPluginModel(pluginId).migrate(versioned),
299
+ * newPluginRegistry: { table1: 'dataTable' as PluginName },
300
+ * createPluginData: (pluginId) => getPluginModel(pluginId).getDefaultData(),
301
+ * });
302
+ *
303
+ * if (result.success) {
304
+ * commitStorage(result.storage);
305
+ * } else {
306
+ * const userChoice = await askUser(`Migration failed: ${result.error}. Reset data?`);
307
+ * if (userChoice === 'reset') {
308
+ * commitStorage(createBlockStorage(initialData, currentVersion));
309
+ * }
310
+ * // else: abort, keep original
311
+ * }
298
312
  */
299
- export function updateStorage<TState, K extends keyof BlockStorage<TState>>(
300
- storage: BlockStorage<TState>,
301
- key: K,
302
- value: BlockStorage<TState>[K],
303
- ): BlockStorage<TState> {
304
- return { ...storage, [key]: value };
305
- }
313
+ export function migrateBlockStorage(
314
+ storage: BlockStorage<unknown>,
315
+ config: MigrateBlockStorageConfig,
316
+ ): MigrationResult<unknown> {
317
+ const { migrateBlockData, migratePluginData, newPluginRegistry, createPluginData } = config;
318
+
319
+ // Step 1: Migrate block data
320
+ let migratedData: unknown;
321
+ let newVersion: string;
322
+ try {
323
+ const result = migrateBlockData({ version: storage.__dataVersion, data: storage.__data });
324
+ migratedData = result.data;
325
+ newVersion = result.version;
326
+ } catch (error) {
327
+ return {
328
+ success: false,
329
+ error: error instanceof Error ? error.message : String(error),
330
+ failedAt: "block",
331
+ };
332
+ }
306
333
 
307
- // =============================================================================
308
- // Storage Handlers (for Phase 2 - Model-Level Customization)
309
- // =============================================================================
334
+ // Step 2: Migrate plugins
335
+ const oldPlugins = storage.__plugins ?? {};
336
+ const oldRegistry = storage.__pluginRegistry ?? {};
337
+ const newPlugins: Record<string, VersionedData<unknown>> = {};
338
+
339
+ for (const [pluginId, pluginName] of Object.entries(newPluginRegistry)) {
340
+ const existingEntry = oldPlugins[pluginId];
341
+ const existingName = oldRegistry[pluginId];
342
+
343
+ try {
344
+ if (existingEntry && existingName === pluginName) {
345
+ // Plugin exists with same type - migrate its data
346
+ const migrated = migratePluginData(pluginId, {
347
+ version: existingEntry.__dataVersion,
348
+ data: existingEntry.__data,
349
+ });
350
+ if (migrated) {
351
+ newPlugins[pluginId] = { __dataVersion: migrated.version, __data: migrated.data };
352
+ }
353
+ // If undefined returned, plugin is intentionally removed
354
+ } else {
355
+ // New plugin or type changed - create with initial data
356
+ const initial = createPluginData(pluginId);
357
+ newPlugins[pluginId] = { __dataVersion: initial.version, __data: initial.data };
358
+ }
359
+ } catch (error) {
360
+ return {
361
+ success: false,
362
+ error: error instanceof Error ? error.message : String(error),
363
+ failedAt: pluginId,
364
+ };
365
+ }
366
+ }
310
367
 
311
- /**
312
- * Interface for model-configurable storage operations.
313
- * These handlers allow block models to customize how storage is managed.
314
- */
315
- export interface BlockStorageHandlers<TState = unknown> {
316
- /**
317
- * Called when setState is invoked - transforms the new state before storing.
318
- * Default behavior: replaces the state directly.
319
- *
320
- * @param currentStorage - The current BlockStorage
321
- * @param newState - The new state being set
322
- * @returns The updated BlockStorage
323
- */
324
- transformStateForStorage?: (
325
- currentStorage: BlockStorage<TState>,
326
- newState: TState,
327
- ) => BlockStorage<TState>;
328
-
329
- /**
330
- * Called when reading state for args derivation.
331
- * Default behavior: returns the state directly.
332
- *
333
- * @param storage - The current BlockStorage
334
- * @returns The state to use for args derivation
335
- */
336
- deriveStateForArgs?: (storage: BlockStorage<TState>) => TState;
337
-
338
- /**
339
- * Called during storage schema migration.
340
- * Default behavior: updates stateVersion only.
341
- *
342
- * @param oldStorage - The storage before migration
343
- * @param fromVersion - The version migrating from
344
- * @param toVersion - The version migrating to
345
- * @returns The migrated BlockStorage
346
- */
347
- migrateStorage?: (
348
- oldStorage: BlockStorage<TState>,
349
- fromVersion: string,
350
- toVersion: string,
351
- ) => BlockStorage<TState>;
352
- }
368
+ // Step 3: Build final storage atomically
369
+ const migratedStorage: BlockStorage = {
370
+ [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,
371
+ __dataVersion: newVersion,
372
+ __data: migratedData,
373
+ __pluginRegistry: newPluginRegistry,
374
+ __plugins: newPlugins,
375
+ };
353
376
 
354
- /**
355
- * Default implementations of storage handlers
356
- */
357
- export const defaultBlockStorageHandlers: Required<BlockStorageHandlers<unknown>> = {
358
- transformStateForStorage: <TState>(
359
- storage: BlockStorage<TState>,
360
- newState: TState,
361
- ): BlockStorage<TState> =>
362
- updateStorageData(storage, { operation: "update-data", value: newState }),
363
-
364
- deriveStateForArgs: <TState>(storage: BlockStorage<TState>): TState => getStorageData(storage),
365
-
366
- migrateStorage: <TState>(
367
- storage: BlockStorage<TState>,
368
- _fromVersion: string,
369
- toVersion: string,
370
- ): BlockStorage<TState> => updateStorageDataVersion(storage, toVersion),
371
- };
377
+ return {
378
+ success: true,
379
+ storage: migratedStorage,
380
+ };
381
+ }
372
382
 
373
383
  /**
374
- * Merges custom handlers with defaults
384
+ * Gets plugin-specific data from BlockStorage (for UI)
375
385
  *
376
- * @param customHandlers - Custom handlers to merge
377
- * @returns Complete handlers with defaults for missing functions
386
+ * @param storage - The BlockStorage instance
387
+ * @param pluginId - The plugin instance id
388
+ * @returns The plugin data or undefined if not set
378
389
  */
379
- export function mergeBlockStorageHandlers<TState>(
380
- customHandlers?: BlockStorageHandlers<TState>,
381
- ): Required<BlockStorageHandlers<TState>> {
382
- return {
383
- ...defaultBlockStorageHandlers,
384
- ...customHandlers,
385
- } as Required<BlockStorageHandlers<TState>>;
390
+ export function getPluginData<TData = unknown>(
391
+ storage: BlockStorage,
392
+ pluginId: string,
393
+ ): TData | undefined {
394
+ const pluginEntry = storage.__plugins?.[pluginId];
395
+ if (!pluginEntry) return undefined;
396
+ return pluginEntry.__data as TData;
386
397
  }