@malloy-publisher/sdk 0.0.1

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 (43) hide show
  1. package/.eslintrc +36 -0
  2. package/.prettierrc +4 -0
  3. package/README.md +1 -0
  4. package/openapitools.json +7 -0
  5. package/package.json +63 -0
  6. package/src/assets/img/spinner.svg +43 -0
  7. package/src/client/.openapi-generator/FILES +9 -0
  8. package/src/client/.openapi-generator/VERSION +1 -0
  9. package/src/client/.openapi-generator-ignore +23 -0
  10. package/src/client/api.ts +1170 -0
  11. package/src/client/base.ts +86 -0
  12. package/src/client/common.ts +150 -0
  13. package/src/client/configuration.ts +110 -0
  14. package/src/client/git_push.sh +57 -0
  15. package/src/client/index.ts +18 -0
  16. package/src/components/DOMElement/DOMElement.tsx +19 -0
  17. package/src/components/DOMElement/index.ts +1 -0
  18. package/src/components/Model/Model.tsx +110 -0
  19. package/src/components/Model/ModelCell.tsx +175 -0
  20. package/src/components/Model/index.ts +1 -0
  21. package/src/components/Notebook/Notebook.tsx +82 -0
  22. package/src/components/Notebook/NotebookCell.tsx +194 -0
  23. package/src/components/Notebook/index.ts +1 -0
  24. package/src/components/Package/Config.tsx +99 -0
  25. package/src/components/Package/Databases.tsx +75 -0
  26. package/src/components/Package/FileTreeView.tsx +224 -0
  27. package/src/components/Package/Models.tsx +77 -0
  28. package/src/components/Package/Package.tsx +78 -0
  29. package/src/components/Package/index.ts +1 -0
  30. package/src/components/Project/About.tsx +56 -0
  31. package/src/components/Project/Packages.tsx +105 -0
  32. package/src/components/Project/Project.tsx +26 -0
  33. package/src/components/Project/index.ts +1 -0
  34. package/src/components/QueryResult/QueryResult.tsx +84 -0
  35. package/src/components/QueryResult/index.ts +1 -0
  36. package/src/components/RenderedResult/RenderedResult.tsx +38 -0
  37. package/src/components/highlighter.ts +649 -0
  38. package/src/components/index.ts +5 -0
  39. package/src/components/styles.ts +21 -0
  40. package/src/index.ts +1 -0
  41. package/src/react-app-env.d.ts +73 -0
  42. package/tsconfig.json +34 -0
  43. package/vite.config.ts +31 -0
