@finos/legend-cube-builder 0.0.5 → 0.0.6

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 (245) hide show
  1. package/docs/adding-an-operation.md +4 -2
  2. package/docs/demo/README.md +1 -1
  3. package/docs/hosting.md +40 -9
  4. package/lib/__lib__/LegendCubeHelpText.d.ts.map +1 -1
  5. package/lib/__lib__/LegendCubeHelpText.js +3 -1
  6. package/lib/__lib__/LegendCubeHelpText.js.map +1 -1
  7. package/lib/__lib__/LegendCubeLabels.d.ts +34 -5
  8. package/lib/__lib__/LegendCubeLabels.d.ts.map +1 -1
  9. package/lib/__lib__/LegendCubeLabels.js +45 -8
  10. package/lib/__lib__/LegendCubeLabels.js.map +1 -1
  11. package/lib/__lib__/LegendCubeNavigation.d.ts +23 -0
  12. package/lib/__lib__/LegendCubeNavigation.d.ts.map +1 -0
  13. package/lib/__lib__/LegendCubeNavigation.js +33 -0
  14. package/lib/__lib__/LegendCubeNavigation.js.map +1 -0
  15. package/lib/__lib__/LegendCubeTesting.d.ts +3 -1
  16. package/lib/__lib__/LegendCubeTesting.d.ts.map +1 -1
  17. package/lib/__lib__/LegendCubeTesting.js +2 -0
  18. package/lib/__lib__/LegendCubeTesting.js.map +1 -1
  19. package/lib/__lib__/LegendCubeTour.d.ts +62 -0
  20. package/lib/__lib__/LegendCubeTour.d.ts.map +1 -0
  21. package/lib/__lib__/LegendCubeTour.js +149 -0
  22. package/lib/__lib__/LegendCubeTour.js.map +1 -0
  23. package/lib/__test-utils__/CubeExampleTestUtils.d.ts +34 -0
  24. package/lib/__test-utils__/CubeExampleTestUtils.d.ts.map +1 -0
  25. package/lib/__test-utils__/CubeExampleTestUtils.js +72 -0
  26. package/lib/__test-utils__/CubeExampleTestUtils.js.map +1 -0
  27. package/lib/__test-utils__/CubeVariantTestUtils.d.ts +16 -0
  28. package/lib/__test-utils__/CubeVariantTestUtils.d.ts.map +1 -0
  29. package/lib/__test-utils__/CubeVariantTestUtils.js +66 -0
  30. package/lib/__test-utils__/CubeVariantTestUtils.js.map +1 -0
  31. package/lib/components/CubeAddItems.d.ts.map +1 -1
  32. package/lib/components/CubeAddItems.js +2 -1
  33. package/lib/components/CubeAddItems.js.map +1 -1
  34. package/lib/components/CubeEditor.d.ts.map +1 -1
  35. package/lib/components/CubeEditor.js +11 -5
  36. package/lib/components/CubeEditor.js.map +1 -1
  37. package/lib/components/CubeFileMenu.d.ts.map +1 -1
  38. package/lib/components/CubeFileMenu.js +2 -1
  39. package/lib/components/CubeFileMenu.js.map +1 -1
  40. package/lib/components/CubeNodeIcon.d.ts.map +1 -1
  41. package/lib/components/CubeNodeIcon.js +2 -1
  42. package/lib/components/CubeNodeIcon.js.map +1 -1
  43. package/lib/components/canvas/CubeCanvas.d.ts.map +1 -1
  44. package/lib/components/canvas/CubeCanvas.js +3 -2
  45. package/lib/components/canvas/CubeCanvas.js.map +1 -1
  46. package/lib/components/canvas/CubeCanvasNode.d.ts.map +1 -1
  47. package/lib/components/canvas/CubeCanvasNode.js +4 -1
  48. package/lib/components/canvas/CubeCanvasNode.js.map +1 -1
  49. package/lib/components/dev-settings/CubeDevSettings.d.ts +2 -2
  50. package/lib/components/dev-settings/CubeDevSettings.d.ts.map +1 -1
  51. package/lib/components/dev-settings/CubeDevSettings.js +12 -8
  52. package/lib/components/dev-settings/CubeDevSettings.js.map +1 -1
  53. package/lib/components/editors/CubeColumnPicker.d.ts.map +1 -1
  54. package/lib/components/editors/CubeColumnPicker.js +3 -1
  55. package/lib/components/editors/CubeColumnPicker.js.map +1 -1
  56. package/lib/components/editors/CubeExplodeEditor.d.ts +26 -0
  57. package/lib/components/editors/CubeExplodeEditor.d.ts.map +1 -0
  58. package/lib/components/editors/CubeExplodeEditor.js +61 -0
  59. package/lib/components/editors/CubeExplodeEditor.js.map +1 -0
  60. package/lib/components/editors/CubeExpressionFilterEditor.d.ts.map +1 -1
  61. package/lib/components/editors/CubeExpressionFilterEditor.js +8 -5
  62. package/lib/components/editors/CubeExpressionFilterEditor.js.map +1 -1
  63. package/lib/components/editors/CubeExtendEditor.d.ts +0 -2
  64. package/lib/components/editors/CubeExtendEditor.d.ts.map +1 -1
  65. package/lib/components/editors/CubeExtendEditor.js +12 -11
  66. package/lib/components/editors/CubeExtendEditor.js.map +1 -1
  67. package/lib/components/editors/CubeJsonFieldsTree.d.ts +46 -0
  68. package/lib/components/editors/CubeJsonFieldsTree.d.ts.map +1 -0
  69. package/lib/components/editors/CubeJsonFieldsTree.js +93 -0
  70. package/lib/components/editors/CubeJsonFieldsTree.js.map +1 -0
  71. package/lib/components/editors/CubeNodeEditorRegistry.d.ts.map +1 -1
  72. package/lib/components/editors/CubeNodeEditorRegistry.js +3 -1
  73. package/lib/components/editors/CubeNodeEditorRegistry.js.map +1 -1
  74. package/lib/components/grid/CubeGridRegion.d.ts.map +1 -1
  75. package/lib/components/grid/CubeGridRegion.js +5 -4
  76. package/lib/components/grid/CubeGridRegion.js.map +1 -1
  77. package/lib/components/grid/CubeGridValues.d.ts +5 -3
  78. package/lib/components/grid/CubeGridValues.d.ts.map +1 -1
  79. package/lib/components/grid/CubeGridValues.js +8 -3
  80. package/lib/components/grid/CubeGridValues.js.map +1 -1
  81. package/lib/components/grid/CubeJsonExplorerDialog.d.ts +27 -0
  82. package/lib/components/grid/CubeJsonExplorerDialog.d.ts.map +1 -0
  83. package/lib/components/grid/CubeJsonExplorerDialog.js +102 -0
  84. package/lib/components/grid/CubeJsonExplorerDialog.js.map +1 -0
  85. package/lib/components/grid/CubeJsonViewerDialog.d.ts +27 -0
  86. package/lib/components/grid/CubeJsonViewerDialog.d.ts.map +1 -0
  87. package/lib/components/grid/CubeJsonViewerDialog.js +68 -0
  88. package/lib/components/grid/CubeJsonViewerDialog.js.map +1 -0
  89. package/lib/components/grid/CubeResultGrid.d.ts +15 -3
  90. package/lib/components/grid/CubeResultGrid.d.ts.map +1 -1
  91. package/lib/components/grid/CubeResultGrid.js +95 -3
  92. package/lib/components/grid/CubeResultGrid.js.map +1 -1
  93. package/lib/components/grid/CubeServerSideGrid.d.ts.map +1 -1
  94. package/lib/components/grid/CubeServerSideGrid.js +4 -2
  95. package/lib/components/grid/CubeServerSideGrid.js.map +1 -1
  96. package/lib/components/palette/CubePalette.d.ts.map +1 -1
  97. package/lib/components/palette/CubePalette.js +4 -1
  98. package/lib/components/palette/CubePalette.js.map +1 -1
  99. package/lib/components/source-picker/CubeIngestTab.d.ts +2 -2
  100. package/lib/components/source-picker/CubeIngestTab.d.ts.map +1 -1
  101. package/lib/components/source-picker/CubeIngestTab.js +19 -13
  102. package/lib/components/source-picker/CubeIngestTab.js.map +1 -1
  103. package/lib/components/tour/CubeTour.d.ts +36 -0
  104. package/lib/components/tour/CubeTour.d.ts.map +1 -0
  105. package/lib/components/tour/CubeTour.js +183 -0
  106. package/lib/components/tour/CubeTour.js.map +1 -0
  107. package/lib/index.css +1 -1
  108. package/lib/index.d.ts +1 -0
  109. package/lib/index.d.ts.map +1 -1
  110. package/lib/index.js +1 -0
  111. package/lib/index.js.map +1 -1
  112. package/lib/stores/CubeEditorState.d.ts +17 -1
  113. package/lib/stores/CubeEditorState.d.ts.map +1 -1
  114. package/lib/stores/CubeEditorState.js +35 -5
  115. package/lib/stores/CubeEditorState.js.map +1 -1
  116. package/lib/stores/CubeExamples.d.ts.map +1 -1
  117. package/lib/stores/CubeExamples.js +101 -4
  118. package/lib/stores/CubeExamples.js.map +1 -1
  119. package/lib/stores/CubeExamplesState.d.ts.map +1 -1
  120. package/lib/stores/CubeExamplesState.js +4 -1
  121. package/lib/stores/CubeExamplesState.js.map +1 -1
  122. package/lib/stores/CubeGridQuickActions.d.ts +7 -0
  123. package/lib/stores/CubeGridQuickActions.d.ts.map +1 -1
  124. package/lib/stores/CubeGridQuickActions.js +20 -11
  125. package/lib/stores/CubeGridQuickActions.js.map +1 -1
  126. package/lib/stores/CubePureText.d.ts +20 -0
  127. package/lib/stores/CubePureText.d.ts.map +1 -0
  128. package/lib/stores/CubePureText.js +24 -0
  129. package/lib/stores/CubePureText.js.map +1 -0
  130. package/lib/stores/CubeTourState.d.ts +49 -0
  131. package/lib/stores/CubeTourState.d.ts.map +1 -0
  132. package/lib/stores/CubeTourState.js +92 -0
  133. package/lib/stores/CubeTourState.js.map +1 -0
  134. package/lib/stores/LegendCubeHost.d.ts +88 -0
  135. package/lib/stores/LegendCubeHost.d.ts.map +1 -0
  136. package/lib/stores/LegendCubeHost.js +149 -0
  137. package/lib/stores/LegendCubeHost.js.map +1 -0
  138. package/lib/stores/editors/CubeExplodeDraft.d.ts +87 -0
  139. package/lib/stores/editors/CubeExplodeDraft.d.ts.map +1 -0
  140. package/lib/stores/editors/CubeExplodeDraft.js +221 -0
  141. package/lib/stores/editors/CubeExplodeDraft.js.map +1 -0
  142. package/lib/stores/editors/CubeExpressionFilterDraft.d.ts +3 -0
  143. package/lib/stores/editors/CubeExpressionFilterDraft.d.ts.map +1 -1
  144. package/lib/stores/editors/CubeExpressionFilterDraft.js +4 -0
  145. package/lib/stores/editors/CubeExpressionFilterDraft.js.map +1 -1
  146. package/lib/stores/editors/CubeExtendDraft.d.ts +9 -0
  147. package/lib/stores/editors/CubeExtendDraft.d.ts.map +1 -1
  148. package/lib/stores/editors/CubeExtendDraft.js +26 -0
  149. package/lib/stores/editors/CubeExtendDraft.js.map +1 -1
  150. package/lib/stores/editors/CubeNodeDraftRegistry.d.ts.map +1 -1
  151. package/lib/stores/editors/CubeNodeDraftRegistry.js +6 -1
  152. package/lib/stores/editors/CubeNodeDraftRegistry.js.map +1 -1
  153. package/lib/stores/fixtures/CubeSalesDuckDbModel.d.ts +1 -0
  154. package/lib/stores/fixtures/CubeSalesDuckDbModel.d.ts.map +1 -1
  155. package/lib/stores/fixtures/CubeSalesDuckDbModel.js +8 -2
  156. package/lib/stores/fixtures/CubeSalesDuckDbModel.js.map +1 -1
  157. package/lib/stores/source-picker/CubeIngestTabState.d.ts +48 -14
  158. package/lib/stores/source-picker/CubeIngestTabState.d.ts.map +1 -1
  159. package/lib/stores/source-picker/CubeIngestTabState.js +159 -62
  160. package/lib/stores/source-picker/CubeIngestTabState.js.map +1 -1
  161. package/lib/stores/variant/CubeJson.d.ts +48 -0
  162. package/lib/stores/variant/CubeJson.d.ts.map +1 -0
  163. package/lib/stores/variant/CubeJson.js +207 -0
  164. package/lib/stores/variant/CubeJson.js.map +1 -0
  165. package/lib/stores/variant/CubeJsonExplorerState.d.ts +88 -0
  166. package/lib/stores/variant/CubeJsonExplorerState.d.ts.map +1 -0
  167. package/lib/stores/variant/CubeJsonExplorerState.js +297 -0
  168. package/lib/stores/variant/CubeJsonExplorerState.js.map +1 -0
  169. package/lib/stores/variant/CubeJsonFieldsState.d.ts +83 -0
  170. package/lib/stores/variant/CubeJsonFieldsState.d.ts.map +1 -0
  171. package/lib/stores/variant/CubeJsonFieldsState.js +207 -0
  172. package/lib/stores/variant/CubeJsonFieldsState.js.map +1 -0
  173. package/lib/stores/variant/CubeJsonOffers.d.ts +76 -0
  174. package/lib/stores/variant/CubeJsonOffers.d.ts.map +1 -0
  175. package/lib/stores/variant/CubeJsonOffers.js +250 -0
  176. package/lib/stores/variant/CubeJsonOffers.js.map +1 -0
  177. package/lib/stores/variant/CubeJsonParse.d.ts +28 -0
  178. package/lib/stores/variant/CubeJsonParse.d.ts.map +1 -0
  179. package/lib/stores/variant/CubeJsonParse.js +53 -0
  180. package/lib/stores/variant/CubeJsonParse.js.map +1 -0
  181. package/lib/stores/variant/CubeJsonSampling.d.ts +41 -0
  182. package/lib/stores/variant/CubeJsonSampling.d.ts.map +1 -0
  183. package/lib/stores/variant/CubeJsonSampling.js +59 -0
  184. package/lib/stores/variant/CubeJsonSampling.js.map +1 -0
  185. package/lib/stores/variant/CubeJsonShape.d.ts +87 -0
  186. package/lib/stores/variant/CubeJsonShape.d.ts.map +1 -0
  187. package/lib/stores/variant/CubeJsonShape.js +209 -0
  188. package/lib/stores/variant/CubeJsonShape.js.map +1 -0
  189. package/lib/stores/variant/CubeJsonViewerState.d.ts +33 -0
  190. package/lib/stores/variant/CubeJsonViewerState.d.ts.map +1 -0
  191. package/lib/stores/variant/CubeJsonViewerState.js +53 -0
  192. package/lib/stores/variant/CubeJsonViewerState.js.map +1 -0
  193. package/package.json +2 -2
  194. package/src/__lib__/LegendCubeHelpText.ts +4 -0
  195. package/src/__lib__/LegendCubeLabels.ts +60 -8
  196. package/src/__lib__/LegendCubeNavigation.ts +48 -0
  197. package/src/__lib__/LegendCubeTesting.ts +2 -0
  198. package/src/__lib__/LegendCubeTour.ts +173 -0
  199. package/src/__test-utils__/CubeExampleTestUtils.ts +119 -0
  200. package/src/__test-utils__/CubeVariantTestUtils.ts +103 -0
  201. package/src/components/CubeAddItems.tsx +2 -0
  202. package/src/components/CubeEditor.tsx +15 -0
  203. package/src/components/CubeFileMenu.tsx +2 -0
  204. package/src/components/CubeNodeIcon.tsx +2 -0
  205. package/src/components/canvas/CubeCanvas.tsx +17 -1
  206. package/src/components/canvas/CubeCanvasNode.tsx +7 -0
  207. package/src/components/dev-settings/CubeDevSettings.tsx +17 -2
  208. package/src/components/editors/CubeColumnPicker.tsx +3 -0
  209. package/src/components/editors/CubeExplodeEditor.tsx +207 -0
  210. package/src/components/editors/CubeExpressionFilterEditor.tsx +25 -8
  211. package/src/components/editors/CubeExtendEditor.tsx +32 -19
  212. package/src/components/editors/CubeJsonFieldsTree.tsx +316 -0
  213. package/src/components/editors/CubeNodeEditorRegistry.ts +3 -0
  214. package/src/components/grid/CubeGridRegion.tsx +21 -3
  215. package/src/components/grid/CubeGridValues.ts +8 -3
  216. package/src/components/grid/CubeJsonExplorerDialog.tsx +304 -0
  217. package/src/components/grid/CubeJsonViewerDialog.tsx +190 -0
  218. package/src/components/grid/CubeResultGrid.tsx +144 -2
  219. package/src/components/grid/CubeServerSideGrid.tsx +13 -0
  220. package/src/components/palette/CubePalette.tsx +9 -0
  221. package/src/components/source-picker/CubeIngestTab.tsx +62 -30
  222. package/src/components/tour/CubeTour.tsx +317 -0
  223. package/src/index.ts +7 -0
  224. package/src/stores/CubeEditorState.ts +36 -5
  225. package/src/stores/CubeExamples.ts +120 -2
  226. package/src/stores/CubeExamplesState.ts +9 -1
  227. package/src/stores/CubeGridQuickActions.ts +23 -12
  228. package/src/stores/CubePureText.ts +28 -0
  229. package/src/stores/CubeTourState.ts +116 -0
  230. package/src/stores/LegendCubeHost.ts +243 -0
  231. package/src/stores/editors/CubeExplodeDraft.ts +272 -0
  232. package/src/stores/editors/CubeExpressionFilterDraft.ts +4 -0
  233. package/src/stores/editors/CubeExtendDraft.ts +29 -0
  234. package/src/stores/editors/CubeNodeDraftRegistry.ts +7 -0
  235. package/src/stores/fixtures/CubeSalesDuckDbModel.ts +8 -2
  236. package/src/stores/source-picker/CubeIngestTabState.ts +253 -102
  237. package/src/stores/variant/CubeJson.ts +229 -0
  238. package/src/stores/variant/CubeJsonExplorerState.ts +378 -0
  239. package/src/stores/variant/CubeJsonFieldsState.ts +252 -0
  240. package/src/stores/variant/CubeJsonOffers.ts +359 -0
  241. package/src/stores/variant/CubeJsonParse.ts +75 -0
  242. package/src/stores/variant/CubeJsonSampling.ts +90 -0
  243. package/src/stores/variant/CubeJsonShape.ts +279 -0
  244. package/src/stores/variant/CubeJsonViewerState.ts +59 -0
  245. package/tsconfig.json +22 -1
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Copyright (c) 2026-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import {
18
+ ColumnComparisonFilter,
19
+ type CubeDocument,
20
+ Filter,
21
+ FilterOperator,
22
+ Restrict,
23
+ Schema,
24
+ type SchemaColumn,
25
+ } from '@finos/legend-cube';
26
+ import type { CubeEditorState } from '../CubeEditorState.js';
27
+ import { runCubeQuery } from '../CubeQueryRun.js';
28
+ import { type CubeJsonSample, inferCubeJsonShape } from './CubeJsonShape.js';
29
+
30
+ /** How many values Explore JSON reads first (user, 2026-10-14) */
31
+ export const CUBE_JSON_SAMPLE_SIZE = 1000;
32
+
33
+ /** No limit: Read every row (the run's `limit(rowLimit + 1)` then takes them all) */
34
+ const EVERY_ROW = Number.MAX_SAFE_INTEGER;
35
+
36
+ /** The runs that read a Variant column's documents, as Runs lists them */
37
+ export const CUBE_JSON_RUN_LABEL = {
38
+ sample: (column: string): string => `Sample of ${column}`,
39
+ everyRow: (column: string): string => `Every value of ${column}`,
40
+ };
41
+
42
+ /**
43
+ * Reads a Variant column's documents (PLAN §11.20) through the cube's own
44
+ * query path, so the read is listed in Runs and can be stopped: the query up
45
+ * to a node, then the column alone and its non-empty values, its first
46
+ * `CUBE_JSON_SAMPLE_SIZE` or every row. Explore JSON reads after the node
47
+ * that ran, the Explode Array editor after its input. `undefined` for a cube
48
+ * with no runtime; a failed or stopped run throws, as `runCubeQuery` does.
49
+ */
50
+ export const readCubeJsonSample = async (
51
+ editorState: CubeEditorState,
52
+ options: {
53
+ document: CubeDocument;
54
+ afterId: string;
55
+ column: SchemaColumn;
56
+ everyRow: boolean;
57
+ abortController: AbortController;
58
+ },
59
+ ): Promise<CubeJsonSample | undefined> => {
60
+ const { document, afterId, column, everyRow, abortController } = options;
61
+ const { context, query } = document;
62
+ if (context?.runtime === undefined) {
63
+ return undefined;
64
+ }
65
+ // each after the one before; the query isn't kept
66
+ const restrict = new Restrict(query.generateId(Restrict.TYPE), [column.name]);
67
+ const nonEmpty = new Filter(
68
+ query.generateId(Filter.TYPE),
69
+ new ColumnComparisonFilter(column.name, FilterOperator.IS_NOT_EMPTY),
70
+ );
71
+ const values = [restrict, nonEmpty].reduce(
72
+ (built, node) => built.add(node, built.selected),
73
+ query.selected === afterId ? query : query.select(afterId),
74
+ );
75
+ const result = await runCubeQuery(editorState, {
76
+ query: values,
77
+ model: context.model,
78
+ runtime: context.runtime,
79
+ schema: new Schema([column]),
80
+ rowLimit: everyRow ? EVERY_ROW : CUBE_JSON_SAMPLE_SIZE,
81
+ abortController,
82
+ label: everyRow
83
+ ? CUBE_JSON_RUN_LABEL.everyRow(column.name)
84
+ : CUBE_JSON_RUN_LABEL.sample(column.name),
85
+ });
86
+ return inferCubeJsonShape(
87
+ result.rows.map((row) => row[0] ?? null),
88
+ !result.limited,
89
+ );
90
+ };
@@ -0,0 +1,279 @@
1
+ /**
2
+ * Copyright (c) 2026-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import type { CubeResultValue } from '../../graph-manager/CubeEngine.js';
18
+ import { type CubeJsonNode, readCubeJson } from './CubeJson.js';
19
+
20
+ // What a Variant column's documents hold (PLAN §11.20), inferred from a
21
+ // sample of them, as legend-lite's Data Cube does: nothing in the model says,
22
+ // since the column is declared only SEMISTRUCTURED. The inferred type only
23
+ // picks the `to(@T)` an offer writes; the engine then types the column. A
24
+ // wrong pick isn't always an error, though: on Snowflake, DuckDB, Postgres
25
+ // and Databricks `to(@Integer)` of `1.5` returns a value, so the type is
26
+ // picked conservatively (`getCubeJsonScalarType`).
27
+
28
+ /** What a scalar looked like, which picks its `to(@T)` */
29
+ export enum CubeJsonScalarKind {
30
+ INTEGER = 'integer',
31
+ FLOAT = 'float',
32
+ BOOLEAN = 'boolean',
33
+ DATE = 'date',
34
+ DATETIME = 'datetime',
35
+ TEXT = 'text',
36
+ }
37
+
38
+ /** The Pure type each kind is read as */
39
+ export const CUBE_JSON_PURE_TYPE: Record<CubeJsonScalarKind, string> = {
40
+ [CubeJsonScalarKind.INTEGER]: 'Integer',
41
+ [CubeJsonScalarKind.FLOAT]: 'Float',
42
+ [CubeJsonScalarKind.BOOLEAN]: 'Boolean',
43
+ [CubeJsonScalarKind.DATE]: 'StrictDate',
44
+ [CubeJsonScalarKind.DATETIME]: 'DateTime',
45
+ [CubeJsonScalarKind.TEXT]: 'String',
46
+ };
47
+
48
+ /** A kind as the dialog names it */
49
+ export const CUBE_JSON_KIND_LABEL: Record<CubeJsonScalarKind, string> = {
50
+ [CubeJsonScalarKind.INTEGER]: 'whole number',
51
+ [CubeJsonScalarKind.FLOAT]: 'number',
52
+ [CubeJsonScalarKind.BOOLEAN]: 'true or false',
53
+ [CubeJsonScalarKind.DATE]: 'date',
54
+ [CubeJsonScalarKind.DATETIME]: 'date and time',
55
+ [CubeJsonScalarKind.TEXT]: 'text',
56
+ };
57
+
58
+ const DATE = /^\d{4}-\d{2}-\d{2}$/u;
59
+ const DATETIME =
60
+ /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?$/u;
61
+ const WHOLE_NUMBER = /^-?\d+$/u;
62
+ /** The largest whole number a 64-bit `to(@Integer)` holds */
63
+ const MAX_INTEGER = 9223372036854775807n;
64
+
65
+ /** At most this many distinct values of a position are counted */
66
+ const MAX_VALUES = 50;
67
+
68
+ const kindOf = (node: CubeJsonNode): CubeJsonScalarKind | undefined => {
69
+ switch (node.kind) {
70
+ case 'number':
71
+ // a whole number past 64 bits would overflow `to(@Integer)`: text
72
+ return WHOLE_NUMBER.test(node.text)
73
+ ? BigInt(node.text) <= MAX_INTEGER && BigInt(node.text) >= -MAX_INTEGER
74
+ ? CubeJsonScalarKind.INTEGER
75
+ : CubeJsonScalarKind.TEXT
76
+ : CubeJsonScalarKind.FLOAT;
77
+ case 'boolean':
78
+ return CubeJsonScalarKind.BOOLEAN;
79
+ case 'string':
80
+ return DATE.test(node.value)
81
+ ? CubeJsonScalarKind.DATE
82
+ : DATETIME.test(node.value)
83
+ ? CubeJsonScalarKind.DATETIME
84
+ : CubeJsonScalarKind.TEXT;
85
+ default:
86
+ return undefined;
87
+ }
88
+ };
89
+
90
+ /** A scalar's text, as it is shown and counted */
91
+ const textOf = (node: CubeJsonNode): string =>
92
+ node.kind === 'number'
93
+ ? node.text
94
+ : node.kind === 'string'
95
+ ? node.value
96
+ : node.kind === 'boolean'
97
+ ? String(node.value)
98
+ : '';
99
+
100
+ /** Everything seen at one position of the documents */
101
+ export interface CubeJsonShape {
102
+ /** Times the position was there, empty included */
103
+ present: number;
104
+ /** Times it held null */
105
+ nulls: number;
106
+ /** The kinds of scalar seen, and how often */
107
+ readonly scalars: Map<CubeJsonScalarKind, number>;
108
+ /** Distinct scalar values and how often, up to `MAX_VALUES` of them */
109
+ readonly values: Map<string, number>;
110
+ /** Times it held an object */
111
+ objects: number;
112
+ /** The objects' fields, in the order first seen */
113
+ readonly fields: Map<string, CubeJsonShape>;
114
+ /** Times it held an array */
115
+ arrays: number;
116
+ /** Every element of every array seen here */
117
+ element: CubeJsonShape | undefined;
118
+ minLength: number;
119
+ maxLength: number;
120
+ }
121
+
122
+ const emptyShape = (): CubeJsonShape => ({
123
+ present: 0,
124
+ nulls: 0,
125
+ scalars: new Map(),
126
+ values: new Map(),
127
+ objects: 0,
128
+ fields: new Map(),
129
+ arrays: 0,
130
+ element: undefined,
131
+ minLength: Infinity,
132
+ maxLength: 0,
133
+ });
134
+
135
+ const observe = (shape: CubeJsonShape, node: CubeJsonNode): void => {
136
+ shape.present += 1;
137
+ if (node.kind === 'null') {
138
+ shape.nulls += 1;
139
+ return;
140
+ }
141
+ if (node.kind === 'object') {
142
+ shape.objects += 1;
143
+ node.entries.forEach(([key, value]) => {
144
+ let field = shape.fields.get(key);
145
+ if (!field) {
146
+ field = emptyShape();
147
+ shape.fields.set(key, field);
148
+ }
149
+ observe(field, value);
150
+ });
151
+ return;
152
+ }
153
+ if (node.kind === 'array') {
154
+ shape.arrays += 1;
155
+ shape.minLength = Math.min(shape.minLength, node.items.length);
156
+ shape.maxLength = Math.max(shape.maxLength, node.items.length);
157
+ const element = shape.element ?? emptyShape();
158
+ shape.element = element;
159
+ node.items.forEach((item) => observe(element, item));
160
+ return;
161
+ }
162
+ const kind = kindOf(node);
163
+ if (kind) {
164
+ shape.scalars.set(kind, (shape.scalars.get(kind) ?? 0) + 1);
165
+ }
166
+ const text = textOf(node);
167
+ if (shape.values.has(text) || shape.values.size < MAX_VALUES) {
168
+ shape.values.set(text, (shape.values.get(text) ?? 0) + 1);
169
+ }
170
+ };
171
+
172
+ /** What a sample of a column showed */
173
+ export interface CubeJsonSample {
174
+ /** The shape of the column's documents */
175
+ readonly shape: CubeJsonShape;
176
+ /** Cells read, empty ones included */
177
+ readonly rows: number;
178
+ /** Cells that couldn't be read as JSON */
179
+ readonly unreadable: number;
180
+ /** Every row of the column was read: the shape is the whole column's */
181
+ readonly complete: boolean;
182
+ }
183
+
184
+ /** A column's shape, built as its cells arrive */
185
+ export class CubeJsonShapeReader {
186
+ private readonly shape = emptyShape();
187
+ private rows = 0;
188
+ private unreadable = 0;
189
+
190
+ /** A cell as the grid receives it: the document's JSON text, or null */
191
+ add(cell: CubeResultValue): void {
192
+ this.rows += 1;
193
+ if (cell === null) {
194
+ return;
195
+ }
196
+ const node = readCubeJson(String(cell));
197
+ if (node) {
198
+ observe(this.shape, node);
199
+ } else {
200
+ this.unreadable += 1;
201
+ }
202
+ }
203
+
204
+ result(complete: boolean): CubeJsonSample {
205
+ return {
206
+ shape: this.shape,
207
+ rows: this.rows,
208
+ unreadable: this.unreadable,
209
+ complete,
210
+ };
211
+ }
212
+ }
213
+
214
+ /** The shape of a sample of a column's cells */
215
+ export const inferCubeJsonShape = (
216
+ cells: readonly CubeResultValue[],
217
+ complete = false,
218
+ ): CubeJsonSample => {
219
+ const reader = new CubeJsonShapeReader();
220
+ cells.forEach((cell) => reader.add(cell));
221
+ return reader.result(complete);
222
+ };
223
+
224
+ /**
225
+ * The one scalar type a position is read as, picked so a wrong guess can't
226
+ * give wrong rows: one kind is that kind; whole numbers and fractions
227
+ * together are numbers (`Float`); any other mix, dates with date-times
228
+ * included, is text, which every value reads as. `undefined` when no scalar
229
+ * was seen.
230
+ */
231
+ export const getCubeJsonScalarType = (
232
+ shape: CubeJsonShape,
233
+ ): CubeJsonScalarKind | undefined => {
234
+ const kinds = [...shape.scalars.keys()];
235
+ if (kinds.length <= 1) {
236
+ return kinds[0];
237
+ }
238
+ return kinds.every(
239
+ (kind) =>
240
+ kind === CubeJsonScalarKind.INTEGER || kind === CubeJsonScalarKind.FLOAT,
241
+ )
242
+ ? CubeJsonScalarKind.FLOAT
243
+ : CubeJsonScalarKind.TEXT;
244
+ };
245
+
246
+ /** A position holds only scalars (and nulls): no object or array */
247
+ export const isCubeJsonScalarShape = (shape: CubeJsonShape): boolean =>
248
+ shape.objects === 0 && shape.arrays === 0 && shape.scalars.size > 0;
249
+
250
+ /** A position's most frequent values, most frequent first, then by text */
251
+ export const getCubeJsonTopValues = (
252
+ shape: CubeJsonShape,
253
+ count = 12,
254
+ ): string[] =>
255
+ [...shape.values.entries()]
256
+ .sort(
257
+ (left, right) => right[1] - left[1] || left[0].localeCompare(right[0]),
258
+ )
259
+ .slice(0, count)
260
+ .map(([value]) => value);
261
+
262
+ /** What a position held, e.g. `text`, `object`, `list of object`, `number or object`; `always empty` for nothing but nulls */
263
+ export const describeCubeJsonShape = (shape: CubeJsonShape): string => {
264
+ const parts: string[] = [];
265
+ const scalar = getCubeJsonScalarType(shape);
266
+ if (scalar) {
267
+ parts.push(CUBE_JSON_KIND_LABEL[scalar]);
268
+ }
269
+ if (shape.objects > 0) {
270
+ parts.push('object');
271
+ }
272
+ if (shape.arrays > 0) {
273
+ const { element } = shape;
274
+ parts.push(
275
+ `list of ${!element || element.present === element.nulls ? 'nothing' : describeCubeJsonShape(element)}`,
276
+ );
277
+ }
278
+ return parts.length ? parts.join(' or ') : 'always empty';
279
+ };
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Copyright (c) 2026-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { action, computed, makeObservable, observable } from 'mobx';
18
+ import { type CubeJsonNode, readCubeJson } from './CubeJson.js';
19
+
20
+ /**
21
+ * Open JSON (PLAN §11.20): one Variant cell's document, shown as a tree in a
22
+ * dialog, from the cell's menu
23
+ */
24
+ export class CubeJsonViewerState {
25
+ /** The cell's column, for the title; `undefined` while closed */
26
+ column: string | undefined;
27
+ /** The cell's text, as the engine sent it */
28
+ text: string | undefined;
29
+
30
+ constructor() {
31
+ makeObservable(this, {
32
+ column: observable,
33
+ text: observable,
34
+ isOpen: computed,
35
+ document: computed,
36
+ open: action,
37
+ close: action,
38
+ });
39
+ }
40
+
41
+ get isOpen(): boolean {
42
+ return this.text !== undefined;
43
+ }
44
+
45
+ /** The document; `undefined` for text that isn't JSON, shown as it is */
46
+ get document(): CubeJsonNode | undefined {
47
+ return this.text === undefined ? undefined : readCubeJson(this.text);
48
+ }
49
+
50
+ open(column: string, text: string): void {
51
+ this.column = column;
52
+ this.text = text;
53
+ }
54
+
55
+ close(): void {
56
+ this.column = undefined;
57
+ this.text = undefined;
58
+ }
59
+ }
package/tsconfig.json CHANGED
@@ -54,20 +54,24 @@
54
54
  "./src/__lib__/LegendCubeHelpText.ts",
