{"version":3,"file":"index.mjs","names":[],"sources":["../src/utils.ts","../src/document.ts","../src/collection.ts"],"sourcesContent":["export function assert(condition: unknown, msg: string): asserts condition {\n  if (!condition) {\n    throw new Error(msg);\n  }\n}\n\nexport function createId(): string {\n  return crypto.randomUUID().slice(0, 8);\n}\n\n/**\n * Idea taken from\n * https://kentcdodds.com/blog/get-a-catch-block-error-message-with-typescript?ck_subscriber_id=553154891\n */\n\ntype ErrorWithMessage = {\n  message: string;\n};\n\nexport function isErrorWithMessage(error: unknown): error is ErrorWithMessage {\n  return typeof error === \"object\" && error !== null && \"message\" in error;\n}\n\nfunction toErrorWithMessage(maybeError: unknown): ErrorWithMessage {\n  if (isErrorWithMessage(maybeError)) return maybeError;\n\n  try {\n    return new Error(JSON.stringify(maybeError));\n  } catch {\n    /**\n     * Fallback in case there's an error stringifying the maybeError like with\n     * circular references for example.\n     */\n    return new Error(String(maybeError));\n  }\n}\n\nexport function getErrorMessage(error: unknown) {\n  return toErrorWithMessage(error).message;\n}\n","import type {\n  CollectionReference,\n  DocumentData,\n  DocumentReference,\n  DocumentSnapshot,\n} from \"firebase/firestore\";\nimport { doc, getDoc, onSnapshot } from \"firebase/firestore\";\nimport {\n  action,\n  computed,\n  makeObservable,\n  observable,\n  onBecomeObserved,\n  onBecomeUnobserved,\n  runInAction,\n  toJS,\n} from \"mobx\";\nimport { assert, createId, getErrorMessage } from \"./utils\";\n\ntype Options = {\n  debug?: boolean;\n  /**\n   * When true, defers loading until the document is observed. By default (lazy:\n   * false), the document stays up-to-date in the background.\n   */\n  lazy?: boolean;\n};\n\nexport type Document<T> = {\n  id: string;\n  data: T;\n  ref: DocumentReference;\n};\n\nfunction isDocumentReference(source: SourceType): source is DocumentReference {\n  return source.type === \"document\";\n}\n\nfunction isCollectionReference(\n  source: SourceType,\n): source is CollectionReference {\n  return source.type === \"collection\";\n}\n\nfunction getPathFromCollectionRef(collectionRef?: CollectionReference) {\n  return collectionRef ? `${collectionRef.path}/__no_document_id` : undefined;\n}\n\nexport type SourceType = DocumentReference | CollectionReference;\n\nexport class ObservableDocument<T extends DocumentData = DocumentData> {\n  _data?: T;\n  isLoading = false;\n\n  private debugId = createId();\n  documentRef?: DocumentReference<T>;\n  private collectionRef?: CollectionReference<T>;\n  private isDebugEnabled = false;\n  private isLazy = false;\n\n  private readyPromise?: Promise<T | undefined>;\n  private readyResolveFn?: (data?: T) => void;\n  private onSnapshotUnsubscribeFn?: () => void;\n  private observedCount = 0;\n  private firedInitialFetch = false;\n  private sourcePath?: string;\n  private listenerSourcePath?: string;\n\n  private onErrorCallback?: (err: Error) => void;\n  private onDataCallback?: (data: T) => void;\n\n  constructor(source?: SourceType, options?: Options) {\n    if (options) {\n      this.isDebugEnabled = options.debug ?? false;\n      this.isLazy = options.lazy ?? false;\n    }\n\n    /**\n     * By placing the Mobx initialization after calling changeLoadingState we\n     * prevent having to make that private method an action.\n     */\n    makeObservable(this, {\n      _data: observable,\n      isLoading: observable,\n      data: computed,\n      document: computed,\n      attachTo: action,\n      documentRef: false,\n    });\n\n    this.initializeReadyPromise();\n\n    if (!source) {\n      // do nothing?\n    } else if (isCollectionReference(source)) {\n      this.collectionRef = source as CollectionReference<T>;\n      this.sourcePath = source.path;\n      this.logDebug(\"Constructor from collection reference\");\n    } else if (isDocumentReference(source)) {\n      this.documentRef = source as DocumentReference<T>;\n      this.collectionRef = source.parent as CollectionReference<T>;\n      this.sourcePath = source.path;\n      this.logDebug(\"Constructor from document reference\");\n      /**\n       * In this case we have data to wait on from the start. So initialize the\n       * promise and resolve function.\n       */\n      this.changeLoadingState(true);\n    }\n\n    onBecomeObserved(this, \"_data\", () => this.resumeUpdates());\n    onBecomeUnobserved(this, \"_data\", () => this.suspendUpdates());\n\n    onBecomeObserved(this, \"isLoading\", () => this.resumeUpdates());\n    onBecomeUnobserved(this, \"isLoading\", () => this.suspendUpdates());\n\n    // By default, start loading immediately (unless lazy mode is enabled)\n    if (!this.isLazy && source) {\n      this.logDebug(\"Starting background loading (default behavior)\");\n      this.updateListeners(true);\n    }\n  }\n\n  get id(): string {\n    return this.documentRef ? this.documentRef.id : \"__no_id\";\n  }\n\n  attachTo(documentId?: string) {\n    this.changeSourceViaId(documentId);\n\n    /** Return \"this\" so we can chain ready() when needed. */\n    return this;\n  }\n\n  get data(): T | undefined {\n    return this._data ? toJS(this._data) : undefined;\n  }\n\n  get document(): Document<T> {\n    assert(this.documentRef && this._data, \"No document available\");\n\n    return {\n      id: this.documentRef.id,\n      data: toJS(this._data),\n      ref: this.documentRef,\n    };\n  }\n\n  onError(cb: (err: Error) => void) {\n    this.onErrorCallback = cb;\n    return this;\n  }\n\n  onData(cb: (data: T) => void) {\n    this.onDataCallback = cb;\n    return this;\n  }\n\n  private get isObserved(): boolean {\n    return this.observedCount > 0;\n  }\n\n  get path(): string | undefined {\n    return this.documentRef ? this.documentRef.path : undefined;\n  }\n\n  ready(): Promise<T | undefined> {\n    const isListening = !!this.onSnapshotUnsubscribeFn;\n\n    if (!isListening && this.documentRef) {\n      /**\n       * If the client is calling ready() but document is not being observed /\n       * no listeners are set up, we treat ready() as a one time fetch request,\n       * so data is available after awaiting the promise.\n       */\n      this.logDebug(\"Ready requested without listeners => fetch\");\n      this.fetchInitialData();\n    } else {\n      this.logDebug(\"Ready requested with active listeners\");\n    }\n\n    assert(this.readyPromise, \"Missing ready promise\");\n\n    return this.readyPromise;\n  }\n\n  private changeReady(isReady: boolean) {\n    this.logDebug(`Change ready ${isReady}`);\n\n    if (isReady) {\n      const readyResolve = this.readyResolveFn;\n      assert(readyResolve, \"Missing ready resolve function\");\n\n      readyResolve(this.data);\n\n      /**\n       * After the first promise has been resolved we want subsequent calls to\n       * ready() to immediately return with the available data. Ready is only\n       * meant to be used for initial data fetching\n       */\n      this.readyPromise = Promise.resolve(this.data);\n    }\n  }\n\n  private initializeReadyPromise() {\n    this.logDebug(\"Initialize new ready promise\");\n    this.readyPromise = new Promise((resolve) => {\n      this.readyResolveFn = resolve;\n    });\n  }\n\n  private fetchInitialData() {\n    if (this.firedInitialFetch || !this.documentRef) {\n      this.logDebug(\"Ignore fetch initial data\");\n      return;\n    }\n\n    this.logDebug(\"Fetch initial data\");\n\n    /**\n     * Pass the promise from the snapshot get to the handler function, which\n     * will resolve the ready promise just like the snapshot passed in from the\n     * normal listener.\n     */\n    getDoc(this.documentRef)\n      .then((snapshot) => this.handleSnapshot(snapshot))\n      .catch((err) =>\n        this.handleError(\n          new Error(`Fetch initial data failed: ${getErrorMessage(err)}`),\n        ),\n      );\n\n    this.firedInitialFetch = true;\n  }\n\n  private resumeUpdates() {\n    this.observedCount += 1;\n\n    this.logDebug(`Resume. Observed count: ${this.observedCount}`);\n\n    if (this.observedCount === 1) {\n      this.logDebug(\"Becoming observed\");\n      // Don't set up listeners if they're already active from default background loading\n      if (!this.onSnapshotUnsubscribeFn) {\n        this.updateListeners(true);\n      }\n    }\n  }\n\n  private suspendUpdates() {\n    this.observedCount -= 1;\n\n    this.logDebug(`Suspend. Observed count: ${this.observedCount}`);\n\n    if (this.observedCount === 0) {\n      this.logDebug(\"Becoming un-observed\");\n      // Only tear down listeners if lazy loading is enabled\n      // By default, we keep listeners active in the background\n      if (this.isLazy) {\n        this.updateListeners(false);\n      }\n    }\n  }\n\n  private handleSnapshot(snapshot: DocumentSnapshot) {\n    const data = snapshot.data() as T | undefined;\n\n    this.logDebug(\"Handle snapshot data:\", data);\n\n    runInAction(() => {\n      this._data = data;\n\n      /**\n       * We only need to call back if data exists. This function needs to fire\n       * before the loading/ready state is set, so that one document can depend\n       * on data from another. For example `isSomethingLoading = a.isLoading ||\n       * b.isLoading` would not work if a has isLoading false before b is able\n       * to access the data via the callback.\n       */\n      if (data && typeof this.onDataCallback === \"function\") {\n        this.onDataCallback(data);\n      }\n\n      this.changeLoadingState(false);\n    });\n  }\n\n  /** If there is an error handler callback we use that, otherwise we throw. */\n  private handleError(err: Error) {\n    if (typeof this.onErrorCallback === \"function\") {\n      this.onErrorCallback(err);\n    } else {\n      this.logError(err);\n      throw err;\n    }\n  }\n\n  private changeSourceViaId(documentId?: string) {\n    if (this.id === documentId) {\n      return;\n    }\n\n    if (documentId && !this.collectionRef) {\n      this.handleError(\n        new Error(\n          `Can not change source via id if there is no known collection reference`,\n        ),\n      );\n      return;\n    }\n\n    const newRef =\n      documentId && this.collectionRef\n        ? doc(this.collectionRef, documentId)\n        : undefined;\n\n    const newPath = newRef\n      ? newRef.path\n      : getPathFromCollectionRef(this.collectionRef);\n\n    this.logDebug(`Change source via id to ${newPath}`);\n    this.documentRef = newRef;\n    this.sourcePath = newPath;\n    this.firedInitialFetch = false;\n\n    const hasSource = !!newRef;\n\n    this.initializeReadyPromise();\n\n    this._data = undefined;\n\n    if (!hasSource) {\n      this.changeLoadingState(false);\n\n      if (this.isObserved || !this.isLazy) {\n        this.logDebug(\"Change document -> clear listeners\");\n        this.updateListeners(false);\n      }\n    } else {\n      this.changeLoadingState(true);\n\n      if (this.isObserved || !this.isLazy) {\n        this.logDebug(\"Change document -> update listeners\");\n        this.updateListeners(true);\n      }\n    }\n  }\n\n  private logDebug(...args: unknown[]) {\n    if (this.isDebugEnabled) {\n      if (!this.documentRef) {\n        console.log(\n          `${this.debugId} (${getPathFromCollectionRef(this.collectionRef)})`,\n          ...args,\n        );\n      } else {\n        console.log(`${this.debugId} (${this.documentRef.path})`, ...args);\n      }\n    }\n  }\n\n  private logError(err: unknown) {\n    if (!this.documentRef) {\n      console.error(\n        `${this.debugId} (${getPathFromCollectionRef(\n          this.collectionRef,\n        )}): ${getErrorMessage(err)}`,\n      );\n    } else {\n      console.error(\n        `${this.debugId} (${this.documentRef.path}): ${getErrorMessage(err)}`,\n      );\n    }\n  }\n\n  private updateListeners(shouldListen: boolean) {\n    const isListening = !!this.onSnapshotUnsubscribeFn;\n\n    if (\n      shouldListen &&\n      isListening &&\n      this.sourcePath === this.listenerSourcePath\n    ) {\n      return;\n    }\n\n    if (isListening) {\n      this.logDebug(\"Unsubscribe listeners\");\n\n      this.onSnapshotUnsubscribeFn?.();\n      this.onSnapshotUnsubscribeFn = undefined;\n      this.listenerSourcePath = undefined;\n    }\n\n    if (shouldListen) {\n      if (!this.documentRef) {\n        return;\n      }\n\n      this.logDebug(\"Subscribe listeners\");\n\n      try {\n        this.onSnapshotUnsubscribeFn = onSnapshot(\n          this.documentRef,\n          (snapshot) => this.handleSnapshot(snapshot),\n          (err) => this.handleError(err),\n        );\n      } catch (err) {\n        throw new Error(\n          `Failed to subscribe with onSnapshot: ${getErrorMessage(err)}`,\n          { cause: err },\n        );\n      }\n\n      this.listenerSourcePath = this.sourcePath;\n    }\n  }\n\n  private changeLoadingState(isLoading: boolean) {\n    runInAction(() => {\n      this.logDebug(`Change loading state: ${isLoading}`);\n      this.changeReady(!isLoading);\n      this.isLoading = isLoading;\n    });\n  }\n}\n\n/**\n * Create an ObservableDocument with automatic type inference from the\n * reference. This aligns with the typed-firestore pattern where types flow from\n * refs.\n *\n * @example\n *   const doc = createObservableDocument(refs.taxonomies);\n *   const doc2 = createObservableDocument(refs.derived.currentReviewStructures);\n */\nexport function createObservableDocument<T extends DocumentData>(\n  ref: DocumentReference<T> | CollectionReference<T>,\n  options?: Options,\n): ObservableDocument<T> {\n  return new ObservableDocument<T>(ref as SourceType, options);\n}\n","import type {\n  CollectionReference,\n  DocumentData,\n  Query,\n  QuerySnapshot,\n} from \"firebase/firestore\";\nimport { getDocs, onSnapshot, queryEqual } from \"firebase/firestore\";\nimport {\n  action,\n  computed,\n  makeObservable,\n  observable,\n  onBecomeObserved,\n  onBecomeUnobserved,\n  runInAction,\n  toJS,\n} from \"mobx\";\n\nimport type { Document } from \"./document\";\nimport { assert, createId, getErrorMessage } from \"./utils\";\n\ntype Options = {\n  /**\n   * For more info read\n   * https://firebase.google.com/docs/firestore/query-data/listen\n   */\n  ignoreInitialSnapshot?: boolean;\n  debug?: boolean;\n  /**\n   * When true, defers loading until the collection is observed. By default\n   * (lazy: false), the collection stays up-to-date in the background.\n   */\n  lazy?: boolean;\n};\n\nconst optionDefaults: Options = {\n  /** @todo Test before making default true */\n  ignoreInitialSnapshot: false,\n  debug: false,\n};\n\ntype QueryCreatorFn = (ref: CollectionReference) => Query;\n\nfunction hasReference(ref?: CollectionReference): ref is CollectionReference {\n  return !!ref;\n}\n\nexport class ObservableCollection<T extends DocumentData = DocumentData> {\n  _documents: Document<T>[] = [];\n  isLoading = false;\n\n  private debugId = createId();\n  private collectionRef?: CollectionReference<T>;\n  private _query?: Query;\n  private queryCreatorFn?: QueryCreatorFn;\n  private isDebugEnabled = false;\n  private isLazy = false;\n  private readyPromise?: Promise<Document<T>[]>;\n  private readyResolveFn?: (docs: Document<T>[]) => void;\n  private onSnapshotUnsubscribeFn?: () => void;\n  private options: Options = optionDefaults;\n  private observedCount = 0;\n  private firedInitialFetch = false;\n  private sourceId = createId();\n  private listenerSourcePath?: string;\n\n  onError?: (err: Error) => void;\n\n  /**\n   * @todo Maybe record a string of the query + reference, so we can figure out\n   *   if the current listeners belong to that combination or we need to update\n   *   them\n   */\n  constructor(\n    /**\n     * Ref is optional because for sub-collections you might not know the full\n     * path in advance. Pass undefined if you want to supply the other\n     * parameters\n     */\n    ref?: CollectionReference<T>,\n    queryCreatorFn?: QueryCreatorFn,\n    options?: Options,\n  ) {\n    makeObservable(this, {\n      _documents: observable,\n      isLoading: observable,\n      isEmpty: computed,\n      hasDocuments: computed,\n      documents: computed,\n      attachTo: action,\n      /**\n       * AttachTo being an action doesn't seem to be sufficient to prevent\n       * strict mode errors\n       */\n      _changeSource: action,\n    });\n\n    this.initializeReadyPromise();\n    /**\n     * NOTE: I wish it was possible to extract the ref from a Query object,\n     * because then we could make a single source parameter\n     * CollectionReference | Query\n     */\n    if (hasReference(ref)) {\n      this.collectionRef = ref;\n    }\n\n    if (queryCreatorFn) {\n      this.queryCreatorFn = queryCreatorFn;\n      this._query = hasReference(ref) ? queryCreatorFn(ref) : undefined;\n      this.sourceId = createId();\n    }\n\n    if (options) {\n      this.options = { ...optionDefaults, ...options };\n      this.isDebugEnabled = options.debug ?? false;\n      this.isLazy = options.lazy ?? false;\n    }\n\n    onBecomeObserved(this, \"documents\", () => this.resumeUpdates());\n    onBecomeUnobserved(this, \"documents\", () => this.suspendUpdates());\n\n    onBecomeObserved(this, \"isLoading\", () => this.resumeUpdates());\n    onBecomeUnobserved(this, \"isLoading\", () => this.suspendUpdates());\n\n    if (hasReference(ref)) {\n      this.changeLoadingState(true);\n    }\n\n    // By default, start loading immediately (unless lazy mode is enabled)\n    if (!this.isLazy && ref) {\n      this.logDebug(\"Starting background loading (default behavior)\");\n      this.updateListeners(true);\n    }\n  }\n\n  get isEmpty(): boolean {\n    return this._documents.length === 0;\n  }\n\n  get hasDocuments(): boolean {\n    return this._documents.length > 0;\n  }\n\n  get documents() {\n    return toJS(this._documents);\n  }\n\n  private get isObserved(): boolean {\n    return this.observedCount > 0;\n  }\n\n  get path(): string | undefined {\n    return this.collectionRef ? this.collectionRef.path : undefined;\n  }\n\n  get ref() {\n    assert(this.collectionRef, \"No collection ref available\");\n    return this.collectionRef;\n  }\n\n  attachTo(newRef?: CollectionReference<T>) {\n    this._changeSource(newRef);\n    /** Return this so we can chain ready() */\n    return this;\n  }\n\n  _changeSource(newRef?: CollectionReference<T>) {\n    if (!this.collectionRef && !newRef) {\n      return;\n    }\n\n    if (\n      this.collectionRef &&\n      newRef &&\n      this.collectionRef.path === newRef.path\n    ) {\n      return;\n    }\n\n    this.logDebug(`Change source to ${newRef ? newRef.path : undefined}`);\n    this.firedInitialFetch = false;\n    this.collectionRef = newRef;\n    this.sourceId = createId();\n\n    this.initializeReadyPromise();\n\n    if (hasReference(newRef)) {\n      if (this.queryCreatorFn) {\n        this.logDebug(\"Update query using new ref source\");\n        this._query = this.queryCreatorFn(newRef);\n      }\n\n      if (this.isObserved || !this.isLazy) {\n        this.logDebug(\"Change collection -> update listeners\");\n        this.updateListeners(true);\n      }\n\n      this.changeLoadingState(true);\n    } else {\n      if (this.isObserved || !this.isLazy) {\n        this.logDebug(\"Change collection -> clear listeners\");\n        this.updateListeners(false);\n      }\n\n      this._documents = [];\n      this.changeLoadingState(false);\n    }\n  }\n\n  ready() {\n    const isListening = !!this.onSnapshotUnsubscribeFn;\n\n    if (!isListening) {\n      /**\n       * If the client is calling ready() but document is not being observed /\n       * no listeners are set up, we treat ready() as a one time fetch request,\n       * so data is available after awaiting the promise.\n       */\n      this.logDebug(\"Ready call without listeners => fetch\");\n      this.fetchInitialData();\n    }\n\n    assert(this.readyPromise, \"Missing ready promise\");\n\n    return this.readyPromise;\n  }\n\n  private changeReady(isReady: boolean) {\n    this.logDebug(`Change ready ${isReady}`);\n\n    if (isReady) {\n      const readyResolve = this.readyResolveFn;\n      assert(readyResolve, \"Missing ready resolve function\");\n\n      this.logDebug(\"Call ready resolve\");\n\n      readyResolve(this._documents);\n\n      /**\n       * After the first promise has been resolved we want subsequent calls to\n       * ready() to immediately return with the available data. Ready is only\n       * meant to be used for initial data fetching\n       */\n      this.readyPromise = Promise.resolve(this._documents);\n    }\n  }\n\n  private initializeReadyPromise() {\n    this.logDebug(\"Initialize new ready promise\");\n    this.readyPromise = new Promise((resolve) => {\n      this.readyResolveFn = resolve;\n    });\n  }\n\n  private fetchInitialData() {\n    if (this.firedInitialFetch) {\n      return;\n    }\n\n    if (!this.collectionRef) {\n      this.handleError(\n        new Error(\"Can not fetch data without a collection reference\"),\n      );\n      return;\n    }\n\n    this.logDebug(\"Fetch initial data\");\n\n    /**\n     * Simply pass the snapshot from the promise to the handler function which\n     * will then resolve the ready promise just like the snapshot from a\n     * listener would.\n     */\n    if (this._query) {\n      getDocs(this._query)\n        .then((snapshot) => this.handleSnapshot(snapshot))\n        .catch((err) =>\n          this.handleError(\n            new Error(`Fetch initial data failed: ${getErrorMessage(err)}`),\n          ),\n        );\n    } else {\n      getDocs(this.ref)\n        .then((snapshot) => this.handleSnapshot(snapshot))\n        .catch((err) =>\n          this.handleError(\n            new Error(`Fetch initial data failed: ${getErrorMessage(err)}`),\n          ),\n        );\n    }\n\n    this.firedInitialFetch = true;\n  }\n\n  private resumeUpdates() {\n    this.observedCount += 1;\n\n    this.logDebug(`Resume. Observed count: ${this.observedCount}`);\n\n    if (this.observedCount === 1) {\n      this.logDebug(\"Becoming observed\");\n      // Don't set up listeners if they're already active from default background loading\n      if (!this.onSnapshotUnsubscribeFn) {\n        this.updateListeners(true);\n      }\n    }\n  }\n\n  private suspendUpdates() {\n    this.observedCount -= 1;\n\n    this.logDebug(`Suspend. Observed count: ${this.observedCount}`);\n\n    if (this.observedCount === 0) {\n      this.logDebug(\"Becoming un-observed\");\n      // Only tear down listeners if lazy loading is enabled\n      // By default, we keep listeners active in the background\n      if (this.isLazy) {\n        this.updateListeners(false);\n      }\n    }\n  }\n\n  private handleError(err: Error) {\n    if (typeof this.onError === \"function\") {\n      this.onError(err);\n    } else {\n      throw err;\n    }\n  }\n\n  private handleSnapshot(snapshot: QuerySnapshot) {\n    this.logDebug(\n      `handleSnapshot, ${Date.now()} docs.length: ${snapshot.docs.length}`,\n    );\n\n    runInAction(() => {\n      this._documents = snapshot.docs.map(\n        (doc) =>\n          ({\n            id: doc.id,\n            ref: doc.ref,\n            data: doc.data() as T,\n          }) as Document<T>,\n      );\n      this.changeLoadingState(false);\n    });\n  }\n\n  set query(queryCreatorFn: QueryCreatorFn | undefined) {\n    this.logDebug(\"Set query\");\n\n    this.queryCreatorFn = queryCreatorFn;\n\n    const newQuery = queryCreatorFn\n      ? hasReference(this.collectionRef)\n        ? queryCreatorFn(this.collectionRef)\n        : undefined\n      : undefined;\n\n    /**\n     * If we set a query that matches the currently active query it would be a\n     * no-op.\n     */\n    if (newQuery && this._query && queryEqual(newQuery, this._query)) {\n      return;\n    }\n\n    /**\n     * If we clear the query but there was none to start with it would be a\n     * no-op.\n     */\n    if (!newQuery && !this._query) {\n      return;\n    }\n\n    this.firedInitialFetch = false;\n\n    const hasSource = !!this.collectionRef || !!newQuery;\n    this._query = newQuery;\n    this.sourceId = createId();\n\n    if (!hasSource) {\n      if (this.isObserved || !this.isLazy) {\n        this.logDebug(\"Set query -> clear listeners\");\n        this.updateListeners(false);\n      }\n\n      this._documents = [];\n      this.changeLoadingState(false);\n    } else {\n      if (this.isObserved || !this.isLazy) {\n        this.logDebug(\"Set query -> update listeners\");\n        this.updateListeners(true);\n      }\n\n      this.changeLoadingState(true);\n    }\n  }\n\n  private logDebug(...args: unknown[]) {\n    if (this.isDebugEnabled) {\n      if (this.collectionRef) {\n        console.log(`${this.debugId} (${this.collectionRef.path})`, ...args);\n      } else {\n        console.log(this.debugId, ...args);\n      }\n    }\n  }\n\n  private updateListeners(shouldListen: boolean) {\n    const isListening = !!this.onSnapshotUnsubscribeFn;\n\n    if (\n      shouldListen &&\n      isListening &&\n      this.sourceId === this.listenerSourcePath\n    ) {\n      return;\n    }\n\n    if (isListening) {\n      this.logDebug(\"Unsubscribe listeners\");\n\n      this.onSnapshotUnsubscribeFn?.();\n      this.onSnapshotUnsubscribeFn = undefined;\n\n      this.listenerSourcePath = undefined;\n    }\n\n    if (shouldListen) {\n      this.logDebug(\"Subscribe listeners\");\n\n      if (this._query) {\n        this.onSnapshotUnsubscribeFn = onSnapshot(\n          this._query,\n          executeFromCount(\n            (snapshot) => this.handleSnapshot(snapshot),\n            this.options.ignoreInitialSnapshot ? 1 : 0,\n          ),\n          (err) => this.handleError(err),\n        );\n      } else if (this.collectionRef) {\n        this.onSnapshotUnsubscribeFn = onSnapshot(\n          this.collectionRef,\n          executeFromCount(\n            (snapshot) => this.handleSnapshot(snapshot),\n            this.options.ignoreInitialSnapshot ? 1 : 0,\n          ),\n          (err) => this.handleError(err),\n        );\n      }\n\n      this.listenerSourcePath = this.sourceId;\n    }\n  }\n\n  private changeLoadingState(isLoading: boolean) {\n    this.logDebug(`Change loading state: ${isLoading}`);\n    this.changeReady(!isLoading);\n    runInAction(() => (this.isLoading = isLoading));\n  }\n}\n\ntype Fn<T> = (...args: T[]) => void;\n\nexport function executeFromCount<T>(fn: Fn<T>, count: number) {\n  let executionCount = 0;\n\n  return (...args: T[]) => {\n    if (executionCount < count) {\n      executionCount++;\n      return false;\n    } else {\n      fn(...args);\n      return true;\n    }\n  };\n}\n\n/**\n * Create an ObservableCollection with automatic type inference from the\n * reference. This aligns with the typed-firestore pattern where types flow from\n * refs.\n *\n * @example\n *   const collection = createObservableCollection(refs.taxonomies);\n *\n *   // When ref is undefined, provide the type explicitly\n *   const collection2 = createObservableCollection<Assessment>(\n *     undefined,\n *     queryFn,\n *   );\n *   // Later attach the ref when it becomes available\n *   collection2.attachTo(refs.reviewAssessments(reviewId));\n */\nexport function createObservableCollection<T extends DocumentData>(\n  ref?: CollectionReference<T>,\n  queryCreatorFn?: QueryCreatorFn,\n  options?: Options,\n): ObservableCollection<T> {\n  return new ObservableCollection<T>(ref, queryCreatorFn, options);\n}\n"],"mappings":"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"}