adds functions for async array traversal. adds CUSTOM pattern field for field formats. updates ember-concurrency to add support for waitForProperty, and updates typings.

This commit is contained in:
Seyi Adebajo 2018-05-02 09:45:26 -07:00
parent b7ba9e4f7a
commit 933982bc0d
19 changed files with 483 additions and 128 deletions

View File

@ -1,6 +1,6 @@
import Component from '@ember/component'; import Component from '@ember/component';
import ComputedProperty from '@ember/object/computed'; import ComputedProperty from '@ember/object/computed';
import { get, getProperties, computed } from '@ember/object'; import { set, get, getProperties, computed } from '@ember/object';
import { ComplianceFieldIdValue, idTypeFieldHasLogicalType, isTagIdType } from 'wherehows-web/constants'; import { ComplianceFieldIdValue, idTypeFieldHasLogicalType, isTagIdType } from 'wherehows-web/constants';
import { import {
IComplianceChangeSet, IComplianceChangeSet,
@ -12,6 +12,7 @@ import { IComplianceDataType } from 'wherehows-web/typings/api/list/compliance-d
import { action } from 'ember-decorators/object'; import { action } from 'ember-decorators/object';
import { IComplianceEntity } from 'wherehows-web/typings/api/datasets/compliance'; import { IComplianceEntity } from 'wherehows-web/typings/api/datasets/compliance';
import { arrayFilter } from 'wherehows-web/utils/array'; import { arrayFilter } from 'wherehows-web/utils/array';
import { IdLogicalType } from 'wherehows-web/constants/datasets/compliance';
/** /**
* Constant definition for an unselected field format * Constant definition for an unselected field format
@ -47,6 +48,11 @@ export default class DatasetComplianceFieldTag extends Component {
*/ */
onTagLogicalTypeChange: (tag: IComplianceChangeSet, value: IComplianceChangeSet['logicalType']) => void; onTagLogicalTypeChange: (tag: IComplianceChangeSet, value: IComplianceChangeSet['logicalType']) => void;
/**
* Describes the interface for the parent action `onTagValuePatternChange`
*/
onTagValuePatternChange: (tag: IComplianceChangeSet, pattern: string) => string | void;
/** /**
* Describes the parent action interface for `onTagOwnerChange` * Describes the parent action interface for `onTagOwnerChange`
*/ */
@ -66,6 +72,12 @@ export default class DatasetComplianceFieldTag extends Component {
*/ */
parentHasSingleTag: boolean; parentHasSingleTag: boolean;
/**
* Stores the value of error result if the valuePattern is invalid
* @type {string}
*/
valuePatternError: string = '';
/** /**
* List of identifierTypes for the parent field * List of identifierTypes for the parent field
* @type {Array<IComplianceEntity['identifierType']>} * @type {Array<IComplianceEntity['identifierType']>}
@ -147,6 +159,15 @@ export default class DatasetComplianceFieldTag extends Component {
return fieldFormatOptions; return fieldFormatOptions;
}); });
/**
* Determines if the CUSTOM input field should be shown for this row's tag
* @type {ComputedProperty<boolean>}
*/
showCustomInput = computed('tag.logicalType', function(this: DatasetComplianceFieldTag): boolean {
const { logicalType } = get(this, 'tag');
return logicalType === IdLogicalType.Custom;
});
/** /**
* Checks if the field format / logical type for this tag is missing, if the field is of ID type * Checks if the field format / logical type for this tag is missing, if the field is of ID type
* @type {ComputedProperty<boolean>} * @type {ComputedProperty<boolean>}
@ -156,6 +177,14 @@ export default class DatasetComplianceFieldTag extends Component {
return get(this, 'isIdType') && !idTypeFieldHasLogicalType(get(this, 'tag')); return get(this, 'isIdType') && !idTypeFieldHasLogicalType(get(this, 'tag'));
}); });
/**
* Sets the value of the pattern error string after p
* @param {string} errorString
*/
setPatternErrorString(errorString: string = '') {
set(this, 'valuePatternError', errorString.replace('SyntaxError: ', ''));
}
/** /**
* Handles UI changes to the tag identifierType * Handles UI changes to the tag identifierType
* @param {{ value: ComplianceFieldIdValue }} { value } * @param {{ value: ComplianceFieldIdValue }} { value }
@ -191,4 +220,23 @@ export default class DatasetComplianceFieldTag extends Component {
// inverts the value of nonOwner, toggle is shown in the UI as `Owner` i.e. not nonOwner // inverts the value of nonOwner, toggle is shown in the UI as `Owner` i.e. not nonOwner
get(this, 'onTagOwnerChange')(get(this, 'tag'), !nonOwner); get(this, 'onTagOwnerChange')(get(this, 'tag'), !nonOwner);
} }
/**
* Invokes the parent action on user input for value pattern
* If an exception is thrown, valuePatternError is updated with string value
* @param {string} pattern user input string
*/
@action
tagValuePatternDidChange(this: DatasetComplianceFieldTag, pattern: string) {
try {
const valuePattern = get(this, 'onTagValuePatternChange')(get(this, 'tag'), pattern);
if (valuePattern) {
//clear pattern error
this.setPatternErrorString();
}
} catch (e) {
this.setPatternErrorString(e.toString());
}
}
} }

View File

@ -36,13 +36,13 @@ export default class DatasetComplianceRollupRow extends Component.extend({
* References the parent external action to add a tag to the list of change sets * References the parent external action to add a tag to the list of change sets
* @memberof DatasetComplianceRollupRow * @memberof DatasetComplianceRollupRow
*/ */
onFieldTagAdded: (tag: IComplianceChangeSet) => IComplianceChangeSet; onFieldTagAdded: (tag: IComplianceChangeSet) => void;
/** /**
* References the parent external action to add a tag to the list of change sets * References the parent external action to add a tag to the list of change sets
* @memberof DatasetComplianceRollupRow * @memberof DatasetComplianceRollupRow
*/ */
onFieldTagRemoved: (tag: IComplianceChangeSet) => IComplianceChangeSet; onFieldTagRemoved: (tag: IComplianceChangeSet) => void;
/** /**
* Describes action interface for `onSuggestionIntent` action * Describes action interface for `onSuggestionIntent` action
@ -153,7 +153,7 @@ export default class DatasetComplianceRollupRow extends Component.extend({
* Checks if any of the tags on this field have a ComplianceFieldIdValue.None identifierType * Checks if any of the tags on this field have a ComplianceFieldIdValue.None identifierType
* @type {ComputedProperty<boolean>} * @type {ComputedProperty<boolean>}
*/ */
hasNoneTag: ComputedProperty<boolean> = computed('fieldChangeSet', function( hasNoneTag: ComputedProperty<boolean> = computed('fieldChangeSet.@each.identifierType', function(
this: DatasetComplianceRollupRow this: DatasetComplianceRollupRow
): boolean { ): boolean {
return tagsHaveNoneType(get(this, 'fieldChangeSet')); return tagsHaveNoneType(get(this, 'fieldChangeSet'));
@ -344,8 +344,10 @@ export default class DatasetComplianceRollupRow extends Component.extend({
// Accept the suggestion for either identifierType and/or logicalType // Accept the suggestion for either identifierType and/or logicalType
if (suggestion && intent === SuggestionIntent.accept) { if (suggestion && intent === SuggestionIntent.accept) {
const { identifierType, logicalType } = suggestion; const { identifierType, logicalType } = suggestion;
// Field has only one tag, that tag does not currently have an identifierType
const updateDefault = hasSingleTag && !fieldTagsHaveIdentifierType(get(this, 'fieldChangeSet')); const updateDefault = hasSingleTag && !fieldTagsHaveIdentifierType(get(this, 'fieldChangeSet'));
// Identifier type and changeSet does not already have suggested type
if (identifierType && !suggestedValuesInChangeSet.includes(identifierType)) { if (identifierType && !suggestedValuesInChangeSet.includes(identifierType)) {
if (updateDefault) { if (updateDefault) {
get(this, 'onTagIdentifierTypeChange')(get(this, 'fieldProps'), { get(this, 'onTagIdentifierTypeChange')(get(this, 'fieldProps'), {

View File

@ -8,7 +8,7 @@ import { assert } from '@ember/debug';
import { IDatasetView } from 'wherehows-web/typings/api/datasets/dataset'; import { IDatasetView } from 'wherehows-web/typings/api/datasets/dataset';
import { IDataPlatform } from 'wherehows-web/typings/api/list/platforms'; import { IDataPlatform } from 'wherehows-web/typings/api/list/platforms';
import { readPlatforms } from 'wherehows-web/utils/api/list/platforms'; import { readPlatforms } from 'wherehows-web/utils/api/list/platforms';
import { task, TaskInstance } from 'ember-concurrency'; import { task, waitForProperty, TaskInstance } from 'ember-concurrency';
import { import {
getSecurityClassificationDropDownOptions, getSecurityClassificationDropDownOptions,
DatasetClassifiers, DatasetClassifiers,
@ -28,13 +28,13 @@ import {
isTagIdType, isTagIdType,
idTypeFieldsHaveLogicalType, idTypeFieldsHaveLogicalType,
changeSetReviewableAttributeTriggers, changeSetReviewableAttributeTriggers,
mapSchemaColumnPropsToCurrentPrivacyPolicy, asyncMapSchemaColumnPropsToCurrentPrivacyPolicy,
foldComplianceChangeSets, foldComplianceChangeSets,
sortFoldedChangeSetTuples sortFoldedChangeSetTuples
} from 'wherehows-web/constants'; } from 'wherehows-web/constants';
import { isPolicyExpectedShape } from 'wherehows-web/utils/datasets/compliance-policy'; import { isPolicyExpectedShape } from 'wherehows-web/utils/datasets/compliance-policy';
import { getTagsSuggestions } from 'wherehows-web/utils/datasets/compliance-suggestions'; import { getTagsSuggestions } from 'wherehows-web/utils/datasets/compliance-suggestions';
import { compact, isListUnique } from 'wherehows-web/utils/array'; import { arrayMap, compact, isListUnique, iterateArrayAsync } from 'wherehows-web/utils/array';
import noop from 'wherehows-web/utils/noop'; import noop from 'wherehows-web/utils/noop';
import { IComplianceDataType } from 'wherehows-web/typings/api/list/compliance-datatypes'; import { IComplianceDataType } from 'wherehows-web/typings/api/list/compliance-datatypes';
import Notifications, { NotificationEvent, IConfirmOptions } from 'wherehows-web/services/notifications'; import Notifications, { NotificationEvent, IConfirmOptions } from 'wherehows-web/services/notifications';
@ -57,6 +57,9 @@ import {
ShowAllShowReview ShowAllShowReview
} from 'wherehows-web/typings/app/dataset-compliance'; } from 'wherehows-web/typings/app/dataset-compliance';
import { uniqBy } from 'lodash'; import { uniqBy } from 'lodash';
import { IColumnFieldProps } from 'wherehows-web/typings/app/dataset-columns';
import { isValidCustomValuePattern } from 'wherehows-web/utils/validators/urn';
import { emptyRegexSource } from 'wherehows-web/utils/validators/regexp';
const { const {
complianceDataException, complianceDataException,
@ -80,12 +83,6 @@ const datasetClassificationKey = 'complianceInfo.datasetClassification';
*/ */
const datasetClassifiersKeys = <Array<keyof typeof DatasetClassifiers>>Object.keys(DatasetClassifiers); const datasetClassifiersKeys = <Array<keyof typeof DatasetClassifiers>>Object.keys(DatasetClassifiers);
/**
* A reference to the compliance policy entities on the complianceInfo map
* @type {string}
*/
const policyComplianceEntitiesKey = 'complianceInfo.complianceEntities';
/** /**
* The initial state of the compliance step for a zero based array * The initial state of the compliance step for a zero based array
* @type {number} * @type {number}
@ -203,6 +200,12 @@ export default class DatasetCompliance extends Component {
*/ */
supportedPurgePolicies: Array<PurgePolicy> = []; supportedPurgePolicies: Array<PurgePolicy> = [];
/**
* Computed prop over the current Id fields in the Privacy Policy
* @type {ISchemaFieldsToPolicy}
*/
columnIdFieldsToCurrentPrivacyPolicy: ISchemaFieldsToPolicy = {};
constructor() { constructor() {
super(...arguments); super(...arguments);
@ -378,6 +381,13 @@ export default class DatasetCompliance extends Component {
didInsertElement(this: DatasetCompliance) { didInsertElement(this: DatasetCompliance) {
get(this, 'complianceAvailabilityTask').perform(); get(this, 'complianceAvailabilityTask').perform();
get(this, 'columnFieldsToCompliancePolicyTask').perform();
get(this, 'foldChangeSetTask').perform();
}
didUpdateAttrs() {
get(this, 'columnFieldsToCompliancePolicyTask').perform();
get(this, 'foldChangeSetTask').perform();
} }
/** /**
@ -517,34 +527,47 @@ export default class DatasetCompliance extends Component {
}); });
/** /**
* Computed prop over the current Id fields in the Privacy Policy * Task to retrieve column fields async and set values on Component
* @type {ComputedProperty<ISchemaFieldsToPolicy>} * @type {Task<Promise<any>, () => TaskInstance<Promise<any>>>}
* @memberof DatasetCompliance
*/ */
columnIdFieldsToCurrentPrivacyPolicy: ComputedProperty<ISchemaFieldsToPolicy> = computed( columnFieldsToCompliancePolicyTask = task(function*(this: DatasetCompliance): IterableIterator<any> {
`{schemaFieldNamesMappedToDataTypes,${policyComplianceEntitiesKey}.[]}`,
function(this: DatasetCompliance): ISchemaFieldsToPolicy {
const {
complianceEntities = [],
modifiedTime
}: Pick<IComplianceInfo, 'complianceEntities' | 'modifiedTime'> = get(this, 'complianceInfo') || {
complianceEntities: []
};
// Truncated list of Dataset field names and data types currently returned from the column endpoint // Truncated list of Dataset field names and data types currently returned from the column endpoint
const columnProps = getWithDefault(this, 'schemaFieldNamesMappedToDataTypes', []).map( const schemaFieldNamesMappedToDataTypes: DatasetCompliance['schemaFieldNamesMappedToDataTypes'] = yield waitForProperty(
({ fieldName, dataType }) => ({ this,
identifierField: fieldName, 'schemaFieldNamesMappedToDataTypes',
dataType ({ length }) => !!length
})
); );
return mapSchemaColumnPropsToCurrentPrivacyPolicy({ const { complianceEntities = [], modifiedTime }: Pick<IComplianceInfo, 'complianceEntities' | 'modifiedTime'> = get(
this,
'complianceInfo'
)!;
const renameFieldNameAttr = ({
fieldName,
dataType
}: Pick<IDatasetColumn, 'dataType' | 'fieldName'>): {
identifierField: IDatasetColumn['fieldName'];
dataType: IDatasetColumn['dataType'];
} => ({
identifierField: fieldName,
dataType
});
const columnProps: Array<IColumnFieldProps> = yield iterateArrayAsync(arrayMap(renameFieldNameAttr))(
schemaFieldNamesMappedToDataTypes
);
const columnIdFieldsToCurrentPrivacyPolicy: ISchemaFieldsToPolicy = yield asyncMapSchemaColumnPropsToCurrentPrivacyPolicy(
{
columnProps, columnProps,
complianceEntities, complianceEntities,
policyModificationTime: modifiedTime policyModificationTime: modifiedTime
});
} }
); );
set(this, 'columnIdFieldsToCurrentPrivacyPolicy', columnIdFieldsToCurrentPrivacyPolicy);
}).enqueue();
/** /**
* Creates a mapping of compliance suggestions to identifierField * Creates a mapping of compliance suggestions to identifierField
* This improves performance in a subsequent merge op since this loop * This improves performance in a subsequent merge op since this loop
@ -635,16 +658,27 @@ export default class DatasetCompliance extends Component {
/** /**
* Reduces the current filtered changeSet to a list of IdentifierFieldWithFieldChangeSetTuple * Reduces the current filtered changeSet to a list of IdentifierFieldWithFieldChangeSetTuple
* @type {ComputedProperty<Array<IdentifierFieldWithFieldChangeSetTuple>>} * @type {Array<IdentifierFieldWithFieldChangeSetTuple>}
* @memberof DatasetCompliance * @memberof DatasetCompliance
*/ */
foldedChangeSet: ComputedProperty<Array<IdentifierFieldWithFieldChangeSetTuple>> = computed( foldedChangeSet: Array<IdentifierFieldWithFieldChangeSetTuple>;
'filteredChangeSet',
function(this: DatasetCompliance): Array<IdentifierFieldWithFieldChangeSetTuple> { /**
return sortFoldedChangeSetTuples(foldComplianceChangeSets(get(this, 'filteredChangeSet'))); * Task to retrieve platform policies and set supported policies for the current platform
} * @type {Task<Promise<any>, () => TaskInstance<Promise<any>>>}
* @memberof DatasetCompliance
*/
foldChangeSetTask = task(function*(this: DatasetCompliance): IterableIterator<any> {
//@ts-ignore dot notation for property access
yield waitForProperty(this, 'columnFieldsToCompliancePolicyTask.isIdle');
const filteredChangeSet = get(this, 'filteredChangeSet');
const foldedChangeSet: Array<IdentifierFieldWithFieldChangeSetTuple> = yield foldComplianceChangeSets(
filteredChangeSet
); );
set(this, 'foldedChangeSet', sortFoldedChangeSetTuples(foldedChangeSet));
}).enqueue();
/** /**
* Invokes external action with flag indicating that at least 1 suggestion exists for a field in the changeSet * Invokes external action with flag indicating that at least 1 suggestion exists for a field in the changeSet
* @param {Array<IComplianceChangeSet>} changeSet * @param {Array<IComplianceChangeSet>} changeSet
@ -716,13 +750,22 @@ export default class DatasetCompliance extends Component {
const formattedAndUnformattedEntities: FormattedAndUnformattedEntities = { formatted: [], unformatted: [] }; const formattedAndUnformattedEntities: FormattedAndUnformattedEntities = { formatted: [], unformatted: [] };
// All candidate fields that can be on policy, excluding tracking type fields // All candidate fields that can be on policy, excluding tracking type fields
const changeSetEntities: Array<IComplianceEntity> = get(this, 'compliancePolicyChangeSet').map( const changeSetEntities: Array<IComplianceEntity> = get(this, 'compliancePolicyChangeSet').map(
({ identifierField, identifierType = null, logicalType, nonOwner, securityClassification, readonly }) => ({ ({
identifierField,
identifierType = null,
logicalType,
nonOwner,
securityClassification,
readonly,
valuePattern
}) => ({
identifierField, identifierField,
identifierType, identifierType,
logicalType, logicalType,
nonOwner, nonOwner,
securityClassification, securityClassification,
readonly readonly,
valuePattern
}) })
); );
@ -885,8 +928,9 @@ export default class DatasetCompliance extends Component {
* @param {IComplianceChangeSet} tag properties for new field tag * @param {IComplianceChangeSet} tag properties for new field tag
* @return {IComplianceChangeSet} * @return {IComplianceChangeSet}
*/ */
onFieldTagAdded(this: DatasetCompliance, tag: IComplianceChangeSet): IComplianceChangeSet { onFieldTagAdded(this: DatasetCompliance, tag: IComplianceChangeSet): void {
return get(this, 'compliancePolicyChangeSet').addObject(tag); get(this, 'compliancePolicyChangeSet').addObject(tag);
get(this, 'foldChangeSetTask').perform();
}, },
/** /**
@ -894,8 +938,9 @@ export default class DatasetCompliance extends Component {
* @param {IComplianceChangeSet} tag * @param {IComplianceChangeSet} tag
* @return {IComplianceChangeSet} * @return {IComplianceChangeSet}
*/ */
onFieldTagRemoved(this: DatasetCompliance, tag: IComplianceChangeSet): IComplianceChangeSet { onFieldTagRemoved(this: DatasetCompliance, tag: IComplianceChangeSet): void {
return get(this, 'compliancePolicyChangeSet').removeObject(tag); get(this, 'compliancePolicyChangeSet').removeObject(tag);
get(this, 'foldChangeSetTask').perform();
}, },
/** /**
@ -914,7 +959,8 @@ export default class DatasetCompliance extends Component {
identifierType, identifierType,
logicalType: null, logicalType: null,
nonOwner: null, nonOwner: null,
isDirty: true isDirty: true,
valuePattern: void 0
}); });
} }
@ -926,21 +972,34 @@ export default class DatasetCompliance extends Component {
* @param {IComplianceChangeSet} tag the tag to be updated * @param {IComplianceChangeSet} tag the tag to be updated
* @param {IComplianceChangeSet.logicalType} logicalType the updated logical type * @param {IComplianceChangeSet.logicalType} logicalType the updated logical type
*/ */
tagLogicalTypeChanged( tagLogicalTypeChanged(tag: IComplianceChangeSet, logicalType: IComplianceChangeSet['logicalType']) {
this: DatasetCompliance,
tag: IComplianceChangeSet,
logicalType: IComplianceChangeSet['logicalType']
) {
setProperties(tag, { logicalType, isDirty: true }); setProperties(tag, { logicalType, isDirty: true });
}, },
/**
* Handles changes to the valuePattern attribute on a tag
* @param {IComplianceChangeSet} tag
* @param {string} pattern
* @return {string | void}
* @throws {SyntaxError}
*/
tagValuePatternChanged(tag: IComplianceChangeSet, pattern: string): string | void {
const isValidRegex = new RegExp(pattern); // Will throw if invalid
const isValidValuePattern = isValidCustomValuePattern(pattern);
if (isValidRegex.source !== emptyRegexSource && isValidRegex && isValidValuePattern) {
return set(tag, 'valuePattern', isValidRegex.source);
}
throw new Error('Pattern not valid');
},
/** /**
* Updates the security classification on a field tag * Updates the security classification on a field tag
* @param {IComplianceChangeSet} tag the tag to be updated * @param {IComplianceChangeSet} tag the tag to be updated
* @param {IComplianceChangeSet.securityClassification} securityClassification the updated security classification value * @param {IComplianceChangeSet.securityClassification} securityClassification the updated security classification value
*/ */
tagClassificationChanged( tagClassificationChanged(
this: DatasetCompliance,
tag: IComplianceChangeSet, tag: IComplianceChangeSet,
{ value: securityClassification = null }: { value: IComplianceChangeSet['securityClassification'] } { value: securityClassification = null }: { value: IComplianceChangeSet['securityClassification'] }
) { ) {
@ -955,7 +1014,7 @@ export default class DatasetCompliance extends Component {
* @param {IComplianceChangeSet} tag the field tag to be updated * @param {IComplianceChangeSet} tag the field tag to be updated
* @param {IComplianceChangeSet.nonOwner} nonOwner flag indicating the field property is a nonOwner * @param {IComplianceChangeSet.nonOwner} nonOwner flag indicating the field property is a nonOwner
*/ */
tagOwnerChanged(this: DatasetCompliance, tag: IComplianceChangeSet, nonOwner: IComplianceChangeSet['nonOwner']) { tagOwnerChanged(tag: IComplianceChangeSet, nonOwner: IComplianceChangeSet['nonOwner']) {
setProperties(tag, { setProperties(tag, {
nonOwner, nonOwner,
isDirty: true isDirty: true
@ -1011,7 +1070,10 @@ export default class DatasetCompliance extends Component {
* @returns {ShowAllShowReview} * @returns {ShowAllShowReview}
*/ */
onFieldReviewChange(this: DatasetCompliance, { value }: { value: ShowAllShowReview }): ShowAllShowReview { onFieldReviewChange(this: DatasetCompliance, { value }: { value: ShowAllShowReview }): ShowAllShowReview {
return set(this, 'fieldReviewOption', value); const option = set(this, 'fieldReviewOption', value);
get(this, 'foldChangeSetTask').perform();
return option;
}, },
/** /**

View File

@ -27,6 +27,7 @@ import {
filterEditableEntities, filterEditableEntities,
SuggestionIntent SuggestionIntent
} from 'wherehows-web/constants'; } from 'wherehows-web/constants';
import { iterateArrayAsync } from 'wherehows-web/utils/array';
/** /**
* Type alias for the response when container data items are batched * Type alias for the response when container data items are batched
@ -170,7 +171,7 @@ export default class DatasetComplianceContainer extends Component {
readDatasetComplianceSuggestionByUrn(urn), readDatasetComplianceSuggestionByUrn(urn),
readDatasetSchemaByUrn(urn) readDatasetSchemaByUrn(urn)
]); ]);
const schemaFieldNamesMappedToDataTypes = columnDataTypesAndFieldNames(columns); const schemaFieldNamesMappedToDataTypes = await iterateArrayAsync(columnDataTypesAndFieldNames)(columns);
this.onCompliancePolicyStateChange.call(this, { isNewComplianceInfo, fromUpstream: !!complianceInfo.fromUpstream }); this.onCompliancePolicyStateChange.call(this, { isNewComplianceInfo, fromUpstream: !!complianceInfo.fromUpstream });
@ -227,10 +228,12 @@ export default class DatasetComplianceContainer extends Component {
* Reads the schema properties for the dataset * Reads the schema properties for the dataset
* @type {Task<Promise<IDatasetSchema>, (a?: any) => TaskInstance<Promise<IDatasetSchema>>>} * @type {Task<Promise<IDatasetSchema>, (a?: any) => TaskInstance<Promise<IDatasetSchema>>>}
*/ */
getDatasetSchemaTask = task(function*(this: DatasetComplianceContainer): IterableIterator<Promise<IDatasetSchema>> { getDatasetSchemaTask = task(function*(
this: DatasetComplianceContainer
): IterableIterator<Promise<IDatasetSchema | Pick<IDatasetColumn, 'dataType' | 'fieldName'>[]>> {
try { try {
const { columns, schemaless }: IDatasetSchema = yield readDatasetSchemaByUrn(get(this, 'urn')); const { columns, schemaless }: IDatasetSchema = yield readDatasetSchemaByUrn(get(this, 'urn'));
const schemaFieldNamesMappedToDataTypes = columnDataTypesAndFieldNames(columns); const schemaFieldNamesMappedToDataTypes = yield iterateArrayAsync(columnDataTypesAndFieldNames)(columns);
setProperties(this, { schemaFieldNamesMappedToDataTypes, schemaless }); setProperties(this, { schemaFieldNamesMappedToDataTypes, schemaless });
} catch (e) { } catch (e) {
// If this schema is missing, silence exception, otherwise propagate // If this schema is missing, silence exception, otherwise propagate

View File

@ -1,7 +1,7 @@
import { PurgePolicy } from 'wherehows-web/constants/index'; import { PurgePolicy } from 'wherehows-web/constants/index';
import { IComplianceEntity, IComplianceInfo } from 'wherehows-web/typings/api/datasets/compliance'; import { IComplianceEntity, IComplianceInfo } from 'wherehows-web/typings/api/datasets/compliance';
import { IComplianceDataType } from 'wherehows-web/typings/api/list/compliance-datatypes'; import { IComplianceDataType } from 'wherehows-web/typings/api/list/compliance-datatypes';
import { arrayEvery, arrayFilter, arrayMap, arrayReduce, arraySome } from 'wherehows-web/utils/array'; import { arrayEvery, arrayFilter, arrayMap, arrayReduce, arraySome, reduceArrayAsync } from 'wherehows-web/utils/array';
import { fleece, hasEnumerableKeys } from 'wherehows-web/utils/object'; import { fleece, hasEnumerableKeys } from 'wherehows-web/utils/object';
import { lastSeenSuggestionInterval } from 'wherehows-web/constants/metadata-acquisition'; import { lastSeenSuggestionInterval } from 'wherehows-web/constants/metadata-acquisition';
import { decodeUrn } from 'wherehows-web/utils/validators/urn'; import { decodeUrn } from 'wherehows-web/utils/validators/urn';
@ -357,10 +357,12 @@ const foldComplianceChangeSetToField = (
* @param {Array<IComplianceChangeSet>} changeSet * @param {Array<IComplianceChangeSet>} changeSet
* @returns {Array<IdentifierFieldWithFieldChangeSetTuple>} * @returns {Array<IdentifierFieldWithFieldChangeSetTuple>}
*/ */
const foldComplianceChangeSets = ( const foldComplianceChangeSets = async (
changeSet: Array<IComplianceChangeSet> changeSet: Array<IComplianceChangeSet>
): Array<IdentifierFieldWithFieldChangeSetTuple> => ): Promise<Array<IdentifierFieldWithFieldChangeSetTuple>> =>
Object.entries<Array<IComplianceChangeSet>>(arrayReduce(foldComplianceChangeSetToField, {})(changeSet)); Object.entries<Array<IComplianceChangeSet>>(
await reduceArrayAsync(arrayReduce(foldComplianceChangeSetToField, {}))(changeSet)
);
/** /**
* Builds a default shape for securitySpecification & privacyCompliancePolicy with default / unset values * Builds a default shape for securitySpecification & privacyCompliancePolicy with default / unset values
@ -390,12 +392,14 @@ const createInitialComplianceInfo = (datasetId: string): IComplianceInfo => {
* } * }
* @returns {ISchemaFieldsToPolicy} * @returns {ISchemaFieldsToPolicy}
*/ */
const mapSchemaColumnPropsToCurrentPrivacyPolicy = ({ const asyncMapSchemaColumnPropsToCurrentPrivacyPolicy = ({
columnProps, columnProps,
complianceEntities, complianceEntities,
policyModificationTime policyModificationTime
}: ISchemaColumnMappingProps): ISchemaFieldsToPolicy => }: ISchemaColumnMappingProps): Promise<ISchemaFieldsToPolicy> =>
arrayReduce(schemaFieldsWithPolicyTagsReducingFn(complianceEntities, policyModificationTime), {})(columnProps); reduceArrayAsync(arrayReduce(schemaFieldsWithPolicyTagsReducingFn(complianceEntities, policyModificationTime), {}))(
columnProps
);
/** /**
* Creates a new tag / change set item for a compliance entity / field with default properties * Creates a new tag / change set item for a compliance entity / field with default properties
@ -420,7 +424,8 @@ const complianceFieldChangeSetItemFactory = ({
nonOwner: null, nonOwner: null,
readonly: false, readonly: false,
privacyPolicyExists: false, privacyPolicyExists: false,
isDirty: true isDirty: true,
valuePattern: void 0
}, },
suggestion ? { suggestion } : void 0, suggestion ? { suggestion } : void 0,
suggestionAuthority ? { suggestionAuthority } : void 0 suggestionAuthority ? { suggestionAuthority } : void 0
@ -490,7 +495,8 @@ const complianceFieldTagFactory = (identifierField: IComplianceEntity['identifie
logicalType: null, logicalType: null,
securityClassification: null, securityClassification: null,
nonOwner: null, nonOwner: null,
readonly: false readonly: false,
valuePattern: void 0
}); });
/** /**
@ -533,7 +539,7 @@ export {
idTypeFieldHasLogicalType, idTypeFieldHasLogicalType,
idTypeFieldsHaveLogicalType, idTypeFieldsHaveLogicalType,
changeSetReviewableAttributeTriggers, changeSetReviewableAttributeTriggers,
mapSchemaColumnPropsToCurrentPrivacyPolicy, asyncMapSchemaColumnPropsToCurrentPrivacyPolicy,
foldComplianceChangeSets, foldComplianceChangeSets,
complianceFieldChangeSetItemFactory, complianceFieldChangeSetItemFactory,
sortFoldedChangeSetTuples sortFoldedChangeSetTuples

View File

@ -25,7 +25,8 @@ enum IdLogicalType {
Numeric = 'NUMERIC', Numeric = 'NUMERIC',
Urn = 'URN', Urn = 'URN',
ReversedUrn = 'REVERSED_URN', ReversedUrn = 'REVERSED_URN',
CompositeUrn = 'COMPOSITE_URN' CompositeUrn = 'COMPOSITE_URN',
Custom = 'CUSTOM'
} }
/** /**

View File

@ -177,6 +177,7 @@
&#{&} { &#{&} {
td { td {
border-bottom: 0; border-bottom: 0;
height: item-spacing(8);
} }
} }
} }
@ -209,6 +210,43 @@
padding: item-spacing(1); padding: item-spacing(1);
} }
&__text-pattern-wrap {
$error-font-size: 12px;
$wrap-width: 280px;
width: $wrap-width;
margin-left: item-spacing(2);
position: relative;
display: flex;
flex-direction: column;
&--input {
&:before,
&:after {
content: '/';
color: get-color(blue5);
font-size: large;
font-weight: bold;
}
}
&--error {
font-size: $error-font-size;
position: absolute;
bottom: -18px;
color: get-color(red7);
}
}
&__text-pattern {
$input-width: 260px;
padding: (item-spacing(3) / 2) item-spacing(2);
white-space: pre;
font-family: monospace;
width: $input-width;
outline: none;
}
&__id-field-wrap { &__id-field-wrap {
display: flex; display: flex;
max-width: 100%; max-width: 100%;

View File

@ -26,9 +26,9 @@
bottom: 150%; bottom: 150%;
left: 50%; left: 50%;
margin-bottom: item-spacing(1); margin-bottom: item-spacing(1);
margin-left: -(item-spacing(8)); margin-left: -(item-spacing(7));
padding: item-spacing(2); padding: item-spacing(2);
width: item-spacing(8) * 2; width: item-spacing(7) * 2;
border-radius: item-spacing(1); border-radius: item-spacing(1);
background-color: get-color(black, 0.9); background-color: get-color(black, 0.9);
color: white; color: white;

View File

@ -2,8 +2,11 @@
rowId=elementId rowId=elementId
isIdType=isIdType isIdType=isIdType
fieldFormats=fieldFormats fieldFormats=fieldFormats
showCustomInput=showCustomInput
valuePatternError=valuePatternError
isTagFormatMissing=isTagFormatMissing isTagFormatMissing=isTagFormatMissing
fieldIdDropDownOptions=fieldIdDropDownOptions fieldIdDropDownOptions=fieldIdDropDownOptions
tagValuePatternDidChange=(action "tagValuePatternDidChange")
tagIdentifierTypeDidChange=(action "tagIdentifierTypeDidChange") tagIdentifierTypeDidChange=(action "tagIdentifierTypeDidChange")
tagLogicalTypeDidChange=(action "tagLogicalTypeDidChange") tagLogicalTypeDidChange=(action "tagLogicalTypeDidChange")
tagOwnerDidChange=(action "tagOwnerDidChange") tagOwnerDidChange=(action "tagOwnerDidChange")

View File

@ -81,7 +81,8 @@
onTagIdentifierTypeChange=(action "tagIdentifierChanged") onTagIdentifierTypeChange=(action "tagIdentifierChanged")
onSuggestionIntent=(action "onFieldSuggestionIntentChange") as |row| onSuggestionIntent=(action "onFieldSuggestionIntentChange") as |row|
}} }}
<tr class="{{if row.isReadonly 'dataset-compliance-fields--readonly'}}" ondblclick={{action row.onFragmentDblClick}}> <tr class="{{if row.isReadonly 'dataset-compliance-fields--readonly'}}" ondblclick={{action
row.onFragmentDblClick}}>
{{#row.cell}} {{#row.cell}}
{{#if row.isReadonly}} {{#if row.isReadonly}}
@ -94,7 +95,8 @@
(and row.suggestion (and (not row.suggestionMatchesCurrentValue) (not row.suggestionResolution)))}} (and row.suggestion (and (not row.suggestionMatchesCurrentValue) (not row.suggestionResolution)))}}
<span class="nacho-tooltip" title="Has suggestions"> <span class="nacho-tooltip" title="Has suggestions">
<i class="fa fa-exclamation dataset-compliance-fields__has-suggestions__icon" title="Compliance field has suggested values"></i> <i class="fa fa-exclamation dataset-compliance-fields__has-suggestions__icon"
title="Compliance field has suggested values"></i>
</span> </span>
{{else}} {{else}}
@ -102,7 +104,8 @@
{{#if row.isReviewRequested}} {{#if row.isReviewRequested}}
<span class="nacho-tooltip" title="Please review"> <span class="nacho-tooltip" title="Please review">
<i class="fa fa-question dataset-compliance-fields--review-required__icon" title="Compliance policy information needs review"></i> <i class="fa fa-question dataset-compliance-fields--review-required__icon"
title="Compliance policy information needs review"></i>
</span> </span>
{{else}} {{else}}
@ -237,6 +240,7 @@
fieldIdentifiers=row.taggedIdentifiers fieldIdentifiers=row.taggedIdentifiers
onTagIdentifierTypeChange=(action "tagIdentifierChanged") onTagIdentifierTypeChange=(action "tagIdentifierChanged")
onTagLogicalTypeChange=(action "tagLogicalTypeChanged") onTagLogicalTypeChange=(action "tagLogicalTypeChanged")
onTagValuePatternChange=(action "tagValuePatternChanged")
onTagOwnerChange=(action "tagOwnerChanged") onTagOwnerChange=(action "tagOwnerChanged")
complianceFieldIdDropdownOptions=complianceFieldIdDropdownOptions complianceFieldIdDropdownOptions=complianceFieldIdDropdownOptions
complianceDataTypes=complianceDataTypes as |tagRowComponent| complianceDataTypes=complianceDataTypes as |tagRowComponent|
@ -277,6 +281,30 @@
}} }}
</div> </div>
{{#if tagRowComponent.showCustomInput}}
<div class="dataset-compliance-fields__text-pattern-wrap">
{{#if (or (not isEditing) row.isReadonly)}}
{{tag.valuePattern}}
{{else}}
<div class="dataset-compliance-fields__text-pattern-wrap--input">
<input
placeholder="Enter regex"
value="{{readonly tag.valuePattern}}"
class="dataset-compliance-fields__text-pattern {{if tagRowComponent.valuePatternError
'dataset-compliance-fields--missing-selection'}}"
oninput={{action tagRowComponent.tagValuePatternDidChange value="target.value"}}
>
</div>
{{#if tagRowComponent.valuePatternError}}
<div class="dataset-compliance-fields__text-pattern-wrap--error">
{{tagRowComponent.valuePatternError}}
</div>
{{/if}}
{{/if}}
</div>
{{/if}}
{{#unless tagRowComponent.isTagFormatMissing}} {{#unless tagRowComponent.isTagFormatMissing}}
<span class="dataset-compliance-fields__tag-label"> <span class="dataset-compliance-fields__tag-label">
Owner: Owner:

View File

@ -31,6 +31,8 @@ export interface IComplianceEntity {
// Flag indicating that this compliance field is not editable by the end user // Flag indicating that this compliance field is not editable by the end user
// field should also be filtered from persisted policy // field should also be filtered from persisted policy
readonly readonly?: boolean; readonly readonly?: boolean;
//Optional attribute for the value of a CUSTOM regex. Required for CUSTOM field format
valuePattern?: string;
} }
/** /**

View File

@ -16,7 +16,7 @@ interface IColumnFieldProps {
} }
/** /**
* Defines the interface for properties passed into the mapping function mapSchemaColumnPropsToCurrentPrivacyPolicy * Defines the interface for properties passed into the mapping function asyncMapSchemaColumnPropsToCurrentPrivacyPolicy
* @interface ISchemaColumnMappingProps * @interface ISchemaColumnMappingProps
*/ */
interface ISchemaColumnMappingProps { interface ISchemaColumnMappingProps {

View File

@ -24,7 +24,13 @@ interface IDatasetComplianceActions {
*/ */
type IComplianceEntityWithMetadata = Pick< type IComplianceEntityWithMetadata = Pick<
IComplianceEntity, IComplianceEntity,
'identifierField' | 'identifierType' | 'logicalType' | 'securityClassification' | 'nonOwner' | 'readonly' | 'identifierField'
| 'identifierType'
| 'logicalType'
| 'securityClassification'
| 'nonOwner'
| 'readonly'
| 'valuePattern'
> & { > & {
// flag indicating that the field has a current policy upstream // flag indicating that the field has a current policy upstream
privacyPolicyExists: boolean; privacyPolicyExists: boolean;

View File

@ -1,8 +1,22 @@
declare module 'ember-concurrency' { declare module 'ember-concurrency' {
export function timeout(delay: number): Promise<void>;
import ComputedProperty from '@ember/object/computed'; import ComputedProperty from '@ember/object/computed';
import RSVP from 'rsvp'; import RSVP from 'rsvp';
type ComputedProperties<T> = { [K in keyof T]: ComputedProperty<T[K]> | T[K] };
export function timeout(delay: number): Promise<void>;
export function waitForProperty<T, K extends keyof T>(
object: ComputedProperties<T>,
key: K,
predicateCallback: (arg: T[K]) => boolean | T[K]
): IterableIterator<T[K]>;
export function waitForProperty<T, K extends keyof T>(
object: T,
key: K,
predicateCallback: (arg: T[K]) => boolean | T[K]
): IterableIterator<T[K]>;
export enum TaskInstanceState { export enum TaskInstanceState {
Dropped = 'dropped', Dropped = 'dropped',
Canceled = 'canceled', Canceled = 'canceled',

View File

@ -60,4 +60,84 @@ const isListUnique = <T>(list: Array<T> = []): boolean => new Set(list).size ===
*/ */
const compact = <T>(list: Array<T> = []): Array<T> => list.filter(item => item); const compact = <T>(list: Array<T> = []): Array<T> => list.filter(item => item);
export { arrayMap, arrayFilter, arrayReduce, isListUnique, compact, arrayEvery, arraySome }; /**
* Defines the interface for options that may be passed into the chunk function
* @interface {IChunkArrayOptions}
*/
interface IChunkArrayOptions {
chunkSize?: 50 | 100;
context?: null | object;
}
/**
* Asynchronously traverses a list in small chunks ensuring that a list can be iterated over without
* blocking the browser main thread.
* @template T type of values in list to be iterated over
* @template U the type of the value that is produced by an iteration of the list
* @param {(arr?: Array<T>) => U} iterateeSync an iteratee that consumes an list and returns a value of type U
* @param {(res: U) => U} accumulator a function that combines the result of successive iterations of the original list
* @param {IChunkArrayOptions} [{chunkSize = 50, context = null}={chunkSize: 50, context: null}]
* @param {50 | 100} chunkSize the maximum size to chunk at a time
* @param {object | null} [context] the optional execution context for the iteratee invocation
* @return {(list: Array<T>) => Promise<U>}
*/
const chunkArrayAsync = <T, U>(
iterateeSync: (arr?: Array<T>) => U,
accumulator: (res: U) => U,
{ chunkSize = 50, context = null }: IChunkArrayOptions = { chunkSize: 50, context: null }
) => (list: Array<T>) =>
new Promise<U>(function(resolve) {
const queue = list.slice(0); // creates a shallow copy of the original list
const delay = 25;
let result: U;
setTimeout(function chunk() {
const startTime = +new Date();
do {
result = accumulator(iterateeSync.call(context, queue.splice(0, chunkSize)));
} while (queue.length && +new Date() + startTime < 50);
// recurse through list if there are more items left
return queue.length ? setTimeout(chunk, delay) : resolve(result);
}, delay);
});
/**
* Asynchronously traverse a list and accumulate another list based on the iteratee
* @template T the type of values in the original list
* @template U the type of values in the transformed list
* @param {(arr?: Array<T>) => Array<U>} iteratee consumes a list and returns a new list of values
* @return {(list: Array<T>, context?: any) => Promise<Array<U>>}
*/
const iterateArrayAsync = <T, U = T>(iteratee: (arr?: Array<T>) => Array<U>) => (
list: Array<T>,
context = null
): Promise<Array<U>> => {
const accumulator = (base: Array<U>) => (arr: Array<U>) => [...base, ...arr];
return chunkArrayAsync(iteratee, accumulator([]), { chunkSize: 50, context })(list);
};
/**
* Asynchronously traverse a list and accumulate a value of type U, applies to cases of reduction or accumulation
* @template T the type of values in the original list
* @template U the type of value to be produced by reducing the list
* @param {(arr?: Array<T>) => U} reducer consumes a list and produces a single value
* @return {(list: Array<T>, context?: any) => Promise<U>}
*/
const reduceArrayAsync = <T, U>(reducer: (arr?: Array<T>) => U) => (list: Array<T>, context = null): Promise<U> => {
const accumulator = (base: U) => (int: U) => Object.assign(base, int);
return chunkArrayAsync(reducer, accumulator(reducer.call(context)))(list);
};
export {
arrayMap,
arrayFilter,
arrayReduce,
isListUnique,
compact,
arrayEvery,
arraySome,
iterateArrayAsync,
reduceArrayAsync
};

View File

@ -1,3 +1,9 @@
/**
* Constant value for an empty regex source string
* @type {string}
*/
const emptyRegexSource = '(?:)';
/** /**
* Sanitizes a string to be used in creating a runtime regular expression pattern by escaping special characters * Sanitizes a string to be used in creating a runtime regular expression pattern by escaping special characters
* @param {string} pattern the string intended to be used to new a RegExp object * @param {string} pattern the string intended to be used to new a RegExp object
@ -15,4 +21,4 @@ const buildSaneRegExp = (pattern: string, flags?: string): RegExp => new RegExp(
export default buildSaneRegExp; export default buildSaneRegExp;
export { sanitizeRegExp, buildSaneRegExp }; export { sanitizeRegExp, buildSaneRegExp, emptyRegexSource };

View File

@ -41,6 +41,13 @@ const isWhUrn = (candidateUrn: string): boolean => datasetUrnRegexWH.test(String
*/ */
const isLiUrn = (candidateUrn: string): boolean => datasetUrnRegexLI.test(String(candidateUrn)); const isLiUrn = (candidateUrn: string): boolean => datasetUrnRegexLI.test(String(candidateUrn));
/**
* Checks that a string matches the expected valuePatternRegex
* @param {string} candidate the supplied pattern string
* @return {boolean}
*/
const isValidCustomValuePattern = (candidate: string): boolean => !!candidate; // TODO:
/** /**
* Asserts that a provided string matches the urn pattern above * Asserts that a provided string matches the urn pattern above
* @param {string} candidateUrn the string to test on * @param {string} candidateUrn the string to test on
@ -165,6 +172,7 @@ export default isUrn;
export { export {
datasetUrnRegexWH, datasetUrnRegexWH,
datasetUrnRegexLI, datasetUrnRegexLI,
isValidCustomValuePattern,
isWhUrn, isWhUrn,
isLiUrn, isLiUrn,
buildLiUrn, buildLiUrn,

View File

@ -55,7 +55,7 @@
"ember-cli-typescript": "^1.0.6", "ember-cli-typescript": "^1.0.6",
"ember-cli-uglify": "^2.0.0", "ember-cli-uglify": "^2.0.0",
"ember-composable-helpers": "^2.1.0", "ember-composable-helpers": "^2.1.0",
"ember-concurrency": "^0.8.12", "ember-concurrency": "^0.8.15",
"ember-decorators": "^1.3.4", "ember-decorators": "^1.3.4",
"ember-export-application-global": "^2.0.0", "ember-export-application-global": "^2.0.0",
"ember-fetch": "^3.4.4", "ember-fetch": "^3.4.4",
@ -99,7 +99,7 @@
"ember-lodash": "4.17.2", "ember-lodash": "4.17.2",
"ember-modal-dialog": "^2.4.1", "ember-modal-dialog": "^2.4.1",
"ember-moment": "^7.5.0", "ember-moment": "^7.5.0",
"ember-power-select": "^1.9.5", "ember-power-select": "^1.10.4",
"ember-radio-button": "^1.2.1", "ember-radio-button": "^1.2.1",
"ember-redux": "^2.10.1", "ember-redux": "^2.10.1",
"ember-redux-actions": "^0.3.0", "ember-redux-actions": "^0.3.0",

View File

@ -635,8 +635,8 @@ babel-core@^5.0.0:
try-resolve "^1.0.0" try-resolve "^1.0.0"
babel-core@^6.14.0, babel-core@^6.24.1, babel-core@^6.26.0: babel-core@^6.14.0, babel-core@^6.24.1, babel-core@^6.26.0:
version "6.26.0" version "6.26.3"
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.0.tgz#af32f78b31a6fcef119c87b0fd8d9753f03a0bb8" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207"
dependencies: dependencies:
babel-code-frame "^6.26.0" babel-code-frame "^6.26.0"
babel-generator "^6.26.0" babel-generator "^6.26.0"
@ -648,15 +648,15 @@ babel-core@^6.14.0, babel-core@^6.24.1, babel-core@^6.26.0:
babel-traverse "^6.26.0" babel-traverse "^6.26.0"
babel-types "^6.26.0" babel-types "^6.26.0"
babylon "^6.18.0" babylon "^6.18.0"
convert-source-map "^1.5.0" convert-source-map "^1.5.1"
debug "^2.6.8" debug "^2.6.9"
json5 "^0.5.1" json5 "^0.5.1"
lodash "^4.17.4" lodash "^4.17.4"
minimatch "^3.0.4" minimatch "^3.0.4"
path-is-absolute "^1.0.1" path-is-absolute "^1.0.1"
private "^0.1.7" private "^0.1.8"
slash "^1.0.0" slash "^1.0.0"
source-map "^0.5.6" source-map "^0.5.7"
babel-eslint@^8.0.1: babel-eslint@^8.0.1:
version "8.2.1" version "8.2.1"
@ -670,8 +670,8 @@ babel-eslint@^8.0.1:
eslint-visitor-keys "^1.0.0" eslint-visitor-keys "^1.0.0"
babel-generator@^6.26.0: babel-generator@^6.26.0:
version "6.26.0" version "6.26.1"
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5" resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90"
dependencies: dependencies:
babel-messages "^6.23.0" babel-messages "^6.23.0"
babel-runtime "^6.26.0" babel-runtime "^6.26.0"
@ -679,7 +679,7 @@ babel-generator@^6.26.0:
detect-indent "^4.0.0" detect-indent "^4.0.0"
jsesc "^1.3.0" jsesc "^1.3.0"
lodash "^4.17.4" lodash "^4.17.4"
source-map "^0.5.6" source-map "^0.5.7"
trim-right "^1.0.1" trim-right "^1.0.1"
babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: babel-helper-builder-binary-assignment-operator-visitor@^6.24.1:
@ -1006,8 +1006,8 @@ babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015
babel-template "^6.24.1" babel-template "^6.24.1"
babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1: babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1:
version "6.26.0" version "6.26.2"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz#0d8394029b7dc6abe1a97ef181e00758dd2e5d8a" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3"
dependencies: dependencies:
babel-plugin-transform-strict-mode "^6.24.1" babel-plugin-transform-strict-mode "^6.24.1"
babel-runtime "^6.26.0" babel-runtime "^6.26.0"
@ -1295,8 +1295,8 @@ binary-extensions@^1.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205"
"binaryextensions@1 || 2": "binaryextensions@1 || 2":
version "2.0.0" version "2.1.1"
resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.0.0.tgz#e597d1a7a6a3558a2d1c7241a16c99965e6aa40f" resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.1.1.tgz#3209a51ca4a4ad541a3b8d3d6a6d5b83a2485935"
bindings@~1.2.1: bindings@~1.2.1:
version "1.2.1" version "1.2.1"
@ -1406,13 +1406,20 @@ boxen@^1.0.0:
term-size "^1.2.0" term-size "^1.2.0"
widest-line "^2.0.0" widest-line "^2.0.0"
brace-expansion@^1.0.0, brace-expansion@^1.1.7: brace-expansion@^1.0.0:
version "1.1.8" version "1.1.8"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
dependencies: dependencies:
balanced-match "^1.0.0" balanced-match "^1.0.0"
concat-map "0.0.1" concat-map "0.0.1"
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
braces@^1.8.2: braces@^1.8.2:
version "1.8.5" version "1.8.5"
resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
@ -1472,7 +1479,7 @@ broccoli-babel-transpiler@^5.6.2:
rsvp "^3.5.0" rsvp "^3.5.0"
workerpool "^2.2.1" workerpool "^2.2.1"
broccoli-babel-transpiler@^6.0.0, broccoli-babel-transpiler@^6.1.2: broccoli-babel-transpiler@^6.0.0:
version "6.1.2" version "6.1.2"
resolved "https://registry.yarnpkg.com/broccoli-babel-transpiler/-/broccoli-babel-transpiler-6.1.2.tgz#26019c045b5ea3e44cfef62821302f9bd483cabd" resolved "https://registry.yarnpkg.com/broccoli-babel-transpiler/-/broccoli-babel-transpiler-6.1.2.tgz#26019c045b5ea3e44cfef62821302f9bd483cabd"
dependencies: dependencies:
@ -1487,6 +1494,21 @@ broccoli-babel-transpiler@^6.0.0, broccoli-babel-transpiler@^6.1.2:
rsvp "^3.5.0" rsvp "^3.5.0"
workerpool "^2.2.1" workerpool "^2.2.1"
broccoli-babel-transpiler@^6.1.2:
version "6.1.4"
resolved "https://registry.yarnpkg.com/broccoli-babel-transpiler/-/broccoli-babel-transpiler-6.1.4.tgz#8be8074c42abf2e17ff79b2d2a21df5c51143c82"
dependencies:
babel-core "^6.14.0"
broccoli-funnel "^1.0.0"
broccoli-merge-trees "^1.0.0"
broccoli-persistent-filter "^1.4.0"
clone "^2.0.0"
hash-for-dep "^1.0.2"
heimdalljs-logger "^0.1.7"
json-stable-stringify "^1.0.0"
rsvp "^3.5.0"
workerpool "^2.3.0"
broccoli-brocfile-loader@^0.18.0: broccoli-brocfile-loader@^0.18.0:
version "0.18.0" version "0.18.0"
resolved "https://registry.yarnpkg.com/broccoli-brocfile-loader/-/broccoli-brocfile-loader-0.18.0.tgz#2e86021c805c34ffc8d29a2fb721cf273e819e4b" resolved "https://registry.yarnpkg.com/broccoli-brocfile-loader/-/broccoli-brocfile-loader-0.18.0.tgz#2e86021c805c34ffc8d29a2fb721cf273e819e4b"
@ -1996,8 +2018,8 @@ can-symlink@^1.0.0:
tmp "0.0.28" tmp "0.0.28"
caniuse-lite@^1.0.30000792: caniuse-lite@^1.0.30000792:
version "1.0.30000792" version "1.0.30000832"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000792.tgz#d0cea981f8118f3961471afbb43c9a1e5bbf0332" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000832.tgz#22a277f1d623774cc9aea2f7c1a65cb1603c63b8"
capture-exit@^1.1.0: capture-exit@^1.1.0:
version "1.2.0" version "1.2.0"
@ -2417,7 +2439,7 @@ continuable-cache@^0.3.1:
version "0.3.1" version "0.3.1"
resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f" resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f"
convert-source-map@^1.1.0, convert-source-map@^1.5.0: convert-source-map@^1.1.0, convert-source-map@^1.5.1:
version "1.5.1" version "1.5.1"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
@ -2442,8 +2464,8 @@ core-js@^1.0.0:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
core-js@^2.4.0, core-js@^2.5.0: core-js@^2.4.0, core-js@^2.5.0:
version "2.5.3" version "2.5.5"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.5.tgz#b14dde936c640c0579a6b50cabcc132dd6127e3b"
core-object@^1.1.0: core-object@^1.1.0:
version "1.1.0" version "1.1.0"
@ -2770,8 +2792,8 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0" jsbn "~0.1.0"
editions@^1.1.1: editions@^1.1.1:
version "1.3.3" version "1.3.4"
resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.3.tgz#0907101bdda20fac3cbe334c27cbd0688dc99a5b" resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.4.tgz#3662cb592347c3168eb8e498a0ff73271d67f50b"
editor@~1.0.0: editor@~1.0.0:
version "1.0.0" version "1.0.0"
@ -2782,8 +2804,8 @@ ee-first@1.1.1:
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
electron-to-chromium@^1.3.30: electron-to-chromium@^1.3.30:
version "1.3.31" version "1.3.44"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.31.tgz#00d832cba9fe2358652b0c48a8816c8e3a037e9f" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.44.tgz#ef6b150a60d523082388cadad88085ecd2fd4684"
elegant-spinner@^1.0.1: elegant-spinner@^1.0.1:
version "1.0.1" version "1.0.1"
@ -2841,7 +2863,7 @@ ember-cli-babel@^5.1.5, ember-cli-babel@^5.1.6, ember-cli-babel@^5.1.7, ember-cl
ember-cli-version-checker "^1.0.2" ember-cli-version-checker "^1.0.2"
resolve "^1.1.2" resolve "^1.1.2"
ember-cli-babel@^6.0.0, ember-cli-babel@^6.0.0-beta.4, ember-cli-babel@^6.0.0-beta.7, ember-cli-babel@^6.1.0, ember-cli-babel@^6.10.0, ember-cli-babel@^6.11.0, ember-cli-babel@^6.3.0, ember-cli-babel@^6.6.0, ember-cli-babel@^6.7.2, ember-cli-babel@^6.8.0, ember-cli-babel@^6.8.1, ember-cli-babel@^6.8.2, ember-cli-babel@^6.9.0, ember-cli-babel@^6.9.2: ember-cli-babel@^6.0.0, ember-cli-babel@^6.0.0-beta.7, ember-cli-babel@^6.1.0, ember-cli-babel@^6.10.0, ember-cli-babel@^6.3.0, ember-cli-babel@^6.6.0, ember-cli-babel@^6.7.2, ember-cli-babel@^6.8.0, ember-cli-babel@^6.8.1, ember-cli-babel@^6.9.0, ember-cli-babel@^6.9.2:
version "6.11.0" version "6.11.0"
resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-6.11.0.tgz#79cb184bac3c05bfe181ddc306bac100ab1f9493" resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-6.11.0.tgz#79cb184bac3c05bfe181ddc306bac100ab1f9493"
dependencies: dependencies:
@ -2859,7 +2881,7 @@ ember-cli-babel@^6.0.0, ember-cli-babel@^6.0.0-beta.4, ember-cli-babel@^6.0.0-be
ember-cli-version-checker "^2.1.0" ember-cli-version-checker "^2.1.0"
semver "^5.4.1" semver "^5.4.1"
ember-cli-babel@^6.0.0-beta.9: ember-cli-babel@^6.0.0-beta.4, ember-cli-babel@^6.0.0-beta.9, ember-cli-babel@^6.11.0, ember-cli-babel@^6.8.2:
version "6.12.0" version "6.12.0"
resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-6.12.0.tgz#3adcdbe1278da1fcd0b9038f1360cb4ac5d4414c" resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-6.12.0.tgz#3adcdbe1278da1fcd0b9038f1360cb4ac5d4414c"
dependencies: dependencies:
@ -3213,13 +3235,20 @@ ember-cli-version-checker@^1.0.2, ember-cli-version-checker@^1.2.0:
dependencies: dependencies:
semver "^5.3.0" semver "^5.3.0"
ember-cli-version-checker@^2.0.0, ember-cli-version-checker@^2.1.0: ember-cli-version-checker@^2.0.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/ember-cli-version-checker/-/ember-cli-version-checker-2.1.0.tgz#fc79a56032f3717cf844ada7cbdec1a06fedb604" resolved "https://registry.yarnpkg.com/ember-cli-version-checker/-/ember-cli-version-checker-2.1.0.tgz#fc79a56032f3717cf844ada7cbdec1a06fedb604"
dependencies: dependencies:
resolve "^1.3.3" resolve "^1.3.3"
semver "^5.3.0" semver "^5.3.0"
ember-cli-version-checker@^2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ember-cli-version-checker/-/ember-cli-version-checker-2.1.2.tgz#305ce102390c66e4e0f1432dea9dc5c7c19fed98"
dependencies:
resolve "^1.3.3"
semver "^5.3.0"
ember-cli@~2.18.0: ember-cli@~2.18.0:
version "2.18.0" version "2.18.0"
resolved "https://registry.yarnpkg.com/ember-cli/-/ember-cli-2.18.0.tgz#75c7cf7be8d195ae2eb072489e6b7243c95f63d4" resolved "https://registry.yarnpkg.com/ember-cli/-/ember-cli-2.18.0.tgz#75c7cf7be8d195ae2eb072489e6b7243c95f63d4"
@ -3324,13 +3353,12 @@ ember-composable-helpers@^2.1.0:
broccoli-funnel "^1.0.1" broccoli-funnel "^1.0.1"
ember-cli-babel "^6.6.0" ember-cli-babel "^6.6.0"
ember-concurrency@^0.8.12: ember-concurrency@^0.8.12, ember-concurrency@^0.8.15:
version "0.8.12" version "0.8.18"
resolved "https://registry.yarnpkg.com/ember-concurrency/-/ember-concurrency-0.8.12.tgz#fb91180e5efeb1024cfa2cfb99d2fe6721930c91" resolved "https://registry.yarnpkg.com/ember-concurrency/-/ember-concurrency-0.8.18.tgz#20a9ac4ced6496ea4ebe52e88f4524a473871396"
dependencies: dependencies:
babel-core "^6.24.1" babel-core "^6.24.1"
ember-cli-babel "^6.8.2" ember-cli-babel "^6.8.2"
ember-getowner-polyfill "^2.0.0"
ember-maybe-import-regenerator "^0.1.5" ember-maybe-import-regenerator "^0.1.5"
ember-cookies@^0.1.0: ember-cookies@^0.1.0:
@ -3535,8 +3563,8 @@ ember-pikaday@^2.2.4:
pikaday "^1.5.1" pikaday "^1.5.1"
ember-power-calendar@^0.7.1: ember-power-calendar@^0.7.1:
version "0.7.1" version "0.7.2"
resolved "https://registry.yarnpkg.com/ember-power-calendar/-/ember-power-calendar-0.7.1.tgz#7dcf1e4b7b8f54c3ccae2f6179002d3494f027ef" resolved "https://registry.yarnpkg.com/ember-power-calendar/-/ember-power-calendar-0.7.2.tgz#08a25fc41a95dc4466a7c2a2cbca6fe91cec3051"
dependencies: dependencies:
ember-assign-helper "0.1.2" ember-assign-helper "0.1.2"
ember-cli-babel "^6.11.0" ember-cli-babel "^6.11.0"
@ -3546,7 +3574,7 @@ ember-power-calendar@^0.7.1:
ember-moment "^7.5.0" ember-moment "^7.5.0"
ember-native-dom-helpers "^0.5.10" ember-native-dom-helpers "^0.5.10"
ember-power-select@^1.9.5: ember-power-select@^1.10.4:
version "1.10.4" version "1.10.4"
resolved "https://registry.yarnpkg.com/ember-power-select/-/ember-power-select-1.10.4.tgz#7f0bb8c55279375391f2d97591ed65f727061579" resolved "https://registry.yarnpkg.com/ember-power-select/-/ember-power-select-1.10.4.tgz#7f0bb8c55279375391f2d97591ed65f727061579"
dependencies: dependencies:
@ -3623,7 +3651,11 @@ ember-rfc176-data@^0.2.7:
version "0.2.7" version "0.2.7"
resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.2.7.tgz#bd355bc9b473e08096b518784170a23388bc973b" resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.2.7.tgz#bd355bc9b473e08096b518784170a23388bc973b"
ember-rfc176-data@^0.3.0, ember-rfc176-data@^0.3.1: ember-rfc176-data@^0.3.0:
version "0.3.2"
resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.3.2.tgz#bde5538939529b263c142b53a47402f8127f8dce"
ember-rfc176-data@^0.3.1:
version "0.3.1" version "0.3.1"
resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.3.1.tgz#6a5a4b8b82ec3af34f3010965fa96b936ca94519" resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.3.1.tgz#6a5a4b8b82ec3af34f3010965fa96b936ca94519"
@ -5258,12 +5290,18 @@ interpret@^1.0.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614"
invariant@^2.2.0, invariant@^2.2.1, invariant@^2.2.2: invariant@^2.2.0, invariant@^2.2.1:
version "2.2.2" version "2.2.2"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
dependencies: dependencies:
loose-envify "^1.0.0" loose-envify "^1.0.0"
invariant@^2.2.2:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
dependencies:
loose-envify "^1.0.0"
invert-kv@^1.0.0: invert-kv@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
@ -6387,10 +6425,14 @@ lodash@^3.10.0, lodash@^3.10.1, lodash@^3.9.3:
version "3.10.1" version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1, lodash@^4.6.1, lodash@^4.8.0, lodash@~4.17.4: lodash@^4.0.0, lodash@^4.13.1, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1, lodash@^4.6.1, lodash@^4.8.0, lodash@~4.17.4:
version "4.17.4" version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
lodash@^4.14.0, lodash@^4.17.4:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
log-symbols@^1.0.2: log-symbols@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
@ -7532,7 +7574,7 @@ printf@^0.2.3:
version "0.2.5" version "0.2.5"
resolved "https://registry.yarnpkg.com/printf/-/printf-0.2.5.tgz#c438ca2ca33e3927671db4ab69c0e52f936a4f0f" resolved "https://registry.yarnpkg.com/printf/-/printf-0.2.5.tgz#c438ca2ca33e3927671db4ab69c0e52f936a4f0f"
private@^0.1.6, private@^0.1.7, private@~0.1.5: private@^0.1.6, private@^0.1.8, private@~0.1.5:
version "0.1.8" version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
@ -8140,12 +8182,18 @@ resolve@1.1.x:
version "1.1.7" version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
resolve@1.5.0, resolve@^1.1.2, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.3.0, resolve@^1.3.3, resolve@^1.4.0, resolve@^1.5.0: resolve@1.5.0, resolve@^1.1.2, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.3.0, resolve@^1.5.0:
version "1.5.0" version "1.5.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36"
dependencies: dependencies:
path-parse "^1.0.5" path-parse "^1.0.5"
resolve@^1.3.3, resolve@^1.4.0:
version "1.7.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.7.1.tgz#aadd656374fd298aee895bc026b8297418677fd3"
dependencies:
path-parse "^1.0.5"
restore-cursor@^1.0.1: restore-cursor@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"
@ -8614,7 +8662,7 @@ source-map@0.5.6:
version "0.5.6" version "0.5.6"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
source-map@^0.5.0, source-map@^0.5.6, source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.6: source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.6:
version "0.5.7" version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
@ -8905,8 +8953,8 @@ symbol-observable@^1.0.3, symbol-observable@^1.0.4:
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.1.0.tgz#5c68fd8d54115d9dfb72a84720549222e8db9b32" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.1.0.tgz#5c68fd8d54115d9dfb72a84720549222e8db9b32"
symlink-or-copy@^1.0.0, symlink-or-copy@^1.0.1, symlink-or-copy@^1.1.8: symlink-or-copy@^1.0.0, symlink-or-copy@^1.0.1, symlink-or-copy@^1.1.8:
version "1.1.8" version "1.2.0"
resolved "https://registry.yarnpkg.com/symlink-or-copy/-/symlink-or-copy-1.1.8.tgz#cabe61e0010c1c023c173b25ee5108b37f4b4aa3" resolved "https://registry.yarnpkg.com/symlink-or-copy/-/symlink-or-copy-1.2.0.tgz#5d49108e2ab824a34069b68974486c290020b393"
sync-disk-cache@^1.3.2: sync-disk-cache@^1.3.2:
version "1.3.2" version "1.3.2"
@ -9025,8 +9073,8 @@ text-table@~0.2.0:
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
"textextensions@1 || 2": "textextensions@1 || 2":
version "2.1.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.1.0.tgz#1be0dc2a0dc244d44be8a09af6a85afb93c4dbc3" resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.2.0.tgz#38ac676151285b658654581987a0ce1a4490d286"
through2@^2.0.0: through2@^2.0.0:
version "2.0.3" version "2.0.3"
@ -9514,7 +9562,7 @@ wordwrap@~0.0.2:
version "0.0.3" version "0.0.3"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
workerpool@^2.2.1: workerpool@^2.2.1, workerpool@^2.3.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-2.3.0.tgz#86c5cbe946b55e7dc9d12b1936c8801a6e2d744d" resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-2.3.0.tgz#86c5cbe946b55e7dc9d12b1936c8801a6e2d744d"
dependencies: dependencies: