/*@license Copyright 2015-2022 Ably Real-time Ltd (ably.com)

Ably JavaScript Library v2.15.0
https://github.com/ably/ably-js

Released under the Apache Licence v2.0*/(function (g, f) {
    if ("object" == typeof exports && "object" == typeof module) {
      module.exports = f(require('dequal'));
    } else if ("function" == typeof define && define.amd) {
      define(['dequal'], f);
    } else if ("object" == typeof exports) {
      exports["AblyObjectsPlugin"] = f(require('dequal'));
    } else {
      g["AblyObjectsPlugin"] = f(g["dequal"]);
    }
  }(this, (__da) => {
var exports = {};
var module = { exports };
"use strict";
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __knownSymbol = (name, symbol) => {
  if (symbol = Symbol[name])
    return symbol;
  throw Error("Symbol." + name + " is not defined");
};
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
  for (var prop in b || (b = {}))
    if (__hasOwnProp.call(b, prop))
      __defNormalProp(a, prop, b[prop]);
  if (__getOwnPropSymbols)
    for (var prop of __getOwnPropSymbols(b)) {
      if (__propIsEnum.call(b, prop))
        __defNormalProp(a, prop, b[prop]);
    }
  return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __objRest = (source, exclude) => {
  var target = {};
  for (var prop in source)
    if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
      target[prop] = source[prop];
  if (source != null && __getOwnPropSymbols)
    for (var prop of __getOwnPropSymbols(source)) {
      if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
        target[prop] = source[prop];
    }
  return target;
};
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __await = function(promise, isYieldStar) {
  this[0] = promise;
  this[1] = isYieldStar;
};
var __yieldStar = (value) => {
  var obj = value[__knownSymbol("asyncIterator")];
  var isAwait = false;
  var method;
  var it = {};
  if (obj == null) {
    obj = value[__knownSymbol("iterator")]();
    method = (k) => it[k] = (x) => obj[k](x);
  } else {
    obj = obj.call(value);
    method = (k) => it[k] = (v) => {
      if (isAwait) {
        isAwait = false;
        if (k === "throw")
          throw v;
        return v;
      }
      isAwait = true;
      return {
        done: false,
        value: new __await(new Promise((resolve) => {
          var x = obj[k](v);
          if (!(x instanceof Object))
            throw TypeError("Object expected");
          resolve(x);
        }), 1)
      };
    };
  }
  return it[__knownSymbol("iterator")] = () => it, method("next"), "throw" in obj ? method("throw") : it.throw = (x) => {
    throw x;
  }, "return" in obj && method("return"), it;
};

// src/plugins/objects/index.ts
var objects_exports = {};
__export(objects_exports, {
  ObjectMessage: () => ObjectMessage,
  Objects: () => Objects,
  WireObjectMessage: () => WireObjectMessage,
  default: () => objects_default
});
module.exports = __toCommonJS(objects_exports);

// src/plugins/objects/objectmessage.ts
function encode(message, utils, messageEncoding, encodeObjectDataFn) {
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
  const result = Object.assign(new WireObjectMessage(utils, messageEncoding), copyMsg(message));
  if ((_b = (_a = message.object) == null ? void 0 : _a.map) == null ? void 0 : _b.entries) {
    result.object.map.entries = encodeMapEntries(message.object.map.entries, encodeObjectDataFn);
  }
  if ((_e = (_d = (_c = message.object) == null ? void 0 : _c.createOp) == null ? void 0 : _d.map) == null ? void 0 : _e.entries) {
    result.object.createOp.map.entries = encodeMapEntries(message.object.createOp.map.entries, encodeObjectDataFn);
  }
  if ((_h = (_g = (_f = message.object) == null ? void 0 : _f.createOp) == null ? void 0 : _g.mapOp) == null ? void 0 : _h.data) {
    result.object.createOp.mapOp.data = encodeObjectData(message.object.createOp.mapOp.data, encodeObjectDataFn);
  }
  if ((_j = (_i = message.operation) == null ? void 0 : _i.map) == null ? void 0 : _j.entries) {
    result.operation.map.entries = encodeMapEntries(message.operation.map.entries, encodeObjectDataFn);
  }
  if ((_l = (_k = message.operation) == null ? void 0 : _k.mapOp) == null ? void 0 : _l.data) {
    result.operation.mapOp.data = encodeObjectData(message.operation.mapOp.data, encodeObjectDataFn);
  }
  return result;
}
function encodeMapEntries(mapEntries, encodeFn) {
  return Object.entries(mapEntries).reduce(
    (acc, v) => {
      const [key, entry] = v;
      const encodedData = entry.data ? encodeObjectData(entry.data, encodeFn) : void 0;
      acc[key] = __spreadProps(__spreadValues({}, entry), {
        data: encodedData
      });
      return acc;
    },
    {}
  );
}
function encodeObjectData(data, encodeFn) {
  const encodedData = encodeFn(data);
  return encodedData;
}
function createInitialValueJSONString(operation, client) {
  const msg = ObjectMessage.fromValues(
    // cast initialValue to ObjectOperation here, even though it may lack some properties
    // that are usually present on ObjectOperation.
    // this ObjectMessage instance is only used to get the encoded body,
    // so it's ok for the operation field to be incomplete in this context.
    // doing the type assertion here avoids the need to define a separate ObjectMessage
    // type that supports a fully optional ObjectOperation.
    { operation },
    client.Utils,
    client.MessageEncoding
  );
  const wireMsg = msg.encode(client);
  const { operation: encodedOperation } = wireMsg.encodeForWire(client.Utils.Format.json);
  return JSON.stringify(encodedOperation);
}
function strMsg(msg, className) {
  let result = "[" + className;
  for (const attr in msg) {
    if (msg[attr] === void 0 || attr === "_utils" || attr === "_messageEncoding") {
      continue;
    }
    if (attr === "operation" || attr === "object" || attr === "extras") {
      result += `; ${attr}=${JSON.stringify(msg[attr])}`;
    } else {
      result += `; ${attr}=${msg[attr]}`;
    }
  }
  result += "]";
  return result;
}
function copyMsg(msg) {
  const result = {
    id: msg.id,
    clientId: msg.clientId,
    connectionId: msg.connectionId,
    timestamp: msg.timestamp,
    serial: msg.serial,
    serialTimestamp: msg.serialTimestamp,
    siteCode: msg.siteCode
  };
  if (msg.operation) {
    result.operation = JSON.parse(JSON.stringify(msg.operation));
  }
  if (msg.object) {
    result.object = JSON.parse(JSON.stringify(msg.object));
  }
  if (msg.extras) {
    result.extras = JSON.parse(JSON.stringify(msg.extras));
  }
  return result;
}
var ObjectMessage = class _ObjectMessage {
  // OM2i
  constructor(_utils, _messageEncoding) {
    this._utils = _utils;
    this._messageEncoding = _messageEncoding;
  }
  static fromValues(values, utils, messageEncoding) {
    return Object.assign(new _ObjectMessage(utils, messageEncoding), values);
  }
  static fromValuesArray(values, utils, messageEncoding) {
    return values.map((x) => _ObjectMessage.fromValues(x, utils, messageEncoding));
  }
  /**
   * Protocol agnostic encoding of this ObjectMessage. Returns a new {@link WireObjectMessage} instance.
   *
   * Uses encoding functions from regular `Message` processing.
   *
   * @spec OM4
   */
  encode(client) {
    const encodeObjectDataFn = (data) => {
      const encodedObjectData = { objectId: data.objectId };
      if (client.Platform.BufferUtils.isBuffer(data.value)) {
        encodedObjectData.bytes = data.value;
      } else if (typeof data.value === "string") {
        encodedObjectData.string = data.value;
      } else if (typeof data.value === "boolean") {
        encodedObjectData.boolean = data.value;
      } else if (typeof data.value === "number") {
        encodedObjectData.number = data.value;
      } else if (typeof data.value === "object" && data.value !== null) {
        encodedObjectData.json = JSON.stringify(data.value);
      }
      return encodedObjectData;
    };
    return encode(this, this._utils, this._messageEncoding, encodeObjectDataFn);
  }
  toString() {
    return strMsg(this, "ObjectMessage");
  }
};
var WireObjectMessage = class _WireObjectMessage {
  // OM2i
  constructor(_utils, _messageEncoding) {
    this._utils = _utils;
    this._messageEncoding = _messageEncoding;
  }
  static fromValues(values, utils, messageEncoding) {
    return Object.assign(new _WireObjectMessage(utils, messageEncoding), values);
  }
  static fromValuesArray(values, utils, messageEncoding) {
    return values.map((x) => _WireObjectMessage.fromValues(x, utils, messageEncoding));
  }
  /**
   * Encodes WireObjectMessage for wire transmission. Does not mutate the provided WireObjectMessage.
   *
   * Uses encoding functions from regular `Message` processing.
   */
  encodeForWire(format) {
    const encodeObjectDataFn = (data) => {
      if (data.bytes != null) {
        const result = this._messageEncoding.encodeDataForWire(data.bytes, null, format);
        return __spreadProps(__spreadValues({}, data), { bytes: result.data });
      }
      return __spreadValues({}, data);
    };
    return encode(this, this._utils, this._messageEncoding, encodeObjectDataFn);
  }
  /**
   * Decodes this WireObjectMessage and returns a new {@link ObjectMessage} instance.
   *
   * Format is used to decode the bytes value as it's implicitly encoded depending on the protocol used:
   * - json: bytes are Base64-encoded string
   * - msgpack: bytes have a binary representation and don't need to be decoded
   *
   * @spec OM5
   */
  decode(client, format) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
    const result = Object.assign(new ObjectMessage(this._utils, this._messageEncoding), copyMsg(this));
    try {
      if ((_b = (_a = this.object) == null ? void 0 : _a.map) == null ? void 0 : _b.entries) {
        result.object.map.entries = this._decodeMapEntries(this.object.map.entries, client, format);
      }
      if ((_e = (_d = (_c = this.object) == null ? void 0 : _c.createOp) == null ? void 0 : _d.map) == null ? void 0 : _e.entries) {
        result.object.createOp.map.entries = this._decodeMapEntries(
          this.object.createOp.map.entries,
          client,
          format
        );
      }
      if ((_h = (_g = (_f = this.object) == null ? void 0 : _f.createOp) == null ? void 0 : _g.mapOp) == null ? void 0 : _h.data) {
        result.object.createOp.mapOp.data = this._decodeObjectData(this.object.createOp.mapOp.data, client, format);
      }
      if ((_j = (_i = this.operation) == null ? void 0 : _i.map) == null ? void 0 : _j.entries) {
        result.operation.map.entries = this._decodeMapEntries(this.operation.map.entries, client, format);
      }
      if ((_l = (_k = this.operation) == null ? void 0 : _k.mapOp) == null ? void 0 : _l.data) {
        result.operation.mapOp.data = this._decodeObjectData(this.operation.mapOp.data, client, format);
      }
    } catch (error) {
      client.Logger.logAction(
        client.logger,
        client.Logger.LOG_ERROR,
        "WireObjectMessage.decode()",
        this._utils.inspectError(error)
      );
    }
    return result;
  }
  /**
   * Overload toJSON() to intercept JSON.stringify().
   *
   * This will prepare the message to be transmitted over the wire to Ably.
   * It will encode the data payload according to the wire protocol used on the client.
   */
  toJSON() {
    const format = arguments.length > 0 ? this._utils.Format.json : this._utils.Format.msgpack;
    const _a = this.encodeForWire(format), { _utils, _messageEncoding } = _a, publicProps = __objRest(_a, ["_utils", "_messageEncoding"]);
    return publicProps;
  }
  toString() {
    return strMsg(this, "WireObjectMessage");
  }
  /** @spec OM3 */
  getMessageSize() {
    var _a, _b;
    let size = 0;
    size += (_b = (_a = this.clientId) == null ? void 0 : _a.length) != null ? _b : 0;
    if (this.operation) {
      size += this._getObjectOperationSize(this.operation);
    }
    if (this.object) {
      size += this._getObjectStateSize(this.object);
    }
    if (this.extras) {
      size += JSON.stringify(this.extras).length;
    }
    return size;
  }
  /** @spec OOP4 */
  _getObjectOperationSize(operation) {
    let size = 0;
    if (operation.mapOp) {
      size += this._getMapOpSize(operation.mapOp);
    }
    if (operation.counterOp) {
      size += this._getCounterOpSize(operation.counterOp);
    }
    if (operation.map) {
      size += this._getObjectMapSize(operation.map);
    }
    if (operation.counter) {
      size += this._getObjectCounterSize(operation.counter);
    }
    return size;
  }
  /** @spec OST3 */
  _getObjectStateSize(obj) {
    let size = 0;
    if (obj.map) {
      size += this._getObjectMapSize(obj.map);
    }
    if (obj.counter) {
      size += this._getObjectCounterSize(obj.counter);
    }
    if (obj.createOp) {
      size += this._getObjectOperationSize(obj.createOp);
    }
    return size;
  }
  /** @spec OMP4 */
  _getObjectMapSize(map) {
    var _a;
    let size = 0;
    Object.entries((_a = map.entries) != null ? _a : {}).forEach(([key, entry]) => {
      var _a2;
      size += (_a2 = key == null ? void 0 : key.length) != null ? _a2 : 0;
      if (entry) {
        size += this._getMapEntrySize(entry);
      }
    });
    return size;
  }
  /** @spec OCN3 */
  _getObjectCounterSize(counter) {
    if (counter.count == null) {
      return 0;
    }
    return 8;
  }
  /** @spec OME3 */
  _getMapEntrySize(entry) {
    let size = 0;
    if (entry.data) {
      size += this._getObjectDataSize(entry.data);
    }
    return size;
  }
  /** @spec OMO3 */
  _getMapOpSize(mapOp) {
    var _a, _b;
    let size = 0;
    size += (_b = (_a = mapOp.key) == null ? void 0 : _a.length) != null ? _b : 0;
    if (mapOp.data) {
      size += this._getObjectDataSize(mapOp.data);
    }
    return size;
  }
  /** @spec OCO3 */
  _getCounterOpSize(operation) {
    if (operation.amount == null) {
      return 0;
    }
    return 8;
  }
  /** @spec OD3 */
  _getObjectDataSize(data) {
    let size = 0;
    if (data.boolean != null) {
      size += this._utils.dataSizeBytes(data.boolean);
    }
    if (data.bytes != null) {
      size += this._utils.dataSizeBytes(data.bytes);
    }
    if (data.number != null) {
      size += this._utils.dataSizeBytes(data.number);
    }
    if (data.string != null) {
      size += this._utils.dataSizeBytes(data.string);
    }
    if (data.json != null) {
      size += this._utils.dataSizeBytes(data.json);
    }
    return size;
  }
  _decodeMapEntries(mapEntries, client, format) {
    return Object.entries(mapEntries).reduce(
      (acc, v) => {
        const [key, entry] = v;
        const decodedData = entry.data ? this._decodeObjectData(entry.data, client, format) : void 0;
        acc[key] = __spreadProps(__spreadValues({}, entry), {
          data: decodedData
        });
        return acc;
      },
      {}
    );
  }
  /** @spec OD5 */
  _decodeObjectData(objectData, client, format) {
    var _a, _b, _c;
    try {
      let decodedBytes;
      if (objectData.bytes != null) {
        decodedBytes = format === "msgpack" ? (
          // OD5a1 - connection is using msgpack protocol, bytes are already a buffer
          objectData.bytes
        ) : (
          // OD5b2 - connection is using JSON protocol, Base64-decode bytes value
          client.Platform.BufferUtils.base64Decode(String(objectData.bytes))
        );
      }
      let decodedJson;
      if (objectData.json != null) {
        decodedJson = JSON.parse(objectData.json);
      }
      return {
        objectId: objectData.objectId,
        value: (_c = (_b = (_a = decodedBytes != null ? decodedBytes : decodedJson) != null ? _a : objectData.boolean) != null ? _b : objectData.number) != null ? _c : objectData.string
      };
    } catch (error) {
      client.Logger.logAction(
        client.logger,
        client.Logger.LOG_ERROR,
        "WireObjectMessage._decodeObjectData()",
        this._utils.inspectError(error)
      );
      return __spreadValues({}, objectData);
    }
  }
};

// src/plugins/objects/liveobject.ts
var LiveObject = class {
  constructor(_objects, objectId) {
    this._objects = _objects;
    this._client = this._objects.getClient();
    this._subscriptions = new this._client.EventEmitter(this._client.logger);
    this._lifecycleEvents = new this._client.EventEmitter(this._client.logger);
    this._objectId = objectId;
    this._dataRef = this._getZeroValueData();
    this._siteTimeserials = {};
    this._createOperationIsMerged = false;
    this._tombstone = false;
  }
  subscribe(listener) {
    this._objects.throwIfInvalidAccessApiConfiguration();
    this._subscriptions.on("updated" /* updated */, listener);
    const unsubscribe = () => {
      this._subscriptions.off("updated" /* updated */, listener);
    };
    return { unsubscribe };
  }
  unsubscribe(listener) {
    if (this._client.Utils.isNil(listener)) {
      return;
    }
    this._subscriptions.off("updated" /* updated */, listener);
  }
  unsubscribeAll() {
    this._subscriptions.off("updated" /* updated */);
  }
  on(event, callback) {
    this._lifecycleEvents.on(event, callback);
    const off = () => {
      this._lifecycleEvents.off(event, callback);
    };
    return { off };
  }
  off(event, callback) {
    if (this._client.Utils.isNil(event) && this._client.Utils.isNil(callback)) {
      return;
    }
    this._lifecycleEvents.off(event, callback);
  }
  offAll() {
    this._lifecycleEvents.off();
  }
  /**
   * @internal
   */
  getObjectId() {
    return this._objectId;
  }
  /**
   * Emits the {@link LiveObjectSubscriptionEvent.updated} event with provided update object if it isn't a noop.
   *
   * @internal
   */
  notifyUpdated(update) {
    if (update.noop) {
      return;
    }
    this._subscriptions.emit("updated" /* updated */, update);
  }
  /**
   * Clears the object's data, cancels any buffered operations and sets the tombstone flag to `true`.
   *
   * @internal
   */
  tombstone(objectMessage) {
    this._tombstone = true;
    if (objectMessage.serialTimestamp != null) {
      this._tombstonedAt = objectMessage.serialTimestamp;
    } else {
      this._client.Logger.logAction(
        this._client.logger,
        this._client.Logger.LOG_MINOR,
        "LiveObject.tombstone()",
        `object has been tombstoned but no "serialTimestamp" found in the message, using local clock instead; objectId=${this.getObjectId()}`
      );
      this._tombstonedAt = Date.now();
    }
    const update = this.clearData();
    update.clientId = objectMessage.clientId;
    update.connectionId = objectMessage.connectionId;
    this._lifecycleEvents.emit("deleted" /* deleted */);
    return update;
  }
  /**
   * @internal
   */
  isTombstoned() {
    return this._tombstone;
  }
  /**
   * @internal
   */
  tombstonedAt() {
    return this._tombstonedAt;
  }
  /**
   * @internal
   */
  clearData() {
    const previousDataRef = this._dataRef;
    this._dataRef = this._getZeroValueData();
    return this._updateFromDataDiff(previousDataRef, this._dataRef);
  }
  /**
   * Returns true if the given serial indicates that the operation to which it belongs should be applied to the object.
   *
   * An operation should be applied if its serial is strictly greater than the serial in the `siteTimeserials` map for the same site.
   * If `siteTimeserials` map does not contain a serial for the same site, the operation should be applied.
   */
  _canApplyOperation(opSerial, opSiteCode) {
    if (!opSerial) {
      throw new this._client.ErrorInfo(`Invalid serial: ${opSerial}`, 92e3, 500);
    }
    if (!opSiteCode) {
      throw new this._client.ErrorInfo(`Invalid site code: ${opSiteCode}`, 92e3, 500);
    }
    const siteSerial = this._siteTimeserials[opSiteCode];
    return !siteSerial || opSerial > siteSerial;
  }
  _applyObjectDelete(objectMessage) {
    return this.tombstone(objectMessage);
  }
};

// src/plugins/objects/objectid.ts
var ObjectId = class _ObjectId {
  constructor(type, hash, msTimestamp) {
    this.type = type;
    this.hash = hash;
    this.msTimestamp = msTimestamp;
  }
  static fromInitialValue(platform, objectType, initialValue, nonce, msTimestamp) {
    const valueForHashBuffer = platform.BufferUtils.concat([
      platform.BufferUtils.utf8Encode(initialValue),
      platform.BufferUtils.utf8Encode(":"),
      platform.BufferUtils.utf8Encode(nonce)
    ]);
    const hashBuffer = platform.BufferUtils.sha256(valueForHashBuffer);
    const hash = platform.BufferUtils.base64UrlEncode(hashBuffer);
    return new _ObjectId(objectType, hash, msTimestamp);
  }
  /**
   * Create ObjectId instance from hashed object id string.
   */
  static fromString(client, objectId) {
    if (client.Utils.isNil(objectId)) {
      throw new client.ErrorInfo("Invalid object id string", 92e3, 500);
    }
    const [type, rest] = objectId.split(":");
    if (!type || !rest) {
      throw new client.ErrorInfo("Invalid object id string", 92e3, 500);
    }
    if (!["map", "counter"].includes(type)) {
      throw new client.ErrorInfo(`Invalid object type in object id: ${objectId}`, 92e3, 500);
    }
    const [hash, msTimestamp] = rest.split("@");
    if (!hash || !msTimestamp) {
      throw new client.ErrorInfo("Invalid object id string", 92e3, 500);
    }
    if (!Number.isInteger(Number.parseInt(msTimestamp))) {
      throw new client.ErrorInfo("Invalid object id string", 92e3, 500);
    }
    return new _ObjectId(type, hash, Number.parseInt(msTimestamp));
  }
  toString() {
    return `${this.type}:${this.hash}@${this.msTimestamp}`;
  }
};

// src/plugins/objects/livecounter.ts
var LiveCounter = class _LiveCounter extends LiveObject {
  /**
   * Returns a {@link LiveCounter} instance with a 0 value.
   *
   * @internal
   * @spec RTLC4
   */
  static zeroValue(objects, objectId) {
    return new _LiveCounter(objects, objectId);
  }
  /**
   * Returns a {@link LiveCounter} instance based on the provided object state.
   * The provided object state must hold a valid counter object data.
   *
   * @internal
   */
  static fromObjectState(objects, objectMessage) {
    const obj = new _LiveCounter(objects, objectMessage.object.objectId);
    obj.overrideWithObjectState(objectMessage);
    return obj;
  }
  /**
   * Returns a {@link LiveCounter} instance based on the provided COUNTER_CREATE object operation.
   * The provided object operation must hold a valid counter object data.
   *
   * @internal
   */
  static fromObjectOperation(objects, objectMessage) {
    const obj = new _LiveCounter(objects, objectMessage.operation.objectId);
    obj._mergeInitialDataFromCreateOperation(objectMessage.operation, objectMessage);
    return obj;
  }
  /**
   * @internal
   */
  static createCounterIncMessage(objects, objectId, amount) {
    const client = objects.getClient();
    if (typeof amount !== "number" || !Number.isFinite(amount)) {
      throw new client.ErrorInfo("Counter value increment should be a valid number", 40003, 400);
    }
    const msg = ObjectMessage.fromValues(
      {
        operation: {
          action: 4 /* COUNTER_INC */,
          objectId,
          counterOp: { amount }
        }
      },
      client.Utils,
      client.MessageEncoding
    );
    return msg;
  }
  /**
   * @internal
   */
  static async createCounterCreateMessage(objects, count) {
    const client = objects.getClient();
    if (count !== void 0 && (typeof count !== "number" || !Number.isFinite(count))) {
      throw new client.ErrorInfo("Counter value should be a valid number", 40003, 400);
    }
    const initialValueOperation = _LiveCounter.createInitialValueOperation(count);
    const initialValueJSONString = createInitialValueJSONString(initialValueOperation, client);
    const nonce = client.Utils.cheapRandStr();
    const msTimestamp = await client.getTimestamp(true);
    const objectId = ObjectId.fromInitialValue(
      client.Platform,
      "counter",
      initialValueJSONString,
      nonce,
      msTimestamp
    ).toString();
    const msg = ObjectMessage.fromValues(
      {
        operation: __spreadProps(__spreadValues({}, initialValueOperation), {
          action: 3 /* COUNTER_CREATE */,
          objectId,
          nonce,
          initialValue: initialValueJSONString
        })
      },
      client.Utils,
      client.MessageEncoding
    );
    return msg;
  }
  /**
   * @internal
   */
  static createInitialValueOperation(count) {
    return {
      counter: {
        count: count != null ? count : 0
      }
    };
  }
  /** @spec RTLC5 */
  value() {
    this._objects.throwIfInvalidAccessApiConfiguration();
    return this._dataRef.data;
  }
  /**
   * Send a COUNTER_INC operation to the realtime system to increment a value on this LiveCounter object.
   *
   * This does not modify the underlying data of this LiveCounter object. Instead, the change will be applied when
   * the published COUNTER_INC operation is echoed back to the client and applied to the object following the regular
   * operation application procedure.
   *
   * @returns A promise which resolves upon receiving the ACK message for the published operation message.
   */
  async increment(amount) {
    this._objects.throwIfInvalidWriteApiConfiguration();
    const msg = _LiveCounter.createCounterIncMessage(this._objects, this.getObjectId(), amount);
    return this._objects.publish([msg]);
  }
  /**
   * An alias for calling {@link LiveCounter.increment | LiveCounter.increment(-amount)}
   */
  async decrement(amount) {
    this._objects.throwIfInvalidWriteApiConfiguration();
    if (typeof amount !== "number" || !Number.isFinite(amount)) {
      throw new this._client.ErrorInfo("Counter value decrement should be a valid number", 40003, 400);
    }
    return this.increment(-amount);
  }
  /**
   * @internal
   */
  applyOperation(op, msg) {
    var _a;
    if (op.objectId !== this.getObjectId()) {
      throw new this._client.ErrorInfo(
        `Cannot apply object operation with objectId=${op.objectId}, to this LiveCounter with objectId=${this.getObjectId()}`,
        92e3,
        500
      );
    }
    const opSerial = msg.serial;
    const opSiteCode = msg.siteCode;
    if (!this._canApplyOperation(opSerial, opSiteCode)) {
      this._client.Logger.logAction(
        this._client.logger,
        this._client.Logger.LOG_MICRO,
        "LiveCounter.applyOperation()",
        `skipping ${op.action} op: op serial ${opSerial.toString()} <= site serial ${(_a = this._siteTimeserials[opSiteCode]) == null ? void 0 : _a.toString()}; objectId=${this.getObjectId()}`
      );
      return;
    }
    this._siteTimeserials[opSiteCode] = opSerial;
    if (this.isTombstoned()) {
      return;
    }
    let update;
    switch (op.action) {
      case 3 /* COUNTER_CREATE */:
        update = this._applyCounterCreate(op, msg);
        break;
      case 4 /* COUNTER_INC */:
        if (this._client.Utils.isNil(op.counterOp)) {
          this._throwNoPayloadError(op);
          return;
        } else {
          update = this._applyCounterInc(op.counterOp, msg);
        }
        break;
      case 5 /* OBJECT_DELETE */:
        update = this._applyObjectDelete(msg);
        break;
      default:
        throw new this._client.ErrorInfo(
          `Invalid ${op.action} op for LiveCounter objectId=${this.getObjectId()}`,
          92e3,
          500
        );
    }
    this.notifyUpdated(update);
  }
  /**
   * @internal
   * @spec RTLC6
   */
  overrideWithObjectState(objectMessage) {
    var _a, _b, _c, _d, _e;
    const objectState = objectMessage.object;
    if (objectState == null) {
      throw new this._client.ErrorInfo(`Missing object state; LiveCounter objectId=${this.getObjectId()}`, 92e3, 500);
    }
    if (objectState.objectId !== this.getObjectId()) {
      throw new this._client.ErrorInfo(
        `Invalid object state: object state objectId=${objectState.objectId}; LiveCounter objectId=${this.getObjectId()}`,
        92e3,
        500
      );
    }
    if (!this._client.Utils.isNil(objectState.createOp)) {
      if (objectState.createOp.objectId !== this.getObjectId()) {
        throw new this._client.ErrorInfo(
          `Invalid object state: object state createOp objectId=${(_a = objectState.createOp) == null ? void 0 : _a.objectId}; LiveCounter objectId=${this.getObjectId()}`,
          92e3,
          500
        );
      }
      if (objectState.createOp.action !== 3 /* COUNTER_CREATE */) {
        throw new this._client.ErrorInfo(
          `Invalid object state: object state createOp action=${(_b = objectState.createOp) == null ? void 0 : _b.action}; LiveCounter objectId=${this.getObjectId()}`,
          92e3,
          500
        );
      }
    }
    this._siteTimeserials = (_c = objectState.siteTimeserials) != null ? _c : {};
    if (this.isTombstoned()) {
      return { noop: true };
    }
    const previousDataRef = this._dataRef;
    if (objectState.tombstone) {
      this.tombstone(objectMessage);
    } else {
      this._createOperationIsMerged = false;
      this._dataRef = { data: (_e = (_d = objectState.counter) == null ? void 0 : _d.count) != null ? _e : 0 };
      if (!this._client.Utils.isNil(objectState.createOp)) {
        this._mergeInitialDataFromCreateOperation(objectState.createOp, objectMessage);
      }
    }
    const update = this._updateFromDataDiff(previousDataRef, this._dataRef);
    update.clientId = objectMessage.clientId;
    update.connectionId = objectMessage.connectionId;
    return update;
  }
  /**
   * @internal
   */
  onGCInterval() {
    return;
  }
  /** @spec RTLC4 */
  _getZeroValueData() {
    return { data: 0 };
  }
  _updateFromDataDiff(prevDataRef, newDataRef) {
    const counterDiff = newDataRef.data - prevDataRef.data;
    return { update: { amount: counterDiff } };
  }
  _mergeInitialDataFromCreateOperation(objectOperation, msg) {
    var _a, _b, _c, _d;
    this._dataRef.data += (_b = (_a = objectOperation.counter) == null ? void 0 : _a.count) != null ? _b : 0;
    this._createOperationIsMerged = true;
    return {
      update: { amount: (_d = (_c = objectOperation.counter) == null ? void 0 : _c.count) != null ? _d : 0 },
      clientId: msg.clientId,
      connectionId: msg.connectionId
    };
  }
  _throwNoPayloadError(op) {
    throw new this._client.ErrorInfo(
      `No payload found for ${op.action} op for LiveCounter objectId=${this.getObjectId()}`,
      92e3,
      500
    );
  }
  _applyCounterCreate(op, msg) {
    if (this._createOperationIsMerged) {
      this._client.Logger.logAction(
        this._client.logger,
        this._client.Logger.LOG_MICRO,
        "LiveCounter._applyCounterCreate()",
        `skipping applying COUNTER_CREATE op on a counter instance as it was already applied before; objectId=${this.getObjectId()}`
      );
      return { noop: true };
    }
    return this._mergeInitialDataFromCreateOperation(op, msg);
  }
  _applyCounterInc(op, msg) {
    this._dataRef.data += op.amount;
    return { update: { amount: op.amount }, clientId: msg.clientId, connectionId: msg.connectionId };
  }
};

// src/plugins/objects/batchcontextlivecounter.ts
var BatchContextLiveCounter = class {
  constructor(_batchContext, _objects, _counter) {
    this._batchContext = _batchContext;
    this._objects = _objects;
    this._counter = _counter;
    this._client = this._objects.getClient();
  }
  value() {
    this._objects.throwIfInvalidAccessApiConfiguration();
    this._batchContext.throwIfClosed();
    return this._counter.value();
  }
  increment(amount) {
    this._objects.throwIfInvalidWriteApiConfiguration();
    this._batchContext.throwIfClosed();
    const msg = LiveCounter.createCounterIncMessage(this._objects, this._counter.getObjectId(), amount);
    this._batchContext.queueMessage(msg);
  }
  decrement(amount) {
    this._objects.throwIfInvalidWriteApiConfiguration();
    this._batchContext.throwIfClosed();
    if (typeof amount !== "number") {
      throw new this._client.ErrorInfo("Counter value decrement should be a number", 40003, 400);
    }
    this.increment(-amount);
  }
};

// src/plugins/objects/livemap.ts
var import_dequal = require("dequal");
var LiveMap = class _LiveMap extends LiveObject {
  constructor(objects, _semantics, objectId) {
    super(objects, objectId);
    this._semantics = _semantics;
  }
  /**
   * Returns a {@link LiveMap} instance with an empty map data.
   *
   * @internal
   * @spec RTLM4
   */
  static zeroValue(objects, objectId) {
    return new _LiveMap(objects, 0 /* LWW */, objectId);
  }
  /**
   * Returns a {@link LiveMap} instance based on the provided object state.
   * The provided object state must hold a valid map object data.
   *
   * @internal
   */
  static fromObjectState(objects, objectMessage) {
    const obj = new _LiveMap(objects, objectMessage.object.map.semantics, objectMessage.object.objectId);
    obj.overrideWithObjectState(objectMessage);
    return obj;
  }
  /**
   * Returns a {@link LiveMap} instance based on the provided MAP_CREATE object operation.
   * The provided object operation must hold a valid map object data.
   *
   * @internal
   */
  static fromObjectOperation(objects, objectMessage) {
    var _a;
    const obj = new _LiveMap(objects, (_a = objectMessage.operation.map) == null ? void 0 : _a.semantics, objectMessage.operation.objectId);
    obj._mergeInitialDataFromCreateOperation(objectMessage.operation, objectMessage);
    return obj;
  }
  /**
   * @internal
   */
  static createMapSetMessage(objects, objectId, key, value) {
    const client = objects.getClient();
    _LiveMap.validateKeyValue(objects, key, value);
    let objectData;
    if (value instanceof LiveObject) {
      const typedObjectData = { objectId: value.getObjectId() };
      objectData = typedObjectData;
    } else {
      const typedObjectData = { value };
      objectData = typedObjectData;
    }
    const msg = ObjectMessage.fromValues(
      {
        operation: {
          action: 1 /* MAP_SET */,
          objectId,
          mapOp: {
            key,
            data: objectData
          }
        }
      },
      client.Utils,
      client.MessageEncoding
    );
    return msg;
  }
  /**
   * @internal
   */
  static createMapRemoveMessage(objects, objectId, key) {
    const client = objects.getClient();
    if (typeof key !== "string") {
      throw new client.ErrorInfo("Map key should be string", 40003, 400);
    }
    const msg = ObjectMessage.fromValues(
      {
        operation: {
          action: 2 /* MAP_REMOVE */,
          objectId,
          mapOp: { key }
        }
      },
      client.Utils,
      client.MessageEncoding
    );
    return msg;
  }
  /**
   * @internal
   */
  static validateKeyValue(objects, key, value) {
    const client = objects.getClient();
    if (typeof key !== "string") {
      throw new client.ErrorInfo("Map key should be string", 40003, 400);
    }
    if (value === null || typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean" && typeof value !== "object") {
      throw new client.ErrorInfo("Map value data type is unsupported", 40013, 400);
    }
  }
  /**
   * @internal
   */
  static async createMapCreateMessage(objects, entries) {
    const client = objects.getClient();
    if (entries !== void 0 && (entries === null || typeof entries !== "object")) {
      throw new client.ErrorInfo("Map entries should be a key-value object", 40003, 400);
    }
    Object.entries(entries != null ? entries : {}).forEach(([key, value]) => _LiveMap.validateKeyValue(objects, key, value));
    const initialValueOperation = _LiveMap.createInitialValueOperation(entries);
    const initialValueJSONString = createInitialValueJSONString(initialValueOperation, client);
    const nonce = client.Utils.cheapRandStr();
    const msTimestamp = await client.getTimestamp(true);
    const objectId = ObjectId.fromInitialValue(
      client.Platform,
      "map",
      initialValueJSONString,
      nonce,
      msTimestamp
    ).toString();
    const msg = ObjectMessage.fromValues(
      {
        operation: __spreadProps(__spreadValues({}, initialValueOperation), {
          action: 0 /* MAP_CREATE */,
          objectId,
          nonce,
          initialValue: initialValueJSONString
        })
      },
      client.Utils,
      client.MessageEncoding
    );
    return msg;
  }
  /**
   * @internal
   */
  static createInitialValueOperation(entries) {
    const mapEntries = {};
    Object.entries(entries != null ? entries : {}).forEach(([key, value]) => {
      let objectData;
      if (value instanceof LiveObject) {
        const typedObjectData = { objectId: value.getObjectId() };
        objectData = typedObjectData;
      } else {
        const typedObjectData = { value };
        objectData = typedObjectData;
      }
      mapEntries[key] = {
        data: objectData
      };
    });
    return {
      map: {
        semantics: 0 /* LWW */,
        entries: mapEntries
      }
    };
  }
  /**
   * Returns the value associated with the specified key in the underlying Map object.
   *
   * - If this map object is tombstoned (deleted), `undefined` is returned.
   * - If no entry is associated with the specified key, `undefined` is returned.
   * - If map entry is tombstoned (deleted), `undefined` is returned.
   * - If the value associated with the provided key is an objectId string of another LiveObject, a reference to that LiveObject
   * is returned, provided it exists in the local pool and is not tombstoned. Otherwise, `undefined` is returned.
   * - If the value is not an objectId, then that value is returned.
   *
   * @spec RTLM5, RTLM5a
   */
  // force the key to be of type string as we only allow strings as key in a map
  get(key) {
    this._objects.throwIfInvalidAccessApiConfiguration();
    if (this.isTombstoned()) {
      return void 0;
    }
    const element = this._dataRef.data.get(key);
    if (element === void 0) {
      return void 0;
    }
    if (element.tombstone === true) {
      return void 0;
    }
    return this._getResolvedValueFromObjectData(element.data);
  }
  size() {
    this._objects.throwIfInvalidAccessApiConfiguration();
    let size = 0;
    for (const value of this._dataRef.data.values()) {
      if (this._isMapEntryTombstoned(value)) {
        continue;
      }
      size++;
    }
    return size;
  }
  *entries() {
    this._objects.throwIfInvalidAccessApiConfiguration();
    for (const [key, entry] of this._dataRef.data.entries()) {
      if (this._isMapEntryTombstoned(entry)) {
        continue;
      }
      const value = this._getResolvedValueFromObjectData(entry.data);
      yield [key, value];
    }
  }
  *keys() {
    for (const [key] of this.entries()) {
      yield key;
    }
  }
  *values() {
    for (const [_, value] of this.entries()) {
      yield value;
    }
  }
  /**
   * Send a MAP_SET operation to the realtime system to set a key on this LiveMap object to a specified value.
   *
   * This does not modify the underlying data of this LiveMap object. Instead, the change will be applied when
   * the published MAP_SET operation is echoed back to the client and applied to the object following the regular
   * operation application procedure.
   *
   * @returns A promise which resolves upon receiving the ACK message for the published operation message.
   */
  async set(key, value) {
    this._objects.throwIfInvalidWriteApiConfiguration();
    const msg = _LiveMap.createMapSetMessage(this._objects, this.getObjectId(), key, value);
    return this._objects.publish([msg]);
  }
  /**
   * Send a MAP_REMOVE operation to the realtime system to tombstone a key on this LiveMap object.
   *
   * This does not modify the underlying data of this LiveMap object. Instead, the change will be applied when
   * the published MAP_REMOVE operation is echoed back to the client and applied to the object following the regular
   * operation application procedure.
   *
   * @returns A promise which resolves upon receiving the ACK message for the published operation message.
   */
  async remove(key) {
    this._objects.throwIfInvalidWriteApiConfiguration();
    const msg = _LiveMap.createMapRemoveMessage(this._objects, this.getObjectId(), key);
    return this._objects.publish([msg]);
  }
  /**
   * @internal
   */
  applyOperation(op, msg) {
    var _a;
    if (op.objectId !== this.getObjectId()) {
      throw new this._client.ErrorInfo(
        `Cannot apply object operation with objectId=${op.objectId}, to this LiveMap with objectId=${this.getObjectId()}`,
        92e3,
        500
      );
    }
    const opSerial = msg.serial;
    const opSiteCode = msg.siteCode;
    if (!this._canApplyOperation(opSerial, opSiteCode)) {
      this._client.Logger.logAction(
        this._client.logger,
        this._client.Logger.LOG_MICRO,
        "LiveMap.applyOperation()",
        `skipping ${op.action} op: op serial ${opSerial.toString()} <= site serial ${(_a = this._siteTimeserials[opSiteCode]) == null ? void 0 : _a.toString()}; objectId=${this.getObjectId()}`
      );
      return;
    }
    this._siteTimeserials[opSiteCode] = opSerial;
    if (this.isTombstoned()) {
      return;
    }
    let update;
    switch (op.action) {
      case 0 /* MAP_CREATE */:
        update = this._applyMapCreate(op, msg);
        break;
      case 1 /* MAP_SET */:
        if (this._client.Utils.isNil(op.mapOp)) {
          this._throwNoPayloadError(op);
          return;
        } else {
          update = this._applyMapSet(op.mapOp, opSerial, msg);
        }
        break;
      case 2 /* MAP_REMOVE */:
        if (this._client.Utils.isNil(op.mapOp)) {
          this._throwNoPayloadError(op);
          return;
        } else {
          update = this._applyMapRemove(op.mapOp, opSerial, msg.serialTimestamp, msg);
        }
        break;
      case 5 /* OBJECT_DELETE */:
        update = this._applyObjectDelete(msg);
        break;
      default:
        throw new this._client.ErrorInfo(
          `Invalid ${op.action} op for LiveMap objectId=${this.getObjectId()}`,
          92e3,
          500
        );
    }
    this.notifyUpdated(update);
  }
  /**
   * @internal
   * @spec RTLM6
   */
  overrideWithObjectState(objectMessage) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _i;
    const objectState = objectMessage.object;
    if (objectState == null) {
      throw new this._client.ErrorInfo(`Missing object state; LiveMap objectId=${this.getObjectId()}`, 92e3, 500);
    }
    if (objectState.objectId !== this.getObjectId()) {
      throw new this._client.ErrorInfo(
        `Invalid object state: object state objectId=${objectState.objectId}; LiveMap objectId=${this.getObjectId()}`,
        92e3,
        500
      );
    }
    if (((_a = objectState.map) == null ? void 0 : _a.semantics) !== this._semantics) {
      throw new this._client.ErrorInfo(
        `Invalid object state: object state map semantics=${(_b = objectState.map) == null ? void 0 : _b.semantics}; LiveMap semantics=${this._semantics}`,
        92e3,
        500
      );
    }
    if (!this._client.Utils.isNil(objectState.createOp)) {
      if (objectState.createOp.objectId !== this.getObjectId()) {
        throw new this._client.ErrorInfo(
          `Invalid object state: object state createOp objectId=${(_c = objectState.createOp) == null ? void 0 : _c.objectId}; LiveMap objectId=${this.getObjectId()}`,
          92e3,
          500
        );
      }
      if (objectState.createOp.action !== 0 /* MAP_CREATE */) {
        throw new this._client.ErrorInfo(
          `Invalid object state: object state createOp action=${(_d = objectState.createOp) == null ? void 0 : _d.action}; LiveMap objectId=${this.getObjectId()}`,
          92e3,
          500
        );
      }
      if (((_e = objectState.createOp.map) == null ? void 0 : _e.semantics) !== this._semantics) {
        throw new this._client.ErrorInfo(
          `Invalid object state: object state createOp map semantics=${(_f = objectState.createOp.map) == null ? void 0 : _f.semantics}; LiveMap semantics=${this._semantics}`,
          92e3,
          500
        );
      }
    }
    this._siteTimeserials = (_g = objectState.siteTimeserials) != null ? _g : {};
    if (this.isTombstoned()) {
      return { noop: true };
    }
    const previousDataRef = this._dataRef;
    if (objectState.tombstone) {
      this.tombstone(objectMessage);
    } else {
      this._createOperationIsMerged = false;
      this._dataRef = this._liveMapDataFromMapEntries((_i = (_h = objectState.map) == null ? void 0 : _h.entries) != null ? _i : {});
      if (!this._client.Utils.isNil(objectState.createOp)) {
        this._mergeInitialDataFromCreateOperation(objectState.createOp, objectMessage);
      }
    }
    const update = this._updateFromDataDiff(previousDataRef, this._dataRef);
    update.clientId = objectMessage.clientId;
    update.connectionId = objectMessage.connectionId;
    return update;
  }
  /**
   * @internal
   */
  onGCInterval() {
    const keysToDelete = [];
    for (const [key, value] of this._dataRef.data.entries()) {
      if (value.tombstone === true && Date.now() - value.tombstonedAt >= this._objects.gcGracePeriod) {
        keysToDelete.push(key);
      }
    }
    keysToDelete.forEach((x) => this._dataRef.data.delete(x));
  }
  /** @spec RTLM4 */
  _getZeroValueData() {
    return { data: /* @__PURE__ */ new Map() };
  }
  _updateFromDataDiff(prevDataRef, newDataRef) {
    const update = { update: {} };
    for (const [key, currentEntry] of prevDataRef.data.entries()) {
      const typedKey = key;
      if (currentEntry.tombstone === false && !newDataRef.data.has(typedKey)) {
        update.update[typedKey] = "removed";
      }
    }
    for (const [key, newEntry] of newDataRef.data.entries()) {
      const typedKey = key;
      if (!prevDataRef.data.has(typedKey)) {
        if (newEntry.tombstone === false) {
          update.update[typedKey] = "updated";
          continue;
        }
        if (newEntry.tombstone === true) {
          continue;
        }
      }
      const currentEntry = prevDataRef.data.get(typedKey);
      if (currentEntry.tombstone === true && newEntry.tombstone === false) {
        update.update[typedKey] = "updated";
        continue;
      }
      if (currentEntry.tombstone === false && newEntry.tombstone === true) {
        update.update[typedKey] = "removed";
        continue;
      }
      if (currentEntry.tombstone === true && newEntry.tombstone === true) {
        continue;
      }
      const valueChanged = !(0, import_dequal.dequal)(currentEntry.data, newEntry.data);
      if (valueChanged) {
        update.update[typedKey] = "updated";
        continue;
      }
    }
    return update;
  }
  _mergeInitialDataFromCreateOperation(objectOperation, msg) {
    var _a;
    if (this._client.Utils.isNil(objectOperation.map)) {
      return { update: {}, clientId: msg.clientId, connectionId: msg.connectionId };
    }
    const aggregatedUpdate = { update: {}, clientId: msg.clientId, connectionId: msg.connectionId };
    Object.entries((_a = objectOperation.map.entries) != null ? _a : {}).forEach(([key, entry]) => {
      const opSerial = entry.timeserial;
      let update;
      if (entry.tombstone === true) {
        update = this._applyMapRemove({ key }, opSerial, entry.serialTimestamp, msg);
      } else {
        update = this._applyMapSet({ key, data: entry.data }, opSerial, msg);
      }
      if (update.noop) {
        return;
      }
      Object.assign(aggregatedUpdate.update, update.update);
    });
    this._createOperationIsMerged = true;
    return aggregatedUpdate;
  }
  _throwNoPayloadError(op) {
    throw new this._client.ErrorInfo(
      `No payload found for ${op.action} op for LiveMap objectId=${this.getObjectId()}`,
      92e3,
      500
    );
  }
  _applyMapCreate(op, msg) {
    var _a, _b;
    if (this._createOperationIsMerged) {
      this._client.Logger.logAction(
        this._client.logger,
        this._client.Logger.LOG_MICRO,
        "LiveMap._applyMapCreate()",
        `skipping applying MAP_CREATE op on a map instance as it was already applied before; objectId=${this.getObjectId()}`
      );
      return { noop: true };
    }
    if (this._semantics !== ((_a = op.map) == null ? void 0 : _a.semantics)) {
      throw new this._client.ErrorInfo(
        `Cannot apply MAP_CREATE op on LiveMap objectId=${this.getObjectId()}; map's semantics=${this._semantics}, but op expected ${(_b = op.map) == null ? void 0 : _b.semantics}`,
        92e3,
        500
      );
    }
    return this._mergeInitialDataFromCreateOperation(op, msg);
  }
  /** @spec RTLM7 */
  _applyMapSet(op, opSerial, msg) {
    var _a;
    const { ErrorInfo, Utils } = this._client;
    const existingEntry = this._dataRef.data.get(op.key);
    if (existingEntry && !this._canApplyMapOperation(existingEntry.timeserial, opSerial)) {
      this._client.Logger.logAction(
        this._client.logger,
        this._client.Logger.LOG_MICRO,
        "LiveMap._applyMapSet()",
        `skipping update for key="${op.key}": op serial ${opSerial == null ? void 0 : opSerial.toString()} <= entry serial ${(_a = existingEntry.timeserial) == null ? void 0 : _a.toString()}; objectId=${this.getObjectId()}`
      );
      return { noop: true };
    }
    if (Utils.isNil(op.data) || Utils.isNil(op.data.objectId) && Utils.isNil(op.data.value)) {
      throw new ErrorInfo(
        `Invalid object data for MAP_SET op on objectId=${this.getObjectId()} on key="${op.key}"`,
        92e3,
        500
      );
    }
    let liveData;
    if (!Utils.isNil(op.data.objectId)) {
      liveData = { objectId: op.data.objectId };
      this._objects.getPool().createZeroValueObjectIfNotExists(op.data.objectId);
    } else {
      liveData = { value: op.data.value };
    }
    if (existingEntry) {
      existingEntry.tombstone = false;
      existingEntry.tombstonedAt = void 0;
      existingEntry.timeserial = opSerial;
      existingEntry.data = liveData;
    } else {
      const newEntry = {
        tombstone: false,
        // RTLM7b2
        tombstonedAt: void 0,
        timeserial: opSerial,
        data: liveData
      };
      this._dataRef.data.set(op.key, newEntry);
    }
    const update = { update: {}, clientId: msg.clientId, connectionId: msg.connectionId };
    const typedKey = op.key;
    update.update[typedKey] = "updated";
    return update;
  }
  /** @spec RTLM8 */
  _applyMapRemove(op, opSerial, opTimestamp, msg) {
    var _a;
    const existingEntry = this._dataRef.data.get(op.key);
    if (existingEntry && !this._canApplyMapOperation(existingEntry.timeserial, opSerial)) {
      this._client.Logger.logAction(
        this._client.logger,
        this._client.Logger.LOG_MICRO,
        "LiveMap._applyMapRemove()",
        `skipping remove for key="${op.key}": op serial ${opSerial == null ? void 0 : opSerial.toString()} <= entry serial ${(_a = existingEntry.timeserial) == null ? void 0 : _a.toString()}; objectId=${this.getObjectId()}`
      );
      return { noop: true };
    }
    let tombstonedAt;
    if (opTimestamp != null) {
      tombstonedAt = opTimestamp;
    } else {
      this._client.Logger.logAction(
        this._client.logger,
        this._client.Logger.LOG_MINOR,
        "LiveMap._applyMapRemove()",
        `map key has been removed but no "serialTimestamp" found in the message, using local clock instead; key="${op.key}", objectId=${this.getObjectId()}`
      );
      tombstonedAt = Date.now();
    }
    if (existingEntry) {
      existingEntry.tombstone = true;
      existingEntry.tombstonedAt = tombstonedAt;
      existingEntry.timeserial = opSerial;
      existingEntry.data = void 0;
    } else {
      const newEntry = {
        tombstone: true,
        // RTLM8b2
        tombstonedAt,
        timeserial: opSerial,
        data: void 0
      };
      this._dataRef.data.set(op.key, newEntry);
    }
    const update = { update: {}, clientId: msg.clientId, connectionId: msg.connectionId };
    const typedKey = op.key;
    update.update[typedKey] = "removed";
    return update;
  }
  /**
   * Returns true if the serials of the given operation and entry indicate that
   * the operation should be applied to the entry, following the CRDT semantics of this LiveMap.
   * @spec RTLM9
   */
  _canApplyMapOperation(mapEntrySerial, opSerial) {
    if (!mapEntrySerial && !opSerial) {
      return false;
    }
    if (!mapEntrySerial) {
      return true;
    }
    if (!opSerial) {
      return false;
    }
    return opSerial > mapEntrySerial;
  }
  _liveMapDataFromMapEntries(entries) {
    const liveMapData = {
      data: /* @__PURE__ */ new Map()
    };
    Object.entries(entries != null ? entries : {}).forEach(([key, entry]) => {
      let liveData = void 0;
      if (!this._client.Utils.isNil(entry.data)) {
        if (!this._client.Utils.isNil(entry.data.objectId)) {
          liveData = { objectId: entry.data.objectId };
        } else {
          liveData = { value: entry.data.value };
        }
      }
      let tombstonedAt;
      if (entry.tombstone === true) {
        if (entry.serialTimestamp != null) {
          tombstonedAt = entry.serialTimestamp;
        } else {
          this._client.Logger.logAction(
            this._client.logger,
            this._client.Logger.LOG_MINOR,
            "LiveMap._liveMapDataFromMapEntries()",
            `map key is removed but no "serialTimestamp" found, using local clock instead; key="${key}", objectId=${this.getObjectId()}`
          );
          tombstonedAt = Date.now();
        }
      }
      const liveDataEntry = {
        timeserial: entry.timeserial,
        data: liveData,
        // consider object as tombstoned only if we received an explicit flag stating that. otherwise it exists
        tombstone: entry.tombstone === true,
        tombstonedAt
      };
      liveMapData.data.set(key, liveDataEntry);
    });
    return liveMapData;
  }
  /**
   * Returns value as is if object data stores a primitive type, or a reference to another LiveObject from the pool if it stores an objectId.
   */
  _getResolvedValueFromObjectData(data) {
    const primitiveValue = data.value;
    if (primitiveValue != null) {
      return primitiveValue;
    }
    const objectId = data.objectId;
    const refObject = this._objects.getPool().get(objectId);
    if (!refObject) {
      return void 0;
    }
    if (refObject.isTombstoned()) {
      return void 0;
    }
    return refObject;
  }
  _isMapEntryTombstoned(entry) {
    if (entry.tombstone === true) {
      return true;
    }
    const data = entry.data;
    if ("objectId" in data) {
      const refObject = this._objects.getPool().get(data.objectId);
      if (refObject == null ? void 0 : refObject.isTombstoned()) {
        return true;
      }
    }
    return false;
  }
};

// src/plugins/objects/batchcontextlivemap.ts
var BatchContextLiveMap = class {
  constructor(_batchContext, _objects, _map) {
    this._batchContext = _batchContext;
    this._objects = _objects;
    this._map = _map;
  }
  get(key) {
    this._objects.throwIfInvalidAccessApiConfiguration();
    this._batchContext.throwIfClosed();
    const value = this._map.get(key);
    if (value instanceof LiveObject) {
      return this._batchContext.getWrappedObject(value.getObjectId());
    } else {
      return value;
    }
  }
  size() {
    this._objects.throwIfInvalidAccessApiConfiguration();
    this._batchContext.throwIfClosed();
    return this._map.size();
  }
  *entries() {
    this._objects.throwIfInvalidAccessApiConfiguration();
    this._batchContext.throwIfClosed();
    yield* __yieldStar(this._map.entries());
  }
  *keys() {
    this._objects.throwIfInvalidAccessApiConfiguration();
    this._batchContext.throwIfClosed();
    yield* __yieldStar(this._map.keys());
  }
  *values() {
    this._objects.throwIfInvalidAccessApiConfiguration();
    this._batchContext.throwIfClosed();
    yield* __yieldStar(this._map.values());
  }
  set(key, value) {
    this._objects.throwIfInvalidWriteApiConfiguration();
    this._batchContext.throwIfClosed();
    const msg = LiveMap.createMapSetMessage(this._objects, this._map.getObjectId(), key, value);
    this._batchContext.queueMessage(msg);
  }
  remove(key) {
    this._objects.throwIfInvalidWriteApiConfiguration();
    this._batchContext.throwIfClosed();
    const msg = LiveMap.createMapRemoveMessage(this._objects, this._map.getObjectId(), key);
    this._batchContext.queueMessage(msg);
  }
};

// src/plugins/objects/defaults.ts
var DEFAULTS = {
  gcInterval: 1e3 * 60 * 5,
  // 5 minutes
  /**
   * The SDK will attempt to use the `objectsGCGracePeriod` value provided by the server in the `connectionDetails` object of the `CONNECTED` event.
   * If the server does not provide this value, the SDK will fall back to this default value.
   *
   * Must be > 2 minutes to ensure we keep tombstones long enough to avoid the possibility of receiving an operation
   * with an earlier serial that would not have been applied if the tombstone still existed.
   *
   * Applies both for map entries tombstones and object tombstones.
   */
  gcGracePeriod: 1e3 * 60 * 60 * 24
  // 24 hours
};

// src/plugins/objects/objectspool.ts
var ROOT_OBJECT_ID = "root";
var ObjectsPool = class {
  constructor(_objects) {
    this._objects = _objects;
    var _a, _b;
    this._client = this._objects.getClient();
    this._pool = this._createInitialPool();
    this._gcInterval = setInterval(() => {
      this._onGCInterval();
    }, DEFAULTS.gcInterval);
    (_b = (_a = this._gcInterval).unref) == null ? void 0 : _b.call(_a);
  }
  get(objectId) {
    return this._pool.get(objectId);
  }
  /**
   * Deletes objects from the pool for which object ids are not found in the provided array of ids.
   */
  deleteExtraObjectIds(objectIds) {
    const poolObjectIds = [...this._pool.keys()];
    const extraObjectIds = poolObjectIds.filter((x) => !objectIds.includes(x));
    extraObjectIds.forEach((x) => this._pool.delete(x));
  }
  set(objectId, liveObject) {
    this._pool.set(objectId, liveObject);
  }
  /**
   * Removes all objects but root from the pool and clears the data for root.
   * Does not create a new root object, so the reference to the root object remains the same.
   */
  resetToInitialPool(emitUpdateEvents) {
    const root = this._pool.get(ROOT_OBJECT_ID);
    this._pool.clear();
    this._pool.set(root.getObjectId(), root);
    this.clearObjectsData(emitUpdateEvents);
  }
  /**
   * Clears the data stored for all objects in the pool.
   */
  clearObjectsData(emitUpdateEvents) {
    for (const object of this._pool.values()) {
      const update = object.clearData();
      if (emitUpdateEvents) {
        object.notifyUpdated(update);
      }
    }
  }
  /** @spec RTO6 */
  createZeroValueObjectIfNotExists(objectId) {
    const existingObject = this.get(objectId);
    if (existingObject) {
      return existingObject;
    }
    const parsedObjectId = ObjectId.fromString(this._client, objectId);
    let zeroValueObject;
    switch (parsedObjectId.type) {
      case "map": {
        zeroValueObject = LiveMap.zeroValue(this._objects, objectId);
        break;
      }
      case "counter":
        zeroValueObject = LiveCounter.zeroValue(this._objects, objectId);
        break;
    }
    this.set(objectId, zeroValueObject);
    return zeroValueObject;
  }
  _createInitialPool() {
    const pool = /* @__PURE__ */ new Map();
    const root = LiveMap.zeroValue(this._objects, ROOT_OBJECT_ID);
    pool.set(root.getObjectId(), root);
    return pool;
  }
  _onGCInterval() {
    const toDelete = [];
    for (const [objectId, obj] of this._pool.entries()) {
      if (obj.isTombstoned() && Date.now() - obj.tombstonedAt() >= this._objects.gcGracePeriod) {
        toDelete.push(objectId);
        continue;
      }
      obj.onGCInterval();
    }
    toDelete.forEach((x) => this._pool.delete(x));
  }
};

// src/plugins/objects/batchcontext.ts
var BatchContext = class {
  constructor(_objects, _root) {
    this._objects = _objects;
    this._root = _root;
    /** Maps object ids to the corresponding batch context object wrappers  */
    this._wrappedObjects = /* @__PURE__ */ new Map();
    this._queuedMessages = [];
    this._isClosed = false;
    this._client = _objects.getClient();
    this._wrappedObjects.set(this._root.getObjectId(), new BatchContextLiveMap(this, this._objects, this._root));
  }
  getRoot() {
    this._objects.throwIfInvalidAccessApiConfiguration();
    this.throwIfClosed();
    return this.getWrappedObject(ROOT_OBJECT_ID);
  }
  /**
   * @internal
   */
  getWrappedObject(objectId) {
    if (this._wrappedObjects.has(objectId)) {
      return this._wrappedObjects.get(objectId);
    }
    const originObject = this._objects.getPool().get(objectId);
    if (!originObject) {
      return void 0;
    }
    let wrappedObject;
    if (originObject instanceof LiveMap) {
      wrappedObject = new BatchContextLiveMap(this, this._objects, originObject);
    } else if (originObject instanceof LiveCounter) {
      wrappedObject = new BatchContextLiveCounter(this, this._objects, originObject);
    } else {
      throw new this._client.ErrorInfo(
        `Unknown LiveObject instance type: objectId=${originObject.getObjectId()}`,
        5e4,
        500
      );
    }
    this._wrappedObjects.set(objectId, wrappedObject);
    return wrappedObject;
  }
  /**
   * @internal
   */
  throwIfClosed() {
    if (this.isClosed()) {
      throw new this._client.ErrorInfo("Batch is closed", 4e4, 400);
    }
  }
  /**
   * @internal
   */
  isClosed() {
    return this._isClosed;
  }
  /**
   * @internal
   */
  close() {
    this._isClosed = true;
  }
  /**
   * @internal
   */
  queueMessage(msg) {
    this._queuedMessages.push(msg);
  }
  /**
   * @internal
   */
  async flush() {
    try {
      this.close();
      if (this._queuedMessages.length > 0) {
        await this._objects.publish(this._queuedMessages);
      }
    } finally {
      this._wrappedObjects.clear();
      this._queuedMessages = [];
    }
  }
};

// src/plugins/objects/syncobjectsdatapool.ts
var SyncObjectsDataPool = class {
  constructor(_objects) {
    this._objects = _objects;
    this._client = this._objects.getClient();
    this._channel = this._objects.getChannel();
    this._pool = /* @__PURE__ */ new Map();
  }
  entries() {
    return this._pool.entries();
  }
  size() {
    return this._pool.size;
  }
  isEmpty() {
    return this._pool.size === 0;
  }
  clear() {
    this._pool.clear();
  }
  applyObjectSyncMessages(objectMessages) {
    for (const objectMessage of objectMessages) {
      if (!objectMessage.object) {
        this._client.Logger.logAction(
          this._client.logger,
          this._client.Logger.LOG_MAJOR,
          "SyncObjectsDataPool.applyObjectSyncMessages()",
          `object message is received during OBJECT_SYNC without 'object' field, skipping message; message id: ${objectMessage.id}, channel: ${this._channel.name}`
        );
        continue;
      }
      const objectState = objectMessage.object;
      if (objectState.counter) {
        this._pool.set(objectState.objectId, this._createLiveCounterDataEntry(objectMessage));
      } else if (objectState.map) {
        this._pool.set(objectState.objectId, this._createLiveMapDataEntry(objectMessage));
      } else {
        this._client.Logger.logAction(
          this._client.logger,
          this._client.Logger.LOG_MAJOR,
          "SyncObjectsDataPool.applyObjectSyncMessages()",
          `received unsupported object state message during OBJECT_SYNC, expected 'counter' or 'map' to be present, skipping message; message id: ${objectMessage.id}, channel: ${this._channel.name}`
        );
      }
    }
  }
  _createLiveCounterDataEntry(objectMessage) {
    const newEntry = {
      objectMessage,
      objectType: "LiveCounter"
    };
    return newEntry;
  }
  _createLiveMapDataEntry(objectMessage) {
    const newEntry = {
      objectMessage,
      objectType: "LiveMap"
    };
    return newEntry;
  }
};

// src/plugins/objects/objects.ts
var StateToEventsMap = {
  initialized: void 0,
  syncing: "syncing" /* syncing */,
  synced: "synced" /* synced */
};
var Objects = class {
  constructor(channel) {
    var _a, _b;
    this._channel = channel;
    this._client = channel.client;
    this._state = "initialized" /* initialized */;
    this._eventEmitterInternal = new this._client.EventEmitter(this._client.logger);
    this._eventEmitterPublic = new this._client.EventEmitter(this._client.logger);
    this._objectsPool = new ObjectsPool(this);
    this._syncObjectsDataPool = new SyncObjectsDataPool(this);
    this._bufferedObjectOperations = [];
    this.gcGracePeriod = (_b = (_a = this._channel.connectionManager.connectionDetails) == null ? void 0 : _a.objectsGCGracePeriod) != null ? _b : DEFAULTS.gcGracePeriod;
    this._channel.connectionManager.on("connectiondetails", (details) => {
      var _a2;
      this.gcGracePeriod = (_a2 = details.objectsGCGracePeriod) != null ? _a2 : DEFAULTS.gcGracePeriod;
    });
  }
  /**
   * When called without a type variable, we return a default root type which is based on globally defined interface for Objects feature.
   * A user can provide an explicit type for the getRoot method to explicitly set the type structure on this particular channel.
   * This is useful when working with multiple channels with different underlying data structure.
   * @spec RTO1
   */
  async getRoot() {
    this.throwIfInvalidAccessApiConfiguration();
    if (this._state !== "synced" /* synced */) {
      await this._eventEmitterInternal.once("synced" /* synced */);
    }
    return this._objectsPool.get(ROOT_OBJECT_ID);
  }
  /**
   * Provides access to the synchronous write API for Objects that can be used to batch multiple operations together in a single channel message.
   */
  async batch(callback) {
    this.throwIfInvalidWriteApiConfiguration();
    const root = await this.getRoot();
    const context = new BatchContext(this, root);
    try {
      callback(context);
      await context.flush();
    } finally {
      context.close();
    }
  }
  /**
   * Send a MAP_CREATE operation to the realtime system to create a new map object in the pool.
   *
   * Once the ACK message is received, the method returns the object from the local pool if it got created due to
   * the echoed MAP_CREATE operation, or if it wasn't received yet, the method creates a new object locally using the provided data and returns it.
   *
   * @returns A promise which resolves upon receiving the ACK message for the published operation message. A promise is resolved with an object containing provided data.
   */
  async createMap(entries) {
    var _a;
    this.throwIfInvalidWriteApiConfiguration();
    const msg = await LiveMap.createMapCreateMessage(this, entries);
    const objectId = (_a = msg.operation) == null ? void 0 : _a.objectId;
    await this.publish([msg]);
    if (this._objectsPool.get(objectId)) {
      return this._objectsPool.get(objectId);
    }
    const map = LiveMap.fromObjectOperation(this, msg);
    this._objectsPool.set(objectId, map);
    return map;
  }
  /**
   * Send a COUNTER_CREATE operation to the realtime system to create a new counter object in the pool.
   *
   * Once the ACK message is received, the method returns the object from the local pool if it got created due to
   * the echoed COUNTER_CREATE operation, or if it wasn't received yet, the method creates a new object locally using the provided data and returns it.
   *
   * @returns A promise which resolves upon receiving the ACK message for the published operation message. A promise is resolved with an object containing provided data.
   */
  async createCounter(count) {
    var _a;
    this.throwIfInvalidWriteApiConfiguration();
    const msg = await LiveCounter.createCounterCreateMessage(this, count);
    const objectId = (_a = msg.operation) == null ? void 0 : _a.objectId;
    await this.publish([msg]);
    if (this._objectsPool.get(objectId)) {
      return this._objectsPool.get(objectId);
    }
    const counter = LiveCounter.fromObjectOperation(this, msg);
    this._objectsPool.set(objectId, counter);
    return counter;
  }
  on(event, callback) {
    this._eventEmitterPublic.on(event, callback);
    const off = () => {
      this._eventEmitterPublic.off(event, callback);
    };
    return { off };
  }
  off(event, callback) {
    if (this._client.Utils.isNil(event) && this._client.Utils.isNil(callback)) {
      return;
    }
    this._eventEmitterPublic.off(event, callback);
  }
  offAll() {
    this._eventEmitterPublic.off();
  }
  /**
   * @internal
   */
  getPool() {
    return this._objectsPool;
  }
  /**
   * @internal
   */
  getChannel() {
    return this._channel;
  }
  /**
   * @internal
   */
  getClient() {
    return this._client;
  }
  /**
   * @internal
   * @spec RTO5
   */
  handleObjectSyncMessages(objectMessages, syncChannelSerial) {
    const { syncId, syncCursor } = this._parseSyncChannelSerial(syncChannelSerial);
    const newSyncSequence = this._currentSyncId !== syncId;
    if (newSyncSequence) {
      this._startNewSync(syncId, syncCursor);
    }
    this._syncObjectsDataPool.applyObjectSyncMessages(objectMessages);
    if (!syncCursor) {
      this._endSync(newSyncSequence);
    }
  }
  /**
   * @internal
   */
  handleObjectMessages(objectMessages) {
    if (this._state !== "synced" /* synced */) {
      this._bufferedObjectOperations.push(...objectMessages);
      return;
    }
    this._applyObjectMessages(objectMessages);
  }
  /**
   * @internal
   * @spec RTO4
   */
  onAttached(hasObjects) {
    this._client.Logger.logAction(
      this._client.logger,
      this._client.Logger.LOG_MINOR,
      "Objects.onAttached()",
      `channel=${this._channel.name}, hasObjects=${hasObjects}`
    );
    const fromInitializedState = this._state === "initialized" /* initialized */;
    if (hasObjects || fromInitializedState) {
      this._startNewSync();
    }
    if (!hasObjects) {
      this._objectsPool.resetToInitialPool(true);
      this._syncObjectsDataPool.clear();
      this._endSync(fromInitializedState);
    }
  }
  /**
   * @internal
   */
  actOnChannelState(state, hasObjects) {
    switch (state) {
      case "attached":
        this.onAttached(hasObjects);
        break;
      case "detached":
      case "failed":
        this._objectsPool.clearObjectsData(false);
        this._syncObjectsDataPool.clear();
        break;
    }
  }
  /**
   * @internal
   */
  async publish(objectMessages) {
    this._channel.throwIfUnpublishableState();
    const encodedMsgs = objectMessages.map((x) => x.encode(this._client));
    const maxMessageSize = this._client.options.maxMessageSize;
    const size = encodedMsgs.reduce((acc, msg) => acc + msg.getMessageSize(), 0);
    if (size > maxMessageSize) {
      throw new this._client.ErrorInfo(
        `Maximum size of object messages that can be published at once exceeded (was ${size} bytes; limit is ${maxMessageSize} bytes)`,
        40009,
        400
      );
    }
    return this._channel.sendState(encodedMsgs);
  }
  /**
   * @internal
   */
  throwIfInvalidAccessApiConfiguration() {
    this._throwIfMissingChannelMode("object_subscribe");
    this._throwIfInChannelState(["detached", "failed"]);
  }
  /**
   * @internal
   */
  throwIfInvalidWriteApiConfiguration() {
    this._throwIfMissingChannelMode("object_publish");
    this._throwIfInChannelState(["detached", "failed", "suspended"]);
    this._throwIfEchoMessagesDisabled();
  }
  _startNewSync(syncId, syncCursor) {
    this._bufferedObjectOperations = [];
    this._syncObjectsDataPool.clear();
    this._currentSyncId = syncId;
    this._currentSyncCursor = syncCursor;
    this._stateChange("syncing" /* syncing */, false);
  }
  /** @spec RTO5c */
  _endSync(deferStateEvent) {
    this._applySync();
    this._applyObjectMessages(this._bufferedObjectOperations);
    this._bufferedObjectOperations = [];
    this._syncObjectsDataPool.clear();
    this._currentSyncId = void 0;
    this._currentSyncCursor = void 0;
    this._stateChange("synced" /* synced */, deferStateEvent);
  }
  _parseSyncChannelSerial(syncChannelSerial) {
    let match;
    let syncId = void 0;
    let syncCursor = void 0;
    if (syncChannelSerial && (match = syncChannelSerial.match(/^([\w-]+):(.*)$/))) {
      syncId = match[1];
      syncCursor = match[2];
    }
    return {
      syncId,
      syncCursor
    };
  }
  _applySync() {
    if (this._syncObjectsDataPool.isEmpty()) {
      return;
    }
    const receivedObjectIds = /* @__PURE__ */ new Set();
    const existingObjectUpdates = [];
    for (const [objectId, entry] of this._syncObjectsDataPool.entries()) {
      receivedObjectIds.add(objectId);
      const existingObject = this._objectsPool.get(objectId);
      if (existingObject) {
        const update = existingObject.overrideWithObjectState(entry.objectMessage);
        existingObjectUpdates.push({ object: existingObject, update });
        continue;
      }
      let newObject;
      const objectType = entry.objectType;
      switch (objectType) {
        case "LiveCounter":
          newObject = LiveCounter.fromObjectState(this, entry.objectMessage);
          break;
        case "LiveMap":
          newObject = LiveMap.fromObjectState(this, entry.objectMessage);
          break;
        default:
          throw new this._client.ErrorInfo(`Unknown LiveObject type: ${objectType}`, 5e4, 500);
      }
      this._objectsPool.set(objectId, newObject);
    }
    this._objectsPool.deleteExtraObjectIds([...receivedObjectIds]);
    existingObjectUpdates.forEach(({ object, update }) => object.notifyUpdated(update));
  }
  _applyObjectMessages(objectMessages) {
    for (const objectMessage of objectMessages) {
      if (!objectMessage.operation) {
        this._client.Logger.logAction(
          this._client.logger,
          this._client.Logger.LOG_MAJOR,
          "Objects._applyObjectMessages()",
          `object operation message is received without 'operation' field, skipping message; message id: ${objectMessage.id}, channel: ${this._channel.name}`
        );
        continue;
      }
      const objectOperation = objectMessage.operation;
      switch (objectOperation.action) {
        case 0 /* MAP_CREATE */:
        case 3 /* COUNTER_CREATE */:
        case 1 /* MAP_SET */:
        case 2 /* MAP_REMOVE */:
        case 4 /* COUNTER_INC */:
        case 5 /* OBJECT_DELETE */:
          this._objectsPool.createZeroValueObjectIfNotExists(objectOperation.objectId);
          this._objectsPool.get(objectOperation.objectId).applyOperation(objectOperation, objectMessage);
          break;
        default:
          this._client.Logger.logAction(
            this._client.logger,
            this._client.Logger.LOG_MAJOR,
            "Objects._applyObjectMessages()",
            `received unsupported action in object operation message: ${objectOperation.action}, skipping message; message id: ${objectMessage.id}, channel: ${this._channel.name}`
          );
      }
    }
  }
  /** @spec RTO2 */
  _throwIfMissingChannelMode(expectedMode) {
    var _a;
    if (this._channel.modes != null && !this._channel.modes.includes(expectedMode)) {
      throw new this._client.ErrorInfo(`"${expectedMode}" channel mode must be set for this operation`, 40024, 400);
    }
    if (!this._client.Utils.allToLowerCase((_a = this._channel.channelOptions.modes) != null ? _a : []).includes(expectedMode)) {
      throw new this._client.ErrorInfo(`"${expectedMode}" channel mode must be set for this operation`, 40024, 400);
    }
  }
  _stateChange(state, deferEvent) {
    if (this._state === state) {
      return;
    }
    this._state = state;
    const event = StateToEventsMap[state];
    if (!event) {
      return;
    }
    if (deferEvent) {
      this._client.Platform.Config.nextTick(() => {
        this._eventEmitterInternal.emit(event);
        this._eventEmitterPublic.emit(event);
      });
    } else {
      this._eventEmitterInternal.emit(event);
      this._eventEmitterPublic.emit(event);
    }
  }
  _throwIfInChannelState(channelState) {
    if (channelState.includes(this._channel.state)) {
      throw this._client.ErrorInfo.fromValues(this._channel.invalidStateError());
    }
  }
  _throwIfEchoMessagesDisabled() {
    if (this._channel.client.options.echoMessages === false) {
      throw new this._channel.client.ErrorInfo(
        `"echoMessages" client option must be enabled for this operation`,
        4e4,
        400
      );
    }
  }
};
// Used by tests
Objects._DEFAULTS = DEFAULTS;

// src/plugins/objects/index.ts
var objects_default = {
  Objects,
  ObjectMessage,
  WireObjectMessage
};
if (typeof module.exports == "object" && typeof exports == "object") {
  var __cp = (to, from, except, desc) => {
    if ((from && typeof from === "object") || typeof from === "function") {
      for (let key of Object.getOwnPropertyNames(from)) {
        if (!Object.prototype.hasOwnProperty.call(to, key) && key !== except)
        Object.defineProperty(to, key, {
          get: () => from[key],
          enumerable: !(desc = Object.getOwnPropertyDescriptor(from, key)) || desc.enumerable,
        });
      }
    }
    return to;
  };
  module.exports = __cp(module.exports, exports);
}
return module.exports;
}))
//# sourceMappingURL=objects.js.map
