@genspectrum/dashboard-components 0.10.2 → 0.10.4

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 (76) hide show
  1. package/README.md +19 -19
  2. package/custom-elements.json +377 -33
  3. package/dist/assets/mutationOverTimeWorker-BjjkMGzd.js.map +1 -0
  4. package/dist/components.d.ts +217 -59
  5. package/dist/components.js +1365 -177
  6. package/dist/components.js.map +1 -1
  7. package/dist/{dateRangeOption-du8H7LWu.js → dateRangeOption-Doo6WHKu.js} +17 -3
  8. package/dist/dateRangeOption-Doo6WHKu.js.map +1 -0
  9. package/dist/style.css +16 -6
  10. package/dist/util.d.ts +107 -41
  11. package/dist/util.js +1 -1
  12. package/package.json +10 -4
  13. package/src/preact/aggregatedData/aggregate.stories.tsx +14 -0
  14. package/src/preact/aggregatedData/aggregate.tsx +17 -15
  15. package/src/preact/components/error-boundary.stories.tsx +24 -3
  16. package/src/preact/components/error-boundary.tsx +3 -4
  17. package/src/preact/components/error-display.tsx +38 -17
  18. package/src/preact/components/tabs.tsx +2 -2
  19. package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +1 -1
  20. package/src/preact/lineageFilter/lineage-filter.stories.tsx +1 -1
  21. package/src/preact/locationFilter/location-filter.stories.tsx +1 -1
  22. package/src/preact/map/__mockData__/aggregatedGermany.json +83 -0
  23. package/src/preact/map/__mockData__/aggregatedWorld.json +259 -0
  24. package/src/preact/map/__mockData__/germanyMap.json +9083 -0
  25. package/src/preact/map/__mockData__/howToGenerateWorldMap.md +9 -0
  26. package/src/preact/map/__mockData__/worldAtlas.json +497127 -0
  27. package/src/preact/map/leafletStyleModifications.css +3 -0
  28. package/src/preact/map/sequences-by-location-map.tsx +202 -0
  29. package/src/preact/map/sequences-by-location-table.tsx +18 -0
  30. package/src/preact/map/sequences-by-location.stories.tsx +144 -0
  31. package/src/preact/map/sequences-by-location.tsx +151 -0
  32. package/src/preact/map/useGeoJsonMap.tsx +62 -0
  33. package/src/preact/mutationComparison/mutation-comparison.tsx +1 -1
  34. package/src/preact/mutationFilter/mutation-filter.tsx +26 -13
  35. package/src/preact/mutations/mutations.tsx +16 -12
  36. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +14 -0
  37. package/src/preact/mutationsOverTime/mutations-over-time.tsx +18 -14
  38. package/src/preact/numberSequencesOverTime/number-sequences-over-time.stories.tsx +14 -0
  39. package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +22 -14
  40. package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +14 -0
  41. package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +28 -19
  42. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +14 -0
  43. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +18 -15
  44. package/src/preact/shared/charts/confideceInterval.ts +10 -8
  45. package/src/preact/shared/charts/getYAxisMax.ts +10 -5
  46. package/src/preact/shared/stories/expectErrorMessage.ts +21 -0
  47. package/src/preact/statistic/statistics.tsx +10 -8
  48. package/src/preact/textInput/text-input.stories.tsx +14 -0
  49. package/src/preact/textInput/text-input.tsx +16 -11
  50. package/src/preact/useQuery.ts +9 -1
  51. package/src/query/queryAggregateData.ts +2 -1
  52. package/src/styles/tailwind.css +1 -1
  53. package/src/types.ts +13 -1
  54. package/src/utilEntrypoint.ts +7 -0
  55. package/src/web-components/app.stories.ts +17 -2
  56. package/src/web-components/app.ts +17 -5
  57. package/src/web-components/input/gs-mutation-filter.stories.ts +2 -0
  58. package/src/web-components/input/gs-text-input.tsx +2 -2
  59. package/src/web-components/introduction.mdx +4 -4
  60. package/src/web-components/visualization/data_visualization_statistical_analysis.mdx +3 -3
  61. package/src/web-components/visualization/gs-mutations-over-time.tsx +1 -3
  62. package/src/web-components/visualization/gs-mutations.tsx +1 -3
  63. package/src/web-components/visualization/gs-number-sequences-over-time.tsx +1 -3
  64. package/src/web-components/visualization/gs-prevalence-over-time.tsx +3 -6
  65. package/src/web-components/visualization/gs-relative-growth-advantage.tsx +1 -5
  66. package/src/web-components/visualization/gs-sequences-by-location.stories.ts +234 -0
  67. package/src/web-components/visualization/gs-sequences-by-location.tsx +253 -0
  68. package/src/web-components/visualization/index.ts +1 -0
  69. package/standalone-bundle/assets/mutationOverTimeWorker-DoUBht2e.js.map +1 -0
  70. package/standalone-bundle/dashboard-components.js +16223 -9292
  71. package/standalone-bundle/dashboard-components.js.map +1 -1
  72. package/standalone-bundle/style.css +1 -1
  73. package/dist/assets/mutationOverTimeWorker-Di6yP1e6.js.map +0 -1
  74. package/dist/dateRangeOption-du8H7LWu.js.map +0 -1
  75. package/src/preact/shared/stories/expectInvalidAttributesErrorMessage.ts +0 -13
  76. package/standalone-bundle/assets/mutationOverTimeWorker-cIyshfj_.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import { type Meta, type StoryObj } from '@storybook/preact';
