import { peekMeta, meta } from '../@ember/-internals/meta/lib/meta.js';
import { f as setupMandatorySetter, e as isObject, d as setListeners, h as setWithMandatorySetter } from './mandatory-setter-CXNsxygN.js';
import { isDevelopingApp } from '@embroider/macros';
import { warn, debug } from '../@ember/debug/index.js';
import { isDestroyed, registerDestructor } from '../@glimmer/destroyable/index.js';
import { tagFor, dirtyTagFor, CONSTANT_TAG, combine, validateTag, createUpdatableTag, tagMetaFor, updateTag as UPDATE_TAG, valueForTag, CURRENT_TAG, untrack, ALLOW_CYCLES, consumeTag, track, isTracking, trackedData } from '../@glimmer/validator/index.js';
import { getCustomTagFor } from '../@glimmer/manager/index.js';
import { E as ENV } from './env-DxZ20QzS.js';
import { assert } from '../@ember/debug/lib/assert.js';
import { t as toString, s as symbol } from './to-string-DaIn5NlY.js';
import { s as setProxy } from './is_proxy-DxRbm8wn.js';
import { isEmberArray } from '../@ember/array/-internals.js';
import { C as Cache } from './cache-qDyqAcpg.js';
import Version from '../ember/version.js';
import { getOwner } from '../@ember/-internals/owner/index.js';
import inspect from '../@ember/debug/lib/inspect.js';

function objectAt(array, index) {
  if (Array.isArray(array)) {
    return array[index];
  } else {
    return array.objectAt(index);
  }
}

// This is exported for `@tracked`, but should otherwise be avoided. Use `tagForObject`.
const SELF_TAG = symbol('SELF_TAG');
function tagForProperty(obj, propertyKey, addMandatorySetter = false, meta) {
  let customTagFor = getCustomTagFor(obj);
  if (customTagFor !== undefined) {
    return customTagFor(obj, propertyKey, addMandatorySetter);
  }
  let tag = tagFor(obj, propertyKey, meta);
  if (isDevelopingApp() && addMandatorySetter) {
    setupMandatorySetter(tag, obj, propertyKey);
  }
  return tag;
}
function tagForObject(obj) {
  if (isObject(obj)) {
    if (isDevelopingApp()) {
      (isDevelopingApp() && !(!isDestroyed(obj)) && assert(isDestroyed(obj) ? `Cannot create a new tag for \`${toString(obj)}\` after it has been destroyed.` : '', !isDestroyed(obj)));
    }
    return tagFor(obj, SELF_TAG);
  }
  return CONSTANT_TAG;
}
function markObjectAsDirty(obj, propertyKey) {
  dirtyTagFor(obj, propertyKey);
  dirtyTagFor(obj, SELF_TAG);
}

const CHAIN_PASS_THROUGH = new WeakSet();
function finishLazyChains(meta, key, value) {
  let lazyTags = meta.readableLazyChainsFor(key);
  if (lazyTags === undefined) {
    return;
  }
  if (isObject(value)) {
    for (let [tag, deps] of lazyTags) {
      UPDATE_TAG(tag, getChainTagsForKey(value, deps, tagMetaFor(value), peekMeta(value)));
    }
  }
  lazyTags.length = 0;
}
function getChainTagsForKeys(obj, keys, tagMeta, meta) {
  let tags = [];
  for (let key of keys) {
    getChainTags(tags, obj, key, tagMeta, meta);
  }
  return combine(tags);
}
function getChainTagsForKey(obj, key, tagMeta, meta) {
  return combine(getChainTags([], obj, key, tagMeta, meta));
}
function getChainTags(chainTags, obj, path, tagMeta, meta$1) {
  let current = obj;
  let currentTagMeta = tagMeta;
  let currentMeta = meta$1;
  let pathLength = path.length;
  let segmentEnd = -1;
  // prevent closures
  let segment, descriptor;
  while (true) {
    let lastSegmentEnd = segmentEnd + 1;
    segmentEnd = path.indexOf('.', lastSegmentEnd);
    if (segmentEnd === -1) {
      segmentEnd = pathLength;
    }
    segment = path.slice(lastSegmentEnd, segmentEnd);

    // If the segment is an @each, we can process it and then break
    if (segment === '@each' && segmentEnd !== pathLength) {
      lastSegmentEnd = segmentEnd + 1;
      segmentEnd = path.indexOf('.', lastSegmentEnd);
      let arrLength = current.length;
      if (typeof arrLength !== 'number' ||
      // TODO: should the second test be `isEmberArray` instead?
      !(Array.isArray(current) || 'objectAt' in current)) {
        // If the current object isn't an array, there's nothing else to do,
        // we don't watch individual properties. Break out of the loop.
        break;
      } else if (arrLength === 0) {
        // Fast path for empty arrays
        chainTags.push(tagForProperty(current, '[]'));
        break;
      }
      if (segmentEnd === -1) {
        segment = path.slice(lastSegmentEnd);
      } else {
        // Deprecated, remove once we turn the deprecation into an assertion
        segment = path.slice(lastSegmentEnd, segmentEnd);
      }

      // Push the tags for each item's property
      for (let i = 0; i < arrLength; i++) {
        let item = objectAt(current, i);
        if (item) {
          (isDevelopingApp() && !(typeof item === 'object') && assert(`When using @each to observe the array \`${current.toString()}\`, the items in the array must be objects`, typeof item === 'object'));
          chainTags.push(tagForProperty(item, segment, true));
          currentMeta = peekMeta(item);
          descriptor = currentMeta !== null ? currentMeta.peekDescriptors(segment) : undefined;

          // If the key is an alias, we need to bootstrap it
          if (descriptor !== undefined && typeof descriptor.altKey === 'string') {
            // eslint-disable-next-line @typescript-eslint/no-unused-expressions
            item[segment];
          }
        }
      }

      // Push the tag for the array length itself
      chainTags.push(tagForProperty(current, '[]', true, currentTagMeta));
      break;
    }
    let propertyTag = tagForProperty(current, segment, true, currentTagMeta);
    descriptor = currentMeta !== null ? currentMeta.peekDescriptors(segment) : undefined;
    chainTags.push(propertyTag);

    // If we're at the end of the path, processing the last segment, and it's
    // not an alias, we should _not_ get the last value, since we already have
    // its tag. There's no reason to access it and do more work.
    if (segmentEnd === pathLength) {
      // If the key was an alias, we should always get the next value in order to
      // bootstrap the alias. This is because aliases, unlike other CPs, should
      // always be in sync with the aliased value.
      if (CHAIN_PASS_THROUGH.has(descriptor)) {
        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
        current[segment];
      }
      break;
    }
    if (descriptor === undefined) {
      // If the descriptor is undefined, then its a normal property, so we should
      // lookup the value to chain off of like normal.

      if (!(segment in current) && typeof current.unknownProperty === 'function') {
        current = current.unknownProperty(segment);
      } else {
        current = current[segment];
      }
    } else if (CHAIN_PASS_THROUGH.has(descriptor)) {
      current = current[segment];
    } else {
      // If the descriptor is defined, then its a normal CP (not an alias, which
      // would have been handled earlier). We get the last revision to check if
      // the CP is still valid, and if so we use the cached value. If not, then
      // we create a lazy chain lookup, and the next time the CP is calculated,
      // it will update that lazy chain.
      let instanceMeta = currentMeta.source === current ? currentMeta : meta(current);
      let lastRevision = instanceMeta.revisionFor(segment);
      if (lastRevision !== undefined && validateTag(propertyTag, lastRevision)) {
        current = instanceMeta.valueFor(segment);
      } else {
        // use metaFor here to ensure we have the meta for the instance
        let lazyChains = instanceMeta.writableLazyChainsFor(segment);
        let rest = path.substring(segmentEnd + 1);
        let placeholderTag = createUpdatableTag();
        lazyChains.push([placeholderTag, rest]);
        chainTags.push(placeholderTag);
        break;
      }
    }
    if (!isObject(current)) {
      // we've hit the end of the chain for now, break out
      break;
    }
    currentTagMeta = tagMetaFor(current);
    currentMeta = peekMeta(current);
  }
  return chainTags;
}

function isElementDescriptor(args) {
  let [maybeTarget, maybeKey, maybeDesc] = args;
  return (
    // Ensure we have the right number of args
    args.length === 3 && (
    // Make sure the target is a class or object (prototype)
    typeof maybeTarget === 'function' || typeof maybeTarget === 'object' && maybeTarget !== null) &&
    // Make sure the key is a string
    typeof maybeKey === 'string' && (
    // Make sure the descriptor is the right shape
    typeof maybeDesc === 'object' && maybeDesc !== null || maybeDesc === undefined)
  );
}
function nativeDescDecorator(propertyDesc) {
  let decorator = function () {
    return propertyDesc;
  };
  setClassicDecorator(decorator);
  return decorator;
}

