@malloy-publisher/sdk 0.0.81 → 0.0.82

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@malloy-publisher/sdk",
3
3
  "description": "Malloy Publisher SDK",
4
- "version": "0.0.81",
4
+ "version": "0.0.82",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs.js",
7
7
  "module": "dist/index.es.js",
@@ -1,34 +1,24 @@
1
1
  import ContentCopyIcon from "@mui/icons-material/ContentCopy";
2
2
  import LinkOutlinedIcon from "@mui/icons-material/LinkOutlined";
3
3
  import {
4
- Box,
5
4
  CardActions,
6
5
  Collapse,
7
6
  Divider,
8
7
  IconButton,
9
8
  Stack,
10
- Tab,
11
- Tabs,
12
9
  Tooltip,
13
10
  Typography,
14
11
  } from "@mui/material";
15
12
  import React, { useEffect } from "react";
16
- import { Configuration, ModelsApi, CompiledModel } from "../../client";
17
13
  import { highlight } from "../highlighter";
18
- import { StyledCard, StyledCardContent, StyledCardMedia } from "../styles";
19
- import { ModelCell } from "./ModelCell";
14
+ import { StyledCard, StyledCardContent } from "../styles";
20
15
  import { ApiErrorDisplay } from "../ApiErrorDisplay";
21
16
 
22
17
  import "@malloydata/malloy-explorer/styles.css";
23
- import { usePackage } from "../Package/PackageProvider";
24
- import {
25
- QueryExplorerResult,
26
- SourceExplorerComponent,
27
- } from "./SourcesExplorer";
18
+ import { QueryExplorerResult } from "./SourcesExplorer";
28
19
  import { Loading } from "../Loading";
29
- import { useQueryWithApiError } from "../../hooks/useQueryWithApiError";
30
-
31
- const modelsApi = new ModelsApi(new Configuration());
20
+ import { ModelExplorer } from "./ModelExplorer";
21
+ import { useModelData } from "./useModelData";
32
22
 
