@malloy-publisher/sdk 0.0.43 → 0.0.46

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 (39) hide show
  1. package/dist/{RenderedResult-wM3HaFcq.js → RenderedResult-Bn1ONmkh.js} +2 -2
  2. package/dist/{RenderedResult-qA34vdbd.cjs → RenderedResult-eQGiBJrZ.cjs} +1 -1
  3. package/dist/components/Home/Home.d.ts +1 -2
  4. package/dist/components/Package/PackageProvider.d.ts +1 -3
  5. package/dist/components/Project/Project.d.ts +1 -3
  6. package/dist/components/ServerProvider.d.ts +12 -0
  7. package/dist/components/index.d.ts +2 -0
  8. package/dist/hooks/useQueryWithApiError.d.ts +4 -3
  9. package/dist/{index-nsXS-gBw.cjs → index-CRJL0zjn.cjs} +558 -573
  10. package/dist/{index-ClVlSQMk.js → index-GV49y9rz.js} +16580 -16687
  11. package/dist/index.cjs.js +1 -1
  12. package/dist/index.es.js +12 -10
  13. package/dist/{vendor-BKsYdkmG.js → vendor-Cr_5VY0T.js} +6361 -6352
  14. package/dist/{vendor-hHfbZ4ZT.cjs → vendor-CxEyd3N4.cjs} +120 -120
  15. package/package.json +1 -1
  16. package/src/components/Home/Home.tsx +3 -10
  17. package/src/components/Model/Model.tsx +6 -26
  18. package/src/components/Model/ModelCell.tsx +0 -16
  19. package/src/components/Model/NamedQueries.tsx +3 -10
  20. package/src/components/Model/SourcesExplorer.tsx +3 -10
  21. package/src/components/MutableNotebook/ModelPicker.tsx +4 -11
  22. package/src/components/MutableNotebook/MutableNotebook.tsx +4 -3
  23. package/src/components/Notebook/Notebook.tsx +6 -44
  24. package/src/components/Package/Config.tsx +10 -11
  25. package/src/components/Package/Connections.tsx +3 -10
  26. package/src/components/Package/Databases.tsx +9 -11
  27. package/src/components/Package/Models.tsx +4 -12
  28. package/src/components/Package/Notebooks.tsx +6 -15
  29. package/src/components/Package/Package.tsx +0 -1
  30. package/src/components/Package/PackageProvider.tsx +1 -7
  31. package/src/components/Package/Schedules.tsx +9 -11
  32. package/src/components/Project/About.tsx +3 -10
  33. package/src/components/Project/ConnectionExplorer.tsx +16 -36
  34. package/src/components/Project/Packages.tsx +3 -10
  35. package/src/components/Project/Project.tsx +1 -5
  36. package/src/components/QueryResult/QueryResult.tsx +3 -11
  37. package/src/components/ServerProvider.tsx +37 -0
  38. package/src/components/index.ts +2 -0
  39. package/src/hooks/useQueryWithApiError.ts +28 -5
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.43",
4
+ "version": "0.0.46",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs.js",
7
7
  "module": "dist/index.es.js",
@@ -7,22 +7,15 @@ import { useQueryWithApiError } from "../../hooks/useQueryWithApiError";
7
7
  const projectsApi = new ProjectsApi(new Configuration());
8
8
 
9
9
  interface HomeProps {
10
- server?: string;
11
10
  navigate?: (to: string, event?: React.MouseEvent) => void;
12
11
  }
13
12
 
