{"version":3,"file":"index.cjs","names":["BroadcastChannel"],"sources":["../../src/index.ts"],"sourcesContent":["import { BroadcastChannel } from 'broadcast-channel'\nimport type { BroadcastChannelOptions } from 'broadcast-channel'\nimport type { QueryClient, QueryKey } from '@tanstack/query-core'\n\n/**\n * Metadata describing a broadcast that failed to be delivered to other tabs.\n * Passed to {@link BroadcastQueryClientOptions.onBroadcastError} so callers\n * can correlate failures with the originating query.\n */\nexport interface BroadcastErrorEvent {\n  type: 'updated' | 'removed' | 'added'\n  queryHash: string\n  queryKey: QueryKey\n}\n\ntype BroadcastMessage =\n  | { type: 'updated'; queryHash: string; queryKey: QueryKey; state: unknown }\n  | { type: 'removed'; queryHash: string; queryKey: QueryKey }\n  | { type: 'added'; queryHash: string; queryKey: QueryKey; state: unknown }\n\ninterface BroadcastQueryClientOptions {\n  /** The QueryClient to sync. */\n  queryClient: QueryClient\n  /**\n   * Unique channel name used to communicate between tabs and windows.\n   * @default 'tanstack-query'\n   */\n  broadcastChannel?: string\n  /** Options forwarded to the underlying `BroadcastChannel`. */\n  options?: BroadcastChannelOptions\n  /**\n   * Called when a query event fails to broadcast to other tabs — most\n   * commonly because the query's `state.data`, `state.error`, or `queryKey`\n   * contains a value the structured-clone algorithm cannot serialize\n   * (e.g. `ReadableStream`, `File`, functions, Vue `reactive` proxies).\n   *\n   * Provide this to route failures to an error tracker. If omitted, a\n   * `console.warn` is emitted in development so failures are never silent.\n   *\n   * May return a `Promise`; any rejection is caught internally so it cannot\n   * cause a secondary unhandled rejection.\n   */\n  onBroadcastError?: (\n    error: unknown,\n    event: BroadcastErrorEvent,\n  ) => void | Promise<void>\n}\n\nexport function broadcastQueryClient({\n  queryClient,\n  broadcastChannel = 'tanstack-query',\n  options,\n  onBroadcastError,\n}: BroadcastQueryClientOptions): () => void {\n  let transaction = false\n  const tx = (cb: () => void) => {\n    transaction = true\n    try {\n      cb()\n    } finally {\n      // Guard against `cb` throwing (e.g. `query.setState`/`queryCache.build`\n      // triggering a listener that throws while applying an incoming\n      // cross-tab message). Without this, `transaction` would stay `true`\n      // forever, silently disabling this tab's own broadcasts to other tabs\n      // for the rest of the session.\n      transaction = false\n    }\n  }\n\n  const channel = new BroadcastChannel(broadcastChannel, {\n    webWorkerSupport: false,\n    ...options,\n  })\n\n  const queryCache = queryClient.getQueryCache()\n\n  const safePost = (message: BroadcastMessage): void => {\n    channel.postMessage(message).catch((error: unknown) => {\n      const event: BroadcastErrorEvent = {\n        type: message.type,\n        queryHash: message.queryHash,\n        queryKey: message.queryKey,\n      }\n\n      if (onBroadcastError) {\n        const warnCallbackError = (callbackError: unknown) => {\n          if (process.env.NODE_ENV !== 'production') {\n            console.warn(\n              `[broadcastQueryClient] onBroadcastError threw while handling \"${event.type}\" for query ${event.queryHash}.`,\n              callbackError,\n            )\n          }\n        }\n        let result: void | Promise<void>\n        try {\n          result = onBroadcastError(error, event)\n        } catch (callbackError) {\n          warnCallbackError(callbackError)\n          return\n        }\n        result?.catch(warnCallbackError)\n      } else if (process.env.NODE_ENV !== 'production') {\n        console.warn(\n          `[broadcastQueryClient] Failed to broadcast \"${event.type}\" event for query ${event.queryHash}. ` +\n            'The query value could not be structured-cloned; cross-tab sync for this query was skipped.',\n          error,\n        )\n      }\n    })\n  }\n\n  const unsubscribe = queryCache.subscribe((queryEvent) => {\n    if (transaction) {\n      return\n    }\n\n    const {\n      query: { queryHash, queryKey, state, observers },\n    } = queryEvent\n\n    if (queryEvent.type === 'updated' && queryEvent.action.type === 'success') {\n      safePost({\n        type: 'updated',\n        queryHash,\n        queryKey,\n        state,\n      })\n    }\n\n    if (queryEvent.type === 'removed' && observers.length > 0) {\n      safePost({\n        type: 'removed',\n        queryHash,\n        queryKey,\n      })\n    }\n\n    if (queryEvent.type === 'added') {\n      safePost({\n        type: 'added',\n        queryHash,\n        queryKey,\n        state,\n      })\n    }\n  })\n\n  channel.onmessage = (action) => {\n    if (!action?.type) {\n      return\n    }\n\n    tx(() => {\n      const { type, queryHash, queryKey, state } = action\n\n      const query = queryCache.get(queryHash)\n\n      if (type === 'updated') {\n        if (query) {\n          query.setState(state)\n          return\n        }\n\n        queryCache.build(\n          queryClient,\n          {\n            queryKey,\n            queryHash,\n          },\n          state,\n        )\n      } else if (type === 'removed') {\n        if (query) {\n          queryCache.remove(query)\n        }\n      } else if (type === 'added') {\n        if (query) {\n          query.setState(state)\n          return\n        }\n        queryCache.build(\n          queryClient,\n          {\n            queryKey,\n            queryHash,\n          },\n          state,\n        )\n      }\n    })\n  }\n  return () => {\n    unsubscribe()\n    channel.close()\n  }\n}\n"],"mappings":";;;AAgDA,SAAgB,qBAAqB,EACnC,aACA,mBAAmB,kBACnB,SACA,oBAC0C;CAC1C,IAAI,cAAc;CAClB,MAAM,MAAM,OAAmB;EAC7B,cAAc;EACd,IAAI;GACF,GAAG;EACL,UAAU;GAMR,cAAc;EAChB;CACF;CAEA,MAAM,UAAU,IAAIA,kBAAAA,iBAAiB,kBAAkB;EACrD,kBAAkB;EAClB,GAAG;CACL,CAAC;CAED,MAAM,aAAa,YAAY,cAAc;CAE7C,MAAM,YAAY,YAAoC;EACpD,QAAQ,YAAY,OAAO,CAAC,CAAC,OAAO,UAAmB;GACrD,MAAM,QAA6B;IACjC,MAAM,QAAQ;IACd,WAAW,QAAQ;IACnB,UAAU,QAAQ;GACpB;GAEA,IAAI,kBAAkB;IACpB,MAAM,qBAAqB,kBAA2B;KACpD,IAAI,QAAQ,IAAI,aAAa,cAC3B,QAAQ,KACN,iEAAiE,MAAM,KAAK,cAAc,MAAM,UAAU,IAC1G,aACF;IAEJ;IACA,IAAI;IACJ,IAAI;KACF,SAAS,iBAAiB,OAAO,KAAK;IACxC,SAAS,eAAe;KACtB,kBAAkB,aAAa;KAC/B;IACF;IACA,QAAQ,MAAM,iBAAiB;GACjC,OAAO,IAAI,QAAQ,IAAI,aAAa,cAClC,QAAQ,KACN,+CAA+C,MAAM,KAAK,oBAAoB,MAAM,UAAU,+FAE9F,KACF;EAEJ,CAAC;CACH;CAEA,MAAM,cAAc,WAAW,WAAW,eAAe;EACvD,IAAI,aACF;EAGF,MAAM,EACJ,OAAO,EAAE,WAAW,UAAU,OAAO,gBACnC;EAEJ,IAAI,WAAW,SAAS,aAAa,WAAW,OAAO,SAAS,WAC9D,SAAS;GACP,MAAM;GACN;GACA;GACA;EACF,CAAC;EAGH,IAAI,WAAW,SAAS,aAAa,UAAU,SAAS,GACtD,SAAS;GACP,MAAM;GACN;GACA;EACF,CAAC;EAGH,IAAI,WAAW,SAAS,SACtB,SAAS;GACP,MAAM;GACN;GACA;GACA;EACF,CAAC;CAEL,CAAC;CAED,QAAQ,aAAa,WAAW;EAC9B,IAAI,CAAC,QAAQ,MACX;EAGF,SAAS;GACP,MAAM,EAAE,MAAM,WAAW,UAAU,UAAU;GAE7C,MAAM,QAAQ,WAAW,IAAI,SAAS;GAEtC,IAAI,SAAS,WAAW;IACtB,IAAI,OAAO;KACT,MAAM,SAAS,KAAK;KACpB;IACF;IAEA,WAAW,MACT,aACA;KACE;KACA;IACF,GACA,KACF;GACF,OAAO,IAAI,SAAS,WACd;QAAA,OACF,WAAW,OAAO,KAAK;GAAA,OAEpB,IAAI,SAAS,SAAS;IAC3B,IAAI,OAAO;KACT,MAAM,SAAS,KAAK;KACpB;IACF;IACA,WAAW,MACT,aACA;KACE;KACA;IACF,GACA,KACF;GACF;EACF,CAAC;CACH;CACA,aAAa;EACX,YAAY;EACZ,QAAQ,MAAM;CAChB;AACF"}