/**
  Objects of this type can implement an interface to respond to requests to
  get and set. The default implementation handles simple properties.

  @class Descriptor
  @private
*/
class ComputedDescriptor {
  enumerable = true;
  configurable = true;
  _dependentKeys = undefined;
  _meta = undefined;
  setup(_obj, keyName, _propertyDesc, meta) {
    meta.writeDescriptors(keyName, this);
  }
  teardown(_obj, keyName, meta) {
    meta.removeDescriptors(keyName);
  }
}
let COMPUTED_GETTERS;
if (isDevelopingApp()) {
  COMPUTED_GETTERS = new WeakSet();
}
function DESCRIPTOR_GETTER_FUNCTION(name, descriptor) {
  function getter() {
    return descriptor.get(this, name);
  }
  if (isDevelopingApp()) {
    COMPUTED_GETTERS.add(getter);
  }
  return getter;
}
function DESCRIPTOR_SETTER_FUNCTION(name, descriptor) {
  let set = function CPSETTER_FUNCTION(value) {
    return descriptor.set(this, name, value);
  };
  COMPUTED_SETTERS.add(set);
  return set;
}
const COMPUTED_SETTERS = new WeakSet();
function makeComputedDecorator(desc, DecoratorClass) {
  let decorator = function COMPUTED_DECORATOR(target, key, propertyDesc, maybeMeta, isClassicDecorator) {
    (isDevelopingApp() && !(isClassicDecorator || !propertyDesc || !propertyDesc.get || !COMPUTED_GETTERS.has(propertyDesc.get)) && assert(`Only one computed property decorator can be applied to a class field or accessor, but '${key}' was decorated twice. You may have added the decorator to both a getter and setter, which is unnecessary.`, isClassicDecorator || !propertyDesc || !propertyDesc.get || !COMPUTED_GETTERS.has(propertyDesc.get)));
    let meta$1 = arguments.length === 3 ? meta(target) : maybeMeta;
    desc.setup(target, key, propertyDesc, meta$1);
    let computedDesc = {
      enumerable: desc.enumerable,
      configurable: desc.configurable,
      get: DESCRIPTOR_GETTER_FUNCTION(key, desc),
      set: DESCRIPTOR_SETTER_FUNCTION(key, desc)
    };
    return computedDesc;
  };
  setClassicDecorator(decorator, desc);
  Object.setPrototypeOf(decorator, DecoratorClass.prototype);
  return decorator;
}

/////////////

const DECORATOR_DESCRIPTOR_MAP = new WeakMap();

/**
  Returns the CP descriptor associated with `obj` and `keyName`, if any.

  @method descriptorForProperty
  @param {Object} obj the object to check
  @param {String} keyName the key to check
  @return {Descriptor}
  @private
*/
function descriptorForProperty(obj, keyName, _meta) {
  (isDevelopingApp() && !(obj !== null) && assert('Cannot call `descriptorForProperty` on null', obj !== null));
  (isDevelopingApp() && !(obj !== undefined) && assert('Cannot call `descriptorForProperty` on undefined', obj !== undefined));
  (isDevelopingApp() && !(typeof obj === 'object' || typeof obj === 'function') && assert(`Cannot call \`descriptorForProperty\` on ${typeof obj}`, typeof obj === 'object' || typeof obj === 'function'));
  let meta = _meta === undefined ? peekMeta(obj) : _meta;
  if (meta !== null) {
    return meta.peekDescriptors(keyName);
  }
}
function descriptorForDecorator(dec) {
  return DECORATOR_DESCRIPTOR_MAP.get(dec);
}

/**
  Check whether a value is a decorator

  @method isClassicDecorator
  @param {any} possibleDesc the value to check
  @return {boolean}
  @private
*/
function isClassicDecorator(dec) {
  return typeof dec === 'function' && DECORATOR_DESCRIPTOR_MAP.has(dec);
}

/**
  Set a value as a decorator

  @method setClassicDecorator
  @param {function} decorator the value to mark as a decorator
  @private
*/
function setClassicDecorator(dec, value = true) {
  DECORATOR_DESCRIPTOR_MAP.set(dec, value);
}

const END_WITH_EACH_REGEX = /\.@each$/;

/**
  Expands `pattern`, invoking `callback` for each expansion.

  The only pattern supported is brace-expansion, anything else will be passed
  once to `callback` directly.

  Example

  ```js
  import { expandProperties } from '@ember/object/computed';

  function echo(arg){ console.log(arg); }

  expandProperties('foo.bar', echo);              //=> 'foo.bar'
  expandProperties('{foo,bar}', echo);            //=> 'foo', 'bar'
  expandProperties('foo.{bar,baz}', echo);        //=> 'foo.bar', 'foo.baz'
  expandProperties('{foo,bar}.baz', echo);        //=> 'foo.baz', 'bar.baz'
  expandProperties('foo.{bar,baz}.[]', echo)      //=> 'foo.bar.[]', 'foo.baz.[]'
  expandProperties('{foo,bar}.{spam,eggs}', echo) //=> 'foo.spam', 'foo.eggs', 'bar.spam', 'bar.eggs'
  expandProperties('{foo}.bar.{baz}')             //=> 'foo.bar.baz'
  ```

  @method expandProperties
  @static
  @for @ember/object/computed
  @public
  @param {String} pattern The property pattern to expand.
  @param {Function} callback The callback to invoke.  It is invoked once per
  expansion, and is passed the expansion.
*/
function expandProperties(pattern, callback) {
  (isDevelopingApp() && !(typeof pattern === 'string') && assert(`A computed property key must be a string, you passed ${typeof pattern} ${pattern}`, typeof pattern === 'string'));
  (isDevelopingApp() && !(pattern.indexOf(' ') === -1) && assert('Brace expanded properties cannot contain spaces, e.g. "user.{firstName, lastName}" should be "user.{firstName,lastName}"', pattern.indexOf(' ') === -1)); // regex to look for double open, double close, or unclosed braces
  (isDevelopingApp() && !(pattern.match(/\{[^}{]*\{|\}[^}{]*\}|\{[^}]*$/g) === null) && assert(`Brace expanded properties have to be balanced and cannot be nested, pattern: ${pattern}`, pattern.match(/\{[^}{]*\{|\}[^}{]*\}|\{[^}]*$/g) === null));
  let start = pattern.indexOf('{');
  if (start < 0) {
    callback(pattern.replace(END_WITH_EACH_REGEX, '.[]'));
  } else {
    dive('', pattern, start, callback);
  }
}
function dive(prefix, pattern, start, callback) {
  let end = pattern.indexOf('}'),
    i = 0,
    newStart,
    arrayLength;
  let tempArr = pattern.substring(start + 1, end).split(',');
  let after = pattern.substring(end + 1);
  prefix = prefix + pattern.substring(0, start);
  arrayLength = tempArr.length;
  while (i < arrayLength) {
    newStart = after.indexOf('{');
    if (newStart < 0) {
      callback((prefix + tempArr[i++] + after).replace(END_WITH_EACH_REGEX, '.[]'));
    } else {
      dive(prefix + tempArr[i++], after, newStart, callback);
    }
  }
}

const AFTER_OBSERVERS = ':change';
function changeEvent(keyName) {
  return keyName + AFTER_OBSERVERS;
}

/**
@module @ember/object
*/

function addListener(obj, eventName, target, method, once, sync = true) {
  (isDevelopingApp() && !(Boolean(obj) && Boolean(eventName)) && assert('You must pass at least an object and event name to addListener', Boolean(obj) && Boolean(eventName)));
  if (!method && 'function' === typeof target) {
    method = target;
    target = null;
  }
  meta(obj).addToListeners(eventName, target, method, once === true, sync);
}

/**
  Remove an event listener

  Arguments should match those passed to `addListener`.

  @method removeListener
  @static
  @for @ember/object/events
  @param obj
  @param {String} eventName
  @param {Object|Function} target A target object or a function
  @param {Function|String} method A function or the name of a function to be called on `target`
  @public
*/
function removeListener(obj, eventName, targetOrFunction, functionOrName) {
  (isDevelopingApp() && !(Boolean(obj) && Boolean(eventName) && (typeof targetOrFunction === 'function' || typeof targetOrFunction === 'object' && Boolean(functionOrName))) && assert('You must pass at least an object, event name, and method or target and method/method name to removeListener', Boolean(obj) && Boolean(eventName) && (typeof targetOrFunction === 'function' || typeof targetOrFunction === 'object' && Boolean(functionOrName))));
  let target, method;
  if (typeof targetOrFunction === 'object') {
    target = targetOrFunction;
    method = functionOrName;
  } else {
    target = null;
    method = targetOrFunction;
  }
  let m = meta(obj);
  m.removeFromListeners(eventName, target, method);
}