14
- export default function Home({ server, navigate }: HomeProps) {
13
+ export default function Home({ navigate }: HomeProps) {
15
14
  const { data, isSuccess, isError, error } = useQueryWithApiError({
16
- queryKey: ["projects", server],
17
- queryFn: () =>
18
- projectsApi.listProjects({
19
- baseURL: server,
20
- withCredentials: true,
21
- }),
15
+ queryKey: ["projects"],
16
+ queryFn: (config) => projectsApi.listProjects(config),
22
17
  });
23
18
 
24
- console.log(JSON.stringify(data?.data, null, 2));
25
-
26
19
  if (isError) {
27
20
  return <ApiErrorDisplay error={error} context="Projects List" />;
28
21
  }
@@ -53,9 +53,8 @@ export default function Model({
53
53
  React.useState<string>();
54
54
  const [selectedTab, setSelectedTab] = React.useState(0);
55
55
 
56
- const { server, projectName, packageName, versionId, accessToken } =
57
- usePackage();
58
- const modelCodeSnippet = getModelCodeSnippet(server, packageName, modelPath);
56
+ const { projectName, packageName, versionId } = usePackage();
57
+ const modelCodeSnippet = getModelCodeSnippet(modelPath);
59
58
  useEffect(() => {
60
59
  highlight(modelCodeSnippet, "typescript").then((code) => {
61
60
  setHighlightedEmbedCode(code);
@@ -64,27 +63,14 @@ export default function Model({
64
63
 
65
64
  const { data, isError, isLoading, error } =
66
65
  useQueryWithApiError<CompiledModel>({
67
- queryKey: [
68
- "package",
69
- server,
70
- projectName,
71
- packageName,
72
- modelPath,
73
- versionId,
74
- ],
75
- queryFn: async () => {
66
+ queryKey: ["package", projectName, packageName, modelPath, versionId],
67
+ queryFn: async (config) => {
76
68
  const response = await modelsApi.getModel(
77
69
  projectName,
78
70
  packageName,
79
71
  modelPath,
80
72
  versionId,
81
- {
82
- baseURL: server,
83
- withCredentials: !accessToken,
84
- headers: {
85
- Authorization: accessToken && `Bearer ${accessToken}`,
86
- },
87
- },
73
+ config,
88
74
  );
89
75
  return response.data;
90
76
  },
@@ -254,14 +240,8 @@ export default function Model({
254
240
  );
255
241
  }
256
242
 
257
- function getModelCodeSnippet(
258
- server: string,
259
- packageName: string,
260
- modelPath: string,
261
- ): string {
243
+ function getModelCodeSnippet(modelPath: string): string {
262
244
  return `<Model
263
- server="${server}"
264
- packageName="${packageName}"
265
245
  modelPath="${modelPath}"
266
246
  accessToken={accessToken}
267
247
  />`;
@@ -17,7 +17,6 @@ import LinkOutlinedIcon from "@mui/icons-material/LinkOutlined";
17
17
  import ContentCopyIcon from "@mui/icons-material/ContentCopy";
18
18
  import { useEffect } from "react";
19
19
  import { highlight } from "../highlighter";
20
- import { usePackage } from "../Package/PackageProvider";
21
20
 
22
21
  const StyledCard = styled(Card)({
23
22
  display: "flex",
@@ -56,13 +55,7 @@ export function ModelCell({
56
55
  const [highlightedAnnotations, setHighlightedAnnotations] =
57
56
  React.useState<string>();
58
57
 
59
- const { server, projectName, packageName, versionId } = usePackage();
60
-
61
58
  const queryResultCodeSnippet = getQueryResultCodeSnippet(
62
- server,
63
- projectName,
64
- packageName,
65
- versionId,
66
59
  sourceName,
67
60
  queryName,
68
61
  );
@@ -219,19 +212,10 @@ export function ModelCell({
219
212
  }
220
213
 
221
214
  function getQueryResultCodeSnippet(
222
- server: string,
223
- projectName: string,
224
- packageName: string,
225
- versionId: string,
226
215
  sourceName: string,
227
216
  queryName: string,
228
217
  ): string {
229
218
  return `<QueryResult
230
- server="${server}"
231
- accessToken={accessToken}
232
- projectName="${projectName}"
233
- packageName="${packageName}"
234
- versionId="${versionId}"
235
219
  sourceName="${sourceName}"
236
220
  queryName="${queryName}"
237
221
  />`;
@@ -25,8 +25,7 @@ export default function NamedQueries({
25
25
  namedQueries,
26
26
  modelPath,
27
27
  }: NamedQueryProps) {
28
- const { server, projectName, packageName, versionId, accessToken } =
29
- usePackage();
28
+ const { projectName, packageName, versionId } = usePackage();
30
29
  const [namedQueryResults, setNamedQueryResults] = React.useState<
31
30
  Record<string, string>
32
31
  >({});
@@ -35,7 +34,7 @@ export default function NamedQueries({
35
34
  >({});
36
35
 
37
36
  const mutation = useMutationWithApiError({
38
- mutationFn: ({ query }: { query: Query }) => {
37
+ mutationFn: ({ query }: { query: Query }, config) => {
39
38
  const val = queryResultsApi.executeQuery(
40
39
  projectName,
41
40
  packageName,
@@ -44,13 +43,7 @@ export default function NamedQueries({
44
43
  undefined,
45
44
  query.name,
46
45
  versionId,
47
- {
48
- baseURL: server,
49
- withCredentials: !accessToken,
50
- headers: {
51
- Authorization: accessToken && `Bearer ${accessToken}`,
52
- },
53
- },
46
+ config,
54
47
  );
55
48
  return val;
56
49
  },
@@ -171,10 +171,9 @@ export function SourceExplorerComponent({
171
171
  onChange(query);
172
172
  }
173
173
  }, [onChange, query]);
174
- const { server, projectName, packageName, versionId, accessToken } =
175
- usePackage();
174
+ const { projectName, packageName, versionId } = usePackage();
176
175
  const mutation = useMutationWithApiError({
177
- mutationFn: () => {
176
+ mutationFn: (_, config) => {
178
177
  const malloy = new QueryBuilder.ASTQuery({
179
178
  source: sourceAndPath.sourceInfo,
180
179
  query: query?.malloyQuery,
@@ -192,13 +191,7 @@ export function SourceExplorerComponent({
192
191
  // sourceInfo.name,
193
192
  undefined,
194
193
  versionId,
195
- {
196
- baseURL: server,
197
- withCredentials: !accessToken,
198
- headers: {
199
- Authorization: accessToken && `Bearer ${accessToken}`,
200
- },
201
- },
194
+ config,
202
195
  );
203
196
  },
204
197
  onSuccess: (data) => {
@@ -31,18 +31,11 @@ export function ModelPicker({
31
31
  initialSelectedModels,
32
32
  onModelChange,
33
33
  }: ModelPickerProps) {
34
- const { server, projectName, packageName, versionId, accessToken } =
35
- usePackage();
34
+ const { projectName, packageName, versionId } = usePackage();
36
35
  const { data, isLoading, isSuccess, isError, error } = useQueryWithApiError({
37
- queryKey: ["models", server, projectName, packageName, versionId],
38
- queryFn: () =>
39
- modelsApi.listModels(projectName, packageName, versionId, {
40
- baseURL: server,
41
- withCredentials: !accessToken,
42
- headers: {
43
- Authorization: accessToken && `Bearer ${accessToken}`,
44
- },
45
- }),
36
+ queryKey: ["models", projectName, packageName, versionId],
37
+ queryFn: (config) =>
38
+ modelsApi.listModels(projectName, packageName, versionId, config),
46
39
  });
47
40
  const [selectedModels, setSelectedModels] = React.useState<string[]>(
48
41
  initialSelectedModels || [],
@@ -22,6 +22,7 @@ import { useRouterClickHandler } from "../click_helper";
22
22
  import { SourceAndPath } from "../Model/SourcesExplorer";
23
23
  import { NotebookManager } from "../NotebookManager";
24
24
  import { usePackage } from "../Package";
25
+ import { useServer } from "../ServerProvider";
25
26
  import { StyledCard, StyledCardContent, StyledCardMedia } from "../styles";
26
27
  import { MutableCell } from "./MutableCell";
27
28
  import { useNotebookStorage } from "./NotebookStorageProvider";
@@ -50,14 +51,14 @@ export default function MutableNotebook({
50
51
  hideEmbeddingIcons,
51
52
  }: MutableNotebookProps) {
52
53
  const navigate = useRouterClickHandler();
53
- const { server, projectName, packageName, versionId, accessToken } =
54
- usePackage();
54
+ const { projectName, packageName, versionId } = usePackage();
55
+ const { server, accessToken } = useServer();
56
+ const { notebookStorage, userContext } = useNotebookStorage();
55
57
  if (!projectName || !packageName) {
56
58
  throw new Error(
57
59
  "Project and package must be provided via PubliserPackageProvider",
58
60
  );
59
61
  }
60
- const { notebookStorage, userContext } = useNotebookStorage();
61
62
  if (!notebookStorage || !userContext) {
62
63
  throw new Error(
63
64
  "Notebook storage and user context must be provided via NotebookStorageProvider",
@@ -44,14 +44,8 @@ export default function Notebook({
44
44
  React.useState<boolean>(false);
45
45
  const [highlightedEmbedCode, setHighlightedEmbedCode] =
46
46
  React.useState<string>();
47
- const { server, projectName, packageName, accessToken, versionId } =
48
- usePackage();
49
- const notebookCodeSnippet = getNotebookCodeSnippet(
50
- server,
51
- packageName,
52
- notebookPath,
53
- true,
54
- );
47
+ const { projectName, packageName, versionId } = usePackage();
48
+ const notebookCodeSnippet = getNotebookCodeSnippet(notebookPath, true);
55
49
 
56
50
  useEffect(() => {
57
51
  highlight(notebookCodeSnippet, "typescript").then((code) => {
@@ -65,27 +59,14 @@ export default function Notebook({
65
59
  isError,
66
60
  error,
67
61
  } = useQueryWithApiError<CompiledNotebook>({
68
- queryKey: [
69
- "notebook",
70
- server,
71
- projectName,
72
- packageName,
73
- notebookPath,
74
- versionId,
75
- ],
76
- queryFn: async () => {
62
+ queryKey: ["notebook", projectName, packageName, notebookPath, versionId],
63
+ queryFn: async (config) => {
77
64
  const response = await notebooksApi.getNotebook(
78
65
  projectName,
79
66
  packageName,
80
67
  notebookPath,
81
68
  versionId,
82
- {
83
- baseURL: server,
84
- withCredentials: !accessToken,
85
- headers: {
86
- Authorization: accessToken && `Bearer ${accessToken}`,
87
- },
88
- },
69
+ config,
89
70
  );
90
71
  return response.data;
91
72
  },
@@ -177,9 +158,6 @@ export default function Notebook({
177
158
  cell={cell}
178
159
  notebookPath={notebookPath}
179
160
  queryResultCodeSnippet={getQueryResultCodeSnippet(
180
- server,
181
- projectName,
182
- packageName,
183
161
  notebookPath,
184
162
  cell.text,
185
163
  )}
@@ -209,18 +187,8 @@ export default function Notebook({
209
187
  );
210
188
  }
211
189
 
212
- function getQueryResultCodeSnippet(
213
- server: string,
214
- projectName: string,
215
- packageName: string,
216
- modelPath: string,
217
- query: string,
218
- ): string {
190
+ function getQueryResultCodeSnippet(modelPath: string, query: string): string {
219
191
  return `<QueryResult
220
- server="${server}"
221
- accessToken={accessToken}
222
- projectName="${projectName}"
223
- packageName="${packageName}"
224
192
  modelPath="${modelPath}"
225
193
  query="
226
194
  ${query}
@@ -229,17 +197,11 @@ function getQueryResultCodeSnippet(
229
197
  }
230
198
 
231
199
  function getNotebookCodeSnippet(
232
- server: string,
233
- packageName: string,
234
200
  notebookPath: string,
235
201
  expandedCodeCells: boolean,
236
202
  ): string {
237
203
  return `<Notebook
238
- server="${server}"
239
- packageName="${packageName}"
240
204
  notebookPath="${notebookPath}"
241
- versionId={versionId}
242
- accessToken={accessToken}
243
205
  expandCodeCells={${expandedCodeCells}}
244
206
  />`;
245
207
  }
@@ -17,19 +17,18 @@ import { useQueryWithApiError } from "../../hooks/useQueryWithApiError";
17
17
  const packagesApi = new PackagesApi(new Configuration());
18
18
 
19
19
  export default function Config() {
20
- const { server, projectName, packageName, versionId, accessToken } =
21
- usePackage();
20
+ const { projectName, packageName, versionId } = usePackage();
22
21
 
23
22
  const { data, isSuccess, isError, error } = useQueryWithApiError({
24
- queryKey: ["package", server, projectName, packageName, versionId],
25
- queryFn: () =>
26
- packagesApi.getPackage(projectName, packageName, versionId, false, {
27
- baseURL: server,
28
- withCredentials: !accessToken,
29
- headers: {
30
- Authorization: accessToken && `Bearer ${accessToken}`,
31
- },
32
- }),
23
+ queryKey: ["package", projectName, packageName, versionId],
24
+ queryFn: (config) =>
25
+ packagesApi.getPackage(
26
+ projectName,
27
+ packageName,
28
+ versionId,
29
+ false,
30
+ config,
31
+ ),
33
32
  });
34
33
 
35
34
  return (
@@ -31,18 +31,11 @@ function Connection({ connection }: { connection: ApiConnection }) {
31
31
  }
32
32
 
33
33
  export default function Connections() {
34
- const { server, projectName, accessToken } = usePackage();
34
+ const { projectName } = usePackage();
35
35
 
36
36
  const { data, isSuccess, isError, error } = useQueryWithApiError({
37
- queryKey: ["connections", server, projectName],
38
- queryFn: () =>
39
- connectionsApi.listConnections(projectName, {
40
- baseURL: server,
41
- withCredentials: !accessToken,
42
- headers: {
43
- Authorization: accessToken && `Bearer ${accessToken}`,
44
- },
45
- }),
37
+ queryKey: ["connections", projectName],
38
+ queryFn: (config) => connectionsApi.listConnections(projectName, config),
46
39
  });
47
40
 
48
41
  return (
@@ -23,8 +23,7 @@ import { useQueryWithApiError } from "../../hooks/useQueryWithApiError";
23
23
  const databasesApi = new DatabasesApi(new Configuration());
24
24
 
25
25
  export default function Databases() {
26
- const { server, projectName, packageName, versionId, accessToken } =
27
- usePackage();
26
+ const { projectName, packageName, versionId } = usePackage();
28
27
 
29
28
  const [open, setOpen] = React.useState(false);
30
29
  const [selectedDatabase, setSelectedDatabase] =
@@ -41,15 +40,14 @@ export default function Databases() {
41
40
  };
42
41
 
43
42
  const { data, isError, error, isSuccess } = useQueryWithApiError({
44
- queryKey: ["databases", server, projectName, packageName, versionId],
45
- queryFn: () =>
46
- databasesApi.listDatabases(projectName, packageName, versionId, {
47
- baseURL: server,
48
- withCredentials: !accessToken,
49
- headers: {
50
- Authorization: accessToken && `Bearer ${accessToken}`,
51
- },
52
- }),
43
+ queryKey: ["databases", projectName, packageName, versionId],
44
+ queryFn: (config) =>
45
+ databasesApi.listDatabases(
46
+ projectName,
47
+ packageName,
48
+ versionId,
49
+ config,
50
+ ),
53
51
  });
54
52
  const formatRowSize = (size: number) => {
55
53
  if (size >= 1024 * 1024 * 1024 * 1024) {
@@ -16,19 +16,11 @@ interface ModelsProps {
16
16
  }
17
17
 
18
18
  export default function Models({ navigate }: ModelsProps) {
19
- const { server, projectName, packageName, versionId, accessToken } =
20
- usePackage();
21
-
19
+ const { projectName, packageName, versionId } = usePackage();
22
20
  const { data, isError, error, isSuccess } = useQueryWithApiError({
23
- queryKey: ["models", server, projectName, packageName, versionId],
24
- queryFn: () =>
25
- modelsApi.listModels(projectName, packageName, versionId, {
26
- baseURL: server,
27
- withCredentials: !accessToken,
28
- headers: {
29
- Authorization: accessToken && `Bearer ${accessToken}`,
30
- },
31
- }),
21
+ queryKey: ["models", projectName, packageName, versionId],
22
+ queryFn: (config) =>
23
+ modelsApi.listModels(projectName, packageName, versionId, config),
32
24
  });
33
25
 
34
26
  return (
@@ -16,26 +16,17 @@ interface NotebooksProps {
16
16
  }
17
17
 
18
18
  export default function Notebooks({ navigate }: NotebooksProps) {
19
- const { server, projectName, packageName, versionId, accessToken } =
20
- usePackage();
19
+ const { projectName, packageName, versionId } = usePackage();
21
20
 
22
21
  const { data, isError, error, isSuccess } = useQueryWithApiError({
23
- queryKey: ["notebooks", server, projectName, packageName, versionId],
24
- queryFn: async () => {
25
- const response = await notebooksApi.listNotebooks(
22
+ queryKey: ["notebooks", projectName, packageName, versionId],
23
+ queryFn: (config) =>
24
+ notebooksApi.listNotebooks(
26
25
  projectName,
27
26
  packageName,
28
27
  versionId,
29
- {
30
- baseURL: server,
31
- withCredentials: !accessToken,
32
- headers: {
33
- Authorization: accessToken && `Bearer ${accessToken}`,
34
- },
35
- },
36
- );
37
- return response;
38
- },
28
+ config,
29
+ ),
39
30
  });
40
31
 
41
32
  return (
@@ -48,7 +48,6 @@ export default function Package({ navigate }: PackageProps) {
48
48
  <Grid size={{ xs: 12, md: 12 }}>
49
49
  <Notebook
50
50
  notebookPath={README_NOTEBOOK}
51
- expandCodeCells={false}
52
51
  hideEmbeddingIcons={true}
53
52
  />
54
53
  </Grid>
@@ -1,11 +1,9 @@
1
1
  import React, { createContext, useContext, ReactNode } from "react";
2
2
 
3
3
  export interface PackageContextProps {
4
- server?: string;
5
4
  projectName: string;
6
5
  packageName: string;
7
6
  versionId?: string;
8
- accessToken?: string;
9
7
  }
10
8
 
11
9
  const PackageContext = createContext<PackageContextProps | undefined>(
@@ -21,17 +19,13 @@ interface PackageProviderProps extends PackageContextProps {
21
19
  // that need it.
22
20
  // The package information is passed to the components via the usePackage hook.
23
21
  export const PackageProvider = ({
24
- server,
25
22
  projectName,
26
23
  packageName,
27
24
  versionId,
28
- accessToken,
29
25
  children,
30
26
  }: PackageProviderProps) => {
31
27
  return (
32
- <PackageContext.Provider
33
- value={{ server, projectName, packageName, versionId, accessToken }}
34
- >
28
+ <PackageContext.Provider value={{ projectName, packageName, versionId }}>
35
29
  {children}
36
30
  </PackageContext.Provider>
37
31
  );
@@ -20,19 +20,17 @@ import { useQueryWithApiError } from "../../hooks/useQueryWithApiError";
20
20
  const schedulesApi = new SchedulesApi(new Configuration());
21
21
 
22
22
  export default function Schedules() {
23
- const { server, projectName, packageName, versionId, accessToken } =
24
- usePackage();
23
+ const { projectName, packageName, versionId } = usePackage();
25
24
 
26
25
  const { data, isError, isLoading, error } = useQueryWithApiError({
27
- queryKey: ["schedules", server, projectName, packageName, versionId],
28
- queryFn: () =>
29
- schedulesApi.listSchedules(projectName, packageName, versionId, {
30
- baseURL: server,
31
- withCredentials: !accessToken,
32
- headers: {
33
- Authorization: accessToken && `Bearer ${accessToken}`,
34
- },
35
- }),
26
+ queryKey: ["schedules", projectName, packageName, versionId],
27
+ queryFn: (config) =>
28
+ schedulesApi.listSchedules(
29
+ projectName,
30
+ packageName,
31
+ versionId,
32
+ config,
33
+ ),
36
34
  });
37
35
 
38
36
  if (isLoading) {
@@ -10,18 +10,11 @@ import { useQueryWithApiError } from "../../hooks/useQueryWithApiError";
10
10
  const projectsApi = new ProjectsApi(new Configuration());
11
11
 
12
12
  export default function About() {
13
- const { server, projectName, accessToken } = useProject();
13
+ const { projectName } = useProject();
14
14
 
15
15
  const { data, isSuccess, isError, error } = useQueryWithApiError({
16
- queryKey: ["about", server, projectName],
17
- queryFn: () =>
18
- projectsApi.getProject(projectName, false, {
19
- baseURL: server,
20
- withCredentials: true,
21
- headers: {
22
- Authorization: accessToken && `Bearer ${accessToken}`,
23
- },
24
- }),
16
+ queryKey: ["about", projectName],
17
+ queryFn: (config) => projectsApi.getProject(projectName, false, config),
25
18
  });
26
19
 
27
20
  return (