@xenide-io/suite-client 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -416,6 +416,8 @@ interface CrossAppSyncConfig {
416
416
  checkpointPath: string;
417
417
  /** Poll interval while the tab is visible (ms). */
418
418
  pollMs?: number;
419
+ /** Reset the checkpoint baseline when the active workspace changes. */
420
+ getScope?: () => string | null;
419
421
  }
420
422
  type CrossAppSync = ReturnType<typeof createCrossAppSync>;
421
423
  /**
package/dist/index.d.ts CHANGED
@@ -416,6 +416,8 @@ interface CrossAppSyncConfig {
416
416
  checkpointPath: string;
417
417
  /** Poll interval while the tab is visible (ms). */
418
418
  pollMs?: number;
419
+ /** Reset the checkpoint baseline when the active workspace changes. */
420
+ getScope?: () => string | null;
419
421
  }
420
422
  type CrossAppSync = ReturnType<typeof createCrossAppSync>;
421
423
  /**
package/dist/index.js CHANGED
@@ -9590,6 +9590,7 @@ function createApiClient(config) {
9590
9590
  let refreshToken = null;
9591
9591
  let activeWorkspaceId = null;
9592
9592
  const etagBodies = /* @__PURE__ */ new Map();
9593
+ const pendingGets = /* @__PURE__ */ new Map();
9593
9594
  let authFailureHandler = null;
9594
9595
  let authFailureFired = false;
9595
9596
  let refreshPromise = null;
