instantsearch.js 4.79.1 → 4.80.0

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.
@@ -60,7 +60,7 @@ var RawHtml = /*#__PURE__*/function (_Component) {
60
60
  });
61
61
  // if there is one TextNode first and one TextNode last, the
62
62
  // last one's nodeValue will be assigned to the first.
63
- if (this.nodes[0].nodeValue) {
63
+ if (this.nodes[0] && this.nodes[0].nodeValue) {
64
64
  this.nodes[0].nodeValue = '';
65
65
  }
66
66
  }
@@ -92,22 +92,20 @@ export default (function connectInfiniteHits(renderFn) {
92
92
  return Math.max.apply(Math, [page].concat(_toConsumableArray(pages)));
93
93
  }
94
94
  };
95
- var getShowPrevious = function getShowPrevious(helper) {
95
+ var getShowPrevious = function getShowPrevious(helper, getCachedHits) {
96
96
  return function () {
97
+ var cachedHits = getCachedHits();
97
98
  // Using the helper's `overrideStateWithoutTriggeringChangeEvent` method
98
99
  // avoid updating the browser URL when the user displays the previous page.
99
100
  helper.overrideStateWithoutTriggeringChangeEvent(_objectSpread(_objectSpread({}, helper.state), {}, {
100
- page: getFirstReceivedPage(helper.state, cache.read({
101
- state: normalizeState(helper.state)
102
- }) || {}) - 1
101
+ page: getFirstReceivedPage(helper.state, cachedHits) - 1
103
102
  })).searchWithoutTriggeringOnStateChange();
104
103
  };
105
104
  };
106
- var getShowMore = function getShowMore(helper) {
105
+ var getShowMore = function getShowMore(helper, getCachedHits) {
107
106
  return function () {
108
- helper.setPage(getLastReceivedPage(helper.state, cache.read({
109
- state: normalizeState(helper.state)
110
- }) || {}) + 1).search();
107
+ var cachedHits = getCachedHits();
108
+ helper.setPage(getLastReceivedPage(helper.state, cachedHits) + 1).search();
111
109
  };
112
110
  };
113
111
  return {
@@ -139,6 +137,12 @@ export default (function connectInfiniteHits(renderFn) {
139
137
  parent = _ref6.parent,
140
138
  existingState = _ref6.state,
141
139
  instantSearchInstance = _ref6.instantSearchInstance;
140
+ var getCacheHits = function getCacheHits() {
141
+ var state = parent.getPreviousState() || existingState;
142
+ return cache.read({
143
+ state: normalizeState(state)
144
+ }) || {};
145
+ };
142
146
  var isFirstPage;
143
147
  var currentPageHits = [];
144
148
  /**
@@ -147,13 +151,15 @@ export default (function connectInfiniteHits(renderFn) {
147
151
  * is loading.
148
152
  */
149
153
  var state = parent.getPreviousState() || existingState;
150
- var cachedHits = cache.read({
151
- state: normalizeState(state)
152
- }) || {};
154
+ var cachedHits = getCacheHits();
153
155
  var banner = results === null || results === void 0 ? void 0 : (_results$renderingCon = results.renderingContent) === null || _results$renderingCon === void 0 ? void 0 : (_results$renderingCon2 = _results$renderingCon.widgets) === null || _results$renderingCon2 === void 0 ? void 0 : (_results$renderingCon3 = _results$renderingCon2.banners) === null || _results$renderingCon3 === void 0 ? void 0 : _results$renderingCon3[0];
154
156
  if (!showPrevious) {
155
- showPrevious = getShowPrevious(helper);
156
- showMore = getShowMore(helper);
157
+ showPrevious = function showPrevious() {
158
+ return getShowPrevious(helper, getCacheHits)();
159
+ };
160
+ showMore = function showMore() {
161
+ return getShowMore(helper, getCacheHits)();
162
+ };
157
163
  }
158
164
  if (!sendEvent) {
159
165
  sendEvent = createSendEventForHits({
@@ -197,7 +197,7 @@ declare class InstantSearch<TUiState extends UiState = UiState, TRouteState = TU
197
197
  * Widgets can be added either before or after InstantSearch has started.
198
198
  * @param widgets The array of widgets to add to InstantSearch.
199
199
  */
200
- addWidgets(widgets: Array<Widget | IndexWidget>): this;
200
+ addWidgets(widgets: Array<Widget | IndexWidget | Widget[]>): this;
201
201
  /**
202
202
  * Removes a widget from the search instance.
203
203
  * @deprecated This method will still be supported in 4.x releases, but not further. It is replaced by `removeWidgets([widget])`
@@ -212,7 +212,7 @@ declare class InstantSearch<TUiState extends UiState = UiState, TRouteState = TU
212
212
  *
213
213
  * The widgets must implement a `dispose()` method to clear their states.
214
214
  */
215
- removeWidgets(widgets: Array<Widget | IndexWidget>): this;
215
+ removeWidgets(widgets: Array<Widget | IndexWidget | Widget[]>): this;
216
216
  /**
217
217
  * Ends the initialization of InstantSearch.js and triggers the
218
218
  * first search.
@@ -322,12 +322,9 @@ var InstantSearch = /*#__PURE__*/function (_EventEmitter) {
322
322
  if (!Array.isArray(widgets)) {
323
323
  throw new Error(withUsage('The `addWidgets` method expects an array of widgets. Please use `addWidget`.'));
324
324
  }
325
- if (widgets.some(function (widget) {
326
- return typeof widget.init !== 'function' && typeof widget.render !== 'function';
325
+ if (this.compositionID && widgets.some(function (w) {
326
+ return !Array.isArray(w) && isIndexWidget(w) && !w._isolated;
327
327
  })) {
328
- throw new Error(withUsage('The widget definition expects a `render` and/or an `init` method.'));
329
- }
330
- if (this.compositionID && widgets.some(isIndexWidget)) {
331
328
  throw new Error(withUsage('The `index` widget cannot be used with a composition-based InstantSearch implementation.'));
332
329
  }
333
330
  this.mainIndex.addWidgets(widgets);
@@ -360,11 +357,6 @@ var InstantSearch = /*#__PURE__*/function (_EventEmitter) {
360
357
  if (!Array.isArray(widgets)) {
361
358
  throw new Error(withUsage('The `removeWidgets` method expects an array of widgets. Please use `removeWidget`.'));
362
359
  }
363
- if (widgets.some(function (widget) {
364
- return typeof widget.dispose !== 'function';
365
- })) {
366
- throw new Error(withUsage('The widget definition expects a `dispose` method.'));
367
- }
368
360
  this.mainIndex.removeWidgets(widgets);
369
361
  return this;
370
362
  }
@@ -1,2 +1,2 @@
1
- declare const _default: "4.79.1";
1
+ declare const _default: "4.80.0";
2
2
  export default _default;
package/es/lib/version.js CHANGED
@@ -1 +1 @@
1
- export default '4.79.1';
1
+ export default '4.80.0';
@@ -1,7 +1,45 @@
1
1
  import type { InstantSearch, UiState, IndexUiState, Widget, ScopedResult, RecommendResponse } from '../../types';
2
2
  import type { AlgoliaSearchHelper as Helper, SearchParameters, SearchResults } from 'algoliasearch-helper';
3
3
  export type IndexWidgetParams = {
4
+ /**
5
+ * The index or composition id to target.
6
+ */
4
7
  indexName: string;
8
+ /**
9
+ * Id to use for the index if there are multiple indices with the same name.
10
+ * This will be used to create the URL and the render state.
11
+ */
12
+ indexId?: string;
13
+ /**
14
+ * If `true`, the index will not be merged with the main helper's state.
15
+ * This means that the index will not be part of the main search request.
16
+ *
17
+ * @default false
18
+ */
19
+ EXPERIMENTAL_isolated?: false;
20
+ } | {
21
+ /**
22
+ * If `true`, the index will not be merged with the main helper's state.
23
+ * This means that the index will not be part of the main search request.
24
+ *
25
+ * This option is EXPERIMENTAL, and implementation details may change in the future.
26
+ * Things that could change are:
27
+ * - which widgets get rendered when a change happens
28
+ * - whether the index searches automatically
29
+ * - whether the index is included in the URL / UiState
30
+ * - whether the index is included in server-side rendering
31
+ *
32
+ * @default false
33
+ */
34
+ EXPERIMENTAL_isolated: true;
35
+ /**
36
+ * The index or composition id to target.
37
+ */
38
+ indexName?: string;
39
+ /**
40
+ * Id to use for the index if there are multiple indices with the same name.
41
+ * This will be used to create the URL and the render state.
42
+ */
5
43
  indexId?: string;
6
44
  };
7
45
  export type IndexInitOptions = {
@@ -29,8 +67,8 @@ export type IndexWidget<TUiState extends UiState = UiState> = Omit<Widget<IndexW
29
67
  getParent: () => IndexWidget | null;
30
68
  getWidgets: () => Array<Widget | IndexWidget>;
31
69
  createURL: (nextState: SearchParameters | ((state: IndexUiState) => IndexUiState)) => string;
32
- addWidgets: (widgets: Array<Widget | IndexWidget>) => IndexWidget;
33
- removeWidgets: (widgets: Array<Widget | IndexWidget>) => IndexWidget;
70
+ addWidgets: (widgets: Array<Widget | IndexWidget | Widget[]>) => IndexWidget;
71
+ removeWidgets: (widgets: Array<Widget | IndexWidget | Widget[]>) => IndexWidget;
34
72
  init: (options: IndexInitOptions) => void;
35
73
  render: (options: IndexRenderOptions) => void;
36
74
  dispose: () => void;
@@ -53,6 +91,12 @@ export type IndexWidget<TUiState extends UiState = UiState> = Omit<Widget<IndexW
53
91
  * Can only be called after `init`.
54
92
  */
55
93
  setIndexUiState: (indexUiState: TUiState[string] | ((previousIndexUiState: TUiState[string]) => TUiState[string])) => void;
94
+ /**
95
+ * This index is isolated, meaning it will not be merged with the main
96
+ * helper's state.
97
+ * @private
98
+ */
99
+ _isolated: boolean;
56
100
  };
57
101
  declare const index: (widgetParams: IndexWidgetParams) => IndexWidget;
58
102
  export default index;
@@ -110,12 +110,18 @@ function resolveScopedResultsFromWidgets(widgets) {
110
110
  }, []);
111
111
  }
112
112
  var index = function index(widgetParams) {
113
- if (widgetParams === undefined || widgetParams.indexName === undefined) {
113
+ if (widgetParams === undefined || widgetParams.indexName === undefined && !widgetParams.EXPERIMENTAL_isolated) {
114
114
  throw new Error(withUsage('The `indexName` option is required.'));
115
115
  }
116
- var indexName = widgetParams.indexName,
116
+
117
+ // When isolated=true, we use an empty string as the default indexName.
118
+ // This is intentional: isolated indices do not require a real index name.
119
+ var _widgetParams$indexNa = widgetParams.indexName,
120
+ indexName = _widgetParams$indexNa === void 0 ? '' : _widgetParams$indexNa,
117
121
  _widgetParams$indexId = widgetParams.indexId,
118
- indexId = _widgetParams$indexId === void 0 ? indexName : _widgetParams$indexId;
122
+ indexId = _widgetParams$indexId === void 0 ? indexName : _widgetParams$indexId,
123
+ _widgetParams$EXPERIM = widgetParams.EXPERIMENTAL_isolated,
124
+ isolated = _widgetParams$EXPERIM === void 0 ? false : _widgetParams$EXPERIM;
119
125
  var localWidgets = [];
120
126
  var localUiState = {};
121
127
  var localInstantSearchInstance = null;
@@ -128,6 +134,7 @@ var index = function index(widgetParams) {
128
134
  return {
129
135
  $$type: 'ais.index',
130
136
  $$widgetType: 'ais.index',
137
+ _isolated: isolated,
131
138
  getIndexName: function getIndexName() {
132
139
  return indexName;
133
140
  },
@@ -179,7 +186,7 @@ var index = function index(widgetParams) {
179
186
  return resolveScopedResultsFromWidgets(widgetSiblings);
180
187
  },
181
188
  getParent: function getParent() {
182
- return localParent;
189
+ return isolated ? null : localParent;
183
190
  },
184
191
  createURL: function createURL(nextState) {
185
192
  if (typeof nextState === 'function') {
@@ -198,12 +205,15 @@ var index = function index(widgetParams) {
198
205
  if (!Array.isArray(widgets)) {
199
206
  throw new Error(withUsage('The `addWidgets` method expects an array of widgets.'));
200
207
  }
201
- if (widgets.some(function (widget) {
208
+ var flatWidgets = widgets.reduce(function (acc, w) {
209
+ return acc.concat(Array.isArray(w) ? w : [w]);
210
+ }, []);
211
+ if (flatWidgets.some(function (widget) {
202
212
  return typeof widget.init !== 'function' && typeof widget.render !== 'function';
203
213
  })) {
204
214
  throw new Error(withUsage('The widget definition expects a `render` and/or an `init` method.'));
205
215
  }
206
- widgets.forEach(function (widget) {
216
+ flatWidgets.forEach(function (widget) {
207
217
  if (isIndexWidget(widget)) {
208
218
  return;
209
219
  }
@@ -218,8 +228,8 @@ var index = function index(widgetParams) {
218
228
  }
219
229
  addWidgetId(widget);
220
230
  });
221
- localWidgets = localWidgets.concat(widgets);
222
- if (localInstantSearchInstance && Boolean(widgets.length)) {
231
+ localWidgets = localWidgets.concat(flatWidgets);
232
+ if (localInstantSearchInstance && Boolean(flatWidgets.length)) {
223
233
  privateHelperSetState(helper, {
224
234
  state: getLocalWidgetsSearchParameters(localWidgets, {
225
235
  uiState: localUiState,
@@ -235,7 +245,7 @@ var index = function index(widgetParams) {
235
245
  // We compute the render state before calling `init` in a separate loop
236
246
  // to construct the whole render state object that is then passed to
237
247
  // `init`.
238
- widgets.forEach(function (widget) {
248
+ flatWidgets.forEach(function (widget) {
239
249
  if (widget.getRenderState) {
240
250
  var renderState = widget.getRenderState(localInstantSearchInstance.renderState[_this.getIndexId()] || {}, createInitArgs(localInstantSearchInstance, _this, localInstantSearchInstance._initialUiState));
241
251
  storeRenderState({
@@ -245,12 +255,17 @@ var index = function index(widgetParams) {
245
255
  });
246
256
  }
247
257
  });
248
- widgets.forEach(function (widget) {
258
+ flatWidgets.forEach(function (widget) {
249
259
  if (widget.init) {
250
260
  widget.init(createInitArgs(localInstantSearchInstance, _this, localInstantSearchInstance._initialUiState));
251
261
  }
252
262
  });
253
- localInstantSearchInstance.scheduleSearch();
263
+ if (isolated) {
264
+ var _helper2;
265
+ (_helper2 = helper) === null || _helper2 === void 0 ? void 0 : _helper2.search();
266
+ } else {
267
+ localInstantSearchInstance.scheduleSearch();
268
+ }
254
269
  }
255
270
  return this;
256
271
  },
@@ -259,13 +274,16 @@ var index = function index(widgetParams) {
259
274
  if (!Array.isArray(widgets)) {
260
275
  throw new Error(withUsage('The `removeWidgets` method expects an array of widgets.'));
261
276
  }
262
- if (widgets.some(function (widget) {
277
+ var flatWidgets = widgets.reduce(function (acc, w) {
278
+ return acc.concat(Array.isArray(w) ? w : [w]);
279
+ }, []);
280
+ if (flatWidgets.some(function (widget) {
263
281
  return typeof widget.dispose !== 'function';
264
282
  })) {
265
283
  throw new Error(withUsage('The widget definition expects a `dispose` method.'));
266
284
  }
267
285
  localWidgets = localWidgets.filter(function (widget) {
268
- return widgets.indexOf(widget) === -1;
286
+ return flatWidgets.indexOf(widget) === -1;
269
287
  });
270
288
  localWidgets.forEach(function (widget) {
271
289
  if (isIndexWidget(widget)) {
@@ -281,8 +299,8 @@ var index = function index(widgetParams) {
281
299
  hasSearchWidget = true;
282
300
  }
283
301
  });
284
- if (localInstantSearchInstance && Boolean(widgets.length)) {
285
- var _widgets$reduce = widgets.reduce(function (states, widget) {
302
+ if (localInstantSearchInstance && Boolean(flatWidgets.length)) {
303
+ var _flatWidgets$reduce = flatWidgets.reduce(function (states, widget) {
286
304
  // the `dispose` method exists at this point we already assert it
287
305
  var next = widget.dispose({
288
306
  helper: helper,
@@ -300,8 +318,8 @@ var index = function index(widgetParams) {
300
318
  cleanedSearchState: helper.state,
301
319
  cleanedRecommendState: helper.recommendState
302
320
  }),
303
- cleanedSearchState = _widgets$reduce.cleanedSearchState,
304
- cleanedRecommendState = _widgets$reduce.cleanedRecommendState;
321
+ cleanedSearchState = _flatWidgets$reduce.cleanedSearchState,
322
+ cleanedRecommendState = _flatWidgets$reduce.cleanedRecommendState;
305
323
  var newState = localInstantSearchInstance.future.preserveSharedStateOnUnmount ? getLocalWidgetsSearchParameters(localWidgets, {
306
324
  uiState: localUiState,
307
325
  initialSearchParameters: new algoliasearchHelper.SearchParameters({
@@ -321,7 +339,12 @@ var index = function index(widgetParams) {
321
339
  helper.setState(newState);
322
340
  helper.recommendState = cleanedRecommendState;
323
341
  if (localWidgets.length) {
324
- localInstantSearchInstance.scheduleSearch();
342
+ if (isolated) {
343
+ var _helper3;
344
+ (_helper3 = helper) === null || _helper3 === void 0 ? void 0 : _helper3.search();
345
+ } else {
346
+ localInstantSearchInstance.scheduleSearch();
347
+ }
325
348
  }
326
349
  }
327
350
  return this;
@@ -359,13 +382,20 @@ var index = function index(widgetParams) {
359
382
  // This Helper is only used for state management we do not care about the
360
383
  // `searchClient`. Only the "main" Helper created at the `InstantSearch`
361
384
  // level is aware of the client.
362
- helper = algoliasearchHelper({}, parameters.index, parameters);
385
+ helper = algoliasearchHelper(mainHelper.getClient(), parameters.index, parameters);
363
386
  helper.recommendState = recommendParameters;
364
387
 
365
388
  // We forward the call to `search` to the "main" instance of the Helper
366
389
  // which is responsible for managing the queries (it's the only one that is
367
390
  // aware of the `searchClient`).
368
391
  helper.search = function () {
392
+ if (isolated) {
393
+ instantSearchInstance.status = 'loading';
394
+ _this3.render({
395
+ instantSearchInstance: instantSearchInstance
396
+ });
397
+ return instantSearchInstance.compositionID ? helper.searchWithComposition() : helper.searchOnlyWithDerivedHelpers();
398
+ }
369
399
  if (instantSearchInstance.onStateChange) {
370
400
  instantSearchInstance.onStateChange({
371
401
  uiState: instantSearchInstance.mainIndex.getWidgetUiState({}),
@@ -389,7 +419,9 @@ var index = function index(widgetParams) {
389
419
  var state = helper.state.setQueryParameters(userState);
390
420
  return mainHelper.searchForFacetValues(facetName, facetValue, maxFacetHits, state);
391
421
  };
392
- derivedHelper = mainHelper.derive(function () {
422
+ var isolatedHelper = indexName ? helper : algoliasearchHelper({}, '__empty_index__', {});
423
+ var derivingHelper = isolated ? isolatedHelper : nearestIsolatedHelper(parent, mainHelper);
424
+ derivedHelper = derivingHelper.derive(function () {
393
425
  return mergeSearchParameters.apply(void 0, [mainHelper.state].concat(_toConsumableArray(resolveSearchParameters(_this3))));
394
426
  }, function () {
395
427
  return _this3.getHelper().recommendState;
@@ -531,7 +563,9 @@ var index = function index(widgetParams) {
531
563
 
532
564
  // We only render index widgets if there are no results.
533
565
  // This makes sure `render` is never called with `results` being `null`.
534
- var widgetsToRender = this.getResults() || (_derivedHelper2 = derivedHelper) !== null && _derivedHelper2 !== void 0 && _derivedHelper2.lastRecommendResults ? localWidgets : localWidgets.filter(isIndexWidget);
566
+ // If it's an isolated index without an index name, we render all widgets,
567
+ // as there are no results to display for the isolated index itself.
568
+ var widgetsToRender = this.getResults() || (_derivedHelper2 = derivedHelper) !== null && _derivedHelper2 !== void 0 && _derivedHelper2.lastRecommendResults || isolated && !indexName ? localWidgets : localWidgets.filter(isIndexWidget);
535
569
  widgetsToRender = widgetsToRender.filter(function (widget) {
536
570
  if (!widget.shouldRender) {
537
571
  return true;
@@ -565,7 +599,7 @@ var index = function index(widgetParams) {
565
599
  },
566
600
  dispose: function dispose() {
567
601
  var _this5 = this,
568
- _helper2,
602
+ _helper4,
569
603
  _derivedHelper3;
570
604
  localWidgets.forEach(function (widget) {
571
605
  if (widget.dispose && helper) {
@@ -585,13 +619,15 @@ var index = function index(widgetParams) {
585
619
  });
586
620
  localInstantSearchInstance = null;
587
621
  localParent = null;
588
- (_helper2 = helper) === null || _helper2 === void 0 ? void 0 : _helper2.removeAllListeners();
622
+ (_helper4 = helper) === null || _helper4 === void 0 ? void 0 : _helper4.removeAllListeners();
589
623
  helper = null;
590
624
  (_derivedHelper3 = derivedHelper) === null || _derivedHelper3 === void 0 ? void 0 : _derivedHelper3.detach();
591
625
  derivedHelper = null;
592
626
  },
593
627
  getWidgetUiState: function getWidgetUiState(uiState) {
594
- return localWidgets.filter(isIndexWidget).reduce(function (previousUiState, innerIndex) {
628
+ return localWidgets.filter(isIndexWidget).filter(function (w) {
629
+ return !w._isolated;
630
+ }).reduce(function (previousUiState, innerIndex) {
595
631
  return innerIndex.getWidgetUiState(previousUiState);
596
632
  }, _objectSpread(_objectSpread({}, uiState), {}, _defineProperty({}, indexId, _objectSpread(_objectSpread({}, uiState[indexId]), localUiState))));
597
633
  },
@@ -627,4 +663,17 @@ function storeRenderState(_ref8) {
627
663
  parent = _ref8.parent;
628
664
  var parentIndexName = parent ? parent.getIndexId() : instantSearchInstance.mainIndex.getIndexId();
629
665
  instantSearchInstance.renderState = _objectSpread(_objectSpread({}, instantSearchInstance.renderState), {}, _defineProperty({}, parentIndexName, _objectSpread(_objectSpread({}, instantSearchInstance.renderState[parentIndexName]), renderState)));
666
+ }
667
+
668
+ /**
669
+ * Walk up the parent chain to find the closest isolated index, or fall back to mainHelper
670
+ */
671
+ function nearestIsolatedHelper(current, mainHelper) {
672
+ while (current) {
673
+ if (current._isolated) {
674
+ return current.getHelper();
675
+ }
676
+ current = current.getParent();
677
+ }
678
+ return mainHelper;
630
679
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instantsearch.js",
3
- "version": "4.79.1",
3
+ "version": "4.80.0",
4
4
  "description": "InstantSearch.js is a JavaScript library for building performant and instant search experiences with Algolia.",
5
5
  "homepage": "https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/js/",
6
6
  "types": "es/index.d.ts",
@@ -56,9 +56,9 @@
56
56
  "watch:es": "yarn --silent build:es:base --watch"
57
57
  },
58
58
  "devDependencies": {
59
- "@instantsearch/mocks": "1.76.0",
60
- "@instantsearch/tests": "1.76.0",
61
- "@instantsearch/testutils": "1.65.0",
59
+ "@instantsearch/mocks": "*",
60
+ "@instantsearch/tests": "*",
61
+ "@instantsearch/testutils": "*",
62
62
  "@storybook/html": "5.3.9",
63
63
  "@types/scriptjs": "0.0.2",
64
64
  "algoliasearch": "5.1.1",
@@ -66,5 +66,5 @@
66
66
  "scriptjs": "2.5.9",
67
67
  "webpack": "4.47.0"
68
68
  },
69
- "gitHead": "d0486032831c0b2ee22169aa1f5052b88b9543b0"
69
+ "gitHead": "6e5bbe12332befe4f609b1540ab997827fd88f88"
70
70
  }