55
55
  "./src/__lib__/LegendCubeIngestLabels.ts",
56
56
  "./src/__lib__/LegendCubeLabels.ts",
57
+ "./src/__lib__/LegendCubeNavigation.ts",
57
58
  "./src/__lib__/LegendCubeProjectLabels.ts",
58
59
  "./src/__lib__/LegendCubeProjectSourceLabels.ts",
59
60
  "./src/__lib__/LegendCubeServiceLabels.ts",
60
61
  "./src/__lib__/LegendCubeTesting.ts",
62
+ "./src/__lib__/LegendCubeTour.ts",
61
63
  "./src/__test-utils__/CubeCanvasTestUtils.ts",
62
64
  "./src/__test-utils__/CubeConnectionTestSupport.ts",
63
65
  "./src/__test-utils__/CubeDirectConnectionFixtures.ts",
64
66
  "./src/__test-utils__/CubeEngineTestSupport.ts",
67
+ "./src/__test-utils__/CubeExampleTestUtils.ts",
65
68
  "./src/__test-utils__/CubeExpressionTestUtils.ts",
66
69
  "./src/__test-utils__/CubeFunctionTestOutline.ts",
67
70
  "./src/__test-utils__/CubeLambdaCorpus.ts",
68
71
  "./src/__test-utils__/CubeNorthwindTestQueries.ts",
