@platforma-sdk/model 1.58.5 → 1.58.9

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 (365) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +43 -0
  2. package/dist/_virtual/_rolldown/runtime.js +18 -0
  3. package/dist/annotations/converter.cjs +15 -20
  4. package/dist/annotations/converter.cjs.map +1 -1
  5. package/dist/annotations/converter.d.ts +6 -2
  6. package/dist/annotations/converter.js +14 -18
  7. package/dist/annotations/converter.js.map +1 -1
  8. package/dist/annotations/index.cjs +1 -0
  9. package/dist/annotations/index.d.ts +2 -3
  10. package/dist/annotations/index.js +1 -0
  11. package/dist/annotations/types.d.ts +21 -16
  12. package/dist/bconfig/index.cjs +2 -0
  13. package/dist/bconfig/index.d.ts +5 -6
  14. package/dist/bconfig/index.js +2 -0
  15. package/dist/bconfig/lambdas.d.ts +52 -52
  16. package/dist/bconfig/normalization.cjs +13 -18
  17. package/dist/bconfig/normalization.cjs.map +1 -1
  18. package/dist/bconfig/normalization.d.ts +10 -6
  19. package/dist/bconfig/normalization.js +12 -16
  20. package/dist/bconfig/normalization.js.map +1 -1
  21. package/dist/bconfig/types.cjs +4 -3
  22. package/dist/bconfig/types.cjs.map +1 -1
  23. package/dist/bconfig/types.d.ts +10 -5
  24. package/dist/bconfig/types.js +4 -2
  25. package/dist/bconfig/types.js.map +1 -1
  26. package/dist/bconfig/util.d.ts +7 -4
  27. package/dist/bconfig/v3.d.ts +10 -6
  28. package/dist/block_api_v1.d.ts +62 -58
  29. package/dist/block_api_v2.d.ts +51 -47
  30. package/dist/block_api_v3.d.ts +33 -29
  31. package/dist/block_migrations.cjs +481 -413
  32. package/dist/block_migrations.cjs.map +1 -1
  33. package/dist/block_migrations.d.ts +258 -204
  34. package/dist/block_migrations.js +482 -408
  35. package/dist/block_migrations.js.map +1 -1
  36. package/dist/block_model.cjs +312 -343
  37. package/dist/block_model.cjs.map +1 -1
  38. package/dist/block_model.d.ts +143 -147
  39. package/dist/block_model.js +312 -341
  40. package/dist/block_model.js.map +1 -1
  41. package/dist/block_model_legacy.cjs +231 -255
  42. package/dist/block_model_legacy.cjs.map +1 -1
  43. package/dist/block_model_legacy.d.ts +108 -106
  44. package/dist/block_model_legacy.js +231 -253
  45. package/dist/block_model_legacy.js.map +1 -1
  46. package/dist/block_state_patch.d.ts +10 -10
  47. package/dist/block_state_util.cjs +15 -19
  48. package/dist/block_state_util.cjs.map +1 -1
  49. package/dist/block_state_util.d.ts +14 -13
  50. package/dist/block_state_util.js +15 -18
  51. package/dist/block_state_util.js.map +1 -1
  52. package/dist/block_storage.cjs +233 -238
  53. package/dist/block_storage.cjs.map +1 -1
  54. package/dist/block_storage.d.ts +62 -78
  55. package/dist/block_storage.js +234 -237
  56. package/dist/block_storage.js.map +1 -1
  57. package/dist/block_storage_callbacks.cjs +156 -195
  58. package/dist/block_storage_callbacks.cjs.map +1 -1
  59. package/dist/block_storage_callbacks.js +156 -192
  60. package/dist/block_storage_callbacks.js.map +1 -1
  61. package/dist/block_storage_facade.cjs +29 -85
  62. package/dist/block_storage_facade.cjs.map +1 -1
  63. package/dist/block_storage_facade.d.ts +83 -126
  64. package/dist/block_storage_facade.js +29 -83
  65. package/dist/block_storage_facade.js.map +1 -1
  66. package/dist/components/PFrameForGraphs.cjs +26 -24
  67. package/dist/components/PFrameForGraphs.cjs.map +1 -1
  68. package/dist/components/PFrameForGraphs.d.ts +12 -7
  69. package/dist/components/PFrameForGraphs.js +25 -22
  70. package/dist/components/PFrameForGraphs.js.map +1 -1
  71. package/dist/components/PlAnnotations/filter.d.ts +62 -79
  72. package/dist/components/PlAnnotations/filters_ui.cjs +135 -171
  73. package/dist/components/PlAnnotations/filters_ui.cjs.map +1 -1
  74. package/dist/components/PlAnnotations/filters_ui.d.ts +48 -46
  75. package/dist/components/PlAnnotations/filters_ui.js +135 -170
  76. package/dist/components/PlAnnotations/filters_ui.js.map +1 -1
  77. package/dist/components/PlAnnotations/index.d.ts +2 -3
  78. package/dist/components/PlDataTable/index.cjs +2 -0
  79. package/dist/components/PlDataTable/index.d.ts +3 -5
  80. package/dist/components/PlDataTable/index.js +2 -0
  81. package/dist/components/PlDataTable/labels.cjs +59 -81
  82. package/dist/components/PlDataTable/labels.cjs.map +1 -1
  83. package/dist/components/PlDataTable/labels.js +58 -79
  84. package/dist/components/PlDataTable/labels.js.map +1 -1
  85. package/dist/components/PlDataTable/state-migration.cjs +186 -144
  86. package/dist/components/PlDataTable/state-migration.cjs.map +1 -1
  87. package/dist/components/PlDataTable/state-migration.d.ts +85 -82
  88. package/dist/components/PlDataTable/state-migration.js +185 -142
  89. package/dist/components/PlDataTable/state-migration.js.map +1 -1
  90. package/dist/components/PlDataTable/table.cjs +172 -194
  91. package/dist/components/PlDataTable/table.cjs.map +1 -1
  92. package/dist/components/PlDataTable/table.d.ts +16 -12
  93. package/dist/components/PlDataTable/table.js +171 -192
  94. package/dist/components/PlDataTable/table.js.map +1 -1
  95. package/dist/components/PlDataTable/v4.d.ts +84 -119
  96. package/dist/components/PlDataTable/v5.d.ts +80 -103
  97. package/dist/components/PlMultiSequenceAlignment.cjs +27 -29
  98. package/dist/components/PlMultiSequenceAlignment.cjs.map +1 -1
  99. package/dist/components/PlMultiSequenceAlignment.d.ts +36 -27
  100. package/dist/components/PlMultiSequenceAlignment.js +26 -27
  101. package/dist/components/PlMultiSequenceAlignment.js.map +1 -1
  102. package/dist/components/PlSelectionModel.cjs +7 -6
  103. package/dist/components/PlSelectionModel.cjs.map +1 -1
  104. package/dist/components/PlSelectionModel.d.ts +10 -8
  105. package/dist/components/PlSelectionModel.js +7 -5
  106. package/dist/components/PlSelectionModel.js.map +1 -1
  107. package/dist/components/index.cjs +8 -0
  108. package/dist/components/index.d.ts +11 -6
  109. package/dist/components/index.js +8 -0
  110. package/dist/config/actions.cjs +138 -171
  111. package/dist/config/actions.cjs.map +1 -1
  112. package/dist/config/actions.d.ts +47 -47
  113. package/dist/config/actions.js +146 -178
  114. package/dist/config/actions.js.map +1 -1
  115. package/dist/config/actions_kinds.d.ts +114 -121
  116. package/dist/config/index.cjs +1 -0
  117. package/dist/config/index.d.ts +6 -7
  118. package/dist/config/index.js +1 -0
  119. package/dist/config/model.d.ts +131 -127
  120. package/dist/config/model_meta.d.ts +4 -1
  121. package/dist/config/type_engine.d.ts +22 -21
  122. package/dist/config/type_util.d.ts +12 -10
  123. package/dist/env_value.cjs +5 -6
  124. package/dist/env_value.cjs.map +1 -1
  125. package/dist/env_value.d.ts +4 -1
  126. package/dist/env_value.js +5 -5
  127. package/dist/env_value.js.map +1 -1
  128. package/dist/filters/converters/filterToQuery.cjs +273 -239
  129. package/dist/filters/converters/filterToQuery.cjs.map +1 -1
  130. package/dist/filters/converters/filterToQuery.d.ts +6 -2
  131. package/dist/filters/converters/filterToQuery.js +272 -237
  132. package/dist/filters/converters/filterToQuery.js.map +1 -1
  133. package/dist/filters/converters/filterUiToExpressionImpl.cjs +56 -85
  134. package/dist/filters/converters/filterUiToExpressionImpl.cjs.map +1 -1
  135. package/dist/filters/converters/filterUiToExpressionImpl.d.ts +8 -4
  136. package/dist/filters/converters/filterUiToExpressionImpl.js +55 -83
  137. package/dist/filters/converters/filterUiToExpressionImpl.js.map +1 -1
  138. package/dist/filters/converters/index.cjs +2 -0
  139. package/dist/filters/converters/index.d.ts +2 -3
  140. package/dist/filters/converters/index.js +2 -0
  141. package/dist/filters/distill.cjs +59 -60
  142. package/dist/filters/distill.cjs.map +1 -1
  143. package/dist/filters/distill.d.ts +6 -3
  144. package/dist/filters/distill.js +58 -58
  145. package/dist/filters/distill.js.map +1 -1
  146. package/dist/filters/index.cjs +4 -0
  147. package/dist/filters/index.d.ts +5 -4
  148. package/dist/filters/index.js +4 -0
  149. package/dist/filters/traverse.cjs +31 -40
  150. package/dist/filters/traverse.cjs.map +1 -1
  151. package/dist/filters/traverse.js +31 -39
  152. package/dist/filters/traverse.js.map +1 -1
  153. package/dist/filters/types.d.ts +10 -7
  154. package/dist/index.cjs +193 -187
  155. package/dist/index.d.ts +61 -28
  156. package/dist/index.js +49 -41
  157. package/dist/internal.cjs +48 -62
  158. package/dist/internal.cjs.map +1 -1
  159. package/dist/internal.js +49 -60
  160. package/dist/internal.js.map +1 -1
  161. package/dist/package.cjs +12 -0
  162. package/dist/package.cjs.map +1 -0
  163. package/dist/package.js +6 -0
  164. package/dist/package.js.map +1 -0
  165. package/dist/pframe.cjs +32 -37
  166. package/dist/pframe.cjs.map +1 -1
  167. package/dist/pframe.d.ts +14 -10
  168. package/dist/pframe.js +32 -35
  169. package/dist/pframe.js.map +1 -1
  170. package/dist/pframe_utils/axes.cjs +81 -114
  171. package/dist/pframe_utils/axes.cjs.map +1 -1
  172. package/dist/pframe_utils/axes.d.ts +10 -13
  173. package/dist/pframe_utils/axes.js +80 -112
  174. package/dist/pframe_utils/axes.js.map +1 -1
  175. package/dist/pframe_utils/columns.cjs +61 -81
  176. package/dist/pframe_utils/columns.cjs.map +1 -1
  177. package/dist/pframe_utils/columns.d.ts +15 -6
  178. package/dist/pframe_utils/columns.js +60 -79
  179. package/dist/pframe_utils/columns.js.map +1 -1
  180. package/dist/pframe_utils/index.cjs +232 -253
  181. package/dist/pframe_utils/index.cjs.map +1 -1
  182. package/dist/pframe_utils/index.d.ts +39 -35
  183. package/dist/pframe_utils/index.js +231 -251
  184. package/dist/pframe_utils/index.js.map +1 -1
  185. package/dist/platforma.d.ts +48 -46
  186. package/dist/plugin_handle.cjs +6 -14
  187. package/dist/plugin_handle.cjs.map +1 -1
  188. package/dist/plugin_handle.d.ts +20 -25
  189. package/dist/plugin_handle.js +6 -13
  190. package/dist/plugin_handle.js.map +1 -1
  191. package/dist/plugin_model.cjs +364 -163
  192. package/dist/plugin_model.cjs.map +1 -1
  193. package/dist/plugin_model.d.ts +280 -129
  194. package/dist/plugin_model.js +362 -163
  195. package/dist/plugin_model.js.map +1 -1
  196. package/dist/raw_globals.cjs +10 -22
  197. package/dist/raw_globals.cjs.map +1 -1
  198. package/dist/raw_globals.d.ts +8 -5
  199. package/dist/raw_globals.js +10 -20
  200. package/dist/raw_globals.js.map +1 -1
  201. package/dist/ref_util.cjs +14 -13
  202. package/dist/ref_util.cjs.map +1 -1
  203. package/dist/ref_util.d.ts +18 -12
  204. package/dist/ref_util.js +14 -11
  205. package/dist/ref_util.js.map +1 -1
  206. package/dist/render/accessor.cjs +213 -226
  207. package/dist/render/accessor.cjs.map +1 -1
  208. package/dist/render/accessor.d.ts +115 -120
  209. package/dist/render/accessor.js +212 -224
  210. package/dist/render/accessor.js.map +1 -1
  211. package/dist/render/api.cjs +478 -580
  212. package/dist/render/api.cjs.map +1 -1
  213. package/dist/render/api.d.ts +207 -209
  214. package/dist/render/api.js +476 -578
  215. package/dist/render/api.js.map +1 -1
  216. package/dist/render/future.cjs +28 -32
  217. package/dist/render/future.cjs.map +1 -1
  218. package/dist/render/future.d.ts +15 -11
  219. package/dist/render/future.js +28 -30
  220. package/dist/render/future.js.map +1 -1
  221. package/dist/render/index.cjs +8 -0
  222. package/dist/render/index.d.ts +10 -8
  223. package/dist/render/index.js +8 -0
  224. package/dist/render/internal.cjs +33 -29
  225. package/dist/render/internal.cjs.map +1 -1
  226. package/dist/render/internal.d.ts +78 -72
  227. package/dist/render/internal.js +29 -26
  228. package/dist/render/internal.js.map +1 -1
  229. package/dist/render/traversal_ops.d.ts +42 -43
  230. package/dist/render/util/axis_filtering.cjs +63 -86
  231. package/dist/render/util/axis_filtering.cjs.map +1 -1
  232. package/dist/render/util/axis_filtering.d.ts +10 -7
  233. package/dist/render/util/axis_filtering.js +63 -85
  234. package/dist/render/util/axis_filtering.js.map +1 -1
  235. package/dist/render/util/column_collection.cjs +266 -321
  236. package/dist/render/util/column_collection.cjs.map +1 -1
  237. package/dist/render/util/column_collection.d.ts +47 -47
  238. package/dist/render/util/column_collection.js +264 -319
  239. package/dist/render/util/column_collection.js.map +1 -1
  240. package/dist/render/util/index.cjs +4 -0
  241. package/dist/render/util/index.d.ts +4 -5
  242. package/dist/render/util/index.js +4 -0
  243. package/dist/render/util/label.cjs +129 -163
  244. package/dist/render/util/label.cjs.map +1 -1
  245. package/dist/render/util/label.d.ts +45 -46
  246. package/dist/render/util/label.js +128 -161
  247. package/dist/render/util/label.js.map +1 -1
  248. package/dist/render/util/pcolumn_data.cjs +315 -375
  249. package/dist/render/util/pcolumn_data.cjs.map +1 -1
  250. package/dist/render/util/pcolumn_data.d.ts +33 -32
  251. package/dist/render/util/pcolumn_data.js +314 -373
  252. package/dist/render/util/pcolumn_data.js.map +1 -1
  253. package/dist/render/util/pframe_upgraders.cjs +37 -42
  254. package/dist/render/util/pframe_upgraders.cjs.map +1 -1
  255. package/dist/render/util/pframe_upgraders.js +37 -41
  256. package/dist/render/util/pframe_upgraders.js.map +1 -1
  257. package/dist/render/util/split_selectors.d.ts +13 -9
  258. package/dist/version.cjs +6 -8
  259. package/dist/version.cjs.map +1 -1
  260. package/dist/version.d.ts +7 -4
  261. package/dist/version.js +5 -5
  262. package/dist/version.js.map +1 -1
  263. package/package.json +10 -4
  264. package/src/block_migrations.test.ts +184 -14
  265. package/src/block_migrations.ts +185 -30
  266. package/src/block_model.ts +111 -66
  267. package/src/block_storage.test.ts +147 -3
  268. package/src/block_storage.ts +43 -8
  269. package/src/block_storage_callbacks.ts +9 -4
  270. package/src/env_value.ts +0 -2
  271. package/src/index.ts +12 -11
  272. package/src/internal.ts +0 -1
  273. package/src/platforma.ts +4 -4
  274. package/src/plugin_handle.ts +1 -1
  275. package/src/plugin_model.test.ts +217 -21
  276. package/src/plugin_model.ts +450 -55
  277. package/src/raw_globals.ts +0 -1
  278. package/dist/annotations/converter.d.ts.map +0 -1
  279. package/dist/annotations/index.d.ts.map +0 -1
  280. package/dist/annotations/types.d.ts.map +0 -1
  281. package/dist/bconfig/index.d.ts.map +0 -1
  282. package/dist/bconfig/lambdas.d.ts.map +0 -1
  283. package/dist/bconfig/normalization.d.ts.map +0 -1
  284. package/dist/bconfig/types.d.ts.map +0 -1
  285. package/dist/bconfig/util.d.ts.map +0 -1
  286. package/dist/bconfig/v3.d.ts.map +0 -1
  287. package/dist/block_api_v1.d.ts.map +0 -1
  288. package/dist/block_api_v2.d.ts.map +0 -1
  289. package/dist/block_api_v3.d.ts.map +0 -1
  290. package/dist/block_migrations.d.ts.map +0 -1
  291. package/dist/block_model.d.ts.map +0 -1
  292. package/dist/block_model_legacy.d.ts.map +0 -1
  293. package/dist/block_state_patch.d.ts.map +0 -1
  294. package/dist/block_state_util.d.ts.map +0 -1
  295. package/dist/block_storage.d.ts.map +0 -1
  296. package/dist/block_storage_callbacks.d.ts +0 -115
  297. package/dist/block_storage_callbacks.d.ts.map +0 -1
  298. package/dist/block_storage_facade.d.ts.map +0 -1
  299. package/dist/components/PFrameForGraphs.d.ts.map +0 -1
  300. package/dist/components/PlAnnotations/filter.d.ts.map +0 -1
  301. package/dist/components/PlAnnotations/filters_ui.d.ts.map +0 -1
  302. package/dist/components/PlAnnotations/index.d.ts.map +0 -1
  303. package/dist/components/PlAnnotations/types.d.ts +0 -3
  304. package/dist/components/PlAnnotations/types.d.ts.map +0 -1
  305. package/dist/components/PlDataTable/index.d.ts.map +0 -1
  306. package/dist/components/PlDataTable/labels.d.ts +0 -7
  307. package/dist/components/PlDataTable/labels.d.ts.map +0 -1
  308. package/dist/components/PlDataTable/state-migration.d.ts.map +0 -1
  309. package/dist/components/PlDataTable/table.d.ts.map +0 -1
  310. package/dist/components/PlDataTable/v4.d.ts.map +0 -1
  311. package/dist/components/PlDataTable/v5.d.ts.map +0 -1
  312. package/dist/components/PlMultiSequenceAlignment.d.ts.map +0 -1
  313. package/dist/components/PlSelectionModel.d.ts.map +0 -1
  314. package/dist/components/index.d.ts.map +0 -1
  315. package/dist/config/actions.d.ts.map +0 -1
  316. package/dist/config/actions_kinds.d.ts.map +0 -1
  317. package/dist/config/index.d.ts.map +0 -1
  318. package/dist/config/model.d.ts.map +0 -1
  319. package/dist/config/model_meta.d.ts.map +0 -1
  320. package/dist/config/type_engine.d.ts.map +0 -1
  321. package/dist/config/type_util.d.ts.map +0 -1
  322. package/dist/env_value.d.ts.map +0 -1
  323. package/dist/filters/converters/filterToQuery.d.ts.map +0 -1
  324. package/dist/filters/converters/filterUiToExpressionImpl.d.ts.map +0 -1
  325. package/dist/filters/converters/index.d.ts.map +0 -1
  326. package/dist/filters/distill.d.ts.map +0 -1
  327. package/dist/filters/index.d.ts.map +0 -1
  328. package/dist/filters/traverse.d.ts +0 -27
  329. package/dist/filters/traverse.d.ts.map +0 -1
  330. package/dist/filters/types.d.ts.map +0 -1
  331. package/dist/index.cjs.map +0 -1
  332. package/dist/index.d.ts.map +0 -1
  333. package/dist/index.js.map +0 -1
  334. package/dist/internal.d.ts +0 -36
  335. package/dist/internal.d.ts.map +0 -1
  336. package/dist/package.json.cjs +0 -6
  337. package/dist/package.json.cjs.map +0 -1
  338. package/dist/package.json.js +0 -4
  339. package/dist/package.json.js.map +0 -1
  340. package/dist/pframe.d.ts.map +0 -1
  341. package/dist/pframe_utils/axes.d.ts.map +0 -1
  342. package/dist/pframe_utils/columns.d.ts.map +0 -1
  343. package/dist/pframe_utils/index.d.ts.map +0 -1
  344. package/dist/pframe_utils/querySpec.d.ts +0 -2
  345. package/dist/pframe_utils/querySpec.d.ts.map +0 -1
  346. package/dist/platforma.d.ts.map +0 -1
  347. package/dist/plugin_handle.d.ts.map +0 -1
  348. package/dist/plugin_model.d.ts.map +0 -1
  349. package/dist/raw_globals.d.ts.map +0 -1
  350. package/dist/ref_util.d.ts.map +0 -1
  351. package/dist/render/accessor.d.ts.map +0 -1
  352. package/dist/render/api.d.ts.map +0 -1
  353. package/dist/render/future.d.ts.map +0 -1
  354. package/dist/render/index.d.ts.map +0 -1
  355. package/dist/render/internal.d.ts.map +0 -1
  356. package/dist/render/traversal_ops.d.ts.map +0 -1
  357. package/dist/render/util/axis_filtering.d.ts.map +0 -1
  358. package/dist/render/util/column_collection.d.ts.map +0 -1
  359. package/dist/render/util/index.d.ts.map +0 -1
  360. package/dist/render/util/label.d.ts.map +0 -1
  361. package/dist/render/util/pcolumn_data.d.ts.map +0 -1
  362. package/dist/render/util/pframe_upgraders.d.ts +0 -3
  363. package/dist/render/util/pframe_upgraders.d.ts.map +0 -1
  364. package/dist/render/util/split_selectors.d.ts.map +0 -1
  365. package/dist/version.d.ts.map +0 -1
