@platforma-sdk/model 1.79.24 → 1.80.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 (356) hide show
  1. package/dist/block_model.d.ts +2 -0
  2. package/dist/block_model.d.ts.map +1 -1
  3. package/dist/columns/column.cjs +28 -0
  4. package/dist/columns/column.cjs.map +1 -0
  5. package/dist/columns/column.d.ts +30 -0
  6. package/dist/columns/column.d.ts.map +1 -0
  7. package/dist/columns/column.js +27 -0
  8. package/dist/columns/column.js.map +1 -0
  9. package/dist/columns/column_lazy.cjs +248 -0
  10. package/dist/columns/column_lazy.cjs.map +1 -0
  11. package/dist/columns/column_lazy.d.ts +137 -0
  12. package/dist/columns/column_lazy.d.ts.map +1 -0
  13. package/dist/columns/column_lazy.js +245 -0
  14. package/dist/columns/column_lazy.js.map +1 -0
  15. package/dist/columns/column_providers/index.cjs +62 -0
  16. package/dist/columns/column_providers/index.cjs.map +1 -0
  17. package/dist/columns/column_providers/index.d.ts +30 -0
  18. package/dist/columns/column_providers/index.d.ts.map +1 -0
  19. package/dist/columns/column_providers/index.js +53 -0
  20. package/dist/columns/column_providers/index.js.map +1 -0
  21. package/dist/columns/column_providers/providers.cjs +123 -0
  22. package/dist/columns/column_providers/providers.cjs.map +1 -0
  23. package/dist/columns/column_providers/providers.d.ts +75 -0
  24. package/dist/columns/column_providers/providers.d.ts.map +1 -0
  25. package/dist/columns/column_providers/providers.js +118 -0
  26. package/dist/columns/column_providers/providers.js.map +1 -0
  27. package/dist/columns/column_providers/types.d.ts +23 -0
  28. package/dist/columns/column_providers/types.d.ts.map +1 -0
  29. package/dist/columns/column_recipes/column_discovered_recipe.cjs +180 -0
  30. package/dist/columns/column_recipes/column_discovered_recipe.cjs.map +1 -0
  31. package/dist/columns/column_recipes/column_discovered_recipe.d.ts +120 -0
  32. package/dist/columns/column_recipes/column_discovered_recipe.d.ts.map +1 -0
  33. package/dist/columns/column_recipes/column_discovered_recipe.js +180 -0
  34. package/dist/columns/column_recipes/column_discovered_recipe.js.map +1 -0
  35. package/dist/columns/column_recipes/column_filtered_recipe.cjs +133 -0
  36. package/dist/columns/column_recipes/column_filtered_recipe.cjs.map +1 -0
  37. package/dist/columns/column_recipes/column_filtered_recipe.d.ts +75 -0
  38. package/dist/columns/column_recipes/column_filtered_recipe.d.ts.map +1 -0
  39. package/dist/columns/column_recipes/column_filtered_recipe.js +133 -0
  40. package/dist/columns/column_recipes/column_filtered_recipe.js.map +1 -0
  41. package/dist/columns/column_recipes/column_overrided_recipe.cjs +101 -0
  42. package/dist/columns/column_recipes/column_overrided_recipe.cjs.map +1 -0
  43. package/dist/columns/column_recipes/column_overrided_recipe.d.ts +73 -0
  44. package/dist/columns/column_recipes/column_overrided_recipe.d.ts.map +1 -0
  45. package/dist/columns/column_recipes/column_overrided_recipe.js +101 -0
  46. package/dist/columns/column_recipes/column_overrided_recipe.js.map +1 -0
  47. package/dist/columns/column_recipes/index.cjs +72 -0
  48. package/dist/columns/column_recipes/index.cjs.map +1 -0
  49. package/dist/columns/column_recipes/index.d.ts +52 -0
  50. package/dist/columns/column_recipes/index.d.ts.map +1 -0
  51. package/dist/columns/column_recipes/index.js +68 -0
  52. package/dist/columns/column_recipes/index.js.map +1 -0
  53. package/dist/columns/column_recipes/leaf_rebrand.cjs +22 -0
  54. package/dist/columns/column_recipes/leaf_rebrand.cjs.map +1 -0
  55. package/dist/columns/column_recipes/leaf_rebrand.js +22 -0
  56. package/dist/columns/column_recipes/leaf_rebrand.js.map +1 -0
  57. package/dist/columns/column_recipes/types.d.ts +101 -0
  58. package/dist/columns/column_recipes/types.d.ts.map +1 -0
  59. package/dist/columns/columns_collection.cjs +124 -0
  60. package/dist/columns/columns_collection.cjs.map +1 -0
  61. package/dist/columns/columns_collection.d.ts +64 -0
  62. package/dist/columns/columns_collection.d.ts.map +1 -0
  63. package/dist/columns/columns_collection.js +122 -0
  64. package/dist/columns/columns_collection.js.map +1 -0
  65. package/dist/columns/derive_axis_values_labels.cjs +76 -0
  66. package/dist/columns/derive_axis_values_labels.cjs.map +1 -0
  67. package/dist/columns/derive_axis_values_labels.d.ts +30 -0
  68. package/dist/columns/derive_axis_values_labels.d.ts.map +1 -0
  69. package/dist/columns/derive_axis_values_labels.js +76 -0
  70. package/dist/columns/derive_axis_values_labels.js.map +1 -0
  71. package/dist/columns/expand_by_partition.cjs +53 -56
  72. package/dist/columns/expand_by_partition.cjs.map +1 -1
  73. package/dist/columns/expand_by_partition.d.ts +22 -18
  74. package/dist/columns/expand_by_partition.d.ts.map +1 -1
  75. package/dist/columns/expand_by_partition.js +55 -58
  76. package/dist/columns/expand_by_partition.js.map +1 -1
  77. package/dist/columns/index.cjs +44 -18
  78. package/dist/columns/index.d.ts +15 -7
  79. package/dist/columns/index.js +12 -6
  80. package/dist/columns/utils.cjs +186 -0
  81. package/dist/columns/utils.cjs.map +1 -0
  82. package/dist/columns/utils.d.ts +96 -0
  83. package/dist/columns/utils.d.ts.map +1 -0
  84. package/dist/columns/utils.js +179 -0
  85. package/dist/columns/utils.js.map +1 -0
  86. package/dist/components/PFrameForGraphs.cjs +1 -0
  87. package/dist/components/PFrameForGraphs.cjs.map +1 -1
  88. package/dist/components/PFrameForGraphs.d.ts +1 -1
  89. package/dist/components/PFrameForGraphs.d.ts.map +1 -1
  90. package/dist/components/PFrameForGraphs.js +1 -0
  91. package/dist/components/PFrameForGraphs.js.map +1 -1
  92. package/dist/components/PlDataTable/columnResolver.cjs +28 -0
  93. package/dist/components/PlDataTable/columnResolver.cjs.map +1 -0
  94. package/dist/components/PlDataTable/columnResolver.js +28 -0
  95. package/dist/components/PlDataTable/columnResolver.js.map +1 -0
  96. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.cjs +4 -2
  97. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.cjs.map +1 -1
  98. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.js +4 -2
  99. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV2.js.map +1 -1
  100. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.cjs +20 -25
  101. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.cjs.map +1 -1
  102. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.js +20 -25
  103. package/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.js.map +1 -1
  104. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.cjs +58 -26
  105. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.cjs.map +1 -1
  106. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.d.ts +1 -1
  107. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.d.ts.map +1 -1
  108. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.js +60 -28
  109. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.js.map +1 -1
  110. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs +224 -194
  111. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.cjs.map +1 -1
  112. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts +13 -16
  113. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.d.ts.map +1 -1
  114. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js +228 -198
  115. package/dist/components/PlDataTable/createPlDataTable/createPlDataTableV3.js.map +1 -1
  116. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs +40 -98
  117. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.cjs.map +1 -1
  118. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.d.ts +17 -8
  119. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.d.ts.map +1 -1
  120. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js +40 -98
  121. package/dist/components/PlDataTable/createPlDataTable/discoverColumns.js.map +1 -1
  122. package/dist/components/PlDataTable/createPlDataTable/index.cjs +1 -1
  123. package/dist/components/PlDataTable/createPlDataTable/index.cjs.map +1 -1
  124. package/dist/components/PlDataTable/createPlDataTable/index.d.ts +2 -2
  125. package/dist/components/PlDataTable/createPlDataTable/index.js +2 -2
  126. package/dist/components/PlDataTable/createPlDataTable/index.js.map +1 -1
  127. package/dist/components/PlDataTable/createPlDataTable/utils.cjs +51 -169
  128. package/dist/components/PlDataTable/createPlDataTable/utils.cjs.map +1 -1
  129. package/dist/components/PlDataTable/createPlDataTable/utils.d.ts +1 -1
  130. package/dist/components/PlDataTable/createPlDataTable/utils.d.ts.map +1 -1
  131. package/dist/components/PlDataTable/createPlDataTable/utils.js +52 -166
  132. package/dist/components/PlDataTable/createPlDataTable/utils.js.map +1 -1
  133. package/dist/components/PlDataTable/createPlDataTableSheet.cjs.map +1 -1
  134. package/dist/components/PlDataTable/createPlDataTableSheet.d.ts +1 -1
  135. package/dist/components/PlDataTable/createPlDataTableSheet.js.map +1 -1
  136. package/dist/components/PlDataTable/index.cjs +1 -1
  137. package/dist/components/PlDataTable/index.d.ts +4 -4
  138. package/dist/components/PlDataTable/index.js +2 -2
  139. package/dist/components/PlDataTable/labels.cjs +0 -1
  140. package/dist/components/PlDataTable/labels.cjs.map +1 -1
  141. package/dist/components/PlDataTable/labels.js +0 -1
  142. package/dist/components/PlDataTable/labels.js.map +1 -1
  143. package/dist/components/PlDataTable/state-migration.cjs +107 -10
  144. package/dist/components/PlDataTable/state-migration.cjs.map +1 -1
  145. package/dist/components/PlDataTable/state-migration.d.ts +5 -4
  146. package/dist/components/PlDataTable/state-migration.d.ts.map +1 -1
  147. package/dist/components/PlDataTable/state-migration.js +107 -10
  148. package/dist/components/PlDataTable/state-migration.js.map +1 -1
  149. package/dist/components/PlDataTable/typesV7.d.ts +1 -36
  150. package/dist/components/PlDataTable/typesV7.d.ts.map +1 -1
  151. package/dist/components/PlDataTable/typesV8.d.ts +123 -0
  152. package/dist/components/PlDataTable/typesV8.d.ts.map +1 -0
  153. package/dist/components/PlDatasetSelector/build_dataset_options.cjs +27 -34
  154. package/dist/components/PlDatasetSelector/build_dataset_options.cjs.map +1 -1
  155. package/dist/components/PlDatasetSelector/build_dataset_options.d.ts.map +1 -1
  156. package/dist/components/PlDatasetSelector/build_dataset_options.js +27 -34
  157. package/dist/components/PlDatasetSelector/build_dataset_options.js.map +1 -1
  158. package/dist/components/PlDatasetSelector/enrichment_discovery.cjs +28 -33
  159. package/dist/components/PlDatasetSelector/enrichment_discovery.cjs.map +1 -1
  160. package/dist/components/PlDatasetSelector/enrichment_discovery.js +29 -34
  161. package/dist/components/PlDatasetSelector/enrichment_discovery.js.map +1 -1
  162. package/dist/components/PlDatasetSelector/filter_discovery.cjs +27 -17
  163. package/dist/components/PlDatasetSelector/filter_discovery.cjs.map +1 -1
  164. package/dist/components/PlDatasetSelector/filter_discovery.d.ts +24 -10
  165. package/dist/components/PlDatasetSelector/filter_discovery.d.ts.map +1 -1
  166. package/dist/components/PlDatasetSelector/filter_discovery.js +28 -18
  167. package/dist/components/PlDatasetSelector/filter_discovery.js.map +1 -1
  168. package/dist/components/PlDatasetSelector/index.d.ts +2 -2
  169. package/dist/components/index.cjs +1 -1
  170. package/dist/components/index.d.ts +5 -5
  171. package/dist/components/index.js +2 -2
  172. package/dist/filters/converters/filterToQuery.cjs +5 -6
  173. package/dist/filters/converters/filterToQuery.cjs.map +1 -1
  174. package/dist/filters/converters/filterToQuery.d.ts +12 -3
  175. package/dist/filters/converters/filterToQuery.d.ts.map +1 -1
  176. package/dist/filters/converters/filterToQuery.js +5 -6
  177. package/dist/filters/converters/filterToQuery.js.map +1 -1
  178. package/dist/filters/converters/index.d.ts +2 -2
  179. package/dist/filters/index.d.ts +2 -2
  180. package/dist/index.cjs +64 -35
  181. package/dist/index.d.ts +23 -15
  182. package/dist/index.js +15 -9
  183. package/dist/labels/derive_distinct_labels.cjs.map +1 -1
  184. package/dist/labels/derive_distinct_labels.d.ts +1 -2
  185. package/dist/labels/derive_distinct_labels.d.ts.map +1 -1
  186. package/dist/labels/derive_distinct_labels.js.map +1 -1
  187. package/dist/labels/derive_distinct_tooltips.cjs +1 -1
  188. package/dist/labels/derive_distinct_tooltips.cjs.map +1 -1
  189. package/dist/labels/derive_distinct_tooltips.d.ts +6 -3
  190. package/dist/labels/derive_distinct_tooltips.d.ts.map +1 -1
  191. package/dist/labels/derive_distinct_tooltips.js +1 -1
  192. package/dist/labels/derive_distinct_tooltips.js.map +1 -1
  193. package/dist/package.cjs +1 -1
  194. package/dist/package.js +1 -1
  195. package/dist/pframe_utils/axes.cjs.map +1 -1
  196. package/dist/pframe_utils/axes.d.ts +2 -2
  197. package/dist/pframe_utils/axes.d.ts.map +1 -1
  198. package/dist/pframe_utils/axes.js.map +1 -1
  199. package/dist/pframe_utils/columns.cjs +6 -10
  200. package/dist/pframe_utils/columns.cjs.map +1 -1
  201. package/dist/pframe_utils/columns.d.ts +4 -4
  202. package/dist/pframe_utils/columns.d.ts.map +1 -1
  203. package/dist/pframe_utils/columns.js +7 -11
  204. package/dist/pframe_utils/columns.js.map +1 -1
  205. package/dist/pframe_utils/index.cjs.map +1 -1
  206. package/dist/pframe_utils/index.d.ts +1 -1
  207. package/dist/pframe_utils/index.d.ts.map +1 -1
  208. package/dist/pframe_utils/index.js.map +1 -1
  209. package/dist/render/accessor.cjs +15 -16
  210. package/dist/render/accessor.cjs.map +1 -1
  211. package/dist/render/accessor.d.ts +7 -10
  212. package/dist/render/accessor.d.ts.map +1 -1
  213. package/dist/render/accessor.js +15 -16
  214. package/dist/render/accessor.js.map +1 -1
  215. package/dist/render/api.cjs +34 -36
  216. package/dist/render/api.cjs.map +1 -1
  217. package/dist/render/api.d.ts +30 -20
  218. package/dist/render/api.d.ts.map +1 -1
  219. package/dist/render/api.js +35 -38
  220. package/dist/render/api.js.map +1 -1
  221. package/dist/render/index.cjs +4 -0
  222. package/dist/render/index.d.ts +4 -4
  223. package/dist/render/index.js +3 -2
  224. package/dist/render/internal.cjs +3 -2
  225. package/dist/render/internal.cjs.map +1 -1
  226. package/dist/render/internal.d.ts +17 -9
  227. package/dist/render/internal.d.ts.map +1 -1
  228. package/dist/render/internal.js +3 -2
  229. package/dist/render/internal.js.map +1 -1
  230. package/dist/render/util/axis_filtering.cjs.map +1 -1
  231. package/dist/render/util/axis_filtering.js.map +1 -1
  232. package/dist/render/util/column_collection.cjs +6 -4
  233. package/dist/render/util/column_collection.cjs.map +1 -1
  234. package/dist/render/util/column_collection.d.ts +9 -9
  235. package/dist/render/util/column_collection.d.ts.map +1 -1
  236. package/dist/render/util/column_collection.js +6 -4
  237. package/dist/render/util/column_collection.js.map +1 -1
  238. package/dist/render/util/index.d.ts +2 -2
  239. package/dist/render/util/pcolumn_data.cjs +1 -1
  240. package/dist/render/util/pcolumn_data.cjs.map +1 -1
  241. package/dist/render/util/pcolumn_data.d.ts +3 -3
  242. package/dist/render/util/pcolumn_data.d.ts.map +1 -1
  243. package/dist/render/util/pcolumn_data.js +1 -1
  244. package/dist/render/util/pcolumn_data.js.map +1 -1
  245. package/dist/services/block_services.cjs +2 -1
  246. package/dist/services/block_services.cjs.map +1 -1
  247. package/dist/services/block_services.d.ts +1 -0
  248. package/dist/services/block_services.d.ts.map +1 -1
  249. package/dist/services/block_services.js +2 -1
  250. package/dist/services/block_services.js.map +1 -1
  251. package/dist/services/get_services.cjs +2 -2
  252. package/dist/services/get_services.cjs.map +1 -1
  253. package/dist/services/get_services.d.ts +4 -1
  254. package/dist/services/get_services.d.ts.map +1 -1
  255. package/dist/services/get_services.js +2 -2
  256. package/dist/services/get_services.js.map +1 -1
  257. package/package.json +9 -7
  258. package/src/columns/__test_helpers__/collection_driver.ts +197 -0
  259. package/src/columns/__test_helpers__/stub_registry.ts +134 -0
  260. package/src/columns/column.ts +46 -0
  261. package/src/columns/column_lazy.test.ts +201 -0
  262. package/src/columns/column_lazy.ts +311 -0
  263. package/src/columns/column_providers/index.ts +78 -0
  264. package/src/columns/column_providers/providers.ts +193 -0
  265. package/src/columns/column_providers/types.ts +24 -0
  266. package/src/columns/column_recipes/column_discovered_recipe.ts +231 -0
  267. package/src/columns/column_recipes/column_filtered_recipe.ts +184 -0
  268. package/src/columns/column_recipes/column_overrided_recipe.ts +146 -0
  269. package/src/columns/column_recipes/column_recipes.test.ts +681 -0
  270. package/src/columns/column_recipes/index.ts +127 -0
  271. package/src/columns/column_recipes/leaf_rebrand.ts +28 -0
  272. package/src/columns/column_recipes/types.ts +113 -0
  273. package/src/columns/columns_collection.test.ts +164 -0
  274. package/src/columns/columns_collection.ts +170 -0
  275. package/src/columns/derive_axis_values_labels.ts +98 -0
  276. package/src/columns/expand_by_partition.test.ts +146 -110
  277. package/src/columns/expand_by_partition.ts +72 -84
  278. package/src/columns/index.ts +7 -5
  279. package/src/columns/utils.test.ts +249 -0
  280. package/src/columns/utils.ts +226 -0
  281. package/src/components/PFrameForGraphs.ts +3 -1
  282. package/src/components/PlAnnotations/filters_ui.test.ts +46 -47
  283. package/src/components/PlDataTable/columnResolver.test.ts +122 -0
  284. package/src/components/PlDataTable/columnResolver.ts +57 -0
  285. package/src/components/PlDataTable/createPlDataTable/createPTableDefV2.ts +9 -9
  286. package/src/components/PlDataTable/createPlDataTable/createPTableDefV3.ts +31 -63
  287. package/src/components/PlDataTable/createPlDataTable/createPlDataTableV2.ts +85 -58
  288. package/src/components/PlDataTable/createPlDataTable/createPlDataTableV3.ts +316 -354
  289. package/src/components/PlDataTable/createPlDataTable/discoverColumns.ts +48 -144
  290. package/src/components/PlDataTable/createPlDataTable/index.ts +2 -2
  291. package/src/components/PlDataTable/createPlDataTable/utils.test.ts +60 -69
  292. package/src/components/PlDataTable/createPlDataTable/utils.ts +65 -173
  293. package/src/components/PlDataTable/createPlDataTableSheet.ts +1 -1
  294. package/src/components/PlDataTable/index.ts +4 -2
  295. package/src/components/PlDataTable/labels.ts +4 -33
  296. package/src/components/PlDataTable/state-migration.ts +151 -22
  297. package/src/components/PlDataTable/typesV8.ts +188 -0
  298. package/src/components/PlDatasetSelector/build_dataset_options.ts +35 -60
  299. package/src/components/PlDatasetSelector/enrichment_discovery.ts +65 -61
  300. package/src/components/PlDatasetSelector/filter_discovery.test.ts +110 -72
  301. package/src/components/PlDatasetSelector/filter_discovery.ts +51 -31
  302. package/src/filters/converters/filterToQuery.test.ts +15 -8
  303. package/src/filters/converters/filterToQuery.ts +22 -9
  304. package/src/filters/converters/filterUiToExpressionImpl.test.ts +35 -36
  305. package/src/labels/derive_distinct_labels.ts +1 -1
  306. package/src/labels/derive_distinct_tooltips.test.ts +7 -4
  307. package/src/labels/derive_distinct_tooltips.ts +3 -3
  308. package/src/pframe_utils/axes.ts +2 -2
  309. package/src/pframe_utils/columns.ts +14 -23
  310. package/src/pframe_utils/index.ts +1 -1
  311. package/src/render/accessor.ts +35 -38
  312. package/src/render/api.ts +45 -61
  313. package/src/render/index.ts +2 -1
  314. package/src/render/internal.ts +43 -10
  315. package/src/render/util/axis_filtering.ts +0 -1
  316. package/src/render/util/column_collection.ts +17 -17
  317. package/src/render/util/pcolumn_data.ts +5 -9
  318. package/src/services/block_services.ts +1 -0
  319. package/src/services/get_services.ts +5 -2
  320. package/dist/columns/column_collection_builder.cjs +0 -241
  321. package/dist/columns/column_collection_builder.cjs.map +0 -1
  322. package/dist/columns/column_collection_builder.d.ts +0 -119
  323. package/dist/columns/column_collection_builder.d.ts.map +0 -1
  324. package/dist/columns/column_collection_builder.js +0 -241
  325. package/dist/columns/column_collection_builder.js.map +0 -1
  326. package/dist/columns/column_selector.cjs +0 -49
  327. package/dist/columns/column_selector.cjs.map +0 -1
  328. package/dist/columns/column_selector.d.ts +0 -33
  329. package/dist/columns/column_selector.d.ts.map +0 -1
  330. package/dist/columns/column_selector.js +0 -47
  331. package/dist/columns/column_selector.js.map +0 -1
  332. package/dist/columns/column_snapshot.cjs +0 -19
  333. package/dist/columns/column_snapshot.cjs.map +0 -1
  334. package/dist/columns/column_snapshot.d.ts +0 -39
  335. package/dist/columns/column_snapshot.d.ts.map +0 -1
  336. package/dist/columns/column_snapshot.js +0 -18
  337. package/dist/columns/column_snapshot.js.map +0 -1
  338. package/dist/columns/column_snapshot_provider.cjs +0 -137
  339. package/dist/columns/column_snapshot_provider.cjs.map +0 -1
  340. package/dist/columns/column_snapshot_provider.d.ts +0 -74
  341. package/dist/columns/column_snapshot_provider.d.ts.map +0 -1
  342. package/dist/columns/column_snapshot_provider.js +0 -133
  343. package/dist/columns/column_snapshot_provider.js.map +0 -1
  344. package/dist/columns/ctx_column_sources.cjs +0 -80
  345. package/dist/columns/ctx_column_sources.cjs.map +0 -1
  346. package/dist/columns/ctx_column_sources.d.ts +0 -30
  347. package/dist/columns/ctx_column_sources.d.ts.map +0 -1
  348. package/dist/columns/ctx_column_sources.js +0 -79
  349. package/dist/columns/ctx_column_sources.js.map +0 -1
  350. package/src/columns/column_collection_builder.test.ts +0 -484
  351. package/src/columns/column_collection_builder.ts +0 -485
  352. package/src/columns/column_selector.test.ts +0 -90
  353. package/src/columns/column_selector.ts +0 -100
  354. package/src/columns/column_snapshot.ts +0 -58
  355. package/src/columns/column_snapshot_provider.ts +0 -194
  356. package/src/columns/ctx_column_sources.ts +0 -99
