import { AsyncStream as b } from "../async-stream/index.js";
import { K } from "../shared-types-CIxCt9tj.js";
class u extends K {
  constructor(t) {
    super(t), this.name = "StreamedDataObjectError";
  }
}
class d extends u {
  constructor(t, e) {
    super(`Invalid object type provided: expected ${t}, got ${e}`), this.name = "InvalidObjectType";
  }
  get userFacingSessionErrorMessage() {
    return "Invalid data type";
  }
}
class p extends u {
  constructor(t, e, s) {
    super(`Invalid type at keypath "${t}": expected ${e}, got ${s}`), this.name = "InvalidTypeAtKeypath";
  }
  get userFacingSessionErrorMessage() {
    return "Invalid data structure";
  }
}
class A extends u {
  constructor() {
    super("Invalid root object: must be an array or object"), this.name = "InvalidRootObject";
  }
  get userFacingSessionErrorMessage() {
    return "Invalid data format";
  }
}
class f extends u {
  constructor(t) {
    super(`Expected data at keypath "${t}" but none was found`), this.name = "ExpectedDataAtKeypath";
  }
  get userFacingSessionErrorMessage() {
    return "Missing required data";
  }
}
class n {
  static ROOT = "root";
  data;
  closed = !1;
  streamPosition = 0;
  dataByKeypath = /* @__PURE__ */ new Map();
  incompleteDataByKeypath = /* @__PURE__ */ new Set();
  completedDataByKeypath = /* @__PURE__ */ new Set();
  subscribersByKeypath = /* @__PURE__ */ new Map();
  constructor(t) {
    this.data = t, this.dataByKeypath.set(n.ROOT, this.data), this._refreshDataReferencesAtKeypath(n.ROOT, this.data), this._updateOpenKeys(n.ROOT, t);
  }
  static ofArray() {
    return new n([]);
  }
  static ofObject() {
    return new n({});
  }
  static ofData(t) {
    if (typeof t != "object" && !Array.isArray(t))
      throw new A();
    return new n(t);
  }
  getRootValue() {
    return this.getValueAtKeypath(n.ROOT);
  }
  getValueAtKeypath(t) {
    return this.dataByKeypath.get(t);
  }
  extendRootArray(t) {
    this.extendArrayAtKeypath(n.ROOT, t);
  }
  extendArrayAtKeypath(t, e) {
    const s = this.dataByKeypath.get(t);
    if (!s) {
      this._setDataAtKeypath(t, e), this._updateOpenKeys(t, e);
      return;
    }
    if (Array.isArray(s) && Array.isArray(e)) {
      for (const r of e)
        this._appendArrayAtKeypath(t, r);
      this._updateOpenKeys(t, e);
      return;
    }
    if (!Array.isArray(s))
      throw new p(t, "array-like", typeof s);
    if (!Array.isArray(e))
      throw new d("array-like", typeof e);
  }
  appendArrayAtKeypath(t, e) {
    this._appendArrayAtKeypath(t, e), this._updateOpenKeys(t, e);
  }
  _appendArrayAtKeypath(t, e) {
    const s = this.dataByKeypath.get(t);
    if (!s)
      throw new f(t);
    if (!Array.isArray(s))
      throw new p(t, "array-like", typeof s);
    const r = s.length, i = `${t}.${r}`;
    s.push(e), this.dataByKeypath.set(i, e), this._refreshDataReferencesAtKeypath(i, e), this._emitArrayExtended(t, r, e);
  }
  mergeRootObject(t) {
    this.mergeObjectAtKeypath(n.ROOT, t);
  }
  mergeObjectAtKeypath(t, e) {
    this._mergeObjectAtKeypath(t, e), this._updateOpenKeys(t, e);
  }
  _mergeObjectAtKeypath(t, e) {
    const s = this.dataByKeypath.get(t), r = typeof s, i = typeof e;
    if (!s) {
      this._setDataAtKeypath(t, e);
      return;
    }
    if (r === "object" && i === "object") {
      for (const [o, c] of Object.entries(e)) {
        const a = `${t}.${o}`;
        s[o] ? this._mergeObjectAtKeypath(a, c) : this._setDataAtKeypath(a, c);
      }
      this._emitValueWritten(t, this.dataByKeypath.get(t));
      return;
    }
    if (r !== "object")
      throw new p(t, "object", r);
    if (i !== "object")
      throw new d("object", i);
  }
  _setDataAtKeypath(t, e, s) {
    const r = t.lastIndexOf("."), i = t.slice(0, Math.max(0, r)), o = t.slice(Math.max(0, r + 1)), c = this.dataByKeypath.get(i);
    if (!c)
      throw new f(i);
    if (Array.isArray(e)) {
      const a = [];
      this.dataByKeypath.set(t, a), Object.assign(c, { [o]: a }), this._refreshDataReferencesAtKeypath(t, a);
      for (const [y, l] of e.entries())
        this._setDataAtKeypath(`${t}.${y}`, l);
      this._emitValueWritten(t, this.dataByKeypath.get(t));
      return;
    }
    if (typeof e == "object" && e !== null) {
      const a = {};
      this.dataByKeypath.set(t, a), Object.assign(c, { [o]: a }), this._refreshDataReferencesAtKeypath(t, a);
      for (const [y, l] of Object.entries(e))
        this._setDataAtKeypath(`${t}.${y}`, l);
      this._emitValueWritten(t, this.dataByKeypath.get(t)), this._emitValueWritten(i, this.dataByKeypath.get(i));
      return;
    }
    c[o] && c[o] === e || (this.dataByKeypath.set(t, e), Object.assign(c, { [o]: e }), this._refreshDataReferencesAtKeypath(t, e), typeof e == "string" ? this._emitStringExtended(t, e, s || e) : this._emitValueWritten(t, e));
  }
  extendStringAtKeypath(t, e) {
    const s = this.dataByKeypath.get(t), r = typeof s;
    if (!s) {
      this._setDataAtKeypath(t, e), this._updateOpenKeys(t, e);
      return;
    }
    if (r === "string" && typeof e == "string") {
      const i = s + e;
      this._setDataAtKeypath(t, i, e), this._updateOpenKeys(t, i);
      return;
    }
    if (r !== "string")
      throw new p(t, "string", r);
    if (typeof e != "string")
      throw new d("string", typeof e);
  }
  _refreshDataReferencesAtKeypath(t, e) {
    if (Array.isArray(e))
      for (const [s, r] of e.entries()) {
        const i = `${t}.${s}`;
        this.dataByKeypath.set(i, r), this._refreshDataReferencesAtKeypath(i, r);
      }
    if (typeof e == "object" && e !== null)
      for (const [s, r] of Object.entries(e)) {
        const i = `${t}.${s}`;
        this.dataByKeypath.set(i, r), this._refreshDataReferencesAtKeypath(i, r);
      }
  }
  _updateOpenKeys(t, e) {
    const s = /* @__PURE__ */ new Set([
      ...this._computeParentKeypaths(t),
      ...this._computeAllObjectKeypaths(t, e)
    ]), r = [];
    for (const i of this.incompleteDataByKeypath)
      s.has(i) || r.push(i);
    r.sort((i, o) => o.localeCompare(i));
    for (const i of r)
      this._emitObjectCompleted(i), this.completedDataByKeypath.add(i);
    this.incompleteDataByKeypath = s;
  }
  _computeParentKeypaths(t) {
    const e = [], s = t.split(".");
    for (let r = 0; r < s.length; r++)
      e.push(s.slice(0, r).join("."));
    return e;
  }
  _computeAllObjectKeypaths(t, e) {
    const s = [t];
    if (Array.isArray(e))
      for (const [r, i] of e.entries()) {
        const o = `${t}.${r}`;
        s.push(...this._computeAllObjectKeypaths(o, i));
      }
    if (typeof e == "object" && e !== null)
      for (const [r, i] of Object.entries(e)) {
        const o = `${t}.${r}`;
        s.push(...this._computeAllObjectKeypaths(o, i));
      }
    return s;
  }
  close() {
    const t = /* @__PURE__ */ new Set();
    for (const s of this.subscribersByKeypath.keys())
      t.add(s);
    for (const s of this.incompleteDataByKeypath.keys())
      t.add(s);
    const e = [...t].sort((s, r) => r.length - s.length);
    for (const s of e)
      s !== "*" && this._emitObjectCompleted(s);
    for (const s of this.subscribersByKeypath.keys())
      for (const r of this._getSubscribers(s))
        r.close();
    this.subscribersByKeypath.clear(), this.closed = !0;
  }
  subscribe(t) {
    const e = {
      ctx: this,
      type: "ObjectSubscribed",
      streamPosition: this.streamPosition,
      keyPath: t,
      value: this._copy(this.getValueAtKeypath(t))
    }, s = new b({ messages: [e] });
    if (this.completedDataByKeypath.has(t) || this.closed) {
      const i = this.getValueAtKeypath(t) ? {
        ctx: this,
        type: "ObjectCompleted",
        streamPosition: this.streamPosition,
        keyPath: t,
        value: this._copy(this.getValueAtKeypath(t))
      } : {
        ctx: this,
        type: "ObjectCompletedWithoutData",
        streamPosition: this.streamPosition,
        keyPath: t
      };
      return s.send(i), s.close(), s;
    }
    const r = this.subscribersByKeypath.get(t) || [];
    return r.push(s), this.subscribersByKeypath.set(t, r), s;
  }
  isKeypathCompleted(t) {
    return this.completedDataByKeypath.has(t);
  }
  subscribeAll() {
    return this.subscribe("*");
  }
  _getSubscribers(t) {
    const e = [...this.subscribersByKeypath.keys()], s = ["*"];
    for (const i of e)
      (t === i || t.startsWith(`${i}.`)) && s.push(i);
    return s.map((i) => this.subscribersByKeypath.get(i) || []).flatMap((i) => i);
  }
  _emitArrayExtended(t, e, s) {
    const r = this._getSubscribers(t);
    if (r.length === 0)
      return;
    const i = {
      ctx: this,
      type: "ArrayExtended",
      streamPosition: this.streamPosition,
      keyPath: t,
      index: e,
      value: this._copy(s)
    };
    for (const o of r)
      o.send(i);
  }
  _emitObjectCompleted(t) {
    const e = this._getSubscribers(t);
    if (e.length === 0)
      return;
    const s = this.getValueAtKeypath(t) ? {
      ctx: this,
      type: "ObjectCompleted",
      streamPosition: this.streamPosition,
      keyPath: t,
      value: this._copy(this.getValueAtKeypath(t))
    } : {
      ctx: this,
      type: "ObjectCompletedWithoutData",
      streamPosition: this.streamPosition,
      keyPath: t
    };
    for (const r of e)
      r.send(s);
    for (const r of this.subscribersByKeypath.get(t) || [])
      r.close();
  }
  _emitValueWritten(t, e) {
    const s = this._getSubscribers(t);
    if (s.length === 0)
      return;
    const r = {
      ctx: this,
      type: "ObjectUpdated",
      streamPosition: this.streamPosition,
      keyPath: t,
      value: this._copy(e)
    };
    for (const i of s)
      i.send(r);
  }
  _emitStringExtended(t, e, s) {
    const r = this._getSubscribers(t);
    if (r.length === 0 || !s)
      return;
    const i = {
      ctx: this,
      type: "StringExtended",
      streamPosition: this.streamPosition,
      keyPath: t,
      value: e,
      extension: s
    };
    for (const o of r)
      o.send(i);
  }
  _copy(t) {
    if (Array.isArray(t))
      return t.map((e) => this._copy(e));
    if (typeof t == "object" && t !== null) {
      const e = {};
      for (const [s, r] of Object.entries(t))
        e[s] = this._copy(r);
      return e;
    }
    return t;
  }
}
export {
  f as ExpectedDataAtKeypath,
  d as InvalidObjectType,
  A as InvalidRootObject,
  p as InvalidTypeAtKeypath,
  n as StreamedDataObject,
  u as StreamedDataObjectError
};
//# sourceMappingURL=index.js.map