@@ -1,269 +1,266 @@
1
+ import { isDataUnrecoverableError } from "./block_migrations.js";
2
+
3
+ //#region src/block_storage.ts
1
4
  /**
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
- */
5
+ * Discriminator key for BlockStorage format detection.
6
+ * This unique hash-based key identifies data as BlockStorage vs legacy formats.
7
+ */
18
8
  const BLOCK_STORAGE_KEY = "__pl_a7f3e2b9__";
19
9
  /**
20
- * Current BlockStorage schema version.
21
- * Increment this when the storage structure itself changes (not block state migrations).
22
- */
10
+ * Current BlockStorage schema version.
11
+ * Increment this when the storage structure itself changes (not block state migrations).
12
+ */
23
13
  const BLOCK_STORAGE_SCHEMA_VERSION = "v1";
24
14
  /**
25
- * Default data version for new blocks without migrations.
26
- * Unique identifier ensures blocks are created via DataModel API.
27
- */
15
+ * Default data version for new blocks without migrations.
16
+ * Unique identifier ensures blocks are created via DataModel API.
17
+ */
28
18
  const DATA_MODEL_LEGACY_VERSION = "__pl_v1_d4e8f2a1__";
29
19
  /**
30
- * Type guard to check if a value is a valid BlockStorage object.
31
- * Checks for the discriminator key and valid schema version.
32
- */
20
+ * Type guard to check if a value is a valid BlockStorage object.
21
+ * Checks for the discriminator key and valid schema version.
22
+ */
33
23
  function isBlockStorage(value) {
34
- if (value === null || typeof value !== "object")
35
- return false;
36
- const obj = value;
37
- const schemaVersion = obj[BLOCK_STORAGE_KEY];
38
- // Currently only 'v1' is valid, but this allows future versions
39
- return schemaVersion === "v1"; // Add more versions as schema evolves
24
+ if (value === null || typeof value !== "object") return false;
25
+ return value[BLOCK_STORAGE_KEY] === "v1";
40
26
  }