@@ -9716,6 +9717,7 @@ function createApiClient(config) {
9716
9717
  refreshToken = null;
9717
9718
  activeWorkspaceId = null;
9718
9719
  etagBodies.clear();
9720
+ pendingGets.clear();
9719
9721
  if (typeof window !== "undefined") {
9720
9722
  window.localStorage.removeItem(ACCESS_KEY);
9721
9723
  window.localStorage.removeItem(REFRESH_KEY);
@@ -9784,6 +9786,30 @@ function createApiClient(config) {
9784
9786
  }
9785
9787
  }
9786
9788
  async function apiFetch(path, init = {}) {
9789
+ if ((init.method ?? "GET").toUpperCase() !== "GET") {
9790
+ pendingGets.clear();
9791
+ try {
9792
+ return await apiFetchInternal(path, init);
9793
+ } finally {
9794
+ pendingGets.clear();
9795
+ }
9796
+ }
9797
+ if (Object.keys(init).length > 0) return apiFetchInternal(path, init);
9798
+ const key = JSON.stringify([
9799
+ getAccessToken(),
9800
+ getActiveWorkspaceId(),
9801
+ path
9802
+ ]);
9803
+ let pending = pendingGets.get(key);
9804
+ if (!pending) {
9805
+ pending = apiFetchInternal(path, init).finally(() => {
9806
+ if (pendingGets.get(key) === pending) pendingGets.delete(key);
9807
+ });
9808
+ pendingGets.set(key, pending);
9809
+ }
9810
+ return (await pending).clone();
9811
+ }
9812
+ async function apiFetchInternal(path, init = {}) {
9787
9813
  const { skipAuth, ...requestInit } = init;
9788
9814
  const token = getAccessToken();
9789
9815
  const headers = {
@@ -10518,40 +10544,49 @@ function createCrossAppSync(config) {
10518
10544
  const subscribers = [];
10519
10545
  let pollInterval = null;
10520
10546
  let pollListeners = 0;
10547
+ let inFlight = false;
10548
+ let primed = false;
10549
+ let scope;
10550
+ let lastRequestedScope;
10551
+ let lastRequestedAt;
10521
10552
  async function fetchCheckpoint() {
10553
+ if (inFlight || document.visibilityState !== "visible") return null;
10554
+ const requestScope = config.getScope?.();
10555
+ if (requestScope === lastRequestedScope && lastRequestedAt !== void 0 && Date.now() - lastRequestedAt < pollMs)
10556
+ return null;
10557
+ inFlight = true;
10558
+ lastRequestedScope = requestScope;
10559
+ lastRequestedAt = Date.now();
10522
10560
  try {
10523
10561
  const res = await config.api.get(config.checkpointPath);
10524
10562
  if (!res.ok) return null;
10525
- return await res.json();
10563
+ const data = await res.json();
10564
+ if (requestScope !== config.getScope?.()) return null;
10565
+ if (scope !== requestScope) {
10566
+ primed = false;
10567
+ scope = requestScope;
10568
+ }
10569
+ return data;
10526
10570
  } catch {
10527
10571
  return null;
10528
- }
10529
- }
10530
- function notify(topic) {
10531
- for (const sub of subscribers) {
10532
- if (!sub.topic || sub.topic === topic) {
10533
- if (lastKnown[topic] > sub.lastSync) {
10534
- sub.lastSync = lastKnown[topic];
10535
- sub.callback();
10536
- }
10537
- }
10572
+ } finally {
10573
+ inFlight = false;
10538
10574
  }
10539
10575
  }
10540
10576
  function checkAndNotify(data) {
10541
- const tasks = data.tasks_synced_at || 0;
10542
- const time = data.time_synced_at || 0;
10543
- const integration = data.integration_synced_at || 0;
10544
- if (tasks > lastKnown.tasks) {
10545
- lastKnown.tasks = tasks;
10546
- notify("tasks");
10547
- }
10548
- if (time > lastKnown.time) {
10549
- lastKnown.time = time;
10550
- notify("time");
10551
- }
10552
- if (integration > lastKnown.integration) {
10553
- lastKnown.integration = integration;
10554
- notify("integration");
10577
+ const next = {
10578
+ tasks: data.tasks_synced_at || 0,
10579
+ time: data.time_synced_at || 0,
10580
+ integration: data.integration_synced_at || 0
10581
+ };
10582
+ const changed = ["tasks", "time", "integration"].filter(
10583
+ (topic) => primed && next[topic] > lastKnown[topic]
10584
+ );
10585
+ Object.assign(lastKnown, next);
10586
+ primed = true;
10587
+ for (const sub of subscribers) {
10588
+ if (changed.some((topic) => !sub.topic || sub.topic === topic))
10589
+ sub.callback();
10555
10590
  }
10556
10591
  }
10557
10592
  function startPolling() {
@@ -10578,8 +10613,7 @@ function createCrossAppSync(config) {
10578
10613
  };
10579
10614
  const sub = {
10580
10615
  topic,
10581
- callback: onSync,
10582
- lastSync: lastKnown[topic || "tasks"]
10616
+ callback: onSync
10583
10617
  };
10584
10618
  subscribers.push(sub);
10585
10619
  pollListeners += 1;
@@ -10684,17 +10718,22 @@ function useSuiteRevisions(options) {
10684
10718
  let seen = {};
10685
10719
  let ready = switching;
10686
10720
  let timer;
10721
+ let inFlight = false;
10722
+ let disposed = false;
10687
10723
  const tick = async () => {
10688
- let data = null;
10724
+ if (inFlight || disposed) return;
10725
+ inFlight = true;
10689
10726
  try {
10690
- data = await loadRef.current();
10727
+ const data = await loadRef.current();
10728
+ if (disposed) return;
10729
+ const step = nextRevisionTick(seen, ready, data);
10730
+ seen = step.seen;
10731
+ ready = step.ready;
10732
+ for (const topic of step.changed) onTopicRef.current(topic);
10691
10733
  } catch {
10692
- return;
10734
+ } finally {
10735
+ inFlight = false;
10693
10736
  }
10694
- const step = nextRevisionTick(seen, ready, data);
10695
- seen = step.seen;
10696
- ready = step.ready;
10697
- for (const topic of step.changed) onTopicRef.current(topic);
10698
10737
  };
10699
10738
  const stop = () => {
10700
10739
  if (timer !== void 0) window.clearInterval(timer);
@@ -10712,6 +10751,7 @@ function useSuiteRevisions(options) {
10712
10751
  if (document.visibilityState === "visible") start();
10713
10752
  document.addEventListener("visibilitychange", onVisibility);
10714
10753
  return () => {
10754
+ disposed = true;
10715
10755
  stop();
10716
10756
  document.removeEventListener("visibilitychange", onVisibility);
10717
10757
  };