@@ -0,0 +1,681 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "vitest";
2
+ import {
3
+ createColumnDiscoveredKey,
4
+ createColumnFilteredKey,
5
+ createColumnOverriddenId,
6
+ createColumnOverriddenKey,
7
+ createGlobalPObjectId,
8
+ createLocalPObjectId,
9
+ isFilteredPColumn,
10
+ isColumnOverriddenKey,
11
+ isColumnDiscoveredKey,
12
+ parseColumnIdSafely,
13
+ parseColumnOverriddenId,
14
+ stringifyColumnDiscoveredId,
15
+ stringifyColumnFilteredId,
16
+ type ColumnDiscoveredKey,
17
+ type PColumnSpec,
18
+ type PObjectId,
19
+ type SpecOverrides,
20
+ type SpecQuery,
21
+ type ColumnUniversalId,
22
+ } from "@milaboratories/pl-model-common";
23
+ import { ColumnDiscoveredRecipe } from "./column_discovered_recipe";
24
+ import { ColumnOverriddenRecipe } from "./column_overrided_recipe";
25
+ import { ColumnFilteredRecipe } from "./column_filtered_recipe";
26
+ import { ColumnRecipe } from "./index";
27
+ import { ColumnAbsentError } from "../column_lazy";
28
+ import type { ColumnFieldStatus } from "./types";
29
+ import { installStubRegistry } from "../__test_helpers__/stub_registry";
30
+ import { throwError } from "@milaboratories/helpers";
31
+
32
+ // ── ctx mock ────────────────────────────────────────────────────────────────
33
+ // Recipe constructors pull `getCfgRenderCtx()` from globalThis. A minimal
34
+ // mock with a no-op service dispatch is enough so that
35
+ // `new ColumnDiscoveredRecipe` does not throw in the constructor
36
+ // (getService("pframeSpec")).
37
+
38
+ function makeCtx(opts: { serviceMethods?: Record<string, (...args: unknown[]) => unknown> } = {}) {
39
+ const methods = opts.serviceMethods ?? {};
40
+ return {
41
+ getAccessorHandleByName: () => undefined,
42
+ getUpstreamBlockCtx: () => [],
43
+ getServiceNames: () => ["pframeSpec"],
44
+ getServiceMethods: () => Object.keys(methods),
45
+ callServiceMethod: (_id: string, method: string, ...args: unknown[]) =>
46
+ methods[method]?.(...args),
47
+ } as unknown as never;
48
+ }
49
+
50
+ beforeEach(() => {
51
+ const ctx = makeCtx();
52
+ (globalThis as { cfgRenderCtx?: unknown }).cfgRenderCtx = ctx;
53
+ });
54
+
55
+ afterEach(() => {
56
+ delete (globalThis as { cfgRenderCtx?: unknown }).cfgRenderCtx;
57
+ });
58
+
59
+ /** Install a fresh cfgRenderCtx (optionally pframeSpec-aware) plus a stub
60
+ * registry resolving the given (id → spec) map. */
61
+ function setupCtx(
62
+ specs: Record<PObjectId, PColumnSpec>,
63
+ opts: Parameters<typeof makeCtx>[0] = {},
64
+ ): void {
65
+ const ctx = makeCtx(opts);
66
+ (globalThis as { cfgRenderCtx?: unknown }).cfgRenderCtx = ctx;
67
+ installStubRegistry(ctx, specs);
68
+ }
69
+
70
+ // ── fixtures ────────────────────────────────────────────────────────────────
71
+
72
+ const baseLeaf = createLocalPObjectId(["main", "out"], "col-1");
73
+ const linkerId = createLocalPObjectId(["main"], "L1");
74
+
75
+ const stubSpec = (
76
+ axes: { name: string; type: string; domain?: Record<string, string> }[] = [],
77
+ ): PColumnSpec =>
78
+ ({
79
+ kind: "PColumn",
80
+ name: "stub",
81
+ valueType: "String",
82
+ axesSpec: axes,
83
+ annotations: {},
84
+ }) as unknown as PColumnSpec;
85
+
86
+ const overrides = (patch: Partial<SpecOverrides>): SpecOverrides => ({
87
+ annotations: patch.annotations ?? {},
88
+ domain: patch.domain ?? {},
89
+ contextDomain: patch.contextDomain ?? {},
90
+ axesSpec: patch.axesSpec ?? {},
91
+ });
92
+
93
+ /**
94
+ * Minimal `ColumnRecipe` stub — lets us test Overridden/Filtered without
95
+ * ctx mocking and without touching the pframeSpec service.
96
+ */
97
+ class StubRecipe implements ColumnRecipe<ColumnUniversalId> {
98
+ constructor(
99
+ readonly id: ColumnUniversalId,
100
+ private readonly state: {
101
+ spec?: PColumnSpec;
102
+ query?: SpecQuery;
103
+ refs?: PObjectId[];
104
+ status?: ColumnFieldStatus;
105
+ } = {},
106
+ ) {}
107
+
108
+ getReferencedIds(): PObjectId[] {
109
+ return this.state.refs ?? [this.id as unknown as PObjectId];
110
+ }
111
+ getDataStatus(): ColumnFieldStatus {
112
+ return this.state.status ?? "present";
113
+ }
114
+ getSpec(): PColumnSpec {
115
+ if (this.state.spec === undefined) throw new Error("StubRecipe: no stub spec");
116
+ return this.state.spec;
117
+ }
118
+ getQuery(): SpecQuery {
119
+ if (!this.state.query) throw new Error("StubRecipe: no stub query");
120
+ return this.state.query;
121
+ }
122
+ withSpecs(o: SpecOverrides): ColumnRecipe {
123
+ return ColumnOverriddenRecipe.wrap(this, o);
124
+ }
125
+ }
126
+
127
+ const leafQuery: SpecQuery = { type: "column", column: baseLeaf } as SpecQuery;
128
+
129
+ const stubInner = (state: ConstructorParameters<typeof StubRecipe>[1] = {}) =>
130
+ new StubRecipe(baseLeaf as unknown as ColumnUniversalId, state);
131
+
132
+ // ════════════════════════════════════════════════════════════════════════════
133
+ // ColumnOverriddenRecipe
134
+ // ════════════════════════════════════════════════════════════════════════════
135
+
136
+ describe("ColumnOverriddenRecipe", () => {
137
+ test("wrap(plain inner): id is Overridden{source: inner.id, specOverrides}", () => {
138
+ const inner = stubInner();
139
+ const ov = overrides({ annotations: { a: "1" } });
140
+ const wrapped = ColumnOverriddenRecipe.wrap(inner, ov);
141
+ const parsed = parseColumnOverriddenId(wrapped.id);
142
+ expect(parsed.__isOverridden).toBe(true);
143
+ expect(parsed.source).toBe(inner.id);
144
+ expect(parsed.specOverrides.annotations).toEqual({ a: "1" });
145
+ });
146
+
147
+ test("wrap on already-Overridden → flat merge (single layer, merged overrides)", () => {
148
+ const inner = stubInner();
149
+ const a = ColumnOverriddenRecipe.wrap(inner, overrides({ annotations: { x: "1" } }));
150
+ const b = ColumnOverriddenRecipe.wrap(a, overrides({ annotations: { y: "2" } }));
151
+
152
+ const parsed = parseColumnOverriddenId(b.id);
153
+ // source is the leaf, not a nested Overridden
154
+ expect(parsed.source).toBe(inner.id);
155
+ expect(parsed.specOverrides.annotations).toEqual({ x: "1", y: "2" });
156
+
157
+ // and source itself is not an Overridden wrap
158
+ const innerParsed = parseColumnIdSafely(parsed.source);
159
+ expect(innerParsed === undefined || !isColumnOverriddenKey(innerParsed)).toBe(true);
160
+ });
161
+
162
+ test("withSpecs(o1).withSpecs(o2) has same id as withSpecs(merge(o1,o2))", () => {
163
+ const inner = stubInner();
164
+ const a = ColumnOverriddenRecipe.wrap(inner, overrides({ annotations: { a: "1" } })).withSpecs(
165
+ overrides({ annotations: { b: "2" } }),
166
+ );
167
+ const b = ColumnOverriddenRecipe.wrap(inner, overrides({ annotations: { a: "1", b: "2" } }));
168
+ expect(a.id).toBe(b.id);
169
+ });
170
+
171
+ test("getSpec applies overrides to inner.getSpec()", () => {
172
+ const inner = stubInner({ spec: stubSpec() });
173
+ const wrapped = ColumnOverriddenRecipe.wrap(inner, overrides({ annotations: { a: "1" } }));
174
+ expect(wrapped.getSpec().annotations).toEqual({ a: "1" });
175
+ });
176
+
177
+ test("getSpec propagates throws from inner.getSpec()", () => {
178
+ // Inner without a stubbed spec throws — Overridden forwards the error.
179
+ const inner = stubInner({ spec: undefined });
180
+ expect(() =>
181
+ ColumnOverriddenRecipe.wrap(inner, overrides({ annotations: { a: "1" } })).getSpec(),
182
+ ).toThrow(/no stub spec/);
183
+ });
184
+
185
+ test("getReferencedIds / getDataStatus forward to inner", () => {
186
+ const refs = [baseLeaf, linkerId];
187
+ const inner = stubInner({
188
+ query: leafQuery,
189
+ refs,
190
+ status: "resolving",
191
+ });
192
+ const wrapped = ColumnOverriddenRecipe.wrap(inner, overrides({ annotations: { a: "1" } }));
193
+ expect(wrapped.getReferencedIds()).toEqual(refs);
194
+ expect(wrapped.getDataStatus()).toBe("resolving");
195
+ });
196
+
197
+ test("getQuery wraps inner query in a specOverride node carrying the overrides", () => {
198
+ const inner = stubInner({ query: leafQuery });
199
+ const ov = overrides({ annotations: { a: "1" } });
200
+ const wrapped = ColumnOverriddenRecipe.wrap(inner, ov);
201
+ const query = wrapped.getQuery();
202
+ expect(query.type).toBe("specOverride");
203
+ if (query.type === "specOverride") {
204
+ // Inner leaf gets rebranded to the Overridden id so every variant of the
205
+ // same physical column produces a distinct leaf for the engine.
206
+ expect(query.input).toEqual({ type: "column", column: wrapped.id });
207
+ expect(query.override).toBe(ov);
208
+ }
209
+ });
210
+
211
+ test("getQuery is cached: repeated calls return the same node", () => {
212
+ const inner = stubInner({ query: leafQuery });
213
+ const wrapped = ColumnOverriddenRecipe.wrap(inner, overrides({ annotations: { a: "1" } }));
214
+ expect(wrapped.getQuery()).toBe(wrapped.getQuery());
215
+ });
216
+ });
217
+
218
+ // ════════════════════════════════════════════════════════════════════════════
219
+ // ColumnFilteredRecipe
220
+ // ════════════════════════════════════════════════════════════════════════════
221
+
222
+ describe("ColumnFilteredRecipe", () => {
223
+ test("wrap(plain inner): id is FilteredPColumnId{source, axisFilters}", () => {
224
+ // Two axes so the filter on axis 0 leaves axis 1 — wrap rejects filters
225
+ // that fix every axis (an empty axis set has no useful column).
226
+ const inner = stubInner({
227
+ spec: stubSpec([
228
+ { name: "a", type: "String" },
229
+ { name: "b", type: "String" },
230
+ ]),
231
+ });
232
+ const filtered = ColumnFilteredRecipe.wrap(inner, [[0, "v1"]]);
233
+ const parsed = parseColumnIdSafely(filtered.id);
234
+ expect(parsed && isFilteredPColumn(parsed)).toBe(true);
235
+ if (parsed && isFilteredPColumn(parsed)) {
236
+ expect(parsed.source).toBe(inner.id);
237
+ expect(parsed.axisFilters).toEqual([[0, "v1"]]);
238
+ }
239
+ });
240
+
241
+ test("wrap on already-Filtered → flat concat axisFilters", () => {
242
+ // Three axes so two stacked filters (axes 0, 1) leave axis 2.
243
+ const inner = stubInner({
244
+ spec: stubSpec([
245
+ { name: "a", type: "String" },
246
+ { name: "b", type: "String" },
247
+ { name: "c", type: "String" },
248
+ ]),
249
+ });
250
+ const a = ColumnFilteredRecipe.wrap(inner, [[0, "v1"]]);
251
+ const b = ColumnFilteredRecipe.wrap(a, [[1, "v2"]]);
252
+ const parsed = parseColumnIdSafely(b.id);
253
+ expect(parsed && isFilteredPColumn(parsed)).toBe(true);
254
+ if (parsed && isFilteredPColumn(parsed)) {
255
+ expect(parsed.source).toBe(inner.id);
256
+ expect(parsed.axisFilters).toEqual([
257
+ [0, "v1"],
258
+ [1, "v2"],
259
+ ]);
260
+ }
261
+ });
262
+
263
+ test("getSpec projects axes — drops by indices from axisFilters", () => {
264
+ const inner = stubInner({
265
+ spec: stubSpec([
266
+ { name: "sample", type: "String" },
267
+ { name: "gene", type: "String" },
268
+ { name: "rep", type: "Int" },
269
+ ]),
270
+ });
271
+ const filtered = ColumnFilteredRecipe.wrap(inner, [
272
+ [0, "S1"],
273
+ [2, 3],
274
+ ]);
275
+ expect(filtered.getSpec().axesSpec).toEqual([{ name: "gene", type: "String" }]);
276
+ });
277
+
278
+ test("wrap throws when no axis filter is provided", () => {
279
+ // The no-op `wrap(inner, [])` form is rejected at construction: a
280
+ // Filtered without filters has no projection over the inner and would
281
+ // collapse to identity in the wrapper chain.
282
+ const inner = stubInner({
283
+ spec: stubSpec([{ name: "a", type: "String" }]),
284
+ query: leafQuery,
285
+ });
286
+ expect(() => ColumnFilteredRecipe.wrap(inner, [])).toThrow(
287
+ /at least one axis filter must be provided/,
288
+ );
289
+ });
290
+
291
+ test("wrap throws when filters fix every axis", () => {
292
+ // A filter set that fixes every axis would leave a 0-axis projection —
293
+ // not a useful column. Construction-time validation rejects it.
294
+ const inner = stubInner({
295
+ spec: stubSpec([{ name: "a", type: "String" }]),
296
+ query: leafQuery,
297
+ });
298
+ expect(() => ColumnFilteredRecipe.wrap(inner, [[0, "v"]])).toThrow(
299
+ /at least one axis must remain/,
300
+ );
301
+ });
302
+
303
+ test("getQuery builds SpecQuerySliceAxes with correct selectors derived from indices", () => {
304
+ const inner = stubInner({
305
+ spec: stubSpec([
306
+ { name: "sample", type: "String", domain: { kind: "patient" } },
307
+ { name: "gene", type: "String" },
308
+ ]),
309
+ query: leafQuery,
310
+ });
311
+ const filtered = ColumnFilteredRecipe.wrap(inner, [[0, "S1"]]);
312
+ const q = filtered.getQuery() as { type: string; input: SpecQuery; axisFilters: unknown[] };
313
+ expect(q.type).toBe("sliceAxes");
314
+ // Inner leaf gets rebranded to the Filtered id so the engine sees a
315
+ // distinct column for each axis-slice variant of the same physical hit.
316
+ expect(q.input).toEqual({ type: "column", column: filtered.id });
317
+ expect(q.axisFilters).toEqual([
318
+ {
319
+ axisSelector: { name: "sample", type: "String", domain: { kind: "patient" } },
320
+ constant: "S1",
321
+ },
322
+ ]);
323
+ });
324
+
325
+ test("getQuery carries contextDomain to disambiguate same name+type+domain axes", () => {
326
+ const inner = stubInner({
327
+ spec: stubSpec([
328
+ { name: "g", type: "String", domain: { d: "x" }, contextDomain: { block: "A" } },
329
+ { name: "g", type: "String", domain: { d: "x" }, contextDomain: { block: "B" } },
330
+ ]),
331
+ query: leafQuery,
332
+ });
333
+ const filtered = ColumnFilteredRecipe.wrap(inner, [[1, "v"]]);
334
+ const q = filtered.getQuery() as {
335
+ axisFilters: { axisSelector: Record<string, unknown> }[];
336
+ };
337
+ // Without contextDomain the selector { name:"g", type:"String", domain:{d:"x"} }
338
+ // matches BOTH axes — the engine's findAxis then errors on the ambiguity.
339
+ expect(q.axisFilters[0].axisSelector).toEqual({
340
+ name: "g",
341
+ type: "String",
342
+ domain: { d: "x" },
343
+ contextDomain: { block: "B" },
344
+ });
345
+ });
346
+
347
+ test("wrap throws on out-of-range axis index (validated at construction)", () => {
348
+ const inner = stubInner({
349
+ spec: stubSpec([{ name: "a", type: "String" }]),
350
+ query: leafQuery,
351
+ });
352
+ expect(() => ColumnFilteredRecipe.wrap(inner, [[5, "v"]])).toThrow(/out of range/);
353
+ });
354
+
355
+ test("withSpecs → Overridden<Filtered<inner>> (Overridden on the outside)", () => {
356
+ const inner = stubInner({
357
+ spec: stubSpec([
358
+ { name: "a", type: "String" },
359
+ { name: "b", type: "String" },
360
+ ]),
361
+ });
362
+ const filtered = ColumnFilteredRecipe.wrap(inner, [[0, "v"]]);
363
+ const layered = filtered.withSpecs(overrides({ annotations: { a: "1" } }));
364
+
365
+ const parsedOuter = parseColumnIdSafely(layered.id);
366
+ expect(parsedOuter && isColumnOverriddenKey(parsedOuter)).toBe(true);
367
+ if (parsedOuter && isColumnOverriddenKey(parsedOuter)) {
368
+ const parsedInner = parseColumnIdSafely(parsedOuter.source);
369
+ expect(parsedInner && isFilteredPColumn(parsedInner)).toBe(true);
370
+ }
371
+ });
372
+
373
+ test("getReferencedIds / getDataStatus forward to inner", () => {
374
+ const refs = [baseLeaf, linkerId];
375
+ const inner = stubInner({
376
+ refs,
377
+ status: "absent",
378
+ spec: stubSpec([
379
+ { name: "a", type: "String" },
380
+ { name: "b", type: "String" },
381
+ ]),
382
+ });
383
+ const filtered = ColumnFilteredRecipe.wrap(inner, [[0, "v"]]);
384
+ expect(filtered.getReferencedIds()).toEqual(refs);
385
+ expect(filtered.getDataStatus()).toBe("absent");
386
+ });
387
+ });
388
+
389
+ // ════════════════════════════════════════════════════════════════════════════
390
+ // ColumnDiscoveredRecipe (id / refs / transformations / getSpec pass-through).
391
+ // getQuery is covered separately; getSpec no longer needs the engine.
392
+ // ════════════════════════════════════════════════════════════════════════════
393
+
394
+ describe("ColumnDiscoveredRecipe", () => {
395
+ const trivialKey: ColumnDiscoveredKey = createColumnDiscoveredKey({ column: baseLeaf });
396
+
397
+ test("fromKey throws on an empty column id (unrecognized id variant)", () => {
398
+ // Empty string isn't a valid `ColumnUniversalId` — `parseColumnIdSafely`
399
+ // returns undefined and the dispatcher falls through to the "unsupported
400
+ // variant" throw rather than silently returning undefined.
401
+ expect(() =>
402
+ ColumnDiscoveredRecipe.fromKey(createColumnDiscoveredKey({ column: "" as PObjectId })),
403
+ ).toThrow(/Unsupported ColumnRecipe id variant/);
404
+ });
405
+
406
+ test("fromKey propagates ColumnAbsentError from a referenced absent column", () => {
407
+ // Default ctx mock has no providers → empty registry → leaf id is provably
408
+ // absent. The leaf-level throw inside `Column(uniId, opts)` propagates
409
+ // unchanged through `fromKey`.
410
+ expect(() => ColumnDiscoveredRecipe.fromKey(trivialKey)).toThrow(ColumnAbsentError);
411
+ });
412
+
413
+ test("id is the canonical stringify(ColumnDiscoveredKey)", () => {
414
+ setupCtx({ [baseLeaf]: stubSpec() });
415
+ const r =
416
+ ColumnDiscoveredRecipe.fromKey(trivialKey) ?? throwError("fromKey returned undefined");
417
+ const parsed = parseColumnIdSafely(r.id);
418
+ expect(parsed && isColumnDiscoveredKey(parsed)).toBe(true);
419
+ if (parsed && isColumnDiscoveredKey(parsed)) {
420
+ expect(parsed.column).toBe(baseLeaf);
421
+ }
422
+ });
423
+
424
+ test("getHitId returns key.column", () => {
425
+ setupCtx({ [baseLeaf]: stubSpec() });
426
+ const r =
427
+ ColumnDiscoveredRecipe.fromKey(trivialKey) ?? throwError("fromKey returned undefined");
428
+ expect(r.getHitId()).toBe(baseLeaf);
429
+ });
430
+
431
+ test("getReferencedIds: hit + path linkers, deduplicated", () => {
432
+ const linker2 = createGlobalPObjectId("blk", "L2");
433
+ setupCtx({ [baseLeaf]: stubSpec(), [linkerId]: stubSpec(), [linker2]: stubSpec() });
434
+ const r =
435
+ ColumnDiscoveredRecipe.fromKey(
436
+ createColumnDiscoveredKey({
437
+ column: baseLeaf,
438
+ path: [
439
+ { type: "linker", column: linkerId },
440
+ { type: "linker", column: linker2 },
441
+ { type: "linker", column: linkerId }, // duplicate
442
+ ],
443
+ }),
444
+ ) ?? throwError("fromKey returned undefined");
445
+ const refs = r.getReferencedIds().sort();
446
+ expect(refs).toEqual([baseLeaf, linkerId, linker2].sort());
447
+ });
448
+
449
+ test("getSpec is the hit column's own spec, unaffected by the linker path", () => {
450
+ const linker2 = createGlobalPObjectId("blk", "L2");
451
+ const hitSpec = stubSpec([
452
+ { name: "group", type: "String" },
453
+ { name: "name", type: "String" },
454
+ ]);
455
+ setupCtx({ [baseLeaf]: hitSpec, [linkerId]: stubSpec(), [linker2]: stubSpec() });
456
+ const r =
457
+ ColumnDiscoveredRecipe.fromKey(
458
+ createColumnDiscoveredKey({
459
+ column: baseLeaf,
460
+ path: [
461
+ { type: "linker", column: linkerId },
462
+ { type: "linker", column: linker2 },
463
+ ],
464
+ }),
465
+ ) ?? throwError("fromKey returned undefined");
466
+ // Pass-through: the linker chain enables co-indexing but does not remap
467
+ // the hit's axesSpec, so getSpec === the hit recipe's spec.
468
+ expect(r.getSpec().axesSpec).toEqual(hitSpec.axesSpec);
469
+ });
470
+
471
+ test("withSpecs → Overridden<Discovered>, flat-merge on repeat", () => {
472
+ setupCtx({ [baseLeaf]: stubSpec() });
473
+ const r =
474
+ ColumnDiscoveredRecipe.fromKey(trivialKey) ?? throwError("fromKey returned undefined");
475
+ const a = r.withSpecs(overrides({ annotations: { x: "1" } }));
476
+ const b = a.withSpecs(overrides({ annotations: { y: "2" } }));
477
+
478
+ const parsed = parseColumnOverriddenId(b.id);
479
+ expect(parsed.specOverrides.annotations).toEqual({ x: "1", y: "2" });
480
+
481
+ // source must be Discovered, not Overridden
482
+ const inner = parseColumnIdSafely(parsed.source);
483
+ expect(inner && isColumnDiscoveredKey(inner)).toBe(true);
484
+ });
485
+ });
486
+
487
+ // ════════════════════════════════════════════════════════════════════════════
488
+ // Static resolution status (getStatusByKey + ColumnRecipe.getStatus dispatcher)
489
+ // — verifies the new contract: status answered before construction; worst-wins
490
+ // aggregation; absent throw propagates through recursive id resolution.
491
+ // ════════════════════════════════════════════════════════════════════════════
492
+
493
+ describe("ColumnDiscoveredRecipe.getStatusByKey (worst-wins across references)", () => {
494
+ const hit = createLocalPObjectId(["main", "out"], "hit");
495
+ const linker = createLocalPObjectId(["main"], "linker");
496
+
497
+ test("present when hit and every linker are present", () => {
498
+ setupCtx({ [hit]: stubSpec(), [linker]: stubSpec() });
499
+ expect(
500
+ ColumnDiscoveredRecipe.getStatusByKey(
501
+ createColumnDiscoveredKey({
502
+ column: hit,
503
+ path: [{ type: "linker", column: linker }],
504
+ }),
505
+ ),
506
+ ).toBe("present");
507
+ });
508
+
509
+ test("resolving when any reference is still resolving (hit present, linker resolving)", () => {
510
+ const ctx = makeCtx();
511
+ (globalThis as { cfgRenderCtx?: unknown }).cfgRenderCtx = ctx;
512
+ installStubRegistry(ctx, {
513
+ [hit]: stubSpec(),
514
+ [linker]: { accessorLocked: false }, // resolving
515
+ });
516
+ expect(
517
+ ColumnDiscoveredRecipe.getStatusByKey(
518
+ createColumnDiscoveredKey({
519
+ column: hit,
520
+ path: [{ type: "linker", column: linker }],
521
+ }),
522
+ ),
523
+ ).toBe("resolving");
524
+ });
525
+
526
+ test("absent when any reference is provably absent (hit present, linker absent)", () => {
527
+ const ctx = makeCtx();
528
+ (globalThis as { cfgRenderCtx?: unknown }).cfgRenderCtx = ctx;
529
+ installStubRegistry(ctx, {
530
+ [hit]: stubSpec(),
531
+ [linker]: { accessorLocked: true }, // absent
532
+ });
533
+ expect(
534
+ ColumnDiscoveredRecipe.getStatusByKey(
535
+ createColumnDiscoveredKey({
536
+ column: hit,
537
+ path: [{ type: "linker", column: linker }],
538
+ }),
539
+ ),
540
+ ).toBe("absent");
541
+ });
542
+ });
543
+
544
+ describe("Wrapper getStatusByKey delegates to source", () => {
545
+ test("ColumnFilteredRecipe.getStatusByKey returns the source's status", () => {
546
+ setupCtx({ [baseLeaf]: stubSpec([{ name: "axis", type: "String" }]) });
547
+ expect(
548
+ ColumnFilteredRecipe.getStatusByKey(
549
+ createColumnFilteredKey({ source: baseLeaf, axisFilters: [] }),
550
+ ),
551
+ ).toBe("present");
552
+
553
+ // Now make the source absent — wrapper status follows.
554
+ const ctx = makeCtx();
555
+ (globalThis as { cfgRenderCtx?: unknown }).cfgRenderCtx = ctx;
556
+ installStubRegistry(ctx, { [baseLeaf]: { accessorLocked: true } });
557
+ expect(
558
+ ColumnFilteredRecipe.getStatusByKey(
559
+ createColumnFilteredKey({ source: baseLeaf, axisFilters: [] }),
560
+ ),
561
+ ).toBe("absent");
562
+ });
563
+
564
+ test("ColumnOverriddenRecipe.getStatusByKey returns the source's status", () => {
565
+ setupCtx({ [baseLeaf]: stubSpec() });
566
+ expect(
567
+ ColumnOverriddenRecipe.getStatusByKey(
568
+ createColumnOverriddenKey({ source: baseLeaf, specOverrides: overrides({}) }),
569
+ ),
570
+ ).toBe("present");
571
+
572
+ const ctx = makeCtx();
573
+ (globalThis as { cfgRenderCtx?: unknown }).cfgRenderCtx = ctx;
574
+ installStubRegistry(ctx, {}, { isFinal: false });
575
+ expect(
576
+ ColumnOverriddenRecipe.getStatusByKey(
577
+ createColumnOverriddenKey({ source: baseLeaf, specOverrides: overrides({}) }),
578
+ ),
579
+ ).toBe("resolving");
580
+ });
581
+ });
582
+
583
+ describe("ColumnRecipe.getStatus (top-level dispatcher)", () => {
584
+ test("routes bare PObjectId to ColumnLazyImpl.getStatusById", () => {
585
+ setupCtx({ [baseLeaf]: stubSpec() });
586
+ expect(ColumnRecipe.getStatus(baseLeaf)).toBe("present");
587
+ });
588
+
589
+ test("routes ColumnDiscoveredKey id to ColumnDiscoveredRecipe.getStatusByKey", () => {
590
+ setupCtx({ [baseLeaf]: stubSpec() });
591
+ const id = stringifyColumnDiscoveredId({ column: baseLeaf });
592
+ expect(ColumnRecipe.getStatus(id)).toBe("present");
593
+ });
594
+
595
+ test("routes ColumnFilteredKey id to ColumnFilteredRecipe.getStatusByKey", () => {
596
+ setupCtx({ [baseLeaf]: stubSpec([{ name: "axis", type: "String" }]) });
597
+ const id = stringifyColumnFilteredId({
598
+ source: baseLeaf,
599
+ axisFilters: [],
600
+ });
601
+ expect(ColumnRecipe.getStatus(id)).toBe("present");
602
+ });
603
+
604
+ test("routes ColumnOverriddenKey id to ColumnOverriddenRecipe.getStatusByKey", () => {
605
+ setupCtx({ [baseLeaf]: stubSpec() });
606
+ const id = createColumnOverriddenId({
607
+ source: baseLeaf,
608
+ specOverrides: overrides({}),
609
+ });
610
+ expect(ColumnRecipe.getStatus(id)).toBe("present");
611
+ });
612
+ });
613
+
614
+ describe("ColumnRecipe(id) throw propagation on absent leaves", () => {
615
+ test("filtered id over an absent source throws ColumnAbsentError", () => {
616
+ // Empty registry with isFinal=true → leaf is provably absent → throw
617
+ // propagates through the Filtered dispatch branch.
618
+ const ctx = makeCtx();
619
+ (globalThis as { cfgRenderCtx?: unknown }).cfgRenderCtx = ctx;
620
+ installStubRegistry(ctx, {}, { isFinal: true });
621
+ const id = stringifyColumnFilteredId({
622
+ source: baseLeaf,
623
+ axisFilters: [],
624
+ });
625
+ expect(() => ColumnRecipe(id)).toThrow(ColumnAbsentError);
626
+ });
627
+
628
+ test("overrided id over an absent source throws ColumnAbsentError", () => {
629
+ const ctx = makeCtx();
630
+ (globalThis as { cfgRenderCtx?: unknown }).cfgRenderCtx = ctx;
631
+ installStubRegistry(ctx, {}, { isFinal: true });
632
+ const id = createColumnOverriddenId({
633
+ source: baseLeaf,
634
+ specOverrides: overrides({}),
635
+ });
636
+ expect(() => ColumnRecipe(id)).toThrow(ColumnAbsentError);
637
+ });
638
+
639
+ test("discovered id over an absent hit throws ColumnAbsentError", () => {
640
+ const ctx = makeCtx();
641
+ (globalThis as { cfgRenderCtx?: unknown }).cfgRenderCtx = ctx;
642
+ installStubRegistry(ctx, {}, { isFinal: true });
643
+ const id = stringifyColumnDiscoveredId({ column: baseLeaf });
644
+ expect(() => ColumnRecipe(id)).toThrow(ColumnAbsentError);
645
+ });
646
+ });
647
+
648
+ describe("ColumnDiscoveredRecipe.getQuery (local assembly)", () => {
649
+ test("trivial (no path) → leaf column query with the Discovered id", () => {
650
+ // `buildSpecQuery` no longer delegates to `pframeSpec.buildQuery`; it
651
+ // assembles the SpecQuery locally and rebrands the hit leaf to `this.id`
652
+ // so sibling variants of the same physical hit stay distinguishable.
653
+ const ctx = makeCtx();
654
+ (globalThis as { cfgRenderCtx?: unknown }).cfgRenderCtx = ctx;
655
+ installStubRegistry(ctx, { [baseLeaf]: stubSpec() });
656
+
657
+ const r =
658
+ ColumnDiscoveredRecipe.fromKey(createColumnDiscoveredKey({ column: baseLeaf })) ??
659
+ throwError("fromKey returned undefined");
660
+ expect(r.getQuery()).toEqual({ type: "column", column: r.id });
661
+ });
662
+
663
+ test("with linker path → linkerJoin nesting, hit leaf rebranded to the Discovered id", () => {
664
+ const ctx = makeCtx();
665
+ (globalThis as { cfgRenderCtx?: unknown }).cfgRenderCtx = ctx;
666
+ installStubRegistry(ctx, { [baseLeaf]: stubSpec(), [linkerId]: stubSpec() });
667
+
668
+ const r =
669
+ ColumnDiscoveredRecipe.fromKey(
670
+ createColumnDiscoveredKey({
671
+ column: baseLeaf,
672
+ path: [{ type: "linker", column: linkerId }],
673
+ }),
674
+ ) ?? throwError("fromKey returned undefined");
675
+ expect(r.getQuery()).toEqual({
676
+ type: "linkerJoin",
677
+ linker: { type: "column", column: linkerId },
678
+ secondary: [{ entry: { type: "column", column: r.id } }],
679
+ });
680
+ });
681
+ });