69
72
  "./src/__test-utils__/CubeOperationsTestUtils.ts",
70
73
  "./src/__test-utils__/CubeTestApplication.ts",
74
+ "./src/__test-utils__/CubeVariantTestUtils.ts",
71
75
  "./src/__test-utils__/FakeCubeConnectionExplorer.ts",
72
76
  "./src/__test-utils__/FakeCubeDataProductCatalog.ts",
73
77
  "./src/__test-utils__/FakeCubeEngine.ts",
@@ -150,6 +154,7 @@
150
154
  "./src/stores/CubeLambdaImportState.ts",
151
155
  "./src/stores/CubeLambdaSources.ts",
152
156
  "./src/stores/CubeNodeEditorState.ts",
157
+ "./src/stores/CubePureText.ts",
153
158
  "./src/stores/CubeQueryRun.ts",
154
159
  "./src/stores/CubeRunStatsState.ts",
155
160
  "./src/stores/CubeRunningStepLambda.ts",
@@ -157,10 +162,13 @@
157
162
  "./src/stores/CubeShowPureState.ts",
158
163
  "./src/stores/CubeSourcePickerState.ts",
159
164
  "./src/stores/CubeSpecTransferState.ts",
165
+ "./src/stores/CubeTourState.ts",
166
+ "./src/stores/LegendCubeHost.ts",
160
167
  "./src/stores/LocalModelCatalog.ts",
161
168
  "./src/stores/editors/CubeAggregationRows.ts",
162
169
  "./src/stores/editors/CubeConcatDraft.ts",
163
170
  "./src/stores/editors/CubeDifferenceDraft.ts",
171
+ "./src/stores/editors/CubeExplodeDraft.ts",
164
172
  "./src/stores/editors/CubeExpressionFilterDraft.ts",
165
173
  "./src/stores/editors/CubeExtendDraft.ts",
166
174
  "./src/stores/editors/CubeFilterDraft.ts",
@@ -192,6 +200,14 @@
192
200
  "./src/stores/source-picker/CubeProjectTabState.ts",
193
201
  "./src/stores/source-picker/CubeServiceTabState.ts",
194
202
  "./src/stores/source-picker/CubeSourcePickerTab.ts",
203
+ "./src/stores/variant/CubeJson.ts",
204
+ "./src/stores/variant/CubeJsonExplorerState.ts",
205
+ "./src/stores/variant/CubeJsonFieldsState.ts",
206
+ "./src/stores/variant/CubeJsonOffers.ts",
207
+ "./src/stores/variant/CubeJsonParse.ts",
208
+ "./src/stores/variant/CubeJsonSampling.ts",
209
+ "./src/stores/variant/CubeJsonShape.ts",
210
+ "./src/stores/variant/CubeJsonViewerState.ts",
195
211
  "./src/__test-utils__/CubePageTestUtils.tsx",