/**
  Send an event. The execution of suspended listeners
  is skipped, and once listeners are removed. A listener without
  a target is executed on the passed object. If an array of actions
  is not passed, the actions stored on the passed object are invoked.

  @method sendEvent
  @static
  @for @ember/object/events
  @param obj
  @param {String} eventName
  @param {Array} params Optional parameters for each listener.
  @return {Boolean} if the event was delivered to one or more actions
  @public
*/
function sendEvent(obj, eventName, params, actions, _meta) {
  if (actions === undefined) {
    let meta = _meta === undefined ? peekMeta(obj) : _meta;
    actions = meta !== null ? meta.matchingListeners(eventName) : undefined;
  }
  if (actions === undefined || actions.length === 0) {
    return false;
  }
  for (let i = actions.length - 3; i >= 0; i -= 3) {
    // looping in reverse for once listeners
    let target = actions[i];
    let method = actions[i + 1];
    let once = actions[i + 2];
    if (!method) {
      continue;
    }
    if (once) {
      removeListener(obj, eventName, target, method);
    }
    if (!target) {
      target = obj;
    }
    let type = typeof method;
    if (type === 'string' || type === 'symbol') {
      method = target[method];
    }
    method.apply(target, params);
  }
  return true;
}

/**
  @public
  @method hasListeners
  @static
  @for @ember/object/events
  @param obj
  @param {String} eventName
  @return {Boolean} if `obj` has listeners for event `eventName`
*/
function hasListeners(obj, eventName) {
  let meta = peekMeta(obj);
  if (meta === null) {
    return false;
  }
  let matched = meta.matchingListeners(eventName);
  return matched !== undefined && matched.length > 0;
}

/**
  Define a property as a function that should be executed when
  a specified event or events are triggered.

  ``` javascript
  import EmberObject from '@ember/object';
  import { on } from '@ember/object/evented';
  import { sendEvent } from '@ember/object/events';

  let Job = EmberObject.extend({
    logCompleted: on('completed', function() {
      console.log('Job completed!');
    })
  });

  let job = Job.create();

  sendEvent(job, 'completed'); // Logs 'Job completed!'
 ```

  @method on
  @static
  @for @ember/object/evented
  @param {String} eventNames*
  @param {Function} func
  @return {Function} the listener function, passed as last argument to on(...)
  @public
*/
function on(...args) {
  let func = args.pop();
  let events = args;
  (isDevelopingApp() && !(typeof func === 'function') && assert('on expects function as last argument', typeof func === 'function'));
  (isDevelopingApp() && !(events.length > 0 && events.every(p => typeof p === 'string' && p.length > 0)) && assert('on called without valid event names', events.length > 0 && events.every(p => typeof p === 'string' && p.length > 0)));
  setListeners(func, events);
  return func;
}

const SYNC_DEFAULT = !ENV._DEFAULT_ASYNC_OBSERVERS;
const SYNC_OBSERVERS = new Map();
const ASYNC_OBSERVERS = new Map();

/**
@module @ember/object
*/

/**
  @method addObserver
  @static
  @for @ember/object/observers
  @param obj
  @param {String} path
  @param {Object|Function} target
  @param {Function|String} [method]
  @public
*/
function addObserver(obj, path, target, method, sync = SYNC_DEFAULT) {
  let eventName = changeEvent(path);
  addListener(obj, eventName, target, method, false, sync);
  let meta = peekMeta(obj);
  if (meta === null || !(meta.isPrototypeMeta(obj) || meta.isInitializing())) {
    activateObserver(obj, eventName, sync);
  }
}

/**
  @method removeObserver
  @static
  @for @ember/object/observers
  @param obj
  @param {String} path
  @param {Object|Function} target
  @param {Function|String} [method]
  @public
*/
function removeObserver(obj, path, target, method, sync = SYNC_DEFAULT) {
  let eventName = changeEvent(path);
  let meta = peekMeta(obj);
  if (meta === null || !(meta.isPrototypeMeta(obj) || meta.isInitializing())) {
    deactivateObserver(obj, eventName, sync);
  }
  removeListener(obj, eventName, target, method);
}
function getOrCreateActiveObserversFor(target, sync) {
  let observerMap = sync === true ? SYNC_OBSERVERS : ASYNC_OBSERVERS;
  if (!observerMap.has(target)) {
    observerMap.set(target, new Map());
    registerDestructor(target, () => destroyObservers(target), true);
  }
  return observerMap.get(target);
}
function activateObserver(target, eventName, sync = false) {
  let activeObservers = getOrCreateActiveObserversFor(target, sync);
  if (activeObservers.has(eventName)) {
    activeObservers.get(eventName).count++;
  } else {
    let path = eventName.substring(0, eventName.lastIndexOf(':'));
    let tag = getChainTagsForKey(target, path, tagMetaFor(target), peekMeta(target));
    activeObservers.set(eventName, {
      count: 1,
      path,
      tag,
      lastRevision: valueForTag(tag),
      suspended: false
    });
  }
}
let DEACTIVATE_SUSPENDED = false;
let SCHEDULED_DEACTIVATE = [];
function deactivateObserver(target, eventName, sync = false) {
  if (DEACTIVATE_SUSPENDED === true) {
    SCHEDULED_DEACTIVATE.push([target, eventName, sync]);
    return;
  }
  let observerMap = sync === true ? SYNC_OBSERVERS : ASYNC_OBSERVERS;
  let activeObservers = observerMap.get(target);
  if (activeObservers !== undefined) {
    let observer = activeObservers.get(eventName);
    observer.count--;
    if (observer.count === 0) {
      activeObservers.delete(eventName);
      if (activeObservers.size === 0) {
        observerMap.delete(target);
      }
    }
  }
}
function suspendedObserverDeactivation() {
  DEACTIVATE_SUSPENDED = true;
}
function resumeObserverDeactivation() {
  DEACTIVATE_SUSPENDED = false;
  for (let [target, eventName, sync] of SCHEDULED_DEACTIVATE) {
    deactivateObserver(target, eventName, sync);
  }
  SCHEDULED_DEACTIVATE = [];
}

/**
 * Primarily used for cases where we are redefining a class, e.g. mixins/reopen
 * being applied later. Revalidates all the observers, resetting their tags.
 *
 * @private
 * @param target
 */
function revalidateObservers(target) {
  if (ASYNC_OBSERVERS.has(target)) {
    ASYNC_OBSERVERS.get(target).forEach(observer => {
      observer.tag = getChainTagsForKey(target, observer.path, tagMetaFor(target), peekMeta(target));
      observer.lastRevision = valueForTag(observer.tag);
    });
  }
  if (SYNC_OBSERVERS.has(target)) {
    SYNC_OBSERVERS.get(target).forEach(observer => {
      observer.tag = getChainTagsForKey(target, observer.path, tagMetaFor(target), peekMeta(target));
      observer.lastRevision = valueForTag(observer.tag);
    });
  }
}
let lastKnownRevision = 0;
function flushAsyncObservers(_schedule) {
  let currentRevision = valueForTag(CURRENT_TAG);
  if (lastKnownRevision === currentRevision) {
    return;
  }
  lastKnownRevision = currentRevision;
  ASYNC_OBSERVERS.forEach((activeObservers, target) => {
    let meta = peekMeta(target);
    activeObservers.forEach((observer, eventName) => {
      if (!validateTag(observer.tag, observer.lastRevision)) {
        let sendObserver = () => {
          try {
            sendEvent(target, eventName, [target, observer.path], undefined, meta);
          } finally {
            observer.tag = getChainTagsForKey(target, observer.path, tagMetaFor(target), peekMeta(target));
            observer.lastRevision = valueForTag(observer.tag);
          }
        };
        if (_schedule) {
          _schedule('actions', sendObserver);
        } else {
          sendObserver();
        }
      }
    });
  });
}
function flushSyncObservers() {
  // When flushing synchronous observers, we know that something has changed (we
  // only do this during a notifyPropertyChange), so there's no reason to check
  // a global revision.

  SYNC_OBSERVERS.forEach((activeObservers, target) => {
    let meta = peekMeta(target);
    activeObservers.forEach((observer, eventName) => {
      if (!observer.suspended && !validateTag(observer.tag, observer.lastRevision)) {
        try {
          observer.suspended = true;
          sendEvent(target, eventName, [target, observer.path], undefined, meta);
        } finally {
          observer.tag = getChainTagsForKey(target, observer.path, tagMetaFor(target), peekMeta(target));
          observer.lastRevision = valueForTag(observer.tag);
          observer.suspended = false;
        }
      }
    });
  });
}
function setObserverSuspended(target, property, suspended) {
  let activeObservers = SYNC_OBSERVERS.get(target);
  if (!activeObservers) {
    return;
  }
  let observer = activeObservers.get(changeEvent(property));
  if (observer) {
    observer.suspended = suspended;
  }
}
function destroyObservers(target) {
  if (SYNC_OBSERVERS.size > 0) SYNC_OBSERVERS.delete(target);
  if (ASYNC_OBSERVERS.size > 0) ASYNC_OBSERVERS.delete(target);
}

