diff --git a/.eslintrc.js b/.eslintrc.js index 6b800f4a..828c75fc 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -149,6 +149,7 @@ module.exports = { '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/no-unnecessary-type-assertion': 'off', '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/member-delimiter-style': [ 'error', { diff --git a/package.json b/package.json index a4822c5c..935ca482 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,9 @@ "test:watch": "yarn test --watch", "test": "jest --runInBand --selectProjects dom --selectProjects node", "test:build": "yarn build && jest --runInBand --selectProjects build", - "test:e2e": "concurrently --kill-others -s first \"NODE_ENV=test yarn playground:angular\" \"cypress run --env playground=angular\"", + "test:e2e": "concurrently --kill-others -s first \"yarn playground:vue\" \"cypress run --env playground=vue\"", "test:e2e:all": "sh scripts/e2e.sh", - "test:e2e:watch": "concurrently --kill-others -s first \"NODE_ENV=test yarn playground:angular\" \"cypress open --env playground=angular\"", + "test:e2e:watch": "concurrently --kill-others -s first \"yarn playground:vue\" \"cypress open --env playground=vue\"", "test:all": "yarn test:e2e:all && yarn test && test:build", "cy:open": "cypress open", "playground:vue": "yarn --cwd ./playgrounds/vue && yarn --cwd ./playgrounds/vue serve", @@ -71,7 +71,7 @@ "babel-jest": "^27.2.2", "concurrently": "^6.2.1", "cssnano": "^4.1.10", - "cypress": "^7.3.0", + "cypress": "^8.5.0", "eslint": "^7.21.0", "eslint-config-prettier": "^8.1.0", "eslint-config-standard": "^16.0.0", diff --git a/src/adapter/__tests__/utils.ts b/src/adapter/__tests__/utils.ts deleted file mode 100644 index 67852736..00000000 --- a/src/adapter/__tests__/utils.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MeiliSearch } from 'meilisearch' -const HOST = 'http://localhost:7700' - -const defaultContext = { - client: new MeiliSearch({ host: HOST }), - paginationTotalHits: 200, - primaryKey: undefined, - placeholderSearch: true, - hitsPerPage: 20, - page: 0, -} - -export { defaultContext } diff --git a/src/adapter/facets-distribution-adapter.ts b/src/adapter/facets-distribution-adapter.ts deleted file mode 100644 index 51f9e2a4..00000000 --- a/src/adapter/facets-distribution-adapter.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { FacetsDistribution, Cache } from '../types' - -/** - * Adapt MeiliSearch facetsDistribution to instantsearch.js facetsDistribution - * by completing the list of distribution with the facets that are checked in the components. - * - * To be aware of which field are checked a cache is provided that was made prior of the search request. - * - * @param {Cache} cache? - * @param {FacetsDistribution} distribution? - * @returns FacetsDistribution - */ -export function facetsDistributionAdapter( - cache?: Cache, - distribution?: FacetsDistribution -): FacetsDistribution { - distribution = distribution || {} - if (cache && Object.keys(cache).length > 0) { - for (const cachedFacet in cache) { - for (const cachedField of cache[cachedFacet]) { - // if cached field is not present in the returned distribution - - if ( - !distribution[cachedFacet] || - !Object.keys(distribution[cachedFacet]).includes(cachedField) - ) { - // add 0 value - distribution[cachedFacet] = distribution[cachedFacet] || {} - distribution[cachedFacet][cachedField] = 0 - } - } - } - } - return distribution -} diff --git a/src/adapter/hits-adapter.ts b/src/adapter/hits-adapter.ts deleted file mode 100644 index bae4496e..00000000 --- a/src/adapter/hits-adapter.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { InstantSearchParams, SearchContext } from '../types' -import { adaptPagination } from './pagination-adapter' -import { adaptFormating } from './highlight-adapter' - -/** - * @param {Array>, - instantSearchParams: InstantSearchParams, - instantMeiliSearchContext: SearchContext -): any { - const { primaryKey } = instantMeiliSearchContext - const { page, hitsPerPage } = instantMeiliSearchContext - const paginatedHits = adaptPagination(meiliSearchHits, page, hitsPerPage) - - return paginatedHits.map((hit: any) => { - // Creates Hit object compliant with InstantSearch - if (Object.keys(hit).length > 0) { - const { _formatted: formattedHit, _matchesInfo, ...restOfHit } = hit - return { - ...restOfHit, - ...adaptFormating(formattedHit, instantSearchParams), - ...(primaryKey && { objectID: hit[primaryKey] }), - } - } - return hit - }) -} diff --git a/src/adapter/index.ts b/src/adapter/index.ts index 5bda2672..ea566b4a 100644 --- a/src/adapter/index.ts +++ b/src/adapter/index.ts @@ -1,6 +1,2 @@ export * from './search-request-adapter' export * from './search-response-adapter' -export * from './hits-adapter' -export * from './highlight-adapter' -export * from './pagination-adapter' -export * from './facets-distribution-adapter' diff --git a/src/adapter/__tests__/facets-distribution-adapter.tests.ts b/src/adapter/search-request-adapter/__tests__/facets-distribution-assigns.tests.ts similarity index 73% rename from src/adapter/__tests__/facets-distribution-adapter.tests.ts rename to src/adapter/search-request-adapter/__tests__/facets-distribution-assigns.tests.ts index b0ff3b88..650065a0 100644 --- a/src/adapter/__tests__/facets-distribution-adapter.tests.ts +++ b/src/adapter/search-request-adapter/__tests__/facets-distribution-assigns.tests.ts @@ -1,7 +1,7 @@ -import { facetsDistributionAdapter } from '../' +import { assignMissingFilters } from '../filters' test('One field in cache present in distribution', () => { - const returnedDistribution = facetsDistributionAdapter( + const returnedDistribution = assignMissingFilters( { genre: ['comedy'] }, { genre: { comedy: 1 } } ) @@ -9,15 +9,12 @@ test('One field in cache present in distribution', () => { }) test('One field in cache not present in distribution', () => { - const returnedDistribution = facetsDistributionAdapter( - { genre: ['comedy'] }, - {} - ) + const returnedDistribution = assignMissingFilters({ genre: ['comedy'] }, {}) expect(returnedDistribution).toMatchObject({ genre: { comedy: 0 } }) }) test('two field in cache only one present in distribution', () => { - const returnedDistribution = facetsDistributionAdapter( + const returnedDistribution = assignMissingFilters( { genre: ['comedy'], title: ['hamlet'] }, { genre: { comedy: 12 } } ) @@ -28,7 +25,7 @@ test('two field in cache only one present in distribution', () => { }) test('two field in cache w/ different facet name none present in distribution', () => { - const returnedDistribution = facetsDistributionAdapter( + const returnedDistribution = assignMissingFilters( { genre: ['comedy'], title: ['hamlet'] }, {} ) @@ -39,7 +36,7 @@ test('two field in cache w/ different facet name none present in distribution', }) test('two field in cache w/ different facet name both present in distribution', () => { - const returnedDistribution = facetsDistributionAdapter( + const returnedDistribution = assignMissingFilters( { genre: ['comedy'], title: ['hamlet'] }, { genre: { comedy: 12 }, title: { hamlet: 1 } } ) @@ -50,7 +47,7 @@ test('two field in cache w/ different facet name both present in distribution', }) test('Three field in cache w/ different facet name two present in distribution', () => { - const returnedDistribution = facetsDistributionAdapter( + const returnedDistribution = assignMissingFilters( { genre: ['comedy', 'horror'], title: ['hamlet'] }, { genre: { comedy: 12 }, title: { hamlet: 1 } } ) @@ -61,14 +58,14 @@ test('Three field in cache w/ different facet name two present in distribution', }) test('Cache is undefined and facets distribution is not', () => { - const returnedDistribution = facetsDistributionAdapter(undefined, { + const returnedDistribution = assignMissingFilters(undefined, { genre: { comedy: 12 }, }) expect(returnedDistribution).toMatchObject({ genre: { comedy: 12 } }) }) test('Cache is empty object and facets distribution is not', () => { - const returnedDistribution = facetsDistributionAdapter( + const returnedDistribution = assignMissingFilters( {}, { genre: { comedy: 12 } } ) @@ -76,16 +73,16 @@ test('Cache is empty object and facets distribution is not', () => { }) test('Cache is empty object and facets distribution empty object', () => { - const returnedDistribution = facetsDistributionAdapter({}, {}) + const returnedDistribution = assignMissingFilters({}, {}) expect(returnedDistribution).toMatchObject({}) }) test('Cache is undefined and facets distribution empty object', () => { - const returnedDistribution = facetsDistributionAdapter(undefined, {}) + const returnedDistribution = assignMissingFilters(undefined, {}) expect(returnedDistribution).toMatchObject({}) }) test('Cache is undefined and facets distribution is undefined', () => { - const returnedDistribution = facetsDistributionAdapter(undefined, undefined) + const returnedDistribution = assignMissingFilters(undefined, undefined) expect(returnedDistribution).toMatchObject({}) }) diff --git a/src/cache/__tests__/cache.tests.ts b/src/adapter/search-request-adapter/__tests__/filter-cache.tests.ts similarity index 98% rename from src/cache/__tests__/cache.tests.ts rename to src/adapter/search-request-adapter/__tests__/filter-cache.tests.ts index b2cd9eed..8687007d 100644 --- a/src/cache/__tests__/cache.tests.ts +++ b/src/adapter/search-request-adapter/__tests__/filter-cache.tests.ts @@ -1,4 +1,4 @@ -import { cacheFilters } from '..' +import { cacheFilters } from '../filters' const facetCacheData = [ { diff --git a/src/adapter/filter-adapter.ts b/src/adapter/search-request-adapter/filter-adapter.ts similarity index 79% rename from src/adapter/filter-adapter.ts rename to src/adapter/search-request-adapter/filter-adapter.ts index cb6d7d5a..f88d8e85 100644 --- a/src/adapter/filter-adapter.ts +++ b/src/adapter/search-request-adapter/filter-adapter.ts @@ -1,17 +1,15 @@ -import type { Filter, AlgoliaSearchOptions } from '../types' -import { replaceColonByEqualSign } from '../utils' +import type { Filter, SearchContext } from '../../types' +import { replaceColonByEqualSign } from '../../utils' /** * Transform InstantSearch filter to MeiliSearch filter. * Change sign from `:` to `=` in nested filter object. * example: [`genres:comedy`] becomes [`genres=comedy`] * - * @param {AlgoliaSearchOptions['facetFilters']} filters? - * @returns Filter + * @param {SearchContext['facetFilters']} filters? + * @returns {Filter} */ -function transformFilter( - filters?: AlgoliaSearchOptions['facetFilters'] -): Filter { +function transformFilter(filters?: SearchContext['facetFilters']): Filter { if (typeof filters === 'string') { return replaceColonByEqualSign(filters) } else if (Array.isArray(filters)) @@ -34,7 +32,7 @@ function transformFilter( * If filter is array, return without change. * * @param {Filter} filter - * @returns Array + * @returns {Array} */ function filterToArray(filter: Filter): Array { // Filter is a string @@ -51,7 +49,7 @@ function filterToArray(filter: Filter): Array { * @param {Filter} facetFilters * @param {Filter} numericFilters * @param {string} filters - * @returns Filter + * @returns {Filter} */ function mergeFilters( facetFilters: Filter, @@ -82,14 +80,14 @@ function mergeFilters( * combining and transforming all provided filters. * * @param {string|undefined} filters - * @param {AlgoliaSearchOptions['numericFilters']} numericFilters - * @param {AlgoliaSearchOptions['facetFilters']} facetFilters - * @returns Filter + * @param {SearchContext['numericFilters']} numericFilters + * @param {SearchContext['facetFilters']} facetFilters + * @returns {Filter} */ export function adaptFilters( filters: string | undefined, - numericFilters: AlgoliaSearchOptions['numericFilters'], - facetFilters: AlgoliaSearchOptions['facetFilters'] + numericFilters: SearchContext['numericFilters'], + facetFilters: SearchContext['facetFilters'] ): Filter { const transformedFilter = transformFilter(facetFilters || []) const transformedNumericFilter = transformFilter(numericFilters || []) diff --git a/src/adapter/search-request-adapter/filters.ts b/src/adapter/search-request-adapter/filters.ts new file mode 100644 index 00000000..52b1aeb2 --- /dev/null +++ b/src/adapter/search-request-adapter/filters.ts @@ -0,0 +1,96 @@ +import { + Filter, + ParsedFilter, + FacetsDistribution, + FilterCache, +} from '../../types' +import { removeUndefined } from '../../utils' + +/** + * @param {string} filter + */ +const adaptFilterSyntax = (filter: string) => { + const matches = filter.match(/([^=]*)="?([^\\"]*)"?$/) + if (matches) { + const [_, filterName, value] = matches + return [{ filterName, value }] + } + return [] +} + +/** + * @param {Filter} filters? + * @returns {Array} + */ +function extractFilters(filters?: Filter): Array { + if (typeof filters === 'string') { + return adaptFilterSyntax(filters) + } else if (Array.isArray(filters)) { + return filters + .map((nestedFilter) => { + if (Array.isArray(nestedFilter)) { + return nestedFilter.map((filter) => adaptFilterSyntax(filter)) + } + return adaptFilterSyntax(nestedFilter) + }) + .flat(2) + } + return [] +} + +/** + * @param {Filter} filters? + * @returns {FilterCache} + */ +export function cacheFilters(filters?: Filter): FilterCache { + const extractedFilters = extractFilters(filters) + const cleanFilters = removeUndefined(extractedFilters) + return cleanFilters.reduce( + (cache, parsedFilter: ParsedFilter) => { + const { filterName, value } = parsedFilter + const prevFields = cache[filterName] || [] + cache = { + ...cache, + [filterName]: [...prevFields, value], + } + return cache + }, + {} as FilterCache + ) +} + +/** + * Assign missing filters to facetsDistribution. + * All facet passed as filter should appear in the facetsDistribution. + * If not present, the facet is added with 0 as value. + * + * + * @param {FilterCache} cache? + * @param {FacetsDistribution} distribution? + * @returns {FacetsDistribution} + */ +export function assignMissingFilters( + cachedFilters?: FilterCache, + distribution?: FacetsDistribution +): FacetsDistribution { + distribution = distribution || {} + + // If cachedFilters contains something + if (cachedFilters && Object.keys(cachedFilters).length > 0) { + // for all filters in cached filters + for (const cachedFacet in cachedFilters) { + // if facet does not exist on returned distribution, add an empty object + if (!distribution[cachedFacet]) distribution[cachedFacet] = {} + // for all fields in every filter + for (const cachedField of cachedFilters[cachedFacet]) { + // if the field is not present in the returned distribution + // set it at 0 + if (!Object.keys(distribution[cachedFacet]).includes(cachedField)) { + // add 0 value + distribution[cachedFacet][cachedField] = 0 + } + } + } + } + return distribution +} diff --git a/src/adapter/search-request-adapter/index.ts b/src/adapter/search-request-adapter/index.ts new file mode 100644 index 00000000..4d77cda1 --- /dev/null +++ b/src/adapter/search-request-adapter/index.ts @@ -0,0 +1,2 @@ +export * from './search-resolver' +export * from './search-params-adapter' diff --git a/src/adapter/search-request-adapter.ts b/src/adapter/search-request-adapter/search-params-adapter.ts similarity index 58% rename from src/adapter/search-request-adapter.ts rename to src/adapter/search-request-adapter/search-params-adapter.ts index cecd6aad..ddecec6d 100644 --- a/src/adapter/search-request-adapter.ts +++ b/src/adapter/search-request-adapter/search-params-adapter.ts @@ -1,4 +1,4 @@ -import type { InstantSearchParams, MeiliSearchParams } from '../types' +import type { MeiliSearchParams, SearchContext } from '../../types' import { adaptFilters } from './filter-adapter' @@ -6,46 +6,38 @@ import { adaptFilters } from './filter-adapter' * Adapt search request from instantsearch.js * to search request compliant with MeiliSearch * - * @param {InstantSearchParams} instantSearchParams - * @param {number} paginationTotalHits - * @param {boolean} placeholderSearch - * @param {string} sort? - * @param {string} query? - * @returns MeiliSearchParams + * @param {SearchContext} searchContext + * @returns {MeiliSearchParams} */ -export function adaptSearchRequest( - instantSearchParams: InstantSearchParams, - paginationTotalHits: number, - placeholderSearch: boolean, - sort?: string, - query?: string +export function adaptSearchParams( + searchContext: SearchContext ): MeiliSearchParams { // Creates search params object compliant with MeiliSearch const meiliSearchParams: Record = {} // Facets - const facets = instantSearchParams?.facets + const facets = searchContext?.facets if (facets?.length) { meiliSearchParams.facetsDistribution = facets } // Attributes To Crop - const attributesToCrop = instantSearchParams?.attributesToSnippet + const attributesToCrop = searchContext?.attributesToSnippet if (attributesToCrop) { meiliSearchParams.attributesToCrop = attributesToCrop } // Attributes To Retrieve - const attributesToRetrieve = instantSearchParams?.attributesToRetrieve + const attributesToRetrieve = searchContext?.attributesToRetrieve if (attributesToRetrieve) { meiliSearchParams.attributesToRetrieve = attributesToRetrieve } // Filter const filter = adaptFilters( - instantSearchParams?.filters, - instantSearchParams?.numericFilters, - instantSearchParams?.facetFilters + searchContext?.filters, + searchContext?.numericFilters, + searchContext?.facetFilters ) if (filter.length) { meiliSearchParams.filter = filter @@ -57,16 +49,21 @@ export function adaptSearchRequest( } // Attributes To Highlight - meiliSearchParams.attributesToHighlight = instantSearchParams?.attributesToHighlight || [ + meiliSearchParams.attributesToHighlight = searchContext?.attributesToHighlight || [ '*', ] + const placeholderSearch = meiliSearchParams.placeholderSearch + const query = meiliSearchParams.query + const paginationTotalHits = meiliSearchParams.paginationTotalHits + if ((!placeholderSearch && query === '') || paginationTotalHits === 0) { meiliSearchParams.limit = 0 } else { meiliSearchParams.limit = paginationTotalHits } + const sort = searchContext.sort // Sort if (sort?.length) { meiliSearchParams.sort = [sort] diff --git a/src/adapter/search-request-adapter/search-resolver.ts b/src/adapter/search-request-adapter/search-resolver.ts new file mode 100644 index 00000000..6ecf5c07 --- /dev/null +++ b/src/adapter/search-request-adapter/search-resolver.ts @@ -0,0 +1,56 @@ +import { + SearchContext, + MeiliSearch, + MeiliSearchResponse, + SearchCacheInterface, + MeiliSearchParams, +} from '../../types' +import { cacheFilters, assignMissingFilters } from './filters' + +/** + * @param {ResponseCacher} cache + */ +export function SearchResolver(cache: SearchCacheInterface) { + return { + /** + * @param {SearchContext} searchContext + * @param {MeiliSearchParams} searchParams + * @param {MeiliSearch} client + * @returns {Promise} + */ + searchResponse: async function ( + searchContext: SearchContext, + searchParams: MeiliSearchParams, + client: MeiliSearch + ): Promise>> { + // Create key with relevant informations + const key = cache.formatKey([ + searchParams, + searchContext.indexUid, + searchContext.query, + ]) + const entry = cache.getEntry(key) + + // Request is cached. + if (entry) return entry + + // Cache filters: todo components + const filterCache = cacheFilters(searchParams?.filter) + + // Make search request + const searchResponse = await client + .index(searchContext.indexUid) + .search(searchContext.query, searchParams) + + // Add facets back into facetsDistribution + searchResponse.facetsDistribution = assignMissingFilters( + filterCache, + searchResponse.facetsDistribution + ) + + // Cache response + cache.setEntry(key, searchResponse) + return searchResponse + }, + } +} diff --git a/src/adapter/search-request-adapter/search.ts b/src/adapter/search-request-adapter/search.ts new file mode 100644 index 00000000..6dbc866b --- /dev/null +++ b/src/adapter/search-request-adapter/search.ts @@ -0,0 +1,19 @@ +import { + SearchContext, + MeiliSearchParams, + MeiliSearchResponse, + MeiliSearch, +} from '../../types' +/** + * @param {MeiliSearch} client + * @param {SearchContext} context + * @param {MeiliSearchParams} searchRequest + * @returns {Promise} + */ +export async function search( + client: MeiliSearch, + context: SearchContext, + searchRequest: MeiliSearchParams +): Promise { + return client.index(context.indexUid).search(context.query, searchRequest) +} diff --git a/src/adapter/__tests__/pagination-adapter.tests.ts b/src/adapter/search-response-adapter/__tests__/pagination-adapter.tests.ts similarity index 97% rename from src/adapter/__tests__/pagination-adapter.tests.ts rename to src/adapter/search-response-adapter/__tests__/pagination-adapter.tests.ts index da4de9dc..8388f5a6 100644 --- a/src/adapter/__tests__/pagination-adapter.tests.ts +++ b/src/adapter/search-response-adapter/__tests__/pagination-adapter.tests.ts @@ -1,5 +1,5 @@ -import { adaptPagination } from '..' -import { ceiledDivision } from '../../utils' +import { adaptPagination } from '../pagination-adapter' +import { ceiledDivision } from '../../../utils' const numberPagesTestParameters = [ { diff --git a/src/adapter/highlight-adapter.ts b/src/adapter/search-response-adapter/highlight-adapter.ts similarity index 89% rename from src/adapter/highlight-adapter.ts rename to src/adapter/search-response-adapter/highlight-adapter.ts index bd5d4680..3d1f7694 100644 --- a/src/adapter/highlight-adapter.ts +++ b/src/adapter/search-response-adapter/highlight-adapter.ts @@ -1,5 +1,5 @@ -import { isString } from '../utils' -import { InstantSearchParams } from '../types' +import { isString } from '../../utils' +import { SearchContext } from '../../types' /** * Replace `em` tags in highlighted MeiliSearch hits to @@ -8,7 +8,7 @@ import { InstantSearchParams } from '../types' * @param {string} value * @param {string} highlightPreTag? * @param {string} highlightPostTag? - * @returns string + * @returns {string} */ function replaceHighlightTags( value: any, @@ -32,7 +32,7 @@ function replaceHighlightTags( * @param {Record, @@ -58,7 +58,7 @@ function adaptHighlight( * @param {string} snippetEllipsisText? * @param {string} highlightPreTag? * @param {string} highlightPostTag? - * @returns string + * @returns {string} */ function snippetValue( value: string, @@ -125,17 +125,17 @@ function adaptSnippet( * Adapt MeiliSearch formating to formating compliant with instantsearch.js. * * @param {Record, - instantSearchParams: InstantSearchParams + searchContext: SearchContext ): Record { - const attributesToSnippet = instantSearchParams?.attributesToSnippet - const snippetEllipsisText = instantSearchParams?.snippetEllipsisText - const highlightPreTag = instantSearchParams?.highlightPreTag - const highlightPostTag = instantSearchParams?.highlightPostTag + const attributesToSnippet = searchContext?.attributesToSnippet + const snippetEllipsisText = searchContext?.snippetEllipsisText + const highlightPreTag = searchContext?.highlightPreTag + const highlightPostTag = searchContext?.highlightPostTag if (!formattedHit || formattedHit.length) return {} return { diff --git a/src/adapter/search-response-adapter/hits-adapter.ts b/src/adapter/search-response-adapter/hits-adapter.ts new file mode 100644 index 00000000..7ddc29d9 --- /dev/null +++ b/src/adapter/search-response-adapter/hits-adapter.ts @@ -0,0 +1,32 @@ +import type { PaginationContext, SearchContext } from '../../types' +import { adaptPagination } from './pagination-adapter' +import { adaptFormating } from './highlight-adapter' + +/** + * @param {Array>, + searchContext: SearchContext, + paginationContext: PaginationContext +): any { + const { primaryKey } = searchContext + const { hitsPerPage, page } = paginationContext + const paginatedHits = adaptPagination(hits, page, hitsPerPage) + + return paginatedHits.map((hit: any) => { + // Creates Hit object compliant with InstantSearch + if (Object.keys(hit).length > 0) { + const { _formatted: formattedHit, _matchesInfo, ...restOfHit } = hit + return { + ...restOfHit, + ...adaptFormating(formattedHit, searchContext), + ...(primaryKey && { objectID: hit[primaryKey] }), + } + } + return hit + }) +} diff --git a/src/adapter/search-response-adapter/index.ts b/src/adapter/search-response-adapter/index.ts new file mode 100644 index 00000000..e1584f52 --- /dev/null +++ b/src/adapter/search-response-adapter/index.ts @@ -0,0 +1 @@ +export * from './search-response-adapter' diff --git a/src/adapter/pagination-adapter.ts b/src/adapter/search-response-adapter/pagination-adapter.ts similarity index 94% rename from src/adapter/pagination-adapter.ts rename to src/adapter/search-response-adapter/pagination-adapter.ts index c7a53972..d5c378db 100644 --- a/src/adapter/pagination-adapter.ts +++ b/src/adapter/search-response-adapter/pagination-adapter.ts @@ -4,7 +4,7 @@ * @param {Record, diff --git a/src/adapter/search-response-adapter.ts b/src/adapter/search-response-adapter/search-response-adapter.ts similarity index 70% rename from src/adapter/search-response-adapter.ts rename to src/adapter/search-response-adapter/search-response-adapter.ts index ebc7596c..9946e6b0 100644 --- a/src/adapter/search-response-adapter.ts +++ b/src/adapter/search-response-adapter/search-response-adapter.ts @@ -1,27 +1,25 @@ import type { SearchContext, - InstantSearchParams, MeiliSearchResponse, AlgoliaSearchResponse, -} from '../types' -import { ceiledDivision } from '../utils' + PaginationContext, +} from '../../types' +import { ceiledDivision } from '../../utils' import { adaptHits } from './hits-adapter' /** * Adapt search response from MeiliSearch * to search response compliant with instantsearch.js * - * @param {string} indexUid * @param {MeiliSearchResponse> }} */ export function adaptSearchResponse( - indexUid: string, searchResponse: MeiliSearchResponse>, - instantSearchParams: InstantSearchParams, - instantMeiliSearchContext: SearchContext + searchContext: SearchContext, + paginationContext: PaginationContext ): { results: Array> } { const searchResponseOptionals: Record = {} @@ -32,15 +30,11 @@ export function adaptSearchResponse( searchResponseOptionals.exhaustiveFacetsCount = exhaustiveFacetsCount } - const hits = adaptHits( - searchResponse.hits, - instantSearchParams, - instantMeiliSearchContext - ) + const hits = adaptHits(searchResponse.hits, searchContext, paginationContext) const nbPages = ceiledDivision( searchResponse.hits.length, - instantMeiliSearchContext.hitsPerPage + paginationContext.hitsPerPage ) const exhaustiveNbHits = searchResponse.exhaustiveNbHits @@ -48,11 +42,11 @@ export function adaptSearchResponse( const processingTimeMs = searchResponse.processingTimeMs const query = searchResponse.query - const { hitsPerPage, page } = instantMeiliSearchContext + const { hitsPerPage, page } = paginationContext // Create response object compliant with InstantSearch const adaptedSearchResponse = { - index: indexUid, + index: searchContext.indexUid, hitsPerPage, page, facets, diff --git a/src/cache/__tests__/assets/utils.ts b/src/cache/__tests__/assets/utils.ts new file mode 100644 index 00000000..8971dc84 --- /dev/null +++ b/src/cache/__tests__/assets/utils.ts @@ -0,0 +1,9 @@ +export const searchResponse = { + hits: [], + query: '', + offset: 0, + limit: 0, + processingTimeMs: 0, + nbHits: 0, + exhaustiveNbHits: false, +} diff --git a/src/cache/__tests__/search-cache.tests.ts b/src/cache/__tests__/search-cache.tests.ts new file mode 100644 index 00000000..1abff0c7 --- /dev/null +++ b/src/cache/__tests__/search-cache.tests.ts @@ -0,0 +1,103 @@ +import { SearchCache } from '../search-cache' +import { searchResponse } from './assets/utils' + +describe('Tests on entries in cache', () => { + test('Test to getEntry on empty cache', () => { + const cache = SearchCache() + const key = cache.getEntry('') + + expect(key).toBeUndefined() + }) + + test('Test to getEntry on invalid json', () => { + const cache = SearchCache({ myKey: 'myValue' }) + const key = cache.getEntry('myKey') + + expect(key).toEqual('myValue') + }) + + test('Test to getEntry on valid json string', () => { + const cache = SearchCache({ myKey: '"myValue"' }) + const key = cache.getEntry('myKey') + + expect(key).toEqual('myValue') + }) + + test('Test to getEntry on valid json object', () => { + const cache = SearchCache({ myKey: '{ "id": 1 }' }) + const key = cache.getEntry('myKey') + + expect(key).toHaveProperty('id', 1) + }) + + test('Test to getEntry on invalid json object', () => { + const cache = SearchCache({ myKey: '{ id: 1 }' }) + const key = cache.getEntry('myKey') + + expect(key).toEqual('{ id: 1 }') + }) +}) + +describe('Tests on key format', () => { + test('Test to format an empty string', () => { + const cache = SearchCache() + const key = cache.formatKey(['']) + expect(key).toEqual('""') + }) + + test('Test to format a number', () => { + const cache = SearchCache() + const key = cache.formatKey([1]) + expect(key).toEqual('1') + }) + + test('Test to format multiple empty strings', () => { + const cache = SearchCache() + const key = cache.formatKey(['', '', '']) + expect(key).toEqual('""""""') + }) + test('Test to format empty string', () => { + const cache = SearchCache() + const key = cache.formatKey([]) + expect(key).toEqual('') + }) + + test('Test to format undefined', () => { + const cache = SearchCache() + const key = cache.formatKey([undefined]) + expect(key).toEqual('undefined') + }) +}) + +describe('Tests on setEntry in cache', () => { + test('Set a response on a key', () => { + const cache = SearchCache() + const key = 'test' + const formattedKey = cache.formatKey([key]) + cache.setEntry(formattedKey, searchResponse) + const cached = cache.getEntry(formattedKey) + + expect(JSON.stringify(cached)).toEqual(JSON.stringify(searchResponse)) + }) + + test('Set a response on an empty key', () => { + const cache = SearchCache() + const key = '' + const formattedKey = cache.formatKey([key]) + cache.setEntry(formattedKey, searchResponse) + const cached = cache.getEntry(formattedKey) + + expect(JSON.stringify(cached)).toEqual(JSON.stringify(searchResponse)) + }) + + test('Set a response on an existing key', () => { + const cache = SearchCache() + const key = 'test' + const formattedKey = cache.formatKey([key]) + cache.setEntry(formattedKey, searchResponse) + cache.setEntry(formattedKey, searchResponse) + const cached = cache.getEntry(formattedKey) + + expect(JSON.stringify(cached)).toEqual(JSON.stringify(searchResponse)) + }) +}) diff --git a/src/cache/filters.ts b/src/cache/filters.ts deleted file mode 100644 index 27d33529..00000000 --- a/src/cache/filters.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Filter, Cache, ParsedFilter } from '../types' -import { removeUndefined } from '../utils' - -/** - * @param {string} filter - */ -const adaptFilterSyntax = (filter: string) => { - const matches = filter.match(/([^=]*)="?([^\\"]*)"?$/) - if (matches) { - const [_, filterName, value] = matches - return [{ filterName, value }] - } - return [undefined] -} - -/** - * @param {Filter} filters? - * @returns Array - */ -function extractFilters(filters?: Filter): Array { - if (typeof filters === 'string') { - return adaptFilterSyntax(filters) - } else if (Array.isArray(filters)) { - return filters - .map((nestedFilter) => { - if (Array.isArray(nestedFilter)) { - return nestedFilter.map((filter) => adaptFilterSyntax(filter)) - } - return adaptFilterSyntax(nestedFilter) - }) - .flat(2) - } - return [undefined] -} - -/** - * @param {Filter} filters? - * @returns Cache - */ -export function cacheFilters(filters?: Filter): Cache { - const extractedFilters = extractFilters(filters) - const cleanFilters = removeUndefined(extractedFilters) - return cleanFilters.reduce((cache, parsedFilter: ParsedFilter) => { - const { filterName, value } = parsedFilter - const prevFields = cache[filterName] || [] - cache = { - ...cache, - [filterName]: [...prevFields, value], - } - return cache - }, {} as Cache) -} diff --git a/src/cache/index.ts b/src/cache/index.ts index 91e01d4d..891215f0 100644 --- a/src/cache/index.ts +++ b/src/cache/index.ts @@ -1 +1 @@ -export * from './filters' +export * from './search-cache' diff --git a/src/cache/search-cache.ts b/src/cache/search-cache.ts new file mode 100644 index 00000000..7f957b4a --- /dev/null +++ b/src/cache/search-cache.ts @@ -0,0 +1,29 @@ +import { SearchCacheInterface } from '../types' +import { stringifyArray } from '../utils' +/** + * @param {Record = {} +): SearchCacheInterface { + const searchCache = cache + return { + getEntry: function (key: string) { + if (searchCache[key]) { + try { + return JSON.parse(searchCache[key]) + } catch (_) { + return searchCache[key] + } + } + return undefined + }, + formatKey: function (components: any[]) { + return stringifyArray(components) + }, + setEntry: function (key: string, searchResponse: T) { + searchCache[key] = JSON.stringify(searchResponse) + }, + } +} diff --git a/src/client/index.ts b/src/client/index.ts index 2015b92d..ab8d335c 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -1,134 +1 @@ -import { MeiliSearch } from 'meilisearch' -import { - InstantMeiliSearchOptions, - InstantMeiliSearchInstance, - AlgoliaSearchResponse, - AlgoliaMultipleQueriesQuery, - InstantSearchParams, - SearchContext, -} from '../types' -import { - adaptSearchRequest, - adaptSearchResponse, - facetsDistributionAdapter, -} from '../adapter' -import { cacheFilters } from '../cache' - -/** - * Create search context. - * - * @param {string} indexName - * @param {InstantSearchParams} params - * @param {InstantMeiliSearchOptions={}} meiliSearchOptions - * @param {MeiliSearch} MeiliSearchClient - * @returns SearchContext - */ -function createContext( - indexName: string, - params: InstantSearchParams, - meiliSearchOptions: InstantMeiliSearchOptions = {}, - MeiliSearchClient: MeiliSearch -): SearchContext { - const { - paginationTotalHits, - primaryKey, - placeholderSearch, - } = meiliSearchOptions - - const page = params?.page - const hitsPerPage = params?.hitsPerPage - - const query = params?.query - // Split index name and possible sorting rules - const [indexUid, ...sortByArray] = indexName.split(':') - - const context = { - client: MeiliSearchClient, - indexUid: indexUid, - paginationTotalHits: paginationTotalHits || 200, - primaryKey: primaryKey || undefined, - placeholderSearch: placeholderSearch !== false, // true by default - hitsPerPage: hitsPerPage === undefined ? 20 : hitsPerPage, // 20 is the MeiliSearch's default limit value. `hitsPerPage` can be changed with `InsantSearch.configure`. - page: page || 0, // default page is 0 if none is provided - sort: sortByArray.join(':') || '', - query, - } - return context -} - -/** - * Instanciate SearchClient required by instantsearch.js. - * - * @param {string} hostUrl - * @param {string} apiKey - * @param {InstantMeiliSearchOptions={}} meiliSearchOptions - * @returns InstantMeiliSearchInstance - */ -export function instantMeiliSearch( - hostUrl: string, - apiKey: string, - meiliSearchOptions: InstantMeiliSearchOptions = {} -): InstantMeiliSearchInstance { - return { - MeiliSearchClient: new MeiliSearch({ host: hostUrl, apiKey: apiKey }), - search: async function >( - instantSearchRequests: readonly AlgoliaMultipleQueriesQuery[] - // options?: RequestOptions & MultipleQueriesOptions - When is this used ? - ): Promise<{ results: Array> }> { - try { - const searchRequest = instantSearchRequests[0] - const { params: instantSearchParams } = searchRequest - - const context = createContext( - searchRequest.indexName, - instantSearchParams, - meiliSearchOptions, - this.MeiliSearchClient - ) - - // Adapt search request to MeiliSearch compliant search request - const adaptedSearchRequest = adaptSearchRequest( - instantSearchParams, - context.paginationTotalHits, - context.placeholderSearch, - context.sort, - context.query - ) - - // Cache filters - const cachedFacet = cacheFilters(adaptedSearchRequest?.filter) - - // Executes the search with MeiliSearch - const searchResponse = await context.client - .index(context.indexUid) - .search(context.query, adaptedSearchRequest) - - // Add the checked facet attributes in facetsDistribution and give them a value of 0 - searchResponse.facetsDistribution = facetsDistributionAdapter( - cachedFacet, - searchResponse.facetsDistribution - ) - - // Adapt the MeiliSearch responsne to a compliant instantsearch.js response - const adaptedSearchResponse = adaptSearchResponse( - context.indexUid, - searchResponse, - instantSearchParams, - context - ) - return adaptedSearchResponse - } catch (e: any) { - console.error(e) - throw new Error(e) - } - }, - searchForFacetValues: async function (_) { - return await new Promise((resolve, reject) => { - reject( - new Error('SearchForFacetValues is not compatible with MeiliSearch') - ) - resolve([]) // added here to avoid compilation error - }) - }, - } -} +export * from './instant-meilisearch-client' diff --git a/src/client/instant-meilisearch-client.ts b/src/client/instant-meilisearch-client.ts new file mode 100644 index 00000000..9284b8cd --- /dev/null +++ b/src/client/instant-meilisearch-client.ts @@ -0,0 +1,134 @@ +import { MeiliSearch } from 'meilisearch' +import { + InstantMeiliSearchOptions, + InstantMeiliSearchInstance, + AlgoliaSearchResponse, + AlgoliaMultipleQueriesQuery, + InstantSearchParams, + Context, + SearchContext, + PaginationContext, +} from '../types' +import { + adaptSearchResponse, + adaptSearchParams, + SearchResolver, +} from '../adapter' +import { SearchCache } from '../cache/search-cache' + +/** + * Instanciate SearchClient required by instantsearch.js. + * + * @param {string} hostUrl + * @param {string} apiKey + * @param {InstantMeiliSearchOptions={}} meiliSearchOptions + * @returns {InstantMeiliSearchInstance} + */ +export function instantMeiliSearch( + hostUrl: string, + apiKey = '', + options: InstantMeiliSearchOptions = {} +): InstantMeiliSearchInstance { + // create search resolver with included cache + const searchResolver = SearchResolver(SearchCache()) + + const context: Context = { + primaryKey: options.primaryKey || undefined, + placeholderSearch: options.placeholderSearch !== false, // true by default + paginationTotalHits: options.paginationTotalHits || 200, + } + + return { + MeiliSearchClient: new MeiliSearch({ host: hostUrl, apiKey: apiKey }), + + /** + * @param {readonlyAlgoliaMultipleQueriesQuery[]} instantSearchRequests + * @returns {Array} + */ + search: async function >( + instantSearchRequests: readonly AlgoliaMultipleQueriesQuery[] + ): Promise<{ results: Array> }> { + try { + const searchRequest = instantSearchRequests[0] + const { params: instantSearchParams } = searchRequest + + const searchContext: SearchContext = createSearchContext( + searchRequest, + context + ) + + const paginationContext = createPaginationContext( + searchContext, + instantSearchParams + ) + + // Adapt search request to MeiliSearch compliant search request + const adaptedSearchRequest = adaptSearchParams(searchContext) + + const searchResponse = await searchResolver.searchResponse( + searchContext, + adaptedSearchRequest, + this.MeiliSearchClient + ) + + // Adapt the MeiliSearch responsne to a compliant instantsearch.js response + const adaptedSearchResponse = adaptSearchResponse( + searchResponse, + searchContext, + paginationContext + ) + return adaptedSearchResponse + } catch (e: any) { + console.error(e) + throw new Error(e) + } + }, + searchForFacetValues: async function (_) { + return await new Promise((resolve, reject) => { + reject( + new Error('SearchForFacetValues is not compatible with MeiliSearch') + ) + resolve([]) // added here to avoid compilation error + }) + }, + } +} + +/** + * @param {AlgoliaMultipleQueriesQuery} searchRequest + * @param {Context} options + * @returns {SearchContext} + */ +function createSearchContext( + searchRequest: AlgoliaMultipleQueriesQuery, + options: Context +): SearchContext { + // Split index name and possible sorting rules + const [indexUid, ...sortByArray] = searchRequest.indexName.split(':') + const { params: instantSearchParams } = searchRequest + + const searchContext: SearchContext = { + ...options, + ...instantSearchParams, + sort: sortByArray.join(':') || '', + indexUid, + } + return searchContext +} + +/** + * @param {AlgoliaMultipleQueriesQuery} searchRequest + * @param {Context} options + * @returns {SearchContext} + */ +function createPaginationContext( + searchContext: SearchContext, + params: InstantSearchParams +): PaginationContext { + return { + paginationTotalHits: searchContext.paginationTotalHits || 200, + hitsPerPage: + searchContext.hitsPerPage === undefined ? 20 : searchContext.hitsPerPage, // 20 is the MeiliSearch's default limit value. `hitsPerPage` can be changed with `InsantSearch.configure`. + page: params?.page || 0, // default page is 0 if none is provided + } +} diff --git a/src/index.ts b/src/index.ts index e1048948..edfed4a6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,2 @@ export * from './client' -export * from './adapter' export * from './types' diff --git a/src/types/instantsearch-types.ts b/src/types/instantsearch-types.ts deleted file mode 100644 index d855aa5a..00000000 --- a/src/types/instantsearch-types.ts +++ /dev/null @@ -1,117 +0,0 @@ -export type GeoRectangle = [number, number, number, number] -export type GeoPolygon = [number, number, number, number, number, number] - -// Documentation: https://www.algolia.com/doc/api-reference/search-api-parameters/ -export type SearchParameters = { - // Attributes - attributesToRetrieve?: string[] - restrictSearchableAttributes?: string[] - - // Filtering - filters?: string - facetFilters?: string[] - optionalFilters?: string[] - numericFilters?: string[] - sumOrFiltersScores?: boolean - - // Faceting - facets?: string[] - maxValuesPerFacet?: number - facetingAfterDistinct?: boolean - sortFacetValuesBy?: string - - // Highlighting / Snippeting - attributesToHighlight?: string[] - attributesToSnippet?: string[] - highlightPreTag?: string - highlightPostTag?: string - snippetEllipsisText?: string - restrictHighlightAndSnippetArrays?: boolean - - // Pagination - page?: number - hitsPerPage?: number - offset?: number - length?: number - - // Typos - minWordSizefor1Typo?: number - minWordSizefor2Typos?: number - typoTolerance?: string | boolean - allowTyposOnNumericTokens?: boolean - ignorePlurals?: boolean | string[] - disableTypoToleranceOnAttributes?: string[] - - // Geo-Search - aroundLatLng?: string - aroundLatLngViaIP?: boolean - aroundRadius?: number | 'all' - aroundPrecision?: number - minimumAroundRadius?: number - insideBoundingBox?: GeoRectangle | GeoRectangle[] - insidePolygon?: GeoPolygon | GeoPolygon[] - - // Query Strategy - queryType?: string - removeWordsIfNoResults?: string - advancedSyntax?: boolean - optionalWords?: string | string[] - removeStopWords?: boolean | string[] - disableExactOnAttributes?: string[] - exactOnSingleWordQuery?: string - alternativesAsExact?: string[] - - // Query Rules - enableRules?: boolean - ruleContexts?: string[] - - // Advanced - minProximity?: number - responseFields?: string[] - maxFacetHits?: number - percentileComputation?: boolean - distinct?: number | boolean - getRankingInfo?: boolean - clickAnalytics?: boolean - analytics?: boolean - analyticsTags?: string[] - synonyms?: boolean - replaceSynonymsInHighlight?: boolean -} - -export interface SearchRequestParameters extends SearchParameters { - query: string -} - -export interface SearchForFacetValuesRequestParameters - extends SearchParameters { - facetQuery: string - facetName: string -} - -export type SearchRequest = { - indexName: string - params: SearchRequestParameters -} - -export type Hit> = T & { - [key: string]: any - _highlightResult?: Record -} - -// Documentation: https://www.algolia.com/doc/rest-api/search/?language=javascript#search-multiple-indexes -export type SearchResponse = { - hits: Hit[] - page?: number - nbHits?: number - nbPages?: number - hitsPerPage?: number - processingTimeMS?: number - query?: string - params?: string - index?: string -} - -export type SearchClient = { - search: (requests: SearchRequest[]) => Promise<{ results: SearchResponse[] }> -} diff --git a/src/types/types.ts b/src/types/types.ts index 78f71c1b..a3109e78 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -1,22 +1,24 @@ -import type { MeiliSearch } from 'meilisearch' -import type { SearchClient } from 'instantsearch.js' import type { - SearchOptions as AlgoliaSearchOptions, - MultipleQueriesQuery as AlgoliaMultipleQueriesQuery, -} from '@algolia/client-search' + MeiliSearch, + SearchResponse as MeiliSearchResponse, +} from 'meilisearch' +import type { SearchClient } from 'instantsearch.js' +import type { MultipleQueriesQuery as AlgoliaMultipleQueriesQuery } from '@algolia/client-search' -export type { AlgoliaMultipleQueriesQuery, AlgoliaSearchOptions } +export type { AlgoliaMultipleQueriesQuery } export type { SearchResponse as AlgoliaSearchResponse } from '@algolia/client-search' + export type { Filter, FacetsDistribution, SearchResponse as MeiliSearchResponse, SearchParams as MeiliSearchParams, + MeiliSearch, } from 'meilisearch' export type InstantSearchParams = AlgoliaMultipleQueriesQuery['params'] -export type Cache = { +export type FilterCache = { [category: string]: string[] } @@ -31,22 +33,31 @@ export type InstantMeiliSearchOptions = { primaryKey?: string } -export type SearchContext = { - page: number +export type Context = { paginationTotalHits: number - hitsPerPage: number - primaryKey?: string - client: MeiliSearch placeholderSearch: boolean + primaryKey?: string +} + +export type SearchCacheInterface = { + getEntry: (key: string) => MeiliSearchResponse | undefined + formatKey: (components: any[]) => string + setEntry: (key: string, searchResponse: T) => void +} + +export type SearchContext = InstantSearchParams & { + primaryKey?: string + placeholderSearch?: boolean sort?: string - query?: string indexUid: string + paginationTotalHits: number } -export type AdaptToMeiliSearchParams = ( - instantSearchParams: InstantSearchParams, - instantMeiliSearchContext: SearchContext -) => Record +export type PaginationContext = { + paginationTotalHits: number + hitsPerPage: number + page: number +} export type InstantMeiliSearchInstance = SearchClient & { MeiliSearchClient: MeiliSearch diff --git a/src/utils/string.ts b/src/utils/string.ts index fc16bb14..6e4503cb 100644 --- a/src/utils/string.ts +++ b/src/utils/string.ts @@ -1,6 +1,6 @@ /** * @param {any} str - * @returns boolean + * @returns {boolean} */ export function isString(str: any): boolean { return typeof str === 'string' || str instanceof String @@ -8,9 +8,19 @@ export function isString(str: any): boolean { /** * @param {string} filter - * @returns string + * @returns {string} */ export function replaceColonByEqualSign(filter: string): string { // will only change first occurence of `:` return filter.replace(/:(.*)/i, '="$1"') } + +/** + * @param {any[]} arr + * @returns {string} + */ +export function stringifyArray(arr: any[]): string { + return arr.reduce((acc: string, curr: any) => { + return (acc += JSON.stringify(curr)) + }, '') +} diff --git a/tests/search-resolver.tests.ts b/tests/search-resolver.tests.ts new file mode 100644 index 00000000..101ee00b --- /dev/null +++ b/tests/search-resolver.tests.ts @@ -0,0 +1,132 @@ +import { Movies } from './assets/utils' +import { instantMeiliSearch } from '../src' +import { MeiliSearch } from 'meilisearch' +import { mocked } from 'ts-jest/utils' + +jest.mock('meilisearch') + +export const searchResponse = { + hits: [], + query: '', + offset: 0, + limit: 0, + processingTimeMs: 0, + nbHits: 0, + exhaustiveNbHits: false, +} + +// Mocking of MeiliSearch package +const mockedMeilisearch = mocked(MeiliSearch, true) +const mockedSearch = jest.fn(() => searchResponse) +const mockedIndex = jest.fn(() => { + return { + search: mockedSearch, + } +}) + +mockedMeilisearch.mockReturnValue({ + // @ts-ignore + index: mockedIndex, +}) + +describe('Pagination browser test', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + test('Test the same search parameters twice', async () => { + const searchParameters = { + indexName: 'movies', + params: { + query: '', + }, + } + const searchClient = instantMeiliSearch('http://localhost:7700') + await searchClient.search([searchParameters]) + await searchClient.search([searchParameters]) + + expect(mockedMeilisearch).toHaveBeenCalledWith({ + host: 'http://localhost:7700', + apiKey: '', + }) + expect(mockedSearch).toHaveBeenCalledTimes(1) + }) + + test('Test two different search parameters', async () => { + const searchParameters1 = { + indexName: 'movies', + params: { + query: '', + }, + } + + const searchParameters2 = { + indexName: 'movies', + params: { + query: 'other query', + }, + } + const searchClient = instantMeiliSearch('http://localhost:7700') + await searchClient.search([searchParameters1]) + await searchClient.search([searchParameters2]) + + expect(mockedMeilisearch).toHaveBeenCalledWith({ + host: 'http://localhost:7700', + apiKey: '', + }) + expect(mockedSearch).toHaveBeenCalledTimes(2) + }) + + test('Test two identical and one different search parameters', async () => { + const searchParameters1 = { + indexName: 'movies', + params: { + query: '', + }, + } + + const searchParameters2 = { + indexName: 'movies', + params: { + query: 'other query', + }, + } + const searchClient = instantMeiliSearch('http://localhost:7700') + await searchClient.search([searchParameters1]) + await searchClient.search([searchParameters2]) + await searchClient.search([searchParameters1]) + + expect(mockedMeilisearch).toHaveBeenCalledWith({ + host: 'http://localhost:7700', + apiKey: '', + }) + expect(mockedSearch).toHaveBeenCalledTimes(2) + }) + + test('Test two same and two different search parameter', async () => { + const searchParameters1 = { + indexName: 'movies', + params: { + query: '', + }, + } + + const searchParameters2 = { + indexName: 'movies', + params: { + query: 'other query', + }, + } + const searchClient = instantMeiliSearch('http://localhost:7700') + await searchClient.search([searchParameters1]) + await searchClient.search([searchParameters2]) + await searchClient.search([searchParameters1]) + await searchClient.search([searchParameters2]) + + expect(mockedMeilisearch).toHaveBeenCalledWith({ + host: 'http://localhost:7700', + apiKey: '', + }) + expect(mockedSearch).toHaveBeenCalledTimes(2) + }) +}) diff --git a/yarn.lock b/yarn.lock index 269cc747..3eef1874 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1161,20 +1161,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@cypress/listr-verbose-renderer@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#a77492f4b11dcc7c446a34b3e28721afd33c642a" - integrity sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo= - dependencies: - chalk "^1.1.3" - cli-cursor "^1.0.2" - date-fns "^1.27.2" - figures "^1.7.0" - -"@cypress/request@^2.88.5": - version "2.88.5" - resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.5.tgz#8d7ecd17b53a849cfd5ab06d5abe7d84976375d7" - integrity sha512-TzEC1XMi1hJkywWpRfD2clreTa/Z+lOrXDCxxBTBPEcY5azdPi56A6Xw+O4tWJnaJH3iIE7G5aDXZC6JgRZLcA== +"@cypress/request@^2.88.6": + version "2.88.6" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.6.tgz#a970dd675befc6bdf8a8921576c01f51cc5798e9" + integrity sha512-z0UxBE/+qaESAHY9p9sM2h8Y4XqtsbDCt0/DPOrqA/RZgKi4PkxdpXyK4wCCnSk1xHqWHZZAE+gV6aDAR6+caQ== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -1189,13 +1179,12 @@ isstream "~0.1.2" json-stringify-safe "~5.0.1" mime-types "~2.1.19" - oauth-sign "~0.9.0" performance-now "^2.1.0" qs "~6.5.2" safe-buffer "^5.1.2" tough-cookie "~2.5.0" tunnel-agent "^0.6.0" - uuid "^3.3.2" + uuid "^8.3.2" "@cypress/xvfb@^1.2.4": version "1.2.4" @@ -1506,13 +1495,6 @@ estree-walker "^2.0.1" picomatch "^2.2.2" -"@samverschueren/stream-to-observable@^0.3.0": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz#a21117b19ee9be70c379ec1877537ef2e1c63301" - integrity sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ== - dependencies: - any-observable "^0.3.0" - "@sinonjs/commons@^1.7.0": version "1.8.2" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.2.tgz#858f5c4b48d80778fde4b9d541f27edc0d56488b" @@ -1716,6 +1698,13 @@ dependencies: "@types/yargs-parser" "*" +"@types/yauzl@^2.9.1": + version "2.9.2" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.2.tgz#c48e5d56aff1444409e39fa164b0b4d4552a7b7a" + integrity sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA== + dependencies: + "@types/node" "*" + "@typescript-eslint/eslint-plugin@^4.32.0": version "4.32.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.32.0.tgz#46d2370ae9311092f2a6f7246d28357daf2d4e89" @@ -1908,6 +1897,14 @@ agent-base@6: dependencies: debug "4" +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -1965,11 +1962,6 @@ ansi-colors@^4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-escapes@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" - integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== - ansi-escapes@^4.2.1, ansi-escapes@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" @@ -1977,15 +1969,12 @@ ansi-escapes@^4.2.1, ansi-escapes@^4.3.1: dependencies: type-fest "^0.11.0" -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= +ansi-escapes@^4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" ansi-regex@^5.0.0: version "5.0.0" @@ -1997,11 +1986,6 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -2021,11 +2005,6 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -any-observable@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" - integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog== - anymatch@^3.0.3, anymatch@~3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" @@ -2409,17 +2388,6 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@^1.0.0, chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -2482,19 +2450,17 @@ classnames@^2.2.5: resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== -cli-cursor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" - integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= - dependencies: - restore-cursor "^1.0.1" +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -cli-cursor@^2.0.0, cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: - restore-cursor "^2.0.0" + restore-cursor "^3.1.0" cli-table3@~0.6.0: version "0.6.0" @@ -2506,13 +2472,13 @@ cli-table3@~0.6.0: optionalDependencies: colors "^1.1.2" -cli-truncate@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" - integrity sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ= +cli-truncate@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== dependencies: - slice-ansi "0.0.4" - string-width "^1.0.1" + slice-ansi "^3.0.0" + string-width "^4.2.0" cliui@^7.0.2: version "7.0.4" @@ -2537,11 +2503,6 @@ coa@^2.0.2: chalk "^2.4.1" q "^1.1.2" -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - collect-v8-coverage@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" @@ -2592,6 +2553,11 @@ colorette@^1.3.0: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.3.0.tgz#ff45d2f0edb244069d3b772adeb04fed38d0a0af" integrity sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w== +colorette@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" + integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== + colors@^1.1.2: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" @@ -2639,16 +2605,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - concurrently@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-6.2.1.tgz#d880fc1d77559084732fa514092a3d5109a0d5bf" @@ -2689,7 +2645,7 @@ core-js-pure@^3.0.0: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.8.3.tgz#10e9e3b2592ecaede4283e8f3ad7020811587c02" integrity sha512-V5qQZVAr9K0xu7jXg1M7qTEwuxUgqr7dUOezGaNa7i+Xn9oXAU/d1fzqD9ObuwpVQOaorO5s70ckyi1woP9lVA== -core-util-is@1.0.2, core-util-is@~1.0.0: +core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= @@ -2866,13 +2822,12 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" -cypress@^7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-7.3.0.tgz#17345b8d18681c120f033e7d8fd0f0271e9d0d51" - integrity sha512-aseRCH1tRVCrM6oEfja6fR/bo5l6e4SkHRRSATh27UeN4f/ANC8U7tGIulmrISJVy9xuOkOdbYKbUb2MNM+nrw== +cypress@^8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-8.5.0.tgz#5712ca170913f8344bf167301205c4217c1eb9bd" + integrity sha512-MMkXIS+Ro2KETn4gAlG3tIc/7FiljuuCZP0zpd9QsRG6MZSyZW/l1J3D4iQM6WHsVxuX4rFChn5jPFlC2tNSvQ== dependencies: - "@cypress/listr-verbose-renderer" "^0.4.1" - "@cypress/request" "^2.88.5" + "@cypress/request" "^2.88.6" "@cypress/xvfb" "^1.2.4" "@types/node" "^14.14.31" "@types/sinonjs__fake-timers" "^6.0.2" @@ -2883,26 +2838,30 @@ cypress@^7.3.0: cachedir "^2.3.0" chalk "^4.1.0" check-more-types "^2.24.0" + cli-cursor "^3.1.0" cli-table3 "~0.6.0" commander "^5.1.0" common-tags "^1.8.0" dayjs "^1.10.4" - debug "4.3.2" + debug "^4.3.2" + enquirer "^2.3.6" eventemitter2 "^6.4.3" execa "4.1.0" executable "^4.1.1" - extract-zip "^1.7.0" + extract-zip "2.0.1" + figures "^3.2.0" fs-extra "^9.1.0" getos "^3.2.1" is-ci "^3.0.0" is-installed-globally "~0.4.0" lazy-ass "^1.6.0" - listr "^0.14.3" + listr2 "^3.8.3" lodash "^4.17.21" log-symbols "^4.0.0" minimist "^1.2.5" ospath "^1.2.2" pretty-bytes "^5.6.0" + proxy-from-env "1.0.0" ramda "~0.27.1" request-progress "^3.0.0" supports-color "^8.1.1" @@ -2932,11 +2891,6 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -date-fns@^1.27.2: - version "1.30.1" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" - integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== - date-fns@^2.16.1: version "2.23.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.23.0.tgz#4e886c941659af0cf7b30fafdd1eaa37e88788a9" @@ -2947,7 +2901,7 @@ dayjs@^1.10.4: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.4.tgz#8e544a9b8683f61783f570980a8a80eaf54ab1e2" integrity sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw== -debug@4, debug@4.3.2, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== @@ -3092,11 +3046,6 @@ electron-to-chromium@^1.3.811: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.822.tgz#7036edc7f669b0aa79e9801dc5f56866c6ddc0b2" integrity sha512-k7jG5oYYHxF4jx6PcqwHX3JVME/OjzolqOZiIogi9xtsfsmTjTdie4x88OakYFPEa8euciTgCCzvVNwvmjHb1Q== -elegant-spinner@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" - integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= - emittery@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" @@ -3119,7 +3068,7 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enquirer@^2.3.5: +enquirer@^2.3.5, enquirer@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== @@ -3189,7 +3138,7 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= @@ -3577,11 +3526,6 @@ executable@^4.1.1: dependencies: pify "^2.2.0" -exit-hook@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" - integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= - exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -3604,15 +3548,16 @@ extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -extract-zip@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" - integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== +extract-zip@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== dependencies: - concat-stream "^1.6.2" - debug "^2.6.9" - mkdirp "^0.5.4" + debug "^4.1.1" + get-stream "^5.1.0" yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" extsprintf@1.3.0: version "1.3.0" @@ -3677,18 +3622,10 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" -figures@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= - dependencies: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" - -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= +figures@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" @@ -3839,7 +3776,7 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-stream@^5.0.0: +get-stream@^5.0.0, get-stream@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== @@ -3945,13 +3882,6 @@ har-validator@~5.1.3: ajv "^6.12.3" har-schema "^2.0.0" -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -4101,10 +4031,10 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= -indent-string@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" - integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== indexes-of@^1.0.1: version "1.0.1" @@ -4119,7 +4049,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@~2.0.3: +inherits@2: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -4233,18 +4163,6 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -4290,13 +4208,6 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-observable@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e" - integrity sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA== - dependencies: - symbol-observable "^1.1.0" - is-path-inside@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" @@ -4307,11 +4218,6 @@ is-potential-custom-element-name@^1.0.1: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== -is-promise@^2.1.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" - integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== - is-reference@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" @@ -4331,11 +4237,6 @@ is-resolvable@^1.0.0: resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - is-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" @@ -4370,7 +4271,7 @@ is-unicode-supported@^0.1.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== -isarray@^1.0.0, isarray@~1.0.0: +isarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -5101,49 +5002,18 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= -listr-silent-renderer@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" - integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4= - -listr-update-renderer@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz#4ea8368548a7b8aecb7e06d8c95cb45ae2ede6a2" - integrity sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA== - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - elegant-spinner "^1.0.1" - figures "^1.7.0" - indent-string "^3.0.0" - log-symbols "^1.0.2" - log-update "^2.3.0" - strip-ansi "^3.0.1" - -listr-verbose-renderer@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz#f1132167535ea4c1261102b9f28dac7cba1e03db" - integrity sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw== - dependencies: - chalk "^2.4.1" - cli-cursor "^2.1.0" - date-fns "^1.27.2" - figures "^2.0.0" - -listr@^0.14.3: - version "0.14.3" - resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" - integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA== - dependencies: - "@samverschueren/stream-to-observable" "^0.3.0" - is-observable "^1.1.0" - is-promise "^2.1.0" - is-stream "^1.1.0" - listr-silent-renderer "^1.1.1" - listr-update-renderer "^0.5.0" - listr-verbose-renderer "^0.5.0" - p-map "^2.0.0" - rxjs "^6.3.3" +listr2@^3.8.3: + version "3.12.2" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.12.2.tgz#2d55cc627111603ad4768a9e87c9c7bb9b49997e" + integrity sha512-64xC2CJ/As/xgVI3wbhlPWVPx0wfTqbUAkpb7bjDi0thSWMqrf07UFhrfsGoo8YSXmF049Rp9C0cjLC8rZxK9A== + dependencies: + cli-truncate "^2.1.0" + colorette "^1.4.0" + log-update "^4.0.0" + p-map "^4.0.0" + rxjs "^6.6.7" + through "^2.3.8" + wrap-ansi "^7.0.0" load-json-file@^2.0.0: version "2.0.0" @@ -5200,13 +5070,6 @@ lodash@4.x, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, l resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" - integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg= - dependencies: - chalk "^1.0.0" - log-symbols@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" @@ -5215,14 +5078,15 @@ log-symbols@^4.0.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" -log-update@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" - integrity sha1-iDKP19HOeTiykoN0bwsbwSayRwg= +log-update@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== dependencies: - ansi-escapes "^3.0.0" - cli-cursor "^2.0.0" - wrap-ansi "^3.0.1" + ansi-escapes "^4.3.0" + cli-cursor "^3.1.0" + slice-ansi "^4.0.0" + wrap-ansi "^6.2.0" loose-envify@^1.4.0: version "1.4.0" @@ -5327,11 +5191,6 @@ mime-types@^2.1.12, mime-types@~2.1.19: dependencies: mime-db "1.45.0" -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== - mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -5354,7 +5213,7 @@ mkdirp@0.3.0: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" integrity sha1-G79asbqCevI1dRQ0kEJkVfSB/h4= -mkdirp@^0.5.4, mkdirp@~0.5.1: +mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -5442,21 +5301,11 @@ nth-check@^1.0.2: dependencies: boolbase "~1.0.0" -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -5528,18 +5377,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" - integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= - -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= - dependencies: - mimic-fn "^1.0.0" - onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" @@ -5609,10 +5446,12 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" -p-map@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" - integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" p-try@^1.0.0: version "1.0.0" @@ -6086,11 +5925,6 @@ pretty-format@^27.0.0, pretty-format@^27.2.2: ansi-styles "^5.0.0" react-is "^17.0.1" -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" @@ -6113,6 +5947,11 @@ prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" +proxy-from-env@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" + integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4= + psl@^1.1.28, psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -6205,19 +6044,6 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -readable-stream@^2.2.2: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - readdirp@~3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" @@ -6345,20 +6171,12 @@ resolve@1.20.0, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12. is-core-module "^2.2.0" path-parse "^1.0.6" -restore-cursor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" - integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= - dependencies: - exit-hook "^1.0.0" - onetime "^1.0.0" - -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: - onetime "^2.0.0" + onetime "^5.1.0" signal-exit "^3.0.2" reusify@^1.0.4: @@ -6447,7 +6265,7 @@ run-parallel@^1.1.9: resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef" integrity sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw== -rxjs@^6.3.3, rxjs@^6.6.3: +rxjs@^6.6.3, rxjs@^6.6.7: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== @@ -6459,7 +6277,7 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== @@ -6587,10 +6405,14 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" - integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" slice-ansi@^4.0.0: version "4.0.0" @@ -6712,23 +6534,6 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -string-width@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - string-width@^4.1.0, string-width@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" @@ -6767,27 +6572,6 @@ string.prototype.trimstart@^1.0.1, string.prototype.trimstart@^1.0.3: call-bind "^1.0.0" define-properties "^1.1.3" -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" @@ -6824,11 +6608,6 @@ stylehacks@^4.0.0: postcss "^7.0.0" postcss-selector-parser "^3.0.0" -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -6884,11 +6663,6 @@ svgo@^1.0.0: unquote "~1.1.1" util.promisify "~1.0.0" -symbol-observable@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" - integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== - symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -6945,6 +6719,11 @@ throttleit@^1.0.0: resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" integrity sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw= +through@^2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + timsort@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" @@ -7099,6 +6878,11 @@ type-fest@^0.11.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + type-fest@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" @@ -7116,11 +6900,6 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - typescript@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.3.tgz#bdc5407caa2b109efd4f82fe130656f977a29324" @@ -7194,7 +6973,7 @@ url@^0.11.0: punycode "1.3.2" querystring "0.2.0" -util-deprecate@^1.0.2, util-deprecate@~1.0.1: +util-deprecate@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -7209,10 +6988,10 @@ util.promisify@~1.0.0: has-symbols "^1.0.1" object.getownpropertydescriptors "^2.1.0" -uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== v8-compile-cache@^2.0.3: version "2.2.0" @@ -7347,13 +7126,14 @@ word-wrap@^1.2.3, word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -wrap-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba" - integrity sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo= +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: - string-width "^2.1.1" - strip-ansi "^4.0.0" + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" wrap-ansi@^7.0.0: version "7.0.0"