2
2
  import { expect, waitFor, within } from '@storybook/test';
3
+ import z from 'zod';
3
4
 
4
5
  import { ErrorBoundary } from './error-boundary';
5
6
  import { UserFacingError } from './error-display';
@@ -21,9 +22,15 @@ const meta: Meta = {
21
22
 
22
23
  export default meta;
23
24
 
25
+ const someSchema = z.object({
26
+ test: z.string().min(1),
27
+ });
28
+ const someValidProps = { test: 'someValue' };
29
+ const someInvalidProps = { test: '' };
30
+
24
31
  export const ErrorBoundaryWithoutErrorStory: StoryObj = {
25
32
  render: (args) => (
26
- <ErrorBoundary size={args.size}>
33
+ <ErrorBoundary size={args.size} schema={someSchema} componentProps={someValidProps}>
27
34
  <div>Some content</div>
28
35
  </ErrorBoundary>
29
36
  ),
@@ -36,7 +43,7 @@ export const ErrorBoundaryWithoutErrorStory: StoryObj = {
36
43
 
37
44
  export const ErrorBoundaryWithErrorStory: StoryObj = {
38
45
  render: (args) => (
39
- <ErrorBoundary size={args.size}>
46
+ <ErrorBoundary size={args.size} schema={someSchema} componentProps={someValidProps}>
40
47
  <ContentThatThrowsError error={() => new Error('Some error')} />
41
48
  </ErrorBoundary>
42
49
  ),
@@ -48,9 +55,23 @@ export const ErrorBoundaryWithErrorStory: StoryObj = {
48
55
  },
49
56
  };
50
57
 
58
+ export const ErrorBoundaryWithParsingErrorStory: StoryObj = {
59
+ render: (args) => (
60
+ <ErrorBoundary size={args.size} schema={someSchema} componentProps={someInvalidProps}>
61
+ <ContentThatThrowsError error={() => new Error('Some error')} />
62
+ </ErrorBoundary>
63
+ ),
64
+ play: async ({ canvasElement }) => {
65
+ const canvas = within(canvasElement);
66
+ const content = canvas.queryByText('Some content.', { exact: false });
67
+ await waitFor(() => expect(content).not.toBeInTheDocument());
68
+ await waitFor(() => expect(canvas.getByText('Error - Invalid component attributes')).toBeInTheDocument());
69
+ },
70
+ };
71
+
51
72
  export const ErrorBoundaryWithUserFacingErrorStory: StoryObj = {
52
73
  render: (args) => (
53
- <ErrorBoundary size={args.size}>
74
+ <ErrorBoundary size={args.size} schema={someSchema} componentProps={someValidProps}>
54
75
  <ContentThatThrowsError error={() => new UserFacingError('Error Headline', 'Some error')} />
55
76
  </ErrorBoundary>
56
77
  ),
@@ -7,9 +7,9 @@ import { ResizeContainer, type Size } from './resize-container';
7
7
 
8
8
  type ErrorBoundaryProps<T> = {
9
9
  size: Size;
10
+ componentProps: T;
11
+ schema: ZodSchema<T>;
10
12
  layout?: ErrorDisplayProps['layout'];
11
- componentProps?: T;
12
- schema?: ZodSchema<T>;
13
13
  };
14
14
 
15
15
  export const ErrorBoundary = <T extends Record<string, unknown>>({
@@ -41,8 +41,7 @@ export const ErrorBoundary = <T extends Record<string, unknown>>({
41
41
  return <>{children}</>;
42
42
  };
43
43
 
44
- // TODO #554 - make both arguments required once all components validate their props
45
- function useCheckComponentProps<T extends Record<string, unknown>>(schema?: ZodSchema<T>, componentProps?: T) {
44
+ function useCheckComponentProps<T extends Record<string, unknown>>(schema: ZodSchema<T>, componentProps: T) {
46
45
  return useMemo(() => {
47
46
  if (schema === undefined || componentProps === undefined) {
48
47
  return undefined;
@@ -77,7 +77,7 @@ export const ErrorDisplay: FunctionComponent<ErrorDisplayProps> = ({ error, rese
77
77
  </button>
78
78
  </form>
79
79
  <h1 class='text-lg'>{details.headline}</h1>
80
- <p class='py-4'>{details.message}</p>
80
+ <div class='py-4'>{details.message}</div>
81
81
  </div>
82
82
  <form method='dialog' class='modal-backdrop'>
83
83
  <button>close</button>
@@ -129,26 +129,47 @@ function getDisplayedErrorMessage(error: Error) {
129
129
  }
130
130
 
131
131
  if (error instanceof InvalidPropsError) {
132
- const firstError = error.zodError.errors[0];
133
- let message = error.zodError.issues
134
- .map((issue) => {
135
- const actual =
136
- issue.path[0] in error.componentProps
137
- ? ` '${JSON.stringify(error.componentProps[issue.path[0]])}'`
138
- : '';
139
- return `Unexpected value${actual} for "${issue.path.join('.')}": ${issue.message}`;
140
- })
141
- .join(' - ');
142
-
143
- if (firstError.code === 'invalid_type' && firstError.received === 'null') {
144
- message = `Is the "${firstError.path[0]}" attribute in the HTML of the correct type? ${message}`;
145
- }
146
-
147
132
  return {
148
133
  headline: 'Error - Invalid component attributes',
149
- details: { headline: 'Invalid component attributes', message },
134
+ details: { headline: 'Invalid component attributes', message: <ZodErrorDetails error={error} /> },
150
135
  };
151
136
  }
152
137
 
153
138
  return { headline: 'Error', details: undefined };
154
139
  }
140
+
141
+ function ZodErrorDetails({ error }: { error: InvalidPropsError }) {
142
+ const firstError = error.zodError.errors[0];
143
+ return (
144
+ <>
145
+ <p>
146
+ <span className='font-bold'>You are a regular user?</span> Unfortunately, there is nothing you can do at
147
+ the moment. This component is misconfigured. Please contact the administrator of this page.
148
+ </p>
149
+ <p>
150
+ <span className='font-bold'>You are the administrator of this page?</span> You supplied invalid
151
+ attributes to this component. Please check the browser console for more detailed error messages.
152
+ </p>
153
+ {firstError.code === 'invalid_type' && firstError.received === 'null' && (
154
+ <p>
155
+ Is the "{firstError.path[0]}" attribute in the HTML of the correct type? The attribute is expected
156
+ to be of type "{firstError.expected}".
157
+ </p>
158
+ )}
159
+ <p>This is a summary of the unexpected attribute values:</p>
160
+ <ul class='m-4 list-outside list-disc '>
161
+ {error.zodError.issues.map((issue, index) => {
162
+ const actual =
163
+ issue.path[0] in error.componentProps
164
+ ? `'${JSON.stringify(error.componentProps[issue.path[0]])}'`
165
+ : '';
166
+ return (
167
+ <li key={index}>
168
+ Unexpected value {actual} for "{issue.path.join('.')}": {issue.message}
169
+ </li>
170
+ );
171
+ })}
172
+ </ul>
173
+ </>
174
+ );
175
+ }
@@ -13,7 +13,7 @@ interface ComponentTabsProps {
13
13
  }
14
14
 
15
15
  const Tabs: FunctionComponent<ComponentTabsProps> = ({ tabs, toolbar }) => {
16
- const [activeTab, setActiveTab] = useState(tabs[0].title);
16
+ const [activeTab, setActiveTab] = useState(tabs[0]?.title);
17
17
  const [heightOfTabs, setHeightOfTabs] = useState('3rem');
18
18
  const tabRef = useRef<HTMLDivElement>(null);
19
19
 
@@ -65,7 +65,7 @@ const Tabs: FunctionComponent<ComponentTabsProps> = ({ tabs, toolbar }) => {
65
65
  {toolbar && <div className='py-2 flex flex-wrap gap-y-1'>{toolbarElement}</div>}
66
66
  </div>
67
67
  <div
68
- className={`p-2 border-2 border-gray-100 rounded-b-md rounded-tr-md ${activeTab === tabs[0].title ? '' : 'rounded-tl-md'}`}
68
+ className={`p-2 border-2 border-gray-100 rounded-b-md rounded-tr-md ${activeTab === tabs[0]?.title ? '' : 'rounded-tl-md'}`}
69
69
  style={{ height: `calc(100% - ${heightOfTabs})` }}
70
70
  >
71
71
  {tabs.map((tab) => (
@@ -8,7 +8,7 @@ import { previewHandles } from '../../../.storybook/preview';
8
8
  import { LAPIS_URL } from '../../constants';
9
9
  import { LapisUrlContext } from '../LapisUrlContext';
10
10
  import { dateRangeOptionPresets } from './dateRangeOption';
11
- import { expectInvalidAttributesErrorMessage } from '../shared/stories/expectInvalidAttributesErrorMessage';
11
+ import { expectInvalidAttributesErrorMessage } from '../shared/stories/expectErrorMessage';
12
12
 
13
13
  const earliestDate = '1970-01-01';
14
14
 
@@ -5,7 +5,7 @@ import { previewHandles } from '../../../.storybook/preview';
5
5
  import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
6
6
  import aggregatedData from '../../preact/lineageFilter/__mockData__/aggregated.json';
7
7
  import { LapisUrlContext } from '../LapisUrlContext';
8
- import { expectInvalidAttributesErrorMessage } from '../shared/stories/expectInvalidAttributesErrorMessage';
8
+ import { expectInvalidAttributesErrorMessage } from '../shared/stories/expectErrorMessage';
9
9
 
10
10
  const meta: Meta = {
11
11
  title: 'Input/LineageFilter',
@@ -5,7 +5,7 @@ import { LocationFilter, type LocationFilterProps } from './location-filter';
5
5
  import { previewHandles } from '../../../.storybook/preview';
6
6
  import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
7
7
  import { LapisUrlContext } from '../LapisUrlContext';
8
- import { expectInvalidAttributesErrorMessage } from '../shared/stories/expectInvalidAttributesErrorMessage';
8
+ import { expectInvalidAttributesErrorMessage } from '../shared/stories/expectErrorMessage';
9
9
 
10
10
  const meta: Meta<LocationFilterProps> = {
11
11
  title: 'Input/LocationFilter',
@@ -0,0 +1,83 @@
1
+ {
2
+ "data": [
3
+ {
4
+ "count": 1748,
5
+ "division": "Mecklenburg-Vorpommern"
6
+ },
7
+ {
8
+ "count": 10343,
9
+ "division": "Rheinland-Pfalz"
10
+ },
11
+ {
12
+ "count": 25459,
13
+ "division": "Saxony"
14
+ },
15
+ {
16
+ "count": 6198,
17
+ "division": "Niedersachsen"
18
+ },
19
+ {
20
+ "count": 3846,
21
+ "division": "Berlin"
22
+ },
23
+ {
24
+ "count": 7,
25
+ "division": "Germany"
26
+ },
27
+ {
28
+ "count": 3778,
29
+ "division": "Brandenburg"
30
+ },
31
+ {
32
+ "count": 1672,
33
+ "division": "Bremen"
34
+ },
35
+ {
36
+ "count": 4969,
37
+ "division": "Hesse"
38
+ },
39
+ {
40
+ "count": 3238,
41
+ "division": "Thuringia"
42
+ },
43
+ {
44
+ "count": 4747,
45
+ "division": "Saxony-Anhalt"
46
+ },
47
+ {
48
+ "count": 109800,
49
+ "division": null
50
+ },
51
+ {
52
+ "count": 4911,
53
+ "division": "Saarland"
54
+ },
55
+ {
56
+ "count": 50650,
57
+ "division": "Baden-Wuerttemberg"
58
+ },
59
+ {
60
+ "count": 50102,
61
+ "division": "North Rhine Westphalia"
62
+ },
63
+ {
64
+ "count": 9832,
65
+ "division": "Schleswig-Holstein"
66
+ },
67
+ {
68
+ "count": 28546,
69
+ "division": "Bavaria"
70
+ },
71
+ {
72
+ "count": 9633,
73
+ "division": "Hamburg"
74
+ }
75
+ ],
76
+ "info": {
77
+ "dataVersion": "1733676577",
78
+ "requestId": "7517dbd2-10ed-4a5a-8b2b-a6e08de55982",
79
+ "requestInfo": "sars_cov-2_nextstrain_open on lapis.cov-spectrum.org at 2024-12-09T16:14:08.190102574",
80
+ "reportTo": "Please report to https://github.com/GenSpectrum/LAPIS/issues in case you encounter any unexpected issues. Please include the request ID and the requestInfo in your report.",
81
+ "lapisVersion": "0.3.10"
82
+ }
83
+ }
@@ -0,0 +1,259 @@
1
+ {
2
+ "data": [
3
+ {
4
+ "count": 3,
5
+ "country": "Turkey"
6
+ },
7
+ {
8
+ "count": 966167,
9
+ "country": "United Kingdom"
10
+ },
11
+ {
12
+ "count": 1,
13
+ "country": "Netherlands"
14
+ },
15
+ {
16
+ "count": 138004,
17
+ "country": "Denmark"
18
+ },
19
+ {
20
+ "count": 2611,
21
+ "country": "Brazil"
22
+ },
23
+ {
24
+ "count": 4,
25
+ "country": "Côte d'Ivoire"
26
+ },
27
+ {
28
+ "count": 4,
29
+ "country": "Finland"
30
+ },
31
+ {
32
+ "count": 204,
33
+ "country": "Djibouti"
34
+ },
35
+ {
36
+ "count": 138,
37
+ "country": "China"
38
+ },
39
+ {
40
+ "count": 5904,
41
+ "country": "Iceland"
42
+ },
43
+ {
44
+ "count": 8,
45
+ "country": "Ireland"
46
+ },
47
+ {
48
+ "count": 180,
49
+ "country": "Malaysia"
50
+ },
51
+ {
52
+ "count": 855,
53
+ "country": "Kenya"
54
+ },
55
+ {
56
+ "count": 868,
57
+ "country": "South Africa"
58
+ },
59
+ {
60
+ "count": 329479,
61
+ "country": "Germany"
62
+ },
63
+ {
64
+ "count": 5,
65
+ "country": "Spain"
66
+ },
67
+ {
68
+ "count": 664,
69
+ "country": "Liechtenstein"
70
+ },
71
+ {
72
+ "count": 2,
73
+ "country": "Chile"
74
+ },
75
+ {
76
+ "count": 2,
77
+ "country": "Mali"
78
+ },
79
+ {
80
+ "count": 31,
81
+ "country": "South Korea"
82
+ },
83
+ {
84
+ "count": 13540,
85
+ "country": "Slovakia"
86
+ },
87
+ {
88
+ "count": 1,
89
+ "country": "Norway"
90
+ },
91
+ {
92
+ "count": 4949,
93
+ "country": "New Zealand"
94
+ },
95
+ {
96
+ "count": 354,
97
+ "country": "Laos"
98
+ },
99
+ {
100
+ "count": 5,
101
+ "country": "Saudi Arabia"
102
+ },
103
+ {
104
+ "count": 566,
105
+ "country": "Vietnam"
106
+ },
107
+ {
108
+ "count": 37,
109
+ "country": "Australia"
110
+ },
111
+ {
112
+ "count": 25,
113
+ "country": "Cameroon"
114
+ },
115
+ {
116
+ "count": 54,
117
+ "country": "Gambia"
118
+ },
119
+ {
120
+ "count": 1310,
121
+ "country": "Argentina"
122
+ },
123
+ {
124
+ "count": 548918,
125
+ "country": "USA"
126
+ },
127
+ {
128
+ "count": 4562,
129
+ "country": "Mexico"
130
+ },
131
+ {
132
+ "count": 28636,
133
+ "country": "Switzerland"
134
+ },
135
+ {
136
+ "count": 9,
137
+ "country": "Palau"
138
+ },
139
+ {
140
+ "count": 6,
141
+ "country": "Taiwan"
142
+ },
143
+ {
144
+ "count": 181,
145
+ "country": "Iraq"
146
+ },
147
+ {
148
+ "count": 7547,
149
+ "country": "France"
150
+ },
151
+ {
152
+ "count": 4536,
153
+ "country": "Japan"
154
+ },
155
+ {
156
+ "count": 1640,
157
+ "country": "Thailand"
158
+ },
159
+ {
160
+ "count": 59,
161
+ "country": "Colombia"
162
+ },
163
+ {
164
+ "count": 243,
165
+ "country": "India"
166
+ },
167
+ {
168
+ "count": 132,
169
+ "country": "Italy"
170
+ },
171
+ {
172
+ "count": 92,
173
+ "country": "Mongolia"
174
+ },
175
+ {
176
+ "count": 2,
177
+ "country": "Libya"
178
+ },
179
+ {
180
+ "count": 216,
181
+ "country": "Peru"
182
+ },
183
+ {
184
+ "count": 4,
185
+ "country": "Singapore"
186
+ },
187
+ {
188
+ "count": 25,
189
+ "country": "Myanmar"
190
+ },
191
+ {
192
+ "count": 395,
193
+ "country": "Bangladesh"
194
+ },
195
+ {
196
+ "count": 37,
197
+ "country": "Malawi"
198
+ },
199
+ {
200
+ "count": 3669,
201
+ "country": "Bahrain"
202
+ },
203
+ {
204
+ "count": 171,
205
+ "country": "Haiti"
206
+ },
207
+ {
208
+ "count": 40,
209
+ "country": "Palestine"
210
+ },
211
+ {
212
+ "count": 499,
213
+ "country": "Hong Kong"
214
+ },
215
+ {
216
+ "count": 19,
217
+ "country": "Austria"
218
+ },
219
+ {
220
+ "count": 9,
221
+ "country": "Jamaica"
222
+ },
223
+ {
224
+ "count": 83,
225
+ "country": "Russia"
226
+ },
227
+ {
228
+ "count": 197,
229
+ "country": "Seychelles"
230
+ },
231
+ {
232
+ "count": 20,
233
+ "country": "Nigeria"
234
+ },
235
+ {
236
+ "count": 6,
237
+ "country": "Canada"
238
+ },
239
+ {
240
+ "count": 208,
241
+ "country": "Kazakhstan"
242
+ },
243
+ {
244
+ "count": 34,
245
+ "country": "Benin"
246
+ },
247
+ {
248
+ "count": 113,
249
+ "country": "Pakistan"
250
+ }
251
+ ],
252
+ "info": {
253
+ "dataVersion": "1733676577",
254
+ "requestId": "91268a09-b192-42da-b7c6-bcfc72bba982",
255
+ "requestInfo": "sars_cov-2_nextstrain_open on lapis.cov-spectrum.org at 2024-12-09T15:45:35.460317204",
256
+ "reportTo": "Please report to https://github.com/GenSpectrum/LAPIS/issues in case you encounter any unexpected issues. Please include the request ID and the requestInfo in your report.",
257
+ "lapisVersion": "0.3.10"
258
+ }
259
+ }