33
23
  interface ModelProps {
34
24
  modelPath: string;
@@ -46,6 +36,7 @@ interface ModelProps {
46
36
 
47
37
  export default function Model({
48
38
  modelPath,
39
+ versionId,
49
40
  expandResults,
50
41
  hideResultIcons,
51
42
  expandEmbeddings,
@@ -56,9 +47,8 @@ export default function Model({
56
47
  React.useState<boolean>(false);
57
48
  const [highlightedEmbedCode, setHighlightedEmbedCode] =
58
49
  React.useState<string>();
59
- const [selectedTab, setSelectedTab] = React.useState(0);
60
50
 
61
- const { projectName, packageName, versionId } = usePackage();
51
+ const { isError, isLoading, error } = useModelData(modelPath, versionId);
62
52
  const modelCodeSnippet = getModelCodeSnippet(modelPath);
63
53
  useEffect(() => {
64
54
  highlight(modelCodeSnippet, "typescript").then((code) => {
@@ -66,33 +56,13 @@ export default function Model({
66
56
  });
67
57
  }, [embeddingExpanded, modelCodeSnippet]);
68
58
 
69
- const { data, isError, isLoading, error } =
70
- useQueryWithApiError<CompiledModel>({
71
- queryKey: ["package", projectName, packageName, modelPath, versionId],
72
- queryFn: async (config) => {
73
- const response = await modelsApi.getModel(
74
- projectName,
75
- packageName,
76
- modelPath,
77
- versionId,
78
- config,
79
- );
80
- return response.data;
81
- },
82
- });
83
-
84
59
  if (isLoading) {
85
60
  return <Loading text="Fetching Model..." />;
86
61
  }
87
62
 
88
63
  if (isError) {
89
64
  console.log("error", error);
90
- return (
91
- <ApiErrorDisplay
92
- error={error}
93
- context={`${packageName} > ${modelPath}`}
94
- />
95
- );
65
+ return <ApiErrorDisplay error={error} context={`Model > ${modelPath}`} />;
96
66
  }
97
67
  return (
98
68
  <StyledCard variant="outlined">
@@ -100,39 +70,9 @@ export default function Model({
100
70
  <Stack
101
71
  sx={{
102
72
  flexDirection: "row",
103
- justifyContent: "space-between",
73
+ justifyContent: "flex-end",
104
74
  }}
105
75
  >
106
- {Array.isArray(data.sourceInfos) &&
107
- data.sourceInfos.length > 0 && (
108
- <Tabs
109
- value={selectedTab}
110
- onChange={(_, newValue) => setSelectedTab(newValue)}
111
- variant="scrollable"
112
- scrollButtons="auto"
113
- sx={{
114
- borderBottom: 1,
115
- borderColor: "divider",
116
- minHeight: 36,
117
- }}
118
- >
119
- {data.sourceInfos.map((source, idx) => {
120
- let sourceInfo;
121
- try {
122
- sourceInfo = JSON.parse(source);
123
- } catch {
124
- sourceInfo = { name: String(idx) };
125
- }
126
- return (
127
- <Tab
128
- key={sourceInfo.name || idx}
129
- label={sourceInfo.name || `Source ${idx + 1}`}
130
- sx={{ minHeight: 36 }}
131
- />
132
- );
133
- })}
134
- </Tabs>
135
- )}
136
76
  {!hideEmbeddingIcons && (
137
77
  <CardActions
138
78
  sx={{
@@ -196,52 +136,15 @@ export default function Model({
196
136
  </Collapse>
197
137
  <Divider />
198
138
  </StyledCardContent>
199
- <StyledCardMedia>
200
- <Stack spacing={2} component="section">
201
- {/* Only render the selected sourceInfo */}
202
- {Array.isArray(data.sourceInfos) &&
203
- data.sourceInfos.length > 0 && (
204
- <SourceExplorerComponent
205
- sourceAndPath={{
206
- modelPath,
207
- sourceInfo: JSON.parse(
208
- data.sourceInfos[selectedTab],
209
- ),
210
- }}
211
- onChange={onChange}
212
- />
213
- )}
214
- {data.queries?.length > 0 && (
215
- <StyledCard
216
- variant="outlined"
217
- sx={{ padding: "0px 10px 0px 10px" }}
218
- >
219
- <StyledCardContent sx={{ p: "10px" }}>
220
- <Typography variant="subtitle1">
221
- Named Queries
222
- </Typography>
223
- </StyledCardContent>
224
- <Stack spacing={1} component="section">
225
- {data.queries.map((query) => (
226
- <ModelCell
227
- key={query.name}
228
- modelPath={modelPath}
229
- queryName={query.name}
230
- expandResult={expandResults}
231
- hideResultIcon={hideResultIcons}
232
- expandEmbedding={expandEmbeddings}
233
- hideEmbeddingIcon={hideEmbeddingIcons}
234
- noView={true}
235
- annotations={query.annotations}
236
- />
237
- ))}
238
- </Stack>
239
- <Box height="10px" />
240
- </StyledCard>
241
- )}
242
- <Box height="5px" />
243
- </Stack>
244
- </StyledCardMedia>
139
+ <ModelExplorer
140
+ modelPath={modelPath}
141
+ versionId={versionId}
142
+ expandResults={expandResults}
143
+ hideResultIcons={hideResultIcons}
144
+ expandEmbeddings={expandEmbeddings}
145
+ hideEmbeddingIcons={hideEmbeddingIcons}
146
+ onChange={onChange}
147
+ />
245
148
  </StyledCard>
246
149
  );
247
150
  }
@@ -0,0 +1,138 @@
1
+ import React from "react";
2
+ import { Box, Stack, Typography, Tabs, Tab } from "@mui/material";
3
+ import { StyledCard, StyledCardContent } from "../styles";
4
+ import { ModelCell } from "./ModelCell";
5
+ import {
6
+ QueryExplorerResult,
7
+ SourceExplorerComponent,
8
+ } from "./SourcesExplorer";
9
+ import { ApiErrorDisplay } from "../ApiErrorDisplay";
10
+ import { Loading } from "../Loading";
11
+ import { useModelData } from "./useModelData";
12
+
13
+ export interface ModelExplorerProps {
14
+ modelPath: string;
15
+ versionId?: string;
16
+ /** Display options forwarded to ModelCell */
17
+ expandResults?: boolean;
18
+ hideResultIcons?: boolean;
19
+ expandEmbeddings?: boolean;
20
+ hideEmbeddingIcons?: boolean;
21
+ /** Callback when the explorer changes (e.g. when a query is selected). */
22
+ onChange?: (query: QueryExplorerResult) => void;
23
+ }
24
+
25
+ /**
26
+ * ModelExplorer renders the main explorer UI for a Malloy model. It shows the
27
+ * selected source (via `SourceExplorerComponent`) along with the list of named
28
+ * queries for that model. This logic was originally embedded inside `Model.tsx`
29
+ * but has been extracted for easier reuse.
30
+ */
31
+ export function ModelExplorer({
32
+ modelPath,
33
+ versionId,
34
+ expandResults,
35
+ hideResultIcons,
36
+ expandEmbeddings,
37
+ hideEmbeddingIcons,
38
+ onChange,
39
+ }: ModelExplorerProps) {
40
+ const [selectedTab, setSelectedTab] = React.useState(0);
41
+
42
+ const { data, isError, isLoading, error } = useModelData(
43
+ modelPath,
44
+ versionId,
45
+ );
46
+
47
+ if (isLoading) {
48
+ return <Loading text="Fetching Model..." />;
49
+ }
50
+
51
+ if (isError) {
52
+ console.log("error", error);
53
+ return (
54
+ <ApiErrorDisplay
55
+ error={error}
56
+ context={`ModelExplorer > ${modelPath}`}
57
+ />
58
+ );
59
+ }
60
+
61
+ return (
62
+ <Stack spacing={2} component="section">
63
+ {/* Render the tabs for source selection */}
64
+ {Array.isArray(data.sourceInfos) && data.sourceInfos.length > 0 && (
65
+ <Stack sx={{ flexDirection: "row", justifyContent: "flex-start" }}>
66
+ <Tabs
67
+ value={selectedTab}
68
+ onChange={(_, newValue) => setSelectedTab(newValue)}
69
+ variant="scrollable"
70
+ scrollButtons="auto"
71
+ sx={{
72
+ borderBottom: 1,
73
+ borderColor: "divider",
74
+ minHeight: 36,
75
+ }}
76
+ >
77
+ {data.sourceInfos.map((source, idx) => {
78
+ let sourceInfo;
79
+ try {
80
+ sourceInfo = JSON.parse(source);
81
+ } catch {
82
+ sourceInfo = { name: String(idx) };
83
+ }
84
+ return (
85
+ <Tab
86
+ key={sourceInfo.name || idx}
87
+ label={sourceInfo.name || `Source ${idx + 1}`}
88
+ sx={{ minHeight: 36 }}
89
+ />
90
+ );
91
+ })}
92
+ </Tabs>
93
+ </Stack>
94
+ )}
95
+
96
+ {/* Render the selected source info */}
97
+ {Array.isArray(data.sourceInfos) && data.sourceInfos.length > 0 && (
98
+ <SourceExplorerComponent
99
+ sourceAndPath={{
100
+ modelPath,
101
+ sourceInfo: JSON.parse(data.sourceInfos[selectedTab]),
102
+ }}
103
+ onChange={onChange}
104
+ />
105
+ )}
106
+
107
+ {/* Render the named queries */}
108
+ {data.queries?.length > 0 && (
109
+ <StyledCard
110
+ variant="outlined"
111
+ sx={{ padding: "0px 10px 0px 10px" }}
112
+ >
113
+ <StyledCardContent sx={{ p: "10px" }}>
114
+ <Typography variant="subtitle1">Named Queries</Typography>
115
+ </StyledCardContent>
116
+
117
+ <Stack spacing={1} component="section">
118
+ {data.queries.map((query) => (
119
+ <ModelCell
120
+ key={query.name}
121
+ modelPath={modelPath}
122
+ queryName={query.name}
123
+ expandResult={expandResults}
124
+ hideResultIcon={hideResultIcons}
125
+ expandEmbedding={expandEmbeddings}
126
+ hideEmbeddingIcon={hideEmbeddingIcons}
127
+ noView={true}
128
+ annotations={query.annotations}
129
+ />
130
+ ))}
131
+ </Stack>
132
+ <Box height="10px" />
133
+ </StyledCard>
134
+ )}
135
+ <Box height="5px" />
136
+ </Stack>
137
+ );
138
+ }
@@ -235,7 +235,7 @@ function SourceExplorerComponentInner({
235
235
 
236
236
  const onQueryChange = React.useCallback(
237
237
  (malloyQuery: Malloy.Query) => {
238
- setQuery({ ...query, malloyQuery });
238
+ setQuery({ ...query, malloyQuery, malloyResult: undefined });
239
239
  },
240
240
  [query],
241
241
  );
@@ -243,88 +243,75 @@ function SourceExplorerComponentInner({
243
243
  if (oldSourceInfo !== sourceAndPath.sourceInfo.name) {
244
244
  return <div>Loading...</div>;
245
245
  }
246
-
247
246
  return (
248
- <StyledExplorerPage key={sourceAndPath.sourceInfo.name}>
249
- <StyledExplorerContent>
250
- <MalloyExplorerProvider
251
- source={sourceAndPath.sourceInfo}
252
- query={query?.malloyQuery}
253
- onQueryChange={onQueryChange}
254
- focusedNestViewPath={focusedNestViewPath}
255
- onFocusedNestViewPathChange={setFocusedNestViewPath}
256
- onDrill={(params) => {
257
- console.info(params);
247
+ <StyledExplorerContent key={sourceAndPath.sourceInfo.name}>
248
+ <MalloyExplorerProvider
249
+ source={sourceAndPath.sourceInfo}
250
+ query={query?.malloyQuery}
251
+ onQueryChange={onQueryChange}
252
+ focusedNestViewPath={focusedNestViewPath}
253
+ onFocusedNestViewPathChange={setFocusedNestViewPath}
254
+ onDrill={(params) => {
255
+ console.info(params);
256
+ }}
257
+ >
258
+ <div
259
+ style={{
260
+ display: "flex",
261
+ height: "100%",
262
+ overflowY: "auto",
258
263
  }}
259
264
  >
260
- <div
261
- style={{
262
- display: "flex",
263
- flexDirection: "column",
264
- height: "100%",
265
- }}
265
+ <ResizableCollapsiblePanel
266
+ isInitiallyExpanded={true}
267
+ initialWidth={180}
268
+ minWidth={180}
269
+ icon="database"
270
+ title={sourceAndPath.sourceInfo.name}
266
271
  >
267
- <div
268
- style={{
269
- display: "flex",
270
- height: "100%",
271
- overflowY: "auto",
272
+ <SourcePanel
273
+ onRefresh={() => setQuery(emptyQueryExplorerResult())}
274
+ />
275
+ </ResizableCollapsiblePanel>
276
+ <ResizableCollapsiblePanel
277
+ isInitiallyExpanded={true}
278
+ initialWidth={280}
279
+ minWidth={280}
280
+ icon="filterSliders"
281
+ title="Query"
282
+ >
283
+ <QueryPanel
284
+ runQuery={() => {
285
+ mutation.mutate();
272
286
  }}
273
- >
274
- <ResizableCollapsiblePanel
275
- isInitiallyExpanded={true}
276
- initialWidth={180}
277
- minWidth={180}
278
- icon="database"
279
- title={sourceAndPath.sourceInfo.name}
280
- >
281
- <SourcePanel
282
- onRefresh={() =>
283
- setQuery(emptyQueryExplorerResult())
284
- }
285
- />
286
- </ResizableCollapsiblePanel>
287
- <ResizableCollapsiblePanel
288
- isInitiallyExpanded={true}
289
- initialWidth={280}
290
- minWidth={280}
291
- icon="filterSliders"
292
- title="Query"
293
- >
294
- <QueryPanel
295
- runQuery={() => {
296
- mutation.mutate();
297
- }}
298
- />
299
- </ResizableCollapsiblePanel>
300
- <ResultPanel
301
- source={sourceAndPath.sourceInfo}
302
- draftQuery={query?.malloyQuery}
303
- setDraftQuery={(malloyQuery) =>
304
- setQuery({ ...query, malloyQuery: malloyQuery })
305
- }
306
- submittedQuery={
307
- query?.malloyQuery
308
- ? {
309
- executionState: mutation.isPending
310
- ? "running"
311
- : "finished",
312
- response: {
313
- result: query.malloyResult,
314
- },
315
- query: query.malloyQuery,
316
- queryResolutionStartMillis: Date.now(),
317
- onCancel: mutation.reset,
318
- }
319
- : undefined
320
- }
321
- options={{ showRawQuery: true }}
322
- />
323
- </div>
324
- </div>
325
- </MalloyExplorerProvider>
326
- </StyledExplorerContent>
327
- </StyledExplorerPage>
287
+ />
288
+ </ResizableCollapsiblePanel>
289
+ <ResultPanel
290
+ source={sourceAndPath.sourceInfo}
291
+ draftQuery={query?.malloyQuery}
292
+ setDraftQuery={(malloyQuery) =>
293
+ setQuery({ ...query, malloyQuery: malloyQuery })
294
+ }
295
+ submittedQuery={
296
+ query?.malloyQuery
297
+ ? {
298
+ executionState: mutation.isPending
299
+ ? "running"
300
+ : "finished",
301
+ response: {
302
+ result: query.malloyResult,
303
+ },
304
+ query: query.malloyQuery,
305
+ queryResolutionStartMillis: Date.now(),
306
+ onCancel: mutation.reset,
307
+ }
308
+ : undefined
309
+ }
310
+ options={{ showRawQuery: true }}
311
+ />
312
+ </div>
313
+ </MalloyExplorerProvider>
314
+ </StyledExplorerContent>
328
315
  );
329
316
  }
330
317
 
@@ -366,7 +353,6 @@ export function SourceExplorerComponent(props: SourceExplorerComponentProps) {
366
353
  <StyledExplorerContent>
367
354
  <div
368
355
  style={{
369
- display: "flex",
370
356
  alignItems: "center",
371
357
  justifyContent: "center",
372
358
  height: "200px",
@@ -1,4 +1,7 @@
1
1
  export { default as Model } from "./Model";
2
+ export { ModelExplorer } from "./ModelExplorer";
2
3
  export { SourcesExplorer } from "./SourcesExplorer";
3
4
  export { SourceExplorerComponent } from "./SourcesExplorer";
4
5
  export type { SourceAndPath } from "./SourcesExplorer";
6
+ export { useModelData } from "./useModelData";
7
+ export type { ModelExplorerProps } from "./ModelExplorer";
@@ -0,0 +1,38 @@
1
+ import { Configuration, ModelsApi, CompiledModel } from "../../client";
2
+ import { usePackage } from "../Package/PackageProvider";
3
+ import { useQueryWithApiError } from "../../hooks/useQueryWithApiError";
4
+
5
+ const modelsApi = new ModelsApi(new Configuration());
6
+
7
+ /**
8
+ * Custom hook for fetching model data. Combines usePackage context with
9
+ * useQueryWithApiError to fetch a compiled model.
10
+ */
11
+ export function useModelData(modelPath: string, versionId?: string) {
12
+ const {
13
+ projectName,
14
+ packageName,
15
+ versionId: packageVersionId,
16
+ } = usePackage();
17
+ const effectiveVersionId = versionId || packageVersionId;
18
+
19
+ return useQueryWithApiError<CompiledModel>({
20
+ queryKey: [
21
+ "package",
22
+ projectName,
23
+ packageName,
24
+ modelPath,
25
+ effectiveVersionId,
26
+ ],
27
+ queryFn: async (config) => {
28
+ const response = await modelsApi.getModel(
29
+ projectName,
30
+ packageName,
31
+ modelPath,
32
+ effectiveVersionId,
33
+ config,
34
+ );
35
+ return response.data;
36
+ },
37
+ });
38
+ }
@@ -18,8 +18,6 @@ export const StyledCardMedia = styled(CardMedia)({
18
18
  });
19
19
 
20
20
  export const StyledExplorerPage = styled("div")({
21
- display: "flex",
22
- flexDirection: "column",
23
21
  height: "100%",
24
22
  });
25
23
 
@@ -32,7 +30,6 @@ export const StyledExplorerBanner = styled("div")({
32
30
  });
33
31
 
34
32
  export const StyledExplorerContent = styled("div")({
35
- display: "flex",
36
33
  height: "75vh",
37
34
  width: "100%",
38
35
  overflowY: "auto",