diff --git a/.eslintrc.js b/.eslintrc.js index 3cb5fd6ea66..3771c46ac3d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,6 +5,7 @@ module.exports = { ecmaVersion: 6, sourceType: 'module', }, + parser: 'babel-eslint', extends: 'eslint:recommended', env: { 'browser': true, @@ -41,7 +42,7 @@ module.exports = { 'curly': ['error', 'all'], 'eol-last': 'error', 'no-trailing-spaces': 'error', - 'comma-dangle': ['error', 'never'], + 'comma-dangle': ['off', 'never'], 'space-before-blocks': ['error', 'always'], 'indent': ['error', 2, { 'SwitchCase': 1, diff --git a/.gitignore b/.gitignore index 3fa6bc6e064..1912672e155 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ tmp/ tests/source/ dist/ *.iml +yarn-error.log benchmarks/results/*.json diff --git a/.travis.yml b/.travis.yml index d9a6b400138..df0bb6adf1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,8 +56,8 @@ jobs: # runs tests against each supported Ember version - stage: older version tests - env: EMBER_TRY_SCENARIO=ember-lts-2.12 - - env: EMBER_TRY_SCENARIO=ember-lts-2.16 + env: EMBER_TRY_SCENARIO=ember-lts-2.16 + - env: EMBER_TRY_SCENARIO=ember-lts-2.18 - env: EMBER_TRY_SCENARIO=ember-release - env: EMBER_TRY_SCENARIO=ember-beta - env: EMBER_TRY_SCENARIO=ember-canary diff --git a/addon/-private/system/many-array.js b/addon/-private/system/many-array.js index f2b8a38ebf6..46b18aa7ef2 100644 --- a/addon/-private/system/many-array.js +++ b/addon/-private/system/many-array.js @@ -63,7 +63,7 @@ export default EmberObject.extend(MutableArray, Evented, { @property {Boolean} isLoaded */ - this.isLoaded = false; + this.isLoaded = this.isLoaded || false; this.length = 0; /** diff --git a/addon/-private/system/model/internal-model.js b/addon/-private/system/model/internal-model.js index 936a6385402..bbf0662cb6f 100644 --- a/addon/-private/system/model/internal-model.js +++ b/addon/-private/system/model/internal-model.js @@ -137,7 +137,7 @@ export default class InternalModel { this.store = store; this.modelName = modelName; - this._loadingPromise = null; + this._promiseProxy = null; this._record = null; this._isDestroyed = false; this.isError = false; @@ -538,11 +538,11 @@ export default class InternalModel { this.dematerializeRecord(); if (this._scheduledDestroy === null) { - // TODO: use run.schedule once we drop 1.13 - if (!run.currentRunLoop) { - assert('You have turned on testing mode, which disabled the run-loop\'s autorun.\n You will need to wrap any code with asynchronous side-effects in a run', Ember.testing); - } - this._scheduledDestroy = run.backburner.schedule('destroy', this, '_checkForOrphanedInternalModels') + this._scheduledDestroy = run.backburner.schedule( + 'destroy', + this, + '_checkForOrphanedInternalModels' + ); } } diff --git a/addon/-private/system/model/states.js b/addon/-private/system/model/states.js index 84085f14fe4..48ccdcf7543 100644 --- a/addon/-private/system/model/states.js +++ b/addon/-private/system/model/states.js @@ -495,7 +495,7 @@ const RootState = { // EVENTS loadingData(internalModel, promise) { - internalModel._loadingPromise = promise; + internalModel._promiseProxy = promise; internalModel.transitionTo('loading'); }, @@ -522,7 +522,7 @@ const RootState = { isLoading: true, exit(internalModel) { - internalModel._loadingPromise = null; + internalModel._promiseProxy = null; }, // EVENTS diff --git a/addon/-private/system/record-array-manager.js b/addon/-private/system/record-array-manager.js index 71b686dbee2..1327cb5d3a5 100644 --- a/addon/-private/system/record-array-manager.js +++ b/addon/-private/system/record-array-manager.js @@ -4,7 +4,7 @@ import { A } from '@ember/array'; import { set, get } from '@ember/object'; -import { run as emberRun } from '@ember/runloop'; +import { run as emberRunloop } from '@ember/runloop'; import { assert } from '@ember/debug'; import cloneNull from './clone-null'; import { @@ -12,6 +12,8 @@ import { AdapterPopulatedRecordArray } from './record-arrays'; +const emberRun = emberRunloop.backburner; + const { _flush, array_remove, diff --git a/addon/-private/system/references/belongs-to.js b/addon/-private/system/references/belongs-to.js index 20a285bbb44..710ad589985 100644 --- a/addon/-private/system/references/belongs-to.js +++ b/addon/-private/system/references/belongs-to.js @@ -346,15 +346,17 @@ export default class BelongsToReference extends Reference { @return {Promise} a promise that resolves with the record in this belongs-to relationship. */ load() { - if (this.remoteType() === "id") { - return this.belongsToRelationship.getRecord(); - } + let rel = this.belongsToRelationship; + + rel.getData(); - if (this.remoteType() === "link") { - return this.belongsToRelationship.findLink().then((internalModel) => { + if (rel.fetchPromise !== null) { + return rel.fetchPromise.then(() => { return this.value(); }); } + + return resolve(this.value()); } /** diff --git a/addon/-private/system/references/has-many.js b/addon/-private/system/references/has-many.js index 5e88af85754..1606a06819c 100644 --- a/addon/-private/system/references/has-many.js +++ b/addon/-private/system/references/has-many.js @@ -354,8 +354,9 @@ export default class HasManyReference extends Reference { this has-many relationship. */ load() { + // TODO this can be simplified if (!this._isLoaded()) { - return this.hasManyRelationship.getRecords(); + return this.hasManyRelationship.getData(); } return resolve(this.hasManyRelationship.manyArray); diff --git a/addon/-private/system/relationships/belongs-to.js b/addon/-private/system/relationships/belongs-to.js index 8256a5737ee..421d7132f96 100644 --- a/addon/-private/system/relationships/belongs-to.js +++ b/addon/-private/system/relationships/belongs-to.js @@ -115,12 +115,12 @@ export default function belongsTo(modelName, options) { }); } - return this._internalModel._relationships.get(key).getRecord(); + return this._internalModel._relationships.get(key).getData(); }, set(key, value) { this._internalModel.setDirtyBelongsTo(key, value); - return this._internalModel._relationships.get(key).getRecord(); - } + return this._internalModel._relationships.get(key).getData(); + }, }).meta(meta); } diff --git a/addon/-private/system/relationships/has-many.js b/addon/-private/system/relationships/has-many.js index 6a575466a41..aee415ece85 100644 --- a/addon/-private/system/relationships/has-many.js +++ b/addon/-private/system/relationships/has-many.js @@ -140,12 +140,12 @@ export default function hasMany(type, options) { return computed({ get(key) { - return this._internalModel._relationships.get(key).getRecords(); + return this._internalModel._relationships.get(key).getData(); }, set(key, records) { this._internalModel.setDirtyHasMany(key, records); - return this._internalModel._relationships.get(key).getRecords(); - } + return this._internalModel._relationships.get(key).getData(); + }, }).meta(meta); } diff --git a/addon/-private/system/relationships/state/belongs-to.js b/addon/-private/system/relationships/state/belongs-to.js index c1d813fe829..0e64ae6a1e5 100644 --- a/addon/-private/system/relationships/state/belongs-to.js +++ b/addon/-private/system/relationships/state/belongs-to.js @@ -1,17 +1,31 @@ -import { Promise as EmberPromise } from 'rsvp'; +import { resolve } from 'rsvp'; import { assert, inspect } from '@ember/debug'; import { assertPolymorphicType } from 'ember-data/-debug'; -import { - PromiseBelongsTo -} from "../../promise-proxies"; -import Relationship from "./relationship"; +import { PromiseBelongsTo, PromiseObject } from '../../promise-proxies'; +import Relationship from './relationship'; export default class BelongsToRelationship extends Relationship { constructor(store, internalModel, inverseKey, relationshipMeta) { super(store, internalModel, inverseKey, relationshipMeta); this.inverseInternalModel = null; this.canonicalState = null; - this._loadingPromise = null; + this._promiseProxy = null; + } + + /** + * Flag indicating whether all inverse records are available + * + * true if the inverse exists and is loaded (not empty) + * true if there is no inverse + * false if the inverse exists and is not loaded (empty) + * + * @returns {boolean} + */ + get allInverseRecordsAreLoaded() { + let internalModel = this.inverseInternalModel; + let isEmpty = internalModel !== null && internalModel.isEmpty(); + + return !isEmpty; } setInternalModel(internalModel) { @@ -23,7 +37,6 @@ export default class BelongsToRelationship extends Relationship { this.setHasAnyRelationshipData(true); this.setRelationshipIsStale(false); this.setRelationshipIsEmpty(false); - this.setHasRelatedResources(!this.localStateIsEmpty()); } setCanonicalInternalModel(internalModel) { @@ -91,6 +104,7 @@ export default class BelongsToRelationship extends Relationship { } if (this.inverseInternalModel !== this.canonicalState) { this.inverseInternalModel = this.canonicalState; + this._promiseProxy = null; this.notifyBelongsToChanged(); } @@ -111,15 +125,23 @@ export default class BelongsToRelationship extends Relationship { this.notifyBelongsToChanged(); } - setRecordPromise(newPromise) { - let content = newPromise.get && newPromise.get('content'); - assert("You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo or hasMany relationship to the get call.", content !== undefined); + setRecordPromise(belongsToPromise) { + assert( + 'You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo or hasMany relationship to the get call.', + belongsToPromise instanceof PromiseObject + ); + + let content = belongsToPromise.get('content'); + let promise = belongsToPromise.get('promise'); + this.setInternalModel(content ? content._internalModel : content); + this._updateLoadingPromise(promise, content); } removeInternalModelFromOwn(internalModel) { if (!this.members.has(internalModel)) { return;} this.inverseInternalModel = null; + this._promiseProxy = null; super.removeInternalModelFromOwn(internalModel); this.notifyBelongsToChanged(); } @@ -127,6 +149,7 @@ export default class BelongsToRelationship extends Relationship { removeAllInternalModelsFromOwn() { super.removeAllInternalModelsFromOwn(); this.inverseInternalModel = null; + this._promiseProxy = null; this.notifyBelongsToChanged(); } @@ -145,91 +168,95 @@ export default class BelongsToRelationship extends Relationship { this.canonicalState = null; } - findRecord() { - if (this.inverseInternalModel) { - return this.store._findByInternalModel(this.inverseInternalModel); - } else { - return EmberPromise.resolve(null); + // called by `getData()` when a request is needed + // but no link is available + _fetchRecord() { + let { inverseInternalModel, shouldForceReload } = this; + + if (inverseInternalModel) { + let promise; + + if (shouldForceReload && !inverseInternalModel.isEmpty() && inverseInternalModel.hasRecord) { + // reload record, if it is already loaded + // if we have a link, we would already be in `findLink()` + promise = inverseInternalModel.getRecord().reload(); + } else { + promise = this.store._findByInternalModel(inverseInternalModel); + } + + return promise; } + + // TODO is this actually an error case? + return resolve(null); } - fetchLink() { - return this.store.findBelongsTo(this.internalModel, this.link, this.relationshipMeta).then((internalModel) => { - if (internalModel) { - this.addInternalModel(internalModel); - } - return internalModel; - }); + // called by `getData()` when a request is needed + // and a link is available + _fetchLink() { + return this.store + .findBelongsTo(this.internalModel, this.link, this.relationshipMeta) + .then(internalModel => { + if (internalModel) { + this.addInternalModel(internalModel); + } + return internalModel; + }); } - getRecord() { + getData() { //TODO(Igor) flushCanonical here once our syncing is not stupid - if (this.isAsync) { + let record = this.inverseInternalModel ? this.inverseInternalModel.getRecord() : null; + + if (this.shouldMakeRequest()) { let promise; - if (this._shouldFindViaLink()) { - promise = this.findLink().then(() => this.findRecord()); + if (this.link) { + promise = this._fetchLink(); } else { - promise = this.findRecord(); + promise = this._fetchRecord(); } - let record = this.inverseInternalModel ? this.inverseInternalModel.getRecord() : null - - return this._updateLoadingPromise(promise, record); - } else { - if (this.inverseInternalModel === null) { - return null; - } - let toReturn = this.inverseInternalModel.getRecord(); - assert("You looked up the '" + this.key + "' relationship on a '" + this.internalModel.modelName + "' with id " + this.internalModel.id + " but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.belongsTo({ async: true })`)", toReturn === null || !toReturn.get('isEmpty')); - return toReturn; - } - } + promise = promise.then(() => handleCompletedFind(this), e => handleCompletedFind(this, e)); - _updateLoadingPromise(promise, content) { - if (this._loadingPromise) { - if (content !== undefined) { - this._loadingPromise.set('content', content) - } - this._loadingPromise.set('promise', promise) - } else { - this._loadingPromise = PromiseBelongsTo.create({ - _belongsToState: this, - promise, - content + promise = promise.then(internalModel => { + return internalModel ? internalModel.getRecord() : null; }); - } - return this._loadingPromise; - } - - reload() { - // we've already fired off a request - if (this._loadingPromise) { - if (this._loadingPromise.get('isPending')) { - return this._loadingPromise; - } + this.fetchPromise = promise; + this._updateLoadingPromise(promise); } - let promise; - this.setRelationshipIsStale(true); + if (this.isAsync) { + if (this._promiseProxy === null) { + let promise = resolve(this.inverseInternalModel).then(internalModel => { + return internalModel ? internalModel.getRecord() : null; + }); + this._updateLoadingPromise(promise, record); + } - if (this.link) { - promise = this.fetchLink(); - } else if (this.inverseInternalModel && this.inverseInternalModel.hasRecord) { - // reload record, if it is already loaded - promise = this.inverseInternalModel.getRecord().reload(); + return this._promiseProxy; } else { - promise = this.findRecord(); + assert( + "You looked up the '" + + this.key + + "' relationship on a '" + + this.internalModel.modelName + + "' with id " + + this.internalModel.id + + ' but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.belongsTo({ async: true })`)', + record === null || !record.get('isEmpty') + ); + return record; } - - return this._updateLoadingPromise(promise); } - localStateIsEmpty() { - let internalModel = this.inverseInternalModel; - - return !internalModel || internalModel.isEmpty(); + _createProxy(promise, content) { + return PromiseBelongsTo.create({ + _belongsToState: this, + promise, + content, + }); } updateData(data, initial) { @@ -242,3 +269,21 @@ export default class BelongsToRelationship extends Relationship { } } } + +function handleCompletedFind(relationship, error) { + let internalModel = relationship.inverseInternalModel; + + relationship.fetchPromise = null; + relationship.setShouldForceReload(false); + + if (error) { + relationship.setHasFailedLoadAttempt(true); + throw error; + } + + relationship.setHasFailedLoadAttempt(false); + // only set to not stale if no error is thrown + relationship.setRelationshipIsStale(false); + + return internalModel; +} diff --git a/addon/-private/system/relationships/state/has-many.js b/addon/-private/system/relationships/state/has-many.js index 6a73365e369..c7af8e60fae 100755 --- a/addon/-private/system/relationships/state/has-many.js +++ b/addon/-private/system/relationships/state/has-many.js @@ -4,6 +4,7 @@ import { PromiseManyArray } from '../../promise-proxies'; import Relationship from './relationship'; import OrderedSet from '../../ordered-set'; import ManyArray from '../../many-array'; +import { resolve } from 'rsvp'; export default class ManyRelationship extends Relationship { constructor(store, internalModel, inverseKey, relationshipMeta) { @@ -16,31 +17,58 @@ export default class ManyRelationship extends Relationship { // we create a new many array, but in the interim it will be updated if // inverse internal models are unloaded. this._retainedManyArray = null; - this._loadingPromise = null; + this._promiseProxy = null; this._willUpdateManyArray = false; this._pendingManyArrayUpdates = null; } - _updateLoadingPromise(promise, content) { - if (this._loadingPromise) { - if (content) { - this._loadingPromise.set('content', content) - } - this._loadingPromise.set('promise', promise) - } else { - this._loadingPromise = PromiseManyArray.create({ - promise, - content - }); - } + get currentState() { + return this.members.list; + } + + /** + * Flag indicating whether all inverse records are available + * + * true if inverse records exist and are all loaded (all not empty) + * true if there are no inverse records + * false if the inverse records exist and any are not loaded (any empty) + * + * @returns {boolean} + */ + get allInverseRecordsAreLoaded() { + // check currentState for unloaded records + let hasEmptyRecords = this.currentState.reduce((hasEmptyModel, i) => { + return hasEmptyModel || i.isEmpty(); + }, false); + + // check un-synced state for unloaded records + if (!hasEmptyRecords && this.willSync) { + hasEmptyRecords = this.canonicalState.reduce((hasEmptyModel, i) => { + return hasEmptyModel || !i.isEmpty(); + }, false); + } + + return !hasEmptyRecords; + } - return this._loadingPromise; + _createProxy(promise, content) { + return PromiseManyArray.create({ + promise, + content, + }); } get manyArray() { - assert(`Error: relationship ${this.parentType}:${this.key} has both many array and retained many array`, this._manyArray === null || this._retainedManyArray === null); + assert( + `Error: relationship ${this.parentType}:${ + this.key + } has both many array and retained many array`, + this._manyArray === null || this._retainedManyArray === null + ); + + if (!this._manyArray && !this.isDestroying) { + let isLoaded = this.hasFailedLoadAttempt || this.isNew || this.allInverseRecordsAreLoaded; - if (!this._manyArray) { this._manyArray = ManyArray.create({ canonicalState: this.canonicalState, store: this.store, @@ -48,7 +76,8 @@ export default class ManyRelationship extends Relationship { type: this.store.modelFor(this.belongsToType), record: this.internalModel, meta: this.meta, - isPolymorphic: this.isPolymorphic + isPolymorphic: this.isPolymorphic, + isLoaded, }); if (this._retainedManyArray !== null) { @@ -67,8 +96,8 @@ export default class ManyRelationship extends Relationship { this._manyArray = null; } - if (this._loadingPromise) { - this._loadingPromise.destroy(); + if (this._promiseProxy) { + this._promiseProxy.destroy(); } } @@ -243,28 +272,6 @@ export default class ManyRelationship extends Relationship { this.internalModel.notifyHasManyAdded(this.key, internalModel, idx); } - reload() { - let manyArray = this.manyArray; - - if (this._loadingPromise) { - if (this._loadingPromise.get('isPending')) { - return this._loadingPromise; - } - } - - this.setRelationshipIsStale(true); - - let promise; - if (this.link) { - promise = this.fetchLink(); - } else { - promise = this.store._scheduleFetchMany(manyArray.currentState).then(() => manyArray); - } - - this._updateLoadingPromise(promise); - return this._loadingPromise; - } - computeChanges(internalModels = []) { let members = this.canonicalMembers; let internalModelsToRemove = []; @@ -304,60 +311,84 @@ export default class ManyRelationship extends Relationship { this.canonicalState = this.canonicalMembers.toArray(); } - fetchLink() { - return this.store.findHasMany(this.internalModel, this.link, this.relationshipMeta).then(records => { - if (records.hasOwnProperty('meta')) { - this.updateMeta(records.meta); - } - this.store._backburner.join(() => { - this.updateInternalModelsFromAdapter(records); - this.manyArray.set('isLoaded', true); - }); - return this.manyArray; - }); - } + // called by `getData()` when a request is needed + // but no link is available + _fetchRecords() { + let internalModels = this.currentState; + let { shouldForceReload } = this; + let promise; - findRecords() { - let manyArray = this.manyArray; - let internalModels = manyArray.currentState; + if (shouldForceReload === true) { + promise = this.store._scheduleFetchMany(internalModels); + } else { + promise = this.store.findMany(internalModels); + } - //TODO CLEANUP - return this.store.findMany(internalModels).then(() => { - if (!manyArray.get('isDestroyed')) { - //Goes away after the manyArray refactor - manyArray.set('isLoaded', true); - } - return manyArray; - }); + return promise; } - notifyHasManyChanged() { - this.internalModel.notifyHasManyAdded(this.key); + // called by `getData()` when a request is needed + // and a link is available + _fetchLink() { + return this.store + .findHasMany(this.internalModel, this.link, this.relationshipMeta) + .then(records => { + if (records.hasOwnProperty('meta')) { + this.updateMeta(records.meta); + } + this.store._backburner.join(() => { + this.updateInternalModelsFromAdapter(records); + }); + return records; + }); } - getRecords() { + getData() { //TODO(Igor) sync server here, once our syncing is not stupid let manyArray = this.manyArray; - if (this.isAsync) { + if (this.shouldMakeRequest()) { let promise; - if (this._shouldFindViaLink()) { - promise = this.findLink().then(() => this.findRecords()); + if (this.link) { + promise = this._fetchLink(); } else { - promise = this.findRecords(); + promise = this._fetchRecords(); } - return this._updateLoadingPromise(promise, manyArray); - } else { - assert(`You looked up the '${this.key}' relationship on a '${this.internalModel.type.modelName}' with id ${this.internalModel.id} but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async ('DS.hasMany({ async: true })')`, manyArray.isEvery('isEmpty', false)); + promise = promise.then( + () => handleCompletedRequest(this), + e => handleCompletedRequest(this, e) + ); + + this.fetchPromise = promise; + this._updateLoadingPromise(promise, manyArray); + } - manyArray.set('isLoaded', true); + if (this.isAsync) { + if (this._promiseProxy === null) { + this._updateLoadingPromise(resolve(manyArray), manyArray); + } + + return this._promiseProxy; + } else { + assert( + `You looked up the '${this.key}' relationship on a '${ + this.internalModel.type.modelName + }' with id ${ + this.internalModel.id + } but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (\`DS.hasMany({ async: true })\`)`, + this.allInverseRecordsAreLoaded + ); return manyArray; } } + notifyHasManyChanged() { + this.internalModel.notifyHasManyAdded(this.key); + } + updateData(data, initial) { let internalModels = this.store._pushResourceIdentifiers(this, data); if (initial) { @@ -367,20 +398,6 @@ export default class ManyRelationship extends Relationship { } } - localStateIsEmpty() { - let manyArray = this.manyArray; - let internalModels = manyArray.currentState; - let manyArrayIsLoaded = manyArray.get('isLoaded'); - - if (!manyArrayIsLoaded && internalModels.length) { - manyArrayIsLoaded = internalModels.reduce((hasNoEmptyModel, i) => { - return hasNoEmptyModel && !i.isEmpty(); - }, true); - } - - return !manyArrayIsLoaded; - } - destroy() { super.destroy(); let manyArray = this._manyArray; @@ -389,15 +406,38 @@ export default class ManyRelationship extends Relationship { this._manyArray = null; } - let proxy = this._loadingPromise; + let proxy = this._promiseProxy; if (proxy) { proxy.destroy(); - this._loadingPromise = null; + this._promiseProxy = null; } } } +function handleCompletedRequest(relationship, error) { + let manyArray = relationship.manyArray; + + //Goes away after the manyArray refactor + if (!manyArray.get('isDestroyed')) { + relationship.manyArray.set('isLoaded', true); + } + + relationship.fetchPromise = null; + relationship.setShouldForceReload(false); + + if (error) { + relationship.setHasFailedLoadAttempt(true); + throw error; + } + + relationship.setHasFailedLoadAttempt(false); + // only set to not stale if no error is thrown + relationship.setRelationshipIsStale(false); + + return manyArray; +} + function setForArray(array) { var set = new OrderedSet(); diff --git a/addon/-private/system/relationships/state/relationship.js b/addon/-private/system/relationships/state/relationship.js index 4d2bef4c5a4..45081c5af33 100644 --- a/addon/-private/system/relationships/state/relationship.js +++ b/addon/-private/system/relationships/state/relationship.js @@ -12,7 +12,6 @@ const { addInternalModel, addInternalModels, clear, - findLink, flushCanonical, flushCanonicalLater, newRelationship, @@ -34,7 +33,6 @@ const { 'addInternalModel', 'addInternalModels', 'clear', - 'findLink', 'flushCanonical', 'flushCanonicalLater', 'newRelationship', @@ -69,15 +67,34 @@ export default class Relationship { //This probably breaks for polymorphic relationship in complex scenarios, due to //multiple possible modelNames this.inverseKeyForImplicit = this.internalModel.modelName + this.key; - this.linkPromise = null; + this.fetchPromise = null; + this._promiseProxy = null; this.meta = null; this.__inverseMeta = undefined; + /* + This flag forces fetch. `true` for a single request once `reload()` + has been called `false` at all other times. + */ + this.shouldForceReload = false; + /* This flag indicates whether we should re-fetch the relationship the next time it is accessed. + The difference between this flag and `shouldForceReload` + is in how we treat the presence of partially missing data: + - for a forced reload, we will reload the link or EVERY record + - for a stale reload, we will reload the link (if present) else only MISSING records + + Ideally these flags could be merged, but because we don't give the + request layer the option of deciding how to resolve the data being queried + we are forced to differentiate for now. + + It is also possible for a relationship to remain stale after a forced reload; however, + in this case `hasFailedLoadAttempt` ought to be `true`. + false when => internalModel.isNew() on initial setup => a previously triggered request has resolved @@ -86,10 +103,9 @@ export default class Relationship { true when => !internalModel.isNew() on initial setup => an inverse has been unloaded - => relationship.reload() has been called => we get a new link for the relationship */ - this.relationshipIsStale = !internalModel.isNew(); + this.relationshipIsStale = !this.isNew; /* This flag indicates whether we should consider the content @@ -137,6 +153,12 @@ export default class Relationship { */ this.relationshipIsEmpty = true; + /* + Flag that indicates whether we have explicitly attempted a load for the relationship + (which may have failed) + */ + this.hasFailedLoadAttempt = false; + /* true when => hasAnyRelationshipData is true @@ -145,7 +167,10 @@ export default class Relationship { TODO, consider changing the conditional here from !isEmpty to !hiddenFromRecordArrays */ - this.hasRelatedResources = false; + } + + get isNew() { + return this.internalModel.isNew(); } _inverseIsSync() { @@ -188,7 +213,7 @@ export default class Relationship { } inverseDidDematerialize(inverseInternalModel) { - this.linkPromise = null; + this.fetchPromise = null; this.setRelationshipIsStale(true); if (!this.isAsync) { @@ -489,7 +514,7 @@ export default class Relationship { assert(`You have pushed a record of type '${this.internalModel.modelName}' with '${this.key}' as a link, but the value of that link is not a string.`, typeof link === 'string' || link === null); this.link = link; - this.linkPromise = null; + this.fetchPromise = null; this.setRelationshipIsStale(true); if (!initial) { @@ -497,24 +522,99 @@ export default class Relationship { } } - _shouldFindViaLink() { - if (!this.link) { - return false; + reload() { + if (this._promiseProxy) { + if (this._promiseProxy.get('isPending')) { + return this._promiseProxy; + } } - return this.relationshipIsStale || - !this.hasRelatedResources; + this.setHasFailedLoadAttempt(false); + this.setShouldForceReload(true); + this.getData(); + + return this._promiseProxy; } - findLink() { - heimdall.increment(findLink); - if (this.linkPromise) { - return this.linkPromise; + shouldMakeRequest() { + let { + relationshipIsStale, + hasFailedLoadAttempt, + allInverseRecordsAreLoaded, + hasAnyRelationshipData, + shouldForceReload, + relationshipIsEmpty, + isAsync, + isNew, + fetchPromise, + } = this; + + // never make a request if this record doesn't exist server side yet + if (isNew === true) { + return false; + } + + // do not re-request if we are already awaiting a request + if (fetchPromise !== null) { + return false; + } + + // Always make a request when forced + // failed attempts must call `reload()`. + // + // For legacy reasons, when a relationship is missing only + // some of it's data we rely on individual `findRecord` + // calls which may resolve from cache in the non-link case. + // This determination is made elsewhere. + // + if (shouldForceReload === true || relationshipIsStale === true) { + return !hasFailedLoadAttempt; + } + + // never make a request if we've explicitly attempted to at least once + // since the last update to canonical state + // this includes failed attempts + // e.g. to re-attempt `reload()` must be called force the attempt. + if (hasFailedLoadAttempt === true) { + return false; + } + + // we were explicitly told that there is no inverse relationship + if (relationshipIsEmpty === true) { + return false; + } + + // we were explicitly told what the inverse is, and we have the inverse records available + if (hasAnyRelationshipData === true && allInverseRecordsAreLoaded === true) { + return false; + } + + // if this is a sync relationship, we should not need to fetch, so getting here is an error + assert( + `You looked up the '${this.key}' relationship on a '${ + this.internalModel.type.modelName + }' with id ${ + this.internalModel.id + } but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (\`DS.${ + this.relationshipMeta.kind + }({ async: true })\`)`, + isAsync === true + ); + + return true; + } + + _updateLoadingPromise(promise, content) { + if (this._promiseProxy) { + if (content !== undefined) { + this._promiseProxy.set('content', content); + } + this._promiseProxy.set('promise', promise); } else { - let promise = this.fetchLink(); - this.linkPromise = promise; - return promise.then((result) => result); + this._promiseProxy = this._createProxy(promise, content); } + + return this._promiseProxy; } updateInternalModelsFromAdapter(internalModels) { @@ -531,8 +631,8 @@ export default class Relationship { this.hasAnyRelationshipData = value; } - setHasRelatedResources(v) { - this.hasRelatedResources = v; + setHasFailedLoadAttempt(value) { + this.hasFailedLoadAttempt = value; } setRelationshipIsStale(value) { @@ -543,6 +643,10 @@ export default class Relationship { this.relationshipIsEmpty = value; } + setShouldForceReload(value) { + this.shouldForceReload = value; + } + /* `push` for a relationship allows the store to push a JSON API Relationship Object onto the relationship. The relationship will then extract and set the @@ -585,12 +689,12 @@ export default class Relationship { relationshipIsEmpty -> true if is empty array (has-many) or is null (belongs-to) hasAnyRelationshipData -> true relationshipIsStale -> false - hasRelatedResources -> run-check-to-determine + allInverseRecordsAreLoaded -> run-check-to-determine IF contains only links relationshipIsStale -> true */ - + this.setHasFailedLoadAttempt(false); if (hasRelationshipDataProperty) { let relationshipIsEmpty = payload.data === null || (Array.isArray(payload.data) && payload.data.length === 0); @@ -598,14 +702,15 @@ export default class Relationship { this.setHasAnyRelationshipData(true); this.setRelationshipIsStale(false); this.setRelationshipIsEmpty(relationshipIsEmpty); - this.setHasRelatedResources( - relationshipIsEmpty || !this.localStateIsEmpty() - ); } else if (hasLink) { this.setRelationshipIsStale(true); } } + getData() {} + + _createProxy() {} + updateData() {} destroy() { diff --git a/addon/-private/system/store.js b/addon/-private/system/store.js index ce00f19d97c..3d07bbea9e4 100644 --- a/addon/-private/system/store.js +++ b/addon/-private/system/store.js @@ -5,7 +5,7 @@ import { A } from '@ember/array'; import EmberError from '@ember/error'; import MapWithDefault from './map-with-default'; -import { run as emberRun } from '@ember/runloop'; +import { run as emberRunLoop } from '@ember/runloop'; import { set, get, computed } from '@ember/object'; import { assign } from '@ember/polyfills'; import { default as RSVP, Promise } from 'rsvp'; @@ -55,7 +55,7 @@ import InternalModel from "./model/internal-model"; import edBackburner from './backburner'; const badIdFormatAssertion = '`id` passed to `findRecord()` has to be non-empty string or number'; - +const emberRun = emberRunLoop.backburner; const { ENV } = Ember; //Get the materialized model from the internalModel/promise that returns @@ -740,7 +740,7 @@ Store = Service.extend({ //TODO double check about reloading if (internalModel.isLoading()) { - return internalModel._loadingPromise; + return internalModel._promiseProxy; } return Promise.resolve(internalModel); @@ -802,8 +802,8 @@ Store = Service.extend({ }, _scheduleFetch(internalModel, options) { - if (internalModel._loadingPromise) { - return internalModel._loadingPromise; + if (internalModel._promiseProxy) { + return internalModel._promiseProxy; } let { id, modelName } = internalModel; @@ -1792,7 +1792,7 @@ Store = Service.extend({ snapshot: snapshot, resolver: resolver }); - emberRun.once(this, this.flushPendingSave); + emberRun.scheduleOnce('actions', this, this.flushPendingSave); }, /** diff --git a/addon/-private/system/store/common.js b/addon/-private/system/store/common.js index 007671f5114..6e13ab24fbc 100644 --- a/addon/-private/system/store/common.js +++ b/addon/-private/system/store/common.js @@ -1,7 +1,7 @@ import { get } from '@ember/object'; import { DEBUG } from '@glimmer/env'; import Ember from 'ember'; -import { Promise } from 'rsvp'; +import { resolve } from 'rsvp'; const { __bind, @@ -23,7 +23,7 @@ export function _bind(fn, ...args) { export function _guard(promise, test) { heimdall.increment(__guard); - let guarded = promise['finally'](function() { + let guarded = promise.finally(() => { if (!test()) { guarded._subscribers.length = 0; } @@ -49,7 +49,12 @@ if (DEBUG) { } export function guardDestroyedStore(promise, store, label) { - promise = Promise.resolve(promise, label); + let wrapperPromise = resolve(promise, label).then(v => promise); - return _guard(promise, () => { if (DEBUG) { ASYNC_REQUEST_COUNT--; } return _objectIsAlive(store); }); + return _guard(wrapperPromise, () => { + if (DEBUG) { + ASYNC_REQUEST_COUNT--; + } + return _objectIsAlive(store); + }); } diff --git a/addon/-private/system/store/finders.js b/addon/-private/system/store/finders.js index d9923eaf29a..db3593f8d4b 100644 --- a/addon/-private/system/store/finders.js +++ b/addon/-private/system/store/finders.js @@ -26,7 +26,9 @@ export function _find(adapter, store, modelClass, id, internalModel, options) { if (DEBUG) { incrementRequestCount(); } let snapshot = internalModel.createSnapshot(options); let { modelName } = internalModel; - let promise = Promise.resolve().then(() => adapter.findRecord(store, modelClass, id, snapshot)); + let promise = Promise.resolve().then(() => { + return adapter.findRecord(store, modelClass, id, snapshot); + }); let label = `DS: Handle Adapter#findRecord of '${modelName}' with id: '${id}'`; promise = guardDestroyedStore(promise, store, label); diff --git a/config/ember-try.js b/config/ember-try.js index 44c3e0be73e..1e16b022a3d 100644 --- a/config/ember-try.js +++ b/config/ember-try.js @@ -17,20 +17,20 @@ module.exports = function() { npm: { } }, { - name: 'ember-lts-2.12', + name: 'ember-lts-2.16', npm: { devDependencies: { - 'ember-source': '~2.12.0' - } - } + 'ember-source': '~2.16.0', + }, + }, }, { - name: 'ember-lts-2.16', + name: 'ember-lts-2.18', npm: { devDependencies: { - 'ember-source': '~2.16.0' - } - } + 'ember-source': '~2.18.0', + }, + }, }, { name: 'ember-release', diff --git a/package.json b/package.json index bb758bcb710..f9a9d6e0698 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "scripts": { "build": "ember build", "build:production": "ember build --environment=production", + "lint:js": "eslint .", "start": "ember server", "test": "ember test", "test:all": "ember try:each", @@ -53,6 +54,9 @@ }, "devDependencies": { "babel-plugin-debug-macros": "^0.1.7", + "@ember-decorators/babel-transforms": "^2.0.0", + "@ember-decorators/data": "^2.1.0", + "babel-eslint": "^8.0.0", "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", "babel-plugin-transform-es2015-classes": "^6.23.0", "babel-plugin-transform-es2015-computed-properties": "^6.22.0", @@ -72,7 +76,7 @@ "broccoli-yuidoc": "^3.0.0", "co": "^4.6.0", "common-tags": "^1.4.0", - "ember-cli": "^2.11.1", + "ember-cli": "^3.1.4", "ember-cli-app-version": "^3.0.0", "ember-cli-blueprint-test-helpers": "^0.18.3", "ember-cli-dependency-checker": "^2.1.0", @@ -87,11 +91,13 @@ "ember-cli-shims": "^1.0.2", "ember-cli-sri": "^2.1.0", "ember-cli-test-loader": "^1.1.0", - "ember-cli-uglify": "^1.2.0", + "ember-cli-uglify": "2.0.0-beta.1", + "ember-decorators": "^2.1.0", "ember-disable-prototype-extensions": "^1.1.0", "ember-disable-proxy-controllers": "^1.0.0", "ember-export-application-global": "^1.0.5", "ember-load-initializers": "^0.6.0", + "ember-maybe-import-regenerator": "^0.1.6", "ember-publisher": "0.0.7", "ember-qunit-assert-helpers": "^0.2.1", "ember-resolver": "^4.1.0", @@ -99,11 +105,15 @@ "ember-source-channel-url": "^1.0.1", "ember-try": "^0.2.23", "ember-watson": "^0.7.0", + "eslint-config-prettier": "^2.9.0", + "eslint-plugin-node": "^6.0.1", + "eslint-plugin-prettier": "^2.6.0", "github": "^1.1.1", "glob": "^5.0.13", "loader.js": "^4.5.0", "mocha": "^2.4.5", "mocha-only-detector": "0.0.2", + "prettier": "^1.12.1", "rimraf": "2.5.2", "rsvp": "4.8.0", "testdouble": "^3.2.6", diff --git a/testem.js b/testem.js index 158a909be30..2833711ad47 100644 --- a/testem.js +++ b/testem.js @@ -14,8 +14,11 @@ module.exports = { Chrome: { mode: 'ci', args: [ - '--disable-gpu', '--headless', + '--disable-gpu', + '--disable-dev-shm-usage', + '--disable-software-rasterizer', + '--mute-audio', '--remote-debugging-port=0', '--window-size=1440,900', '--no-sandbox' diff --git a/tests/acceptance/relationships/has-many-test.js b/tests/acceptance/relationships/has-many-test.js new file mode 100644 index 00000000000..b62329848c1 --- /dev/null +++ b/tests/acceptance/relationships/has-many-test.js @@ -0,0 +1,664 @@ +/* eslint-disable */ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import JSONAPIAdapter from 'ember-data/adapters/json-api'; +import Model from 'ember-data/model'; +import { render } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; +import { attr, hasMany, belongsTo } from '@ember-decorators/data'; +import JSONAPISerializer from 'ember-data/serializers/json-api'; +import Store from 'ember-data/store'; +import { resolve, reject } from 'rsvp'; +import { ServerError } from 'ember-data/adapters/errors'; +import Ember from 'ember'; + +function domListToArray(domList) { + return Array.prototype.slice.call(domList); +} + +class Person extends Model { + @attr name; + @hasMany('person', { async: true, inverse: 'parent' }) + children; + @belongsTo('person', { async: true, inverse: 'children' }) + parent; +} + +class TestAdapter extends JSONAPIAdapter { + setupPayloads(assert, arr) { + this.assert = assert; + this._payloads = arr; + } + + shouldBackgroundReload() { + return false; + } + + _nextPayload() { + let payload = this._payloads.shift(); + + if (payload === undefined) { + this.assert.ok(false, 'Too many adapter requests have been made!'); + return resolve({ data: null }); + } + + if (payload instanceof ServerError) { + return reject(payload); + } + return resolve(payload); + } + + // find by link + findHasMany() { + return this._nextPayload(); + } + + // find by data with coalesceFindRequests set to true + findMany() { + return this._nextPayload(); + } + + // find by partial data / individual records + findRecord() { + return this._nextPayload(); + } +} + +function makePeopleWithRelationshipData() { + let people = [ + { + type: 'person', + id: '1:no-children-or-parent', + attributes: { name: 'Chris Has No Children or Parent' }, + relationships: { + children: { data: [] }, + parent: { data: null }, + }, + }, + { + type: 'person', + id: '2:has-1-child-no-parent', + attributes: { + name: 'James has one child and no parent', + }, + relationships: { + children: { + data: [{ type: 'person', id: '3:has-2-children-and-parent' }], + }, + parent: { data: null }, + }, + }, + { + type: 'person', + id: '3:has-2-children-and-parent', + attributes: { + name: 'Kevin has two children and one parent', + }, + relationships: { + children: { + data: [ + { type: 'person', id: '4:has-parent-no-children' }, + { type: 'person', id: '5:has-parent-no-children' }, + ], + }, + parent: { + data: { + type: 'person', + id: '2:has-1-child-no-parent', + }, + }, + }, + }, + { + type: 'person', + id: '4:has-parent-no-children', + attributes: { + name: 'Selena has a parent', + }, + relationships: { + children: { + data: [], + }, + parent: { + data: { + type: 'person', + id: '3:has-2-children-and-parent', + }, + }, + }, + }, + { + type: 'person', + id: '5:has-parent-no-children', + attributes: { + name: 'Sedona has a parent', + }, + relationships: { + children: { + data: [], + }, + parent: { + data: { + type: 'person', + id: '3:has-2-children-and-parent', + }, + }, + }, + }, + ]; + + let peopleHash = {}; + people.forEach(person => { + peopleHash[person.id] = person; + }); + + return { + dict: peopleHash, + all: people, + }; +} + +function makePeopleWithRelationshipLinks(removeData = true) { + let people = makePeopleWithRelationshipData(); + let linkPayloads = (people.links = {}); + + people.all.map(person => { + Object.keys(person.relationships).forEach(relName => { + let rel = person.relationships[relName]; + let data = rel.data; + + if (removeData === true) { + delete rel.data; + } + + if (Array.isArray(data)) { + data = data.map(ref => people.dict[ref.id]); + } else { + if (data !== null) { + data = people.dict[data.id]; + } + } + + rel.links = { + related: `./${person.type}/${person.id}/${relName}`, + }; + linkPayloads[rel.links.related] = { + data, + }; + }); + }); + + return people; +} + +module('async has-many rendering tests', function(hooks) { + let store; + let adapter; + setupRenderingTest(hooks); + + hooks.beforeEach(function() { + let { owner } = this; + owner.register('model:person', Person); + owner.register('adapter:application', TestAdapter); + owner.register('serializer:application', JSONAPISerializer); + owner.register('service:store', Store); + store = owner.lookup('service:store'); + adapter = store.adapterFor('application'); + }); + + module('for data-no-link scenarios', function() { + test('We can render an async hasMany', async function(assert) { + let people = makePeopleWithRelationshipData(); + let parent = store.push({ + data: people.dict['3:has-2-children-and-parent'], + }); + + adapter.setupPayloads(assert, [ + { data: people.dict['4:has-parent-no-children'] }, + { data: people.dict['5:has-parent-no-children'] }, + ]); + + // render + this.set('parent', parent); + + await render(hbs` + + `); + + let items = this.element.querySelectorAll('li'); + let names = domListToArray(items).map(e => e.textContent); + + assert.deepEqual( + names, + ['Selena has a parent', 'Sedona has a parent'], + 'We rendered the names' + ); + }); + + test('Re-rendering an async hasMany does not cause a new fetch', async function(assert) { + let people = makePeopleWithRelationshipData(); + let parent = store.push({ + data: people.dict['3:has-2-children-and-parent'], + }); + + adapter.setupPayloads(assert, [ + { data: people.dict['4:has-parent-no-children'] }, + { data: people.dict['5:has-parent-no-children'] }, + ]); + + // render + this.set('parent', parent); + + await render(hbs` + + `); + + let items = this.element.querySelectorAll('li'); + let names = domListToArray(items).map(e => e.textContent); + + assert.deepEqual( + names, + ['Selena has a parent', 'Sedona has a parent'], + 'We rendered the names' + ); + + this.set('parent', null); + + items = this.element.querySelectorAll('li'); + assert.ok(items.length === 0, 'We have no items'); + + this.set('parent', parent); + + items = this.element.querySelectorAll('li'); + names = domListToArray(items).map(e => e.textContent); + + assert.deepEqual( + names, + ['Selena has a parent', 'Sedona has a parent'], + 'We rendered the names' + ); + }); + + test('Rendering an async hasMany whose fetch fails does not trigger a new request', async function(assert) { + assert.expect(12); + let people = makePeopleWithRelationshipData(); + let parent = store.push({ + data: people.dict['3:has-2-children-and-parent'], + }); + + adapter.setupPayloads(assert, [ + { data: people.dict['4:has-parent-no-children'] }, + new ServerError([], 'hard error while finding 5:has-parent-no-children'), + ]); + + // render + this.set('parent', parent); + + let originalOnError = Ember.onerror; + Ember.onerror = function(e) { + assert.ok(true, 'Children promise did reject'); + assert.equal( + e.message, + 'hard error while finding 5:has-parent-no-children', + 'Rejection has the correct message' + ); + }; + + // needed for LTS 2.12 and 2.16 + Ember.Test.adapter.exception = e => { + assert.ok(true, 'Children promise did reject'); + assert.equal( + e.message, + 'hard error while finding 5:has-parent-no-children', + 'Rejection has the correct message' + ); + }; + + await render(hbs` +
    + {{#each parent.children as |child|}} +
  • {{child.name}}
  • + {{/each}} +
+ `); + + let items = this.element.querySelectorAll('li'); + let names = domListToArray(items).map(e => e.textContent); + + assert.deepEqual( + names, + ['Selena has a parent'], + 'We rendered only the names for successful requests' + ); + + let relationshipState = parent.hasMany('children').hasManyRelationship; + + assert.equal(relationshipState.isAsync, true, 'The relationship is async'); + assert.equal(relationshipState.relationshipIsEmpty, false, 'The relationship is not empty'); + assert.equal(relationshipState.relationshipIsStale, true, 'The relationship is still stale'); + assert.equal( + relationshipState.allInverseRecordsAreLoaded, + false, + 'The relationship is missing some or all related resources' + ); + assert.equal( + relationshipState.hasAnyRelationshipData, + true, + 'The relationship knows which record it needs' + ); + assert.equal( + relationshipState.fetchPromise === null, + true, + 'The relationship has no fetchPromise' + ); + assert.equal( + relationshipState.hasFailedLoadAttempt === true, + true, + 'The relationship has attempted a load' + ); + assert.equal( + relationshipState._promiseProxy !== null, + true, + 'The relationship has a loadingPromise' + ); + assert.equal(!!relationshipState.link, false, 'The relationship does not have a link'); + + Ember.onerror = originalOnError; + }); + }); + + module('for link-no-data scenarios', function() { + test('We can render an async hasMany with a link', async function(assert) { + let people = makePeopleWithRelationshipLinks(true); + let parent = store.push({ + data: people.dict['3:has-2-children-and-parent'], + }); + + adapter.setupPayloads(assert, [ + people.links['./person/3:has-2-children-and-parent/children'], + ]); + + // render + this.set('parent', parent); + + await render(hbs` +
    + {{#each parent.children as |child|}} +
  • {{child.name}}
  • + {{/each}} +
+ `); + + let items = this.element.querySelectorAll('li'); + let names = domListToArray(items).map(e => e.textContent); + + assert.deepEqual( + names, + ['Selena has a parent', 'Sedona has a parent'], + 'We rendered the names' + ); + }); + + test('Re-rendering an async hasMany with a link does not cause a new fetch', async function(assert) { + let people = makePeopleWithRelationshipLinks(true); + let parent = store.push({ + data: people.dict['3:has-2-children-and-parent'], + }); + + adapter.setupPayloads(assert, [ + people.links['./person/3:has-2-children-and-parent/children'], + ]); + + // render + this.set('parent', parent); + + await render(hbs` +
    + {{#each parent.children as |child|}} +
  • {{child.name}}
  • + {{/each}} +
+ `); + + let items = this.element.querySelectorAll('li'); + let names = domListToArray(items).map(e => e.textContent); + + assert.deepEqual( + names, + ['Selena has a parent', 'Sedona has a parent'], + 'We rendered the names' + ); + + this.set('parent', null); + + items = this.element.querySelectorAll('li'); + assert.ok(items.length === 0, 'We have no items'); + + this.set('parent', parent); + + items = this.element.querySelectorAll('li'); + names = domListToArray(items).map(e => e.textContent); + + assert.deepEqual( + names, + ['Selena has a parent', 'Sedona has a parent'], + 'We rendered the names' + ); + }); + + test('Rendering an async hasMany with a link whose fetch fails does not trigger a new request', async function(assert) { + assert.expect(12); + let people = makePeopleWithRelationshipLinks(true); + let parent = store.push({ + data: people.dict['3:has-2-children-and-parent'], + }); + + adapter.setupPayloads(assert, [ + people.links['./person/3:has-2-children-and-parent/children'], + ]); + + adapter.setupPayloads(assert, [ + new ServerError( + [], + 'hard error while finding link ./person/3:has-2-children-and-parent/children' + ), + ]); + + // render + this.set('parent', parent); + + let originalOnError = Ember.onerror; + Ember.onerror = function(e) { + assert.ok(true, 'Children promise did reject'); + assert.equal( + e.message, + 'hard error while finding link ./person/3:has-2-children-and-parent/children', + 'Rejection has the correct message' + ); + }; + + // needed for LTS 2.12 and 2.16 + Ember.Test.adapter.exception = e => { + assert.ok(true, 'Children promise did reject'); + assert.equal( + e.message, + 'hard error while finding link ./person/3:has-2-children-and-parent/children', + 'Rejection has the correct message' + ); + }; + + await render(hbs` +
    + {{#each parent.children as |child|}} +
  • {{child.name}}
  • + {{/each}} +
+ `); + + let items = this.element.querySelectorAll('li'); + let names = domListToArray(items).map(e => e.textContent); + + assert.deepEqual(names, [], 'We rendered no names'); + + let relationshipState = parent.hasMany('children').hasManyRelationship; + + assert.equal(relationshipState.isAsync, true, 'The relationship is async'); + assert.equal( + relationshipState.relationshipIsEmpty, + true, + 'The relationship is empty because no signal has been received as to true state' + ); + assert.equal(relationshipState.relationshipIsStale, true, 'The relationship is still stale'); + assert.equal( + relationshipState.allInverseRecordsAreLoaded, + true, + 'The relationship is missing some or all related resources' + ); + assert.equal( + relationshipState.hasAnyRelationshipData, + false, + 'The relationship knows which record it needs' + ); + assert.equal( + relationshipState.fetchPromise === null, + true, + 'The relationship has no fetchPromise' + ); + assert.equal( + relationshipState.hasFailedLoadAttempt === true, + true, + 'The relationship has attempted a load' + ); + assert.equal( + relationshipState._promiseProxy !== null, + true, + 'The relationship has a loadingPromise' + ); + assert.equal(!!relationshipState.link, true, 'The relationship has a link'); + + Ember.onerror = originalOnError; + }); + }); + + module('for link-and-data scenarios', function() { + test('We can render an async hasMany with a link and data', async function(assert) { + let people = makePeopleWithRelationshipLinks(false); + let parent = store.push({ + data: people.dict['3:has-2-children-and-parent'], + }); + + adapter.setupPayloads(assert, [ + people.links['./person/3:has-2-children-and-parent/children'], + ]); + + // render + this.set('parent', parent); + + await render(hbs` +
    + {{#each parent.children as |child|}} +
  • {{child.name}}
  • + {{/each}} +
+ `); + + let items = this.element.querySelectorAll('li'); + let names = domListToArray(items).map(e => e.textContent); + + assert.deepEqual( + names, + ['Selena has a parent', 'Sedona has a parent'], + 'We rendered the names' + ); + }); + + test('Rendering an async hasMany with a link and data where data has been side-loaded does not fetch the link', async function(assert) { + let people = makePeopleWithRelationshipLinks(false); + let parent = store.push({ + data: people.dict['3:has-2-children-and-parent'], + included: [ + people.dict['4:has-parent-no-children'], + people.dict['5:has-parent-no-children'], + ], + }); + + // no requests should be made + adapter.setupPayloads(assert, []); + + // render + this.set('parent', parent); + + await render(hbs` +
    + {{#each parent.children as |child|}} +
  • {{child.name}}
  • + {{/each}} +
+ `); + + let items = this.element.querySelectorAll('li'); + let names = domListToArray(items).map(e => e.textContent); + + assert.deepEqual( + names, + ['Selena has a parent', 'Sedona has a parent'], + 'We rendered the names' + ); + }); + + test('Re-rendering an async hasMany with a link and data does not cause a new fetch', async function(assert) { + let people = makePeopleWithRelationshipLinks(false); + let parent = store.push({ + data: people.dict['3:has-2-children-and-parent'], + }); + + adapter.setupPayloads(assert, [ + people.links['./person/3:has-2-children-and-parent/children'], + ]); + + // render + this.set('parent', parent); + + await render(hbs` +
    + {{#each parent.children as |child|}} +
  • {{child.name}}
  • + {{/each}} +
+ `); + + let items = this.element.querySelectorAll('li'); + let names = domListToArray(items).map(e => e.textContent); + + assert.deepEqual( + names, + ['Selena has a parent', 'Sedona has a parent'], + 'We rendered the names' + ); + + this.set('parent', null); + + items = this.element.querySelectorAll('li'); + assert.ok(items.length === 0, 'We have no items'); + + this.set('parent', parent); + + items = this.element.querySelectorAll('li'); + names = domListToArray(items).map(e => e.textContent); + + assert.deepEqual( + names, + ['Selena has a parent', 'Sedona has a parent'], + 'We rendered the names' + ); + }); + }); +}); diff --git a/tests/helpers/model-factory-injection.js b/tests/helpers/model-factory-injection.js index 1a364a43163..1096af16f2e 100644 --- a/tests/helpers/model-factory-injection.js +++ b/tests/helpers/model-factory-injection.js @@ -1,5 +1,5 @@ import Ember from 'ember'; -import hasEmberVersion from 'ember-test-helpers/has-ember-version'; +import hasEmberVersion from '@ember/test-helpers/has-ember-version'; const ORIGINAL_MODEL_FACTORY_INJECTIONS = Ember.MODEL_FACTORY_INJECTIONS; diff --git a/tests/integration/adapter/json-api-adapter-test.js b/tests/integration/adapter/json-api-adapter-test.js index c945eeb51cf..932ed18bd94 100644 --- a/tests/integration/adapter/json-api-adapter-test.js +++ b/tests/integration/adapter/json-api-adapter-test.js @@ -299,13 +299,21 @@ test('find a single record with belongsTo link as object { related }', function( return run(() => { return store.findRecord('post', 1).then(post => { - assert.equal(passedUrl[0], '/posts/1'); + assert.equal( + passedUrl[0], + '/posts/1', + 'The primary record post:1 was fetched by the correct url' + ); assert.equal(post.get('id'), '1'); assert.equal(post.get('title'), 'Ember.js rocks'); return post.get('author').then(author => { - assert.equal(passedUrl[1], 'http://example.com/user/2'); + assert.equal( + passedUrl[1], + 'http://example.com/user/2', + 'The relationship user:2 was fetched by the correct url' + ); assert.equal(author.get('id'), '2'); assert.equal(author.get('firstName'), 'Yehuda'); @@ -344,13 +352,21 @@ test('find a single record with belongsTo link as object { data }', function(ass return run(() => { return store.findRecord('post', 1).then(post => { - assert.equal(passedUrl[0], '/posts/1'); + assert.equal( + passedUrl[0], + '/posts/1', + 'The primary record post:1 was fetched by the correct url' + ); assert.equal(post.get('id'), '1'); assert.equal(post.get('title'), 'Ember.js rocks'); return post.get('author').then(author => { - assert.equal(passedUrl[1], '/users/2'); + assert.equal( + passedUrl[1], + '/users/2', + 'The relationship user:2 was fetched by the correct url' + ); assert.equal(author.get('id'), '2'); assert.equal(author.get('firstName'), 'Yehuda'); @@ -435,7 +451,11 @@ test('find a single record with sideloaded belongsTo link as object { data }', f return run(() => { return store.findRecord('post', 1).then(post => { - assert.equal(passedUrl[0], '/posts/1'); + assert.equal( + passedUrl[0], + '/posts/1', + 'The primary record post:1 was fetched by the correct url' + ); assert.equal(post.get('id'), '1'); assert.equal(post.get('title'), 'Ember.js rocks'); diff --git a/tests/integration/relationships/has-many-test.js b/tests/integration/relationships/has-many-test.js index f73db1f5c7d..28d1204677d 100644 --- a/tests/integration/relationships/has-many-test.js +++ b/tests/integration/relationships/has-many-test.js @@ -5,22 +5,12 @@ import { reset as resetModelFactoryInjections } from 'dummy/tests/helpers/model-factory-injection'; import { A } from '@ember/array'; - -import { - resolve, - Promise as EmberPromise, - all, - reject, - hash -} from 'rsvp'; +import { resolve, Promise as EmberPromise, all, reject, hash } from 'rsvp'; import { get } from '@ember/object'; import { run } from '@ember/runloop'; - import setupStore from 'dummy/tests/helpers/store'; - import testInDebug from 'dummy/tests/helpers/test-in-debug'; -import { module, test } from 'qunit'; - +import { module, test, skip } from 'qunit'; import DS from 'ember-data'; let env, store, User, Contact, Email, Phone, Message, Post, Comment; @@ -867,57 +857,75 @@ test("A hasMany relationship can be reloaded if it was fetched via ids", functio }); }); -test("A hasMany relationship can be reloaded even if it failed at the first time", function(assert) { - assert.expect(4); +test( + 'A hasMany relationship can be reloaded even if it failed at the first time', + async function(assert) { + assert.expect(6); - Post.reopen({ - comments: DS.hasMany('comment', { async: true }) - }); + const { store, adapter } = env; - env.adapter.findRecord = function(store, type, id) { - return resolve({ - data: { - id: 1, - type: 'post', - relationships: { - comments: { - links: { related: "/posts/1/comments" } - } - } - } + Post.reopen({ + comments: DS.hasMany('comment', { async: true }), }); - }; - let loadingCount = -1; - env.adapter.findHasMany = function(store, record, link, relationship) { - loadingCount++; - if (loadingCount % 2 === 0) { - return reject(); - } else { - return resolve({ data: [ - { id: 1, type: 'comment', attributes: { body: "FirstUpdated" } }, - { id: 2, type: 'comment', attributes: { body: "Second" } } - ]}); - } - }; - run(function() { - env.store.findRecord('post', 1).then(function(post) { - let comments = post.get('comments'); - return comments.catch(function() { - return comments.reload(); - }).then(function(manyArray) { - assert.equal(manyArray.get('isLoaded'), true, "the reload worked, comments are now loaded"); - return manyArray.reload().catch(function () { - assert.equal(manyArray.get('isLoaded'), true, "the second reload failed, comments are still loaded though"); - return manyArray.reload().then(function(reloadedManyArray) { - assert.equal(reloadedManyArray.get('isLoaded'), true, "the third reload worked, comments are loaded again"); - assert.ok(reloadedManyArray === manyArray, "the many array stays the same"); - }); - }); + adapter.findRecord = function(store, type, id) { + return resolve({ + data: { + id: 1, + type: 'post', + relationships: { + comments: { + links: { related: '/posts/1/comments' }, + }, + }, + }, }); + }; + + let loadingCount = -1; + adapter.findHasMany = function(store, record, link, relationship) { + loadingCount++; + if (loadingCount % 2 === 0) { + return reject({ data: null }); + } else { + return resolve({ + data: [ + { id: 1, type: 'comment', attributes: { body: 'FirstUpdated' } }, + { id: 2, type: 'comment', attributes: { body: 'Second' } }, + ], + }); + } + }; + + let post = await store.findRecord('post', 1); + let comments = post.get('comments'); + let manyArray = await comments.catch(() => { + assert.ok(true, 'An error was thrown on the first reload of comments'); + return comments.reload(); }); - }); -}); + + assert.equal(manyArray.get('isLoaded'), true, 'the reload worked, comments are now loaded'); + + await manyArray.reload().catch(() => { + assert.ok(true, 'An error was thrown on the second reload via manyArray'); + }); + + assert.equal( + manyArray.get('isLoaded'), + true, + 'the second reload failed, comments are still loaded though' + ); + + let reloadedManyArray = await manyArray.reload(); + + assert.equal( + reloadedManyArray.get('isLoaded'), + true, + 'the third reload worked, comments are loaded again' + ); + assert.ok(reloadedManyArray === manyArray, 'the many array stays the same'); + } +); test("A hasMany relationship can be directly reloaded if it was fetched via links", function(assert) { assert.expect(6); @@ -1863,70 +1871,81 @@ test("dual non-async HM <-> BT", function(assert) { }); }); -test("When an unloaded record is added to the hasMany, it gets fetched once the hasMany is accessed even if the hasMany has been already fetched", function(assert) { +test('When an unloaded record is added to the hasMany, it gets fetched once the hasMany is accessed even if the hasMany has been already fetched', async function(assert) { + assert.expect(6); Post.reopen({ comments: DS.hasMany('comment', { async: true }) }); - env.adapter.findMany = function(store, type, ids, snapshots) { - return resolve({ data: [ - { id: 1, type: 'comment', attributes: { body: 'first' } }, - { id: 2, type: 'comment', attributes: { body: 'second' } } - ]}); + const { store, adapter } = env; + + let findManyCalls = 0; + let findRecordCalls = 0; + + adapter.findMany = function(store, type, ids, snapshots) { + assert.ok(true, `findMany called ${++findManyCalls}x`); + return resolve({ + data: [ + { id: 1, type: 'comment', attributes: { body: 'first' } }, + { id: 2, type: 'comment', attributes: { body: 'second' } }, + ], + }); }; - env.adapter.findRecord = function(store, type, id, snapshot) { + adapter.findRecord = function(store, type, id, snapshot) { + assert.ok(true, `findRecord called ${++findRecordCalls}x`); + return resolve({ data: { id: 3, type: 'comment', attributes: { body: 'third' } } }); }; - let post; - run(() => { - env.store.push({ - data: { - type: 'post', - id: '1', - relationships: { - comments: { - data: [ - { type: 'comment', id: '1' }, - { type: 'comment', id: '2' } - ] - } - } - } - }); - post = env.store.peekRecord('post', 1); + let post = store.push({ + data: { + type: 'post', + id: '1', + relationships: { + comments: { + data: [{ type: 'comment', id: '1' }, { type: 'comment', id: '2' }], + }, + }, + }, }); - return run(() => { - return post.get('comments').then(fetchedComments => { - assert.equal(fetchedComments.get('length'), 2, 'comments fetched successfully'); - assert.equal(fetchedComments.objectAt(0).get('body'), 'first', 'first comment loaded successfully'); - env.store.push({ - data: { - type: 'post', - id: '1', - relationships: { - comments: { - data: [ - { type: 'comment', id: '1' }, - { type: 'comment', id: '2' }, - { type: 'comment', id: '3' } - ] - } - } - } - }); + let fetchedComments = await post.get('comments'); - return post.get('comments').then(newlyFetchedComments => { - assert.equal(newlyFetchedComments.get('length'), 3, 'all three comments fetched successfully'); - assert.equal(newlyFetchedComments.objectAt(2).get('body'), 'third', 'third comment loaded successfully'); - }); - }); + assert.equal(fetchedComments.get('length'), 2, 'comments fetched successfully'); + assert.equal( + fetchedComments.objectAt(0).get('body'), + 'first', + 'first comment loaded successfully' + ); + + store.push({ + data: { + type: 'post', + id: '1', + relationships: { + comments: { + data: [ + { type: 'comment', id: '1' }, + { type: 'comment', id: '2' }, + { type: 'comment', id: '3' }, + ], + }, + }, + }, }); + + let newlyFetchedComments = await post.get('comments'); + + assert.equal(newlyFetchedComments.get('length'), 3, 'all three comments fetched successfully'); + assert.equal( + newlyFetchedComments.objectAt(2).get('body'), + 'third', + 'third comment loaded successfully' + ); }); -testInDebug('A sync hasMany errors out if there are unlaoded records in it', function(assert) { +skip('A sync hasMany errors out if there are unloaded records in it', function(assert) { let post = run(() => { env.store.push({ data: { @@ -1947,7 +1966,7 @@ testInDebug('A sync hasMany errors out if there are unlaoded records in it', fun assert.expectAssertion(() => { run(post, 'get', 'comments'); - }, /You looked up the 'comments' relationship on a 'post' with id 1 but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async \('DS.hasMany\({ async: true }\)'\)/); + }, /You looked up the 'comments' relationship on a 'post' with id 1 but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async \(`DS.hasMany\({ async: true }\)`\)/); }); test('After removing and unloading a record, a hasMany relationship should still be valid', function(assert) { diff --git a/tests/integration/relationships/json-api-links-test.js b/tests/integration/relationships/json-api-links-test.js index d92ea3d1879..247f950cbbf 100644 --- a/tests/integration/relationships/json-api-links-test.js +++ b/tests/integration/relationships/json-api-links-test.js @@ -1638,31 +1638,35 @@ test(`get+unload+get hasMany with missing data setup from the other side, no lin attributes: { name: '@runspired' }, - relationships: {} + relationships: {}, }, included: [ { type: 'pet', id: '1', attributes: { - name: 'Shen' + name: 'Shen', }, relationships: { owner: { data: { type: 'user', - id: '1' - } - } - } - } - ] + id: '1', + }, + }, + }, + }, + ], })); + + // should trigger a fetch bc we don't consider `pets` to have complete knowledge let pets = run(() => user.get('pets')); assert.ok(!!pets, 'We found our pets'); run(() => pets.objectAt(0).unloadRecord()); + + // should trigger a findRecord for the unloaded pet run(() => user.get('pets')); }); test(`get+reload belongsTo with missing data setup from the other side, no links`, function(assert) { @@ -1912,12 +1916,12 @@ test('We should not fetch a hasMany relationship with links that we know is empt // should not fire a request requestedUser = null; - failureDescription = 'We fetched the link for a known empty relationship'; + failureDescription = 'We improperly fetched the link for a known empty relationship'; run(() => user1.get('pets')); // still should not fire a request requestedUser = null; - failureDescription = 'We fetched the link (again) for a known empty relationship'; + failureDescription = 'We improperly fetched the link (again) for a known empty relationship'; run(() => user1.get('pets')); // should fire a request @@ -1926,6 +1930,7 @@ test('We should not fetch a hasMany relationship with links that we know is empt // should not fire a request requestedUser = null; - failureDescription = 'We fetched the link for a previously fetched and found to be empty relationship'; + failureDescription = + 'We improperly fetched the link for a previously fetched and found to be empty relationship'; run(() => user2.get('pets')); }); diff --git a/tests/integration/relationships/one-to-one-test.js b/tests/integration/relationships/one-to-one-test.js index c65f848b149..e4b0626cb8c 100644 --- a/tests/integration/relationships/one-to-one-test.js +++ b/tests/integration/relationships/one-to-one-test.js @@ -442,10 +442,18 @@ test("Setting a BelongsTo to a promise unwraps the promise before setting- async run(function() { newFriend.set('bestFriend', stanleysFriend.get('bestFriend')); stanley.get('bestFriend').then(function(fetchedUser) { - assert.equal(fetchedUser, newFriend, 'User relationship was updated correctly'); + assert.equal( + fetchedUser, + newFriend, + `Stanley's bestFriend relationship was updated correctly to newFriend` + ); }); newFriend.get('bestFriend').then(function(fetchedUser) { - assert.equal(fetchedUser, stanley, 'User relationship was updated correctly'); + assert.equal( + fetchedUser, + stanley, + `newFriend's bestFriend relationship was updated correctly to be Stanley` + ); }); }); }); diff --git a/yarn.lock b/yarn.lock index 194ada067a3..ed91d3b00c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,134 @@ # yarn lockfile v1 +"@babel/code-frame@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz#2a02643368de80916162be70865c97774f3adbd9" + dependencies: + "@babel/highlight" "7.0.0-beta.44" + +"@babel/generator@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.44.tgz#c7e67b9b5284afcf69b309b50d7d37f3e5033d42" + dependencies: + "@babel/types" "7.0.0-beta.44" + jsesc "^2.5.1" + lodash "^4.2.0" + source-map "^0.5.0" + trim-right "^1.0.1" + +"@babel/helper-function-name@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.44.tgz#e18552aaae2231100a6e485e03854bc3532d44dd" + dependencies: + "@babel/helper-get-function-arity" "7.0.0-beta.44" + "@babel/template" "7.0.0-beta.44" + "@babel/types" "7.0.0-beta.44" + +"@babel/helper-get-function-arity@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.44.tgz#d03ca6dd2b9f7b0b1e6b32c56c72836140db3a15" + dependencies: + "@babel/types" "7.0.0-beta.44" + +"@babel/helper-split-export-declaration@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz#c0b351735e0fbcb3822c8ad8db4e583b05ebd9dc" + dependencies: + "@babel/types" "7.0.0-beta.44" + +"@babel/highlight@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.44.tgz#18c94ce543916a80553edcdcf681890b200747d5" + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^3.0.0" + +"@babel/template@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f" + dependencies: + "@babel/code-frame" "7.0.0-beta.44" + "@babel/types" "7.0.0-beta.44" + babylon "7.0.0-beta.44" + lodash "^4.2.0" + +"@babel/traverse@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.44.tgz#a970a2c45477ad18017e2e465a0606feee0d2966" + dependencies: + "@babel/code-frame" "7.0.0-beta.44" + "@babel/generator" "7.0.0-beta.44" + "@babel/helper-function-name" "7.0.0-beta.44" + "@babel/helper-split-export-declaration" "7.0.0-beta.44" + "@babel/types" "7.0.0-beta.44" + babylon "7.0.0-beta.44" + debug "^3.1.0" + globals "^11.1.0" + invariant "^2.2.0" + lodash "^4.2.0" + +"@babel/types@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.44.tgz#6b1b164591f77dec0a0342aca995f2d046b3a757" + dependencies: + esutils "^2.0.2" + lodash "^4.2.0" + to-fast-properties "^2.0.0" + +"@ember-decorators/babel-transforms@^2.0.0": + version "2.0.1" + resolved "https://registry.npmjs.org/@ember-decorators/babel-transforms/-/babel-transforms-2.0.1.tgz#75b668cfe996fa920c940ad723be015cddf904db" + dependencies: + babel-plugin-transform-class-properties "^6.24.1" + babel-plugin-transform-decorators-legacy "^1.3.4" + ember-cli-babel "^6.6.0" + ember-cli-version-checker "^2.1.0" + +"@ember-decorators/component@^2.1.0": + version "2.1.0" + resolved "https://registry.npmjs.org/@ember-decorators/component/-/component-2.1.0.tgz#846ad4197a06c9814d02654e947c023d35107cee" + dependencies: + "@ember-decorators/utils" "^2.1.0" + ember-cli-babel "^6.6.0" + +"@ember-decorators/controller@^2.1.0": + version "2.1.0" + resolved "https://registry.npmjs.org/@ember-decorators/controller/-/controller-2.1.0.tgz#57a7be3d4eaa18e5021096d404417b1b943e7124" + dependencies: + "@ember-decorators/utils" "^2.1.0" + ember-cli-babel "^6.6.0" + +"@ember-decorators/data@^2.1.0": + version "2.1.0" + resolved "https://registry.npmjs.org/@ember-decorators/data/-/data-2.1.0.tgz#df29cc4bb21b7f246a02f810e2c7b56bd6f25294" + dependencies: + "@ember-decorators/utils" "^2.1.0" + ember-cli-babel "^6.6.0" + +"@ember-decorators/object@^2.1.0": + version "2.1.0" + resolved "https://registry.npmjs.org/@ember-decorators/object/-/object-2.1.0.tgz#725c790c030299a4bc8c15f21048c3c0abc1499c" + dependencies: + "@ember-decorators/utils" "^2.1.0" + ember-cli-babel "^6.6.0" + ember-compatibility-helpers "^1.0.0" + +"@ember-decorators/service@^2.1.0": + version "2.1.0" + resolved "https://registry.npmjs.org/@ember-decorators/service/-/service-2.1.0.tgz#c6bbfd34fbb9fb19317ab2be351d8577701e5315" + dependencies: + "@ember-decorators/utils" "^2.1.0" + ember-cli-babel "^6.6.0" + +"@ember-decorators/utils@^2.1.0": + version "2.1.0" + resolved "https://registry.npmjs.org/@ember-decorators/utils/-/utils-2.1.0.tgz#cd354ff59ce0d5faa74880c15db0f72fefe6dc70" + dependencies: + ember-cli-babel "^6.6.0" + ember-compatibility-helpers "^1.0.0" + "@ember/ordered-set@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@ember/ordered-set/-/ordered-set-1.0.0.tgz#cf9ab5fd7510bcad370370ebcded705f6d1c542b" @@ -105,9 +233,9 @@ amd-name-resolver@0.0.7: dependencies: ensure-posix-path "^1.0.1" -amd-name-resolver@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/amd-name-resolver/-/amd-name-resolver-1.0.0.tgz#0e593b28d6fa3326ab1798107edaea961046e8d8" +amd-name-resolver@1.2.0, amd-name-resolver@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/amd-name-resolver/-/amd-name-resolver-1.2.0.tgz#fc41b3848824b557313897d71f8d5a0184fbe679" dependencies: ensure-posix-path "^1.0.1" @@ -299,7 +427,7 @@ async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" -async-promise-queue@^1.0.3: +async-promise-queue@^1.0.3, async-promise-queue@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/async-promise-queue/-/async-promise-queue-1.0.4.tgz#308baafbc74aff66a0bb6e7f4a18d4fe8434440c" dependencies: @@ -441,6 +569,17 @@ babel-core@^6.14.0, babel-core@^6.26.0: slash "^1.0.0" source-map "^0.5.6" +babel-eslint@^8.0.0: + version "8.2.3" + resolved "https://registry.npmjs.org/babel-eslint/-/babel-eslint-8.2.3.tgz#1a2e6681cc9bc4473c32899e59915e19cd6733cf" + dependencies: + "@babel/code-frame" "7.0.0-beta.44" + "@babel/traverse" "7.0.0-beta.44" + "@babel/types" "7.0.0-beta.44" + babylon "7.0.0-beta.44" + eslint-scope "~3.7.1" + eslint-visitor-keys "^1.0.0" + babel-generator@^6.26.0: version "6.26.1" resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" @@ -655,6 +794,14 @@ 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-class-properties@^6.8.0: + version "6.13.0" + resolved "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" + +babel-plugin-syntax-decorators@^6.1.18: + version "6.13.0" + resolved "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz#312563b4dbde3cc806cee3e416cceeaddd11ac0b" + 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" @@ -671,6 +818,23 @@ babel-plugin-transform-async-to-generator@^6.22.0: babel-plugin-syntax-async-functions "^6.8.0" babel-runtime "^6.22.0" +babel-plugin-transform-class-properties@^6.24.1: + version "6.24.1" + resolved "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac" + dependencies: + babel-helper-function-name "^6.24.1" + babel-plugin-syntax-class-properties "^6.8.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-decorators-legacy@^1.3.4: + version "1.3.5" + resolved "https://registry.npmjs.org/babel-plugin-transform-decorators-legacy/-/babel-plugin-transform-decorators-legacy-1.3.5.tgz#0e492dffa0edd70529072887f8aa86d4dd8b40a1" + dependencies: + babel-plugin-syntax-decorators "^6.1.18" + babel-runtime "^6.2.0" + babel-template "^6.3.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" @@ -876,7 +1040,7 @@ babel-plugin-undefined-to-void@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/babel-plugin-undefined-to-void/-/babel-plugin-undefined-to-void-1.1.6.tgz#7f578ef8b78dfae6003385d8417a61eda06e2f81" -babel-polyfill@^6.16.0: +babel-polyfill@^6.16.0, babel-polyfill@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153" dependencies: @@ -919,6 +1083,41 @@ babel-preset-env@^1.5.1: invariant "^2.2.2" semver "^5.3.0" +babel-preset-env@^1.7.0: + version "1.7.0" + resolved "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a" + 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 "^3.2.6" + invariant "^2.2.2" + semver "^5.3.0" + babel-register@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" @@ -937,14 +1136,14 @@ babel-runtime@^5.0.0: dependencies: core-js "^1.0.0" -babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: +babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" dependencies: core-js "^2.4.0" regenerator-runtime "^0.11.0" -babel-template@^6.24.1, babel-template@^6.26.0: +babel-template@^6.24.1, babel-template@^6.26.0, babel-template@^6.3.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" dependencies: @@ -985,6 +1184,10 @@ babel6-plugin-strip-heimdall@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/babel6-plugin-strip-heimdall/-/babel6-plugin-strip-heimdall-6.0.1.tgz#35f80eddec1f7fffdc009811dfbd46d9965072b6" +babylon@7.0.0-beta.44: + version "7.0.0-beta.44" + resolved "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz#89159e15e6e30c5096e22d738d8c0af8a0e8ca1d" + babylon@^5.8.38: version "5.8.38" resolved "https://registry.yarnpkg.com/babylon/-/babylon-5.8.38.tgz#ec9b120b11bf6ccd4173a18bf217e60b79859ffd" @@ -1211,11 +1414,20 @@ broccoli-babel-transpiler@^6.0.0, broccoli-babel-transpiler@^6.1.2: rsvp "^3.5.0" workerpool "^2.3.0" -broccoli-brocfile-loader@^0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/broccoli-brocfile-loader/-/broccoli-brocfile-loader-0.18.0.tgz#2e86021c805c34ffc8d29a2fb721cf273e819e4b" +broccoli-babel-transpiler@^6.4.2: + version "6.4.3" + resolved "https://registry.npmjs.org/broccoli-babel-transpiler/-/broccoli-babel-transpiler-6.4.3.tgz#06e399298d41700cdc10d675b1d808a89ef6b2d0" dependencies: - findup-sync "^0.4.2" + babel-core "^6.26.0" + broccoli-funnel "^2.0.1" + broccoli-merge-trees "^2.0.0" + broccoli-persistent-filter "^1.4.3" + clone "^2.0.0" + hash-for-dep "^1.2.3" + heimdalljs-logger "^0.1.7" + json-stable-stringify "^1.0.0" + rsvp "^4.8.2" + workerpool "^2.3.0" broccoli-builder@^0.18.8: version "0.18.11" @@ -1291,7 +1503,7 @@ broccoli-config-replace@^1.1.2: debug "^2.2.0" fs-extra "^0.24.0" -broccoli-debug@^0.6.1, broccoli-debug@^0.6.2, broccoli-debug@^0.6.3: +broccoli-debug@^0.6.1, broccoli-debug@^0.6.2, broccoli-debug@^0.6.3, broccoli-debug@^0.6.4: version "0.6.4" resolved "https://registry.yarnpkg.com/broccoli-debug/-/broccoli-debug-0.6.4.tgz#986eb3d2005e00e3bb91f9d0a10ab137210cd150" dependencies: @@ -1413,9 +1625,9 @@ broccoli-merge-trees@^2.0.0: broccoli-plugin "^1.3.0" merge-trees "^1.0.1" -broccoli-middleware@^1.0.0: +broccoli-middleware@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/broccoli-middleware/-/broccoli-middleware-1.2.1.tgz#a21f255f8bfe5a21c2f0fbf2417addd9d24c9436" + resolved "https://registry.npmjs.org/broccoli-middleware/-/broccoli-middleware-1.2.1.tgz#a21f255f8bfe5a21c2f0fbf2417addd9d24c9436" dependencies: handlebars "^4.0.4" mime-types "^2.1.18" @@ -1424,7 +1636,7 @@ broccoli-node-info@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/broccoli-node-info/-/broccoli-node-info-1.1.0.tgz#3aa2e31e07e5bdb516dd25214f7c45ba1c459412" -broccoli-persistent-filter@^1.0.3, broccoli-persistent-filter@^1.1.5, broccoli-persistent-filter@^1.1.6, broccoli-persistent-filter@^1.2.0, broccoli-persistent-filter@^1.4.0, broccoli-persistent-filter@^1.4.2: +broccoli-persistent-filter@^1.0.3, broccoli-persistent-filter@^1.1.5, broccoli-persistent-filter@^1.1.6, broccoli-persistent-filter@^1.2.0, broccoli-persistent-filter@^1.4.0, broccoli-persistent-filter@^1.4.2, broccoli-persistent-filter@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/broccoli-persistent-filter/-/broccoli-persistent-filter-1.4.3.tgz#3511bc52fc53740cda51621f58a28152d9911bc1" dependencies: @@ -1537,7 +1749,7 @@ broccoli-test-helper@^1.2.0: rimraf "^2.5.4" walk-sync "^0.3.1" -broccoli-uglify-sourcemap@^1.0.0, broccoli-uglify-sourcemap@^1.0.1: +broccoli-uglify-sourcemap@^1.0.1: version "1.5.2" resolved "https://registry.yarnpkg.com/broccoli-uglify-sourcemap/-/broccoli-uglify-sourcemap-1.5.2.tgz#04f84ab0db539031fa868ccfa563c9932d50cedb" dependencies: @@ -1551,6 +1763,22 @@ broccoli-uglify-sourcemap@^1.0.0, broccoli-uglify-sourcemap@^1.0.1: uglify-js "^2.7.0" walk-sync "^0.1.3" +broccoli-uglify-sourcemap@^2.0.0-beta.1: + version "2.2.0" + resolved "https://registry.npmjs.org/broccoli-uglify-sourcemap/-/broccoli-uglify-sourcemap-2.2.0.tgz#2ff49389bdf342a550c3596750ba2dde95a8f7d4" + dependencies: + async-promise-queue "^1.0.4" + broccoli-plugin "^1.2.1" + debug "^3.1.0" + lodash.defaultsdeep "^4.6.0" + matcher-collection "^1.0.5" + mkdirp "^0.5.0" + source-map-url "^0.4.0" + symlink-or-copy "^1.0.1" + terser "^3.7.5" + walk-sync "^0.3.2" + workerpool "^2.3.0" + broccoli-writer@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/broccoli-writer/-/broccoli-writer-0.1.1.tgz#d4d71aa8f2afbc67a3866b91a2da79084b96ab2d" @@ -1595,6 +1823,13 @@ browserslist@^2.1.2: caniuse-lite "^1.0.30000792" electron-to-chromium "^1.3.30" +browserslist@^3.2.6: + version "3.2.8" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz#b0005361d6471f0f5952797a76fc985f1f978fc6" + dependencies: + caniuse-lite "^1.0.30000844" + electron-to-chromium "^1.3.47" + bser@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719" @@ -1696,6 +1931,10 @@ caniuse-lite@^1.0.30000792: version "1.0.30000813" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000813.tgz#7b25e27fdfb8d133f3c932b01f77452140fcc6c9" +caniuse-lite@^1.0.30000844: + version "1.0.30000856" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000856.tgz#ecc16978135a6f219b138991eb62009d25ee8daa" + capture-exit@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-1.2.0.tgz#1c5fcc489fd0ab00d4f1ac7ae1072e3173fbab6f" @@ -1777,7 +2016,7 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" dependencies: @@ -1966,7 +2205,7 @@ commander@2.8.x: dependencies: graceful-readlink ">= 1.0.0" -commander@^2.5.0, commander@^2.6.0: +commander@^2.5.0, commander@^2.6.0, commander@~2.14.1: version "2.14.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa" @@ -2074,9 +2313,9 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" -console-ui@^2.0.0: +console-ui@^2.1.0: version "2.2.2" - resolved "https://registry.yarnpkg.com/console-ui/-/console-ui-2.2.2.tgz#b294a2934de869dd06789ab4be69555411edef29" + resolved "https://registry.npmjs.org/console-ui/-/console-ui-2.2.2.tgz#b294a2934de869dd06789ab4be69555411edef29" dependencies: chalk "^2.1.0" inquirer "^2" @@ -2417,6 +2656,10 @@ electron-to-chromium@^1.3.30: version "1.3.36" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.36.tgz#0eabf71a9ebea9013fb1cc35a390e068624f27e8" +electron-to-chromium@^1.3.47: + version "1.3.49" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.49.tgz#651384b0d81f078a96639b2b36975141b7915004" + ember-cli-app-version@^3.0.0: version "3.1.3" resolved "https://registry.yarnpkg.com/ember-cli-app-version/-/ember-cli-app-version-3.1.3.tgz#26d25f5e653ff0106f0b39da6d75518ba8ed282d" @@ -2452,6 +2695,24 @@ ember-cli-babel@^5.0.0, ember-cli-babel@^5.1.10, ember-cli-babel@^5.1.5, ember-c ember-cli-version-checker "^1.0.2" resolve "^1.1.2" +ember-cli-babel@^6.0.0-beta.4, ember-cli-babel@^6.6.0: + version "6.14.1" + resolved "https://registry.npmjs.org/ember-cli-babel/-/ember-cli-babel-6.14.1.tgz#796339229035910b625593caffbc2683792ada68" + dependencies: + amd-name-resolver "1.2.0" + babel-plugin-debug-macros "^0.1.11" + babel-plugin-ember-modules-api-polyfill "^2.3.0" + babel-plugin-transform-es2015-modules-amd "^6.24.0" + babel-polyfill "^6.26.0" + babel-preset-env "^1.7.0" + broccoli-babel-transpiler "^6.4.2" + broccoli-debug "^0.6.4" + broccoli-funnel "^2.0.0" + broccoli-source "^1.1.0" + clone "^2.0.0" + ember-cli-version-checker "^2.1.2" + semver "^5.5.0" + ember-cli-blueprint-test-helpers@^0.18.3: version "0.18.3" resolved "https://registry.yarnpkg.com/ember-cli-blueprint-test-helpers/-/ember-cli-blueprint-test-helpers-0.18.3.tgz#945c606d855f0263f5e8c03522e4040a74f259cc" @@ -2496,10 +2757,6 @@ ember-cli-get-component-path-option@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/ember-cli-get-component-path-option/-/ember-cli-get-component-path-option-1.0.0.tgz#0d7b595559e2f9050abed804f1d8eff1b08bc771" -ember-cli-get-dependency-depth@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/ember-cli-get-dependency-depth/-/ember-cli-get-dependency-depth-1.0.0.tgz#e0afecf82a2d52f00f28ab468295281aec368d11" - ember-cli-htmlbars-inline-precompile@^0.4.3: version "0.4.4" resolved "https://registry.yarnpkg.com/ember-cli-htmlbars-inline-precompile/-/ember-cli-htmlbars-inline-precompile-0.4.4.tgz#24a7617152630d64a047e553b72e00963a4f8d73" @@ -2571,28 +2828,6 @@ ember-cli-is-package-missing@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/ember-cli-is-package-missing/-/ember-cli-is-package-missing-1.0.0.tgz#6e6184cafb92635dd93ca6c946b104292d4e3390" -ember-cli-legacy-blueprints@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/ember-cli-legacy-blueprints/-/ember-cli-legacy-blueprints-0.2.1.tgz#480f37cb83f1eda2d46bbc7d07c59ea2e8ce9b84" - dependencies: - chalk "^2.3.0" - ember-cli-get-component-path-option "^1.0.0" - ember-cli-get-dependency-depth "^1.0.0" - ember-cli-is-package-missing "^1.0.0" - ember-cli-lodash-subset "^2.0.1" - ember-cli-normalize-entity-name "^1.0.0" - ember-cli-path-utils "^1.0.0" - ember-cli-string-utils "^1.0.0" - ember-cli-test-info "^1.0.0" - ember-cli-valid-component-name "^1.0.0" - ember-cli-version-checker "^2.1.0" - ember-router-generator "^1.0.0" - exists-sync "0.0.3" - fs-extra "^4.0.0" - inflection "^1.7.1" - rsvp "^4.7.0" - silent-error "^1.0.0" - ember-cli-lodash-subset@^1.0.7: version "1.0.12" resolved "https://registry.yarnpkg.com/ember-cli-lodash-subset/-/ember-cli-lodash-subset-1.0.12.tgz#af2e77eba5dcb0d77f3308d3a6fd7d3450f6e537" @@ -2685,11 +2920,12 @@ ember-cli-test-loader@^2.2.0: dependencies: ember-cli-babel "^6.8.1" -ember-cli-uglify@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/ember-cli-uglify/-/ember-cli-uglify-1.2.0.tgz#3208c32b54bc2783056e8bb0d5cfe9bbaf17ffb2" +ember-cli-uglify@2.0.0-beta.1: + version "2.0.0-beta.1" + resolved "https://registry.npmjs.org/ember-cli-uglify/-/ember-cli-uglify-2.0.0-beta.1.tgz#9cb509f7f3f3342271df4a59b41176f0ffbda48a" dependencies: - broccoli-uglify-sourcemap "^1.0.0" + broccoli-uglify-sourcemap "^2.0.0-beta.1" + lodash.defaultsdeep "^4.6.0" ember-cli-valid-component-name@^1.0.0: version "1.0.0" @@ -2710,16 +2946,22 @@ ember-cli-version-checker@^2.0.0, ember-cli-version-checker@^2.1.0: resolve "^1.3.3" semver "^5.3.0" -ember-cli@^2.11.1: - version "2.18.2" - resolved "https://registry.yarnpkg.com/ember-cli/-/ember-cli-2.18.2.tgz#bb15313a15139a85248a86d203643f918ba40f57" +ember-cli-version-checker@^2.1.1, ember-cli-version-checker@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/ember-cli-version-checker/-/ember-cli-version-checker-2.1.2.tgz#305ce102390c66e4e0f1432dea9dc5c7c19fed98" + dependencies: + resolve "^1.3.3" + semver "^5.3.0" + +ember-cli@^3.1.4: + version "3.1.4" + resolved "https://registry.npmjs.org/ember-cli/-/ember-cli-3.1.4.tgz#95f7ff4302d535619b5d5ff1c7040877a67d4468" dependencies: - amd-name-resolver "1.0.0" + amd-name-resolver "^1.2.0" babel-plugin-transform-es2015-modules-amd "^6.24.0" bower-config "^1.3.0" bower-endpoint-parser "0.2.2" broccoli-babel-transpiler "^6.0.0" - broccoli-brocfile-loader "^0.18.0" broccoli-builder "^0.18.8" broccoli-concat "^3.2.2" broccoli-config-loader "^1.0.0" @@ -2728,7 +2970,7 @@ ember-cli@^2.11.1: broccoli-funnel "^2.0.0" broccoli-funnel-reducer "^1.0.0" broccoli-merge-trees "^2.0.0" - broccoli-middleware "^1.0.0" + broccoli-middleware "^1.2.1" broccoli-source "^1.1.0" broccoli-stew "^1.2.0" calculate-cache-key-for-tree "^1.0.0" @@ -2737,32 +2979,31 @@ ember-cli@^2.11.1: clean-base-url "^1.0.0" compression "^1.4.4" configstore "^3.0.0" - console-ui "^2.0.0" + console-ui "^2.1.0" core-object "^3.1.3" dag-map "^2.0.2" diff "^3.2.0" ember-cli-broccoli-sane-watcher "^2.0.4" ember-cli-is-package-missing "^1.0.0" - ember-cli-legacy-blueprints "^0.2.0" ember-cli-lodash-subset "^2.0.1" ember-cli-normalize-entity-name "^1.0.0" ember-cli-preprocess-registry "^3.1.0" ember-cli-string-utils "^1.0.0" - ember-try "^0.2.15" ensure-posix-path "^1.0.2" - execa "^0.8.0" + execa "^0.9.0" exists-sync "0.0.4" exit "^0.1.2" express "^4.12.3" filesize "^3.1.3" find-up "^2.1.0" - fs-extra "^4.0.0" + find-yarn-workspace-root "^1.0.0" + fs-extra "^5.0.0" fs-tree-diff "^0.5.2" get-caller-file "^1.0.0" git-repo-info "^1.4.1" - glob "7.1.1" + glob "^7.1.2" heimdalljs "^0.2.3" - heimdalljs-fs-monitor "^0.1.0" + heimdalljs-fs-monitor "^0.2.0" heimdalljs-graph "^0.3.1" heimdalljs-logger "^0.1.7" http-proxy "^1.9.0" @@ -2797,7 +3038,16 @@ ember-cli@^2.11.1: uuid "^3.0.0" validate-npm-package-name "^3.0.0" walk-sync "^0.3.0" - yam "0.0.22" + watch-detector "^0.1.0" + yam "^0.0.24" + +ember-compatibility-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/ember-compatibility-helpers/-/ember-compatibility-helpers-1.0.0.tgz#616b22d2e14b4c6a1f4441e5390a49abf52b3c68" + dependencies: + babel-plugin-debug-macros "^0.1.11" + ember-cli-version-checker "^2.1.1" + semver "^5.4.1" ember-compatibility-helpers@^1.0.0-beta.2: version "1.0.0-beta.2" @@ -2807,6 +3057,18 @@ ember-compatibility-helpers@^1.0.0-beta.2: ember-cli-version-checker "^2.0.0" semver "^5.4.1" +ember-decorators@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/ember-decorators/-/ember-decorators-2.1.0.tgz#4910835da3517c633532c8d6fcfcf026e13701c0" + dependencies: + "@ember-decorators/component" "^2.1.0" + "@ember-decorators/controller" "^2.1.0" + "@ember-decorators/data" "^2.1.0" + "@ember-decorators/object" "^2.1.0" + "@ember-decorators/service" "^2.1.0" + ember-cli-babel "^6.0.0" + semver "^5.5.0" + ember-disable-prototype-extensions@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/ember-disable-prototype-extensions/-/ember-disable-prototype-extensions-1.1.3.tgz#1969135217654b5e278f9fe2d9d4e49b5720329e" @@ -2835,6 +3097,15 @@ ember-load-initializers@^0.6.0: dependencies: ember-cli-babel "^5.1.6" +ember-maybe-import-regenerator@^0.1.6: + version "0.1.6" + resolved "https://registry.npmjs.org/ember-maybe-import-regenerator/-/ember-maybe-import-regenerator-0.1.6.tgz#35d41828afa6d6a59bc0da3ce47f34c573d776ca" + dependencies: + broccoli-funnel "^1.0.1" + broccoli-merge-trees "^1.0.0" + ember-cli-babel "^6.0.0-beta.4" + regenerator-runtime "^0.9.5" + ember-publisher@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ember-publisher/-/ember-publisher-0.0.7.tgz#865ca69ef47bafb38120a84244aa58b950ca4850" @@ -2880,7 +3151,7 @@ ember-rfc176-data@^0.3.0, ember-rfc176-data@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.3.1.tgz#6a5a4b8b82ec3af34f3010965fa96b936ca94519" -ember-router-generator@^1.0.0, ember-router-generator@^1.2.3: +ember-router-generator@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/ember-router-generator/-/ember-router-generator-1.2.3.tgz#8ed2ca86ff323363120fc14278191e9e8f1315ee" dependencies: @@ -2926,7 +3197,7 @@ ember-try-config@^2.2.0: rsvp "^3.2.1" semver "^5.1.0" -ember-try@^0.2.15, ember-try@^0.2.23: +ember-try@^0.2.23: version "0.2.23" resolved "https://registry.yarnpkg.com/ember-try/-/ember-try-0.2.23.tgz#39b57141b4907541d0ac8b503d211e6946b08718" dependencies: @@ -3098,6 +3369,39 @@ escope@^3.6.0: esrecurse "^4.1.0" estraverse "^4.1.1" +eslint-config-prettier@^2.9.0: + version "2.9.0" + resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-2.9.0.tgz#5ecd65174d486c22dff389fe036febf502d468a3" + dependencies: + get-stdin "^5.0.1" + +eslint-plugin-node@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-6.0.1.tgz#bf19642298064379315d7a4b2a75937376fa05e4" + dependencies: + ignore "^3.3.6" + minimatch "^3.0.4" + resolve "^1.3.3" + semver "^5.4.1" + +eslint-plugin-prettier@^2.6.0: + version "2.6.0" + resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.6.0.tgz#33e4e228bdb06142d03c560ce04ec23f6c767dd7" + dependencies: + fast-diff "^1.1.1" + jest-docblock "^21.0.0" + +eslint-scope@~3.7.1: + version "3.7.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-visitor-keys@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" + eslint@^2.13.0: version "2.13.1" resolved "https://registry.yarnpkg.com/eslint/-/eslint-2.13.1.tgz#e4cc8fa0f009fb829aaae23855a29360be1f6c11" @@ -3222,18 +3526,6 @@ exec-sh@^0.2.0: dependencies: merge "^1.1.3" -execa@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.9.0.tgz#adb7ce62cf985071f60580deb4a88b9e34712d01" @@ -3393,6 +3685,10 @@ fake-xml-http-request@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/fake-xml-http-request/-/fake-xml-http-request-1.6.0.tgz#bd0ac79ae3e2660098282048a12c730a6f64d550" +fast-diff@^1.1.1: + version "1.1.2" + resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154" + fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" @@ -3504,6 +3800,13 @@ find-up@^2.1.0: dependencies: locate-path "^2.0.0" +find-yarn-workspace-root@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-1.1.0.tgz#9817b6748cb90719f4dc37b4538bb200c697356f" + dependencies: + fs-extra "^4.0.3" + micromatch "^3.1.4" + findup-sync@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" @@ -3513,15 +3816,6 @@ findup-sync@2.0.0: micromatch "^3.0.4" resolve-dir "^1.0.1" -findup-sync@^0.4.2: - version "0.4.3" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.4.3.tgz#40043929e7bc60adf0b7f4827c4c6e75a0deca12" - dependencies: - detect-file "^0.1.0" - is-glob "^2.0.1" - micromatch "^2.3.7" - resolve-dir "^0.1.0" - findup-sync@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-1.0.0.tgz#6f7e4b57b6ee3a4037b4414eaedea3f58f71e0ec" @@ -3667,9 +3961,17 @@ fs-extra@^2.0.0: graceful-fs "^4.1.2" jsonfile "^2.1.0" -fs-extra@^4.0.0: +fs-extra@^4.0.0, fs-extra@^4.0.2, fs-extra@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" dependencies: graceful-fs "^4.1.2" jsonfile "^4.0.0" @@ -3761,6 +4063,10 @@ get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" +get-stdin@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" + get-stream@3.0.0, get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -3819,17 +4125,6 @@ glob@3.2.11: inherits "2" minimatch "0.3" -glob@7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.2" - once "^1.3.0" - path-is-absolute "^1.0.0" - glob@^4.3.2: version "4.5.3" resolved "https://registry.yarnpkg.com/glob/-/glob-4.5.3.tgz#c6cb73d3226c1efef04de3c56d012f03377ee15f" @@ -3849,7 +4144,7 @@ glob@^5.0.10, glob@^5.0.13, glob@^5.0.15: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.4, glob@^7.0.5, glob@^7.1.0: +glob@^7.0.0, glob@^7.0.3, glob@^7.0.4, glob@^7.0.5, glob@^7.1.0, glob@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -3894,6 +4189,10 @@ global-prefix@^1.0.1: is-windows "^1.0.1" which "^1.2.14" +globals@^11.1.0: + version "11.7.0" + resolved "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673" + globals@^6.4.0: version "6.4.1" resolved "https://registry.yarnpkg.com/globals/-/globals-6.4.1.tgz#8498032b3b6d1cc81eebc5f79690d8fe29fabf4f" @@ -4045,7 +4344,7 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" -hash-for-dep@^1.0.2: +hash-for-dep@^1.0.2, hash-for-dep@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/hash-for-dep/-/hash-for-dep-1.2.3.tgz#5ec69fca32c23523972d52acb5bb65ffc3664cab" dependencies: @@ -4072,9 +4371,9 @@ hawk@3.1.3, hawk@~3.1.3: hoek "2.x.x" sntp "1.x.x" -heimdalljs-fs-monitor@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/heimdalljs-fs-monitor/-/heimdalljs-fs-monitor-0.1.0.tgz#d404a65688c6714c485469ed3495da4853440272" +heimdalljs-fs-monitor@^0.2.0: + version "0.2.1" + resolved "https://registry.npmjs.org/heimdalljs-fs-monitor/-/heimdalljs-fs-monitor-0.2.1.tgz#b4079cfb85fb8326b8c75a7538fdbfa3d8afaa63" dependencies: heimdalljs "^0.2.0" heimdalljs-logger "^0.1.7" @@ -4083,7 +4382,7 @@ heimdalljs-graph@^0.3.1: version "0.3.4" resolved "https://registry.yarnpkg.com/heimdalljs-graph/-/heimdalljs-graph-0.3.4.tgz#0bd75797beeaa20b0ed59017aed3b2d95312acee" -heimdalljs-logger@^0.1.7: +heimdalljs-logger@^0.1.7, heimdalljs-logger@^0.1.9: version "0.1.9" resolved "https://registry.yarnpkg.com/heimdalljs-logger/-/heimdalljs-logger-0.1.9.tgz#d76ada4e45b7bb6f786fc9c010a68eb2e2faf176" dependencies: @@ -4194,6 +4493,10 @@ ignore@^3.1.2: version "3.3.7" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021" +ignore@^3.3.6: + version "3.3.10" + resolved "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -4212,7 +4515,7 @@ inflected@^1.1.6: version "1.1.7" resolved "https://registry.yarnpkg.com/inflected/-/inflected-1.1.7.tgz#c393df6e28472d0d77b3082ec3aa2091f4bc96f9" -inflection@^1.12.0, inflection@^1.7.0, inflection@^1.7.1, inflection@^1.8.0: +inflection@^1.12.0, inflection@^1.7.0, inflection@^1.8.0: version "1.12.0" resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.12.0.tgz#a200935656d6f5f6bc4dc7502e1aecb703228416" @@ -4285,6 +4588,12 @@ into-stream@^3.1.0: from2 "^2.1.1" p-is-promise "^1.1.0" +invariant@^2.2.0: + version "2.2.4" + resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + dependencies: + loose-envify "^1.0.0" + invariant@^2.2.2: version "2.2.3" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.3.tgz#1a827dfde7dcbd7c323f0ca826be8fa7c5e9d688" @@ -4601,6 +4910,10 @@ jade@0.26.3: commander "0.6.1" mkdirp "0.3.0" +jest-docblock@^21.0.0: + version "21.2.0" + resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414" + jmespath@0.15.0: version "0.15.0" resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" @@ -4640,6 +4953,10 @@ jsesc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" +jsesc@^2.5.1: + version "2.5.1" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz#e421a2a8e20d6b0819df28908f782526b96dd1fe" + jsesc@~0.3.x: version "0.3.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.3.0.tgz#1bf5ee63b4539fe2e26d0c1e99c240b97a457972" @@ -4986,6 +5303,10 @@ lodash.defaults@~2.3.0: lodash._objecttypes "~2.3.0" lodash.keys "~2.3.0" +lodash.defaultsdeep@^4.6.0: + version "4.6.0" + resolved "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.0.tgz#bec1024f85b1bd96cbea405b23c14ad6443a6f81" + lodash.escape@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-2.3.0.tgz#844c38c58f844e1362ebe96726159b62cf5f2a58" @@ -5058,7 +5379,7 @@ lodash.keys@~2.3.0: lodash._shimkeys "~2.3.0" lodash.isobject "~2.3.0" -lodash.merge@^4.3.0, lodash.merge@^4.4.0, lodash.merge@^4.5.1, lodash.merge@^4.6.0: +lodash.merge@^4.3.0, lodash.merge@^4.5.1, lodash.merge@^4.6.0: version "4.6.1" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54" @@ -5134,6 +5455,10 @@ lodash@^4.0.0, lodash@^4.14.0, lodash@^4.16.1, lodash@^4.17.2, lodash@^4.17.4, l version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" +lodash@^4.2.0: + version "4.17.10" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + log-symbols@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" @@ -5232,7 +5557,7 @@ markdown-it@^8.3.0, markdown-it@^8.3.1: mdurl "^1.0.1" uc.micro "^1.0.5" -matcher-collection@^1.0.0, matcher-collection@^1.0.4: +matcher-collection@^1.0.0, matcher-collection@^1.0.4, matcher-collection@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/matcher-collection/-/matcher-collection-1.0.5.tgz#2ee095438372cb8884f058234138c05c644ec339" dependencies: @@ -5340,6 +5665,24 @@ micromatch@^3.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" +micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + "mime-db@>= 1.33.0 < 2", mime-db@~1.33.0: version "1.33.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" @@ -5965,6 +6308,10 @@ pretender@^1.4.2: fake-xml-http-request "^1.6.0" route-recognizer "^0.3.3" +prettier@^1.12.1: + version "1.13.5" + resolved "https://registry.npmjs.org/prettier/-/prettier-1.13.5.tgz#7ae2076998c8edce79d63834e9b7b09fead6bfd0" + printf@^0.2.3: version "0.2.5" resolved "https://registry.yarnpkg.com/printf/-/printf-0.2.5.tgz#c438ca2ca33e3927671db4ab69c0e52f936a4f0f" @@ -6225,6 +6572,10 @@ regenerator-runtime@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" +regenerator-runtime@^0.9.5: + version "0.9.6" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.9.6.tgz#d33eb95d0d2001a4be39659707c51b0cb71ce029" + regenerator-transform@^0.10.0: version "0.10.1" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" @@ -6458,9 +6809,9 @@ rsvp@^3.0.14, rsvp@^3.0.16, rsvp@^3.0.17, rsvp@^3.0.18, rsvp@^3.0.21, rsvp@^3.0. version "3.6.2" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a" -rsvp@^4.7.0: +rsvp@^4.7.0, rsvp@^4.8.2: version "4.8.2" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.2.tgz#9d5647108735784eb13418cdddb56f75b919d722" + resolved "https://registry.npmjs.org/rsvp/-/rsvp-4.8.2.tgz#9d5647108735784eb13418cdddb56f75b919d722" rsvp@~3.0.6: version "3.0.21" @@ -6538,7 +6889,7 @@ sax@>=0.6.0: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" -"semver@2 || 3 || 4 || 5", semver@^5.4.1: +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" @@ -6828,6 +7179,10 @@ source-map@~0.1.x: dependencies: amdefine ">=0.0.4" +source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + sourcemap-validator@^1.0.5: version "1.0.7" resolved "https://registry.yarnpkg.com/sourcemap-validator/-/sourcemap-validator-1.0.7.tgz#d76aaadbe2c6ec269293b5f212100fad91eef260" @@ -7107,6 +7462,13 @@ temp@0.8.3: os-tmpdir "^1.0.0" rimraf "~2.2.6" +terser@^3.7.5: + version "3.7.6" + resolved "https://registry.npmjs.org/terser/-/terser-3.7.6.tgz#0b3c609f22278c089780ac1cdc63627071e3b96a" + dependencies: + commander "~2.14.1" + source-map "~0.6.1" + testdouble@^3.2.6: version "3.5.2" resolved "https://registry.yarnpkg.com/testdouble/-/testdouble-3.5.2.tgz#7ac91d08be05bac3b2acba57c430f6c62d0ad8af" @@ -7243,6 +7605,10 @@ to-fast-properties@^1.0.0, to-fast-properties@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + to-iso-string@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/to-iso-string/-/to-iso-string-0.0.2.tgz#4dc19e664dfccbe25bd8db508b00c6da158255d1" @@ -7260,7 +7626,7 @@ to-regex-range@^2.1.0: is-number "^3.0.0" repeat-string "^1.6.1" -to-regex@^3.0.1: +to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" dependencies: @@ -7518,7 +7884,7 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -walk-sync@0.3.2, walk-sync@^0.3.0, walk-sync@^0.3.1: +walk-sync@0.3.2, walk-sync@^0.3.0, walk-sync@^0.3.1, walk-sync@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/walk-sync/-/walk-sync-0.3.2.tgz#4827280afc42d0e035367c4a4e31eeac0d136f75" dependencies: @@ -7542,6 +7908,16 @@ walker@~1.0.5: dependencies: makeerror "1.0.x" +watch-detector@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/watch-detector/-/watch-detector-0.1.0.tgz#e37b410d149e2a8bf263a4f8b71e2f667633dbf8" + dependencies: + heimdalljs-logger "^0.1.9" + quick-temp "^0.1.8" + rsvp "^4.7.0" + semver "^5.4.1" + silent-error "^1.1.0" + watch@~0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/watch/-/watch-0.10.0.tgz#77798b2da0f9910d595f1ace5b0c2258521f21dc" @@ -7688,12 +8064,12 @@ yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" -yam@0.0.22: - version "0.0.22" - resolved "https://registry.yarnpkg.com/yam/-/yam-0.0.22.tgz#38a76cb79a19284d9206ed49031e359a1340bd06" +yam@^0.0.24: + version "0.0.24" + resolved "https://registry.npmjs.org/yam/-/yam-0.0.24.tgz#11e9630444735f66a561d29221407de6d037cd95" dependencies: - fs-extra "^0.30.0" - lodash.merge "^4.4.0" + fs-extra "^4.0.2" + lodash.merge "^4.6.0" yargs@~3.10.0: version "3.10.0"