@malloy-publisher/sdk 0.0.205 → 0.0.207

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 (31) hide show
  1. package/dist/ServerProvider-UAgM1ojG.cjs.js +1 -0
  2. package/dist/{ServerProvider-CV37_sr_.es.js → ServerProvider-jpXNccQX.es.js} +919 -1311
  3. package/dist/client/api.d.ts +356 -416
  4. package/dist/client/index.cjs.js +1 -1
  5. package/dist/client/index.es.js +53 -55
  6. package/dist/components/Materializations/CreateMaterializationDialog.d.ts +0 -1
  7. package/dist/components/Materializations/ManifestView.d.ts +6 -7
  8. package/dist/components/Materializations/utils.d.ts +20 -16
  9. package/dist/components/ServerProvider.d.ts +1 -2
  10. package/dist/components/given/GivenInput.d.ts +13 -8
  11. package/dist/{core-C2sQrwVu.es.js → core-AOmIKwkc.es.js} +1 -1
  12. package/dist/{core-BWQQVODR.cjs.js → core-D1ZybzhJ.cjs.js} +1 -1
  13. package/dist/hooks/useGivensForm.d.ts +0 -2
  14. package/dist/{index-Dky6W6d1.cjs.js → index-Di675Z82.cjs.js} +77 -77
  15. package/dist/{index-CBRWGN55.es.js → index-eoQZlIjY.es.js} +8184 -8157
  16. package/dist/index.cjs.js +1 -1
  17. package/dist/index.es.js +2 -2
  18. package/package.json +1 -1
  19. package/src/components/Materializations/CreateMaterializationDialog.tsx +6 -20
  20. package/src/components/Materializations/ManifestView.tsx +29 -46
  21. package/src/components/Materializations/MaterializationDetailDialog.tsx +104 -74
  22. package/src/components/Materializations/MaterializationRunsList.tsx +2 -1
  23. package/src/components/Materializations/Materializations.tsx +13 -85
  24. package/src/components/Materializations/utils.ts +60 -27
  25. package/src/components/Model/Model.tsx +39 -0
  26. package/src/components/Notebook/Notebook.tsx +22 -9
  27. package/src/components/Package/Package.tsx +1 -1
  28. package/src/components/ServerProvider.tsx +0 -2
  29. package/src/components/given/GivenInput.tsx +54 -31
  30. package/src/hooks/useGivensForm.ts +0 -12
  31. package/dist/ServerProvider-Ci6NBOLe.cjs.js +0 -1