@@ -0,0 +1,224 @@
1
+ import * as React from "react";
2
+ import {
3
+ TreeItem2Content,
4
+ TreeItem2Label,
5
+ TreeItem2Root,
6
+ TreeItem2IconContainer,
7
+ } from "@mui/x-tree-view/TreeItem2";
8
+ import { RichTreeView } from "@mui/x-tree-view/RichTreeView";
9
+ import { TreeItem2Provider } from "@mui/x-tree-view/TreeItem2Provider";
10
+ import { TreeViewBaseItem } from "@mui/x-tree-view/models";
11
+ import {
12
+ unstable_useTreeItem2 as useTreeItem2,
13
+ UseTreeItem2Parameters,
14
+ } from "@mui/x-tree-view/useTreeItem2";
15
+ import { TreeItem2Icon } from "@mui/x-tree-view/TreeItem2Icon";
16
+ import FolderIcon from "@mui/icons-material/FolderOutlined";
17
+ import ArticleIcon from "@mui/icons-material/ArticleOutlined";
18
+ import ErrorIcon from "@mui/icons-material/ErrorOutlined";
19
+ import { TransitionProps } from "@mui/material/transitions";
20
+ import { animated, useSpring } from "@react-spring/web";
21
+ import Collapse from "@mui/material/Collapse";
22
+ import DnsIcon from "@mui/icons-material/DnsOutlined";
23
+ import DataArrayIcon from "@mui/icons-material/DataArrayOutlined";
24
+ import { Database, Model } from "../../client";
25
+ import { Typography } from "@mui/material";
26
+
27
+ interface FiieTreeViewProps {
28
+ items: Model[] | Database[];
29
+ defaultExpandedItems: string[];
30
+ navigate?: (to: string) => void;
31
+ }
32
+
33
+ export function FileTreeView({
34
+ items,
35
+ defaultExpandedItems,
36
+ navigate,
37
+ }: FiieTreeViewProps) {
38
+ return (
39
+ <RichTreeView
40
+ items={getTreeView(items, navigate)}
41
+ defaultExpandedItems={defaultExpandedItems}
42
+ slots={{ item: CustomTreeItem }}
43
+ />
44
+ );
45
+ }
46
+
47
+ const AnimatedCollapse = animated(Collapse);
48
+
49
+ function TransitionComponent(props: TransitionProps) {
50
+ const style = useSpring({
51
+ to: {
52
+ opacity: props.in ? 1 : 0,
53
+ transform: `translate3d(0,${props.in ? 0 : 20}px,0)`,
54
+ },
55
+ });
56
+
57
+ return <AnimatedCollapse style={style} {...props} />;
58
+ }
59
+
60
+ type FileType = "directory" | "model" | "notebook" | "database" | "unknown";
61
+
62
+ type ExtendedTreeItemProps = {
63
+ id: string;
64
+ label: string;
65
+ fileType: FileType;
66
+ selectable: boolean;
67
+ link: () => void | undefined;
68
+ };
69
+
70
+ interface CustomLabelProps {
71
+ item: ExtendedTreeItemProps;
72
+ }
73
+
74
+ function CustomTreeItem2Label({ item, ...other }: CustomLabelProps) {
75
+ return (
76
+ <TreeItem2Label
77
+ {...other}
78
+ sx={{
79
+ display: "flex",
80
+ alignItems: "center",
81
+ color: "grey.600",
82
+ }}
83
+ >
84
+ {(item.fileType === "directory" && <FolderIcon />) ||
85
+ (item.fileType === "notebook" && <ArticleIcon />) ||
86
+ (item.fileType === "model" && <DataArrayIcon />) ||
87
+ (item.fileType == "database" && <DnsIcon />) || <ErrorIcon />}
88
+ <Typography
89
+ variant="body2"
90
+ sx={{ marginLeft: "5px" }}
91
+ color={item.link ? "primary.main" : "grey.600"}
92
+ >
93
+ {item.label}
94
+ </Typography>
95
+ </TreeItem2Label>
96
+ );
97
+ }
98
+
99
+ interface CustomTreeItemProps
100
+ extends Omit<UseTreeItem2Parameters, "rootRef">,
101
+ Omit<React.HTMLAttributes<HTMLLIElement>, "onFocus"> {}
102
+
103
+ const CustomTreeItem = React.forwardRef(function CustomTreeItem(
104
+ props: CustomTreeItemProps,
105
+ ref: React.Ref<HTMLLIElement>,
106
+ ) {
107
+ const { id, itemId, label, children, disabled, ...other } = props;
108
+ const {
109
+ getRootProps,
110
+ getContentProps,
111
+ getLabelProps,
112
+ getIconContainerProps,
113
+ getGroupTransitionProps,
114
+ status,
115
+ publicAPI,
116
+ } = useTreeItem2({
117
+ id,
118
+ itemId,
119
+ children,
120
+ label,
121
+ disabled,
122
+ rootRef: ref,
123
+ });
124
+
125
+ const item = publicAPI.getItem(itemId);
126
+
127
+ // Disable select and focus. We want to either click through to a model or notebook or just exapnd
128
+ // collapse
129
+ status.selected = false;
130
+ status.focused = false;
131
+
132
+ return (
133
+ <TreeItem2Provider itemId={itemId}>
134
+ <TreeItem2Root {...getRootProps(other)}>
135
+ <TreeItem2Content
136
+ {...getContentProps()}
137
+ {...(item.link && {
138
+ onClick: () => item.link(),
139
+ })}
140
+ sx={{
141
+ // If the item is not selectable, disable pointer events.
142
+ pointerEvents: !item.selectable && "none",
143
+ }}
144
+ >
145
+ <TreeItem2IconContainer {...getIconContainerProps()}>
146
+ <TreeItem2Icon status={status} />
147
+ </TreeItem2IconContainer>
148
+ <CustomTreeItem2Label
149
+ {...getLabelProps({
150
+ item,
151
+ })}
152
+ />
153
+ </TreeItem2Content>
154
+ {children && <TransitionComponent {...getGroupTransitionProps()} />}
155
+ </TreeItem2Root>
156
+ </TreeItem2Provider>
157
+ );
158
+ });
159
+
160
+ function getTreeView(
161
+ metadataEntries: Model[] | Database[],
162
+ navigate: (to: string) => void,
163
+ ): TreeViewBaseItem<ExtendedTreeItemProps>[] {
164
+ const tree = new Map<string, unknown>();
165
+ metadataEntries.map((entry: Model | Database) => {
166
+ let node = tree;
167
+ const pathParts = entry.path.split("/");
168
+ pathParts.forEach((part, index) => {
169
+ if (index === pathParts.length - 1) {
170
+ node.set(part, entry);
171
+ } else if (!node.has(part)) {
172
+ node.set(part, new Map<string, unknown>());
173
+ node = node.get(part) as Map<string, unknown>;
174
+ } else {
175
+ node = node.get(part) as Map<string, unknown>;
176
+ }
177
+ });
178
+ });
179
+ return getTreeViewRecursive(tree, "", navigate);
180
+ }
181
+
182
+ function getTreeViewRecursive(
183
+ node: Map<string, unknown>,
184
+ path: string,
185
+ navigate: (to: string) => void,
186
+ ): TreeViewBaseItem<ExtendedTreeItemProps>[] {
187
+ const treeViewItems: TreeViewBaseItem<ExtendedTreeItemProps>[] = [];
188
+ node.forEach((value, key) => {
189
+ if ((value as Model | Database).type !== undefined) {
190
+ const fileType =
191
+ (key.endsWith(".malloy") && "model") ||
192
+ (key.endsWith(".malloynb") && "notebook") ||
193
+ (key.endsWith(".parquet") && "database") ||
194
+ "unknown";
195
+ // This is a model or database.
196
+ treeViewItems.push({
197
+ id: path + key,
198
+ label: key,
199
+ fileType: fileType,
200
+ link:
201
+ fileType === "model" || fileType === "notebook"
202
+ ? navigate.bind(null, path + key)
203
+ : undefined,
204
+ selectable: fileType === "model" || fileType === "notebook",
205
+ });
206
+ } else {
207
+ // This is a directory.
208
+ path += `${key}/`;
209
+ treeViewItems.push({
210
+ id: path,
211
+ label: key,
212
+ fileType: "directory",
213
+ selectable: true,
214
+ link: undefined,
215
+ children: getTreeViewRecursive(
216
+ value as Map<string, unknown>,
217
+ path,
218
+ navigate,
219
+ ),
220
+ });
221
+ }
222
+ });
223
+ return treeViewItems;
224
+ }
@@ -0,0 +1,77 @@
1
+ import { Box, Divider, Typography } from "@mui/material";
2
+ import { QueryClient, useQuery } from "@tanstack/react-query";
3
+ import axios from "axios";
4
+ import { Configuration, ModelsApi } from "../../client";
5
+ import { StyledCard, StyledCardContent } from "../styles";
6
+ import { FileTreeView } from "./FileTreeView";
7
+
8
+ axios.defaults.baseURL = "http://localhost:4000";
9
+ const modelsApi = new ModelsApi(new Configuration());
10
+ const queryClient = new QueryClient();
11
+
12
+ const DEFAULT_EXPANDED_FOLDERS = ["notebooks/", "models/"];
13
+
14
+ interface ModelsProps {
15
+ server?: string;
16
+ packageName: string;
17
+ versionId?: string;
18
+ navigate: (to: string) => void;
19
+ }
20
+
21
+ export default function Models({
22
+ server,
23
+ packageName,
24
+ versionId,
25
+ navigate,
26
+ }: ModelsProps) {
27
+ const { data, isSuccess, isError, error } = useQuery(
28
+ {
29
+ queryKey: ["models", server, packageName, versionId],
30
+ queryFn: () =>
31
+ modelsApi.listModels(packageName, versionId, {
32
+ baseURL: server,
33
+ withCredentials: true,
34
+ }),
35
+ retry: false,
36
+ },
37
+ queryClient,
38
+ );
39
+
40
+ return (
41
+ <StyledCard variant="outlined" sx={{ padding: "10px", width: "100%" }}>
42
+ <StyledCardContent>
43
+ <Typography variant="overline" fontWeight="bold">
44
+ Models
45
+ </Typography>
46
+ <Divider />
47
+ <Box
48
+ sx={{
49
+ mt: "10px",
50
+ maxHeight: "300px",
51
+ overflowY: "auto",
52
+ }}
53
+ >
54
+ {!isSuccess && !isError && (
55
+ <Typography variant="body2" sx={{ p: "20px", m: "auto" }}>
56
+ Fetching Models
57
+ </Typography>
58
+ )}
59
+ {isSuccess && data.data.length > 0 && (
60
+ <FileTreeView
61
+ items={data.data.sort((a, b) => {
62
+ return a.path.localeCompare(b.path);
63
+ })}
64
+ defaultExpandedItems={DEFAULT_EXPANDED_FOLDERS}
65
+ navigate={navigate}
66
+ />
67
+ )}
68
+ {isError && (
69
+ <Typography variant="body2" sx={{ p: "10px", m: "auto" }}>
70
+ {`${packageName} > ${versionId} - ${error.message}`}
71
+ </Typography>
72
+ )}
73
+ </Box>
74
+ </StyledCardContent>
75
+ </StyledCard>
76
+ );
77
+ }
@@ -0,0 +1,78 @@
1
+ import { Divider, Grid2, Typography } from "@mui/material";
2
+ import { Notebook } from "../Notebook";
3
+ import { StyledCard, StyledCardContent, StyledCardMedia } from "../styles";
4
+ import Config from "./Config";
5
+ import Models from "./Models";
6
+ import Databases from "./Databases";
7
+
8
+ const README_NOTEBOOK = "README.malloynb";
9
+
10
+ interface PackageProps {
11
+ server?: string;
12
+ packageName: string;
13
+ versionId?: string;
14
+ navigate?: (to: string) => void;
15
+ }
16
+
17
+ export default function Package({
18
+ server,
19
+ packageName,
20
+ versionId,
21
+ navigate,
22
+ }: PackageProps) {
23
+ if (!navigate) {
24
+ navigate = (to: string) => {
25
+ window.location.href = to;
26
+ };
27
+ }
28
+
29
+ return (
30
+ <Grid2
31
+ container
32
+ spacing={2}
33
+ columns={12}
34
+ sx={{ mb: (theme) => theme.spacing(2) }}
35
+ >
36
+ <Grid2 size={{ md: 12, lg: 4 }}>
37
+ <Config
38
+ server={server}
39
+ packageName={packageName}
40
+ versionId={versionId}
41
+ />
42
+ </Grid2>
43
+ <Grid2 size={{ md: 12, lg: 4 }}>
44
+ <Models
45
+ server={server}
46
+ packageName={packageName}
47
+ versionId={versionId}
48
+ navigate={navigate}
49
+ />
50
+ </Grid2>
51
+ <Grid2 size={{ md: 12, lg: 4 }}>
52
+ <Databases
53
+ server={server}
54
+ packageName={packageName}
55
+ versionId={versionId}
56
+ />
57
+ </Grid2>
58
+ <Grid2 size={{ md: 12 }}>
59
+ <StyledCard variant="outlined">
60
+ <StyledCardContent>
61
+ <Typography variant="overline" fontWeight="bold">
62
+ Readme
63
+ </Typography>
64
+ <Divider />
65
+ </StyledCardContent>
66
+ <StyledCardMedia>
67
+ <Notebook
68
+ server={server}
69
+ packageName={packageName}
70
+ notebookPath={README_NOTEBOOK}
71
+ versionId={versionId}
72
+ />
73
+ </StyledCardMedia>
74
+ </StyledCard>
75
+ </Grid2>
76
+ </Grid2>
77
+ );
78
+ }
@@ -0,0 +1 @@
1
+ export { default as Package } from "./Package";
@@ -0,0 +1,56 @@
1
+ import { Divider, Typography } from "@mui/material";
2
+ import { Configuration, DefaultApi } from "../../client";
3
+ import axios from "axios";
4
+ import Markdown from "markdown-to-jsx";
5
+ import { StyledCard, StyledCardContent, StyledCardMedia } from "../styles";
6
+ import { QueryClient, useQuery } from "@tanstack/react-query";
7
+
8
+ axios.defaults.baseURL = "http://localhost:4000";
9
+ const aboutApi = new DefaultApi(new Configuration());
10
+ const queryClient = new QueryClient();
11
+
12
+ interface AboutProps {
13
+ server?: string;
14
+ }
15
+
16
+ export default function About({ server }: AboutProps) {
17
+ const { data, isSuccess, isError, error } = useQuery(
18
+ {
19
+ queryKey: ["about", server],
20
+ queryFn: () =>
21
+ aboutApi.about({
22
+ baseURL: server,
23
+ withCredentials: true,
24
+ }),
25
+ },
26
+ queryClient,
27
+ );
28
+
29
+ return (
30
+ <>
31
+ {!isSuccess && !isError && (
32
+ <Typography variant="body2" sx={{ p: "10px", m: "auto" }}>
33
+ Fetching About...
34
+ </Typography>
35
+ )}
36
+ {isSuccess && (
37
+ <StyledCard variant="outlined">
38
+ <StyledCardContent>
39
+ <Typography variant="overline" fontWeight="bold">
40
+ Readme
41
+ </Typography>
42
+ <Divider />
43
+ </StyledCardContent>
44
+ <StyledCardMedia>
45
+ <Markdown>{data.data.readme}</Markdown>
46
+ </StyledCardMedia>
47
+ </StyledCard>
48
+ )}
49
+ {isError && (
50
+ <Typography variant="body2" sx={{ p: "10px", m: "auto" }}>
51
+ {error.message}
52
+ </Typography>
53
+ )}
54
+ </>
55
+ );
56
+ }
@@ -0,0 +1,105 @@
1
+ import { Box, Divider, Grid2, Typography } from "@mui/material";
2
+ import { Configuration, PackagesApi } from "../../client";
3
+ import axios from "axios";
4
+ import { StyledCard, StyledCardContent, StyledCardMedia } from "../styles";
5
+ import { QueryClient, useQuery } from "@tanstack/react-query";
6
+
7
+ axios.defaults.baseURL = "http://localhost:4000";
8
+ const packagesApi = new PackagesApi(new Configuration());
9
+ const queryClient = new QueryClient();
10
+
11
+ interface ProjectProps {
12
+ server?: string;
13
+ navigate?: (to: string) => void;
14
+ }
15
+
16
+ export default function Project({ server, navigate }: ProjectProps) {
17
+ const { data, isSuccess, isError, error } = useQuery(
18
+ {
19
+ queryKey: ["packages", server],
20
+ queryFn: () =>
21
+ packagesApi.listPackages({
22
+ baseURL: server,
23
+ withCredentials: true,
24
+ }),
25
+ },
26
+ queryClient,
27
+ );
28
+
29
+ if (!navigate) {
30
+ navigate = (to: string) => {
31
+ window.location.href = to;
32
+ };
33
+ }
34
+
35
+ return (
36
+ <>
37
+ {!isSuccess && !isError && (
38
+ <Typography variant="body2" sx={{ p: "20px", m: "auto" }}>
39
+ Fetching Packages...
40
+ </Typography>
41
+ )}
42
+ {isSuccess && (
43
+ <StyledCard variant="outlined">
44
+ <StyledCardContent>
45
+ <Typography variant="overline" fontWeight="bold">
46
+ Packages
47
+ </Typography>
48
+ <Divider />
49
+ </StyledCardContent>
50
+ <StyledCardMedia>
51
+ <Grid2
52
+ container
53
+ spacing={2}
54
+ columns={12}
55
+ sx={{ mb: (theme) => theme.spacing(2) }}
56
+ >
57
+ {data.data
58
+ .sort((a, b) => {
59
+ return a.name.localeCompare(b.name);
60
+ })
61
+ .map((p) => (
62
+ <Grid2
63
+ size={{ xs: 12, sm: 12, md: 12, lg: 4 }}
64
+ key={p.name}
65
+ >
66
+ <StyledCard
67
+ variant="outlined"
68
+ sx={{ padding: "10px", cursor: "pointer" }}
69
+ onClick={() => navigate(p.name + "/")}
70
+ >
71
+ <StyledCardContent>
72
+ <Typography
73
+ variant="overline"
74
+ color="primary.main"
75
+ >
76
+ {p.name}
77
+ </Typography>
78
+ <Divider />
79
+ <Box
80
+ sx={{
81
+ mt: "10px",
82
+ maxHeight: "100px",
83
+ overflowY: "auto",
84
+ }}
85
+ >
86
+ <Typography variant="body2">
87
+ {p.description}
88
+ </Typography>
89
+ </Box>
90
+ </StyledCardContent>
91
+ </StyledCard>
92
+ </Grid2>
93
+ ))}
94
+ </Grid2>
95
+ </StyledCardMedia>
96
+ </StyledCard>
97
+ )}
98
+ {isError && (
99
+ <Typography variant="body2" sx={{ p: "10px", m: "auto" }}>
100
+ {error.message}
101
+ </Typography>
102
+ )}
103
+ </>
104
+ );
105
+ }
@@ -0,0 +1,26 @@
1
+ import { Grid2 } from "@mui/material";
2
+ import About from "./About";
3
+ import Packages from "./Packages";
4
+
5
+ interface ProjectProps {
6
+ server?: string;
7
+ navigate?: (to: string) => void;
8
+ }
9
+
10
+ export default function Project({ server, navigate }: ProjectProps) {
11
+ return (
12
+ <Grid2
13
+ container
14
+ spacing={2}
15
+ columns={12}
16
+ sx={{ mb: (theme) => theme.spacing(2) }}
17
+ >
18
+ <Grid2 size={{ xs: 12, md: 12 }}>
19
+ <Packages server={server} navigate={navigate} />
20
+ </Grid2>
21
+ <Grid2 size={{ xs: 12, md: 12 }}>
22
+ <About server={server} />
23
+ </Grid2>
24
+ </Grid2>
25
+ );
26
+ }
@@ -0,0 +1 @@
1
+ export { default as Project } from "./Project";
@@ -0,0 +1,84 @@
1
+ import { Suspense, lazy } from "react";
2
+ import { Configuration, QueryresultsApi } from "../../client";
3
+ import axios from "axios";
4
+ import { Typography } from "@mui/material";
5
+ import { QueryClient, useQuery } from "@tanstack/react-query";
6
+
7
+ const RenderedResult = lazy(() => import("../RenderedResult/RenderedResult"));
8
+
9
+ axios.defaults.baseURL = "http://localhost:4000";
10
+ const queryResultsApi = new QueryresultsApi(new Configuration());
11
+ const queryClient = new QueryClient();
12
+
13
+ interface QueryResultProps {
14
+ server?: string;
15
+ packageName: string;
16
+ modelPath: string;
17
+ versionId?: string;
18
+ query?: string;
19
+ sourceName?: string;
20
+ queryName?: string;
21
+ }
22
+
23
+ export default function QueryResult({
24
+ server,
25
+ packageName,
26
+ modelPath,
27
+ versionId,
28
+ query,
29
+ sourceName,
30
+ queryName,
31
+ }: QueryResultProps) {
32
+ const { data, isSuccess, isError, error } = useQuery(
33
+ {
34
+ queryKey: [
35
+ "queryResult",
36
+ server,
37
+ packageName,
38
+ modelPath,
39
+ versionId,
40
+ query,
41
+ sourceName,
42
+ queryName,
43
+ ],
44
+ queryFn: () =>
45
+ queryResultsApi.executeQuery(
46
+ packageName,
47
+ modelPath,
48
+ versionId,
49
+ query,
50
+ sourceName,
51
+ queryName,
52
+ {
53
+ baseURL: server,
54
+ withCredentials: true,
55
+ },
56
+ ),
57
+ },
58
+ queryClient,
59
+ );
60
+
61
+ return (
62
+ <>
63
+ {!isSuccess && !isError && (
64
+ <Typography variant="body2" sx={{ p: "20px", m: "auto" }}>
65
+ Fetching Query Results...
66
+ </Typography>
67
+ )}
68
+ {isSuccess && (
69
+ <Suspense fallback="Loading malloy...">
70
+ <RenderedResult
71
+ queryResult={data.data.queryResult}
72
+ modelDef={data.data.modelDef}
73
+ dataStyles={data.data.dataStyles}
74
+ />
75
+ </Suspense>
76
+ )}
77
+ {isError && (
78
+ <Typography variant="body2" sx={{ p: "10px", m: "auto" }}>
79
+ {`${packageName} > ${modelPath} > ${versionId} - ${error.message}`}
80
+ </Typography>
81
+ )}
82
+ </>
83
+ );
84
+ }
@@ -0,0 +1 @@
1
+ export { default as QueryResult } from "./QueryResult";