@platforma-sdk/model 1.51.6 → 1.52.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 (89) hide show
  1. package/dist/bconfig/lambdas.d.ts +26 -4
  2. package/dist/bconfig/lambdas.d.ts.map +1 -1
  3. package/dist/bconfig/v3.d.ts +4 -2
  4. package/dist/bconfig/v3.d.ts.map +1 -1
  5. package/dist/block_api_v3.d.ts +32 -0
  6. package/dist/block_api_v3.d.ts.map +1 -0
  7. package/dist/block_migrations.cjs +138 -0
  8. package/dist/block_migrations.cjs.map +1 -0
  9. package/dist/block_migrations.d.ts +79 -0
  10. package/dist/block_migrations.d.ts.map +1 -0
  11. package/dist/block_migrations.js +136 -0
  12. package/dist/block_migrations.js.map +1 -0
  13. package/dist/block_model.cjs +222 -0
  14. package/dist/block_model.cjs.map +1 -0
  15. package/dist/block_model.d.ts +132 -0
  16. package/dist/block_model.d.ts.map +1 -0
  17. package/dist/block_model.js +220 -0
  18. package/dist/block_model.js.map +1 -0
  19. package/dist/block_storage.cjs +244 -0
  20. package/dist/block_storage.cjs.map +1 -0
  21. package/dist/block_storage.d.ts +208 -0
  22. package/dist/block_storage.d.ts.map +1 -0
  23. package/dist/block_storage.js +225 -0
  24. package/dist/block_storage.js.map +1 -0
  25. package/dist/block_storage_vm.cjs +264 -0
  26. package/dist/block_storage_vm.cjs.map +1 -0
  27. package/dist/block_storage_vm.d.ts +67 -0
  28. package/dist/block_storage_vm.d.ts.map +1 -0
  29. package/dist/block_storage_vm.js +260 -0
  30. package/dist/block_storage_vm.js.map +1 -0
  31. package/dist/builder.cjs +9 -6
  32. package/dist/builder.cjs.map +1 -1
  33. package/dist/builder.d.ts +15 -30
  34. package/dist/builder.d.ts.map +1 -1
  35. package/dist/builder.js +10 -7
  36. package/dist/builder.js.map +1 -1
  37. package/dist/components/PlDataTable.cjs.map +1 -1
  38. package/dist/components/PlDataTable.d.ts +2 -2
  39. package/dist/components/PlDataTable.d.ts.map +1 -1
  40. package/dist/components/PlDataTable.js.map +1 -1
  41. package/dist/index.cjs +25 -0
  42. package/dist/index.cjs.map +1 -1
  43. package/dist/index.d.ts +3 -0
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +4 -1
  46. package/dist/index.js.map +1 -1
  47. package/dist/internal.cjs +38 -0
  48. package/dist/internal.cjs.map +1 -1
  49. package/dist/internal.d.ts +21 -0
  50. package/dist/internal.d.ts.map +1 -1
  51. package/dist/internal.js +36 -1
  52. package/dist/internal.js.map +1 -1
  53. package/dist/package.json.cjs +1 -1
  54. package/dist/package.json.js +1 -1
  55. package/dist/platforma.d.ts +18 -3
  56. package/dist/platforma.d.ts.map +1 -1
  57. package/dist/render/api.cjs +43 -16
  58. package/dist/render/api.cjs.map +1 -1
  59. package/dist/render/api.d.ts +19 -7
  60. package/dist/render/api.d.ts.map +1 -1
  61. package/dist/render/api.js +42 -17
  62. package/dist/render/api.js.map +1 -1
  63. package/dist/render/internal.cjs.map +1 -1
  64. package/dist/render/internal.d.ts +3 -1
  65. package/dist/render/internal.d.ts.map +1 -1
  66. package/dist/render/internal.js.map +1 -1
  67. package/dist/render/util/label.cjs +9 -4
  68. package/dist/render/util/label.cjs.map +1 -1
  69. package/dist/render/util/label.d.ts.map +1 -1
  70. package/dist/render/util/label.js +9 -4
  71. package/dist/render/util/label.js.map +1 -1
  72. package/package.json +5 -5
  73. package/src/bconfig/lambdas.ts +35 -4
  74. package/src/bconfig/v3.ts +12 -2
  75. package/src/block_api_v3.ts +49 -0
  76. package/src/block_migrations.ts +173 -0
  77. package/src/block_model.ts +440 -0
  78. package/src/block_storage.test.ts +258 -0
  79. package/src/block_storage.ts +365 -0
  80. package/src/block_storage_vm.ts +349 -0
  81. package/src/builder.ts +24 -59
  82. package/src/components/PlDataTable.ts +2 -1
  83. package/src/index.ts +3 -0
  84. package/src/internal.ts +51 -0
  85. package/src/platforma.ts +31 -5
  86. package/src/render/api.ts +52 -21
  87. package/src/render/internal.ts +3 -1
  88. package/src/render/util/label.test.ts +25 -0
  89. package/src/render/util/label.ts +11 -5