const PROPERTY_DID_CHANGE = Symbol('PROPERTY_DID_CHANGE');
function hasPropertyDidChange(obj) {
  return obj != null && typeof obj === 'object' && typeof obj[PROPERTY_DID_CHANGE] === 'function';
}
let deferred = 0;

/**
  This function is called just after an object property has changed.
  It will notify any observers and clear caches among other things.

  Normally you will not need to call this method directly but if for some
  reason you can't directly watch a property you can invoke this method
  manually.

  @method notifyPropertyChange
  @for @ember/object
  @param {Object} obj The object with the property that will change
  @param {String} keyName The property key (or path) that will change.
  @param {Meta} [_meta] The objects meta.
  @param {unknown} [value] The new value to set for the property
  @return {void}
  @since 3.1.0
  @public
*/
function notifyPropertyChange(obj, keyName, _meta, value) {
  let meta = _meta === undefined ? peekMeta(obj) : _meta;
  if (meta !== null && (meta.isInitializing() || meta.isPrototypeMeta(obj))) {
    return;
  }
  markObjectAsDirty(obj, keyName);
  if (deferred <= 0) {
    flushSyncObservers();
  }
  if (PROPERTY_DID_CHANGE in obj) {
    // It's redundant to do this here, but we don't want to check above so we can avoid an extra function call in prod.
    (isDevelopingApp() && !(hasPropertyDidChange(obj)) && assert('property did change hook is invalid', hasPropertyDidChange(obj))); // we need to check the arguments length here; there's a check in Component's `PROPERTY_DID_CHANGE`
    // that checks its arguments length, so we have to explicitly not call this with `value`
    // if it is not passed to `notifyPropertyChange`
    if (arguments.length === 4) {
      obj[PROPERTY_DID_CHANGE](keyName, value);
    } else {
      obj[PROPERTY_DID_CHANGE](keyName);
    }
  }
}

/**
  @method beginPropertyChanges
  @chainable
  @private
*/
function beginPropertyChanges() {
  deferred++;
  suspendedObserverDeactivation();
}

/**
  @method endPropertyChanges
  @private
*/
function endPropertyChanges() {
  deferred--;
  if (deferred <= 0) {
    flushSyncObservers();
    resumeObserverDeactivation();
  }
}

/**
  Make a series of property changes together in an
  exception-safe way.

  ```javascript
  Ember.changeProperties(function() {
    obj1.set('foo', mayBlowUpWhenSet);
    obj2.set('bar', baz);
  });
  ```

  @method changeProperties
  @param {Function} callback
  @private
*/
function changeProperties(callback) {
  beginPropertyChanges();
  try {
    callback();
  } finally {
    endPropertyChanges();
  }
}

/**
@module @ember/object
*/

const DEEP_EACH_REGEX = /\.@each\.[^.]+\./;
function noop() {}
/**
  `@computed` is a decorator that turns a JavaScript getter and setter into a
  computed property, which is a _cached, trackable value_. By default the getter
  will only be called once and the result will be cached. You can specify
  various properties that your computed property depends on. This will force the
  cached result to be cleared if the dependencies are modified, and lazily recomputed the next time something asks for it.

  In the following example we decorate a getter - `fullName` -  by calling
  `computed` with the property dependencies (`firstName` and `lastName`) as
  arguments. The `fullName` getter will be called once (regardless of how many
  times it is accessed) as long as its dependencies do not change. Once
  `firstName` or `lastName` are updated any future calls to `fullName` will
  incorporate the new values, and any watchers of the value such as templates
  will be updated:

  ```javascript
  import { computed, set } from '@ember/object';

  class Person {
    constructor(firstName, lastName) {
      set(this, 'firstName', firstName);
      set(this, 'lastName', lastName);
    }

    @computed('firstName', 'lastName')
    get fullName() {
      return `${this.firstName} ${this.lastName}`;
    }
  });

  let tom = new Person('Tom', 'Dale');

  tom.fullName; // 'Tom Dale'
  ```

  You can also provide a setter, which will be used when updating the computed
  property. Ember's `set` function must be used to update the property
  since it will also notify observers of the property:

  ```javascript
  import { computed, set } from '@ember/object';

  class Person {
    constructor(firstName, lastName) {
      set(this, 'firstName', firstName);
      set(this, 'lastName', lastName);
    }

    @computed('firstName', 'lastName')
    get fullName() {
      return `${this.firstName} ${this.lastName}`;
    }

    set fullName(value) {
      let [firstName, lastName] = value.split(' ');

      set(this, 'firstName', firstName);
      set(this, 'lastName', lastName);
    }
  });

  let person = new Person();

  set(person, 'fullName', 'Peter Wagenet');
  person.firstName; // 'Peter'
  person.lastName;  // 'Wagenet'
  ```

  You can also pass a getter function or object with `get` and `set` functions
  as the last argument to the computed decorator. This allows you to define
  computed property _macros_:

  ```js
  import { computed } from '@ember/object';

  function join(...keys) {
    return computed(...keys, function() {
      return keys.map(key => this[key]).join(' ');
    });
  }

  class Person {
    @join('firstName', 'lastName')
    fullName;
  }
  ```

  Note that when defined this way, getters and setters receive the _key_ of the
  property they are decorating as the first argument. Setters receive the value
  they are setting to as the second argument instead. Additionally, setters must
  _return_ the value that should be cached:

  ```javascript
  import { computed, set } from '@ember/object';

  function fullNameMacro(firstNameKey, lastNameKey) {
    return computed(firstNameKey, lastNameKey, {
      get() {
        return `${this[firstNameKey]} ${this[lastNameKey]}`;
      }

      set(key, value) {
        let [firstName, lastName] = value.split(' ');

        set(this, firstNameKey, firstName);
        set(this, lastNameKey, lastName);

        return value;
      }
    });
  }

  class Person {
    constructor(firstName, lastName) {
      set(this, 'firstName', firstName);
      set(this, 'lastName', lastName);
    }

    @fullNameMacro('firstName', 'lastName') fullName;
  });

  let person = new Person();

  set(person, 'fullName', 'Peter Wagenet');
  person.firstName; // 'Peter'
  person.lastName;  // 'Wagenet'
  ```

  Computed properties can also be used in classic classes. To do this, we
  provide the getter and setter as the last argument like we would for a macro,
  and we assign it to a property on the class definition. This is an _anonymous_
  computed macro:

  ```javascript
  import EmberObject, { computed, set } from '@ember/object';

  let Person = EmberObject.extend({
    // these will be supplied by `create`
    firstName: null,
    lastName: null,

    fullName: computed('firstName', 'lastName', {
      get() {
        return `${this.firstName} ${this.lastName}`;
      }

      set(key, value) {
        let [firstName, lastName] = value.split(' ');

        set(this, 'firstName', firstName);
        set(this, 'lastName', lastName);

        return value;
      }
    })
  });

  let tom = Person.create({
    firstName: 'Tom',
    lastName: 'Dale'
  });

  tom.get('fullName') // 'Tom Dale'
  ```

  You can overwrite computed property without setters with a normal property (no
  longer computed) that won't change if dependencies change. You can also mark
  computed property as `.readOnly()` and block all attempts to set it.

  ```javascript
  import { computed, set } from '@ember/object';

  class Person {
    constructor(firstName, lastName) {
      set(this, 'firstName', firstName);
      set(this, 'lastName', lastName);
    }

    @computed('firstName', 'lastName').readOnly()
    get fullName() {
      return `${this.firstName} ${this.lastName}`;
    }
  });

  let person = new Person();
  person.set('fullName', 'Peter Wagenet'); // Uncaught Error: Cannot set read-only property "fullName" on object: <(...):emberXXX>
  ```

  Additional resources:
  - [Decorators RFC](https://github.com/emberjs/rfcs/blob/master/text/0408-decorators.md)
  - [New CP syntax RFC](https://github.com/emberjs/rfcs/blob/master/text/0011-improved-cp-syntax.md)
  - [New computed syntax explained in "Ember 1.12 released" ](https://emberjs.com/blog/2015/05/13/ember-1-12-released.html#toc_new-computed-syntax)

  @class ComputedProperty
  @public
*/
class ComputedProperty extends ComputedDescriptor {
  _readOnly = false;
  _hasConfig = false;
  _getter = undefined;
  _setter = undefined;
  constructor(args) {
    super();
    let maybeConfig = args[args.length - 1];
    if (typeof maybeConfig === 'function' || maybeConfig !== null && typeof maybeConfig === 'object') {
      this._hasConfig = true;
      let config = args.pop();
      if (typeof config === 'function') {
        (isDevelopingApp() && !(!isClassicDecorator(config)) && assert(`You attempted to pass a computed property instance to computed(). Computed property instances are decorator functions, and cannot be passed to computed() because they cannot be turned into decorators twice`, !isClassicDecorator(config)));
        this._getter = config;
      } else {
        const objectConfig = config;
        (isDevelopingApp() && !(typeof objectConfig === 'object' && !Array.isArray(objectConfig)) && assert('computed expects a function or an object as last argument.', typeof objectConfig === 'object' && !Array.isArray(objectConfig)));
        (isDevelopingApp() && !(Object.keys(objectConfig).every(key => key === 'get' || key === 'set')) && assert('Config object passed to computed can only contain `get` and `set` keys.', Object.keys(objectConfig).every(key => key === 'get' || key === 'set')));
        (isDevelopingApp() && !(Boolean(objectConfig.get) || Boolean(objectConfig.set)) && assert('Computed properties must receive a getter or a setter, you passed none.', Boolean(objectConfig.get) || Boolean(objectConfig.set)));
        this._getter = objectConfig.get || noop;
        this._setter = objectConfig.set;
      }
    }
    if (args.length > 0) {
      this._property(...args);
    }
  }
  setup(obj, keyName, propertyDesc, meta) {
    super.setup(obj, keyName, propertyDesc, meta);
    (isDevelopingApp() && !(!(propertyDesc && typeof propertyDesc.value === 'function')) && assert(`@computed can only be used on accessors or fields, attempted to use it with ${keyName} but that was a method. Try converting it to a getter (e.g. \`get ${keyName}() {}\`)`, !(propertyDesc && typeof propertyDesc.value === 'function')));
    (isDevelopingApp() && !(!propertyDesc || !propertyDesc.initializer) && assert(`@computed can only be used on empty fields. ${keyName} has an initial value (e.g. \`${keyName} = someValue\`)`, !propertyDesc || !propertyDesc.initializer));
    (isDevelopingApp() && !(!(this._hasConfig && propertyDesc && (typeof propertyDesc.get === 'function' || typeof propertyDesc.set === 'function'))) && assert(`Attempted to apply a computed property that already has a getter/setter to a ${keyName}, but it is a method or an accessor. If you passed @computed a function or getter/setter (e.g. \`@computed({ get() { ... } })\`), then it must be applied to a field`, !(this._hasConfig && propertyDesc && (typeof propertyDesc.get === 'function' || typeof propertyDesc.set === 'function'))));
    if (this._hasConfig === false) {
      (isDevelopingApp() && !(propertyDesc && (typeof propertyDesc.get === 'function' || typeof propertyDesc.set === 'function')) && assert(`Attempted to use @computed on ${keyName}, but it did not have a getter or a setter. You must either pass a get a function or getter/setter to @computed directly (e.g. \`@computed({ get() { ... } })\`) or apply @computed directly to a getter/setter`, propertyDesc && (typeof propertyDesc.get === 'function' || typeof propertyDesc.set === 'function')));
      let {
        get,
        set
      } = propertyDesc;
      if (get !== undefined) {
        this._getter = get;
      }
      if (set !== undefined) {
        this._setter = function setterWrapper(_key, value) {
          let ret = set.call(this, value);
          if (get !== undefined) {
            return typeof ret === 'undefined' ? get.call(this) : ret;
          }
          return ret;
        };
      }
    }
  }
  _property(...passedArgs) {
    let args = [];
    function addArg(property) {
      (isDevelopingApp() && !(DEEP_EACH_REGEX.test(property) === false) && assert(`Dependent keys containing @each only work one level deep. ` + `You used the key "${property}" which is invalid. ` + `Please create an intermediary computed property or ` + `switch to using tracked properties.`, DEEP_EACH_REGEX.test(property) === false));
      args.push(property);
    }
    for (let arg of passedArgs) {
      expandProperties(arg, addArg);
    }
    this._dependentKeys = args;
  }
  get(obj, keyName) {
    let meta$1 = meta(obj);
    let tagMeta = tagMetaFor(obj);
    let propertyTag = tagFor(obj, keyName, tagMeta);
    let ret;
    let revision = meta$1.revisionFor(keyName);
    if (revision !== undefined && validateTag(propertyTag, revision)) {
      ret = meta$1.valueFor(keyName);
    } else {
      // For backwards compatibility, we only throw if the CP has any dependencies. CPs without dependencies
      // should be allowed, even after the object has been destroyed, which is why we check _dependentKeys.
      (isDevelopingApp() && !(this._dependentKeys === undefined || !isDestroyed(obj)) && assert(`Attempted to access the computed ${obj}.${keyName} on a destroyed object, which is not allowed`, this._dependentKeys === undefined || !isDestroyed(obj)));
      let {
        _getter,
        _dependentKeys
      } = this;

      // Create a tracker that absorbs any trackable actions inside the CP
      untrack(() => {
        ret = _getter.call(obj, keyName);
      });
      if (_dependentKeys !== undefined) {
        UPDATE_TAG(propertyTag, getChainTagsForKeys(obj, _dependentKeys, tagMeta, meta$1));
        if (isDevelopingApp()) {
          ALLOW_CYCLES.set(propertyTag, true);
        }
      }
      meta$1.setValueFor(keyName, ret);
      meta$1.setRevisionFor(keyName, valueForTag(propertyTag));
      finishLazyChains(meta$1, keyName, ret);
    }
    consumeTag(propertyTag);

    // Add the tag of the returned value if it is an array, since arrays
    // should always cause updates if they are consumed and then changed
    if (Array.isArray(ret)) {
      consumeTag(tagFor(ret, '[]'));
    }
    return ret;
  }
  set(obj, keyName, value) {
    if (this._readOnly) {
      this._throwReadOnlyError(obj, keyName);
    }
    (isDevelopingApp() && !(this._setter !== undefined) && assert(`Cannot override the computed property \`${keyName}\` on ${toString(obj)}.`, this._setter !== undefined));
    let meta$1 = meta(obj);

    // ensure two way binding works when the component has defined a computed
    // property with both a setter and dependent keys, in that scenario without
    // the sync observer added below the caller's value will never be updated
    //
    // See GH#18147 / GH#19028 for details.
    if (
    // ensure that we only run this once, while the component is being instantiated
    meta$1.isInitializing() && this._dependentKeys !== undefined && this._dependentKeys.length > 0 && typeof obj[PROPERTY_DID_CHANGE] === 'function' && obj.isComponent) {
      // It's redundant to do this here, but we don't want to check above so we can avoid an extra function call in prod.
      (isDevelopingApp() && !(hasPropertyDidChange(obj)) && assert('property did change hook is invalid', hasPropertyDidChange(obj)));
      addObserver(obj, keyName, () => {
        obj[PROPERTY_DID_CHANGE](keyName);
      }, undefined, true);
    }
    let ret;
    try {
      beginPropertyChanges();
      ret = this._set(obj, keyName, value, meta$1);
      finishLazyChains(meta$1, keyName, ret);
      let tagMeta = tagMetaFor(obj);
      let propertyTag = tagFor(obj, keyName, tagMeta);
      let {
        _dependentKeys
      } = this;
      if (_dependentKeys !== undefined) {
        UPDATE_TAG(propertyTag, getChainTagsForKeys(obj, _dependentKeys, tagMeta, meta$1));
        if (isDevelopingApp()) {
          ALLOW_CYCLES.set(propertyTag, true);
        }
      }
      meta$1.setRevisionFor(keyName, valueForTag(propertyTag));
    } finally {
      endPropertyChanges();
    }
    return ret;
  }
  _throwReadOnlyError(obj, keyName) {
    throw new Error(`Cannot set read-only property "${keyName}" on object: ${inspect(obj)}`);
  }
  _set(obj, keyName, value, meta) {
    let hadCachedValue = meta.revisionFor(keyName) !== undefined;
    let cachedValue = meta.valueFor(keyName);
    let ret;
    let {
      _setter
    } = this;
    setObserverSuspended(obj, keyName, true);
    try {
      ret = _setter.call(obj, keyName, value, cachedValue);
    } finally {
      setObserverSuspended(obj, keyName, false);
    }

    // allows setter to return the same value that is cached already
    if (hadCachedValue && cachedValue === ret) {
      return ret;
    }
    meta.setValueFor(keyName, ret);
    notifyPropertyChange(obj, keyName, meta, value);
    return ret;
  }

  /* called before property is overridden */
  teardown(obj, keyName, meta) {
    if (meta.revisionFor(keyName) !== undefined) {
      meta.setRevisionFor(keyName, undefined);
      meta.setValueFor(keyName, undefined);
    }
    super.teardown(obj, keyName, meta);
  }
}
class AutoComputedProperty extends ComputedProperty {
  get(obj, keyName) {
    let meta$1 = meta(obj);
    let tagMeta = tagMetaFor(obj);
    let propertyTag = tagFor(obj, keyName, tagMeta);
    let ret;
    let revision = meta$1.revisionFor(keyName);
    if (revision !== undefined && validateTag(propertyTag, revision)) {
      ret = meta$1.valueFor(keyName);
    } else {
      (isDevelopingApp() && !(!isDestroyed(obj)) && assert(`Attempted to access the computed ${obj}.${keyName} on a destroyed object, which is not allowed`, !isDestroyed(obj)));
      let {
        _getter
      } = this;

      // Create a tracker that absorbs any trackable actions inside the CP
      let tag = track(() => {
        ret = _getter.call(obj, keyName);
      });
      UPDATE_TAG(propertyTag, tag);
      meta$1.setValueFor(keyName, ret);
      meta$1.setRevisionFor(keyName, valueForTag(propertyTag));
      finishLazyChains(meta$1, keyName, ret);
    }
    consumeTag(propertyTag);

    // Add the tag of the returned value if it is an array, since arrays
    // should always cause updates if they are consumed and then changed
    if (Array.isArray(ret)) {
      consumeTag(tagFor(ret, '[]', tagMeta));
    }
    return ret;
  }
}
// TODO: This class can be svelted once `meta` has been deprecated
class ComputedDecoratorImpl extends Function {
  /**
    Call on a computed property to set it into read-only mode. When in this
    mode the computed property will throw an error when set.
     Example:
     ```javascript
    import { computed, set } from '@ember/object';
     class Person {
      @computed().readOnly()
      get guid() {
        return 'guid-guid-guid';
      }
    }
     let person = new Person();
    set(person, 'guid', 'new-guid'); // will throw an exception
    ```
     Classic Class Example:
     ```javascript
    import EmberObject, { computed } from '@ember/object';
     let Person = EmberObject.extend({
      guid: computed(function() {
        return 'guid-guid-guid';
      }).readOnly()
    });
     let person = Person.create();
    person.set('guid', 'new-guid'); // will throw an exception
    ```
     @method readOnly
    @return {ComputedProperty} this
    @chainable
    @public
  */
  readOnly() {
    let desc = descriptorForDecorator(this);
    (isDevelopingApp() && !(!(desc._setter && desc._setter !== desc._getter)) && assert('Computed properties that define a setter using the new syntax cannot be read-only', !(desc._setter && desc._setter !== desc._getter)));
    desc._readOnly = true;
    return this;
  }

  /**
    In some cases, you may want to annotate computed properties with additional
    metadata about how they function or what values they operate on. For example,
    computed property functions may close over variables that are then no longer
    available for introspection. You can pass a hash of these values to a
    computed property.
     Example:
     ```javascript
    import { computed } from '@ember/object';
    import Person from 'my-app/utils/person';
     class Store {
      @computed().meta({ type: Person })
      get person() {
        let personId = this.personId;
        return Person.create({ id: personId });
      }
    }
    ```
     Classic Class Example:
     ```javascript
    import { computed } from '@ember/object';
    import Person from 'my-app/utils/person';
     const Store = EmberObject.extend({
      person: computed(function() {
        let personId = this.get('personId');
        return Person.create({ id: personId });
      }).meta({ type: Person })
    });
    ```
     The hash that you pass to the `meta()` function will be saved on the
    computed property descriptor under the `_meta` key. Ember runtime
    exposes a public API for retrieving these values from classes,
    via the `metaForProperty()` function.
     @method meta
    @param {Object} meta
    @chainable
    @public
  */

  meta(meta) {
    let prop = descriptorForDecorator(this);
    if (arguments.length === 0) {
      return prop._meta || {};
    } else {
      prop._meta = meta;
      return this;
    }
  }

  // TODO: Remove this when we can provide alternatives in the ecosystem to
  // addons such as ember-macro-helpers that use it.
  /** @internal */
  get _getter() {
    return descriptorForDecorator(this)._getter;
  }

  // TODO: Refactor this, this is an internal API only
  /** @internal */
  set enumerable(value) {
    descriptorForDecorator(this).enumerable = value;
  }
}

/**
  This helper returns a new property descriptor that wraps the passed
  computed property function. You can use this helper to define properties with
  native decorator syntax, mixins, or via `defineProperty()`.

  Example:

  ```js
  import { computed, set } from '@ember/object';

  class Person {
    constructor() {
      this.firstName = 'Betty';
      this.lastName = 'Jones';
    },

    @computed('firstName', 'lastName')
    get fullName() {
      return `${this.firstName} ${this.lastName}`;
    }
  }

  let client = new Person();

  client.fullName; // 'Betty Jones'

  set(client, 'lastName', 'Fuller');
  client.fullName; // 'Betty Fuller'
  ```

  Classic Class Example:

  ```js
  import EmberObject, { computed } from '@ember/object';

  let Person = EmberObject.extend({
    init() {
      this._super(...arguments);

      this.firstName = 'Betty';
      this.lastName = 'Jones';
    },

    fullName: computed('firstName', 'lastName', function() {
      return `${this.get('firstName')} ${this.get('lastName')}`;
    })
  });

  let client = Person.create();

  client.get('fullName'); // 'Betty Jones'

  client.set('lastName', 'Fuller');
  client.get('fullName'); // 'Betty Fuller'
  ```

  You can also provide a setter, either directly on the class using native class
  syntax, or by passing a hash with `get` and `set` functions.

  Example:

  ```js
  import { computed, set } from '@ember/object';

  class Person {
    constructor() {
      this.firstName = 'Betty';
      this.lastName = 'Jones';
    },

    @computed('firstName', 'lastName')
    get fullName() {
      return `${this.firstName} ${this.lastName}`;
    }

    set fullName(value) {
      let [firstName, lastName] = value.split(/\s+/);

      set(this, 'firstName', firstName);
      set(this, 'lastName', lastName);

      return value;
    }
  }

  let client = new Person();

  client.fullName; // 'Betty Jones'

  set(client, 'lastName', 'Fuller');
  client.fullName; // 'Betty Fuller'
  ```

  Classic Class Example:

  ```js
  import EmberObject, { computed } from '@ember/object';

  let Person = EmberObject.extend({
    init() {
      this._super(...arguments);

      this.firstName = 'Betty';
      this.lastName = 'Jones';
    },

    fullName: computed('firstName', 'lastName', {
      get(key) {
        return `${this.get('firstName')} ${this.get('lastName')}`;
      },
      set(key, value) {
        let [firstName, lastName] = value.split(/\s+/);
        this.setProperties({ firstName, lastName });
        return value;
      }
    })
  });

  let client = Person.create();
  client.get('firstName'); // 'Betty'

  client.set('fullName', 'Carroll Fuller');
  client.get('firstName'); // 'Carroll'
  ```

  When passed as an argument, the `set` function should accept two parameters,
  `key` and `value`. The value returned from `set` will be the new value of the
  property.

  _Note: This is the preferred way to define computed properties when writing third-party
  libraries that depend on or use Ember, since there is no guarantee that the user
  will have [prototype Extensions](https://guides.emberjs.com/release/configuring-ember/disabling-prototype-extensions/) enabled._

  @method computed
  @for @ember/object
  @static
  @param {String} [dependentKeys*] Optional dependent keys that trigger this computed property.
  @param {Function} func The computed property function.
  @return {ComputedDecorator} property decorator instance
  @public
*/
// @computed without parens or computed with descriptor args

