@internetarchive/collection-browser 1.2.0 → 1.2.1-alpha.2

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.
@@ -107,6 +107,13 @@ export declare class CollectionBrowser extends LitElement implements InfiniteScr
107
107
  */
108
108
  private dataSource;
109
109
  private infiniteScroller;
110
+ private sessionIdGenPromise?;
111
+ /**
112
+ * Returns a promise resolving to a unique string that persists for the current browser session.
113
+ * Used in generating unique IDs for search requests, so that multiple requests coming from the
114
+ * same browser session can be identified.
115
+ */
116
+ private getSessionId;
110
117
  /**
111
118
  * Go to the given page of results
112
119
  *
@@ -268,6 +275,23 @@ export declare class CollectionBrowser extends LitElement implements InfiniteScr
268
275
  private initialQueryChangeHappened;
269
276
  private historyPopOccurred;
270
277
  private previousQueryKey?;
278
+ /**
279
+ * Internal property to store the `resolve` function for the most recent
280
+ * `initialSearchComplete` promise, allowing us to resolve it at the appropriate time.
281
+ */
282
+ private _initialSearchCompleteResolver;
283
+ /**
284
+ * Internal property to store the private value backing the `initialSearchComplete` getter.
285
+ */
286
+ private _initialSearchCompletePromise;
287
+ /**
288
+ * A Promise which, after each query change, resolves once the fetches for the initial
289
+ * search have completed. Waits for *both* the hits and aggregations fetches to finish.
290
+ *
291
+ * Ensure you await this component's `updateComplete` promise before awaiting this
292
+ * one, to ensure you do not await an obsolete promise from the previous update.
293
+ */
294
+ get initialSearchComplete(): Promise<boolean>;
271
295
  private handleQueryChange;
272
296
  private setupStateRestorationObserver;
273
297
  private boundNavigationHandler?;
@@ -276,6 +300,16 @@ export declare class CollectionBrowser extends LitElement implements InfiniteScr
276
300
  private persistState;
277
301
  private doInitialPageFetch;
278
302
  private emitSearchResultsLoadingChanged;
303
+ /**
304
+ * Produces a compact unique ID for a search request that can help with debugging
305
+ * on the backend by making related requests easier to trace through different services.
306
+ * (e.g., tying the hits/aggregations requests for the same page back to a single hash).
307
+ *
308
+ * @param params The search service parameters for the request
309
+ * @param kind The kind of request (hits-only, aggregations-only, or both)
310
+ * @returns A Promise resolving to the uid to apply to the request
311
+ */
312
+ private requestUID;
279
313
  /**
280
314
  * Constructs a search service FilterMap object from the combination of
281
315
  * all the currently-applied filters. This includes any facets, letter
@@ -17,6 +17,7 @@ import chevronIcon from './assets/img/icons/chevron';
17
17
  import './empty-placeholder';
18
18
  import { analyticsActions, analyticsCategories, } from './utils/analytics-events';
19
19
  import { srOnlyStyle } from './styles/sr-only';
20
+ import { sha1 } from './utils/sha1';
20
21
  let CollectionBrowser = class CollectionBrowser extends LitElement {
21
22
  constructor() {
22
23
  super(...arguments);
@@ -114,6 +115,12 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
114
115
  // so this keeps track of whether we've already set the initial query
115
116
  this.initialQueryChangeHappened = false;
116
117
  this.historyPopOccurred = false;
118
+ /**
119
+ * Internal property to store the private value backing the `initialSearchComplete` getter.
120
+ */
121
+ this._initialSearchCompletePromise = new Promise(res => {
122
+ this._initialSearchCompleteResolver = res;
123
+ });
117
124
  // this maps the query to the pages being fetched for that query
118
125
  this.pageFetchesInProgress = {};
119
126
  }
@@ -145,6 +152,32 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
145
152
  get estimatedTileCount() {
146
153
  return this.pagesToRender * this.pageSize;
147
154
  }
155
+ /**
156
+ * Returns a promise resolving to a unique string that persists for the current browser session.
157
+ * Used in generating unique IDs for search requests, so that multiple requests coming from the
158
+ * same browser session can be identified.
159
+ */
160
+ async getSessionId() {
161
+ try {
162
+ const storedSessionId = sessionStorage === null || sessionStorage === void 0 ? void 0 : sessionStorage.getItem('cb-session');
163
+ if (storedSessionId) {
164
+ return storedSessionId;
165
+ }
166
+ // If we enter this method a second time while a first session ID is already being generated,
167
+ // ensure we produce the same ID from both calls instead of generating another one.
168
+ if (this.sessionIdGenPromise) {
169
+ return this.sessionIdGenPromise;
170
+ }
171
+ this.sessionIdGenPromise = sha1(Math.random().toString());
172
+ const newSessionId = await this.sessionIdGenPromise;
173
+ sessionStorage === null || sessionStorage === void 0 ? void 0 : sessionStorage.setItem('cb-session', newSessionId);
174
+ return newSessionId;
175
+ }
176
+ catch (err) {
177
+ // Either we can't generate the hash or we're restricted from accessing sessionStorage
178
+ return '';
179
+ }
180
+ }
148
181
  /**
149
182
  * Go to the given page of results
150
183
  *
@@ -807,6 +840,16 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
807
840
  });
808
841
  this.dispatchEvent(event);
809
842
  }
843
+ /**
844
+ * A Promise which, after each query change, resolves once the fetches for the initial
845
+ * search have completed. Waits for *both* the hits and aggregations fetches to finish.
846
+ *
847
+ * Ensure you await this component's `updateComplete` promise before awaiting this
848
+ * one, to ensure you do not await an obsolete promise from the previous update.
849
+ */
850
+ get initialSearchComplete() {
851
+ return this._initialSearchCompletePromise;
852
+ }
810
853
  async handleQueryChange() {
811
854
  // only reset if the query has actually changed
812
855
  if (!this.searchService || this.pageFetchQueryKey === this.previousQueryKey)
@@ -839,7 +882,14 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
839
882
  this.persistState();
840
883
  }