@@ -0,0 +1,225 @@
1
+ /**
2
+ * BlockStorage - Typed storage abstraction for block persistent data.
3
+ *
4
+ * This module provides:
5
+ * - A typed structure for block storage with versioning and plugin support
6
+ * - Utility functions for manipulating storage
7
+ * - Handler interfaces for model-level customization
8
+ *
9
+ * @module block_storage
10
+ */
11
+ // =============================================================================
12
+ // Core Types
13
+ // =============================================================================
14
+ /**
15
+ * Discriminator key for BlockStorage format detection.
16
+ * This unique hash-based key identifies data as BlockStorage vs legacy formats.
17
+ */
18
+ const BLOCK_STORAGE_KEY = '__pl_a7f3e2b9__';
19
+ /**
20
+ * Current BlockStorage schema version.
21
+ * Increment this when the storage structure itself changes (not block state migrations).
22
+ */
23
+ const BLOCK_STORAGE_SCHEMA_VERSION = 'v1';
24
+ /**
25
+ * Type guard to check if a value is a valid BlockStorage object.
26
+ * Checks for the discriminator key and valid schema version.
27
+ */
28
+ function isBlockStorage(value) {
29
+ if (value === null || typeof value !== 'object')
30
+ return false;
31
+ const obj = value;
32
+ const schemaVersion = obj[BLOCK_STORAGE_KEY];
33
+ // Currently only 'v1' is valid, but this allows future versions
34
+ return schemaVersion === 'v1'; // Add more versions as schema evolves
35
+ }
36
+ // =============================================================================
37
+ // Factory Functions
38
+ // =============================================================================
39
+ /**
40
+ * Creates a BlockStorage with the given initial data
41
+ *
42
+ * @param initialData - The initial data value (defaults to empty object)
43
+ * @param version - The initial data version (defaults to 1)
44
+ * @returns A new BlockStorage instance with discriminator key
45
+ */
46
+ function createBlockStorage(initialData = {}, version = 1) {
47
+ return {
48
+ [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,
49
+ __dataVersion: version,
50
+ __data: initialData,
51
+ };
52
+ }
53
+ /**
54
+ * Normalizes raw storage data to BlockStorage format.
55
+ * If the input is already a BlockStorage, returns it as-is.
56
+ * If the input is legacy format (raw state), wraps it in BlockStorage structure.
57
+ *
58
+ * @param raw - Raw storage data (may be legacy format or BlockStorage)
59
+ * @returns Normalized BlockStorage
60
+ */
61
+ function normalizeBlockStorage(raw) {
62
+ if (isBlockStorage(raw)) {
63
+ return raw;
64
+ }
65
+ // Legacy format: raw is the state directly
66
+ return createBlockStorage(raw, 1);
67
+ }
68
+ // =============================================================================
69
+ // Data Access & Update Functions
70
+ // =============================================================================
71
+ /**
72
+ * Gets the data from BlockStorage
73
+ *
74
+ * @param storage - The BlockStorage instance
75
+ * @returns The data value
76
+ */
77
+ function getStorageData(storage) {
78
+ return storage.__data;
79
+ }
80
+ /**
81
+ * Derives data from raw block storage.
82
+ * This function is meant to be called from sdk/ui-vue to extract
83
+ * user-facing data from the raw storage returned by the middle layer.
84
+ *
85
+ * The middle layer returns raw storage (opaque to it), and the UI
86
+ * uses this function to derive the actual data value.
87
+ *
88
+ * @param rawStorage - Raw storage data from middle layer (may be any format)
89
+ * @returns The extracted data value, or undefined if storage is undefined/null
90
+ */
91
+ function deriveDataFromStorage(rawStorage) {
92
+ // Normalize to BlockStorage format (handles legacy formats too)
93
+ const storage = normalizeBlockStorage(rawStorage);
94
+ return getStorageData(storage);
95
+ }
96
+ /**
97
+ * Updates the data in BlockStorage (immutable)
98
+ *
99
+ * @param storage - The current BlockStorage
100
+ * @param payload - The update payload with operation and value
101
+ * @returns A new BlockStorage with updated data
102
+ */
103
+ function updateStorageData(storage, payload) {
104
+ switch (payload.operation) {
105
+ case 'update-data':
106
+ return { ...storage, __data: payload.value };
107
+ default:
108
+ throw new Error(`Unknown storage operation: ${payload.operation}`);
109
+ }
110
+ }
111
+ /**
112
+ * Gets the data version from BlockStorage
113
+ *
114
+ * @param storage - The BlockStorage instance
115
+ * @returns The data version number
116
+ */
117
+ function getStorageDataVersion(storage) {
118
+ return storage.__dataVersion;
119
+ }
120
+ /**
121
+ * Updates the data version in BlockStorage (immutable)
122
+ *
123
+ * @param storage - The current BlockStorage
124
+ * @param version - The new version number
125
+ * @returns A new BlockStorage with updated version
126
+ */
127
+ function updateStorageDataVersion(storage, version) {
128
+ return { ...storage, __dataVersion: version };
129
+ }
130
+ // =============================================================================
131
+ // Plugin Data Functions
132
+ // =============================================================================
133
+ /**
134
+ * Gets plugin-specific data from BlockStorage
135
+ *
136
+ * @param storage - The BlockStorage instance
137
+ * @param pluginName - The plugin name (without `@plugin/` prefix)
138
+ * @returns The plugin data or undefined if not set
139
+ */
140
+ function getPluginData(storage, pluginName) {
141
+ const key = `@plugin/${pluginName}`;
142
+ return storage[key];
143
+ }
144
+ /**
145
+ * Sets plugin-specific data in BlockStorage (immutable)
146
+ *
147
+ * @param storage - The current BlockStorage
148
+ * @param pluginName - The plugin name (without `@plugin/` prefix)
149
+ * @param data - The plugin data to store
150
+ * @returns A new BlockStorage with updated plugin data
151
+ */
152
+ function setPluginData(storage, pluginName, data) {
153
+ const key = `@plugin/${pluginName}`;
154
+ return { ...storage, [key]: data };
155
+ }
156
+ /**
157
+ * Removes plugin-specific data from BlockStorage (immutable)
158
+ *
159
+ * @param storage - The current BlockStorage
160
+ * @param pluginName - The plugin name (without `@plugin/` prefix)
161
+ * @returns A new BlockStorage with the plugin data removed
162
+ */
163
+ function removePluginData(storage, pluginName) {
164
+ const key = `@plugin/${pluginName}`;
165
+ const { [key]: _, ...rest } = storage;
166
+ return rest;
167
+ }
168
+ /**
169
+ * Gets all plugin names that have data stored
170
+ *
171
+ * @param storage - The BlockStorage instance
172
+ * @returns Array of plugin names (without `@plugin/` prefix)
173
+ */
174
+ function getPluginNames(storage) {
175
+ return Object.keys(storage)
176
+ .filter((key) => key.startsWith('@plugin/'))
177
+ .map((key) => key.slice('@plugin/'.length));
178
+ }
179
+ // =============================================================================
180
+ // Generic Storage Access
181
+ // =============================================================================
182
+ /**
183
+ * Gets a value from BlockStorage by key
184
+ *
185
+ * @param storage - The BlockStorage instance
186
+ * @param key - The key to retrieve
187
+ * @returns The value at the given key
188
+ */
189
+ function getFromStorage(storage, key) {
190
+ return storage[key];
191
+ }
192
+ /**
193
+ * Updates a value in BlockStorage by key (immutable)
194
+ *
195
+ * @param storage - The current BlockStorage
196
+ * @param key - The key to update
197
+ * @param value - The new value
198
+ * @returns A new BlockStorage with the updated value
199
+ */
200
+ function updateStorage(storage, key, value) {
201
+ return { ...storage, [key]: value };
202
+ }
203
+ /**
204
+ * Default implementations of storage handlers
205
+ */
206
+ const defaultBlockStorageHandlers = {
207
+ transformStateForStorage: (storage, newState) => updateStorageData(storage, { operation: 'update-data', value: newState }),
208
+ deriveStateForArgs: (storage) => getStorageData(storage),
209
+ migrateStorage: (storage, _fromVersion, toVersion) => updateStorageDataVersion(storage, toVersion),
210
+ };
211
+ /**
212
+ * Merges custom handlers with defaults
213
+ *
214
+ * @param customHandlers - Custom handlers to merge
215
+ * @returns Complete handlers with defaults for missing functions
216
+ */
217
+ function mergeBlockStorageHandlers(customHandlers) {
218
+ return {
219
+ ...defaultBlockStorageHandlers,
220
+ ...customHandlers,
221
+ };
222
+ }
223
+
224
+ export { BLOCK_STORAGE_KEY, BLOCK_STORAGE_SCHEMA_VERSION, createBlockStorage, defaultBlockStorageHandlers, deriveDataFromStorage, getFromStorage, getPluginData, getPluginNames, getStorageData, getStorageDataVersion, isBlockStorage, mergeBlockStorageHandlers, normalizeBlockStorage, removePluginData, setPluginData, updateStorage, updateStorageData, updateStorageDataVersion };
225
+ //# sourceMappingURL=block_storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block_storage.js","sources":["../src/block_storage.ts"],"sourcesContent":["/**\n * BlockStorage - Typed storage abstraction for block persistent data.\n *\n * This module provides:\n * - A typed structure for block storage with versioning and plugin support\n * - Utility functions for manipulating storage\n * - Handler interfaces for model-level customization\n *\n * @module block_storage\n */\n\n// =============================================================================\n// Core Types\n// =============================================================================\n\n/**\n * Discriminator key for BlockStorage format detection.\n * This unique hash-based key identifies data as BlockStorage vs legacy formats.\n */\nexport const BLOCK_STORAGE_KEY = '__pl_a7f3e2b9__';\n\n/**\n * Current BlockStorage schema version.\n * Increment this when the storage structure itself changes (not block state migrations).\n */\nexport const BLOCK_STORAGE_SCHEMA_VERSION = 'v1';\n\n/**\n * Type for valid schema versions\n */\nexport type BlockStorageSchemaVersion = 'v1'; // Add 'v2', 'v3', etc. as schema evolves\n\n/**\n * Plugin key type - keys starting with `@plugin/` are reserved for plugin data\n */\nexport type PluginKey = `@plugin/${string}`;\n\n/**\n * Core BlockStorage type that holds:\n * - __pl_a7f3e2b9__: Schema version (discriminator key identifies BlockStorage format)\n * - __dataVersion: Version number for block data migrations\n * - __data: The block's user-facing data (state)\n * - @plugin/*: Optional plugin-specific data\n */\nexport type BlockStorage<TState = unknown> = {\n /** Schema version - the key itself is the discriminator */\n readonly [BLOCK_STORAGE_KEY]: BlockStorageSchemaVersion;\n /** Version of the block data, used for migrations */\n __dataVersion: number;\n /** The block's user-facing data (state) */\n __data: TState;\n} & {\n /** Plugin-specific data, keyed by `@plugin/<pluginName>` */\n [K in PluginKey]?: unknown;\n};\n\n/**\n * Type guard to check if a value is a valid BlockStorage object.\n * Checks for the discriminator key and valid schema version.\n */\nexport function isBlockStorage(value: unknown): value is BlockStorage {\n if (value === null || typeof value !== 'object') return false;\n const obj = value as Record<string, unknown>;\n const schemaVersion = obj[BLOCK_STORAGE_KEY];\n // Currently only 'v1' is valid, but this allows future versions\n return schemaVersion === 'v1'; // Add more versions as schema evolves\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Creates a BlockStorage with the given initial data\n *\n * @param initialData - The initial data value (defaults to empty object)\n * @param version - The initial data version (defaults to 1)\n * @returns A new BlockStorage instance with discriminator key\n */\nexport function createBlockStorage<TState = unknown>(\n initialData: TState = {} as TState,\n version: number = 1,\n): BlockStorage<TState> {\n return {\n [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,\n __dataVersion: version,\n __data: initialData,\n };\n}\n\n/**\n * Normalizes raw storage data to BlockStorage format.\n * If the input is already a BlockStorage, returns it as-is.\n * If the input is legacy format (raw state), wraps it in BlockStorage structure.\n *\n * @param raw - Raw storage data (may be legacy format or BlockStorage)\n * @returns Normalized BlockStorage\n */\nexport function normalizeBlockStorage<TState = unknown>(raw: unknown): BlockStorage<TState> {\n if (isBlockStorage(raw)) {\n return raw as BlockStorage<TState>;\n }\n // Legacy format: raw is the state directly\n return createBlockStorage(raw as TState, 1);\n}\n\n// =============================================================================\n// Data Access & Update Functions\n// =============================================================================\n\n/**\n * Gets the data from BlockStorage\n *\n * @param storage - The BlockStorage instance\n * @returns The data value\n */\nexport function getStorageData<TState>(storage: BlockStorage<TState>): TState {\n return storage.__data;\n}\n\n/**\n * Derives data from raw block storage.\n * This function is meant to be called from sdk/ui-vue to extract\n * user-facing data from the raw storage returned by the middle layer.\n *\n * The middle layer returns raw storage (opaque to it), and the UI\n * uses this function to derive the actual data value.\n *\n * @param rawStorage - Raw storage data from middle layer (may be any format)\n * @returns The extracted data value, or undefined if storage is undefined/null\n */\nexport function deriveDataFromStorage<TData = unknown>(\n rawStorage: unknown,\n): TData {\n // Normalize to BlockStorage format (handles legacy formats too)\n const storage = normalizeBlockStorage<TData>(rawStorage);\n return getStorageData(storage);\n}\n\n/** Payload for storage mutation operations. SDK defines specific operations. */\nexport type MutateStoragePayload<T = unknown> = { operation: 'update-data'; value: T };\n\n/**\n * Updates the data in BlockStorage (immutable)\n *\n * @param storage - The current BlockStorage\n * @param payload - The update payload with operation and value\n * @returns A new BlockStorage with updated data\n */\nexport function updateStorageData<TValue = unknown>(\n storage: BlockStorage<TValue>,\n payload: MutateStoragePayload<TValue>,\n): BlockStorage<TValue> {\n switch (payload.operation) {\n case 'update-data':\n return { ...storage, __data: payload.value };\n default:\n throw new Error(`Unknown storage operation: ${(payload as { operation: string }).operation}`);\n }\n}\n\n/**\n * Gets the data version from BlockStorage\n *\n * @param storage - The BlockStorage instance\n * @returns The data version number\n */\nexport function getStorageDataVersion(storage: BlockStorage): number {\n return storage.__dataVersion;\n}\n\n/**\n * Updates the data version in BlockStorage (immutable)\n *\n * @param storage - The current BlockStorage\n * @param version - The new version number\n * @returns A new BlockStorage with updated version\n */\nexport function updateStorageDataVersion<TState>(\n storage: BlockStorage<TState>,\n version: number,\n): BlockStorage<TState> {\n return { ...storage, __dataVersion: version };\n}\n\n// =============================================================================\n// Plugin Data Functions\n// =============================================================================\n\n/**\n * Gets plugin-specific data from BlockStorage\n *\n * @param storage - The BlockStorage instance\n * @param pluginName - The plugin name (without `@plugin/` prefix)\n * @returns The plugin data or undefined if not set\n */\nexport function getPluginData<TData = unknown>(\n storage: BlockStorage,\n pluginName: string,\n): TData | undefined {\n const key: PluginKey = `@plugin/${pluginName}`;\n return storage[key] as TData | undefined;\n}\n\n/**\n * Sets plugin-specific data in BlockStorage (immutable)\n *\n * @param storage - The current BlockStorage\n * @param pluginName - The plugin name (without `@plugin/` prefix)\n * @param data - The plugin data to store\n * @returns A new BlockStorage with updated plugin data\n */\nexport function setPluginData<TState>(\n storage: BlockStorage<TState>,\n pluginName: string,\n data: unknown,\n): BlockStorage<TState> {\n const key: PluginKey = `@plugin/${pluginName}`;\n return { ...storage, [key]: data };\n}\n\n/**\n * Removes plugin-specific data from BlockStorage (immutable)\n *\n * @param storage - The current BlockStorage\n * @param pluginName - The plugin name (without `@plugin/` prefix)\n * @returns A new BlockStorage with the plugin data removed\n */\nexport function removePluginData<TState>(\n storage: BlockStorage<TState>,\n pluginName: string,\n): BlockStorage<TState> {\n const key: PluginKey = `@plugin/${pluginName}`;\n const { [key]: _, ...rest } = storage;\n return rest as BlockStorage<TState>;\n}\n\n/**\n * Gets all plugin names that have data stored\n *\n * @param storage - The BlockStorage instance\n * @returns Array of plugin names (without `@plugin/` prefix)\n */\nexport function getPluginNames(storage: BlockStorage): string[] {\n return Object.keys(storage)\n .filter((key): key is PluginKey => key.startsWith('@plugin/'))\n .map((key) => key.slice('@plugin/'.length));\n}\n\n// =============================================================================\n// Generic Storage Access\n// =============================================================================\n\n/**\n * Gets a value from BlockStorage by key\n *\n * @param storage - The BlockStorage instance\n * @param key - The key to retrieve\n * @returns The value at the given key\n */\nexport function getFromStorage<\n TState,\n K extends keyof BlockStorage<TState>,\n>(storage: BlockStorage<TState>, key: K): BlockStorage<TState>[K] {\n return storage[key];\n}\n\n/**\n * Updates a value in BlockStorage by key (immutable)\n *\n * @param storage - The current BlockStorage\n * @param key - The key to update\n * @param value - The new value\n * @returns A new BlockStorage with the updated value\n */\nexport function updateStorage<\n TState,\n K extends keyof BlockStorage<TState>,\n>(\n storage: BlockStorage<TState>,\n key: K,\n value: BlockStorage<TState>[K],\n): BlockStorage<TState> {\n return { ...storage, [key]: value };\n}\n\n// =============================================================================\n// Storage Handlers (for Phase 2 - Model-Level Customization)\n// =============================================================================\n\n/**\n * Interface for model-configurable storage operations.\n * These handlers allow block models to customize how storage is managed.\n */\nexport interface BlockStorageHandlers<TState = unknown> {\n /**\n * Called when setState is invoked - transforms the new state before storing.\n * Default behavior: replaces the state directly.\n *\n * @param currentStorage - The current BlockStorage\n * @param newState - The new state being set\n * @returns The updated BlockStorage\n */\n transformStateForStorage?: (\n currentStorage: BlockStorage<TState>,\n newState: TState,\n ) => BlockStorage<TState>;\n\n /**\n * Called when reading state for args derivation.\n * Default behavior: returns the state directly.\n *\n * @param storage - The current BlockStorage\n * @returns The state to use for args derivation\n */\n deriveStateForArgs?: (storage: BlockStorage<TState>) => TState;\n\n /**\n * Called during storage schema migration.\n * Default behavior: updates stateVersion only.\n *\n * @param oldStorage - The storage before migration\n * @param fromVersion - The version migrating from\n * @param toVersion - The version migrating to\n * @returns The migrated BlockStorage\n */\n migrateStorage?: (\n oldStorage: BlockStorage<TState>,\n fromVersion: number,\n toVersion: number,\n ) => BlockStorage<TState>;\n}\n\n/**\n * Default implementations of storage handlers\n */\nexport const defaultBlockStorageHandlers: Required<BlockStorageHandlers<unknown>> = {\n transformStateForStorage: <TState>(\n storage: BlockStorage<TState>,\n newState: TState,\n ): BlockStorage<TState> => updateStorageData(storage, { operation: 'update-data', value: newState }),\n\n deriveStateForArgs: <TState>(storage: BlockStorage<TState>): TState => getStorageData(storage),\n\n migrateStorage: <TState>(\n storage: BlockStorage<TState>,\n _fromVersion: number,\n toVersion: number,\n ): BlockStorage<TState> => updateStorageDataVersion(storage, toVersion),\n};\n\n/**\n * Merges custom handlers with defaults\n *\n * @param customHandlers - Custom handlers to merge\n * @returns Complete handlers with defaults for missing functions\n */\nexport function mergeBlockStorageHandlers<TState>(\n customHandlers?: BlockStorageHandlers<TState>,\n): Required<BlockStorageHandlers<TState>> {\n return {\n ...defaultBlockStorageHandlers,\n ...customHandlers,\n } as Required<BlockStorageHandlers<TState>>;\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,264 @@
1
+ 'use strict';
2
+
3
+ var block_storage = require('./block_storage.cjs');
4
+ var internal = require('./internal.cjs');
5
+
6
+ /**
7
+ * BlockStorage VM Integration - Internal module for VM-based storage operations.
8
+ *
9
+ * This module auto-registers internal callbacks that the middle layer can invoke
10
+ * to perform storage transformations. Block developers never interact with these
11
+ * directly - they only see `state`.
12
+ *
13
+ * Registered callbacks (all prefixed with `__pl_` for internal SDK use):
14
+ * - `__pl_storage_normalize`: (rawStorage) => { storage, data }
15
+ * - `__pl_storage_applyUpdate`: (currentStorageJson, payload) => updatedStorageJson
16
+ * - `__pl_storage_getInfo`: (rawStorage) => JSON string with storage info
17
+ * - `__pl_storage_migrate`: (currentStorageJson) => MigrationResult
18
+ * - `__pl_args_derive`: (storageJson) => ArgsDeriveResult
19
+ * - `__pl_prerunArgs_derive`: (storageJson) => ArgsDeriveResult
20
+ *
21
+ * Callbacks registered by DataModel.registerCallbacks():
22
+ * - `__pl_data_initial`: () => initial data
23
+ * - `__pl_data_upgrade`: (versioned) => UpgradeResult
24
+ * - `__pl_storage_initial`: () => initial BlockStorage as JSON string
25
+ *
26
+ * @module block_storage_vm
27
+ * @internal
28
+ */
29
+ /**
30
+ * Normalizes raw storage data and extracts state.
31
+ * Handles all formats:
32
+ * - New BlockStorage format (has discriminator)
33
+ * - Legacy V1/V2 format ({ args, uiState })
34
+ * - Raw V3 state (any other format)
35
+ *
36
+ * @param rawStorage - Raw data from blockStorage field (may be JSON string or object)
37
+ * @returns Object with normalized storage and extracted state
38
+ */
39
+ function normalizeStorage(rawStorage) {
40
+ // Handle undefined/null
41
+ if (rawStorage === undefined || rawStorage === null) {
42
+ const storage = block_storage.createBlockStorage({});
43
+ return { storage, data: {} };
44
+ }
45
+ // Parse JSON string if needed
46
+ let parsed = rawStorage;
47
+ if (typeof rawStorage === 'string') {
48
+ try {
49
+ parsed = JSON.parse(rawStorage);
50
+ }
51
+ catch {
52
+ // If parsing fails, treat string as the data
53
+ const storage = block_storage.createBlockStorage(rawStorage);
54
+ return { storage, data: rawStorage };
55
+ }
56
+ }
57
+ // Check for BlockStorage format (has discriminator)
58
+ if (block_storage.isBlockStorage(parsed)) {
59
+ return { storage: parsed, data: block_storage.getStorageData(parsed) };
60
+ }
61
+ // Check for legacy V1/V2 format: { args, uiState }
62
+ if (isLegacyModelV1ApiFormat(parsed)) {
63
+ // For legacy format, the whole object IS the data
64
+ const storage = block_storage.createBlockStorage(parsed);
65
+ return { storage, data: parsed };
66
+ }
67
+ // Raw V3 data - wrap it
68
+ const storage = block_storage.createBlockStorage(parsed);
69
+ return { storage, data: parsed };
70
+ }
71
+ /**
72
+ * Applies a state update to existing storage.
73
+ * Used when setData is called from the frontend.
74
+ *
75
+ * @param currentStorageJson - Current storage as JSON string (must be defined)
76
+ * @param newData - New data from application
77
+ * @returns Updated storage as JSON string
78
+ */
79
+ function applyStorageUpdate(currentStorageJson, payload) {
80
+ const { storage: currentStorage } = normalizeStorage(currentStorageJson);
81
+ // Update data while preserving other storage fields (version, plugins)
82
+ const updatedStorage = block_storage.updateStorageData(currentStorage, payload);
83
+ return JSON.stringify(updatedStorage);
84
+ }
85
+ /**
86
+ * Checks if data is in legacy Model API v1 format.
87
+ * Legacy format has { args, uiState? } at top level without the BlockStorage discriminator.
88
+ */
89
+ function isLegacyModelV1ApiFormat(data) {
90
+ if (data === null || typeof data !== 'object')
91
+ return false;
92
+ if (block_storage.isBlockStorage(data))
93
+ return false;
94
+ const obj = data;
95
+ return 'args' in obj;
96
+ }
97
+ // =============================================================================
98
+ // Auto-register internal callbacks when module is loaded in VM
99
+ // =============================================================================
100
+ // Register normalize callback
101
+ internal.tryRegisterCallback('__pl_storage_normalize', (rawStorage) => {
102
+ return normalizeStorage(rawStorage);
103
+ });
104
+ // Register apply update callback (requires existing storage)
105
+ internal.tryRegisterCallback('__pl_storage_applyUpdate', (currentStorageJson, payload) => {
106
+ return applyStorageUpdate(currentStorageJson, payload);
107
+ });
108
+ /**
109
+ * Gets storage info from raw storage data.
110
+ * Returns structured info about the storage state.
111
+ *
112
+ * @param rawStorage - Raw data from blockStorage field (may be JSON string or object)
113
+ * @returns JSON string with storage info
114
+ */
115
+ function getStorageInfo(rawStorage) {
116
+ const { storage } = normalizeStorage(rawStorage);
117
+ const info = {
118
+ dataVersion: storage.__dataVersion,
119
+ };
120
+ return JSON.stringify(info);
121
+ }
122
+ // Register get info callback
123
+ internal.tryRegisterCallback('__pl_storage_getInfo', (rawStorage) => {
124
+ return getStorageInfo(rawStorage);
125
+ });
126
+ /**
127
+ * Runs storage migration using the DataModel's upgrade callback.
128
+ * This is the main entry point for the middle layer to trigger migrations.
129
+ *
130
+ * Uses the '__pl_data_upgrade' callback registered by DataModel.registerCallbacks() which:
131
+ * - Handles all migration logic internally
132
+ * - Returns { version, data, warning? } - warning present if reset to initial data
133
+ *
134
+ * @param currentStorageJson - Current storage as JSON string (or undefined)
135
+ * @returns MigrationResult
136
+ */
137
+ function migrateStorage(currentStorageJson) {
138
+ // Get the callback registry context
139
+ const ctx = internal.tryGetCfgRenderCtx();
140
+ if (ctx === undefined) {
141
+ return { error: 'Not in config rendering context' };
142
+ }
143
+ // Normalize storage to get current data and version
144
+ const { storage: currentStorage, data: currentData } = normalizeStorage(currentStorageJson);
145
+ const currentVersion = currentStorage.__dataVersion;
146
+ // Helper to create storage with given data and version
147
+ const createStorageJson = (data, version) => {
148
+ return JSON.stringify({
149
+ ...currentStorage,
150
+ __dataVersion: version,
151
+ __data: data,
152
+ });
153
+ };
154
+ // Get the upgrade callback (registered by DataModel.registerCallbacks())
155
+ const upgradeCallback = ctx.callbackRegistry['__pl_data_upgrade'];
156
+ if (typeof upgradeCallback !== 'function') {
157
+ return { error: '__pl_data_upgrade callback not found (DataModel not registered)' };
158
+ }
159
+ // Call the migrator's upgrade function
160
+ let result;
161
+ try {
162
+ result = upgradeCallback({ version: currentVersion, data: currentData });
163
+ }
164
+ catch (e) {
165
+ const errorMsg = e instanceof Error ? e.message : String(e);
166
+ return { error: `upgrade() threw: ${errorMsg}` };
167
+ }
168
+ // Build info message
169
+ const info = result.version === currentVersion
170
+ ? `No migration needed (v${currentVersion})`
171
+ : result.warning
172
+ ? `Reset to initial data (v${result.version})`
173
+ : `Migrated v${currentVersion}→v${result.version}`;
174
+ return {
175
+ newStorageJson: createStorageJson(result.data, result.version),
176
+ info,
177
+ warn: result.warning,
178
+ };
179
+ }
180
+ // Register migrate callback
181
+ internal.tryRegisterCallback('__pl_storage_migrate', (currentStorageJson) => {
182
+ return migrateStorage(currentStorageJson);
183
+ });
184
+ /**
185
+ * Derives args from storage using the registered 'args' callback.
186
+ * This extracts data from storage and passes it to the block's args() function.
187
+ *
188
+ * @param storageJson - Storage as JSON string
189
+ * @returns ArgsDeriveResult with derived args or error
190
+ */
191
+ function deriveArgsFromStorage(storageJson) {
192
+ const ctx = internal.tryGetCfgRenderCtx();
193
+ if (ctx === undefined) {
194
+ return { error: 'Not in config rendering context' };
195
+ }
196
+ // Extract data from storage
197
+ const { data } = normalizeStorage(storageJson);
198
+ // Get the args callback (registered by BlockModelV3.args())
199
+ const argsCallback = ctx.callbackRegistry['args'];
200
+ if (typeof argsCallback !== 'function') {
201
+ return { error: 'args callback not found' };
202
+ }
203
+ // Call the args callback with extracted data
204
+ try {
205
+ const result = argsCallback(data);
206
+ return { value: result };
207
+ }
208
+ catch (e) {
209
+ const errorMsg = e instanceof Error ? e.message : String(e);
210
+ return { error: `args() threw: ${errorMsg}` };
211
+ }
212
+ }
213
+ // Register args derivation callback
214
+ internal.tryRegisterCallback('__pl_args_derive', (storageJson) => {
215
+ return deriveArgsFromStorage(storageJson);
216
+ });
217
+ /**
218
+ * Derives prerunArgs from storage using the registered 'prerunArgs' callback.
219
+ * Falls back to 'args' callback if 'prerunArgs' is not defined.
220
+ *
221
+ * @param storageJson - Storage as JSON string
222
+ * @returns ArgsDeriveResult with derived prerunArgs or error
223
+ */
224
+ function derivePrerunArgsFromStorage(storageJson) {
225
+ const ctx = internal.tryGetCfgRenderCtx();
226
+ if (ctx === undefined) {
227
+ return { error: 'Not in config rendering context' };
228
+ }
229
+ // Extract data from storage
230
+ const { data } = normalizeStorage(storageJson);
231
+ // Try prerunArgs callback first
232
+ const prerunArgsCallback = ctx.callbackRegistry['prerunArgs'];
233
+ if (typeof prerunArgsCallback === 'function') {
234
+ try {
235
+ const result = prerunArgsCallback(data);
236
+ return { value: result };
237
+ }
238
+ catch (e) {
239
+ const errorMsg = e instanceof Error ? e.message : String(e);
240
+ return { error: `prerunArgs() threw: ${errorMsg}` };
241
+ }
242
+ }
243
+ // Fall back to args callback
244
+ const argsCallback = ctx.callbackRegistry['args'];
245
+ if (typeof argsCallback !== 'function') {
246
+ return { error: 'args callback not found (fallback from missing prerunArgs)' };
247
+ }
248
+ try {
249
+ const result = argsCallback(data);
250
+ return { value: result };
251
+ }
252
+ catch (e) {
253
+ const errorMsg = e instanceof Error ? e.message : String(e);
254
+ return { error: `args() threw (fallback): ${errorMsg}` };
255
+ }
256
+ }
257
+ // Register prerunArgs derivation callback
258
+ internal.tryRegisterCallback('__pl_prerunArgs_derive', (storageJson) => {
259
+ return derivePrerunArgsFromStorage(storageJson);
260
+ });
261
+
262
+ exports.BLOCK_STORAGE_KEY = block_storage.BLOCK_STORAGE_KEY;
263
+ exports.BLOCK_STORAGE_SCHEMA_VERSION = block_storage.BLOCK_STORAGE_SCHEMA_VERSION;
264
+ //# sourceMappingURL=block_storage_vm.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block_storage_vm.cjs","sources":["../src/block_storage_vm.ts"],"sourcesContent":["/**\n * BlockStorage VM Integration - Internal module for VM-based storage operations.\n *\n * This module auto-registers internal callbacks that the middle layer can invoke\n * to perform storage transformations. Block developers never interact with these\n * directly - they only see `state`.\n *\n * Registered callbacks (all prefixed with `__pl_` for internal SDK use):\n * - `__pl_storage_normalize`: (rawStorage) => { storage, data }\n * - `__pl_storage_applyUpdate`: (currentStorageJson, payload) => updatedStorageJson\n * - `__pl_storage_getInfo`: (rawStorage) => JSON string with storage info\n * - `__pl_storage_migrate`: (currentStorageJson) => MigrationResult\n * - `__pl_args_derive`: (storageJson) => ArgsDeriveResult\n * - `__pl_prerunArgs_derive`: (storageJson) => ArgsDeriveResult\n *\n * Callbacks registered by DataModel.registerCallbacks():\n * - `__pl_data_initial`: () => initial data\n * - `__pl_data_upgrade`: (versioned) => UpgradeResult\n * - `__pl_storage_initial`: () => initial BlockStorage as JSON string\n *\n * @module block_storage_vm\n * @internal\n */\n\nimport {\n BLOCK_STORAGE_KEY,\n BLOCK_STORAGE_SCHEMA_VERSION,\n type BlockStorage,\n type MutateStoragePayload,\n createBlockStorage,\n getStorageData,\n isBlockStorage,\n updateStorageData,\n} from './block_storage';\nimport { tryGetCfgRenderCtx, tryRegisterCallback } from './internal';\n\n/**\n * Result of storage normalization\n */\nexport interface NormalizeStorageResult {\n /** The normalized BlockStorage object */\n storage: BlockStorage;\n /** The extracted data (what developers see) */\n data: unknown;\n}\n\n/**\n * Normalizes raw storage data and extracts state.\n * Handles all formats:\n * - New BlockStorage format (has discriminator)\n * - Legacy V1/V2 format ({ args, uiState })\n * - Raw V3 state (any other format)\n *\n * @param rawStorage - Raw data from blockStorage field (may be JSON string or object)\n * @returns Object with normalized storage and extracted state\n */\nfunction normalizeStorage(rawStorage: unknown): NormalizeStorageResult {\n // Handle undefined/null\n if (rawStorage === undefined || rawStorage === null) {\n const storage = createBlockStorage({});\n return { storage, data: {} };\n }\n\n // Parse JSON string if needed\n let parsed = rawStorage;\n if (typeof rawStorage === 'string') {\n try {\n parsed = JSON.parse(rawStorage);\n } catch {\n // If parsing fails, treat string as the data\n const storage = createBlockStorage(rawStorage);\n return { storage, data: rawStorage };\n }\n }\n\n // Check for BlockStorage format (has discriminator)\n if (isBlockStorage(parsed)) {\n return { storage: parsed, data: getStorageData(parsed) };\n }\n\n // Check for legacy V1/V2 format: { args, uiState }\n if (isLegacyModelV1ApiFormat(parsed)) {\n // For legacy format, the whole object IS the data\n const storage = createBlockStorage(parsed);\n return { storage, data: parsed };\n }\n\n // Raw V3 data - wrap it\n const storage = createBlockStorage(parsed);\n return { storage, data: parsed };\n}\n\n/**\n * Applies a state update to existing storage.\n * Used when setData is called from the frontend.\n *\n * @param currentStorageJson - Current storage as JSON string (must be defined)\n * @param newData - New data from application\n * @returns Updated storage as JSON string\n */\nfunction applyStorageUpdate(currentStorageJson: string, payload: MutateStoragePayload): string {\n const { storage: currentStorage } = normalizeStorage(currentStorageJson);\n\n // Update data while preserving other storage fields (version, plugins)\n const updatedStorage = updateStorageData(currentStorage, payload);\n\n return JSON.stringify(updatedStorage);\n}\n\n/**\n * Checks if data is in legacy Model API v1 format.\n * Legacy format has { args, uiState? } at top level without the BlockStorage discriminator.\n */\nfunction isLegacyModelV1ApiFormat(data: unknown): data is { args?: unknown } {\n if (data === null || typeof data !== 'object') return false;\n if (isBlockStorage(data)) return false;\n\n const obj = data as Record<string, unknown>;\n return 'args' in obj;\n}\n\n// =============================================================================\n// Auto-register internal callbacks when module is loaded in VM\n// =============================================================================\n\n// Register normalize callback\ntryRegisterCallback('__pl_storage_normalize', (rawStorage: unknown) => {\n return normalizeStorage(rawStorage);\n});\n\n// Register apply update callback (requires existing storage)\ntryRegisterCallback('__pl_storage_applyUpdate', (currentStorageJson: string, payload: MutateStoragePayload) => {\n return applyStorageUpdate(currentStorageJson, payload);\n});\n\n/**\n * Storage info result returned by __pl_storage_getInfo callback.\n */\nexport interface StorageInfo {\n /** Current data version (1-based, starts at 1) */\n dataVersion: number;\n}\n\n/**\n * Gets storage info from raw storage data.\n * Returns structured info about the storage state.\n *\n * @param rawStorage - Raw data from blockStorage field (may be JSON string or object)\n * @returns JSON string with storage info\n */\nfunction getStorageInfo(rawStorage: unknown): string {\n const { storage } = normalizeStorage(rawStorage);\n const info: StorageInfo = {\n dataVersion: storage.__dataVersion,\n };\n return JSON.stringify(info);\n}\n\n// Register get info callback\ntryRegisterCallback('__pl_storage_getInfo', (rawStorage: unknown) => {\n return getStorageInfo(rawStorage);\n});\n\n// =============================================================================\n// Migration Support\n// =============================================================================\n\n/**\n * Result of storage migration.\n * Returned by __pl_storage_migrate callback.\n *\n * - Error result: { error: string } - serious failure (no context, etc.)\n * - Success result: { newStorageJson: string, info: string, warn?: string } - migration succeeded or reset to initial\n */\nexport type MigrationResult =\n | { error: string }\n | { error?: undefined; newStorageJson: string; info: string; warn?: string };\n\n/** Result from Migrator.upgrade() */\ninterface UpgradeResult {\n version: number;\n data: unknown;\n warning?: string;\n}\n\n/**\n * Runs storage migration using the DataModel's upgrade callback.\n * This is the main entry point for the middle layer to trigger migrations.\n *\n * Uses the '__pl_data_upgrade' callback registered by DataModel.registerCallbacks() which:\n * - Handles all migration logic internally\n * - Returns { version, data, warning? } - warning present if reset to initial data\n *\n * @param currentStorageJson - Current storage as JSON string (or undefined)\n * @returns MigrationResult\n */\nfunction migrateStorage(currentStorageJson: string | undefined): MigrationResult {\n // Get the callback registry context\n const ctx = tryGetCfgRenderCtx();\n if (ctx === undefined) {\n return { error: 'Not in config rendering context' };\n }\n\n // Normalize storage to get current data and version\n const { storage: currentStorage, data: currentData } = normalizeStorage(currentStorageJson);\n const currentVersion = currentStorage.__dataVersion;\n\n // Helper to create storage with given data and version\n const createStorageJson = (data: unknown, version: number): string => {\n return JSON.stringify({\n ...currentStorage,\n __dataVersion: version,\n __data: data,\n });\n };\n\n // Get the upgrade callback (registered by DataModel.registerCallbacks())\n const upgradeCallback = ctx.callbackRegistry['__pl_data_upgrade'] as ((v: { version: number; data: unknown }) => UpgradeResult) | undefined;\n if (typeof upgradeCallback !== 'function') {\n return { error: '__pl_data_upgrade callback not found (DataModel not registered)' };\n }\n\n // Call the migrator's upgrade function\n let result: UpgradeResult;\n try {\n result = upgradeCallback({ version: currentVersion, data: currentData });\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n return { error: `upgrade() threw: ${errorMsg}` };\n }\n\n // Build info message\n const info = result.version === currentVersion\n ? `No migration needed (v${currentVersion})`\n : result.warning\n ? `Reset to initial data (v${result.version})`\n : `Migrated v${currentVersion}→v${result.version}`;\n\n return {\n newStorageJson: createStorageJson(result.data, result.version),\n info,\n warn: result.warning,\n };\n}\n\n// Register migrate callback\ntryRegisterCallback('__pl_storage_migrate', (currentStorageJson: string | undefined) => {\n return migrateStorage(currentStorageJson);\n});\n\n// =============================================================================\n// Args Derivation from Storage\n// =============================================================================\n\n/**\n * Result of args derivation from storage.\n * Returned by __pl_args_derive and __pl_prerunArgs_derive callbacks.\n */\nexport type ArgsDeriveResult =\n | { error: string }\n | { error?: undefined; value: unknown };\n\n/**\n * Derives args from storage using the registered 'args' callback.\n * This extracts data from storage and passes it to the block's args() function.\n *\n * @param storageJson - Storage as JSON string\n * @returns ArgsDeriveResult with derived args or error\n */\nfunction deriveArgsFromStorage(storageJson: string): ArgsDeriveResult {\n const ctx = tryGetCfgRenderCtx();\n if (ctx === undefined) {\n return { error: 'Not in config rendering context' };\n }\n\n // Extract data from storage\n const { data } = normalizeStorage(storageJson);\n\n // Get the args callback (registered by BlockModelV3.args())\n const argsCallback = ctx.callbackRegistry['args'] as ((data: unknown) => unknown) | undefined;\n if (typeof argsCallback !== 'function') {\n return { error: 'args callback not found' };\n }\n\n // Call the args callback with extracted data\n try {\n const result = argsCallback(data);\n return { value: result };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n return { error: `args() threw: ${errorMsg}` };\n }\n}\n\n// Register args derivation callback\ntryRegisterCallback('__pl_args_derive', (storageJson: string) => {\n return deriveArgsFromStorage(storageJson);\n});\n\n/**\n * Derives prerunArgs from storage using the registered 'prerunArgs' callback.\n * Falls back to 'args' callback if 'prerunArgs' is not defined.\n *\n * @param storageJson - Storage as JSON string\n * @returns ArgsDeriveResult with derived prerunArgs or error\n */\nfunction derivePrerunArgsFromStorage(storageJson: string): ArgsDeriveResult {\n const ctx = tryGetCfgRenderCtx();\n if (ctx === undefined) {\n return { error: 'Not in config rendering context' };\n }\n\n // Extract data from storage\n const { data } = normalizeStorage(storageJson);\n\n // Try prerunArgs callback first\n const prerunArgsCallback = ctx.callbackRegistry['prerunArgs'] as ((data: unknown) => unknown) | undefined;\n if (typeof prerunArgsCallback === 'function') {\n try {\n const result = prerunArgsCallback(data);\n return { value: result };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n return { error: `prerunArgs() threw: ${errorMsg}` };\n }\n }\n\n // Fall back to args callback\n const argsCallback = ctx.callbackRegistry['args'] as ((data: unknown) => unknown) | undefined;\n if (typeof argsCallback !== 'function') {\n return { error: 'args callback not found (fallback from missing prerunArgs)' };\n }\n\n try {\n const result = argsCallback(data);\n return { value: result };\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n return { error: `args() threw (fallback): ${errorMsg}` };\n }\n}\n\n// Register prerunArgs derivation callback\ntryRegisterCallback('__pl_prerunArgs_derive', (storageJson: string) => {\n return derivePrerunArgsFromStorage(storageJson);\n});\n\n// Export discriminator key and schema version for external checks\nexport { BLOCK_STORAGE_KEY, BLOCK_STORAGE_SCHEMA_VERSION };\n"],"names":["createBlockStorage","isBlockStorage","getStorageData","updateStorageData","tryRegisterCallback","tryGetCfgRenderCtx"],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * BlockStorage VM Integration - Internal module for VM-based storage operations.
3
+ *
4
+ * This module auto-registers internal callbacks that the middle layer can invoke
5
+ * to perform storage transformations. Block developers never interact with these
6
+ * directly - they only see `state`.
7
+ *
8
+ * Registered callbacks (all prefixed with `__pl_` for internal SDK use):
9
+ * - `__pl_storage_normalize`: (rawStorage) => { storage, data }
10
+ * - `__pl_storage_applyUpdate`: (currentStorageJson, payload) => updatedStorageJson
11
+ * - `__pl_storage_getInfo`: (rawStorage) => JSON string with storage info
12
+ * - `__pl_storage_migrate`: (currentStorageJson) => MigrationResult
13
+ * - `__pl_args_derive`: (storageJson) => ArgsDeriveResult
14
+ * - `__pl_prerunArgs_derive`: (storageJson) => ArgsDeriveResult
15
+ *
16
+ * Callbacks registered by DataModel.registerCallbacks():
17
+ * - `__pl_data_initial`: () => initial data
18
+ * - `__pl_data_upgrade`: (versioned) => UpgradeResult
19
+ * - `__pl_storage_initial`: () => initial BlockStorage as JSON string
20
+ *
21
+ * @module block_storage_vm
22
+ * @internal
23
+ */
24
+ import { BLOCK_STORAGE_KEY, BLOCK_STORAGE_SCHEMA_VERSION, type BlockStorage } from './block_storage';
25
+ /**
26
+ * Result of storage normalization
27
+ */
28
+ export interface NormalizeStorageResult {
29
+ /** The normalized BlockStorage object */
30
+ storage: BlockStorage;
31
+ /** The extracted data (what developers see) */
32
+ data: unknown;
33
+ }
34
+ /**
35
+ * Storage info result returned by __pl_storage_getInfo callback.
36
+ */
37
+ export interface StorageInfo {
38
+ /** Current data version (1-based, starts at 1) */
39
+ dataVersion: number;
40
+ }
41
+ /**
42
+ * Result of storage migration.
43
+ * Returned by __pl_storage_migrate callback.
44
+ *
45
+ * - Error result: { error: string } - serious failure (no context, etc.)
46
+ * - Success result: { newStorageJson: string, info: string, warn?: string } - migration succeeded or reset to initial
47
+ */
48
+ export type MigrationResult = {
49
+ error: string;
50
+ } | {
51
+ error?: undefined;
52
+ newStorageJson: string;
53
+ info: string;
54
+ warn?: string;
55
+ };
56
+ /**
57
+ * Result of args derivation from storage.
58
+ * Returned by __pl_args_derive and __pl_prerunArgs_derive callbacks.
59
+ */
60
+ export type ArgsDeriveResult = {
61
+ error: string;
62
+ } | {
63
+ error?: undefined;
64
+ value: unknown;
65
+ };
66
+ export { BLOCK_STORAGE_KEY, BLOCK_STORAGE_SCHEMA_VERSION };
67
+ //# sourceMappingURL=block_storage_vm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block_storage_vm.d.ts","sourceRoot":"","sources":["../src/block_storage_vm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EACL,iBAAiB,EACjB,4BAA4B,EAC5B,KAAK,YAAY,EAMlB,MAAM,iBAAiB,CAAC;AAGzB;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,yCAAyC;IACzC,OAAO,EAAE,YAAY,CAAC;IACtB,+CAA+C;IAC/C,IAAI,EAAE,OAAO,CAAC;CACf;AA2FD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,kDAAkD;IAClD,WAAW,EAAE,MAAM,CAAC;CACrB;AA0BD;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GACjB;IAAE,KAAK,CAAC,EAAE,SAAS,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AA8E/E;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GACjB;IAAE,KAAK,CAAC,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAwF1C,OAAO,EAAE,iBAAiB,EAAE,4BAA4B,EAAE,CAAC"}