// @computed with keys only

// @computed with keys and config

// @computed with config only

function computed(...args) {
  (isDevelopingApp() && !(!(isElementDescriptor(args.slice(0, 3)) && args.length === 5 && args[4] === true)) && assert(`@computed can only be used directly as a native decorator. If you're using tracked in classic classes, add parenthesis to call it like a function: computed()`, !(isElementDescriptor(args.slice(0, 3)) && args.length === 5 && args[4] === true)));
  if (isElementDescriptor(args)) {
    // SAFETY: We passed in the impl for this class
    let decorator = makeComputedDecorator(new ComputedProperty([]), ComputedDecoratorImpl);
    return decorator(args[0], args[1], args[2]);
  }

  // SAFETY: We passed in the impl for this class
  return makeComputedDecorator(new ComputedProperty(args), ComputedDecoratorImpl);
}
function autoComputed(...config) {
  // SAFETY: We passed in the impl for this class
  return makeComputedDecorator(new AutoComputedProperty(config), ComputedDecoratorImpl);
}

/**
  Allows checking if a given property on an object is a computed property. For the most part,
  this doesn't matter (you would normally just access the property directly and use its value),
  but for some tooling specific scenarios (e.g. the ember-inspector) it is important to
  differentiate if a property is a computed property or a "normal" property.

  This will work on either a class's prototype or an instance itself.

  @static
  @method isComputed
  @for @ember/debug
  @private
 */
function isComputed(obj, key) {
  return Boolean(descriptorForProperty(obj, key));
}

/**
@module @ember/object
*/

function defineProperty(obj, keyName, desc, data, _meta) {
  let meta$1 = _meta === undefined ? meta(obj) : _meta;
  let previousDesc = descriptorForProperty(obj, keyName, meta$1);
  let wasDescriptor = previousDesc !== undefined;
  if (wasDescriptor) {
    previousDesc.teardown(obj, keyName, meta$1);
  }
  if (isClassicDecorator(desc)) {
    defineDecorator(obj, keyName, desc, meta$1);
  } else if (desc === null || desc === undefined) {
    defineValue(obj, keyName, data, wasDescriptor, true);
  } else {
    // fallback to ES5
    Object.defineProperty(obj, keyName, desc);
  }

  // if key is being watched, override chains that
  // were initialized with the prototype
  if (!meta$1.isPrototypeMeta(obj)) {
    revalidateObservers(obj);
  }
}
function defineDecorator(obj, keyName, desc, meta) {
  let propertyDesc;
  if (isDevelopingApp()) {
    propertyDesc = desc(obj, keyName, undefined, meta, true);
  } else {
    propertyDesc = desc(obj, keyName, undefined, meta);
  }
  Object.defineProperty(obj, keyName, propertyDesc);

  // pass the decorator function forward for backwards compat
  return desc;
}
function defineValue(obj, keyName, value, wasDescriptor, enumerable = true) {
  if (wasDescriptor === true || enumerable === false) {
    Object.defineProperty(obj, keyName, {
      configurable: true,
      enumerable,
      writable: true,
      value
    });
  } else {
    if (isDevelopingApp()) {
      setWithMandatorySetter(obj, keyName, value);
    } else {
      obj[keyName] = value;
    }
  }
  return value;
}

const firstDotIndexCache = new Cache(1000, key => key.indexOf('.'));
function isPath(path) {
  return typeof path === 'string' && firstDotIndexCache.get(path) !== -1;
}

/**
@module @ember/object
*/

const PROXY_CONTENT = symbol('PROXY_CONTENT');
let getPossibleMandatoryProxyValue;
if (isDevelopingApp()) {
  getPossibleMandatoryProxyValue = function getPossibleMandatoryProxyValue(obj, keyName) {
    let content = obj[PROXY_CONTENT];
    if (content === undefined) {
      return obj[keyName];
    } else {
      /* global Reflect */
      return Reflect.get(content, keyName, obj);
    }
  };
}
function hasUnknownProperty(val) {
  return typeof val === 'object' && val !== null && typeof val.unknownProperty === 'function';
}

// ..........................................................
// GET AND SET
//
// If we are on a platform that supports accessors we can use those.
// Otherwise simulate accessors by looking up the property directly on the
// object.

/**
  Gets the value of a property on an object. If the property is computed,
  the function will be invoked. If the property is not defined but the
  object implements the `unknownProperty` method then that will be invoked.

  ```javascript
  import { get } from '@ember/object';
  get(obj, "name");
  ```

  If you plan to run on IE8 and older browsers then you should use this
  method anytime you want to retrieve a property on an object that you don't
  know for sure is private. (Properties beginning with an underscore '_'
  are considered private.)

  On all newer browsers, you only need to use this method to retrieve
  properties if the property might not be defined on the object and you want
  to respect the `unknownProperty` handler. Otherwise you can ignore this
  method.

  Note that if the object itself is `undefined`, this method will throw
  an error.

  @method get
  @for @ember/object
  @static
  @param {Object} obj The object to retrieve from.
  @param {String} keyName The property key to retrieve
  @return {Object} the property value or `null`.
  @public
*/

function get(obj, keyName) {
  (isDevelopingApp() && !(arguments.length === 2) && assert(`Get must be called with two arguments; an object and a property key`, arguments.length === 2));
  (isDevelopingApp() && !(obj !== undefined && obj !== null) && assert(`Cannot call get with '${keyName}' on an undefined object.`, obj !== undefined && obj !== null));
  (isDevelopingApp() && !(typeof keyName === 'string' || typeof keyName === 'number' && !isNaN(keyName)) && assert(`The key provided to get must be a string or number, you passed ${keyName}`, typeof keyName === 'string' || typeof keyName === 'number' && !isNaN(keyName)));
  (isDevelopingApp() && !(typeof keyName !== 'string' || keyName.lastIndexOf('this.', 0) !== 0) && assert(`'this' in paths is not supported`, typeof keyName !== 'string' || keyName.lastIndexOf('this.', 0) !== 0));
  return isPath(keyName) ? _getPath(obj, keyName) : _getProp(obj, keyName);
}
function _getProp(obj, keyName) {
  if (obj == null) {
    return;
  }
  let value;
  if (typeof obj === 'object' || typeof obj === 'function') {
    if (isDevelopingApp()) {
      value = getPossibleMandatoryProxyValue(obj, keyName);
    } else {
      value = obj[keyName];
    }
    if (value === undefined && typeof obj === 'object' && !(keyName in obj) && hasUnknownProperty(obj)) {
      value = obj.unknownProperty(keyName);
    }
    if (isTracking()) {
      consumeTag(tagFor(obj, keyName));
      if (Array.isArray(value) || isEmberArray(value)) {
        // Add the tag of the returned value if it is an array, since arrays
        // should always cause updates if they are consumed and then changed
        consumeTag(tagFor(value, '[]'));
      }
    }
  } else {
    // SAFETY: It should be ok to access properties on any non-nullish value
    value = obj[keyName];
  }
  return value;
}
function _getPath(obj, path, forSet) {
  let parts = typeof path === 'string' ? path.split('.') : path;
  for (let part of parts) {
    if (obj === undefined || obj === null || obj.isDestroyed) {
      return undefined;
    }
    if (forSet && (part === '__proto__' || part === 'constructor')) {
      return;
    }
    obj = _getProp(obj, part);
  }
  return obj;
}

// Warm it up
_getProp('foo', 'a');
_getProp('foo', 1);
_getProp({}, 'a');
_getProp({}, 1);
_getProp({
  unknownProperty() {}
}, 'a');
_getProp({
  unknownProperty() {}
}, 1);
get({}, 'foo');
get({}, 'foo.bar');
let fakeProxy = {};
setProxy(fakeProxy);
track(() => _getProp({}, 'a'));
track(() => _getProp({}, 1));
track(() => _getProp({
  a: []
}, 'a'));
track(() => _getProp({
  a: fakeProxy
}, 'a'));