841
884
  this.historyPopOccurred = false;
885
+ // Reset the `initialSearchComplete` promise with a new value for the imminent search
886
+ this._initialSearchCompletePromise = new Promise(res => {
887
+ this._initialSearchCompleteResolver = res;
888
+ });
889
+ // Fire the initial page and facets requests
842
890
  await Promise.all([this.doInitialPageFetch(), this.fetchFacets()]);
891
+ // Resolve the `initialSearchComplete` promise for this search
892
+ this._initialSearchCompleteResolver(true);
843
893
  }
844
894
  setupStateRestorationObserver() {
845
895
  if (this.boundNavigationHandler)
@@ -904,6 +954,33 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
904
954
  },
905
955
  }));
906
956
  }
957
+ /**
958
+ * Produces a compact unique ID for a search request that can help with debugging
959
+ * on the backend by making related requests easier to trace through different services.
960
+ * (e.g., tying the hits/aggregations requests for the same page back to a single hash).
961
+ *
962
+ * @param params The search service parameters for the request
963
+ * @param kind The kind of request (hits-only, aggregations-only, or both)
964
+ * @returns A Promise resolving to the uid to apply to the request
965
+ */
966
+ async requestUID(params, kind) {
967
+ var _a;
968
+ const paramsToHash = JSON.stringify({
969
+ pageType: params.pageType,
970
+ pageTarget: params.pageTarget,
971
+ query: params.query,
972
+ fields: params.fields,
973
+ filters: params.filters,
974
+ sort: params.sort,
975
+ searchType: this.searchType,
976
+ });
977
+ const fullQueryHash = (await sha1(paramsToHash)).slice(0, 20); // First 80 bits of SHA-1 are plenty for this
978
+ const sessionId = (await this.getSessionId()).slice(0, 20); // Likewise
979
+ const page = (_a = params.page) !== null && _a !== void 0 ? _a : 0;
980
+ const kindPrefix = kind.charAt(0); // f = full, h = hits, a = aggregations
981
+ const currentTime = Date.now();
982
+ return `R:${fullQueryHash}-S:${sessionId}-P:${page}-K:${kindPrefix}-T:${currentTime}`;
983
+ }
907
984
  /**
908
985
  * Constructs a search service FilterMap object from the combination of
909
986
  * all the currently-applied filters. This includes any facets, letter
@@ -1063,6 +1140,7 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
1063
1140
  if (!this.searchService)
1064
1141
  return;
1065
1142
  const { facetFetchQueryKey } = this;
1143
+ const sortParams = this.sortParam ? [this.sortParam] : [];
1066
1144
  const params = {
1067
1145
  query: trimmedQuery,
1068
1146
  rows: 0,
@@ -1071,8 +1149,8 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
1071
1149
  aggregationsSize: 10,
1072
1150
  // Note: we don't need an aggregations param to fetch the default aggregations from the PPS.
1073
1151
  // The default aggregations for the search_results page type should be what we need here.
1074
- uid: this.facetFetchQueryKey,
1075
1152
  };
1153
+ params.uid = await this.requestUID({ ...params, sort: sortParams }, 'aggregations');
1076
1154
  this.facetsLoading = true;
1077
1155
  const searchResponse = await this.searchService.search(params, this.searchType);
1078
1156
  const success = searchResponse === null || searchResponse === void 0 ? void 0 : searchResponse.success;
@@ -1192,8 +1270,8 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
1192
1270
  sort: sortParams,
1193
1271
  filters: this.filterMap,
1194
1272
  aggregations: { omit: true },
1195
- uid: this.pageFetchQueryKey,
1196
1273
  };
1274
+ params.uid = await this.requestUID(params, 'hits');
1197
1275
  const searchResponse = await this.searchService.search(params, this.searchType);
1198
1276
  const success = searchResponse === null || searchResponse === void 0 ? void 0 : searchResponse.success;
1199
1277
  // This is checking to see if the query has changed since the data was fetched.
@@ -1351,6 +1429,7 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
1351
1429
  if (!trimmedQuery)
1352
1430
  return [];
1353
1431
  const filterAggregationKey = prefixFilterAggregationKeys[filterType];
1432
+ const sortParams = this.sortParam ? [this.sortParam] : [];
1354
1433
  const params = {
1355
1434
  query: trimmedQuery,
1356
1435
  rows: 0,
@@ -1360,6 +1439,7 @@ let CollectionBrowser = class CollectionBrowser extends LitElement {
1360
1439
  // Fetch all 26 letter buckets
1361
1440
  aggregationsSize: 26,
1362
1441
  };
1442
+ params.uid = await this.requestUID({ ...params, sort: sortParams }, 'aggregations');
1363
1443
  const searchResponse = await ((_b = this.searchService) === null || _b === void 0 ? void 0 : _b.search(params, this.searchType));
1364
1444
  return ((_g = (_f = (_e = (_d = (_c = searchResponse === null || searchResponse === void 0 ? void 0 : searchResponse.success) === null || _c === void 0 ? void 0 : _c.response) === null || _d === void 0 ? void 0 : _d.aggregations) === null || _e === void 0 ? void 0 : _e[filterAggregationKey]) === null || _f === void 0 ? void 0 : _f.buckets) !== null && _g !== void 0 ? _g : []);
1365
1445
  }