From 2d767f94cf99b43a30cb9953f2b8eb99cbe2bee5 Mon Sep 17 00:00:00 2001 From: Ben Teichman Date: Fri, 9 Jun 2017 10:31:07 -0400 Subject: [PATCH 1/8] some stuff --- src/core/decorators.ts | 2 +- src/core/defaults.ts | 7 + src/core/index.ts | 5 +- src/core/service/index.ts | 30 ++++ src/core/service/lazy.ts | 25 +++ src/core/system.ts | 10 +- src/core/url-beautifier/details/generator.ts | 48 ++++++ src/core/url-beautifier/details/parser.ts | 63 +++++++ src/core/url-beautifier/factory.ts | 35 ++++ src/core/url-beautifier/index.ts | 157 ++++++++++++++++++ .../url-beautifier/navigation/generator.ts | 14 ++ src/core/url-beautifier/navigation/parser.ts | 22 +++ src/core/url-beautifier/search/generator.ts | 146 ++++++++++++++++ src/core/url-beautifier/search/parser.ts | 113 +++++++++++++ src/globals.ts | 6 +- src/index.ts | 2 +- src/services/autocomplete.ts | 6 +- src/services/collections.ts | 6 +- src/services/details.ts | 8 +- src/services/index.ts | 19 +-- src/services/lazy.ts | 25 --- src/services/logging.ts | 13 +- src/services/search.ts | 10 +- src/services/url.ts | 45 +++-- src/storefront.ts | 10 +- src/tag/index.ts | 4 +- test/unit/core/decorators.ts | 2 +- test/unit/core/system.ts | 2 +- test/unit/services/logging.ts | 6 +- 29 files changed, 740 insertions(+), 101 deletions(-) create mode 100644 src/core/service/index.ts create mode 100644 src/core/service/lazy.ts create mode 100644 src/core/url-beautifier/details/generator.ts create mode 100644 src/core/url-beautifier/details/parser.ts create mode 100644 src/core/url-beautifier/factory.ts create mode 100644 src/core/url-beautifier/index.ts create mode 100644 src/core/url-beautifier/navigation/generator.ts create mode 100644 src/core/url-beautifier/navigation/parser.ts create mode 100644 src/core/url-beautifier/search/generator.ts create mode 100644 src/core/url-beautifier/search/parser.ts delete mode 100644 src/services/lazy.ts diff --git a/src/core/decorators.ts b/src/core/decorators.ts index 54d8a16b..5341c194 100644 --- a/src/core/decorators.ts +++ b/src/core/decorators.ts @@ -1,6 +1,6 @@ +import { CORE } from '../globals'; import StoreFront from '../storefront'; import Tag from '../tag'; -import { CORE } from './system'; export const core = (target) => { target[CORE] = true; }; diff --git a/src/core/defaults.ts b/src/core/defaults.ts index 01092cf8..af112462 100644 --- a/src/core/defaults.ts +++ b/src/core/defaults.ts @@ -19,6 +19,13 @@ const DEFAULTS: Partial = { services: { logging: { level: 'debug' + }, + url: { + routes: { + search: '/search', + details: '/details', + navigation: '/navigation' + } } }, diff --git a/src/core/index.ts b/src/core/index.ts index 3525df88..aef238c3 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,10 +1,11 @@ -import { Service } from '../services'; import Tag from '../tag'; import Configuration from './configuration'; import ProductTransformer from './product-transformer'; +import Service from './service'; import System from './system'; +import UrlBeautifier from './url-beautifier'; import * as utils from './utils'; export * from './decorators'; export * from './types'; -export { Configuration, ProductTransformer, Service, System, Tag, utils }; +export { Configuration, ProductTransformer, Service, System, Tag, UrlBeautifier, utils }; diff --git a/src/core/service/index.ts b/src/core/service/index.ts new file mode 100644 index 00000000..0b7c5f17 --- /dev/null +++ b/src/core/service/index.ts @@ -0,0 +1,30 @@ +import { SystemServices } from '../../services'; +import StoreFront from '../../storefront'; + +export abstract class BaseService implements Service { + + constructor(protected app: StoreFront, protected opts: T) { } + + abstract init(services: SystemServices): void; +} + +export interface Service { + + init(services: SystemServices): void; +} +export namespace Service { + + export namespace Constructor { + export type Map = { [key: string]: Constructor }; + } + + export interface Constructor { + new (app: StoreFront, config: any): Service; + } + + export type Map = { [key: string]: Service }; + + export type Options = Service.Constructor | T | false; +} + +export default Service; diff --git a/src/core/service/lazy.ts b/src/core/service/lazy.ts new file mode 100644 index 00000000..ecf71bb4 --- /dev/null +++ b/src/core/service/lazy.ts @@ -0,0 +1,25 @@ +import { BaseService } from '.'; +import { SystemServices } from '../../services'; +import StoreFront from '../../storefront'; + +abstract class LazyService extends BaseService { + + initialized: boolean = false; + registered: any[] = []; + + init() { + // no-op + } + + abstract lazyInit(services: SystemServices); + + register(tag: any) { + this.registered.push(tag); + if (!this.initialized) { + this.initialized = true; + this.lazyInit(this.app.services); + } + } +} + +export default LazyService; diff --git a/src/core/system.ts b/src/core/system.ts index a98917db..bdaa8ce6 100644 --- a/src/core/system.ts +++ b/src/core/system.ts @@ -1,12 +1,10 @@ import FluxCapacitor from '@storefront/flux-capacitor'; import { utils, Configuration } from '.'; -import Globals from '../globals'; -import { CoreServices, Service } from '../services'; +import Globals, { CORE } from '../globals'; +import { SystemServices } from '../services'; import StoreFront from '../storefront'; import Tag from '../tag'; - -export const CORE = Symbol.for('storfront_core_service'); -export const TAGS = Symbol.for('storefront_tags'); +import Service from './service'; export default class System { @@ -62,7 +60,7 @@ export default class System { } static buildServices(app: StoreFront, services: Service.Constructor.Map, config: any) { - return Object.keys(services) + return Object.keys(services) .filter((key) => services[key][CORE] || config[key] !== false) .reduce((svcs, key) => { const serviceConfig = typeof config[key] === 'object' ? config[key] : {}; diff --git a/src/core/url-beautifier/details/generator.ts b/src/core/url-beautifier/details/generator.ts new file mode 100644 index 00000000..dd3079f2 --- /dev/null +++ b/src/core/url-beautifier/details/generator.ts @@ -0,0 +1,48 @@ +import { SelectedValueRefinement } from 'groupby-api'; +import UrlBeautifier, { UrlGenerator } from '../index'; + +export default class DetailsUrlGenerator extends UrlGenerator { + + build = (request: UrlBeautifier.DetailsRequest) => { + let paths = []; + + if (request.refinements.length !== 0) { + if (this.config.useReferenceKeys) { + paths = DetailsUrlGenerator.convertRefinements(request.refinements, this.refinementsToKeys); + } else { + request.refinements.forEach(({ value, navigationName }) => paths.push(value, navigationName)); + } + } + + paths.unshift(request.title); + paths.push(request.id); + + return `/${paths.map((path) => encodeURIComponent(path.replace(/\s/g, '-'))).join('/')}`; + } + + static convertRefinements(refinements: SelectedValueRefinement[], refinementsToKeys: object) { + let referenceKeys = ''; + + return refinements.sort(DetailsUrlGenerator.refinementsComparator) + .reduce((paths, { navigationName, value }) => { + + if (!(navigationName in refinementsToKeys)) { + throw new Error(`no mapping found for navigation '${navigationName}'`); + } + + paths.push(value); + referenceKeys += refinementsToKeys[navigationName]; + + return paths; + }, []) + .concat(referenceKeys); + } + + static refinementsComparator(lhs: SelectedValueRefinement, rhs: SelectedValueRefinement): number { + let comparison = lhs.navigationName.localeCompare(rhs.navigationName); + if (comparison === 0) { + comparison = lhs.value.localeCompare(rhs.value); + } + return comparison; + } +} diff --git a/src/core/url-beautifier/details/parser.ts b/src/core/url-beautifier/details/parser.ts new file mode 100644 index 00000000..927fac7b --- /dev/null +++ b/src/core/url-beautifier/details/parser.ts @@ -0,0 +1,63 @@ +import * as URI from 'urijs'; +import UrlBeautifier, { UrlParser } from '../index'; + +export default class DetailsUrlParser extends UrlParser { + + parse = (url: string) => { + const uri = URI.parse(url); + const paths = uri.path.split('/') + .filter((val) => val) + .map((val) => decodeURIComponent(val).replace(/-/g, ' ')); + + if (paths.length < 2) { + throw new Error('path has fewer than two parts'); + } + + const title = paths.shift(); + const id = paths.pop(); + let refinements = []; + + if (paths.length !== 0) { + if (!this.config.useReferenceKeys) { + refinements = DetailsUrlParser.extractPathRefinements(paths); + } else { + refinements = DetailsUrlParser.extractReferencesRefinements(paths, this.keysToRefinements); + } + } + + return { id, refinements, title }; + } + + static extractReferencesRefinements(paths: string[], keysToRefinements: { [key: string]: string }) { + if (paths.length < 2) { + throw new Error('path has wrong number of parts'); + } + + const referenceKeys = paths.pop().split(''); + + if (paths.length !== referenceKeys.length) { + throw new Error('token reference is invalid'); + } + + return paths.map((value) => ({ + value, + navigationName: keysToRefinements[referenceKeys.shift()], + type: 'Value' + })); + } + + static extractPathRefinements(paths: string[]) { + if (paths.length % 2 !== 0) { + throw new Error('path has an odd number of parts'); + } + + const refinements = []; + while (paths.length !== 0) { + const value = paths.shift(); + const navigationName = paths.shift(); + refinements.push({ navigationName, value, type: 'Value' }); + } + + return refinements; + } +} diff --git a/src/core/url-beautifier/factory.ts b/src/core/url-beautifier/factory.ts new file mode 100644 index 00000000..0527c3fa --- /dev/null +++ b/src/core/url-beautifier/factory.ts @@ -0,0 +1,35 @@ +import UrlBeautifier from '.'; +import DetailsUrlGenerator from './details/generator'; +import DetailsUrlParser from './details/parser'; +import NavigationUrlGenerator from './navigation/generator'; +import NavigationUrlParser from './navigation/parser'; +import SearchUrlGenerator from './search/generator'; +import SearchUrlParser from './search/parser'; + +namespace BeautifierFactory { + export function create(beautifier: UrlBeautifier): UrlBeautifier.Beautifiers { + const detailsParser = new DetailsUrlParser(beautifier); + const detailsGenerator = new DetailsUrlGenerator(beautifier); + const navigationParser = new NavigationUrlParser(beautifier); + const navigationGenerator = new NavigationUrlGenerator(beautifier); + const searchParser = new SearchUrlParser(beautifier); + const searchGenerator = new SearchUrlGenerator(beautifier); + + return { + search: { + parse: searchParser.parse, + build: searchGenerator.build + }, + navigation: { + parse: navigationParser.parse, + build: navigationGenerator.build + }, + details: { + parse: detailsParser.parse, + build: detailsGenerator.build + } + }; + } +} + +export default BeautifierFactory; diff --git a/src/core/url-beautifier/index.ts b/src/core/url-beautifier/index.ts new file mode 100644 index 00000000..a50b5f03 --- /dev/null +++ b/src/core/url-beautifier/index.ts @@ -0,0 +1,157 @@ +import { Store } from '@storefront/flux-capacitor'; +import { Request, SelectedRefinement, SelectedValueRefinement } from 'groupby-api'; +import * as URI from 'urijs'; +import UrlService from '../../services/url'; +import BeautifierFactory from './factory'; + +const DEFAULTS: UrlBeautifier.Configuration = { + refinementMapping: [], + params: { + refinements: 'refinements', + page: 'page', + pageSize: 'page_size' + }, + queryToken: 'q', + suffix: '', + useReferenceKeys: true, + navigations: {} +}; + +class UrlBeautifier { + + config: UrlBeautifier.Configuration; + routes: UrlService.Routes; + beautifiers: UrlBeautifier.Beautifiers = BeautifierFactory.create(this); + + constructor(options: UrlService.Options) { + this.config = { ...DEFAULTS, ...this.config }; + this.routes = options.routes; + + const keys = []; + for (let mapping of this.config.refinementMapping) { + const key = Object.keys(mapping)[0]; + UrlBeautifier.validateToken(key, keys); + keys.push(key); + } + UrlBeautifier.validateToken(this.config.queryToken, keys); + } + + parse(url: string) { + const uri = URI.parse(url); + const activeRoute = Object.keys(this.routes).find((route) => uri.path.startsWith(this.routes[route])); + + if (activeRoute) { + return this.beautifiers[activeRoute].parse(UrlBeautifier.extractAppRoute(uri, this.routes[activeRoute])); + } else { + throw new Error('invalid route'); + } + } + + build(type: string, request: object) { + if (type in this.routes) { + return this.beautifiers[type].build(request); + } else { + throw new Error('invalid route'); + } + } + + static validateToken(token: string, keys: string[]) { + if (token.length !== 1) { + throw new Error(`token '${token}' must be a single character`); + } else if (token.match(/[aeiouy]/)) { + throw new Error(`token '${token}' must not be a vowel`); + } else if (keys.indexOf(token) > -1) { + throw new Error(`token '${token}' must be unique`); + } + } + + static extractAppRoute({ query, path }: uri.URIOptions, route: string) { + return new URI({ path: path.substr(route.length), query }).toString(); + } +} + +namespace UrlBeautifier { + + export interface Configuration { + refinementMapping?: any[]; + params?: { + page?: string; + pageSize?: string; + refinements?: string; + sort?: string; + }; + queryToken?: string; + suffix?: string; + useReferenceKeys?: boolean; + navigations?: any; + } + + export interface Url { + query: string; + path: string; + } + + export interface DetailsRequest { + id: string; + title: string; + refinements: SelectedValueRefinement[]; + } + + export interface SearchRequest { + query?: string; + page: number; + pageSize: number; + refinements: SelectedRefinement[]; + } + + export interface Parser { + parse: (url: string) => T; + } + + export interface Generator { + build: (request: T) => string; + } + + export type Beautifier = Parser & Generator; + + export interface Beautifiers { + search: Beautifier; + navigation: Beautifier; + details: Beautifier; + [key: string]: Beautifier; + } +} + +export { UrlBeautifier }; +export default UrlBeautifier; + +export abstract class UrlHandler { + + protected config: UrlBeautifier.Configuration = this.beautifier.config; + + constructor(protected beautifier: UrlBeautifier) { } + + get keysToRefinements() { + return this.config.refinementMapping.reduce((map, mapping) => { + const key = Object.keys(mapping)[0]; + return Object.assign(map, { [key]: mapping[key] }); + }, {}); + } + + get refinementsToKeys() { + return this.config.refinementMapping.reduce((map, mapping) => { + const key = Object.keys(mapping)[0]; + return Object.assign(map, { [mapping[key]]: key }); + }, {}); + } +} + +export abstract class UrlParser extends UrlHandler implements UrlBeautifier.Parser { + + abstract parse: (url: string) => T; +} + +export abstract class UrlGenerator extends UrlHandler implements UrlBeautifier.Generator { + + abstract build: (request: T) => string; +} diff --git a/src/core/url-beautifier/navigation/generator.ts b/src/core/url-beautifier/navigation/generator.ts new file mode 100644 index 00000000..8c59a5d6 --- /dev/null +++ b/src/core/url-beautifier/navigation/generator.ts @@ -0,0 +1,14 @@ +import { Request } from 'groupby-api'; +import { UrlGenerator } from '../index'; + +export default class NavigationUrlGenerator extends UrlGenerator { + + build = (name: string) => { + + if (!(name in this.config.navigations)) { + throw new Error(`no navigation mapping found for ${name}`); + } + + return `/${encodeURIComponent(name.replace(/\s/g, '-'))}`; + } +} diff --git a/src/core/url-beautifier/navigation/parser.ts b/src/core/url-beautifier/navigation/parser.ts new file mode 100644 index 00000000..8d9e49c9 --- /dev/null +++ b/src/core/url-beautifier/navigation/parser.ts @@ -0,0 +1,22 @@ +import { Request } from 'groupby-api'; +import * as URI from 'urijs'; +import { UrlParser } from '../index'; + +export default class NavigationUrlParser extends UrlParser { + + parse = (url: string) => { + const uri = URI.parse(url); + const paths = uri.path.split('/').filter((val) => val); + + if (paths.length > 1) { + throw new Error('path contains more than one part'); + } + + const name = decodeURIComponent(paths[0]).replace(/-/g, ' '); + if (!(name in this.config.navigations)) { + throw new Error(`no navigation mapping found for ${name}`); + } + + return this.config.navigations[name]; + } +} diff --git a/src/core/url-beautifier/search/generator.ts b/src/core/url-beautifier/search/generator.ts new file mode 100644 index 00000000..61348058 --- /dev/null +++ b/src/core/url-beautifier/search/generator.ts @@ -0,0 +1,146 @@ +import { Store } from '@storefront/flux-capacitor'; +import { Request, SelectedRangeRefinement, SelectedRefinement, SelectedValueRefinement } from 'groupby-api'; +import UrlBeautifier, { UrlGenerator } from '../index'; + +export default class SearchUrlGenerator extends UrlGenerator { + + build = (request: UrlBeautifier.SearchRequest) => { + const paths = []; + const query = {}; + + // layman's clone + request = { ...request, refinements: [...request.refinements] }; + + if (request.query) { + paths.push(request.query); + } + + if (this.config.useReferenceKeys) { + paths.push(...this.convertReferencedRefinements(request)); + } else { + paths.push(...SearchUrlGenerator.convertPathRefinements(request)); + } + + if (request.refinements.length !== 0) { + query[this.config.params.refinements] = SearchUrlGenerator.convertTrailingRefinements(request); + } + + query[this.config.params.pageSize] = request.pageSize; + query[this.config.params.page] = request.page; + + return this.buildUrl(paths, query); + } + + // tslint:disable-next-line max-line-length + convertReferencedRefinements(request: UrlBeautifier.SearchRequest) { + const paths = []; + const countMap = {}; + const { map, keys } = SearchUrlGenerator.generateRefinementMap(request.refinements, this.config.refinementMapping); + + // add refinements + keys.forEach((key) => { + const refinements = map[key]; + countMap[key] = refinements.length; + refinements.map(SearchUrlGenerator.convertToSelectedValueRefinement) + .sort(SearchUrlGenerator.refinementsComparator) + .forEach((selectedValueRefinement) => paths.push(selectedValueRefinement.value)); + }); + + // add reference key + if (keys.length !== 0 || request.query) { + let referenceKey = ''; + + if (request.query) { + referenceKey += this.config.queryToken; + } + keys.forEach((key) => referenceKey += key.repeat(countMap[key])); + + paths.push(referenceKey); + } + + return paths; + } + + buildUrl(paths: string[], query: object) { + let url = `/${paths.map((path) => encodeURIComponent(path)).join('/')}`; + + if (this.config.suffix) { + url += `/${this.config.suffix.replace(/^\/+/, '')}`; + } + + const queryPart = Object.keys(query) + .sort() + .map((key) => `${key}=${encodeURIComponent(query[key])}`) + .join('&'); + + if (queryPart) { + url += '?' + queryPart; + } + + return url.replace(/\s|%20/g, '-'); + } + + static convertPathRefinements({ refinements }: UrlBeautifier.SearchRequest) { + const paths = []; + const valueRefinements = []; + + for (let i = refinements.length - 1; i >= 0; --i) { + if (refinements[i].type === 'Value') { + valueRefinements.push(...refinements.splice(i, 1)); + } + } + + valueRefinements.map(SearchUrlGenerator.convertToSelectedValueRefinement) + .sort(SearchUrlGenerator.refinementsComparator) + .forEach((selectedValueRefinement) => + paths.push(selectedValueRefinement.value, selectedValueRefinement.navigationName)); + + return paths; + } + + static convertTrailingRefinements({ refinements }: UrlBeautifier.SearchRequest) { + return refinements.sort((lhs, rhs) => lhs.navigationName.localeCompare(rhs.navigationName)) + .map(SearchUrlGenerator.stringifyRefinement) + .join('~'); + } + + static convertToSelectedValueRefinement(refinement: SelectedRefinement): SelectedValueRefinement { + if (refinement.type === 'Value') { + return refinement; + } else { + throw new Error('cannot map range refinements'); + } + } + + static refinementsComparator(lhs: SelectedValueRefinement, rhs: SelectedValueRefinement) { + let comparison = lhs.navigationName.localeCompare(rhs.navigationName); + if (comparison === 0) { + comparison = lhs.value.localeCompare(rhs.value); + } + return comparison; + } + + static stringifyRefinement(refinement: SelectedRefinement): string { + const name = refinement.navigationName; + if (refinement.type === 'Value') { + return `${name}:${(refinement).value}`; + } else { + return `${name}:${(refinement).low}..${(refinement).high}`; + } + } + + static generateRefinementMap(refinements: SelectedRefinement[], refinementMapping: any[]) { + const refinementMap = {}; + const refinementKeys = []; + for (let mapping of refinementMapping) { + const key = Object.keys(mapping)[0]; + const matchingRefinements = refinements.filter((refinement) => refinement.navigationName === mapping[key]); + if (matchingRefinements.length !== 0) { + refinementKeys.push(key); + refinementMap[key] = matchingRefinements; + matchingRefinements.forEach((ref) => refinements.splice(refinements.indexOf(ref), 1)); + } + } + return { map: refinementMap, keys: refinementKeys }; + } +} diff --git a/src/core/url-beautifier/search/parser.ts b/src/core/url-beautifier/search/parser.ts new file mode 100644 index 00000000..badac87d --- /dev/null +++ b/src/core/url-beautifier/search/parser.ts @@ -0,0 +1,113 @@ +import { Request, SelectedRangeRefinement, SelectedRefinement, SelectedValueRefinement } from 'groupby-api'; +import * as queryString from 'query-string'; +import * as URI from 'urijs'; +import UrlBeautifier, { UrlParser } from '../index'; + +export default class SearchUrlParser extends UrlParser { + + suffixPattern: RegExp = RegExp(`${this.config.suffix}$`); + + parse = (url: string): UrlBeautifier.SearchRequest => { + const uri = URI.parse(url); + const path = uri.path.replace(this.suffixPattern, '') + .split('/').filter((val) => val); + + const query = this.config.useReferenceKeys + ? this.parsePathWithReferenceKeys(path) + : SearchUrlParser.parsePathWithoutReferenceKeys(path); + + const queryVariables = queryString.parse(uri.query); + + const refinements = queryVariables[this.config.params.refinements]; + if (refinements) { + query.refinements.push(...SearchUrlParser.extractUnmapped(refinements)); + } + + const pageSize = parseInt(queryVariables[this.config.params.pageSize]); + if (pageSize) { + query.pageSize = pageSize; + } + + const page = parseInt(queryVariables[this.config.params.page]); + if (page) { + query.page = page; + } + + return query; + } + + parsePathWithReferenceKeys(path: string[]): Partial { + const keys = (path.pop() || '').split(''); + const refinements = []; + let query; + + if (path.length < keys.length) { + throw new Error('token reference is invalid'); + } + + const map = this.generateRefinementMapping(); + keys.forEach((key) => { + if (!(key in map || key === this.config.queryToken)) { + throw new Error(`unexpected token '${key}' found in reference`); + } + }); + + // remove prefixed paths + path.splice(0, path.length - keys.length); + + for (let i = 0; i < keys.length; i++) { + if (keys[i] === this.config.queryToken) { + query = SearchUrlParser.decode(path[i]); + } else { + refinements.push(...SearchUrlParser.extractRefinements(path[i], map[keys[i]])); + } + } + + return { query, refinements }; + } + + generateRefinementMapping() { + return this.config.refinementMapping.reduce((map, mapping) => Object.assign(map, mapping), {}); + } + + static parsePathWithoutReferenceKeys(path: string[]): Partial { + const refinements = []; + let query; + + if (path.length % 2 === 1) { + query = SearchUrlParser.decode(path.shift()); + } + + while (path.length > 0) { + const value = SearchUrlParser.decode(path.shift()); + const navigationName = path.shift(); + refinements.push({ navigationName, type: 'Value', value }); + } + + return { query, refinements }; + } + + static extractUnmapped(refinementString: string): SelectedRefinement[] { + return refinementString.split('~') + .map(SearchUrlParser.decode) + .map((refinement) => { + const [navigationName, value] = refinement.split(':'); + if (value.indexOf('..') >= 0) { + const [low, high] = value.split('..'); + return { navigationName, low: Number(low), high: Number(high), type: 'Range' }; + } else { + return { navigationName, value, type: 'Value' }; + } + }); + } + + static extractRefinements(refinementString: string, navigationName: string) { + const refinementStrings = refinementString.split('~'); + + return refinementStrings.map((value) => ({ navigationName, type: 'Value', value: SearchUrlParser.decode(value) })); + } + + static decode(value: string): string { + return decodeURIComponent(value.replace(/-/g, ' ')); + } +} diff --git a/src/globals.ts b/src/globals.ts index 23ae2e1a..938e5488 100644 --- a/src/globals.ts +++ b/src/globals.ts @@ -1,6 +1,8 @@ -import { TAGS } from './core/system'; import * as utils from './core/utils'; -import { RIOT } from './storefront'; + +export const CORE = Symbol.for('storfront_core_service'); +export const RIOT = Symbol.for('storefront_riot_instance'); +export const TAGS = Symbol.for('storefront_tags'); const GLOBALS = (typeof global === 'undefined' ? window : global) || {}; diff --git a/src/index.ts b/src/index.ts index d4bb6a50..5dc6a7c4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import './globals'; import { utils } from './core'; -import StoreFront, { RIOT } from './storefront'; +import StoreFront from './storefront'; export * from './core'; export { Events, Selectors, Store } from '@storefront/flux-capacitor'; diff --git a/src/services/autocomplete.ts b/src/services/autocomplete.ts index 7070e261..e784455d 100644 --- a/src/services/autocomplete.ts +++ b/src/services/autocomplete.ts @@ -1,10 +1,10 @@ import FluxCapacitor, { Events, Selectors, Store } from '@storefront/flux-capacitor'; import { core } from '../core'; +import LazyService from '../core/service/lazy'; import StoreFront from '../storefront'; -import LazyService from './lazy'; @core -class Autocomplete extends LazyService { +class AutocompleteService extends LazyService { lazyInit() { this.app.flux.on(Events.AUTOCOMPLETE_QUERY_UPDATED, this.updateSearchTerms); @@ -18,4 +18,4 @@ class Autocomplete extends LazyService { this.app.flux.saytProducts(suggestion) } -export default Autocomplete; +export default AutocompleteService; diff --git a/src/services/collections.ts b/src/services/collections.ts index 807a122b..8c75ad73 100644 --- a/src/services/collections.ts +++ b/src/services/collections.ts @@ -1,10 +1,10 @@ import FluxCapacitor, { Events, Selectors, Store } from '@storefront/flux-capacitor'; import { core } from '../core'; +import LazyService from '../core/service/lazy'; import StoreFront from '../storefront'; -import LazyService from './lazy'; @core -class Collections extends LazyService { +class CollectionsService extends LazyService { lazyInit() { this.app.flux.on(Events.RECALL_CHANGED, this.waitForResults); @@ -20,4 +20,4 @@ class Collections extends LazyService { } } -export default Collections; +export default CollectionsService; diff --git a/src/services/details.ts b/src/services/details.ts index 7fbdd8fe..963bf0cb 100644 --- a/src/services/details.ts +++ b/src/services/details.ts @@ -1,12 +1,10 @@ import { Events } from '@storefront/flux-capacitor'; -import { Service } from '.'; import { core } from '../core'; +import { BaseService } from '../core/service'; import StoreFront from '../storefront'; @core -class Details implements Service { - - constructor(private app: StoreFront) { } +class DetailsService extends BaseService { init() { this.app.flux.on(Events.DETAILS_ID_UPDATED, this.fetchDetails); @@ -15,4 +13,4 @@ class Details implements Service { fetchDetails = (id: string) => this.app.flux.details(id); } -export default Details; +export default DetailsService; diff --git a/src/services/index.ts b/src/services/index.ts index ba19a375..ec5c1dc4 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -1,3 +1,4 @@ +import Service from '../core/service'; import StoreFront from '../storefront'; import autocomplete from './autocomplete'; import collections from './collections'; @@ -36,20 +37,4 @@ export interface CoreServices { url: url; } -export namespace Service { - export namespace Constructor { - export type Map = { [key: string]: Constructor }; - } - - export interface Constructor { - new (app: StoreFront, config: any): Service; - } - - export type Map = { [key: string]: Service }; - - export type Options = Service.Constructor | T | false; -} - -export interface Service { - init(services: Service.Map): void; -} +export type SystemServices = CoreServices & Service.Map; diff --git a/src/services/lazy.ts b/src/services/lazy.ts deleted file mode 100644 index f8c95de6..00000000 --- a/src/services/lazy.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Service } from '.'; -import StoreFront from '../storefront'; - -abstract class LazyService implements Service { - - initialized: boolean = false; - registered: any[] = []; - - constructor(protected app: StoreFront) { } - - init() { - // no-op - } - - abstract lazyInit(); - - register(tag: any) { - this.registered.push(tag); - if (!this.initialized) { - this.initialized = true; - this.lazyInit(); - } - } -} -export default LazyService; diff --git a/src/services/logging.ts b/src/services/logging.ts index 47bb6447..24c2f8e8 100644 --- a/src/services/logging.ts +++ b/src/services/logging.ts @@ -1,6 +1,6 @@ import { Events } from '@storefront/flux-capacitor'; -import { Service } from '.'; import { core, utils } from '../core'; +import { BaseService } from '../core/service'; import StoreFront from '../storefront'; import Tag from '../tag'; import Lifecycle from '../tag/lifecycle'; @@ -23,15 +23,16 @@ export const ALIASING_COLOURS = { }; @core -class Logging implements Service { +class LoggingService extends BaseService { - constructor(private app: StoreFront, private opts: Logging.Options) { + constructor(app: StoreFront, opts: LoggingService.Options) { + super(app, opts); utils.log.setLevel(opts.level); } init() { const debugAll = this.opts.debug && typeof this.opts.debug !== 'object'; - const debug: Logging.Debug = this.opts.debug || {}; + const debug: LoggingService.Debug = this.opts.debug || {}; this.app.log = utils.log; @@ -70,7 +71,7 @@ class Logging implements Service { ) } -namespace Logging { +namespace LoggingService { export interface Options { level?: string; debug?: boolean | Debug; @@ -83,4 +84,4 @@ namespace Logging { } } -export default Logging; +export default LoggingService; diff --git a/src/services/search.ts b/src/services/search.ts index 6e2b2b78..a2a6b705 100644 --- a/src/services/search.ts +++ b/src/services/search.ts @@ -1,12 +1,10 @@ import { Events } from '@storefront/flux-capacitor'; -import { Service } from '.'; import { core } from '../core'; +import { BaseService } from '../core/service'; import StoreFront from '../storefront'; @core -class Search implements Service { - - constructor(private app: StoreFront) { } +class SearchService extends BaseService { init() { this.app.flux.on(Events.SEARCH_CHANGED, this.fetchProducts); @@ -18,8 +16,8 @@ class Search implements Service { } } -namespace Search { +namespace SearchService { export interface Options { } } -export default Search; +export default SearchService; diff --git a/src/services/url.ts b/src/services/url.ts index ebf316ef..633ef440 100644 --- a/src/services/url.ts +++ b/src/services/url.ts @@ -1,26 +1,49 @@ -import { Events } from '@storefront/flux-capacitor'; -import { Service } from '.'; +import { Events, Selectors, Store } from '@storefront/flux-capacitor'; import { core } from '../core'; +import { BaseService } from '../core/service'; +// import UrlBeautifier from '../core/url-beautifier'; import StoreFront from '../storefront'; @core -class Url implements Service { +class UrlService extends BaseService { - constructor(private app: StoreFront) { } + // beautifier: UrlBeautifier = new UrlBeautifier(this.opts); init() { - this.app.flux.on('history:save', this.updateHistory); + this.app.flux.on(Events.HISTORY_SAVE, this.updateHistory); window.addEventListener('popstate', this.rewind); } - updateHistory = (store: any) => window.history.pushState({ store }, '', ''); + updateHistory = (state: Store.State) => { + // const urlState: UrlBeautifier.SearchRequest = { + // query: Selectors.query(state), + // page: state.data.page.current, + // pageSize: Selectors.pageSize(state), + // refinements: Selectors.selectedRefinements(state) + // }; - rewind = (event: PopStateEvent) => - this.app.flux.store.dispatch(this.app.flux.actions.refreshState(event.state.store)) + // this.app.log.warn(this.beautifier.build('search', urlState)); + window.history.pushState({ store: state.data }, '', ''); + } + + rewind = (event: PopStateEvent) => { + if (event.state && event.state.store) { + this.app.flux.store.dispatch(this.app.flux.actions.refreshState(event.state.store)); + } + } } -namespace Url { - export interface Options { } +namespace UrlService { + export interface Options { + // beautifier?: UrlBeautifier.Configuration; + routes?: Routes; + } + + export interface Routes { + search: string; + details: string; + navigation: string; + } } -export default Url; +export default UrlService; diff --git a/src/storefront.ts b/src/storefront.ts index 68237414..66b44dbf 100644 --- a/src/storefront.ts +++ b/src/storefront.ts @@ -1,13 +1,11 @@ -import FluxCapacitor, { Events } from '@storefront/flux-capacitor'; +import FluxCapacitor from '@storefront/flux-capacitor'; import Configuration from './core/configuration'; -import System, { TAGS } from './core/system'; +import System from './core/system'; import { log, register, riot } from './core/utils'; import Globals from './globals'; -import { CoreServices, Service } from './services'; +import { SystemServices } from './services'; import services from './services'; -export const RIOT = Symbol.for('storefront_riot_instance'); - // declare var VERSION; export default class StoreFront { @@ -19,7 +17,7 @@ export default class StoreFront { log: typeof log; flux: FluxCapacitor; - services: Service.Map & CoreServices; + services: SystemServices; constructor(public config: Configuration = { options: {} }) { if (StoreFront._instance) { diff --git a/src/tag/index.ts b/src/tag/index.ts index 6bb83ee9..18412ef4 100644 --- a/src/tag/index.ts +++ b/src/tag/index.ts @@ -1,6 +1,6 @@ import FluxCapacitor, { Actions, ActionCreator, Events, Store } from '@storefront/flux-capacitor'; import { utils, Configuration } from '../core'; -import { CoreServices, Service } from '../services'; +import { SystemServices } from '../services'; import StoreFront from '../storefront'; import Alias from './alias'; import Attribute from './attribute'; @@ -154,7 +154,7 @@ namespace Tag { export interface Mixin { config: Configuration; flux: FluxCapacitor; - services: Service.Map & CoreServices; + services: SystemServices; log: typeof utils.log; init: (this: utils.riot.TagInterface) => void; } diff --git a/test/unit/core/decorators.ts b/test/unit/core/decorators.ts index 8557927e..bb47137e 100644 --- a/test/unit/core/decorators.ts +++ b/test/unit/core/decorators.ts @@ -1,5 +1,5 @@ import { core, css, tag, view } from '../../../src/core/decorators'; -import { CORE } from '../../../src/core/system'; +import { CORE } from '../../../src/globals'; import StoreFront from '../../../src/storefront'; import Tag from '../../../src/tag'; import Attribute from '../../../src/tag/attribute'; diff --git a/test/unit/core/system.ts b/test/unit/core/system.ts index e38f69c2..1f549280 100644 --- a/test/unit/core/system.ts +++ b/test/unit/core/system.ts @@ -1,7 +1,7 @@ import * as fluxPkg from '@storefront/flux-capacitor'; import * as riot from 'riot'; import { core, utils, Configuration } from '../../../src/core'; -import System, { TAGS } from '../../../src/core/system'; +import System from '../../../src/core/system'; import Globals from '../../../src/globals'; import Tag from '../../../src/tag'; import suite from '../_suite'; diff --git a/test/unit/services/logging.ts b/test/unit/services/logging.ts index ca5f8d02..5f3d4c21 100644 --- a/test/unit/services/logging.ts +++ b/test/unit/services/logging.ts @@ -1,5 +1,5 @@ import { log } from '../../../src/core/utils'; -import Logging from '../../../src/services/logging'; +import LoggingService from '../../../src/services/logging'; import suite from '../_suite'; suite('Logging', ({ expect, stub }) => { @@ -8,7 +8,7 @@ suite('Logging', ({ expect, stub }) => { it('should set log level', () => { const setLevel = stub(log, 'setLevel'); - new Logging({}, { level: 'warn' }); // tslint:disable-line:no-unused-new + new LoggingService({}, { level: 'warn' }); // tslint:disable-line:no-unused-new expect(setLevel).to.be.calledWith('warn'); }); @@ -18,7 +18,7 @@ suite('Logging', ({ expect, stub }) => { it('should set log', () => { const app: any = { flux: { on: () => null } }; - new Logging(app, { level: 'debug' }).init(); + new LoggingService(app, { level: 'debug' }).init(); expect(app.log).to.eq(log); }); From 6c609c84af0fd91826d7a86033869b2ec4697db5 Mon Sep 17 00:00:00 2001 From: Ben Teichman Date: Fri, 9 Jun 2017 14:09:42 -0400 Subject: [PATCH 2/8] without tests --- src/core/defaults.ts | 12 +++ src/core/url-beautifier/details/generator.ts | 21 +++--- src/core/url-beautifier/details/parser.ts | 41 +++++----- src/core/url-beautifier/handler.ts | 32 ++++++++ src/core/url-beautifier/index.ts | 55 +------------- .../url-beautifier/navigation/generator.ts | 3 +- src/core/url-beautifier/navigation/parser.ts | 9 ++- src/core/url-beautifier/search/generator.ts | 32 ++++---- src/core/url-beautifier/search/parser.ts | 5 +- src/services/autocomplete.ts | 2 +- src/services/collections.ts | 2 +- src/services/details.ts | 2 +- src/services/logging.ts | 3 +- src/services/search.ts | 2 +- src/services/url.ts | 75 +++++++++++++++---- 15 files changed, 173 insertions(+), 123 deletions(-) create mode 100644 src/core/url-beautifier/handler.ts diff --git a/src/core/defaults.ts b/src/core/defaults.ts index af112462..3e95e2c9 100644 --- a/src/core/defaults.ts +++ b/src/core/defaults.ts @@ -21,6 +21,18 @@ const DEFAULTS: Partial = { level: 'debug' }, url: { + beautifier: { + refinementMapping: [], + params: { + refinements: 'refinements', + page: 'page', + pageSize: 'page_size' + }, + queryToken: 'q', + suffix: '', + useReferenceKeys: true, + navigations: {} + }, routes: { search: '/search', details: '/details', diff --git a/src/core/url-beautifier/details/generator.ts b/src/core/url-beautifier/details/generator.ts index dd3079f2..0d97b877 100644 --- a/src/core/url-beautifier/details/generator.ts +++ b/src/core/url-beautifier/details/generator.ts @@ -1,39 +1,40 @@ import { SelectedValueRefinement } from 'groupby-api'; -import UrlBeautifier, { UrlGenerator } from '../index'; +import UrlBeautifier from '..'; +import { UrlGenerator } from '../handler'; export default class DetailsUrlGenerator extends UrlGenerator { build = (request: UrlBeautifier.DetailsRequest) => { - let paths = []; + let path = []; if (request.refinements.length !== 0) { if (this.config.useReferenceKeys) { - paths = DetailsUrlGenerator.convertRefinements(request.refinements, this.refinementsToKeys); + path = DetailsUrlGenerator.convertRefinements(request.refinements, this.refinementsToKeys); } else { - request.refinements.forEach(({ value, navigationName }) => paths.push(value, navigationName)); + request.refinements.forEach(({ value, navigationName }) => path.push(value, navigationName)); } } - paths.unshift(request.title); - paths.push(request.id); + path.unshift(request.title); + path.push(request.id); - return `/${paths.map((path) => encodeURIComponent(path.replace(/\s/g, '-'))).join('/')}`; + return `/${path.map((part) => encodeURIComponent(part.replace(/\s/g, '-'))).join('/')}`; } static convertRefinements(refinements: SelectedValueRefinement[], refinementsToKeys: object) { let referenceKeys = ''; return refinements.sort(DetailsUrlGenerator.refinementsComparator) - .reduce((paths, { navigationName, value }) => { + .reduce((path, { navigationName, value }) => { if (!(navigationName in refinementsToKeys)) { throw new Error(`no mapping found for navigation '${navigationName}'`); } - paths.push(value); + path.push(value); referenceKeys += refinementsToKeys[navigationName]; - return paths; + return path; }, []) .concat(referenceKeys); } diff --git a/src/core/url-beautifier/details/parser.ts b/src/core/url-beautifier/details/parser.ts index 927fac7b..5898b282 100644 --- a/src/core/url-beautifier/details/parser.ts +++ b/src/core/url-beautifier/details/parser.ts @@ -1,63 +1,66 @@ import * as URI from 'urijs'; -import UrlBeautifier, { UrlParser } from '../index'; +import UrlBeautifier from '..'; +import { UrlParser } from '../handler'; -export default class DetailsUrlParser extends UrlParser { +export class DetailsUrlParser extends UrlParser { parse = (url: string) => { const uri = URI.parse(url); - const paths = uri.path.split('/') + const path = uri.path.split('/') .filter((val) => val) .map((val) => decodeURIComponent(val).replace(/-/g, ' ')); - if (paths.length < 2) { + if (path.length < 2) { throw new Error('path has fewer than two parts'); } - const title = paths.shift(); - const id = paths.pop(); + const title = path.shift(); + const id = path.pop(); let refinements = []; - if (paths.length !== 0) { + if (path.length !== 0) { if (!this.config.useReferenceKeys) { - refinements = DetailsUrlParser.extractPathRefinements(paths); + refinements = DetailsUrlParser.extractPathRefinements(path); } else { - refinements = DetailsUrlParser.extractReferencesRefinements(paths, this.keysToRefinements); + refinements = DetailsUrlParser.extractReferencesRefinements(path, this.keysToRefinements); } } return { id, refinements, title }; } - static extractReferencesRefinements(paths: string[], keysToRefinements: { [key: string]: string }) { - if (paths.length < 2) { + static extractReferencesRefinements(path: string[], keysToRefinements: { [key: string]: string }) { + if (path.length < 2) { throw new Error('path has wrong number of parts'); } - const referenceKeys = paths.pop().split(''); + const referenceKeys = path.pop().split(''); - if (paths.length !== referenceKeys.length) { + if (path.length !== referenceKeys.length) { throw new Error('token reference is invalid'); } - return paths.map((value) => ({ + return path.map((value) => ({ value, navigationName: keysToRefinements[referenceKeys.shift()], type: 'Value' })); } - static extractPathRefinements(paths: string[]) { - if (paths.length % 2 !== 0) { + static extractPathRefinements(path: string[]) { + if (path.length % 2 !== 0) { throw new Error('path has an odd number of parts'); } const refinements = []; - while (paths.length !== 0) { - const value = paths.shift(); - const navigationName = paths.shift(); + while (path.length) { + const value = path.shift(); + const navigationName = path.shift(); refinements.push({ navigationName, value, type: 'Value' }); } return refinements; } } + +export default DetailsUrlParser; diff --git a/src/core/url-beautifier/handler.ts b/src/core/url-beautifier/handler.ts new file mode 100644 index 00000000..0361961e --- /dev/null +++ b/src/core/url-beautifier/handler.ts @@ -0,0 +1,32 @@ +import UrlBeautifier from '.'; + +export abstract class UrlHandler { + + protected config: UrlBeautifier.Configuration = this.beautifier.config; + + constructor(protected beautifier: UrlBeautifier) { } + + get keysToRefinements() { + return this.config.refinementMapping.reduce((map, mapping) => { + const key = Object.keys(mapping)[0]; + return Object.assign(map, { [key]: mapping[key] }); + }, {}); + } + + get refinementsToKeys() { + return this.config.refinementMapping.reduce((map, mapping) => { + const key = Object.keys(mapping)[0]; + return Object.assign(map, { [mapping[key]]: key }); + }, {}); + } +} + +export abstract class UrlParser extends UrlHandler implements UrlBeautifier.Parser { + + abstract parse: (url: string) => T; +} + +export abstract class UrlGenerator extends UrlHandler implements UrlBeautifier.Generator { + + abstract build: (request: T) => string; +} diff --git a/src/core/url-beautifier/index.ts b/src/core/url-beautifier/index.ts index a50b5f03..d77f45e6 100644 --- a/src/core/url-beautifier/index.ts +++ b/src/core/url-beautifier/index.ts @@ -4,28 +4,12 @@ import * as URI from 'urijs'; import UrlService from '../../services/url'; import BeautifierFactory from './factory'; -const DEFAULTS: UrlBeautifier.Configuration = { - refinementMapping: [], - params: { - refinements: 'refinements', - page: 'page', - pageSize: 'page_size' - }, - queryToken: 'q', - suffix: '', - useReferenceKeys: true, - navigations: {} -}; - class UrlBeautifier { - config: UrlBeautifier.Configuration; - routes: UrlService.Routes; beautifiers: UrlBeautifier.Beautifiers = BeautifierFactory.create(this); - constructor(options: UrlService.Options) { - this.config = { ...DEFAULTS, ...this.config }; - this.routes = options.routes; + constructor(public routes: UrlService.Routes, public config: UrlBeautifier.Configuration) { + this.beautifiers = BeautifierFactory.create(this); const keys = []; for (let mapping of this.config.refinementMapping) { @@ -49,7 +33,7 @@ class UrlBeautifier { build(type: string, request: object) { if (type in this.routes) { - return this.beautifiers[type].build(request); + return `${this.routes[type]}${this.beautifiers[type].build(request)}`; } else { throw new Error('invalid route'); } @@ -71,7 +55,6 @@ class UrlBeautifier { } namespace UrlBeautifier { - export interface Configuration { refinementMapping?: any[]; params?: { @@ -122,36 +105,4 @@ namespace UrlBeautifier { } } -export { UrlBeautifier }; export default UrlBeautifier; - -export abstract class UrlHandler { - - protected config: UrlBeautifier.Configuration = this.beautifier.config; - - constructor(protected beautifier: UrlBeautifier) { } - - get keysToRefinements() { - return this.config.refinementMapping.reduce((map, mapping) => { - const key = Object.keys(mapping)[0]; - return Object.assign(map, { [key]: mapping[key] }); - }, {}); - } - - get refinementsToKeys() { - return this.config.refinementMapping.reduce((map, mapping) => { - const key = Object.keys(mapping)[0]; - return Object.assign(map, { [mapping[key]]: key }); - }, {}); - } -} - -export abstract class UrlParser extends UrlHandler implements UrlBeautifier.Parser { - - abstract parse: (url: string) => T; -} - -export abstract class UrlGenerator extends UrlHandler implements UrlBeautifier.Generator { - - abstract build: (request: T) => string; -} diff --git a/src/core/url-beautifier/navigation/generator.ts b/src/core/url-beautifier/navigation/generator.ts index 8c59a5d6..fc26dad1 100644 --- a/src/core/url-beautifier/navigation/generator.ts +++ b/src/core/url-beautifier/navigation/generator.ts @@ -1,5 +1,6 @@ import { Request } from 'groupby-api'; -import { UrlGenerator } from '../index'; +import UrlBeautifier from '..'; +import { UrlGenerator } from '../handler'; export default class NavigationUrlGenerator extends UrlGenerator { diff --git a/src/core/url-beautifier/navigation/parser.ts b/src/core/url-beautifier/navigation/parser.ts index 8d9e49c9..ffdf5e6f 100644 --- a/src/core/url-beautifier/navigation/parser.ts +++ b/src/core/url-beautifier/navigation/parser.ts @@ -1,18 +1,19 @@ import { Request } from 'groupby-api'; import * as URI from 'urijs'; -import { UrlParser } from '../index'; +import UrlBeautifier from '..'; +import { UrlParser } from '../handler'; export default class NavigationUrlParser extends UrlParser { parse = (url: string) => { const uri = URI.parse(url); - const paths = uri.path.split('/').filter((val) => val); + const path = uri.path.split('/').filter((val) => val); - if (paths.length > 1) { + if (path.length > 1) { throw new Error('path contains more than one part'); } - const name = decodeURIComponent(paths[0]).replace(/-/g, ' '); + const name = decodeURIComponent(path[0]).replace(/-/g, ' '); if (!(name in this.config.navigations)) { throw new Error(`no navigation mapping found for ${name}`); } diff --git a/src/core/url-beautifier/search/generator.ts b/src/core/url-beautifier/search/generator.ts index 61348058..149f64b4 100644 --- a/src/core/url-beautifier/search/generator.ts +++ b/src/core/url-beautifier/search/generator.ts @@ -1,24 +1,25 @@ import { Store } from '@storefront/flux-capacitor'; import { Request, SelectedRangeRefinement, SelectedRefinement, SelectedValueRefinement } from 'groupby-api'; -import UrlBeautifier, { UrlGenerator } from '../index'; +import UrlBeautifier from '..'; +import { UrlGenerator } from '../handler'; export default class SearchUrlGenerator extends UrlGenerator { build = (request: UrlBeautifier.SearchRequest) => { - const paths = []; + const path = []; const query = {}; // layman's clone request = { ...request, refinements: [...request.refinements] }; if (request.query) { - paths.push(request.query); + path.push(request.query); } if (this.config.useReferenceKeys) { - paths.push(...this.convertReferencedRefinements(request)); + path.push(...this.convertReferencedRefinements(request)); } else { - paths.push(...SearchUrlGenerator.convertPathRefinements(request)); + path.push(...SearchUrlGenerator.convertPathRefinements(request)); } if (request.refinements.length !== 0) { @@ -28,12 +29,11 @@ export default class SearchUrlGenerator extends UrlGenerator paths.push(selectedValueRefinement.value)); + .forEach((selectedValueRefinement) => path.push(selectedValueRefinement.value)); }); // add reference key @@ -55,14 +55,14 @@ export default class SearchUrlGenerator extends UrlGenerator referenceKey += key.repeat(countMap[key])); - paths.push(referenceKey); + path.push(referenceKey); } - return paths; + return path; } - buildUrl(paths: string[], query: object) { - let url = `/${paths.map((path) => encodeURIComponent(path)).join('/')}`; + buildUrl(path: string[], query: object) { + let url = `/${path.map((part) => encodeURIComponent(part)).join('/')}`; if (this.config.suffix) { url += `/${this.config.suffix.replace(/^\/+/, '')}`; @@ -81,7 +81,7 @@ export default class SearchUrlGenerator extends UrlGenerator= 0; --i) { @@ -93,9 +93,9 @@ export default class SearchUrlGenerator extends UrlGenerator - paths.push(selectedValueRefinement.value, selectedValueRefinement.navigationName)); + path.push(selectedValueRefinement.value, selectedValueRefinement.navigationName)); - return paths; + return path; } static convertTrailingRefinements({ refinements }: UrlBeautifier.SearchRequest) { diff --git a/src/core/url-beautifier/search/parser.ts b/src/core/url-beautifier/search/parser.ts index badac87d..1d55501d 100644 --- a/src/core/url-beautifier/search/parser.ts +++ b/src/core/url-beautifier/search/parser.ts @@ -1,7 +1,8 @@ import { Request, SelectedRangeRefinement, SelectedRefinement, SelectedValueRefinement } from 'groupby-api'; import * as queryString from 'query-string'; import * as URI from 'urijs'; -import UrlBeautifier, { UrlParser } from '../index'; +import UrlBeautifier from '..'; +import { UrlParser } from '../handler'; export default class SearchUrlParser extends UrlParser { @@ -78,7 +79,7 @@ export default class SearchUrlParser extends UrlParser 0) { + while (path.length) { const value = SearchUrlParser.decode(path.shift()); const navigationName = path.shift(); refinements.push({ navigationName, type: 'Value', value }); diff --git a/src/services/autocomplete.ts b/src/services/autocomplete.ts index e784455d..525716cc 100644 --- a/src/services/autocomplete.ts +++ b/src/services/autocomplete.ts @@ -1,5 +1,5 @@ import FluxCapacitor, { Events, Selectors, Store } from '@storefront/flux-capacitor'; -import { core } from '../core'; +import { core } from '../core/decorators'; import LazyService from '../core/service/lazy'; import StoreFront from '../storefront'; diff --git a/src/services/collections.ts b/src/services/collections.ts index 8c75ad73..3d505ac3 100644 --- a/src/services/collections.ts +++ b/src/services/collections.ts @@ -1,5 +1,5 @@ import FluxCapacitor, { Events, Selectors, Store } from '@storefront/flux-capacitor'; -import { core } from '../core'; +import { core } from '../core/decorators'; import LazyService from '../core/service/lazy'; import StoreFront from '../storefront'; diff --git a/src/services/details.ts b/src/services/details.ts index 963bf0cb..520e5d05 100644 --- a/src/services/details.ts +++ b/src/services/details.ts @@ -1,5 +1,5 @@ import { Events } from '@storefront/flux-capacitor'; -import { core } from '../core'; +import { core } from '../core/decorators'; import { BaseService } from '../core/service'; import StoreFront from '../storefront'; diff --git a/src/services/logging.ts b/src/services/logging.ts index 24c2f8e8..7ecf0af9 100644 --- a/src/services/logging.ts +++ b/src/services/logging.ts @@ -1,6 +1,7 @@ import { Events } from '@storefront/flux-capacitor'; -import { core, utils } from '../core'; +import { core } from '../core/decorators'; import { BaseService } from '../core/service'; +import * as utils from '../core/utils'; import StoreFront from '../storefront'; import Tag from '../tag'; import Lifecycle from '../tag/lifecycle'; diff --git a/src/services/search.ts b/src/services/search.ts index a2a6b705..78966cae 100644 --- a/src/services/search.ts +++ b/src/services/search.ts @@ -1,5 +1,5 @@ import { Events } from '@storefront/flux-capacitor'; -import { core } from '../core'; +import { core } from '../core/decorators'; import { BaseService } from '../core/service'; import StoreFront from '../storefront'; diff --git a/src/services/url.ts b/src/services/url.ts index 633ef440..0808e707 100644 --- a/src/services/url.ts +++ b/src/services/url.ts @@ -1,41 +1,88 @@ import { Events, Selectors, Store } from '@storefront/flux-capacitor'; -import { core } from '../core'; +import { core } from '../core/decorators'; import { BaseService } from '../core/service'; -// import UrlBeautifier from '../core/url-beautifier'; +import UrlBeautifier from '../core/url-beautifier'; import StoreFront from '../storefront'; @core class UrlService extends BaseService { - // beautifier: UrlBeautifier = new UrlBeautifier(this.opts); + beautifier: UrlBeautifier; init() { - this.app.flux.on(Events.HISTORY_SAVE, this.updateHistory); + this.beautifier = new UrlBeautifier(this.opts.routes, this.opts.beautifier); window.addEventListener('popstate', this.rewind); + this.readInitialUrl(); + } + + readInitialUrl() { + try { + const request = this.beautifier.parse(window.location.href); + const state = this.app.flux.store.getState().data; + const pageSizeIndex = state.page.sizes.items.indexOf(request.pageSize); + const newState = { + ...state, + query: { + ...state.query, + original: request.query + }, + page: { + ...state.page, + current: request.page, + sizes: { + ...state.page.sizes, + selected: pageSizeIndex === -1 ? state.page.sizes.selected : pageSizeIndex + } + }, + navigation: { + allIds: request.refinements.reduce((fields, refinement) => { + if (!fields.includes(refinement.navigationName)) { + fields.push(refinement.navigationName); + } + return fields; + }, []) + } + }; + + this.refreshState(newState); + this.app.flux.once(Events.HISTORY_SAVE, this.listenForHistoryChange); + this.app.flux.store.dispatch(this.app.flux.actions.fetchProducts()); + } catch (e) { + this.app.log.error('unable to parse state from url', e); + this.listenForHistoryChange(); + } + } + + listenForHistoryChange = () => { + this.app.flux.on(Events.HISTORY_SAVE, this.updateHistory); } updateHistory = (state: Store.State) => { - // const urlState: UrlBeautifier.SearchRequest = { - // query: Selectors.query(state), - // page: state.data.page.current, - // pageSize: Selectors.pageSize(state), - // refinements: Selectors.selectedRefinements(state) - // }; + const urlState: UrlBeautifier.SearchRequest = { + query: Selectors.query(state), + page: state.data.page.current, + pageSize: Selectors.pageSize(state), + refinements: Selectors.selectedRefinements(state) + }; - // this.app.log.warn(this.beautifier.build('search', urlState)); - window.history.pushState({ store: state.data }, '', ''); + const url = this.beautifier.build('search', urlState); + window.history.pushState({ store: state.data }, urlState.query, url); } rewind = (event: PopStateEvent) => { if (event.state && event.state.store) { - this.app.flux.store.dispatch(this.app.flux.actions.refreshState(event.state.store)); + this.refreshState(event.state.store); } } + + refreshState(state: any) { + this.app.flux.store.dispatch(this.app.flux.actions.refreshState(state)); + } } namespace UrlService { export interface Options { - // beautifier?: UrlBeautifier.Configuration; + beautifier?: UrlBeautifier.Configuration; routes?: Routes; } From a6c6719819f3bf37201a31827e13998bfa4f2fd2 Mon Sep 17 00:00:00 2001 From: Ben Teichman Date: Fri, 9 Jun 2017 14:11:22 -0400 Subject: [PATCH 3/8] update flux capacitor --- yarn.lock | 123 ++++++++++++++++++++++++++---------------------------- 1 file changed, 59 insertions(+), 64 deletions(-) diff --git a/yarn.lock b/yarn.lock index aa26af9c..369495dc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -59,8 +59,8 @@ urijs "^1.18.10" "@storefront/flux-capacitor@dev": - version "0.0.27-development" - resolved "https://registry.yarnpkg.com/@storefront/flux-capacitor/-/flux-capacitor-0.0.27-development.tgz#63149451c73d18d80bc82ae884aeedcc45d7ba73" + version "0.0.28-development" + resolved "https://registry.yarnpkg.com/@storefront/flux-capacitor/-/flux-capacitor-0.0.28-development.tgz#a0409b52ae54be638f4c8df97e4edd95929835e9" dependencies: "@types/uuid" "^2.0.29" eventemitter3 "^2.0.3" @@ -395,19 +395,19 @@ babel-code-frame@^6.16.0, babel-code-frame@^6.22.0: js-tokens "^3.0.0" babel-core@^6.13.2, babel-core@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.1.tgz#8c428564dce1e1f41fb337ec34f4c3b022b5ad83" + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.25.0.tgz#7dd42b0463c742e9d5296deb3ec67a9322dad729" dependencies: babel-code-frame "^6.22.0" - babel-generator "^6.24.1" + babel-generator "^6.25.0" babel-helpers "^6.24.1" babel-messages "^6.23.0" babel-register "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - babylon "^6.11.0" + babel-template "^6.25.0" + babel-traverse "^6.25.0" + babel-types "^6.25.0" + babylon "^6.17.2" convert-source-map "^1.1.0" debug "^2.1.1" json5 "^0.5.0" @@ -418,13 +418,13 @@ babel-core@^6.13.2, babel-core@^6.24.1: slash "^1.0.0" source-map "^0.5.0" -babel-generator@^6.18.0, babel-generator@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.24.1.tgz#e715f486c58ded25649d888944d52aa07c5d9497" +babel-generator@^6.18.0, babel-generator@^6.25.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.25.0.tgz#33a1af70d5f2890aeb465a4a7793c1df6a9ea9fc" dependencies: babel-messages "^6.23.0" babel-runtime "^6.22.0" - babel-types "^6.24.1" + babel-types "^6.25.0" detect-indent "^4.0.0" jsesc "^1.3.0" lodash "^4.2.0" @@ -848,42 +848,42 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0: core-js "^2.4.0" regenerator-runtime "^0.10.0" -babel-template@^6.16.0, babel-template@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.24.1.tgz#04ae514f1f93b3a2537f2a0f60a5a45fb8308333" +babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.25.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.25.0.tgz#665241166b7c2aa4c619d71e192969552b10c071" dependencies: babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - babylon "^6.11.0" + babel-traverse "^6.25.0" + babel-types "^6.25.0" + babylon "^6.17.2" lodash "^4.2.0" -babel-traverse@^6.18.0, babel-traverse@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.24.1.tgz#ab36673fd356f9a0948659e7b338d5feadb31695" +babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.25.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1" dependencies: babel-code-frame "^6.22.0" babel-messages "^6.23.0" babel-runtime "^6.22.0" - babel-types "^6.24.1" - babylon "^6.15.0" + babel-types "^6.25.0" + babylon "^6.17.2" debug "^2.2.0" globals "^9.0.0" invariant "^2.2.0" lodash "^4.2.0" -babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.24.1.tgz#a136879dc15b3606bda0d90c1fc74304c2ff0975" +babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.25.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e" dependencies: babel-runtime "^6.22.0" esutils "^2.0.2" lodash "^4.2.0" to-fast-properties "^1.0.1" -babylon@^6.11.0, babylon@^6.13.0, babylon@^6.15.0: - version "6.17.2" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.2.tgz#201d25ef5f892c41bae49488b08db0dd476e9f5c" +babylon@^6.13.0, babylon@^6.17.2: + version "6.17.3" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.3.tgz#1327d709950b558f204e5352587fd0290f8d8e48" balanced-match@^0.4.1: version "0.4.2" @@ -1080,8 +1080,8 @@ camelcase@^4.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" caniuse-lite@^1.0.30000670: - version "1.0.30000679" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000679.tgz#0fb5bb3658d4d4448f8f86a1c48df15664aa05ef" + version "1.0.30000680" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000680.tgz#d94d81294471617e86500f0aab90f11d22bc8934" capture-stack-trace@^1.0.0: version "1.0.0" @@ -1307,7 +1307,7 @@ conventional-commit-types@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/conventional-commit-types/-/conventional-commit-types-2.1.0.tgz#45d860386c9a2e6537ee91d8a1b61bd0411b3d04" -convert-source-map@^1.1.0, convert-source-map@^1.3.0: +convert-source-map@^1.1.0, convert-source-map@^1.3.0, convert-source-map@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" @@ -1603,8 +1603,8 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" electron-to-chromium@^1.3.11: - version "1.3.13" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.13.tgz#1b3a5eace6e087bb5e257a100b0cbfe81b2891fc" + version "1.3.14" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.14.tgz#64af0f9efd3c3c6acd57d71f83b49ca7ee9c4b43" elegant-spinner@^1.0.1: version "1.0.1" @@ -2255,8 +2255,8 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.5, resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" groupby-api@dev: - version "0.0.2-development" - resolved "https://registry.yarnpkg.com/groupby-api/-/groupby-api-0.0.2-development.tgz#6312d1ef8a8249cb80ecfc1824283a188ed65fc7" + version "0.0.3-development" + resolved "https://registry.yarnpkg.com/groupby-api/-/groupby-api-0.0.3-development.tgz#a4db30914c1e0efcf25fb2f9e1fd2215b6f29a91" dependencies: "@types/axios" "^0.9.35" "@types/clone" "^0.1.30" @@ -2766,12 +2766,6 @@ istanbul-reports@^1.1.1: dependencies: handlebars "^4.0.3" -jodid25519@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" - dependencies: - jsbn "~0.1.0" - js-cookie@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.1.4.tgz#da4ec503866f149d164cf25f579ef31015025d8d" @@ -3777,8 +3771,8 @@ rc@^1.0.1, rc@^1.1.5, rc@^1.1.6, rc@^1.1.7: strip-json-comments "~2.0.1" re-start@^1.14.6: - version "1.14.6" - resolved "https://registry.yarnpkg.com/re-start/-/re-start-1.14.6.tgz#2fe9368bd0410bb9f8ceb7e7e5453206dbc01506" + version "1.14.7" + resolved "https://registry.yarnpkg.com/re-start/-/re-start-1.14.7.tgz#019aea22d599bad0e80bf797f9286995a405bca3" dependencies: minimist "^1.2.0" start "^5.0.0" @@ -3795,8 +3789,8 @@ re-start@^1.14.6: start-pretty-reporter "^0.3.0" start-read "^0.3.0" start-release "^1.4.1" - start-split "^1.0.7" - start-typescript "^1.3.9" + start-split "^1.1.0" + start-typescript "^1.3.10" start-watch "^3.0.0" start-webpack "^0.2.0" start-write "^2.0.2" @@ -4108,7 +4102,7 @@ riot-cli@^3.0.2: rollup "^0.41.6" shelljs "^0.7.7" -riot-compiler@^3.2.1: +riot-compiler@^3.2.1, riot-compiler@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/riot-compiler/-/riot-compiler-3.2.3.tgz#db3cf1e920cd313f19eeac4f0c5314fc21db6314" @@ -4116,20 +4110,20 @@ riot-observable@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/riot-observable/-/riot-observable-3.0.0.tgz#8bbd2daf72a2141bb043082df4023dc504bad2eb" -riot-tmpl@^3.0.4: +riot-tmpl@^3.0.8: version "3.0.8" resolved "https://registry.yarnpkg.com/riot-tmpl/-/riot-tmpl-3.0.8.tgz#dd654e72a3a1520cb009cbef70c73856ded584a6" dependencies: eslint-config-riot "^1.0.0" riot@^3.4.4: - version "3.5.1" - resolved "https://registry.yarnpkg.com/riot/-/riot-3.5.1.tgz#a31755181873052ef44a90a448d11b8696e661e2" + version "3.6.0" + resolved "https://registry.yarnpkg.com/riot/-/riot-3.6.0.tgz#036706d43b401c4988488f8a5cafd189669441be" dependencies: riot-cli "^3.0.2" - riot-compiler "^3.2.1" + riot-compiler "^3.2.3" riot-observable "^3.0.0" - riot-tmpl "^3.0.4" + riot-tmpl "^3.0.8" simple-dom "0.3.2" simple-html-tokenizer "^0.4.1" @@ -4365,8 +4359,8 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" sshpk@^1.7.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c" + version "1.13.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" @@ -4375,7 +4369,6 @@ sshpk@^1.7.0: optionalDependencies: bcrypt-pbkdf "^1.0.0" ecc-jsbn "~0.1.1" - jodid25519 "^1.0.0" jsbn "~0.1.0" tweetnacl "~0.14.0" @@ -4491,19 +4484,21 @@ start-simple-cli@^4.0.0: commander "^2.9.0" resolve-cwd "^1.0.0" -start-split@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/start-split/-/start-split-1.0.7.tgz#263354eba7c4002bee8e0866d7aa626344530df2" +start-split@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/start-split/-/start-split-1.1.0.tgz#0198b2abf7eba888bb09e04873b5df84971977e2" dependencies: + convert-source-map "^1.5.0" start "^5.1.0" start-concurrent "^0.1.0" start-input-connector "^0.2.0" -start-typescript@^1.3.9: - version "1.3.9" - resolved "https://registry.yarnpkg.com/start-typescript/-/start-typescript-1.3.9.tgz#231202584c3adcc9041f2f7d08946de51dab1a21" +start-typescript@^1.3.10: + version "1.3.10" + resolved "https://registry.yarnpkg.com/start-typescript/-/start-typescript-1.3.10.tgz#390729bfb1cf2d8edbfac2e9ed7ec018dc67ebd2" dependencies: common-path-prefix "^1.0.0" + convert-source-map "^1.5.0" tsconfig "^6.0.0" typescript "^2.3.3" typescript-compiler "^1.4.1-2" @@ -4816,8 +4811,8 @@ tslint@^5.2.0: tsutils "^2.3.0" tsutils@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.3.0.tgz#96e661d7c2363f31adc8992ac67bbe7b7fc175e5" + version "2.4.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.4.0.tgz#ad4ce6dba0e5a3edbddf8626b7ca040782189fea" tty-browserify@0.0.0: version "0.0.0" From 963ef2347044dc6cd1d71d8f3958fb83f484130f Mon Sep 17 00:00:00 2001 From: Victoria Johns Date: Fri, 9 Jun 2017 17:22:54 -0400 Subject: [PATCH 4/8] Change to navigations and change error to warn --- src/services/url.ts | 5 +++-- yarn.lock | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/services/url.ts b/src/services/url.ts index 0808e707..18b2574a 100644 --- a/src/services/url.ts +++ b/src/services/url.ts @@ -34,7 +34,7 @@ class UrlService extends BaseService { selected: pageSizeIndex === -1 ? state.page.sizes.selected : pageSizeIndex } }, - navigation: { + navigations: { allIds: request.refinements.reduce((fields, refinement) => { if (!fields.includes(refinement.navigationName)) { fields.push(refinement.navigationName); @@ -48,7 +48,7 @@ class UrlService extends BaseService { this.app.flux.once(Events.HISTORY_SAVE, this.listenForHistoryChange); this.app.flux.store.dispatch(this.app.flux.actions.fetchProducts()); } catch (e) { - this.app.log.error('unable to parse state from url', e); + this.app.log.warn('unable to parse state from url', e); this.listenForHistoryChange(); } } @@ -71,6 +71,7 @@ class UrlService extends BaseService { rewind = (event: PopStateEvent) => { if (event.state && event.state.store) { + console.log(event.state.store); this.refreshState(event.state.store); } } diff --git a/yarn.lock b/yarn.lock index 369495dc..61a5247c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1435,13 +1435,13 @@ dateformat@^1.0.11: get-stdin "^4.0.1" meow "^3.3.0" -debug@2, debug@^2.1.3, debug@^2.6.3: +debug@2, debug@^2.6.3: version "2.6.8" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" dependencies: ms "2.0.0" -debug@2.2.0, debug@^2.1.1, debug@^2.2.0: +debug@2.2.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" dependencies: From 994610e47ece5e06cbe47d47a76eb2bd7461d363 Mon Sep 17 00:00:00 2001 From: Ben Teichman Date: Fri, 9 Jun 2017 22:39:10 -0400 Subject: [PATCH 5/8] better url service integration --- src/core/url-beautifier/index.ts | 2 +- src/services/url.ts | 69 +++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/src/core/url-beautifier/index.ts b/src/core/url-beautifier/index.ts index d77f45e6..2a44f74f 100644 --- a/src/core/url-beautifier/index.ts +++ b/src/core/url-beautifier/index.ts @@ -20,7 +20,7 @@ class UrlBeautifier { UrlBeautifier.validateToken(this.config.queryToken, keys); } - parse(url: string) { + parse(url: string): T { const uri = URI.parse(url); const activeRoute = Object.keys(this.routes).find((route) => uri.path.startsWith(this.routes[route])); diff --git a/src/services/url.ts b/src/services/url.ts index 18b2574a..a34509b8 100644 --- a/src/services/url.ts +++ b/src/services/url.ts @@ -4,6 +4,8 @@ import { BaseService } from '../core/service'; import UrlBeautifier from '../core/url-beautifier'; import StoreFront from '../storefront'; +const STOREFRONT_APP_ID = 'GroupBy StoreFront'; + @core class UrlService extends BaseService { @@ -17,21 +19,22 @@ class UrlService extends BaseService { readInitialUrl() { try { - const request = this.beautifier.parse(window.location.href); - const state = this.app.flux.store.getState().data; - const pageSizeIndex = state.page.sizes.items.indexOf(request.pageSize); + const request = this.beautifier.parse(window.location.href); + const currentState = this.app.flux.store.getState().data; + const pageSizeIndex = currentState.page.sizes.items.indexOf(request.pageSize); + // TODO this should probably go somewhere else const newState = { - ...state, + ...currentState, query: { - ...state.query, + ...currentState.query, original: request.query }, page: { - ...state.page, + ...currentState.page, current: request.page, sizes: { - ...state.page.sizes, - selected: pageSizeIndex === -1 ? state.page.sizes.selected : pageSizeIndex + ...currentState.page.sizes, + selected: pageSizeIndex === -1 ? currentState.page.sizes.selected : pageSizeIndex } }, navigations: { @@ -40,13 +43,40 @@ class UrlService extends BaseService { fields.push(refinement.navigationName); } return fields; - }, []) + }, []), + byId: request.refinements.reduce((navigations, refinement) => { + const field = refinement.navigationName; + const transformed = refinement.type === 'Range' + ? { low: refinement.low, high: refinement.high } + : { value: refinement.value }; + + if (field in navigations) { + const navigation = navigations[field]; + navigation.selected.push(navigation.refinements.push(transformed) - 1); + } else { + navigations[field] = { + field, + label: field, + range: refinement.type === 'Range', + refinements: [transformed], + selected: [0] + }; + } + + return navigations; + }, {}) } }; - this.refreshState(newState); - this.app.flux.once(Events.HISTORY_SAVE, this.listenForHistoryChange); - this.app.flux.store.dispatch(this.app.flux.actions.fetchProducts()); + this.refreshState(newState) + .then(() => { + const state = this.app.flux.store.getState(); + const url = window.location.pathname + window.location.search; + + window.history.replaceState({ url, state: state.data, app: STOREFRONT_APP_ID }, document.title, url); + this.app.flux.once(Events.HISTORY_SAVE, this.listenForHistoryChange); + this.app.flux.store.dispatch(this.app.flux.actions.fetchProducts()); + }); } catch (e) { this.app.log.warn('unable to parse state from url', e); this.listenForHistoryChange(); @@ -64,20 +94,21 @@ class UrlService extends BaseService { pageSize: Selectors.pageSize(state), refinements: Selectors.selectedRefinements(state) }; - const url = this.beautifier.build('search', urlState); - window.history.pushState({ store: state.data }, urlState.query, url); + + window.history.pushState({ url, state: state.data, app: STOREFRONT_APP_ID }, urlState.query, url); } rewind = (event: PopStateEvent) => { - if (event.state && event.state.store) { - console.log(event.state.store); - this.refreshState(event.state.store); + const eventState = event.state; + if (eventState && event.state.app === STOREFRONT_APP_ID) { + const historyState = eventState.state; + this.refreshState(historyState); } } - refreshState(state: any) { - this.app.flux.store.dispatch(this.app.flux.actions.refreshState(state)); + refreshState(state: any): Promise { + return this.app.flux.store.dispatch(this.app.flux.actions.refreshState(state)); } } From 5b123ade2c3ed2be9c8152ec04c59587b82b475d Mon Sep 17 00:00:00 2001 From: Ben Teichman Date: Sat, 10 Jun 2017 12:47:22 -0400 Subject: [PATCH 6/8] clean up build process --- .babelrc | 3 - package.json | 15 +- tsconfig.esnext.json | 3 + tsconfig.json | 2 +- yarn.lock | 1152 +++--------------------------------------- 5 files changed, 81 insertions(+), 1094 deletions(-) delete mode 100644 .babelrc diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 4b1d3f50..00000000 --- a/.babelrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "presets": ["env", "stage-3"] -} diff --git a/package.json b/package.json index 8fb89841..4d68db4d 100644 --- a/package.json +++ b/package.json @@ -47,31 +47,27 @@ } }, "devDependencies": { - "@types/chai": "^3.5.2", + "@types/chai": "^4.0.0", "@types/mocha": "^2.2.41", "@types/node": "^7.0.18", "@types/sinon": "^2.2.1", "@types/sinon-chai": "^2.7.28", - "babel-preset-env": "^1.4.0", - "babel-preset-stage-3": "^6.24.1", "chai": "^4.0.0", "condition-circle": "^1.5.0", "cz-conventional-changelog": "^2.0.0", "husky": "^0.13.3", "mocha-suite": "^1.0.9", - "re-start": "^1.14.6", - "sinon": "^2.3.2", + "re-start": "^1.14.8", + "sinon": "^2.3.4", "sinon-chai": "^2.10.0", - "start-babel-cli": "^4.0.1", + "start-simple-cli": "^4.1.1", "tslint": "^5.2.0", "tslint-config-groupby": "^1.0.0", "typescript": "^2.3.2", - "typings": "^2.1.1", "webpack": "^2.5.1" }, "dependencies": { - "@storefront/core": "dev", - "@storefront/flux-capacitor": "dev", + "@storefront/flux-capacitor": "^1.0.0", "@types/deep-assign": "^0.1.0", "@types/js-cookie": "^2.0.28", "@types/loglevel": "^1.4.29", @@ -85,7 +81,6 @@ "lodash.kebabcase": "^4.1.1", "loglevel": "^1.4.1", "query-string": "^4.3.4", - "redux": "^3.6.0", "riot": "^3.4.4", "urijs": "^1.18.10" } diff --git a/tsconfig.esnext.json b/tsconfig.esnext.json index 2521960d..eaa5e196 100644 --- a/tsconfig.esnext.json +++ b/tsconfig.esnext.json @@ -5,6 +5,9 @@ "ES2016.Array.Include", "DOM" ], + "types": [ + "node" + ], "module": "es2015", "moduleResolution": "node", "target": "es2015", diff --git a/tsconfig.json b/tsconfig.json index db6a8a83..7c976bec 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ "DOM" ], "types": [ - "loglevel" + "node" ], "module": "commonjs", "moduleResolution": "node", diff --git a/yarn.lock b/yarn.lock index 61a5247c..231c397a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -35,37 +35,16 @@ conventional-changelog "0.0.17" github-url-from-git "^1.4.0" -"@storefront/core@dev": - version "0.0.29-development" - resolved "https://registry.yarnpkg.com/@storefront/core/-/core-0.0.29-development.tgz#7df963693b581df1bf129602a5577a293208b76b" - dependencies: - "@storefront/core" dev - "@storefront/flux-capacitor" dev - "@types/deep-assign" "^0.1.0" - "@types/js-cookie" "^2.0.28" - "@types/loglevel" "^1.4.29" - "@types/query-string" "^4.3.1" - "@types/riot" "^2.6.2" - "@types/urijs" "^1.15.32" - deep-assign "^2.0.0" - dot-prop "^4.1.1" - gb-tracker-client "^3.4.3" - js-cookie "^2.1.4" - lodash.kebabcase "^4.1.1" - loglevel "^1.4.1" - query-string "^4.3.4" - redux "^3.6.0" - riot "^3.4.4" - urijs "^1.18.10" - -"@storefront/flux-capacitor@dev": - version "0.0.28-development" - resolved "https://registry.yarnpkg.com/@storefront/flux-capacitor/-/flux-capacitor-0.0.28-development.tgz#a0409b52ae54be638f4c8df97e4edd95929835e9" +"@storefront/flux-capacitor@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@storefront/flux-capacitor/-/flux-capacitor-1.0.0.tgz#d9e25446b6f693882ee66e94eb9c188a2614d6ab" dependencies: - "@types/uuid" "^2.0.29" + "@types/redux-logger" "^3.0.0" + "@types/uuid" "^3.0.0" eventemitter3 "^2.0.3" - groupby-api dev + groupby-api "^2.0.1" redux "^3.6.0" + redux-logger "^3.0.6" redux-thunk "^2.2.0" sayt "^0.1.7" uuid "^3.0.1" @@ -74,9 +53,9 @@ version "0.9.36" resolved "https://registry.yarnpkg.com/@types/axios/-/axios-0.9.36.tgz#94d156269562c726ce38115aeb8f34f09bfa3cd8" -"@types/chai@*", "@types/chai@^3.5.2": - version "3.5.2" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-3.5.2.tgz#c11cd2817d3a401b7ba0f5a420f35c56139b1c1e" +"@types/chai@*", "@types/chai@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.0.0.tgz#4c9adabd2d04265769e6d9e847e86cc404dc7dcd" "@types/clone@^0.1.30": version "0.1.30" @@ -106,7 +85,7 @@ version "2.2.41" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.41.tgz#e27cf0817153eb9f2713b2d3f6c68f1e1c3ca608" -"@types/node@*", "@types/node@^7.0.18": +"@types/node@^7.0.18": version "7.0.29" resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.29.tgz#ccfcec5b7135c7caf6c4ffb8c7f33102340d99df" @@ -118,6 +97,12 @@ version "4.3.1" resolved "https://registry.yarnpkg.com/@types/query-string/-/query-string-4.3.1.tgz#59ea5c75d425817370158789aa4f6197e5abd987" +"@types/redux-logger@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/redux-logger/-/redux-logger-3.0.0.tgz#abe464fe812d03c3580ec56e6b5b375861439fca" + dependencies: + redux "^3.6.0" + "@types/riot@^2.6.2": version "2.6.2" resolved "https://registry.yarnpkg.com/@types/riot/-/riot-2.6.2.tgz#5721ee98fd20916a0e85e292291a74f72692c0da" @@ -139,11 +124,9 @@ dependencies: "@types/jquery" "*" -"@types/uuid@^2.0.29": - version "2.0.30" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-2.0.30.tgz#4dca12da43ae530f89f46d6d203935d2199652d5" - dependencies: - "@types/node" "*" +"@types/uuid@^3.0.0": + version "3.3.28" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.3.28.tgz#e0ca8f62e667783180b179ed69068586d6fba5cc" abbrev@1: version "1.1.0" @@ -203,13 +186,7 @@ amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" -ansi-align@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" - dependencies: - string-width "^2.0.0" - -ansi-escapes@^1.0.0, ansi-escapes@^1.1.0: +ansi-escapes@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" @@ -225,10 +202,6 @@ ansi@^0.3.0, ansi@~0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/ansi/-/ansi-0.3.1.tgz#0c42d4fb17160d5a9af1e484bace1c66922c1b21" -any-promise@^1.0.0, any-promise@^1.1.0, any-promise@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" - anymatch@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507" @@ -246,10 +219,6 @@ aproba@^1.0.3: version "1.1.2" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1" -archy@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" - are-we-there-yet@~1.0.0: version "1.0.6" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.0.6.tgz#a2d28c93102aa6cc96245a26cb954de06ec53f0c" @@ -294,7 +263,7 @@ array-union@^1.0.1: dependencies: array-uniq "^1.0.1" -array-uniq@^1.0.1, array-uniq@^1.0.2: +array-uniq@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" @@ -302,20 +271,6 @@ array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" -array.prototype.find@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.0.4.tgz#556a5c5362c08648323ddaeb9de9d14bc1864c90" - dependencies: - define-properties "^1.1.2" - es-abstract "^1.7.0" - -array.prototype.findindex@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/array.prototype.findindex/-/array.prototype.findindex-2.0.2.tgz#58068d25887ef505e49dc92cb00c44dcee55b067" - dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.0" - arrify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -431,100 +386,6 @@ babel-generator@^6.18.0, babel-generator@^6.25.0: source-map "^0.5.0" trim-right "^1.0.1" -babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" - dependencies: - babel-helper-explode-assignable-expression "^6.24.1" - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-call-delegate@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" - dependencies: - babel-helper-hoist-variables "^6.24.1" - babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-define-map@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz#7a9747f258d8947d32d515f6aa1c7bd02204a080" - dependencies: - babel-helper-function-name "^6.24.1" - babel-runtime "^6.22.0" - babel-types "^6.24.1" - lodash "^4.2.0" - -babel-helper-explode-assignable-expression@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" - dependencies: - babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-function-name@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" - dependencies: - babel-helper-get-function-arity "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-get-function-arity@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-hoist-variables@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-optimise-call-expression@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-regex@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.24.1.tgz#d36e22fab1008d79d88648e32116868128456ce8" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - lodash "^4.2.0" - -babel-helper-remap-async-to-generator@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" - dependencies: - babel-helper-function-name "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-replace-supers@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" - dependencies: - babel-helper-optimise-call-expression "^6.24.1" - babel-messages "^6.23.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - babel-helpers@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" @@ -538,244 +399,6 @@ babel-messages@^6.23.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-check-es2015-constants@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-syntax-async-functions@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" - -babel-plugin-syntax-async-generators@^6.5.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz#6bc963ebb16eccbae6b92b596eb7f35c342a8b9a" - -babel-plugin-syntax-exponentiation-operator@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" - -babel-plugin-syntax-object-rest-spread@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" - -babel-plugin-syntax-trailing-function-commas@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" - -babel-plugin-transform-async-generator-functions@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz#f058900145fd3e9907a6ddf28da59f215258a5db" - dependencies: - babel-helper-remap-async-to-generator "^6.24.1" - babel-plugin-syntax-async-generators "^6.5.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-async-to-generator@^6.22.0, babel-plugin-transform-async-to-generator@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" - dependencies: - babel-helper-remap-async-to-generator "^6.24.1" - babel-plugin-syntax-async-functions "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-arrow-functions@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-block-scoping@^6.23.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.24.1.tgz#76c295dc3a4741b1665adfd3167215dcff32a576" - dependencies: - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - lodash "^4.2.0" - -babel-plugin-transform-es2015-classes@^6.23.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" - dependencies: - babel-helper-define-map "^6.24.1" - babel-helper-function-name "^6.24.1" - babel-helper-optimise-call-expression "^6.24.1" - babel-helper-replace-supers "^6.24.1" - babel-messages "^6.23.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-computed-properties@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" - dependencies: - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-destructuring@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-duplicate-keys@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-for-of@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-function-name@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" - dependencies: - babel-helper-function-name "^6.24.1" - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-literals@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" - dependencies: - babel-plugin-transform-es2015-modules-commonjs "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz#d3e310b40ef664a36622200097c6d440298f2bfe" - dependencies: - babel-plugin-transform-strict-mode "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-modules-systemjs@^6.23.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" - dependencies: - babel-helper-hoist-variables "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-modules-umd@^6.23.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" - dependencies: - babel-plugin-transform-es2015-modules-amd "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-object-super@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" - dependencies: - babel-helper-replace-supers "^6.24.1" - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-parameters@^6.23.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" - dependencies: - babel-helper-call-delegate "^6.24.1" - babel-helper-get-function-arity "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-shorthand-properties@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-spread@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-sticky-regex@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" - dependencies: - babel-helper-regex "^6.24.1" - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-template-literals@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-typeof-symbol@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-unicode-regex@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" - dependencies: - babel-helper-regex "^6.24.1" - babel-runtime "^6.22.0" - regexpu-core "^2.0.0" - -babel-plugin-transform-exponentiation-operator@^6.22.0, babel-plugin-transform-exponentiation-operator@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" - dependencies: - babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" - babel-plugin-syntax-exponentiation-operator "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-object-rest-spread@^6.22.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz#875d6bc9be761c58a2ae3feee5dc4895d8c7f921" - dependencies: - babel-plugin-syntax-object-rest-spread "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-regenerator@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz#b8da305ad43c3c99b4848e4fe4037b770d23c418" - dependencies: - regenerator-transform "0.9.11" - -babel-plugin-transform-strict-mode@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - babel-polyfill@^6.16.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d" @@ -784,52 +407,7 @@ babel-polyfill@^6.16.0: core-js "^2.4.0" regenerator-runtime "^0.10.0" -babel-preset-env@^1.4.0: - version "1.5.2" - resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.5.2.tgz#cd4ae90a6e94b709f97374b33e5f8b983556adef" - dependencies: - babel-plugin-check-es2015-constants "^6.22.0" - babel-plugin-syntax-trailing-function-commas "^6.22.0" - babel-plugin-transform-async-to-generator "^6.22.0" - babel-plugin-transform-es2015-arrow-functions "^6.22.0" - babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" - babel-plugin-transform-es2015-block-scoping "^6.23.0" - babel-plugin-transform-es2015-classes "^6.23.0" - babel-plugin-transform-es2015-computed-properties "^6.22.0" - babel-plugin-transform-es2015-destructuring "^6.23.0" - babel-plugin-transform-es2015-duplicate-keys "^6.22.0" - babel-plugin-transform-es2015-for-of "^6.23.0" - babel-plugin-transform-es2015-function-name "^6.22.0" - babel-plugin-transform-es2015-literals "^6.22.0" - babel-plugin-transform-es2015-modules-amd "^6.22.0" - babel-plugin-transform-es2015-modules-commonjs "^6.23.0" - babel-plugin-transform-es2015-modules-systemjs "^6.23.0" - babel-plugin-transform-es2015-modules-umd "^6.23.0" - babel-plugin-transform-es2015-object-super "^6.22.0" - babel-plugin-transform-es2015-parameters "^6.23.0" - babel-plugin-transform-es2015-shorthand-properties "^6.22.0" - babel-plugin-transform-es2015-spread "^6.22.0" - babel-plugin-transform-es2015-sticky-regex "^6.22.0" - babel-plugin-transform-es2015-template-literals "^6.22.0" - babel-plugin-transform-es2015-typeof-symbol "^6.23.0" - babel-plugin-transform-es2015-unicode-regex "^6.22.0" - babel-plugin-transform-exponentiation-operator "^6.22.0" - babel-plugin-transform-regenerator "^6.22.0" - browserslist "^2.1.2" - invariant "^2.2.2" - semver "^5.3.0" - -babel-preset-stage-3@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz#836ada0a9e7a7fa37cb138fb9326f87934a48395" - dependencies: - babel-plugin-syntax-trailing-function-commas "^6.22.0" - babel-plugin-transform-async-generator-functions "^6.24.1" - babel-plugin-transform-async-to-generator "^6.24.1" - babel-plugin-transform-exponentiation-operator "^6.24.1" - babel-plugin-transform-object-rest-spread "^6.22.0" - -babel-register@^6.11.6, babel-register@^6.24.1: +babel-register@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.24.1.tgz#7e10e13a2f71065bdfad5a1787ba45bca6ded75f" dependencies: @@ -841,7 +419,7 @@ babel-register@^6.11.6, babel-register@^6.24.1: mkdirp "^0.5.1" source-map-support "^0.4.2" -babel-runtime@^6.18.0, babel-runtime@^6.22.0: +babel-runtime@^6.22.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b" dependencies: @@ -858,7 +436,7 @@ babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.25.0: babylon "^6.17.2" lodash "^4.2.0" -babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.25.0: +babel-traverse@^6.18.0, babel-traverse@^6.25.0: version "6.25.0" resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1" dependencies: @@ -872,7 +450,7 @@ babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.25.0: invariant "^2.2.0" lodash "^4.2.0" -babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.25.0: +babel-types@^6.18.0, babel-types@^6.25.0: version "6.25.0" resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e" dependencies: @@ -919,7 +497,7 @@ block-stream@*: dependencies: inherits "~2.0.0" -bluebird@^3.1.1, bluebird@^3.4.6, bluebird@^3.5.0: +bluebird@^3.4.6, bluebird@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" @@ -933,18 +511,6 @@ boom@2.x.x: dependencies: hoek "2.x.x" -boxen@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.1.0.tgz#b1b69dd522305e807a99deee777dbd6e5167b102" - dependencies: - ansi-align "^2.0.0" - camelcase "^4.0.0" - chalk "^1.1.1" - cli-boxes "^1.0.0" - string-width "^2.0.0" - term-size "^0.1.0" - widest-line "^1.0.0" - brace-expansion@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59" @@ -1019,13 +585,6 @@ browserify-zlib@^0.1.4: dependencies: pako "~0.2.0" -browserslist@^2.1.2: - version "2.1.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.1.4.tgz#cc526af4a1312b7d2e05653e56d0c8ab70c0e053" - dependencies: - caniuse-lite "^1.0.30000670" - electron-to-chromium "^1.3.11" - buffer-xor@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" @@ -1075,18 +634,6 @@ camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" -camelcase@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - -caniuse-lite@^1.0.30000670: - version "1.0.30000680" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000680.tgz#d94d81294471617e86500f0aab90f11d22bc8934" - -capture-stack-trace@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d" - caseless@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" @@ -1156,23 +703,12 @@ circular-json@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" -cli-boxes@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" - -cli-cursor@^1.0.1, cli-cursor@^1.0.2: +cli-cursor@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" dependencies: restore-cursor "^1.0.1" -cli-truncate@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-1.0.0.tgz#21eb91f47b3f6560f004db77a769b4668d9c5518" - dependencies: - slice-ansi "0.0.4" - string-width "^2.0.0" - cli-width@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" @@ -1218,13 +754,6 @@ colors@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" -columnify@^1.5.2: - version "1.5.4" - resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" - dependencies: - strip-ansi "^3.0.0" - wcwidth "^1.0.0" - combined-stream@^1.0.5, combined-stream@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" @@ -1245,7 +774,7 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@^1.4.7, concat-stream@^1.5.2: +concat-stream@^1.5.2: version "1.6.0" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" dependencies: @@ -1268,17 +797,6 @@ config-chain@~1.1.8: ini "^1.3.4" proto-list "~1.2.1" -configstore@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.0.tgz#45df907073e26dfa1cf4b2d52f5b60545eaa11d1" - dependencies: - dot-prop "^4.1.0" - graceful-fs "^4.1.2" - make-dir "^1.0.0" - unique-string "^1.0.0" - write-file-atomic "^2.0.0" - xdg-basedir "^3.0.0" - console-browserify@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" @@ -1330,12 +848,6 @@ create-ecdh@^4.0.0: bn.js "^4.1.0" elliptic "^6.0.0" -create-error-class@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" - dependencies: - capture-stack-trace "^1.0.0" - create-hash@^1.1.0, create-hash@^1.1.1, create-hash@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd" @@ -1356,13 +868,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn-async@^2.1.1: - version "2.2.5" - resolved "https://registry.yarnpkg.com/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz#845ff0c0834a3ded9d160daca6d390906bb288cc" - dependencies: - lru-cache "^4.0.0" - which "^1.2.8" - cross-spawn@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.0.tgz#8254774ab4786b8c5b3cf4dfba66ce563932c252" @@ -1391,10 +896,6 @@ crypto-browserify@^3.11.0: public-encrypt "^4.0.0" randombytes "^2.0.0" -crypto-random-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" - currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -1435,13 +936,13 @@ dateformat@^1.0.11: get-stdin "^4.0.1" meow "^3.3.0" -debug@2, debug@^2.6.3: +debug@2, debug@^2.2.0, debug@^2.6.3: version "2.6.8" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" dependencies: ms "2.0.0" -debug@2.2.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0: +debug@2.2.0, debug@^2.1.1, debug@^2.1.3: version "2.2.0" resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" dependencies: @@ -1463,7 +964,7 @@ deep-assign@^2.0.0: dependencies: is-obj "^1.0.0" -deep-diff@^0.3.4: +deep-diff@^0.3.4, deep-diff@^0.3.5: version "0.3.8" resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.8.tgz#c01de63efb0eec9798801d40c7e0dae25b582c84" @@ -1491,19 +992,6 @@ default-require-extensions@^1.0.0: dependencies: strip-bom "^2.0.0" -defaults@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - dependencies: - clone "^1.0.2" - -define-properties@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" - dependencies: - foreach "^2.0.5" - object-keys "^1.0.8" - del@^2.0.2: version "2.2.2" resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" @@ -1537,10 +1025,6 @@ detect-indent@^4.0.0: dependencies: repeating "^2.0.0" -detect-indent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" - dezalgo@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -1578,20 +1062,12 @@ domain-browser@^1.1.1: version "1.1.7" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" -dot-prop@^4.1.0, dot-prop@^4.1.1: +dot-prop@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.1.1.tgz#a8493f0b7b5eeec82525b5c7587fa7de7ca859c1" dependencies: is-obj "^1.0.0" -double-ended-queue@^2.1.0-0: - version "2.1.0-0" - resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c" - -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - duplexer@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" @@ -1602,14 +1078,6 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.1.0" -electron-to-chromium@^1.3.11: - version "1.3.14" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.14.tgz#64af0f9efd3c3c6acd57d71f83b49ca7ee9c4b43" - -elegant-spinner@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" - elliptic@^6.0.0: version "6.4.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" @@ -1647,23 +1115,6 @@ error-ex@^1.2.0: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.5.0, es-abstract@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.7.0.tgz#dfade774e01bfcd97f96180298c449c8623fb94c" - dependencies: - es-to-primitive "^1.1.1" - function-bind "^1.1.0" - is-callable "^1.1.3" - is-regex "^1.0.3" - -es-to-primitive@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" - dependencies: - is-callable "^1.1.1" - is-date-object "^1.0.1" - is-symbol "^1.0.1" - es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: version "0.10.23" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.23.tgz#7578b51be974207a5487821b56538c224e4e7b38" @@ -1694,7 +1145,7 @@ es6-object-assign@^1.0.3: version "1.1.0" resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" -es6-promise@^3.1.2, es6-promise@^3.3.1: +es6-promise@^3.1.2: version "3.3.1" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" @@ -1854,17 +1305,6 @@ evp_bytestokey@^1.0.0: dependencies: create-hash "^1.1.1" -execa@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.4.0.tgz#4eb6467a36a095fabb2970ff9d5e3fb7bce6ebc3" - dependencies: - cross-spawn-async "^2.1.1" - is-stream "^1.1.0" - npm-run-path "^1.0.0" - object-assign "^4.0.1" - path-key "^1.0.0" - strip-eof "^1.0.0" - exit-hook@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" @@ -2000,10 +1440,6 @@ for-own@^0.1.3, for-own@^0.1.4: dependencies: for-in "^1.0.1" -foreach@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" - foreachasync@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/foreachasync/-/foreachasync-3.0.0.tgz#5502987dc8714be3392097f32e0071c9dee07cf6" @@ -2012,14 +1448,6 @@ forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" -form-data@^2.0.0, form-data@~2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.5" - mime-types "^2.1.12" - form-data@~1.0.0-rc4: version "1.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-1.0.1.tgz#ae315db9a4907fa065502304a66d7733475ee37c" @@ -2028,6 +1456,14 @@ form-data@~1.0.0-rc4: combined-stream "^1.0.5" mime-types "^2.1.11" +form-data@~2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + formatio@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.2.0.tgz#f3b2167d9068c4698a8d51f4f760a39a54d818eb" @@ -2066,10 +1502,6 @@ fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: mkdirp ">=0.5 0" rimraf "2" -function-bind@^1.0.2, function-bind@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" - gauge@~1.2.0: version "1.2.7" resolved "https://registry.yarnpkg.com/gauge/-/gauge-1.2.7.tgz#e9cec5483d3d4ee0ef44b60a7d99e4935e136d93" @@ -2125,10 +1557,6 @@ get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -2230,23 +1658,7 @@ globby@^6.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" -got@^6.7.1: - version "6.7.1" - resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" - dependencies: - create-error-class "^3.0.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-redirect "^1.0.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - lowercase-keys "^1.0.0" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - unzip-response "^2.0.1" - url-parse-lax "^1.0.0" - -graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.5, graceful-fs@^4.1.6: +graceful-fs@^4.1.2, graceful-fs@^4.1.5, graceful-fs@^4.1.6: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -2254,21 +1666,17 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.5, version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" -groupby-api@dev: - version "0.0.3-development" - resolved "https://registry.yarnpkg.com/groupby-api/-/groupby-api-0.0.3-development.tgz#a4db30914c1e0efcf25fb2f9e1fd2215b6f29a91" +groupby-api@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/groupby-api/-/groupby-api-2.0.1.tgz#823ecc765a484aea16370aa2c6a289a2f1fe33d1" dependencies: "@types/axios" "^0.9.35" "@types/clone" "^0.1.30" "@types/deep-equal" "^0.0.30" "@types/qs" "^6.2.30" - array.prototype.find "^2.0.0" - array.prototype.findindex "^2.0.0" axios "^0.12.0" clone "^1.0.2" deep-equal "^1.0.1" - es6-object-assign "^1.0.3" - es6-promise "^3.3.1" filter-object "^2.1.0" qs "^6.1.0" @@ -2316,16 +1724,10 @@ has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" -has-unicode@^2.0.0, has-unicode@^2.0.1: +has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" -has@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" - dependencies: - function-bind "^1.0.2" - hash-base@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1" @@ -2370,14 +1772,6 @@ hosted-git-info@^2.1.4, hosted-git-info@^2.1.5: version "2.4.2" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.4.2.tgz#0076b9f46a270506ddbaaea56496897460612a67" -http-proxy-agent@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz#cc1ce38e453bf984a0f7702d2dd59c73d081284a" - dependencies: - agent-base "2" - debug "2" - extend "3" - http-signature@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" @@ -2415,10 +1809,6 @@ ignore@^3.2.0: version "3.3.3" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d" -import-lazy@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" - imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -2474,7 +1864,7 @@ interpret@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90" -invariant@^2.2.0, invariant@^2.2.2: +invariant@^2.2.0: version "2.2.2" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" dependencies: @@ -2484,13 +1874,6 @@ invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" -is-absolute@^0.2.3: - version "0.2.6" - resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-0.2.6.tgz#20de69f3db942ef2d87b9c2da36f172235b1b5eb" - dependencies: - is-relative "^0.2.1" - is-windows "^0.2.0" - is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -2511,20 +1894,12 @@ is-builtin-module@^1.0.0: dependencies: builtin-modules "^1.0.0" -is-callable@^1.1.1, is-callable@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" - is-ci@^1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.0.10.tgz#f739336b2632365061a9d48270cd56ae3369318e" dependencies: ci-info "^1.0.0" -is-date-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" - is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" @@ -2583,10 +1958,6 @@ is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4: jsonpointer "^4.0.0" xtend "^4.0.0" -is-npm@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" - is-number@^2.0.2, is-number@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" @@ -2613,10 +1984,6 @@ is-path-inside@^1.0.0: dependencies: path-is-inside "^1.0.1" -is-plain-obj@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - is-posix-bracket@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" @@ -2629,58 +1996,20 @@ is-property@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" -is-redirect@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" - -is-regex@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" - dependencies: - has "^1.0.1" - -is-relative@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.2.1.tgz#d27f4c7d516d175fb610db84bbeef23c3bc97aa5" - dependencies: - is-unc-path "^0.1.1" - is-resolvable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" dependencies: tryit "^1.0.1" -is-retry-allowed@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" - -is-stream@^1.0.0, is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - -is-symbol@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" - is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" -is-unc-path@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-0.1.2.tgz#6ab053a72573c10250ff416a3814c35178af39b9" - dependencies: - unc-path-regex "^0.1.0" - is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" -is-windows@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -2789,10 +2118,6 @@ jsesc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - json-loader@^0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.4.tgz#8baa1365a632f58a3c46d20175fc6002c96e37de" @@ -2833,20 +2158,6 @@ jsonpointer@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" -jspm-config@^0.3.0: - version "0.3.4" - resolved "https://registry.yarnpkg.com/jspm-config/-/jspm-config-0.3.4.tgz#44c26902e4ae8ece2366cedc9ff16b10a5f391c6" - dependencies: - any-promise "^1.3.0" - graceful-fs "^4.1.4" - make-error-cause "^1.2.1" - object.pick "^1.1.2" - parse-json "^2.2.0" - strip-bom "^3.0.0" - thenify "^3.2.0" - throat "^3.0.0" - xtend "^4.0.1" - jsprim@^1.2.2: version "1.4.0" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918" @@ -2868,12 +2179,6 @@ kind-of@^3.0.2: dependencies: is-buffer "^1.1.5" -latest-version@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" - dependencies: - package-json "^4.0.0" - lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" @@ -2891,10 +2196,6 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -listify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/listify/-/listify-1.0.0.tgz#03ca7ba2d150d4267773f74e57558d1053d2bee3" - load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -2918,10 +2219,6 @@ loader-utils@^0.2.16: json5 "^0.5.0" object-assign "^4.0.1" -lockfile@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/lockfile/-/lockfile-1.0.3.tgz#2638fc39a0331e9cac1a04b71799931c9c50df79" - lodash-es@^4.2.1: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7" @@ -3029,13 +2326,6 @@ lodash@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.3.1.tgz#a4663b53686b895ff074e2ba504dfb76a8e2b770" -log-update@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-1.0.2.tgz#19929f64c4093d2d2e7075a1dad8af59c296b8d1" - dependencies: - ansi-escapes "^1.0.0" - cli-cursor "^1.0.2" - loglevel@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.4.1.tgz#95b383f91a3c2756fd4ab093667e4309161f2bcd" @@ -3061,11 +2351,7 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" -lowercase-keys@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" - -lru-cache@^4.0.0, lru-cache@^4.0.1: +lru-cache@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.0.tgz#59be49a683b8d986a939f1ca60fdb6989f4b2046" dependencies: @@ -3076,22 +2362,6 @@ lz-string@^1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" -make-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.0.0.tgz#97a011751e91dd87cfadef58832ebb04936de978" - dependencies: - pify "^2.3.0" - -make-error-cause@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/make-error-cause/-/make-error-cause-1.2.2.tgz#df0388fcd0b37816dff0a5fb8108939777dcbc9d" - dependencies: - make-error "^1.2.0" - -make-error@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.0.tgz#52ad3a339ccf10ce62b4040b708fe707244b8b96" - makethen@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/makethen/-/makethen-0.1.0.tgz#dd69b9e7296deaf6566135f1496c82b3236c5d08" @@ -3304,12 +2574,6 @@ nopt@^4.0.0, nopt@^4.0.1: abbrev "1" osenv "^0.1.4" -nopt@~1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" - dependencies: - abbrev "1" - nopt@~3.0.1: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" @@ -3366,12 +2630,6 @@ npm-registry-client@^7.0.1: optionalDependencies: npmlog "2 || ^3.1.0 || ^4.0.0" -npm-run-path@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-1.0.0.tgz#f5c32bf595fe81ae927daec52e82f8b000ac3c8f" - dependencies: - path-key "^1.0.0" - npmconf@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/npmconf/-/npmconf-2.1.2.tgz#66606a4a736f1e77a059aa071a79c94ab781853a" @@ -3415,10 +2673,6 @@ object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" -object-keys@^1.0.8: - version "1.0.11" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" - object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -3426,7 +2680,7 @@ object.omit@^2.0.0: for-own "^0.1.4" is-extendable "^0.1.1" -object.pick@^1.1.1, object.pick@^1.1.2: +object.pick@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.2.0.tgz#b5392bee9782da6d9fb7d6afaf539779f1234c2b" dependencies: @@ -3491,15 +2745,6 @@ osenv@^0.1.0, osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" -package-json@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed" - dependencies: - got "^6.7.1" - registry-auth-token "^3.0.1" - registry-url "^3.0.3" - semver "^5.1.0" - pad-right@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/pad-right/-/pad-right-0.2.2.tgz#6fbc924045d244f2a2a244503060d3bfc6009774" @@ -3557,10 +2802,6 @@ path-is-inside@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" -path-key@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-1.0.0.tgz#5d53d578019646c0d68800db4e146e6bdc2ac7af" - path-object@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/path-object/-/path-object-2.3.0.tgz#03e46653e5c375c60af1cabdd94bc6448a5d9110" @@ -3610,7 +2851,7 @@ performance-now@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" -pify@^2.0.0, pify@^2.3.0: +pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -3628,45 +2869,10 @@ pluralize@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" -popsicle-proxy-agent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/popsicle-proxy-agent/-/popsicle-proxy-agent-3.0.0.tgz#b9133c55d945759ab7ee61b7711364620d3aeadc" - dependencies: - http-proxy-agent "^1.0.0" - https-proxy-agent "^1.0.0" - -popsicle-retry@^3.2.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/popsicle-retry/-/popsicle-retry-3.2.1.tgz#e06e866533b42a7a123eb330cbe63a7cebcba10c" - dependencies: - any-promise "^1.1.0" - xtend "^4.0.1" - -popsicle-rewrite@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/popsicle-rewrite/-/popsicle-rewrite-1.0.0.tgz#1dd4e8ea9c3182351fb820f87934d992f7fb9007" - -popsicle-status@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/popsicle-status/-/popsicle-status-2.0.1.tgz#8dd70c4fe7c694109add784ffe80eacac1e7b28d" - -popsicle@^9.0.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/popsicle/-/popsicle-9.1.0.tgz#4f900f38d57a574ec170eda40496e364082bff66" - dependencies: - concat-stream "^1.4.7" - form-data "^2.0.0" - make-error-cause "^1.2.1" - tough-cookie "^2.0.0" - prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" -prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" @@ -3687,10 +2893,6 @@ progress@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" -promise-finally@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/promise-finally/-/promise-finally-3.0.0.tgz#ddd5d0f895432b1206ceb8da1275064d18e7aa23" - proto-list@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" @@ -3761,7 +2963,7 @@ randombytes@^2.0.0, randombytes@^2.0.1: dependencies: safe-buffer "^5.1.0" -rc@^1.0.1, rc@^1.1.5, rc@^1.1.6, rc@^1.1.7: +rc@^1.1.7: version "1.2.1" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" dependencies: @@ -3770,9 +2972,9 @@ rc@^1.0.1, rc@^1.1.5, rc@^1.1.6, rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -re-start@^1.14.6: - version "1.14.7" - resolved "https://registry.yarnpkg.com/re-start/-/re-start-1.14.7.tgz#019aea22d599bad0e80bf797f9286995a405bca3" +re-start@^1.14.8: + version "1.14.8" + resolved "https://registry.yarnpkg.com/re-start/-/re-start-1.14.8.tgz#aed06e90af0ee49f920e3208d4da0a3c0738aad0" dependencies: minimist "^1.2.0" start "^5.0.0" @@ -3863,6 +3065,12 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" +redux-logger@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf" + dependencies: + deep-diff "^0.3.5" + redux-thunk@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.2.0.tgz#e615a16e16b47a19a515766133d1e3e99b7852e5" @@ -3876,22 +3084,10 @@ redux@^3.6.0: loose-envify "^1.1.0" symbol-observable "^1.0.2" -regenerate@^1.2.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260" - regenerator-runtime@^0.10.0: version "0.10.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" -regenerator-transform@0.9.11: - version "0.9.11" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.11.tgz#3a7d067520cb7b7176769eb5ff868691befe1283" - dependencies: - babel-runtime "^6.18.0" - babel-types "^6.19.0" - private "^0.1.6" - regex-cache@^0.4.2: version "0.4.3" resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" @@ -3899,37 +3095,6 @@ regex-cache@^0.4.2: is-equal-shallow "^0.1.3" is-primitive "^2.0.0" -regexpu-core@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" - dependencies: - regenerate "^1.2.1" - regjsgen "^0.2.0" - regjsparser "^0.1.4" - -registry-auth-token@^3.0.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.1.tgz#fb0d3289ee0d9ada2cbb52af5dfe66cb070d3006" - dependencies: - rc "^1.1.6" - safe-buffer "^5.0.1" - -registry-url@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" - dependencies: - rc "^1.0.1" - -regjsgen@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" - -regjsparser@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" - dependencies: - jsesc "~0.5.0" - remove-trailing-separator@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz#69b062d978727ad14dc6b56ba4ab772fd8d70511" @@ -4084,7 +3249,7 @@ right-pad@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/right-pad/-/right-pad-1.0.1.tgz#8ca08c2cbb5b55e74dafa96bf7fd1a27d568c8d0" -rimraf@2, rimraf@^2.2.8, rimraf@^2.4.4, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.1: +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" dependencies: @@ -4217,12 +3382,6 @@ semantic-release@^6.3.6: run-series "^1.1.3" semver "^5.2.0" -semver-diff@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" - dependencies: - semver "^5.0.3" - "semver@2 >=2.2.1 || 3.x || 4 || 5", "semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.2.0, semver@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -4277,9 +3436,9 @@ sinon-chai@^2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-2.10.0.tgz#6ab3008bb8cae9929e744d766574b4cf35f34b5b" -sinon@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-2.3.2.tgz#c43a9c570f32baac1159505cfeed19108855df89" +sinon@^2.3.4: + version "2.3.4" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-2.3.4.tgz#466ad8d1bae86d6db51aa218b92e997bc3e5db88" dependencies: diff "^3.1.0" formatio "1.2.0" @@ -4298,7 +3457,7 @@ slice-ansi@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" -slide@^1.1.3, slide@^1.1.5: +slide@^1.1.3: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" @@ -4308,12 +3467,6 @@ sntp@1.x.x: dependencies: hoek "2.x.x" -sort-keys@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" - dependencies: - is-plain-obj "^1.0.0" - source-list-map@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.2.tgz#9889019d1024cce55cdc069498337ef6186a11a1" @@ -4376,13 +3529,6 @@ stack-utils@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-0.4.0.tgz#940cb82fccfa84e8ff2f3fdf293fe78016beccd1" -start-babel-cli@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/start-babel-cli/-/start-babel-cli-4.0.1.tgz#205ffb1629fb519a748d9fffbf7db0acabd9d098" - dependencies: - babel-register "^6.11.6" - start-simple-cli "^4.0.0" - start-babel@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/start-babel/-/start-babel-3.0.0.tgz#b8eaed317e098173650490b1c1cdd51498fc8cf9" @@ -4477,7 +3623,7 @@ start-release@^1.4.1: npmconf "^2.1.2" semantic-release "^6.3.6" -start-simple-cli@^4.0.0: +start-simple-cli@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/start-simple-cli/-/start-simple-cli-4.1.1.tgz#ef68ffe915ca2fc5ca44959103f5085443be58e6" dependencies: @@ -4562,10 +3708,6 @@ strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" -string-template@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/string-template/-/string-template-1.0.0.tgz#9e9f2233dc00f218718ec379a28a5673ecca8b96" - string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -4611,10 +3753,6 @@ strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - strip-indent@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" @@ -4681,12 +3819,6 @@ tar@^2.2.1: fstream "^1.0.2" inherits "2" -term-size@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-0.1.1.tgz#87360b96396cab5760963714cda0d0cbeecad9ca" - dependencies: - execa "^0.4.0" - text-encoding@0.6.4: version "0.6.4" resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19" @@ -4695,26 +3827,10 @@ text-table@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" -thenify@^3.1.0, thenify@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839" - dependencies: - any-promise "^1.0.0" - -throat@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/throat/-/throat-3.1.0.tgz#ef22d8855963b3fdc626d043508f24c4cdf7d3c3" - dependencies: - double-ended-queue "^2.1.0-0" - through@2, through@^2.3.6, through@~2.3, through@~2.3.1: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" -timed-out@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" - timers-browserify@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.2.tgz#ab4883cf597dcd50af211349a00fbca56ac86b86" @@ -4729,13 +3845,7 @@ to-fast-properties@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" -touch@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/touch/-/touch-1.0.0.tgz#449cbe2dbae5a8c8038e30d71fa0ff464947c4de" - dependencies: - nopt "~1.0.10" - -tough-cookie@>=2.3.0, tough-cookie@^2.0.0, tough-cookie@~2.3.0: +tough-cookie@>=2.3.0, tough-cookie@~2.3.0: version "2.3.2" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" dependencies: @@ -4854,67 +3964,10 @@ typescript-compiler@^1.4.1-2: version "1.4.1" resolved "https://registry.yarnpkg.com/typescript-compiler/-/typescript-compiler-1.4.1.tgz#3639a276d7f3bc97ddbe121fd933713d475a9aa8" -typescript@^2.1.4, typescript@^2.3.2, typescript@^2.3.3: +typescript@^2.3.2, typescript@^2.3.3: version "2.3.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.3.4.tgz#3d38321828231e434f287514959c37a82b629f42" -typings-core@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/typings-core/-/typings-core-2.3.3.tgz#09ec54cd5b11dd5f1ef2fc0ab31d37002ca2b5ad" - dependencies: - array-uniq "^1.0.2" - configstore "^3.0.0" - debug "^2.2.0" - detect-indent "^5.0.0" - graceful-fs "^4.1.2" - has "^1.0.1" - invariant "^2.2.0" - is-absolute "^0.2.3" - jspm-config "^0.3.0" - listify "^1.0.0" - lockfile "^1.0.1" - make-error-cause "^1.2.1" - mkdirp "^0.5.1" - object.pick "^1.1.1" - parse-json "^2.2.0" - popsicle "^9.0.0" - popsicle-proxy-agent "^3.0.0" - popsicle-retry "^3.2.0" - popsicle-rewrite "^1.0.0" - popsicle-status "^2.0.0" - promise-finally "^3.0.0" - rc "^1.1.5" - rimraf "^2.4.4" - sort-keys "^1.0.0" - string-template "^1.0.0" - strip-bom "^3.0.0" - thenify "^3.1.0" - throat "^3.0.0" - touch "^1.0.0" - typescript "^2.1.4" - xtend "^4.0.0" - zip-object "^0.1.0" - -typings@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/typings/-/typings-2.1.1.tgz#bacc69d255970a478e09f76c7f689975d535a78a" - dependencies: - archy "^1.0.0" - bluebird "^3.1.1" - chalk "^1.0.0" - cli-truncate "^1.0.0" - columnify "^1.5.2" - elegant-spinner "^1.0.1" - has-unicode "^2.0.1" - listify "^1.0.0" - log-update "^1.0.2" - minimist "^1.2.0" - promise-finally "^3.0.0" - typings-core "^2.3.3" - update-notifier "^2.0.0" - wordwrap "^1.0.0" - xtend "^4.0.1" - uglify-js@^2.6, uglify-js@^2.8.27: version "2.8.28" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.28.tgz#e335032df9bb20dcb918f164589d5af47f38834a" @@ -4936,47 +3989,14 @@ uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" -unc-path-regex@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" - underscore.string@~2.2.0rc: version "2.2.1" resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-2.2.1.tgz#d7c0fa2af5d5a1a67f4253daee98132e733f0f19" -unique-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" - dependencies: - crypto-random-string "^1.0.0" - -unzip-response@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" - -update-notifier@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.2.0.tgz#1b5837cf90c0736d88627732b661c138f86de72f" - dependencies: - boxen "^1.0.0" - chalk "^1.0.0" - configstore "^3.0.0" - import-lazy "^2.1.0" - is-npm "^1.0.0" - latest-version "^3.0.0" - semver-diff "^2.0.0" - xdg-basedir "^3.0.0" - urijs@^1.18.10: version "1.18.10" resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.18.10.tgz#b94463eaba59a1a796036a467bb633c667f221ab" -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" - dependencies: - prepend-http "^1.0.1" - url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -5037,12 +4057,6 @@ watchpack@^1.3.1: chokidar "^1.4.3" graceful-fs "^4.1.2" -wcwidth@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - dependencies: - defaults "^1.0.3" - webpack-sources@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.2.3.tgz#17c62bfaf13c707f9d02c479e0dcdde8380697fb" @@ -5080,7 +4094,7 @@ which-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" -which@^1.2.8, which@^1.2.9: +which@^1.2.9: version "1.2.14" resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" dependencies: @@ -5092,12 +4106,6 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2" -widest-line@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-1.0.0.tgz#0c09c85c2a94683d0d7eaf8ee097d564bf0e105c" - dependencies: - string-width "^1.0.1" - window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" @@ -5110,7 +4118,7 @@ wordwrap@0.0.2, wordwrap@~0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" -wordwrap@^1.0.0, wordwrap@~1.0.0: +wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" @@ -5125,25 +4133,13 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" -write-file-atomic@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.1.0.tgz#1769f4b551eedce419f0505deae2e26763542d37" - dependencies: - graceful-fs "^4.1.11" - imurmurhash "^0.1.4" - slide "^1.1.5" - write@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" dependencies: mkdirp "^0.5.1" -xdg-basedir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" - -xtend@^4.0.0, xtend@^4.0.1: +xtend@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" @@ -5187,7 +4183,3 @@ yargs@~3.10.0: cliui "^2.1.0" decamelize "^1.0.0" window-size "0.1.0" - -zip-object@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/zip-object/-/zip-object-0.1.0.tgz#c1a0da04c88c837756e248680a03ff902ec3f53a" From 244e0eceab63717f59556aeaed02d6f86dbf3df5 Mon Sep 17 00:00:00 2001 From: Ben Teichman Date: Sat, 10 Jun 2017 17:32:47 -0400 Subject: [PATCH 7/8] most tests added --- src/core/url-beautifier/details/generator.ts | 4 +- src/core/url-beautifier/handler.ts | 2 +- src/core/url-beautifier/index.ts | 2 +- test/unit/_suite.ts | 11 + .../core/url-beautifier/details/generator.ts | 99 +++++++ .../core/url-beautifier/details/parser.ts | 105 ++++++++ test/unit/core/url-beautifier/factory.ts | 143 +++++++++++ test/unit/core/url-beautifier/index.ts | 134 ++++++++++ .../url-beautifier/navigation/generator.ts | 37 +++ .../core/url-beautifier/navigation/parser.ts | 47 ++++ .../core/url-beautifier/search/generator.ts | 219 ++++++++++++++++ .../unit/core/url-beautifier/search/parser.ts | 241 ++++++++++++++++++ 12 files changed, 1041 insertions(+), 3 deletions(-) create mode 100644 test/unit/core/url-beautifier/details/generator.ts create mode 100644 test/unit/core/url-beautifier/details/parser.ts create mode 100644 test/unit/core/url-beautifier/factory.ts create mode 100644 test/unit/core/url-beautifier/index.ts create mode 100644 test/unit/core/url-beautifier/navigation/generator.ts create mode 100644 test/unit/core/url-beautifier/navigation/parser.ts create mode 100644 test/unit/core/url-beautifier/search/generator.ts create mode 100644 test/unit/core/url-beautifier/search/parser.ts diff --git a/src/core/url-beautifier/details/generator.ts b/src/core/url-beautifier/details/generator.ts index 0d97b877..1080f17f 100644 --- a/src/core/url-beautifier/details/generator.ts +++ b/src/core/url-beautifier/details/generator.ts @@ -2,7 +2,7 @@ import { SelectedValueRefinement } from 'groupby-api'; import UrlBeautifier from '..'; import { UrlGenerator } from '../handler'; -export default class DetailsUrlGenerator extends UrlGenerator { +export class DetailsUrlGenerator extends UrlGenerator { build = (request: UrlBeautifier.DetailsRequest) => { let path = []; @@ -47,3 +47,5 @@ export default class DetailsUrlGenerator extends UrlGenerator((tests) => { stub: (...args) => (sandbox.stub)(...args) }); }); + +export function refinement(field: string, value: any): SelectedValueRefinement; +export function refinement(field: string, low: number, high: number): SelectedRangeRefinement; +export function refinement(navigationName: string, valueOrLow: any, high?: number): SelectedRefinement { + if (high) { + return { navigationName, low: valueOrLow, high, type: 'Range' }; + } else { + return { navigationName, value: valueOrLow, type: 'Value' }; + } +} diff --git a/test/unit/core/url-beautifier/details/generator.ts b/test/unit/core/url-beautifier/details/generator.ts new file mode 100644 index 00000000..6c196c44 --- /dev/null +++ b/test/unit/core/url-beautifier/details/generator.ts @@ -0,0 +1,99 @@ +import DetailsUrlGenerator from '../../../../../src/core/url-beautifier/details/generator'; +import { UrlGenerator } from '../../../../../src/core/url-beautifier/handler'; +import suite, { refinement } from '../../../_suite'; + +suite('DetailsUrlGenerator', ({ expect }) => { + let generator: DetailsUrlGenerator; + + beforeEach(() => generator = new DetailsUrlGenerator({ config: { refinementMapping: [] } })); + + it('should extend UrlGenerator', () => { + expect(generator).to.be.an.instanceOf(UrlGenerator); + }); + + it('should convert a simple detail to a URL', () => { + expect(generator.build({ + title: 'red and delicious apples', + id: '1923', + refinements: [] + })) + .to.eq('/red-and-delicious-apples/1923'); + }); + + it('should encode special characters + in detail', () => { + expect(generator.build({ + title: 'red+and+delicious+apples', + id: '1923', + refinements: [] + })) + .to.eq('/red%2Band%2Bdelicious%2Bapples/1923'); + }); + + it('should encode special characters / in detail', () => { + expect(generator.build({ + title: 'red/and/delicious/apples', + id: '1923', + refinements: [] + })) + .to.eq('/red%2Fand%2Fdelicious%2Fapples/1923'); + }); + + it('should convert a detail with refinements to a URL without reference keys', () => { + generator.config.useReferenceKeys = false; + const url = generator.build({ + title: 'satin shiny party dress', + id: '293014', + refinements: [refinement('colour', 'red')] + }); + + expect(url).to.eq('/satin-shiny-party-dress/red/colour/293014'); + }); + + it('should convert detail with refinements to a URL and encode special characters without reference keys', () => { + generator.config.useReferenceKeys = false; + const url = generator.build({ + title: 'satin shiny party dress', + id: '293014', + refinements: [refinement('colour', 'red+green/blue')] + }); + + expect(url).to.eq('/satin-shiny-party-dress/red%2Bgreen%2Fblue/colour/293014'); + }); + + it('should convert a detail with a single refinement to a URL with a reference key', () => { + generator.config.useReferenceKeys = true; + generator.config.refinementMapping = [{ c: 'colour' }]; + const url = generator.build({ + title: 'dress', + id: '293014', + refinements: [refinement('colour', 'red')] + }); + + expect(url).to.eq('/dress/red/c/293014'); + }); + + it('should convert a detail with multiple refinements to a URL with reference keys', () => { + generator.config.useReferenceKeys = true; + generator.config.refinementMapping = [{ c: 'colour' }, { b: 'brand' }]; + const url = generator.build({ + title: 'dress', + id: '293014', + refinements: [refinement('colour', 'red'), refinement('brand', 'h&m')] + }); + + expect(url).to.eq('/dress/h%26m/red/bc/293014'); + }); + + describe('error states', () => { + it('should throw an error if no reference key found for refinement navigation name', () => { + generator.config.useReferenceKeys = true; + const build = () => generator.build({ + title: 'dress', + id: '293014', + refinements: [refinement('colour', 'red')] + }); + + expect(build).to.throw("no mapping found for navigation 'colour'"); + }); + }); +}); diff --git a/test/unit/core/url-beautifier/details/parser.ts b/test/unit/core/url-beautifier/details/parser.ts new file mode 100644 index 00000000..f1f8b6f6 --- /dev/null +++ b/test/unit/core/url-beautifier/details/parser.ts @@ -0,0 +1,105 @@ +import DetailsUrlParser from '../../../../../src/core/url-beautifier/details/parser'; +import { UrlParser } from '../../../../../src/core/url-beautifier/handler'; +import suite, { refinement } from '../../../_suite'; + +suite('DetailsUrlParser', ({ expect }) => { + let parser: DetailsUrlParser; + + beforeEach(() => parser = new DetailsUrlParser({ config: { refinementMapping: [] } })); + + it('should extend UrlParser', () => { + expect(parser).to.be.an.instanceOf(UrlParser); + }); + + it('should parse a simple URL and return a detail object', () => { + const expectedDetail = { + title: 'apples', + id: '1923', + refinements: [] + }; + + expect(parser.parse('/apples/1923')).to.eql(expectedDetail); + }); + + it('should parse a simple URL, replace \'-\' with \' \' and return a detail object', () => { + const expectedDetail = { + title: 'red and delicious apples', + id: '1923', + refinements: [] + }; + + expect(parser.parse('/red-and-delicious-apples/1923')).to.eql(expectedDetail); + }); + + it('should parse a simple URL, decode special characters and return a detail object', () => { + const expectedDetail = { + title: 'red+and+delicious+apples', + id: '1923', + refinements: [] + }; + + expect(parser.parse('/red%2Band%2Bdelicious%2Bapples/1923')).to.eql(expectedDetail); + }); + + it('should parse a URL with navigation names and values and return a detail object without reference keys', () => { + parser.config.useReferenceKeys = false; + const expectedDetail = { + title: 'satin shiny party dress', + id: '293014', + refinements: [refinement('colour', 'blue')] + }; + + expect(parser.parse('/satin-shiny-party-dress/blue/colour/293014')).to.eql(expectedDetail); + }); + + it('should decode special characters in navigation name and values', () => { + parser.config.useReferenceKeys = false; + const url = '/satin-shiny-party-dress/h%26m/brand/blue/colour/red/colour/293014'; + const expectedDetail = { + title: 'satin shiny party dress', + id: '293014', + refinements: [refinement('brand', 'h&m'), refinement('colour', 'blue'), refinement('colour', 'red')] + }; + + expect(parser.parse(url)).to.eql(expectedDetail); + }); + + it('should parse a URL with reference keys', () => { + parser.config.refinementMapping = [{ c: 'colour' }, { b: 'brand' }]; + parser.config.useReferenceKeys = true; + const expectedDetail = { + title: 'dress', + id: '293014', + refinements: [refinement('brand', 'h&m'), refinement('colour', 'blue'), refinement('colour', 'red')] + }; + + const parsed = parser.parse('/dress/h%26m/blue/red/bcc/293014'); + + expect(parsed.id).to.eql(expectedDetail.id); + expect(parsed.title).to.eql(expectedDetail.title); + expect(parsed.refinements).to.eql(expectedDetail.refinements); + }); + + describe('error states', () => { + it('should throw an error if the path has less than two parts', () => { + expect(() => parser.parse('/dress')).to.throw('path has fewer than two parts'); + }); + + it('should throw an error if the path without reference keys has an odd number of parts', () => { + parser.config.useReferenceKeys = false; + + expect(() => parser.parse('/dress/blue/colour/red/293014')) + .to.throw('path has an odd number of parts'); + }); + + it('should throw an error if the path has wrong number of parts', () => { + expect(() => parser.parse('/shoe/blue/colour')).to.throw('path has an odd number of parts'); + }); + + it('should throw an error if token reference is invalid', () => { + parser.config.useReferenceKeys = true; + + expect(() => parser.parse('/apples/green/cs/2931')).to.throw('token reference is invalid'); + }); + }); +}); diff --git a/test/unit/core/url-beautifier/factory.ts b/test/unit/core/url-beautifier/factory.ts new file mode 100644 index 00000000..88d2c36a --- /dev/null +++ b/test/unit/core/url-beautifier/factory.ts @@ -0,0 +1,143 @@ +import UrlBeautifier from '../../../../src/core/url-beautifier'; +import * as DetailsGenerator from '../../../../src/core/url-beautifier/details/generator'; +import * as DetailsParser from '../../../../src/core/url-beautifier/details/parser'; +import Factory from '../../../../src/core/url-beautifier/factory'; +import * as NavigationGenerator from '../../../../src/core/url-beautifier/navigation/generator'; +import * as NavigationParser from '../../../../src/core/url-beautifier/navigation/parser'; +import * as SearchGenerator from '../../../../src/core/url-beautifier/search/generator'; +import * as SearchParser from '../../../../src/core/url-beautifier/search/parser'; +import suite, { refinement } from '../../_suite'; + +const URL_BEAUTIFIER: any = { a: 'b' }; +suite('BeautifierFactory', ({ expect, stub }) => { + let config: any; + + beforeEach(() => config = URL_BEAUTIFIER.config = {}); + + describe('create()', () => { + it('should create all beautifiers', () => { + const detailsGenerator = { build: () => null }; + const detailsParser = { parse: () => null }; + const navigationGenerator = { build: () => null }; + const navigationParser = { parse: () => null }; + const searchGenerator = { build: () => null }; + const searchParser = { parse: () => null }; + const detailsGeneratorStub = stub(DetailsGenerator, 'default').returns(detailsGenerator); + const detailsParserStub = stub(DetailsParser, 'default').returns(detailsParser); + const navigationGeneratorStub = stub(NavigationGenerator, 'default').returns(navigationGenerator); + const navigationParserStub = stub(NavigationParser, 'default').returns(navigationParser); + const searchGeneratorStub = stub(SearchGenerator, 'default').returns(searchGenerator); + const searchParserStub = stub(SearchParser, 'default').returns(searchParser); + + const beautifiers = Factory.create(URL_BEAUTIFIER); + + expect(beautifiers).to.eql({ + details: { + parse: detailsParser.parse, + build: detailsGenerator.build, + }, + navigation: { + parse: navigationParser.parse, + build: navigationGenerator.build, + }, + search: { + parse: searchParser.parse, + build: searchGenerator.build, + } + }); + expect(detailsGeneratorStub).to.be.calledWith(URL_BEAUTIFIER); + expect(detailsParserStub).to.be.calledWith(URL_BEAUTIFIER); + expect(navigationGeneratorStub).to.be.calledWith(URL_BEAUTIFIER); + expect(navigationParserStub).to.be.calledWith(URL_BEAUTIFIER); + expect(searchGeneratorStub).to.be.calledWith(URL_BEAUTIFIER); + expect(searchParserStub).to.be.calledWith(URL_BEAUTIFIER); + }); + }); + + describe('compatibility', () => { + describe('details', () => { + const request = { + title: 'dress', + id: '293014', + refinements: [ + refinement('brand', 'h&m'), + refinement('colour', 'blue'), + refinement('colour', 'red') + ] + }; + let generator; + let parser; + + beforeEach(() => { + generator = new DetailsGenerator.default(URL_BEAUTIFIER); + parser = new DetailsParser.default(URL_BEAUTIFIER); + }); + + it('should convert from detail object to a URL and back with reference keys', () => { + config.refinementMapping = [{ c: 'colour' }, { b: 'brand' }]; + + expect(parser.parse(generator.build(request))).to.eql(request); + }); + + it('should convert from URL to a detail and back with reference keys', () => { + const url = '/dress/h%26m/blue/red/bcc/293014'; + config.refinementMapping = [{ c: 'colour' }, { b: 'brand' }]; + + expect(generator.build(parser.parse(url))).to.eq(url); + }); + + it('should convert from detail object to a URL and back without reference keys', () => { + config.useReferenceKeys = false; + + expect(parser.parse(generator.build(request))).to.eql(request); + }); + + it('should convert from URL to a detail and back without reference keys', () => { + const url = '/dress/h%26m/brand/blue/colour/red/colour/293014'; + config.useReferenceKeys = false; + + expect(generator.build(parser.parse(url))).to.eq(url); + }); + }); + + describe('search', () => { + const request = { + query: 'dress', + refinements: [refinement('brand', 'h&m')] + }; + let generator; + let parser; + + beforeEach(() => { + generator = new SearchGenerator.default(URL_BEAUTIFIER); + parser = new SearchParser.default(URL_BEAUTIFIER); + }); + + it('should convert from query object to a URL and back with reference keys', () => { + config.refinementMapping = [{ b: 'brand' }]; + config.useReferenceKeys = true; + + expect(parser.parse(generator.build(request))).to.eql(request); + }); + + it('should convert from URL to a query and back with reference keys', () => { + const url = '/dress/h%26m/qb'; + config.refinementMapping = [{ b: 'brand' }]; + config.useReferenceKeys = true; + config.queryToken = 'q'; + + expect(generator.build(parser.parse(url))).to.eq(url); + }); + + it('should convert from query object to a URL and back without reference keys', () => { + expect(parser.parse(generator.build(request))).to.eql(request); + }); + + it('should convert from URL to a query and back without reference keys', () => { + const url = '/dress/h%26m/brand'; + + expect(generator.build(parser.parse(url))).to.eq(url); + }); + }); + }); +}); diff --git a/test/unit/core/url-beautifier/index.ts b/test/unit/core/url-beautifier/index.ts new file mode 100644 index 00000000..d45a82e3 --- /dev/null +++ b/test/unit/core/url-beautifier/index.ts @@ -0,0 +1,134 @@ +import UrlBeautifier from '../../../../src/core/url-beautifier'; +import BeautifierFactory from '../../../../src/core/url-beautifier/factory'; +import suite, { refinement } from '../../_suite'; + +const ROUTES: any = { + search: '/search', + navigation: '/navigation', + details: '/details' +}; + +suite('URL beautifier', ({ expect, spy, stub }) => { + let config: any; + let beautifier: UrlBeautifier; + let beautifiers: UrlBeautifier.Beautifiers; + + beforeEach(() => { + beautifiers = {}; + config = { refinementMapping: [], queryToken: 'q' }; + stub(BeautifierFactory, 'create').returns(beautifiers); + beautifier = new UrlBeautifier(ROUTES, config); + }); + + describe('build()', () => { + it('should call search url generator', () => { + const build = spy(); + const request = { a: 'b' }; + beautifiers.search = { build }; + + beautifier.build('search', request); + + expect(build).to.have.been.calledWith(request); + }); + + it('should call navigation url generator', () => { + const name = 'Apples'; + const build = spy(); + beautifiers.navigation = { build }; + + beautifier.build('navigation', name); + + expect(build).to.have.been.calledWith(name); + }); + + it('should call details url generator', () => { + const detail = { + title: 'Apples', + id: '12345', + refinements: [] + }; + const build = spy(); + beautifiers.details = { build }; + + beautifier.build('details', detail); + + expect(build).to.have.been.calledWith(detail); + }); + }); + + describe('parse()', () => { + it('should call search url parser', () => { + const parse = spy(); + beautifiers.search = { parse }; + + beautifier.parse('http://example.com/search/apples/green/qc'); + + expect(parse).to.have.been.calledWith('/apples/green/qc'); + }); + + it('should call details url parser', () => { + const parse = spy(); + beautifiers.details = { parse }; + + beautifier.parse('http://example.com/details/apples/green/qc/1045'); + + expect(parse).to.have.been.calledWith('/apples/green/qc/1045'); + }); + + it('should call navigation url parser', () => { + const parse = spy(); + beautifiers.navigation = { parse }; + + beautifier.parse('http://example.com/navigation/Apples'); + + expect(parse).to.have.been.calledWith('/Apples'); + }); + + describe('error state', () => { + it('should throw an error if prefix is none of query, detail or navigation', () => { + expect(() => beautifier.parse('/my/nested/path/power-drill/q')).to.throw('invalid route'); + }); + }); + }); + + describe('configuration errors', () => { + it('should not allow refinement mapping with non-character tokens', () => { + config = { useReferenceKeys: true, refinementMapping: [{ br: 'brand' }] }; + + expect(() => new UrlBeautifier({}, config)).to.throw("token 'br' must be a single character"); + }); + + it('should not allow refinement mapping with vowel tokens', () => { + config = { useReferenceKeys: true, refinementMapping: [{ u: 'brand' }] }; + + expect(() => new UrlBeautifier({}, config)).to.throw("token 'u' must not be a vowel"); + }); + + it('should not allow duplicate refinement tokens', () => { + config = { useReferenceKeys: true, refinementMapping: [{ c: 'brand' }, { c: 'price' }] }; + + expect(() => new UrlBeautifier({}, config)).to.throw("token 'c' must be unique"); + }); + + it('should not allow non-character query token', () => { + config = { queryToken: 'qu', refinementMapping: [] }; + + expect(() => new UrlBeautifier({}, config)).to.throw("token 'qu' must be a single character"); + }); + + it('should not allow vowel query token', () => { + config = { queryToken: 'e', refinementMapping: [] }; + + expect(() => new UrlBeautifier({}, config)).to.throw("token 'e' must not be a vowel"); + }); + + it('should not allow duplicates between query and refinement tokens', () => { + config = { + queryToken: 'k', + refinementMapping: [{ k: 'brand' }] + }; + + expect(() => new UrlBeautifier({}, config)).to.throw("token 'k' must be unique"); + }); + }); +}); diff --git a/test/unit/core/url-beautifier/navigation/generator.ts b/test/unit/core/url-beautifier/navigation/generator.ts new file mode 100644 index 00000000..227c338f --- /dev/null +++ b/test/unit/core/url-beautifier/navigation/generator.ts @@ -0,0 +1,37 @@ +import { UrlGenerator } from '../../../../../src/core/url-beautifier/handler'; +import NavigationUrlGenerator from '../../../../../src/core/url-beautifier/navigation/generator'; +import suite, { refinement } from '../../../_suite'; + +suite('NavigationUrlGenerator', ({ expect }) => { + let generator: NavigationUrlGenerator; + + beforeEach(() => generator = new NavigationUrlGenerator({ config: { navigations: {} } })); + + it('should extend UrlGenerator', () => { + expect(generator).to.be.an.instanceOf(UrlGenerator); + }); + + it('should convert a simple navigation name to a URL', () => { + generator.config.navigations['Apples'] = {}; + + expect(generator.build('Apples')).to.be.eq('/Apples'); + }); + + it('should replace spaces in a navigation name with hyphen', () => { + generator.config.navigations['red apples'] = {}; + + expect(generator.build('red apples')).to.be.eq('/red-apples'); + }); + + it('should encode special characters in navigation name', () => { + generator.config.navigations['red&green apples/grapes'] = {}; + + expect(generator.build('red&green apples/grapes')).to.be.eq('/red%26green-apples%2Fgrapes'); + }); + + describe('error states', () => { + it('should throw an error if the given name is not mapped', () => { + expect(() => generator.build('Apples')).to.throw('no navigation mapping found for Apples'); + }); + }); +}); diff --git a/test/unit/core/url-beautifier/navigation/parser.ts b/test/unit/core/url-beautifier/navigation/parser.ts new file mode 100644 index 00000000..e69b74d2 --- /dev/null +++ b/test/unit/core/url-beautifier/navigation/parser.ts @@ -0,0 +1,47 @@ +import { UrlParser } from '../../../../../src/core/url-beautifier/handler'; +import NavigationUrlParser from '../../../../../src/core/url-beautifier/navigation/parser'; +import suite, { refinement } from '../../../_suite'; + +const QUERY = { a: 'b' }; + +suite('NavigationUrlParser', ({ expect }) => { + let config: any; + let parser: NavigationUrlParser; + + beforeEach(() => { + config = { navigations: { Apples: QUERY } }; + parser = new NavigationUrlParser({ config }); + }); + + it('should extend UrlParser', () => { + expect(parser).to.be.an.instanceOf(UrlParser); + }); + + it('should parse URL and return the associated query', () => { + expect(parser.parse('/Apples')).to.be.eql(QUERY); + }); + + it('should parse URL with encoded characters', () => { + const navigationName = 'Red apples/cherries'; + config.navigations[navigationName] = QUERY; + + expect(parser.parse('/Red-apples%2Fcherries')).to.be.eql(QUERY); + }); + + it('should parse URL with hyphen', () => { + const navigationName = 'Red apples'; + config.navigations[navigationName] = QUERY; + + expect(parser.parse('/' + encodeURIComponent(navigationName))).to.be.eql(QUERY); + }); + + describe('error states', () => { + it('should parse URL and throw an error if associated query is not found', () => { + expect(() => parser.parse('/Orange')).to.throw('no navigation mapping found for Orange'); + }); + + it('should parse URL and throw an error if the path has more than one part', () => { + expect(() => parser.parse('/Apples/Orange')).to.throw('path contains more than one part'); + }); + }); +}); diff --git a/test/unit/core/url-beautifier/search/generator.ts b/test/unit/core/url-beautifier/search/generator.ts new file mode 100644 index 00000000..4f40f8a4 --- /dev/null +++ b/test/unit/core/url-beautifier/search/generator.ts @@ -0,0 +1,219 @@ +import { UrlGenerator } from '../../../../../src/core/url-beautifier/handler'; +import SearchUrlGenerator from '../../../../../src/core/url-beautifier/search/generator'; +import suite, { refinement } from '../../../_suite'; + +const REQUEST = { refinements: [] }; + +suite('SearchUrlGenerator', ({ expect }) => { + let config: any; + let generator: SearchUrlGenerator; + + beforeEach(() => { + config = { params: {}, refinementMapping: [] }; + generator = new SearchUrlGenerator({ config }); + }); + + it('should convert a simple query to a URL', () => { + const query = 'red apples'; + config.queryToken = 'x'; + config.useReferenceKeys = true; + + expect(generator.build({ ...REQUEST, query })).to.have.string('/red-apples/x'); + }); + + it('should convert a simple query to a URL without reference keys', () => { + const query = 'red apples'; + + expect(generator.build({ ...REQUEST, query })).to.have.string('/red-apples'); + }); + + it('should convert query with a slash to a URL', () => { + const query = 'red/apples'; + + expect(generator.build({ ...REQUEST, query })).to.have.string('/red%2Fapples'); + }); + + it('should convert query with a plus to a URL', () => { + const query = 'red+apples'; + config.useReferenceKeys = true; + config.queryToken = 'q'; + + expect(generator.build({ ...REQUEST, query })).to.have.string('/red%2Bapples/q'); + }); + + it('should convert a value refinement query to a URL', () => { + config.refinementMapping = [{ b: 'brand' }]; + + expect(generator.build({ refinements: [refinement('brand', 'DeWalt')] })).to.have.string('/DeWalt/b'); + }); + + it('should convert a multiple refinements on same field a URL', () => { + const refinements = [refinement('brand', 'DeWalt'), refinement('brand', 'Henson')]; + config.useReferenceKeys = true; + config.refinementMapping = [{ b: 'brand' }]; + + expect(generator.build({ refinements })).to.have.string('/DeWalt/Henson/bb'); + }); + + it('should convert a multiple refinements on same field a URL without reference keys', () => { + const query = 'tool'; + const refinements = [refinement('brand', 'DeWalt'), refinement('brand', 'Henson')]; + + expect(generator.build({ query, refinements })).to.have.string('/tool/DeWalt/brand/Henson/brand'); + }); + + it('should convert a sorted refinements list on same field a URL without reference keys', () => { + const query = 'shoe'; + const refinements = [ + refinement('colour', 'blue'), + refinement('Brand', 'nike'), + refinement('Brand', 'adidas'), + refinement('colour', 'red') + ]; + + expect(generator.build({ query, refinements })) + .to.have.string('/shoe/adidas/Brand/nike/Brand/blue/colour/red/colour'); + }); + + it('should convert a refinement with a slash to a URL', () => { + config.refinementMapping = [{ b: 'brand' }]; + + expect(generator.build({ refinements: [refinement('brand', 'De/Walt')] })).to.have.string('/De%2FWalt/b'); + }); + + it('should convert a refinement with a plus to a URL', () => { + config.refinementMapping = [{ b: 'brand' }]; + + expect(generator.build({ refinements: [refinement('brand', 'De+Walt')] })).to.have.string('/De%2BWalt/b'); + }); + + it('should convert a multiple refinement query to a URL', () => { + const refinements = [refinement('brand', 'Farmer John'), refinement('height', '20in')]; + config.useReferenceKeys = true; + config.refinementMapping = [{ b: 'brand' }, { h: 'height' }]; + + expect(generator.build({ refinements })).to.have.string('/Farmer-John/20in/bh'); + }); + + it('should convert query and refinements to a URL', () => { + const request: any = { query: 'cool sneakers', refinements: [refinement('colour', 'green')] }; + config.useReferenceKeys = true; + config.queryToken = 'q'; + config.refinementMapping = [{ c: 'colour' }]; + + expect(generator.build(request)).to.have.string('/cool-sneakers/green/qc'); + }); + + it('should not convert range refinements to a URL', () => { + config.useReferenceKeys = true; + config.refinementMapping = [{ p: 'price' }]; + + expect(() => generator.build({ refinements: [refinement('price', 20, 40)] })) + .to.throw('cannot map range refinements'); + }); + + it('should convert unmapped refinements to a query parameter', () => { + const refinements = [refinement('colour', 'dark purple'), refinement('price', 100, 220)]; + config.useReferenceKeys = true; + config.params = { refinements: 'refinements' }; + + expect(generator.build({ refinements })) + .to.have.string('/?refinements=colour%3Adark-purple~price%3A100..220'); + }); + + it('should convert pageSize to a query parameter', () => { + config.params = { pageSize: 'page_size' }; + + expect(generator.build({ ...REQUEST, pageSize: 24 })).to.have.string('/?page_size=24'); + }); + + it('should convert skip to a query parameter', () => { + config.params = { page: 'page' }; + + expect(generator.build({ ...REQUEST, page: 4 })).to.have.string('/?page=4'); + }); + + it('should convert skip and pageSize to a query parameter', () => { + config.useReferenceKeys = true; + config.params = { pageSize: 'page_size', page: 'page' }; + + expect(generator.build({ ...REQUEST, pageSize: 30, page: 2 })) + .to.have.string('/?page=2&page_size=30'); + }); + + it('should convert query with skip, page size and unmapped refinements to a URL without reference keys', () => { + const request: any = { + query: 'red apples', + refinements: [refinement('colour', 'dark purple'), refinement('price', 100, 220)], + page: 4, + pageSize: 19 + }; + config.params = { pageSize: 'page_size', page: 'page', refinements: 'refinements' }; + + expect(generator.build(request)) + .to.eq('/red-apples/dark-purple/colour?page=4&page_size=19&refinements=price%3A100..220'); + }); + + it('should convert query with unmapped refinements to a URL with reference keys', () => { + const request: any = { + query: 'long red dress', + refinements: [ + refinement('category', 'evening wear'), + refinement('category', 'formal'), + refinement('size', 'large'), + refinement('shipping', 'true') + ] + }; + config.useReferenceKeys = true; + config.params = { pageSize: 'page_size', page: 'page', refinements: 'refinements' }; + config.queryToken = 'q'; + config.refinementMapping = [{ c: 'category' }]; + + const url = generator.build(request); + + expect(url).to.have.string(`/long-red-dress/evening-wear/formal/qcc`); + expect(url).to.have.string(`refinements=shipping%3Atrue~size%3Alarge`); + }); + + describe('canonical URLs', () => { + const ref1 = refinement('colour', 'orange'); + const ref2 = refinement('brand', 'DeWalt'); + const ref3 = refinement('category', 'Drills'); + + it('should create canonical URLs', () => { + const refinements = [ref3, ref1, ref2]; + const otherRefinements = [ref1, ref2, ref3]; + config.refinementMapping = [{ c: 'colour' }, { b: 'brand' }, { h: 'category' }]; + + expect(generator.build({ refinements })) + .to.eq(generator.build({ refinements: otherRefinements })); + }); + + it('should create canonical URLs with multiple refinements on same field', () => { + config.useReferenceKeys = true; + config.refinementMapping = [{ b: 'brand' }]; + + expect(generator.build({ refinements: [refinement('brand', 'Henson'), refinement('brand', 'DeWalt')] })) + .to.have.string('/DeWalt/Henson/bb'); + }); + + it('should create canonical query parameters', () => { + expect(generator.build({ refinements: [ref1, ref2, ref3] })) + .to.eq(generator.build({ refinements: [ref3, ref1, ref2] })); + }); + + it('should combine mapped and unmapped refinements with query and suffix', () => { + config.useReferenceKeys = true; + config.refinementMapping = [{ b: 'brand' }, { c: 'category' }]; + config.queryToken = 's'; + config.params = { refinements: 'refs' }; + config.suffix = 'index.php'; + + const url = generator.build({ query: 'power drill', refinements: [ref1, ref3, ref2] }); + + expect(url).to.have.string('/power-drill/DeWalt/Drills/sbc/index.php'); + expect(url).to.have.string('refs=colour%3Aorange'); + expect(url).to.eq(generator.build({ query: 'power drill', refinements: [ref2, ref1, ref3] })); + }); + }); +}); diff --git a/test/unit/core/url-beautifier/search/parser.ts b/test/unit/core/url-beautifier/search/parser.ts new file mode 100644 index 00000000..4c0063e6 --- /dev/null +++ b/test/unit/core/url-beautifier/search/parser.ts @@ -0,0 +1,241 @@ +import { UrlParser } from '../../../../../src/core/url-beautifier/handler'; +import SearchUrlParser from '../../../../../src/core/url-beautifier/search/parser'; +import suite, { refinement } from '../../../_suite'; + +const QUERY = { a: 'b' }; + +suite('SearchUrlParser', ({ expect }) => { + let config: any; + let parser: SearchUrlParser; + + beforeEach(() => { + config = { + params: { page: 'page', pageSize: 'page_size', refinements: 'refinements' }, + queryToken: 'q', + refinementMapping: [], + suffix: 'index.html' + }; + parser = new SearchUrlParser({ config }); + }); + + it('should parse simple query URL', () => { + config.useReferenceKeys = true; + + expect(parser.parse('/apples/q').query).to.eq('apples'); + }); + + it('should parse URL with a slash in the query', () => { + config.useReferenceKeys = true; + + expect(parser.parse('/red%2Fapples/q').query).to.eq('red/apples'); + }); + + it('should parse URL with a plus in the query', () => { + config.useReferenceKeys = true; + + expect(parser.parse('/red%2Bapples/q').query).to.eq('red+apples'); + }); + + it('should parse simple query URL with dash and without reference keys', () => { + expect(parser.parse('/red-apples').query).to.eq('red apples'); + }); + + it('should parse simple query URL with custom token', () => { + config.useReferenceKeys = true; + config.queryToken = 'c'; + + expect(parser.parse('/sneakers/c').query).to.eq('sneakers'); + }); + + it('should extract a value refinement from URL', () => { + config.useReferenceKeys = true; + config.refinementMapping = [{ c: 'colour' }]; + + expect(parser.parse('/green/c').refinements).to.eql([refinement('colour', 'green')]); + }); + + it('should extract a multiple value refinements for field from URL', () => { + config.useReferenceKeys = true; + config.refinementMapping = [{ c: 'colour' }]; + + expect(parser.parse('/green/blue/cc').refinements).to.eql([ + refinement('colour', 'green'), + refinement('colour', 'blue') + ]); + }); + + it('should extract a value refinement with a slash from URL', () => { + config.useReferenceKeys = true; + config.refinementMapping = [{ b: 'brand' }]; + + expect(parser.parse('/De%2FWalt/b').refinements).to.eql([refinement('brand', 'De/Walt')]); + }); + + it('should extract a value refinement with a plus from URL', () => { + config.useReferenceKeys = true; + config.refinementMapping = [{ b: 'brand' }]; + + expect(parser.parse('/De%2BWalt/b').refinements).to.eql([refinement('brand', 'De+Walt')]); + }); + + it('should extract multiple refinements from URL', () => { + config.useReferenceKeys = true; + config.refinementMapping = [{ c: 'colour', b: 'brand' }]; + + expect(parser.parse('/dark-purple/Wellingtons/cb').refinements).to.eql([ + refinement('colour', 'dark purple'), + refinement('brand', 'Wellingtons') + ]); + }); + + it('should extract a query and refinement from URL', () => { + config.useReferenceKeys = true; + config.refinementMapping = [{ c: 'colour' }]; + + expect(parser.parse('/sneakers/green/qc')).to.eql({ + query: 'sneakers', + refinements: [refinement('colour', 'green')] + }); + }); + + it('should extract query and value refinements from URL without reference keys', () => { + expect(parser.parse('/shoe/blue/colour/red/colour/adidas/Brand/nike/Brand')) + .to.eql({ + query: 'shoe', + refinements: [ + refinement('colour', 'blue'), + refinement('colour', 'red'), + refinement('Brand', 'adidas'), + refinement('Brand', 'nike') + ] + }); + }); + + it('should extract value refinements from URL without reference keys', () => { + expect(parser.parse('/blue/colour/red/colour/adidas/Brand/nike/Brand').refinements).to.eql([ + refinement('colour', 'blue'), + refinement('colour', 'red'), + refinement('Brand', 'adidas'), + refinement('Brand', 'nike') + ]); + }); + + it('should extract unmapped query from URL parameters', () => { + expect(parser.parse('/?refinements=height%3A20in~price%3A20..30').refinements).to.eql([ + refinement('height', '20in'), + refinement('price', 20, 30) + ]); + }); + + it('should extract query and range refinements from URL without reference key', () => { + const url = '/long-red-dress/evening-wear/category/formal/category?refinements=price:50..200'; + + expect(parser.parse(url)).to.eql({ + query: 'long red dress', + refinements: [ + refinement('category', 'evening wear'), + refinement('category', 'formal'), + refinement('price', 50, 200) + ] + }); + }); + + it('should extract page size from URL', () => { + expect(parser.parse('/?page_size=5').pageSize).to.eq(5); + }); + + it('should extract page from URL', () => { + expect(parser.parse('/?page=2').page).to.eq(2); + }); + + it('should extract page and page size from URL', () => { + const page = 3; + const pageSize = 6; + const skip = (page - 1) * pageSize; + + const request = parser.parse('/?page=3&page_size=6'); + + expect(request.page).to.eq(3); + expect(request.pageSize).to.eq(6); + }); + + it('should ignore suffix', () => { + config.useReferenceKeys = true; + config.refinementMapping = [{ h: 'height' }]; + config.suffix = 'index.html'; + + expect(parser.parse('/20in/h/index.html?refinements=price%3A20..30').refinements).to.eql([ + refinement('height', '20in'), + refinement('price', 20, 30) + ]); + }); + + it('should extract mapped and unmapped refinements with query and suffix', () => { + const refs = [refinement('category', 'Drills'), refinement('brand', 'DeWalt'), refinement('colour', 'orange')]; + config.refinementMapping = [{ s: 'colour' }, { c: 'category' }]; + config.params.refinements = 'nav'; + config.queryToken = 'n'; + config.suffix = 'index.html'; + + const request = parser.parse('/power-drill/orange/Drills/nsc/index.html?nav=brand%3ADeWalt'); + + expect(request.query).to.eql('power drill'); + expect(request.refinements).to.have.deep.members(refs); + }); + + it('should extract mapped and unmapped refinements with query and suffix from URL without reference keys', () => { + const url = '/power-drill/DeWalt/brand/Drills/category/orange/colour/index.html'; + config.suffix = 'index.html'; + + const request = parser.parse(url); + + expect(request.query).to.eq('power drill'); + expect(request.refinements).to.eql([ + refinement('brand', 'DeWalt'), + refinement('category', 'Drills'), + refinement('colour', 'orange') + ]); + }); + + it('should extract deeply nested URL', () => { + config.useReferenceKeys = true; + + const request = parser.parse('http://example.com/my/nested/path/power-drill/q'); + + expect(request.query).to.eql('power drill'); + }); + + it('should extract mapped and unmapped refinements with query and suffix', () => { + const url = '/power-drill/orange/Drills/nsc/index.html?nav=brand%3ADeWalt'; + config.useReferenceKeys = true; + config.refinementMapping = [{ s: 'colour' }, { c: 'category' }]; + config.params = { refinements: 'nav' }; + config.queryToken = 'n'; + + const request = parser.parse(url); + + expect(request.query).to.eq('power drill'); + expect(request.refinements).to.have.deep.members([ + refinement('category', 'Drills'), + refinement('brand', 'DeWalt'), + refinement('colour', 'orange') + ]); + }); + + describe('error states', () => { + it('should error on invalid reference keys', () => { + config.useReferenceKeys = true; + config.refinementMapping = [{ c: 'colour' }, { b: 'brand' }]; + + expect(() => parser.parse('/power-drill/orange/Drills/qccb')) + .to.throw('token reference is invalid'); + }); + + it('should error on unrecognized key', () => { + config.useReferenceKeys = true; + config.refinementMapping = [{ c: 'colour' }]; + + expect(() => parser.parse('/Drills/b')).to.throw('unexpected token \'b\' found in reference'); + }); + }); +}); From a3cfb44ddc157f9d3ae9d6bd3d27798437890a43 Mon Sep 17 00:00:00 2001 From: Ben Teichman Date: Sat, 10 Jun 2017 17:43:30 -0400 Subject: [PATCH 8/8] merge all url-beautifier tests --- src/core/url-beautifier/search/generator.ts | 8 ++++++-- test/unit/core/url-beautifier/factory.ts | 6 ++++++ test/unit/core/url-beautifier/search/parser.ts | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/core/url-beautifier/search/generator.ts b/src/core/url-beautifier/search/generator.ts index 149f64b4..ecf09db5 100644 --- a/src/core/url-beautifier/search/generator.ts +++ b/src/core/url-beautifier/search/generator.ts @@ -26,8 +26,12 @@ export default class SearchUrlGenerator extends UrlGenerator { }); it('should convert from query object to a URL and back with reference keys', () => { + config.params = { refinements: 'refinements' }; config.refinementMapping = [{ b: 'brand' }]; config.useReferenceKeys = true; + config.queryToken = 's'; expect(parser.parse(generator.build(request))).to.eql(request); }); it('should convert from URL to a query and back with reference keys', () => { const url = '/dress/h%26m/qb'; + config.params = { refinements: 'refinements' }; config.refinementMapping = [{ b: 'brand' }]; config.useReferenceKeys = true; config.queryToken = 'q'; @@ -130,11 +133,14 @@ suite('BeautifierFactory', ({ expect, stub }) => { }); it('should convert from query object to a URL and back without reference keys', () => { + config.params = { refinements: 'refinements' }; + expect(parser.parse(generator.build(request))).to.eql(request); }); it('should convert from URL to a query and back without reference keys', () => { const url = '/dress/h%26m/brand'; + config.params = { refinements: 'refinements' }; expect(generator.build(parser.parse(url))).to.eq(url); }); diff --git a/test/unit/core/url-beautifier/search/parser.ts b/test/unit/core/url-beautifier/search/parser.ts index 4c0063e6..61df5869 100644 --- a/test/unit/core/url-beautifier/search/parser.ts +++ b/test/unit/core/url-beautifier/search/parser.ts @@ -172,6 +172,7 @@ suite('SearchUrlParser', ({ expect }) => { it('should extract mapped and unmapped refinements with query and suffix', () => { const refs = [refinement('category', 'Drills'), refinement('brand', 'DeWalt'), refinement('colour', 'orange')]; + config.useReferenceKeys = true; config.refinementMapping = [{ s: 'colour' }, { c: 'category' }]; config.params.refinements = 'nav'; config.queryToken = 'n';