41
- // =============================================================================
42
- // Factory Functions
43
- // =============================================================================
44
27
  /**
45
- * Creates a BlockStorage with the given initial data
46
- *
47
- * @param initialData - The initial data value (defaults to empty object)
48
- * @param version - The initial data version key (defaults to DATA_MODEL_LEGACY_VERSION)
49
- * @returns A new BlockStorage instance with discriminator key
50
- */
28
+ * Creates a BlockStorage with the given initial data
29
+ *
30
+ * @param initialData - The initial data value (defaults to empty object)
31
+ * @param version - The initial data version key (defaults to DATA_MODEL_LEGACY_VERSION)
32
+ * @returns A new BlockStorage instance with discriminator key
33
+ */
51
34
  function createBlockStorage(initialData = {}, version = DATA_MODEL_LEGACY_VERSION) {
52
- return {
53
- [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,
54
- __dataVersion: version,
55
- __data: initialData,
56
- __pluginRegistry: {},
57
- __plugins: {},
58
- };
35
+ return {
36
+ [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,
37
+ __dataVersion: version,
38
+ __data: initialData,
39
+ __pluginRegistry: {},
40
+ __plugins: {}
41
+ };
59
42
  }
60
43
  /**
61
- * Normalizes raw storage data to BlockStorage format.
62
- * If the input is already a BlockStorage, returns it as-is (with defaults for missing fields).
63
- * If the input is legacy format (raw state), wraps it in BlockStorage structure.
64
- *
65
- * @param raw - Raw storage data (may be legacy format or BlockStorage)
66
- * @returns Normalized BlockStorage
67
- */
44
+ * Normalizes raw storage data to BlockStorage format.
45
+ * If the input is already a BlockStorage, returns it as-is (with defaults for missing fields).
46
+ * If the input is legacy format (raw state), wraps it in BlockStorage structure.
47
+ *
48
+ * @param raw - Raw storage data (may be legacy format or BlockStorage)
49
+ * @returns Normalized BlockStorage
50
+ */
68
51
  function normalizeBlockStorage(raw) {
69
- if (isBlockStorage(raw)) {
70
- const storage = raw;
71
- return {
72
- ...storage,
73
- // Fix for early released version where __dataVersion was a number
74
- __dataVersion: typeof storage.__dataVersion === "number"
75
- ? DATA_MODEL_LEGACY_VERSION
76
- : storage.__dataVersion,
77
- // Ensure plugin fields have defaults
78
- __pluginRegistry: storage.__pluginRegistry ?? {},
79
- __plugins: storage.__plugins ?? {},
80
- };
81
- }
82
- // Legacy format: raw is the state directly
83
- return createBlockStorage(raw);
52
+ if (isBlockStorage(raw)) {
53
+ const storage = raw;
54
+ return {
55
+ ...storage,
56
+ __dataVersion: typeof storage.__dataVersion === "number" ? DATA_MODEL_LEGACY_VERSION : storage.__dataVersion,
57
+ __pluginRegistry: storage.__pluginRegistry ?? {},
58
+ __plugins: storage.__plugins ?? {}
59
+ };
60
+ }
61
+ return createBlockStorage(raw);
84
62
  }
85
- // =============================================================================
86
- // Data Access & Update Functions
87
- // =============================================================================
88
63
  /**
89
- * Gets the data from BlockStorage
90
- *
91
- * @param storage - The BlockStorage instance
92
- * @returns The data value
93
- */
64
+ * Gets the data from BlockStorage
65
+ *
66
+ * @param storage - The BlockStorage instance
67
+ * @returns The data value
68
+ */
94
69
  function getStorageData(storage) {
95
- return storage.__data;
70
+ return storage.__data;
96
71
  }
97
72
  /**
98
- * Derives data from raw block storage.
99
- * This function is meant to be called from sdk/ui-vue to extract
100
- * user-facing data from the raw storage returned by the middle layer.
101
- *
102
- * The middle layer returns raw storage (opaque to it), and the UI
103
- * uses this function to derive the actual data value.
104
- *
105
- * @param rawStorage - Raw storage data from middle layer (may be any format)
106
- * @returns The extracted data value, or undefined if storage is undefined/null
107
- */
73
+ * Derives data from raw block storage.
74
+ * This function is meant to be called from sdk/ui-vue to extract
75
+ * user-facing data from the raw storage returned by the middle layer.
76
+ *
77
+ * The middle layer returns raw storage (opaque to it), and the UI
78
+ * uses this function to derive the actual data value.
79
+ *
80
+ * @param rawStorage - Raw storage data from middle layer (may be any format)
81
+ * @returns The extracted data value, or undefined if storage is undefined/null
82
+ */
108
83
  function deriveDataFromStorage(rawStorage) {
109
- // Normalize to BlockStorage format (handles legacy formats too)
110
- const storage = normalizeBlockStorage(rawStorage);
111
- return getStorageData(storage);
84
+ return getStorageData(normalizeBlockStorage(rawStorage));
112
85
  }
113
86
  /**
114
- * Updates the data in BlockStorage (immutable)
115
- *
116
- * @param storage - The current BlockStorage
117
- * @param payload - The update payload with operation and value
118
- * @returns A new BlockStorage with updated data
119
- */
87
+ * Updates the data in BlockStorage (immutable)
88
+ *
89
+ * @param storage - The current BlockStorage
90
+ * @param payload - The update payload with operation and value
91
+ * @returns A new BlockStorage with updated data
92
+ */
120
93
  function updateStorageData(storage, payload) {
121
- switch (payload.operation) {
122
- case "update-block-data":
123
- return { ...storage, __data: payload.value };
124
- case "update-plugin-data": {
125
- const { pluginId, value } = payload;
126
- const currentPlugins = storage.__plugins ?? {};
127
- const existingEntry = currentPlugins[pluginId];
128
- const version = existingEntry?.__dataVersion ?? DATA_MODEL_LEGACY_VERSION;
129
- return {
130
- ...storage,
131
- __plugins: {
132
- ...currentPlugins,
133
- [pluginId]: {
134
- __dataVersion: version,
135
- __data: value,
136
- },
137
- },
138
- };
139
- }
140
- default:
141
- throw new Error(`Unknown storage operation: ${payload.operation}`);
142
- }
94
+ switch (payload.operation) {
95
+ case "update-block-data": return {
96
+ ...storage,
97
+ __data: payload.value
98
+ };
99
+ case "update-plugin-data": {
100
+ const { pluginId, value } = payload;
101
+ const currentPlugins = storage.__plugins ?? {};
102
+ const version = currentPlugins[pluginId]?.__dataVersion ?? DATA_MODEL_LEGACY_VERSION;
103
+ return {
104
+ ...storage,
105
+ __plugins: {
106
+ ...currentPlugins,
107
+ [pluginId]: {
108
+ __dataVersion: version,
109
+ __data: value
110
+ }
111
+ }
112
+ };
113
+ }
114
+ default: throw new Error(`Unknown storage operation: ${payload.operation}`);
115
+ }
143
116
  }
144
117
  /**
145
- * Performs atomic migration of block storage including block data and all plugins.
146
- *
147
- * Migration is atomic: either everything succeeds and a new storage is returned,
148
- * or an error is returned and the original storage is completely untouched.
149
- *
150
- * Migration steps:
151
- * 1. Migrate block data
152
- * 2. For each plugin in newPluginRegistry:
153
- * - If plugin exists with same name: migrate its data
154
- * - Otherwise (new or type changed): create with initial data
155
- * Plugins not in newPluginRegistry are dropped.
156
- *
157
- * If any step throws, migration fails and original storage is preserved.
158
- * User can then choose to:
159
- * - Abort: keep original storage, don't update block
160
- * - Reset: call createBlockStorage() to start fresh
161
- *
162
- * @param storage - The original storage (will not be modified)
163
- * @param config - Migration configuration
164
- * @returns Migration result - either success with new storage, or failure with error info
165
- *
166
- * @example
167
- * const result = migrateBlockStorage(storage, {
168
- * migrateBlockData: (versioned) => blockDataModel.migrate(versioned),
169
- * migratePluginData: (pluginId, versioned) => getPluginModel(pluginId).migrate(versioned),
170
- * newPluginRegistry: { table1: 'dataTable' as PluginName },
171
- * createPluginData: (pluginId) => getPluginModel(pluginId).getDefaultData(),
172
- * });
173
- *
174
- * if (result.success) {
175
- * commitStorage(result.storage);
176
- * } else {
177
- * const userChoice = await askUser(`Migration failed: ${result.error}. Reset data?`);
178
- * if (userChoice === 'reset') {
179
- * commitStorage(createBlockStorage(initialData, currentVersion));
180
- * }
181
- * // else: abort, keep original
182
- * }
183
- */
118
+ * Performs atomic migration of block storage including block data and all plugins.
119
+ *
120
+ * Migration is atomic: either everything succeeds and a new storage is returned,
121
+ * or an error is returned and the original storage is completely untouched.
122
+ *
123
+ * Migration steps:
124
+ * 1. Migrate block data
125
+ * 2. For each plugin in newPluginRegistry:
126
+ * - If plugin exists with same name: migrate its data
127
+ * - Otherwise (new or type changed): create with initial data
128
+ * Plugins not in newPluginRegistry are dropped.
129
+ *
130
+ * If any step throws, migration fails and original storage is preserved.
131
+ * User can then choose to:
132
+ * - Abort: keep original storage, don't update block
133
+ * - Reset: call createBlockStorage() to start fresh
134
+ *
135
+ * @param storage - The original storage (will not be modified)
136
+ * @param config - Migration configuration
137
+ * @returns Migration result - either success with new storage, or failure with error info
138
+ *
139
+ * @example
140
+ * const result = migrateBlockStorage(storage, {
141
+ * migrateBlockData: (versioned) => blockDataModel.migrate(versioned),
142
+ * migratePluginData: (pluginId, versioned) => getPluginModel(pluginId).migrate(versioned),
143
+ * newPluginRegistry: { table1: 'dataTable' as PluginName },
144
+ * createPluginData: (pluginId) => getPluginModel(pluginId).getDefaultData(),
145
+ * });
146
+ *
147
+ * if (result.success) {
148
+ * commitStorage(result.storage);
149
+ * } else {
150
+ * const userChoice = await askUser(`Migration failed: ${result.error}. Reset data?`);
151
+ * if (userChoice === 'reset') {
152
+ * commitStorage(createBlockStorage(initialData, currentVersion));
153
+ * }
154
+ * // else: abort, keep original
155
+ * }
156
+ */
184
157
  function migrateBlockStorage(storage, config) {
185
- const { migrateBlockData, migratePluginData, newPluginRegistry, createPluginData } = config;
186
- // Step 1: Migrate block data
187
- let migratedData;
188
- let newVersion;
189
- try {
190
- const result = migrateBlockData({ version: storage.__dataVersion, data: storage.__data });
191
- migratedData = result.data;
192
- newVersion = result.version;
193
- }
194
- catch (error) {
195
- return {
196
- success: false,
197
- error: error instanceof Error ? error.message : String(error),
198
- failedAt: "block",
199
- };
200
- }
201
- // Step 2: Migrate plugins
202
- const oldPlugins = storage.__plugins ?? {};
203
- const oldRegistry = storage.__pluginRegistry ?? {};
204
- const newPlugins = {};
205
- for (const [key, pluginName] of Object.entries(newPluginRegistry)) {
206
- const handle = key;
207
- const existingEntry = oldPlugins[handle];
208
- const existingName = oldRegistry[handle];
209
- try {
210
- if (existingEntry && existingName === pluginName) {
211
- // Plugin exists with same type - migrate its data
212
- const migrated = migratePluginData(handle, {
213
- version: existingEntry.__dataVersion,
214
- data: existingEntry.__data,
215
- });
216
- if (migrated) {
217
- newPlugins[handle] = { __dataVersion: migrated.version, __data: migrated.data };
218
- }
219
- // If undefined returned, plugin is intentionally removed
220
- }
221
- else {
222
- // New plugin or type changed - create with initial data
223
- const initial = createPluginData(handle);
224
- newPlugins[handle] = { __dataVersion: initial.version, __data: initial.data };
225
- }
226
- }
227
- catch (error) {
228
- return {
229
- success: false,
230
- error: error instanceof Error ? error.message : String(error),
231
- failedAt: handle,
232
- };
233
- }
234
- }
235
- // Step 3: Build final storage atomically
236
- const migratedStorage = {
237
- [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,
238
- __dataVersion: newVersion,
239
- __data: migratedData,
240
- __pluginRegistry: newPluginRegistry,
241
- __plugins: newPlugins,
242
- };
243
- return {
244
- success: true,
245
- storage: migratedStorage,
246
- };
158
+ const { migrateBlockData, migratePluginData, newPluginRegistry, createPluginData } = config;
159
+ let migratedData;
160
+ let newVersion;
161
+ let transfers;
162
+ try {
163
+ const result = migrateBlockData({
164
+ version: storage.__dataVersion,
165
+ data: storage.__data
166
+ });
167
+ migratedData = result.data;
168
+ newVersion = result.version;
169
+ transfers = result.transfers;
170
+ } catch (error) {
171
+ return {
172
+ success: false,
173
+ error: error instanceof Error ? error.message : String(error),
174
+ failedAt: "block"
175
+ };
176
+ }
177
+ const oldPlugins = storage.__plugins ?? {};
178
+ const oldRegistry = storage.__pluginRegistry ?? {};
179
+ const newPlugins = {};
180
+ for (const [key, pluginName] of Object.entries(newPluginRegistry)) {
181
+ const handle = key;
182
+ const existingEntry = oldPlugins[handle];
183
+ const existingName = oldRegistry[handle];
184
+ try {
185
+ if (existingEntry && existingName === pluginName) {
186
+ const migrated = migratePluginData(handle, {
187
+ version: existingEntry.__dataVersion,
188
+ data: existingEntry.__data
189
+ });
190
+ if (migrated) newPlugins[handle] = {
191
+ __dataVersion: migrated.version,
192
+ __data: migrated.data
193
+ };
194
+ } else if (existingEntry) {
195
+ let recovered = false;
196
+ try {
197
+ const migrated = migratePluginData(handle, {
198
+ version: DATA_MODEL_LEGACY_VERSION,
199
+ data: existingEntry.__data
200
+ });
201
+ if (migrated) {
202
+ newPlugins[handle] = {
203
+ __dataVersion: migrated.version,
204
+ __data: migrated.data
205
+ };
206
+ recovered = true;
207
+ }
208
+ } catch (recoverError) {
209
+ if (!isDataUnrecoverableError(recoverError)) throw recoverError;
210
+ }
211
+ if (!recovered) {
212
+ const transfer = transfers[handle];
213
+ const initial = createPluginData(handle, transfer);
214
+ newPlugins[handle] = {
215
+ __dataVersion: initial.version,
216
+ __data: initial.data
217
+ };
218
+ }
219
+ } else {
220
+ const transfer = transfers[handle];
221
+ const initial = createPluginData(handle, transfer);
222
+ newPlugins[handle] = {
223
+ __dataVersion: initial.version,
224
+ __data: initial.data
225
+ };
226
+ }
227
+ } catch (error) {
228
+ return {
229
+ success: false,
230
+ error: error instanceof Error ? error.message : String(error),
231
+ failedAt: handle
232
+ };
233
+ }
234
+ }
235
+ return {
236
+ success: true,
237
+ storage: {
238
+ [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,
239
+ __dataVersion: newVersion,
240
+ __data: migratedData,
241
+ __pluginRegistry: newPluginRegistry,
242
+ __plugins: newPlugins
243
+ }
244
+ };
247
245
  }
248
246
  /**
249
- * Gets plugin-specific data from block storage.
250
- * Accepts raw storage (any format) and normalizes internally.
251
- *
252
- * When called with a typed PluginHandle<F>, the return type is automatically
253
- * inferred from the factory's phantom `__types.data` field.
254
- *
255
- * @param rawStorage - Raw block storage (may be legacy format or BlockStorage)
256
- * @param handle - The plugin handle (branded plugin instance id)
257
- * @returns The plugin data, typed via factory inference
258
- * @throws If plugin is not found in storage
259
- */
247
+ * Gets plugin-specific data from block storage.
248
+ * Accepts raw storage (any format) and normalizes internally.
249
+ *
250
+ * When called with a typed PluginHandle<F>, the return type is automatically
251
+ * inferred from the factory's phantom `__types.data` field.
252
+ *
253
+ * @param rawStorage - Raw block storage (may be legacy format or BlockStorage)
254
+ * @param handle - The plugin handle (branded plugin instance id)
255
+ * @returns The plugin data, typed via factory inference
256
+ * @throws If plugin is not found in storage
257
+ */
260
258
  function getPluginData(rawStorage, handle) {
261
- const storage = normalizeBlockStorage(rawStorage);
262
- const pluginEntry = storage.__plugins?.[handle];
263
- if (!pluginEntry)
264
- throw new Error(`Plugin '${handle}' not found in block storage`);
265
- return pluginEntry.__data;
259
+ const pluginEntry = normalizeBlockStorage(rawStorage).__plugins?.[handle];
260
+ if (!pluginEntry) throw new Error(`Plugin '${handle}' not found in block storage`);
261
+ return pluginEntry.__data;
266
262
  }
267
263
 
264
+ //#endregion
268
265
  export { BLOCK_STORAGE_KEY, BLOCK_STORAGE_SCHEMA_VERSION, DATA_MODEL_LEGACY_VERSION, createBlockStorage, deriveDataFromStorage, getPluginData, getStorageData, isBlockStorage, migrateBlockStorage, normalizeBlockStorage, updateStorageData };
269
- //# sourceMappingURL=block_storage.js.map
266
+ //# sourceMappingURL=block_storage.js.map
@@ -1 +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\nimport type { Branded } from \"@milaboratories/pl-model-common\";\nimport type { DataVersioned } from \"./block_migrations\";\nimport type { PluginHandle, PluginFactoryLike, InferFactoryData } from \"./plugin_handle\";\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 * Default data version for new blocks without migrations.\n * Unique identifier ensures blocks are created via DataModel API.\n */\nexport const DATA_MODEL_LEGACY_VERSION = \"__pl_v1_d4e8f2a1__\";\n\n/**\n * Type for valid schema versions\n */\nexport type BlockStorageSchemaVersion = \"v1\"; // Add 'v2', 'v3', etc. as schema evolves\n\n/**\n * Branded type for plugin names - globally unique plugin type identifiers.\n * Using a branded type enforces explicit casting (`as PluginName`) which makes\n * it easy to find all plugin name definitions in the codebase and verify uniqueness.\n */\nexport type PluginName = Branded<string, \"PluginName\">;\n\n/**\n * Plugin registry - maps pluginId (unique within a block) to pluginName (globally unique plugin type).\n * Using a Record highlights that pluginIds must be unique within a block.\n */\nexport type PluginRegistry = Record<PluginHandle, PluginName>;\n\n/**\n * Versioned data - used for both block data and plugin data\n */\nexport interface VersionedData<TData = unknown> {\n /** Version of the data, used for migrations */\n __dataVersion: string;\n /** The persistent data */\n __data: TData;\n}\n\n/**\n * Core BlockStorage type that holds:\n * - __pl_a7f3e2b9__: Schema version (discriminator key identifies BlockStorage format)\n * - __dataVersion: Version key for block data migrations\n * - __data: The block's user-facing data (state)\n * - __pluginRegistry: Map from pluginId to pluginName (optional)\n * - __plugins: Plugin-specific data keyed by pluginId (optional)\n */\nexport type BlockStorage<TState = unknown> = {\n /** Schema version - the key itself is the discriminator */\n readonly [BLOCK_STORAGE_KEY]: BlockStorageSchemaVersion;\n /** Registry of plugins: pluginId -> pluginName */\n __pluginRegistry?: PluginRegistry;\n /** Plugin-specific data, keyed by plugin handle */\n __plugins?: Record<PluginHandle, VersionedData<unknown>>;\n} & VersionedData<TState>;\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 key (defaults to DATA_MODEL_LEGACY_VERSION)\n * @returns A new BlockStorage instance with discriminator key\n */\nexport function createBlockStorage<TState = unknown>(\n initialData: TState = {} as TState,\n version: string = DATA_MODEL_LEGACY_VERSION,\n): BlockStorage<TState> {\n return {\n [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,\n __dataVersion: version,\n __data: initialData,\n __pluginRegistry: {},\n __plugins: {},\n };\n}\n\n/**\n * Normalizes raw storage data to BlockStorage format.\n * If the input is already a BlockStorage, returns it as-is (with defaults for missing fields).\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 const storage = raw as BlockStorage<TState>;\n return {\n ...storage,\n // Fix for early released version where __dataVersion was a number\n __dataVersion:\n typeof storage.__dataVersion === \"number\"\n ? DATA_MODEL_LEGACY_VERSION\n : storage.__dataVersion,\n // Ensure plugin fields have defaults\n __pluginRegistry: storage.__pluginRegistry ?? {},\n __plugins: storage.__plugins ?? {},\n };\n }\n // Legacy format: raw is the state directly\n return createBlockStorage(raw as TState);\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>(rawStorage: unknown): 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> =\n | { operation: \"update-block-data\"; value: T }\n | { operation: \"update-plugin-data\"; pluginId: PluginHandle; value: unknown };\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-block-data\":\n return { ...storage, __data: payload.value };\n case \"update-plugin-data\": {\n const { pluginId, value } = payload;\n const currentPlugins = storage.__plugins ?? {};\n const existingEntry = currentPlugins[pluginId];\n const version = existingEntry?.__dataVersion ?? DATA_MODEL_LEGACY_VERSION;\n return {\n ...storage,\n __plugins: {\n ...currentPlugins,\n [pluginId]: {\n __dataVersion: version,\n __data: value,\n },\n },\n };\n }\n default:\n throw new Error(`Unknown storage operation: ${(payload as { operation: string }).operation}`);\n }\n}\n\n/**\n * Storage debug view returned by __pl_storage_debugView callback.\n * Used by developer tools to display block storage info.\n */\nexport interface StorageDebugView {\n /** Current data version key */\n dataVersion: string;\n /** Raw data payload stored in BlockStorage */\n data: unknown;\n}\n\n// =============================================================================\n// Atomic Migration\n// =============================================================================\n\n/**\n * Result of a successful atomic migration.\n */\nexport interface MigrationSuccess<TState> {\n success: true;\n /** The fully migrated storage - commit this to persist */\n storage: BlockStorage<TState>;\n}\n\n/**\n * Result of a failed atomic migration.\n * The original storage is untouched - user must choose to abort or reset.\n */\nexport interface MigrationFailure {\n success: false;\n /** Description of what failed */\n error: string;\n /** Which step failed: 'block' or pluginId */\n failedAt: string;\n}\n\nexport type MigrationResult<TState> = MigrationSuccess<TState> | MigrationFailure;\n\n/**\n * Configuration for atomic block storage migration.\n * Callbacks use DataVersioned format (the DataModel API format).\n * Conversion to internal VersionedData format is handled by migrateBlockStorage().\n */\nexport interface MigrateBlockStorageConfig {\n /** Migrate block data from any version to latest. Throws on failure. */\n migrateBlockData: (versioned: DataVersioned<unknown>) => DataVersioned<unknown>;\n /** Migrate each plugin's data. Return undefined to remove the plugin. Throws on failure. */\n migratePluginData: (\n handle: PluginHandle,\n versioned: DataVersioned<unknown>,\n ) => DataVersioned<unknown> | undefined;\n /** The new plugin registry after migration (pluginId -> pluginName) */\n newPluginRegistry: PluginRegistry;\n /** Factory to create initial data for new plugins */\n createPluginData: (handle: PluginHandle) => DataVersioned<unknown>;\n}\n\n/**\n * Performs atomic migration of block storage including block data and all plugins.\n *\n * Migration is atomic: either everything succeeds and a new storage is returned,\n * or an error is returned and the original storage is completely untouched.\n *\n * Migration steps:\n * 1. Migrate block data\n * 2. For each plugin in newPluginRegistry:\n * - If plugin exists with same name: migrate its data\n * - Otherwise (new or type changed): create with initial data\n * Plugins not in newPluginRegistry are dropped.\n *\n * If any step throws, migration fails and original storage is preserved.\n * User can then choose to:\n * - Abort: keep original storage, don't update block\n * - Reset: call createBlockStorage() to start fresh\n *\n * @param storage - The original storage (will not be modified)\n * @param config - Migration configuration\n * @returns Migration result - either success with new storage, or failure with error info\n *\n * @example\n * const result = migrateBlockStorage(storage, {\n * migrateBlockData: (versioned) => blockDataModel.migrate(versioned),\n * migratePluginData: (pluginId, versioned) => getPluginModel(pluginId).migrate(versioned),\n * newPluginRegistry: { table1: 'dataTable' as PluginName },\n * createPluginData: (pluginId) => getPluginModel(pluginId).getDefaultData(),\n * });\n *\n * if (result.success) {\n * commitStorage(result.storage);\n * } else {\n * const userChoice = await askUser(`Migration failed: ${result.error}. Reset data?`);\n * if (userChoice === 'reset') {\n * commitStorage(createBlockStorage(initialData, currentVersion));\n * }\n * // else: abort, keep original\n * }\n */\nexport function migrateBlockStorage(\n storage: BlockStorage<unknown>,\n config: MigrateBlockStorageConfig,\n): MigrationResult<unknown> {\n const { migrateBlockData, migratePluginData, newPluginRegistry, createPluginData } = config;\n\n // Step 1: Migrate block data\n let migratedData: unknown;\n let newVersion: string;\n try {\n const result = migrateBlockData({ version: storage.__dataVersion, data: storage.__data });\n migratedData = result.data;\n newVersion = result.version;\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n failedAt: \"block\",\n };\n }\n\n // Step 2: Migrate plugins\n const oldPlugins = storage.__plugins ?? {};\n const oldRegistry = storage.__pluginRegistry ?? {};\n const newPlugins: Record<PluginHandle, VersionedData<unknown>> = {};\n\n for (const [key, pluginName] of Object.entries(newPluginRegistry)) {\n const handle = key as PluginHandle;\n const existingEntry = oldPlugins[handle];\n const existingName = oldRegistry[handle];\n\n try {\n if (existingEntry && existingName === pluginName) {\n // Plugin exists with same type - migrate its data\n const migrated = migratePluginData(handle, {\n version: existingEntry.__dataVersion,\n data: existingEntry.__data,\n });\n if (migrated) {\n newPlugins[handle] = { __dataVersion: migrated.version, __data: migrated.data };\n }\n // If undefined returned, plugin is intentionally removed\n } else {\n // New plugin or type changed - create with initial data\n const initial = createPluginData(handle);\n newPlugins[handle] = { __dataVersion: initial.version, __data: initial.data };\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n failedAt: handle,\n };\n }\n }\n\n // Step 3: Build final storage atomically\n const migratedStorage: BlockStorage = {\n [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,\n __dataVersion: newVersion,\n __data: migratedData,\n __pluginRegistry: newPluginRegistry,\n __plugins: newPlugins,\n };\n\n return {\n success: true,\n storage: migratedStorage,\n };\n}\n\n/**\n * Gets plugin-specific data from block storage.\n * Accepts raw storage (any format) and normalizes internally.\n *\n * When called with a typed PluginHandle<F>, the return type is automatically\n * inferred from the factory's phantom `__types.data` field.\n *\n * @param rawStorage - Raw block storage (may be legacy format or BlockStorage)\n * @param handle - The plugin handle (branded plugin instance id)\n * @returns The plugin data, typed via factory inference\n * @throws If plugin is not found in storage\n */\nexport function getPluginData<F extends PluginFactoryLike>(\n rawStorage: unknown,\n handle: PluginHandle<F>,\n): InferFactoryData<F> {\n const storage = normalizeBlockStorage(rawStorage);\n const pluginEntry = storage.__plugins?.[handle];\n if (!pluginEntry) throw new Error(`Plugin '${handle}' not found in block storage`);\n return pluginEntry.__data as InferFactoryData<F>;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"block_storage.js","names":[],"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\nimport type { Branded } from \"@milaboratories/pl-model-common\";\nimport {\n type DataVersioned,\n type TransferRecord,\n isDataUnrecoverableError,\n} from \"./block_migrations\";\nimport type { PluginHandle, PluginFactoryLike, InferFactoryData } from \"./plugin_handle\";\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 * Default data version for new blocks without migrations.\n * Unique identifier ensures blocks are created via DataModel API.\n */\nexport const DATA_MODEL_LEGACY_VERSION = \"__pl_v1_d4e8f2a1__\";\n\n/**\n * Type for valid schema versions\n */\nexport type BlockStorageSchemaVersion = \"v1\"; // Add 'v2', 'v3', etc. as schema evolves\n\n/**\n * Branded type for plugin names - globally unique plugin type identifiers.\n * Using a branded type enforces explicit casting (`as PluginName`) which makes\n * it easy to find all plugin name definitions in the codebase and verify uniqueness.\n */\nexport type PluginName = Branded<string, \"PluginName\">;\n\n/**\n * Plugin registry - maps pluginId (unique within a block) to pluginName (globally unique plugin type).\n * Using a Record highlights that pluginIds must be unique within a block.\n */\nexport type PluginRegistry = Record<PluginHandle, PluginName>;\n\n/**\n * Versioned data - used for both block data and plugin data\n */\nexport interface VersionedData<TData = unknown> {\n /** Version of the data, used for migrations */\n __dataVersion: string;\n /** The persistent data */\n __data: TData;\n}\n\n/**\n * Core BlockStorage type that holds:\n * - __pl_a7f3e2b9__: Schema version (discriminator key identifies BlockStorage format)\n * - __dataVersion: Version key for block data migrations\n * - __data: The block's user-facing data (state)\n * - __pluginRegistry: Map from pluginId to pluginName (optional)\n * - __plugins: Plugin-specific data keyed by pluginId (optional)\n */\nexport type BlockStorage<TState = unknown> = {\n /** Schema version - the key itself is the discriminator */\n readonly [BLOCK_STORAGE_KEY]: BlockStorageSchemaVersion;\n /** Registry of plugins: pluginId -> pluginName */\n __pluginRegistry?: PluginRegistry;\n /** Plugin-specific data, keyed by plugin handle */\n __plugins?: Record<PluginHandle, VersionedData<unknown>>;\n} & VersionedData<TState>;\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 key (defaults to DATA_MODEL_LEGACY_VERSION)\n * @returns A new BlockStorage instance with discriminator key\n */\nexport function createBlockStorage<TState = unknown>(\n initialData: TState = {} as TState,\n version: string = DATA_MODEL_LEGACY_VERSION,\n): BlockStorage<TState> {\n return {\n [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,\n __dataVersion: version,\n __data: initialData,\n __pluginRegistry: {},\n __plugins: {},\n };\n}\n\n/**\n * Normalizes raw storage data to BlockStorage format.\n * If the input is already a BlockStorage, returns it as-is (with defaults for missing fields).\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 const storage = raw as BlockStorage<TState>;\n return {\n ...storage,\n // Fix for early released version where __dataVersion was a number\n __dataVersion:\n typeof storage.__dataVersion === \"number\"\n ? DATA_MODEL_LEGACY_VERSION\n : storage.__dataVersion,\n // Ensure plugin fields have defaults\n __pluginRegistry: storage.__pluginRegistry ?? {},\n __plugins: storage.__plugins ?? {},\n };\n }\n // Legacy format: raw is the state directly\n return createBlockStorage(raw as TState);\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>(rawStorage: unknown): 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> =\n | { operation: \"update-block-data\"; value: T }\n | { operation: \"update-plugin-data\"; pluginId: PluginHandle; value: unknown };\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-block-data\":\n return { ...storage, __data: payload.value };\n case \"update-plugin-data\": {\n const { pluginId, value } = payload;\n const currentPlugins = storage.__plugins ?? {};\n const existingEntry = currentPlugins[pluginId];\n const version = existingEntry?.__dataVersion ?? DATA_MODEL_LEGACY_VERSION;\n return {\n ...storage,\n __plugins: {\n ...currentPlugins,\n [pluginId]: {\n __dataVersion: version,\n __data: value,\n },\n },\n };\n }\n default:\n throw new Error(`Unknown storage operation: ${(payload as { operation: string }).operation}`);\n }\n}\n\n/**\n * Storage debug view returned by __pl_storage_debugView callback.\n * Used by developer tools to display block storage info.\n */\nexport interface StorageDebugView {\n /** Current data version key */\n dataVersion: string;\n /** Raw data payload stored in BlockStorage */\n data: unknown;\n}\n\n// =============================================================================\n// Atomic Migration\n// =============================================================================\n\n/**\n * Result of a successful atomic migration.\n */\nexport interface MigrationSuccess<TState> {\n success: true;\n /** The fully migrated storage - commit this to persist */\n storage: BlockStorage<TState>;\n}\n\n/**\n * Result of a failed atomic migration.\n * The original storage is untouched - user must choose to abort or reset.\n */\nexport interface MigrationFailure {\n success: false;\n /** Description of what failed */\n error: string;\n /** Which step failed: 'block' or pluginId */\n failedAt: string;\n}\n\nexport type MigrationResult<TState> = MigrationSuccess<TState> | MigrationFailure;\n\n/**\n * Configuration for atomic block storage migration.\n * Callbacks use DataVersioned format (the DataModel API format).\n * Conversion to internal VersionedData format is handled by migrateBlockStorage().\n */\nexport interface MigrateBlockStorageConfig {\n /** Migrate block data from any version to latest. Returns migrated data and transfers. */\n migrateBlockData: (versioned: DataVersioned<unknown>) => DataVersioned<unknown> & {\n transfers: TransferRecord;\n };\n /** Migrate each plugin's data. Return undefined to remove the plugin. Throws on failure. */\n migratePluginData: (\n handle: PluginHandle,\n versioned: DataVersioned<unknown>,\n ) => DataVersioned<unknown> | undefined;\n /** The new plugin registry after migration (pluginId -> pluginName) */\n newPluginRegistry: PluginRegistry;\n /** Factory to create initial data for new plugins. Transfer is provided when a\n * .transfer() was defined for this plugin in the block's migration chain. */\n createPluginData: (\n handle: PluginHandle,\n transfer?: DataVersioned<unknown>,\n ) => DataVersioned<unknown>;\n}\n\n/**\n * Performs atomic migration of block storage including block data and all plugins.\n *\n * Migration is atomic: either everything succeeds and a new storage is returned,\n * or an error is returned and the original storage is completely untouched.\n *\n * Migration steps:\n * 1. Migrate block data\n * 2. For each plugin in newPluginRegistry:\n * - If plugin exists with same name: migrate its data\n * - Otherwise (new or type changed): create with initial data\n * Plugins not in newPluginRegistry are dropped.\n *\n * If any step throws, migration fails and original storage is preserved.\n * User can then choose to:\n * - Abort: keep original storage, don't update block\n * - Reset: call createBlockStorage() to start fresh\n *\n * @param storage - The original storage (will not be modified)\n * @param config - Migration configuration\n * @returns Migration result - either success with new storage, or failure with error info\n *\n * @example\n * const result = migrateBlockStorage(storage, {\n * migrateBlockData: (versioned) => blockDataModel.migrate(versioned),\n * migratePluginData: (pluginId, versioned) => getPluginModel(pluginId).migrate(versioned),\n * newPluginRegistry: { table1: 'dataTable' as PluginName },\n * createPluginData: (pluginId) => getPluginModel(pluginId).getDefaultData(),\n * });\n *\n * if (result.success) {\n * commitStorage(result.storage);\n * } else {\n * const userChoice = await askUser(`Migration failed: ${result.error}. Reset data?`);\n * if (userChoice === 'reset') {\n * commitStorage(createBlockStorage(initialData, currentVersion));\n * }\n * // else: abort, keep original\n * }\n */\nexport function migrateBlockStorage(\n storage: BlockStorage<unknown>,\n config: MigrateBlockStorageConfig,\n): MigrationResult<unknown> {\n const { migrateBlockData, migratePluginData, newPluginRegistry, createPluginData } = config;\n\n // Step 1: Migrate block data and collect transfers\n let migratedData: unknown;\n let newVersion: string;\n let transfers: TransferRecord;\n try {\n const result = migrateBlockData({ version: storage.__dataVersion, data: storage.__data });\n migratedData = result.data;\n newVersion = result.version;\n transfers = result.transfers;\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n failedAt: \"block\",\n };\n }\n\n // Step 2: Migrate plugins\n const oldPlugins = storage.__plugins ?? {};\n const oldRegistry = storage.__pluginRegistry ?? {};\n const newPlugins: Record<PluginHandle, VersionedData<unknown>> = {};\n\n for (const [key, pluginName] of Object.entries(newPluginRegistry)) {\n const handle = key as PluginHandle;\n const existingEntry = oldPlugins[handle];\n const existingName = oldRegistry[handle];\n\n try {\n if (existingEntry && existingName === pluginName) {\n // Plugin exists with same type - migrate its data\n const migrated = migratePluginData(handle, {\n version: existingEntry.__dataVersion,\n data: existingEntry.__data,\n });\n if (migrated) {\n newPlugins[handle] = { __dataVersion: migrated.version, __data: migrated.data };\n }\n // If undefined returned, plugin is intentionally removed\n } else if (existingEntry) {\n // Plugin type changed — pass old data with DATA_MODEL_LEGACY_VERSION.\n // If the new plugin has upgradeLegacy(), it migrates the old data.\n // If not, defaultRecover throws DataUnrecoverableError → fall back to init.\n let recovered = false;\n try {\n const migrated = migratePluginData(handle, {\n version: DATA_MODEL_LEGACY_VERSION,\n data: existingEntry.__data,\n });\n if (migrated) {\n newPlugins[handle] = { __dataVersion: migrated.version, __data: migrated.data };\n recovered = true;\n }\n } catch (recoverError) {\n if (!isDataUnrecoverableError(recoverError)) throw recoverError;\n }\n if (!recovered) {\n const transfer = transfers[handle];\n const initial = createPluginData(handle, transfer);\n newPlugins[handle] = { __dataVersion: initial.version, __data: initial.data };\n }\n } else {\n // New plugin - create with initial data, passing transfer if available\n const transfer = transfers[handle];\n const initial = createPluginData(handle, transfer);\n newPlugins[handle] = { __dataVersion: initial.version, __data: initial.data };\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n failedAt: handle,\n };\n }\n }\n\n // Step 3: Build final storage atomically\n const migratedStorage: BlockStorage = {\n [BLOCK_STORAGE_KEY]: BLOCK_STORAGE_SCHEMA_VERSION,\n __dataVersion: newVersion,\n __data: migratedData,\n __pluginRegistry: newPluginRegistry,\n __plugins: newPlugins,\n };\n\n return {\n success: true,\n storage: migratedStorage,\n };\n}\n\n/**\n * Gets plugin-specific data from block storage.\n * Accepts raw storage (any format) and normalizes internally.\n *\n * When called with a typed PluginHandle<F>, the return type is automatically\n * inferred from the factory's phantom `__types.data` field.\n *\n * @param rawStorage - Raw block storage (may be legacy format or BlockStorage)\n * @param handle - The plugin handle (branded plugin instance id)\n * @returns The plugin data, typed via factory inference\n * @throws If plugin is not found in storage\n */\nexport function getPluginData<F extends PluginFactoryLike>(\n rawStorage: unknown,\n handle: PluginHandle<F>,\n): InferFactoryData<F> {\n const storage = normalizeBlockStorage(rawStorage);\n const pluginEntry = storage.__plugins?.[handle];\n if (!pluginEntry) throw new Error(`Plugin '${handle}' not found in block storage`);\n return pluginEntry.__data as InferFactoryData<F>;\n}\n"],"mappings":"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"}