196
212
  "./src/components/CubeAddItems.tsx",
197
213
  "./src/components/CubeButton.tsx",
@@ -213,6 +229,7 @@
213
229
  "./src/components/editors/CubeDataProductSourceEditor.tsx",
214
230
  "./src/components/editors/CubeDifferenceEditor.tsx",
215
231
  "./src/components/editors/CubeDistinctEditor.tsx",
232
+ "./src/components/editors/CubeExplodeEditor.tsx",
216
233
  "./src/components/editors/CubeExpressionFilterEditor.tsx",
217
234
  "./src/components/editors/CubeExtendEditor.tsx",
218
235
  "./src/components/editors/CubeFilterEditor.tsx",
@@ -222,6 +239,7 @@
222
239
  "./src/components/editors/CubeIntegerField.tsx",
223
240
  "./src/components/editors/CubeJoinEditor.tsx",
224
241
  "./src/components/editors/CubeJoinKeyRows.tsx",
242
+ "./src/components/editors/CubeJsonFieldsTree.tsx",
225
243
  "./src/components/editors/CubeNodeEditorPanel.tsx",
226
244
  "./src/components/editors/CubeNodeEditorPopper.tsx",
227
245
  "./src/components/editors/CubePartitionEditor.tsx",
@@ -240,6 +258,8 @@
240
258
  "./src/components/grid/CubeColumnFormatDialog.tsx",
241
259
  "./src/components/grid/CubeDrilldownBar.tsx",
242
260
  "./src/components/grid/CubeGridRegion.tsx",
261
+ "./src/components/grid/CubeJsonExplorerDialog.tsx",
262
+ "./src/components/grid/CubeJsonViewerDialog.tsx",
243
263
  "./src/components/grid/CubeResultGrid.tsx",
244
264
  "./src/components/grid/CubeRunStatsPanel.tsx",
245
265
  "./src/components/grid/CubeServerSideGrid.tsx",
@@ -258,7 +278,8 @@
258
278
  "./src/components/source-picker/CubeSourcePicker.tsx",
259
279
  "./src/components/source-picker/CubeTablePreview.tsx",
260
280
  "./src/components/spec-transfer/CubeImportPureDialog.tsx",
261
- "./src/components/spec-transfer/CubeSpecTransferDialog.tsx"
281
+ "./src/components/spec-transfer/CubeSpecTransferDialog.tsx",
282
+ "./src/components/tour/CubeTour.tsx"
262
283
  ],
263
284
  "include": [
264
285
  "src/**/*.ts",