package/dist/index.cjs.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-Dky6W6d1.cjs.js"),o=require("./ServerProvider-Ci6NBOLe.cjs.js");exports.AnalyzePackageButton=e.AnalyzePackageButton;exports.BrowserWorkbookStorage=e.BrowserWorkbookStorage;exports.ConnectionExplorer=e.ConnectionExplorer;exports.DimensionFilter=e.DimensionFilter;exports.EmbeddedQueryResult=e.EmbeddedQueryResult;exports.Environment=e.Environment;exports.Home=e.Home;exports.Loading=e.Loading;exports.Materializations=e.Materializations;exports.Model=e.Model;exports.ModelExplorer=e.ModelExplorer;exports.ModelExplorerDialog=e.ModelExplorerDialog;exports.Models=e.Models;exports.Notebook=e.Notebook;exports.Package=e.Package;exports.Packages=e.Packages;exports.PageViewer=e.PageViewer;exports.QueryResult=e.QueryResult;exports.RenderedResult=e.RenderedResult;exports.ResultContainer=e.ResultContainer;exports.SourceExplorerComponent=e.SourceExplorerComponent;exports.SourcesExplorer=e.SourcesExplorer;exports.Workbook=e.Workbook;exports.WorkbookList=e.WorkbookList;exports.WorkbookManager=e.WorkbookManager;exports.WorkbookStorageProvider=e.WorkbookStorageProvider;exports.createEmbeddedQueryResult=e.createEmbeddedQueryResult;exports.encodeResourceUri=e.encodeResourceUri;exports.extractDimensionSpecs=e.extractDimensionSpecs;exports.extractSourceFromQuery=e.extractSourceFromQuery;exports.generateFilterClause=e.generateFilterClause;exports.getDimensionKey=e.getDimensionKey;exports.getJoinedSources=e.getJoinedSources;exports.injectWhereClause=e.injectWhereClause;exports.makeDimensionKey=e.makeDimensionKey;exports.parseAllSourceInfos=e.parseAllSourceInfos;exports.parseDimensionFilterAnnotation=e.parseDimensionFilterAnnotation;exports.parseNotebookFilterAnnotation=e.parseNotebookFilterAnnotation;exports.parseResourceUri=e.parseResourceUri;exports.useDimensionFilters=e.useDimensionFilters;exports.useDimensionFiltersFromSpec=e.useDimensionFiltersFromSpec;exports.useDimensionFiltersQuery=e.useDimensionFiltersQuery;exports.useDimensionalFilterRangeData=e.useDimensionalFilterRangeData;exports.useGivensForm=e.useGivensForm;exports.useModelData=e.useModelData;exports.useModelGivens=e.useModelGivens;exports.useRawQueryData=e.useRawQueryData;exports.useRouterClickHandler=e.useRouterClickHandler;exports.ServerProvider=o.ServerProvider;exports.useServer=o.useServer;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-Di675Z82.cjs.js"),o=require("./ServerProvider-UAgM1ojG.cjs.js");exports.AnalyzePackageButton=e.AnalyzePackageButton;exports.BrowserWorkbookStorage=e.BrowserWorkbookStorage;exports.ConnectionExplorer=e.ConnectionExplorer;exports.DimensionFilter=e.DimensionFilter;exports.EmbeddedQueryResult=e.EmbeddedQueryResult;exports.Environment=e.Environment;exports.Home=e.Home;exports.Loading=e.Loading;exports.Materializations=e.Materializations;exports.Model=e.Model;exports.ModelExplorer=e.ModelExplorer;exports.ModelExplorerDialog=e.ModelExplorerDialog;exports.Models=e.Models;exports.Notebook=e.Notebook;exports.Package=e.Package;exports.Packages=e.Packages;exports.PageViewer=e.PageViewer;exports.QueryResult=e.QueryResult;exports.RenderedResult=e.RenderedResult;exports.ResultContainer=e.ResultContainer;exports.SourceExplorerComponent=e.SourceExplorerComponent;exports.SourcesExplorer=e.SourcesExplorer;exports.Workbook=e.Workbook;exports.WorkbookList=e.WorkbookList;exports.WorkbookManager=e.WorkbookManager;exports.WorkbookStorageProvider=e.WorkbookStorageProvider;exports.createEmbeddedQueryResult=e.createEmbeddedQueryResult;exports.encodeResourceUri=e.encodeResourceUri;exports.extractDimensionSpecs=e.extractDimensionSpecs;exports.extractSourceFromQuery=e.extractSourceFromQuery;exports.generateFilterClause=e.generateFilterClause;exports.getDimensionKey=e.getDimensionKey;exports.getJoinedSources=e.getJoinedSources;exports.injectWhereClause=e.injectWhereClause;exports.makeDimensionKey=e.makeDimensionKey;exports.parseAllSourceInfos=e.parseAllSourceInfos;exports.parseDimensionFilterAnnotation=e.parseDimensionFilterAnnotation;exports.parseNotebookFilterAnnotation=e.parseNotebookFilterAnnotation;exports.parseResourceUri=e.parseResourceUri;exports.useDimensionFilters=e.useDimensionFilters;exports.useDimensionFiltersFromSpec=e.useDimensionFiltersFromSpec;exports.useDimensionFiltersQuery=e.useDimensionFiltersQuery;exports.useDimensionalFilterRangeData=e.useDimensionalFilterRangeData;exports.useGivensForm=e.useGivensForm;exports.useModelData=e.useModelData;exports.useModelGivens=e.useModelGivens;exports.useRawQueryData=e.useRawQueryData;exports.useRouterClickHandler=e.useRouterClickHandler;exports.ServerProvider=o.ServerProvider;exports.useServer=o.useServer;
package/dist/index.es.js CHANGED
@@ -1,5 +1,5 @@
1
- import { A as a, O as o, C as r, D as n, F as i, E as t, H as l, L as u, M as m, o as d, q as c, r as k, y as p, N as g, x as D, P as S, B as F, Q as R, R as x, I as y, t as b, S as E, W as M, J as v, T as C, K as P, G as Q, a2 as W, g as A, i as f, j as w, Z as B, k as G, l as H, _ as K, p as L, m as N, n as U, a1 as j, U as z, V as I, X as J, Y as V, a0 as h, v as q, $ as O, u as T, e as X } from "./index-CBRWGN55.es.js";
2
- import { S as Z, u as _ } from "./ServerProvider-CV37_sr_.es.js";
1
+ import { A as a, O as o, C as r, D as n, F as i, E as t, H as l, L as u, M as m, o as d, q as c, r as k, y as p, N as g, x as D, P as S, B as F, Q as R, R as x, I as y, t as b, S as E, W as M, J as v, T as C, K as P, G as Q, a2 as W, g as A, i as f, j as w, Z as B, k as G, l as H, _ as K, p as L, m as N, n as U, a1 as j, U as z, V as I, X as J, Y as V, a0 as h, v as q, $ as O, u as T, e as X } from "./index-eoQZlIjY.es.js";
2
+ import { S as Z, u as _ } from "./ServerProvider-jpXNccQX.es.js";
3
3
  export {
4
4
  a as AnalyzePackageButton,
5
5
  o as BrowserWorkbookStorage,
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.205",
4
+ "version": "0.0.207",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs.js",
7
7
  "module": "dist/index.es.js",
@@ -14,10 +14,7 @@ import {
14
14
  import { useState } from "react";
15
15
 
16
16
  type CreateMaterializationDialogProps = {
17
- onSubmit: (opts: {
18
- forceRefresh: boolean;
19
- autoLoadManifest: boolean;
20
- }) => Promise<unknown>;
17
+ onSubmit: (opts: { forceRefresh: boolean }) => Promise<unknown>;
21
18
  isSubmitting: boolean;
22
19
  disabled?: boolean;
23
20
  disabledReason?: string;
@@ -31,13 +28,12 @@ export default function CreateMaterializationDialog({
31
28
  }: CreateMaterializationDialogProps) {
32
29
  const [open, setOpen] = useState(false);
33
30
  const [forceRefresh, setForceRefresh] = useState(false);
34
- const [autoLoadManifest, setAutoLoadManifest] = useState(true);
35
31
 
36
32
  const handleClose = () => setOpen(false);
37
33
 
38
34
  const handleRun = async () => {
39
35
  try {
40
- await onSubmit({ forceRefresh, autoLoadManifest });
36
+ await onSubmit({ forceRefresh });
41
37
  setOpen(false);
42
38
  } catch {
43
39
  // The mutation surfaces the error through the caller's Snackbar;
@@ -79,8 +75,9 @@ export default function CreateMaterializationDialog({
79
75
  </DialogTitle>
80
76
  <DialogContent>
81
77
  <DialogContentText sx={{ mb: 2 }}>
82
- Build every persist source in this package and write the
83
- results to the configured connection.
78
+ Materialize every persist source in this package: compile,
79
+ build the tables, and load them so queries serve from the
80
+ materialized tables.
84
81
  </DialogContentText>
85
82
  <FormGroup>
86
83
  <FormControlLabel
@@ -94,17 +91,6 @@ export default function CreateMaterializationDialog({
94
91
  }
95
92
  label="Force refresh (rebuild even if unchanged)"
96
93
  />
97
- <FormControlLabel
98
- control={
99
- <Switch
100
- checked={autoLoadManifest}
101
- onChange={(event) =>
102
- setAutoLoadManifest(event.target.checked)
103
- }
104
- />
105
- }
106
- label="Auto-load manifest after build"
107
- />
108
94
  </FormGroup>
109
95
  </DialogContent>
110
96
  <DialogActions>
@@ -114,7 +100,7 @@ export default function CreateMaterializationDialog({
114
100
  loading={isSubmitting}
115
101
  onClick={handleRun}
116
102
  >
117
- Run
103
+ Materialize
118
104
  </Button>
119
105
  </DialogActions>
120
106
  </Dialog>
@@ -1,7 +1,5 @@
1
- import RefreshIcon from "@mui/icons-material/Refresh";
2
1
  import {
3
2
  Box,
4
- Button,
5
3
  Stack,
6
4
  Table,
7
5
  TableBody,
@@ -11,67 +9,42 @@ import {
11
9
  Typography,
12
10
  } from "@mui/material";
13
11
  import { ManifestEntry } from "../../client";
14
- import { ApiError, ApiErrorDisplay } from "../ApiErrorDisplay";
15
12
  import { MONO_FONT_FAMILY } from "../styles";
13
+ import { formatTimestamp } from "./utils";
16
14
 
17
15
  type ManifestViewProps = {
18
16
  entries: { [buildId: string]: ManifestEntry } | undefined;
19
- mutable: boolean;
20
- isReloading: boolean;
21
- isError?: boolean;
22
- error?: ApiError | null;
23
- onReload: () => void;
17
+ builtAt?: string;
24
18
  };
25
19
 
26
- export default function ManifestView({
27
- entries,
28
- mutable,
29
- isReloading,
30
- isError,
31
- error,
32
- onReload,
33
- }: ManifestViewProps) {
20
+ /**
21
+ * Read-only view of a materialization's build manifest: the physical tables
22
+ * produced from the build plan, carried on the materialization resource.
23
+ */
24
+ export default function ManifestView({ entries, builtAt }: ManifestViewProps) {
34
25
  const rows = Object.entries(entries ?? {});
35
26
 
36
27
  return (
37
28
  <Box>
38
29
  <Stack
39
30
  direction="row"
40
- alignItems="flex-start"
31
+ alignItems="baseline"
41
32
  justifyContent="space-between"
42
- sx={{ mb: 1 }}
43
33
  >
44
- <Box>
45
- <Typography
46
- variant="h6"
47
- sx={{ fontWeight: 600, letterSpacing: "-0.025em" }}
48
- >
49
- Build manifest
50
- </Typography>
51
- <Typography variant="body2" color="text.secondary">
52
- Tables produced by materializing this package
34
+ <Typography variant="subtitle2" gutterBottom>
35
+ Build manifest
36
+ </Typography>
37
+ {builtAt && (
38
+ <Typography variant="caption" color="text.secondary">
39
+ Built {formatTimestamp(builtAt)}
53
40
  </Typography>
54
- </Box>
55
- {mutable && (
56
- <Button
57
- size="small"
58
- startIcon={<RefreshIcon />}
59
- loading={isReloading}
60
- onClick={onReload}
61
- aria-label="Reload manifest"
62
- >
63
- Reload manifest
64
- </Button>
65
41
  )}
66
42
  </Stack>
67
-
68
- {isError ? (
69
- <ApiErrorDisplay error={error ?? null} context="Build manifest" />
70
- ) : rows.length === 0 ? (
43
+ {rows.length === 0 ? (
71
44
  <Typography
72
45
  variant="body2"
73
46
  color="text.secondary"
74
- sx={{ py: 1, fontStyle: "italic" }}
47
+ sx={{ fontStyle: "italic" }}
75
48
  >
76
49
  No materialized tables yet.
77
50
  </Typography>
@@ -79,18 +52,28 @@ export default function ManifestView({
79
52
  <Table size="small">
80
53
  <TableHead>
81
54
  <TableRow>
82
- <TableCell>Build ID</TableCell>
55
+ <TableCell>Source</TableCell>
83
56
  <TableCell>Table name</TableCell>
57
+ <TableCell>Connection</TableCell>
58
+ <TableCell>Realization</TableCell>
59
+ <TableCell align="right">Rows</TableCell>
84
60
  </TableRow>
85
61
  </TableHead>
86
62
  <TableBody>
87
63
  {rows.map(([buildId, entry]) => (
88
64
  <TableRow key={buildId}>
89
65
  <TableCell sx={{ fontFamily: MONO_FONT_FAMILY }}>
90
- {buildId}
66
+ {entry.sourceName ?? buildId}
91
67
  </TableCell>
92
68
  <TableCell sx={{ fontFamily: MONO_FONT_FAMILY }}>
93
- {entry.tableName ?? "-"}
69
+ {entry.physicalTableName ?? "-"}
70
+ </TableCell>
71
+ <TableCell sx={{ fontFamily: MONO_FONT_FAMILY }}>
72
+ {entry.connectionName ?? "-"}
73
+ </TableCell>
74
+ <TableCell>{entry.realization ?? "-"}</TableCell>
75
+ <TableCell align="right">
76
+ {entry.rowCount ?? "-"}
94
77
  </TableCell>
95
78
  </TableRow>
96
79
  ))}
@@ -16,11 +16,13 @@ import {
16
16
  } from "@mui/material";
17
17
  import { Materialization } from "../../client";
18
18
  import { MONO_FONT_FAMILY } from "../styles";
19
+ import ManifestView from "./ManifestView";
19
20
  import {
20
21
  formatDuration,
21
- formatRelativeTime,
22
+ formatTimestamp,
22
23
  parseMetadata,
23
24
  statusColor,
25
+ statusLabel,
24
26
  } from "./utils";
25
27
 
26
28
  type MaterializationDetailDialogProps = {
@@ -33,8 +35,7 @@ export default function MaterializationDetailDialog({
33
35
  onClose,
34
36
  }: MaterializationDetailDialogProps) {
35
37
  const meta = materialization ? parseMetadata(materialization) : {};
36
- const sources = meta.sources ?? [];
37
- const gcErrors = meta.gcErrors ?? [];
38
+ const planSources = Object.values(materialization?.buildPlan?.sources ?? {});
38
39
 
39
40
  return (
40
41
  <Dialog
@@ -50,7 +51,7 @@ export default function MaterializationDetailDialog({
50
51
  <Stack direction="row" alignItems="center" spacing={1.5}>
51
52
  <Chip
52
53
  size="small"
53
- label={materialization.status ?? "UNKNOWN"}
54
+ label={statusLabel(materialization.status)}
54
55
  color={statusColor(materialization.status)}
55
56
  />
56
57
  <Typography
@@ -69,14 +70,42 @@ export default function MaterializationDetailDialog({
69
70
  </IconButton>
70
71
  </DialogTitle>
71
72
  <DialogContent dividers>
72
- <Stack direction="row" spacing={4} sx={{ mb: 2 }}>
73
+ <Box
74
+ sx={{
75
+ display: "grid",
76
+ gridTemplateColumns:
77
+ "repeat(auto-fit, minmax(150px, 1fr))",
78
+ gap: 2,
79
+ mb: 3,
80
+ }}
81
+ >
82
+ <DetailField
83
+ label="Package"
84
+ value={materialization.packageName ?? "—"}
85
+ />
86
+ <DetailField
87
+ label="Mode"
88
+ value={
89
+ materialization.pauseBetweenPhases
90
+ ? "Two-round"
91
+ : "Auto-run"
92
+ }
93
+ />
94
+ <DetailField
95
+ label="Force refresh"
96
+ value={meta.forceRefresh ? "Yes" : "No"}
97
+ />
73
98
  <DetailField
74
99
  label="Started"
75
- value={formatRelativeTime(
100
+ value={formatTimestamp(
76
101
  materialization.startedAt ??
77
102
  materialization.createdAt,
78
103
  )}
79
104
  />
105
+ <DetailField
106
+ label="Completed"
107
+ value={formatTimestamp(materialization.completedAt)}
108
+ />
80
109
  <DetailField
81
110
  label="Duration"
82
111
  value={formatDuration(
@@ -92,10 +121,18 @@ export default function MaterializationDetailDialog({
92
121
  label="Sources skipped"
93
122
  value={`${meta.sourcesSkipped ?? 0}`}
94
123
  />
95
- </Stack>
124
+ <DetailField
125
+ label="Created"
126
+ value={formatTimestamp(materialization.createdAt)}
127
+ />
128
+ <DetailField
129
+ label="Updated"
130
+ value={formatTimestamp(materialization.updatedAt)}
131
+ />
132
+ </Box>
96
133
 
97
134
  {materialization.error && (
98
- <Box sx={{ mb: 2 }}>
135
+ <Box sx={{ mb: 3 }}>
99
136
  <Typography
100
137
  variant="subtitle2"
101
138
  color="error"
@@ -115,76 +152,69 @@ export default function MaterializationDetailDialog({
115
152
  </Box>
116
153
  )}
117
154
 
118
- <Typography variant="subtitle2" gutterBottom>
119
- Per-source results
120
- </Typography>
121
- {sources.length === 0 ? (
122
- <Typography
123
- variant="body2"
124
- color="text.secondary"
125
- sx={{ fontStyle: "italic" }}
126
- >
127
- No per-source results recorded.
155
+ <Box sx={{ mb: 3 }}>
156
+ <Typography variant="subtitle2" gutterBottom>
157
+ Build plan
128
158
  </Typography>
129
- ) : (
130
- <Table size="small">
131
- <TableHead>
132
- <TableRow>
133
- <TableCell>Source</TableCell>
134
- <TableCell>Table</TableCell>
135
- <TableCell>Status</TableCell>
136
- <TableCell align="right">Duration</TableCell>
137
- </TableRow>
138
- </TableHead>
139
- <TableBody>
140
- {sources.map((source, index) => (
141
- <TableRow
142
- key={
143
- source.buildId ?? source.sourceName ?? index
144
- }
145
- >
146
- <TableCell
147
- sx={{ fontFamily: MONO_FONT_FAMILY }}
148
- >
149
- {source.sourceName ?? "-"}
150
- </TableCell>
151
- <TableCell
152
- sx={{ fontFamily: MONO_FONT_FAMILY }}
153
- >
154
- {source.tableName ?? "-"}
155
- </TableCell>
156
- <TableCell>{source.status ?? "-"}</TableCell>
157
- <TableCell align="right">
158
- {source.durationMs !== undefined
159
- ? `${Math.round(source.durationMs)}ms`
160
- : "-"}
161
- </TableCell>
162
- </TableRow>
163
- ))}
164
- </TableBody>
165
- </Table>
166
- )}
167
-
168
- {gcErrors.length > 0 && (
169
- <Box sx={{ mt: 2 }}>
159
+ {planSources.length === 0 ? (
170
160
  <Typography
171
- variant="subtitle2"
172
- color="warning.main"
173
- gutterBottom
161
+ variant="body2"
162
+ color="text.secondary"
163
+ sx={{ fontStyle: "italic" }}
174
164
  >
175
- Garbage-collection errors
165
+ No build plan yet.
176
166
  </Typography>
177
- {gcErrors.map((gcError, index) => (
178
- <Typography
179
- key={index}
180
- variant="body2"
181
- sx={{ fontFamily: MONO_FONT_FAMILY }}
182
- >
183
- {gcError}
184
- </Typography>
185
- ))}
186
- </Box>
187
- )}
167
+ ) : (
168
+ <Table size="small">
169
+ <TableHead>
170
+ <TableRow>
171
+ <TableCell>Source</TableCell>
172
+ <TableCell>Connection</TableCell>
173
+ <TableCell>Dialect</TableCell>
174
+ <TableCell align="right">Columns</TableCell>
175
+ <TableCell>Build ID</TableCell>
176
+ </TableRow>
177
+ </TableHead>
178
+ <TableBody>
179
+ {planSources.map((source) => (
180
+ <TableRow key={source.sourceID}>
181
+ <TableCell
182
+ sx={{ fontFamily: MONO_FONT_FAMILY }}
183
+ >
184
+ {source.name}
185
+ </TableCell>
186
+ <TableCell
187
+ sx={{ fontFamily: MONO_FONT_FAMILY }}
188
+ >
189
+ {source.connectionName}
190
+ </TableCell>
191
+ <TableCell>
192
+ {source.dialect ?? "-"}
193
+ </TableCell>
194
+ <TableCell align="right">
195
+ {source.columns?.length ?? 0}
196
+ </TableCell>
197
+ <TableCell
198
+ sx={{
199
+ fontFamily: MONO_FONT_FAMILY,
200
+ fontSize: "0.75rem",
201
+ wordBreak: "break-all",
202
+ maxWidth: 220,
203
+ }}
204
+ >
205
+ {source.buildId}
206
+ </TableCell>
207
+ </TableRow>
208
+ ))}
209
+ </TableBody>
210
+ </Table>
211
+ )}
212
+ </Box>
213
+
214
+ <ManifestView
215
+ entries={materialization.manifest?.entries}
216
+ builtAt={materialization.manifest?.builtAt}
217
+ />
188
218
  </DialogContent>
189
219
  </>
190
220
  )}
@@ -27,6 +27,7 @@ import {
27
27
  isTerminalStatus,
28
28
  parseMetadata,
29
29
  statusColor,
30
+ statusLabel,
30
31
  } from "./utils";
31
32
 
32
33
  type MaterializationRunsListProps = {
@@ -141,7 +142,7 @@ function MaterializationRow({
141
142
  <Box sx={{ display: "flex", alignItems: "center", gap: 0.5 }}>
142
143
  <Chip
143
144
  size="small"
144
- label={materialization.status ?? "UNKNOWN"}
145
+ label={statusLabel(materialization.status)}
145
146
  color={statusColor(materialization.status)}
146
147
  variant={active ? "filled" : "outlined"}
147
148
  />
@@ -8,12 +8,8 @@ import {
8
8
  Typography,
9
9
  } from "@mui/material";
10
10
  import { useQueryClient } from "@tanstack/react-query";
11
- import React, { useEffect, useRef, useState } from "react";
12
- import {
13
- ManifestActionActionEnum,
14
- Materialization,
15
- MaterializationActionActionEnum,
16
- } from "../../client";
11
+ import React, { useState } from "react";
12
+ import { Materialization, MaterializationActionActionEnum } from "../../client";
17
13
  import {
18
14
  useMutationWithApiError,
19
15
  useQueryWithApiError,
@@ -23,7 +19,6 @@ import { ApiErrorDisplay } from "../ApiErrorDisplay";
23
19
  import { Loading } from "../Loading";
24
20
  import { useServer } from "../ServerProvider";
25
21
  import CreateMaterializationDialog from "./CreateMaterializationDialog";
26
- import ManifestView from "./ManifestView";
27
22
  import MaterializationDetailDialog from "./MaterializationDetailDialog";
28
23
  import MaterializationRunsList from "./MaterializationRunsList";
29
24
  import { isActiveStatus } from "./utils";
@@ -75,57 +70,28 @@ export default function Materializations({
75
70
  },
76
71
  });
77
72
 
78
- const manifestQuery = useQueryWithApiError({
79
- queryKey: ["manifest", environmentName, packageName],
80
- queryFn: () =>
81
- apiClients.manifests.getManifest(environmentName, packageName),
82
- });
83
-
84
73
  const invalidateList = () =>
85
74
  queryClient.invalidateQueries({
86
75
  queryKey: ["materializations", environmentName, packageName],
87
76
  });
88
- const invalidateManifest = () =>
89
- queryClient.invalidateQueries({
90
- queryKey: ["manifest", environmentName, packageName],
91
- });
92
77
 
78
+ // Auto-run: the publisher compiles, builds every persist source, and loads
79
+ // the resulting manifest in a single pass.
93
80
  const createMaterialization = useMutationWithApiError({
94
- mutationFn: async (opts: {
95
- forceRefresh: boolean;
96
- autoLoadManifest: boolean;
97
- }) => {
98
- const created =
99
- await apiClients.materializations.createMaterialization(
100
- environmentName,
101
- packageName,
102
- {
103
- forceRefresh: opts.forceRefresh,
104
- autoLoadManifest: opts.autoLoadManifest,
105
- },
106
- );
107
- const id = created.data?.id;
108
- if (!id) {
109
- throw new Error(
110
- "Server did not return a materialization id; nothing was started.",
111
- );
112
- }
113
- await apiClients.materializations.materializationAction(
81
+ mutationFn: (opts: { forceRefresh: boolean }) =>
82
+ apiClients.materializations.createMaterialization(
114
83
  environmentName,
115
84
  packageName,
116
- id,
117
- MaterializationActionActionEnum.Start,
118
- );
119
- return created;
120
- },
85
+ {
86
+ forceRefresh: opts.forceRefresh,
87
+ },
88
+ ),
121
89
  onSuccess() {
122
- setNotificationMessage("Materialization started");
90
+ setNotificationMessage("Materialization requested");
123
91
  invalidateList();
124
92
  },
125
93
  onError(error) {
126
94
  setNotificationMessage(error.message);
127
- // A create can succeed (a PENDING row now exists) before the start
128
- // action fails; refetch so that row surfaces and polling resumes.
129
95
  invalidateList();
130
96
  },
131
97
  });
@@ -163,22 +129,6 @@ export default function Materializations({
163
129
  },
164
130
  });
165
131
 
166
- const reloadManifest = useMutationWithApiError({
167
- mutationFn: () =>
168
- apiClients.manifests.manifestAction(
169
- environmentName,
170
- packageName,
171
- ManifestActionActionEnum.Reload,
172
- ),
173
- onSuccess() {
174
- setNotificationMessage("Manifest reloaded");
175
- invalidateManifest();
176
- },
177
- onError(error) {
178
- setNotificationMessage(error.message);
179
- },
180
- });
181
-
182
132
  const materializations = (listQuery.data?.data ?? []) as Materialization[];
183
133
  const selected =
184
134
  materializations.find((row) => row.id === selectedId) ?? null;
@@ -188,19 +138,6 @@ export default function Materializations({
188
138
  stopMaterialization.isPending ||
189
139
  deleteMaterialization.isPending;
190
140
 
191
- // When the last active run finishes, a build with autoLoadManifest enabled
192
- // may have written new manifest entries, so refetch the manifest once on
193
- // that active-to-terminal transition.
194
- const wasActive = useRef(hasActive);
195
- useEffect(() => {
196
- if (wasActive.current && !hasActive) {
197
- queryClient.invalidateQueries({
198
- queryKey: ["manifest", environmentName, packageName],
199
- });
200
- }
201
- wasActive.current = hasActive;
202
- }, [hasActive, queryClient, environmentName, packageName]);
203
-
204
141
  return (
205
142
  <Container
206
143
  maxWidth={false}
@@ -244,8 +181,8 @@ export default function Materializations({
244
181
  Materializations
245
182
  </Typography>
246
183
  <Typography variant="body2" color="text.secondary">
247
- Build persist sources for {packageName} and inspect the
248
- tables they produce
184
+ Materialize the persist sources in {packageName} into
185
+ tables and serve queries from them
249
186
  </Typography>
250
187
  </Box>
251
188
  {mutable && (
@@ -295,15 +232,6 @@ export default function Materializations({
295
232
  )}
296
233
  </Box>
297
234
 
298
- <ManifestView
299
- entries={manifestQuery.data?.data?.entries}
300
- mutable={mutable}
301
- isReloading={reloadManifest.isPending}
302
- isError={manifestQuery.isError}
303
- error={manifestQuery.error}
304
- onReload={() => reloadManifest.mutate()}
305
- />
306
-
307
235
  <MaterializationDetailDialog
308
236
  materialization={selected}
309
237
  onClose={() => setSelectedId(null)}