/**
 @module ember
*/
/**
  Helper class that allows you to register your library with Ember.

  Singleton created at `Ember.libraries`.

  @class Libraries
  @constructor
  @private
*/
class Libraries {
  _registry;
  _coreLibIndex;
  constructor() {
    this._registry = [];
    this._coreLibIndex = 0;
  }
  _getLibraryByName(name) {
    let libs = this._registry;
    for (let lib of libs) {
      if (lib.name === name) {
        return lib;
      }
    }
    return undefined;
  }
  register(name, version, isCoreLibrary) {
    let index = this._registry.length;
    if (!this._getLibraryByName(name)) {
      if (isCoreLibrary) {
        index = this._coreLibIndex++;
      }
      this._registry.splice(index, 0, {
        name,
        version
      });
    } else {
      (isDevelopingApp() && warn(`Library "${name}" is already registered with Ember.`, false, {
        id: 'ember-metal.libraries-register'
      }));
    }
  }
  registerCoreLibrary(name, version) {
    this.register(name, version, true);
  }
  deRegister(name) {
    let lib = this._getLibraryByName(name);
    let index;
    if (lib) {
      index = this._registry.indexOf(lib);
      this._registry.splice(index, 1);
    }
  }
}
if (isDevelopingApp()) {
  Libraries.prototype.logVersions = function () {
    let libs = this._registry;
    let nameLengths = libs.map(item => get(item, 'name.length'));
    (isDevelopingApp() && !(nameLengths instanceof Array && nameLengths.every(n => typeof n === 'number')) && assert('nameLengths is number array', nameLengths instanceof Array && nameLengths.every(n => typeof n === 'number')));
    let maxNameLength = Math.max.apply(null, nameLengths);
    debug('-------------------------------');
    for (let lib of libs) {
      let spaces = new Array(maxNameLength - lib.name.length + 1).join(' ');
      debug([lib.name, spaces, ' : ', lib.version].join(''));
    }
    debug('-------------------------------');
  };
}
const LIBRARIES = new Libraries();
LIBRARIES.registerCoreLibrary('Ember', Version);

let DEBUG_INJECTION_FUNCTIONS;
if (isDevelopingApp()) {
  DEBUG_INJECTION_FUNCTIONS = new WeakMap();
}

/**
 @module ember
 @private
 */

/**
  Read-only property that returns the result of a container lookup.

  @class InjectedProperty
  @namespace Ember
  @constructor
  @param {String} type The container type the property will lookup
  @param {String} nameOrDesc (optional) The name the property will lookup, defaults
         to the property's name
  @private
*/
// Decorator factory (with args)
// (Also matches non-decorator form, types may be incorrect for this.)

// Non-decorator

// Decorator (without args)

// Catch-all for service and controller injections

function inject(type, ...args) {
  (isDevelopingApp() && !(typeof type === 'string') && assert('a string type must be provided to inject', typeof type === 'string'));
  let elementDescriptor;
  let name;
  if (isElementDescriptor(args)) {
    elementDescriptor = args;
  } else if (typeof args[0] === 'string') {
    name = args[0];
  }
  let getInjection = function (propertyName) {
    let owner = getOwner(this) || this.container; // fallback to `container` for backwards compat
    (isDevelopingApp() && !(Boolean(owner)) && assert(`Attempting to lookup an injected property on an object without a container, ensure that the object was instantiated via a container.`, Boolean(owner)));
    return owner.lookup(`${type}:${name || propertyName}`);
  };
  if (isDevelopingApp()) {
    DEBUG_INJECTION_FUNCTIONS.set(getInjection, {
      type,
      name
    });
  }
  let decorator = computed({
    get: getInjection,
    set(keyName, value) {
      defineProperty(this, keyName, null, value);
    }
  });
  if (elementDescriptor) {
    return decorator(elementDescriptor[0], elementDescriptor[1], elementDescriptor[2]);
  } else {
    return decorator;
  }
}

function tracked(...args) {
  (isDevelopingApp() && !(!(isElementDescriptor(args.slice(0, 3)) && args.length === 5 && args[4] === true)) && assert(`@tracked can only be used directly as a native decorator. If you're using tracked in classic classes, add parenthesis to call it like a function: tracked()`, !(isElementDescriptor(args.slice(0, 3)) && args.length === 5 && args[4] === true)));
  if (!isElementDescriptor(args)) {
    let propertyDesc = args[0];
    (isDevelopingApp() && !(args.length === 0 || typeof propertyDesc === 'object' && propertyDesc !== null) && assert(`tracked() may only receive an options object containing 'value' or 'initializer', received ${propertyDesc}`, args.length === 0 || typeof propertyDesc === 'object' && propertyDesc !== null));
    if (isDevelopingApp() && propertyDesc) {
      let keys = Object.keys(propertyDesc);
      (isDevelopingApp() && !(keys.length <= 1 && (keys[0] === undefined || keys[0] === 'value' || keys[0] === 'initializer')) && assert(`The options object passed to tracked() may only contain a 'value' or 'initializer' property, not both. Received: [${keys}]`, keys.length <= 1 && (keys[0] === undefined || keys[0] === 'value' || keys[0] === 'initializer')));
      (isDevelopingApp() && !(!('initializer' in propertyDesc) || typeof propertyDesc.initializer === 'function') && assert(`The initializer passed to tracked must be a function. Received ${propertyDesc.initializer}`, !('initializer' in propertyDesc) || typeof propertyDesc.initializer === 'function'));
    }
    let initializer = propertyDesc ? propertyDesc.initializer : undefined;
    let value = propertyDesc ? propertyDesc.value : undefined;
    let decorator = function (target, key, _desc, _meta, isClassicDecorator) {
      (isDevelopingApp() && !(isClassicDecorator) && assert(`You attempted to set a default value for ${key} with the @tracked({ value: 'default' }) syntax. You can only use this syntax with classic classes. For native classes, you can use class initializers: @tracked field = 'default';`, isClassicDecorator));
      let fieldDesc = {
        initializer: initializer || (() => value)
      };
      return descriptorForField([target, key, fieldDesc]);
    };
    setClassicDecorator(decorator);
    return decorator;
  }
  return descriptorForField(args);
}
if (isDevelopingApp()) {
  // Normally this isn't a classic decorator, but we want to throw a helpful
  // error in development so we need it to treat it like one
  setClassicDecorator(tracked);
}
function descriptorForField([target, key, desc]) {
  (isDevelopingApp() && !(!desc || !desc.value && !desc.get && !desc.set) && assert(`You attempted to use @tracked on ${key}, but that element is not a class field. @tracked is only usable on class fields. Native getters and setters will autotrack add any tracked fields they encounter, so there is no need mark getters and setters with @tracked.`, !desc || !desc.value && !desc.get && !desc.set));
  let {
    getter,
    setter
  } = trackedData(key, desc ? desc.initializer : undefined);
  function get() {
    let value = getter(this);

    // Add the tag of the returned value if it is an array, since arrays
    // should always cause updates if they are consumed and then changed
    if (Array.isArray(value) || isEmberArray(value)) {
      consumeTag(tagFor(value, '[]'));
    }
    return value;
  }
  function set(newValue) {
    setter(this, newValue);
    dirtyTagFor(this, SELF_TAG);
  }
  let newDesc = {
    enumerable: true,
    configurable: true,
    isTracked: true,
    get,
    set
  };
  COMPUTED_SETTERS.add(set);
  meta(target).writeDescriptors(key, new TrackedDescriptor(get, set));
  return newDesc;
}
class TrackedDescriptor {
  constructor(_get, _set) {
    this._get = _get;
    this._set = _set;
    CHAIN_PASS_THROUGH.add(this);
  }
  get(obj) {
    return this._get.call(obj);
  }
  set(obj, _key, value) {
    this._set.call(obj, value);
  }
}

export { TrackedDescriptor as $, defineDecorator as A, revalidateObservers as B, ComputedProperty as C, DEBUG_INJECTION_FUNCTIONS as D, addListener as E, removeListener as F, descriptorForDecorator as G, makeComputedDecorator as H, flushAsyncObservers as I, ComputedDescriptor as J, CHAIN_PASS_THROUGH as K, getChainTagsForKey as L, finishLazyChains as M, on as N, LIBRARIES as O, PROXY_CONTENT as P, PROPERTY_DID_CHANGE as Q, autoComputed as R, tracked as S, changeProperties as T, isComputed as U, _getProp as V, Libraries as W, ASYNC_OBSERVERS as X, SYNC_OBSERVERS as Y, markObjectAsDirty as Z, _getPath as _, tagForProperty as a, descriptorForProperty as b, computed as c, defineProperty as d, expandProperties as e, isClassicDecorator as f, get as g, hasUnknownProperty as h, isElementDescriptor as i, activateObserver as j, sendEvent as k, beginPropertyChanges as l, endPropertyChanges as m, notifyPropertyChange as n, objectAt as o, inject as p, isPath as q, COMPUTED_SETTERS as r, setClassicDecorator as s, tagForObject as t, getPossibleMandatoryProxyValue as u, hasListeners as v, removeObserver as w, addObserver as x, nativeDescDecorator as y, defineValue as z };
