@xenide-io/suite-client 0.5.0 → 0.6.0

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.js CHANGED
@@ -26,6 +26,8 @@ __export(index_exports, {
26
26
  ERROR_RETRYABLE: () => ERROR_RETRYABLE,
27
27
  FREE_PLAN_SEAT_HELP: () => FREE_PLAN_SEAT_HELP,
28
28
  ORGANISATION_SEAT_HELP: () => ORGANISATION_SEAT_HELP,
29
+ SUITE_APP_QUERY_CACHES: () => SUITE_APP_QUERY_CACHES,
30
+ SUITE_QUERY_CACHE_MAX_AGE_MS: () => SUITE_QUERY_CACHE_MAX_AGE_MS,
29
31
  TIDES_ACCESS_HELP: () => TIDES_ACCESS_HELP,
30
32
  TURTLETIME_ACCESS_HELP: () => TURTLETIME_ACCESS_HELP,
31
33
  WEBHOOK_ACTIVITY_ACTIONS: () => WEBHOOK_ACTIVITY_ACTIONS,
@@ -53,9 +55,12 @@ __export(index_exports, {
53
55
  formatAppError: () => formatAppError,
54
56
  hasAppAccess: () => hasAppAccess,
55
57
  isErrorCode: () => isErrorCode,
58
+ nextRevisionTick: () => nextRevisionTick,
56
59
  normalizeError: () => normalizeError,
57
60
  normalizeThrown: () => normalizeThrown,
61
+ persistSuiteQueries: () => persistSuiteQueries,
58
62
  planSeatSummary: () => planSeatSummary,
63
+ readJsonSnapshot: () => readJsonSnapshot,
59
64
  resolveSubscriptionTier: () => resolveSubscriptionTier,
60
65
  seatTypeDescription: () => seatTypeDescription,
61
66
  seatTypeFromFlags: () => seatTypeFromFlags,
@@ -63,11 +68,15 @@ __export(index_exports, {
63
68
  selectedToWebhookActions: () => selectedToWebhookActions,
64
69
  sortedSuiteAiEvents: () => sortedSuiteAiEvents,
65
70
  suiteAiEventSummary: () => suiteAiEventSummary,
71
+ suiteCacheBuster: () => suiteCacheBuster,
66
72
  tierAllowsTidesOnlySeats: () => tierAllowsTidesOnlySeats,
67
73
  tierAllowsTurtletime: () => tierAllowsTurtletime,
68
74
  tierLabel: () => tierLabel,
75
+ useEmailNotificationPreference: () => useEmailNotificationPreference,
69
76
  useIsNight: () => useIsNight,
70
- webhookActionsToSelected: () => webhookActionsToSelected
77
+ useSuiteRevisions: () => useSuiteRevisions,
78
+ webhookActionsToSelected: () => webhookActionsToSelected,
79
+ writeJsonSnapshot: () => writeJsonSnapshot
71
80
  });
72
81
  module.exports = __toCommonJS(index_exports);
73
82
 
@@ -9490,8 +9499,12 @@ function createAnalyticsClient(config) {
9490
9499
  defaults,
9491
9500
  person_profiles: "identified_only",
9492
9501
  autocapture: true,
9493
- capture_pageview: true,
9494
- capture_pageleave: true,
9502
+ // Pageviews are captured explicitly via `pageview()` (mount + route
9503
+ // change) so every `$pageview` carries the app/super-properties and the
9504
+ // behaviour is identical across all apps. The SDK's own pageview capture
9505
+ // is disabled to avoid double-counting and `defaults`-dependent behaviour.
9506
+ capture_pageview: false,
9507
+ capture_pageleave: false,
9495
9508
  persistence: "localStorage+cookie",
9496
9509
  cross_subdomain_cookie: true
9497
9510
  });
@@ -9549,6 +9562,7 @@ function createAnalyticsClient(config) {
9549
9562
  if (sessionId) headers["X-POSTHOG-SESSION-ID"] = sessionId;
9550
9563
  return headers;
9551
9564
  }
9565
+ if (isBrowser()) init();
9552
9566
  return {
9553
9567
  init,
9554
9568
  identify,
@@ -9575,6 +9589,7 @@ function createApiClient(config) {
9575
9589
  let accessToken = null;
9576
9590
  let refreshToken = null;
9577
9591
  let activeWorkspaceId = null;
9592
+ const etagBodies = /* @__PURE__ */ new Map();
9578
9593
  let authFailureHandler = null;
9579
9594
  let authFailureFired = false;
9580
9595
  let refreshPromise = null;
@@ -9666,6 +9681,7 @@ function createApiClient(config) {
9666
9681
  window.localStorage.removeItem(WORKSPACE_KEY);
9667
9682
  }
9668
9683
  if (changed) {
9684
+ etagBodies.clear();
9669
9685
  config.onWorkspaceChange?.(id2);
9670
9686
  if (id2 && previousWorkspaceId) {
9671
9687
  track("Workspace Switched", {
@@ -9699,6 +9715,7 @@ function createApiClient(config) {
9699
9715
  accessToken = null;
9700
9716
  refreshToken = null;
9701
9717
  activeWorkspaceId = null;
9718
+ etagBodies.clear();
9702
9719
  if (typeof window !== "undefined") {
9703
9720
  window.localStorage.removeItem(ACCESS_KEY);
9704
9721
  window.localStorage.removeItem(REFRESH_KEY);
@@ -9786,6 +9803,12 @@ function createApiClient(config) {
9786
9803
  headers["X-Active-Workspace-Id"] = wsId;
9787
9804
  }
9788
9805
  applyContextHeaders(headers);
9806
+ const method = (requestInit.method ?? "GET").toUpperCase();
9807
+ const etagKey = `${method}:${wsId ?? ""}:${path}`;
9808
+ if (method === "GET") {
9809
+ const hit = etagBodies.get(etagKey);
9810
+ if (hit) headers["If-None-Match"] = hit.etag;
9811
+ }
9789
9812
  const url = path.startsWith("http") ? path : `${API_URL}${path}`;
9790
9813
  if (config.enforceSameOrigin && !skipAuth && path.startsWith("http") && !url.startsWith(API_URL)) {
9791
9814
  throw new Error("Refusing to send the session to an unexpected origin.");
@@ -9817,6 +9840,26 @@ function createApiClient(config) {
9817
9840
  triggerAuthFailure();
9818
9841
  }
9819
9842
  }
9843
+ if (method === "GET" && res.status === 304) {
9844
+ const hit = etagBodies.get(etagKey);
9845
+ if (hit) {
9846
+ return new Response(hit.body, {
9847
+ status: 200,
9848
+ headers: { "Content-Type": "application/json", ETag: hit.etag }
9849
+ });
9850
+ }
9851
+ }
9852
+ if (method === "GET" && res.ok) {
9853
+ const etag = res.headers.get("ETag");
9854
+ if (etag) {
9855
+ const cachedBody = await res.clone().text();
9856
+ if (etagBodies.size >= 40) {
9857
+ const oldest = etagBodies.keys().next().value;
9858
+ if (oldest) etagBodies.delete(oldest);
9859
+ }
9860
+ etagBodies.set(etagKey, { etag, body: cachedBody });
9861
+ }
9862
+ }
9820
9863
  return res;
9821
9864
  }
9822
9865
  async function parseApiError(res) {
@@ -9911,8 +9954,6 @@ function createAuthReturnPath(config) {
9911
9954
  }
9912
9955
  }
9913
9956
  function readPortalHandoff(fallbackNext = DEFAULT_NEXT) {
9914
- const cached = cachedHandoff(fallbackNext);
9915
- if (cached) return cached;
9916
9957
  const hash = new URLSearchParams(window.location.hash.replace(/^#/, ""));
9917
9958
  const query = new URLSearchParams(window.location.search);
9918
9959
  const nextRaw = hash.get("next") || query.get("next");
@@ -9925,19 +9966,18 @@ function createAuthReturnPath(config) {
9925
9966
  if (result.code || result.token) {
9926
9967
  sessionStorage.setItem(HANDOFF_KEY, JSON.stringify(result));
9927
9968
  }
9928
- return result;
9969
+ return result.code || result.token ? result : cachedHandoff(fallbackNext) ?? result;
9929
9970
  }
9930
9971
  let redeemInflight = null;
9972
+ let redeemKey = "";
9931
9973
  function track(event, properties) {
9932
9974
  try {
9933
9975
  config.onAnalyticsEvent?.(event, properties);
9934
9976
  } catch {
9935
9977
  }
9936
9978
  }
9937
- async function redeemPortalHandoff(apiUrl, fallbackNext = DEFAULT_NEXT) {
9938
- redeemInflight ?? (redeemInflight = (async () => {
9939
- const { code, token, refresh, next } = readPortalHandoff(fallbackNext);
9940
- if (!code) return { code: null, token, refresh, next };
9979
+ async function requestSwitchCode(apiUrl, code) {
9980
+ try {
9941
9981
  const response = await fetch(`${apiUrl.replace(/\/$/, "")}${REDEEM_PATH}`, {
9942
9982
  method: "POST",
9943
9983
  headers: {
@@ -9946,21 +9986,57 @@ function createAuthReturnPath(config) {
9946
9986
  },
9947
9987
  body: JSON.stringify({ code })
9948
9988
  });
9949
- if (!response.ok) {
9989
+ if (!response.ok) return "dead";
9990
+ return await response.json();
9991
+ } catch {
9992
+ return "retry";
9993
+ }
9994
+ }
9995
+ async function redeemPortalHandoff(apiUrl, fallbackNext = DEFAULT_NEXT) {
9996
+ const handoff = readPortalHandoff(fallbackNext);
9997
+ const key = JSON.stringify([apiUrl, handoff]);
9998
+ if (redeemInflight && redeemKey === key) {
9999
+ return redeemInflight.then((result) => {
10000
+ if (result.token) sessionStorage.removeItem(HANDOFF_KEY);
10001
+ return result;
10002
+ });
10003
+ }
10004
+ redeemKey = key;
10005
+ const { code, token, refresh, next } = handoff;
10006
+ redeemInflight = (async () => {
10007
+ if (!code) {
10008
+ sessionStorage.removeItem(HANDOFF_KEY);
10009
+ return { code: null, token, refresh, next };
10010
+ }
10011
+ let outcome = await requestSwitchCode(apiUrl, code);
10012
+ if (outcome === "retry") outcome = await requestSwitchCode(apiUrl, code);
10013
+ if (outcome === "retry") {
10014
+ track("Cross-App Switch", { success: false });
10015
+ return { code: null, token: null, refresh: null, next };
10016
+ }
10017
+ if (outcome === "dead") {
10018
+ sessionStorage.removeItem(HANDOFF_KEY);
10019
+ track("Cross-App Switch", { success: false });
10020
+ return { code: null, token: null, refresh: null, next };
10021
+ }
10022
+ const redeemedToken = outcome.token || outcome.access || null;
10023
+ if (!redeemedToken) {
10024
+ sessionStorage.removeItem(HANDOFF_KEY);
9950
10025
  track("Cross-App Switch", { success: false });
9951
10026
  return { code: null, token: null, refresh: null, next };
9952
10027
  }
9953
- const data = await response.json();
9954
10028
  sessionStorage.removeItem(HANDOFF_KEY);
9955
- const redeemedToken = data.token || data.access || null;
9956
- track("Cross-App Switch", { success: Boolean(redeemedToken) });
10029
+ track("Cross-App Switch", { success: true });
9957
10030
  return {
9958
10031
  code: null,
9959
10032
  token: redeemedToken,
9960
- refresh: data.refresh || null,
10033
+ refresh: outcome.refresh || null,
9961
10034
  next
9962
10035
  };
9963
- })());
10036
+ })().then((result) => {
10037
+ if (!result.token && redeemKey === key) redeemInflight = null;
10038
+ return result;
10039
+ });
9964
10040
  return redeemInflight;
9965
10041
  }
9966
10042
  function readAuthReturnPath() {
@@ -10272,6 +10348,165 @@ function createWorkspaceAppearanceApi(config) {
10272
10348
  };
10273
10349
  }
10274
10350
 
10351
+ // src/cache/persist-query-cache.ts
10352
+ var SUITE_QUERY_CACHE_MAX_AGE_MS = 30 * 60 * 1e3;
10353
+ var MAX_SNAPSHOT_CHARS = 15e5;
10354
+ function suiteCacheBuster(tokenKey, workspaceKey) {
10355
+ if (typeof window === "undefined") return "";
10356
+ let token = null;
10357
+ let workspace = null;
10358
+ try {
10359
+ token = window.localStorage.getItem(tokenKey);
10360
+ workspace = window.localStorage.getItem(workspaceKey) || readCookie("xenide_active_workspace_id");
10361
+ } catch {
10362
+ return "";
10363
+ }
10364
+ const subject = subjectFromAccessToken(token);
10365
+ if (!subject || !workspace) return "";
10366
+ return `${subject}:${workspace}`;
10367
+ }
10368
+ function readJsonSnapshot(key, buster, maxAgeMs) {
10369
+ if (typeof window === "undefined" || !buster) return null;
10370
+ try {
10371
+ const raw = window.localStorage.getItem(key);
10372
+ if (!raw) return null;
10373
+ const parsed = JSON.parse(raw);
10374
+ if (parsed.buster !== buster || typeof parsed.savedAt !== "number") return null;
10375
+ if (Date.now() - parsed.savedAt > maxAgeMs) return null;
10376
+ return parsed.value ?? null;
10377
+ } catch {
10378
+ return null;
10379
+ }
10380
+ }
10381
+ function writeJsonSnapshot(key, buster, value) {
10382
+ if (typeof window === "undefined" || !buster) return;
10383
+ try {
10384
+ const raw = JSON.stringify({ buster, savedAt: Date.now(), value });
10385
+ if (raw.length > MAX_SNAPSHOT_CHARS) return;
10386
+ window.localStorage.setItem(key, raw);
10387
+ } catch {
10388
+ }
10389
+ }
10390
+ function persistSuiteQueries(queryClient, options) {
10391
+ const maxAgeMs = options.maxAgeMs ?? SUITE_QUERY_CACHE_MAX_AGE_MS;
10392
+ restore(queryClient, options, maxAgeMs);
10393
+ let timer = 0;
10394
+ const unsubscribe = queryClient.getQueryCache().subscribe(() => {
10395
+ if (typeof window === "undefined") return;
10396
+ window.clearTimeout(timer);
10397
+ timer = window.setTimeout(() => save(queryClient, options), 400);
10398
+ });
10399
+ return () => {
10400
+ if (typeof window !== "undefined") window.clearTimeout(timer);
10401
+ unsubscribe();
10402
+ };
10403
+ }
10404
+ function restore(queryClient, options, maxAgeMs) {
10405
+ const buster = suiteCacheBuster(options.tokenKey, options.workspaceKey);
10406
+ const stored = readJsonSnapshot(
10407
+ options.storageKey,
10408
+ buster,
10409
+ maxAgeMs
10410
+ );
10411
+ if (!Array.isArray(stored)) return;
10412
+ for (const entry of stored) {
10413
+ if (!entry || !Array.isArray(entry.queryKey) || !allowed(entry.queryKey, options.allow)) {
10414
+ continue;
10415
+ }
10416
+ queryClient.setQueryData(entry.queryKey, entry.data);
10417
+ void queryClient.invalidateQueries({
10418
+ queryKey: entry.queryKey,
10419
+ refetchType: "none"
10420
+ });
10421
+ }
10422
+ }
10423
+ function save(queryClient, options) {
10424
+ const buster = suiteCacheBuster(options.tokenKey, options.workspaceKey);
10425
+ if (!buster) return;
10426
+ const queries = queryClient.getQueryCache().getAll().filter(
10427
+ (query) => query.state.status === "success" && query.state.data !== void 0 && allowed(query.queryKey, options.allow)
10428
+ ).map((query) => ({ queryKey: query.queryKey, data: query.state.data }));
10429
+ writeJsonSnapshot(options.storageKey, buster, queries);
10430
+ }
10431
+ function allowed(queryKey, allow) {
10432
+ return allow.some((rule) => {
10433
+ if (typeof rule === "string") return queryKey[0] === rule;
10434
+ return rule.every((part, index) => queryKey[index] === part);
10435
+ });
10436
+ }
10437
+ function readCookie(name) {
10438
+ if (typeof document === "undefined") return null;
10439
+ const prefix = `${name}=`;
10440
+ const entry = document.cookie.split("; ").find((item) => item.startsWith(prefix));
10441
+ if (!entry) return null;
10442
+ try {
10443
+ return decodeURIComponent(entry.slice(prefix.length)) || null;
10444
+ } catch {
10445
+ return entry.slice(prefix.length) || null;
10446
+ }
10447
+ }
10448
+ function subjectFromAccessToken(token) {
10449
+ if (!token) return "";
10450
+ const segment = token.split(".")[1];
10451
+ if (!segment) return "";
10452
+ try {
10453
+ const base64 = segment.replace(/-/g, "+").replace(/_/g, "/");
10454
+ const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
10455
+ const json = JSON.parse(atob(padded));
10456
+ return typeof json.sub === "string" ? json.sub : "";
10457
+ } catch {
10458
+ return "";
10459
+ }
10460
+ }
10461
+ var SUITE_APP_QUERY_CACHES = {
10462
+ kraken: {
10463
+ storageKey: "kraken-query-cache:v1",
10464
+ tokenKey: "kraken_access_token",
10465
+ workspaceKey: "kraken_active_workspace_id",
10466
+ allow: ["kraken-sidebar"]
10467
+ },
10468
+ shellstack: {
10469
+ storageKey: "shellstack-query-cache:v1",
10470
+ tokenKey: "portal_access_token",
10471
+ workspaceKey: "portal_active_workspace_id",
10472
+ allow: ["calendar-events", "org-workspaces", "organisation", "org-team", "directory"]
10473
+ },
10474
+ tides: {
10475
+ storageKey: "tides-query-cache:v1",
10476
+ tokenKey: "tides_access_token",
10477
+ workspaceKey: "tides_active_workspace_id",
10478
+ allow: [
10479
+ ["tides", "projects"],
10480
+ ["tides", "clients"],
10481
+ ["tides", "workspace-members"],
10482
+ ["tides", "workspace-tags"],
10483
+ ["tides", "project-statuses"]
10484
+ ]
10485
+ },
10486
+ turtletime: {
10487
+ storageKey: "tt-query-cache:v1",
10488
+ tokenKey: "tt_access_token",
10489
+ workspaceKey: "tt_active_workspace_id",
10490
+ allow: [
10491
+ ["tt", "tracker-catalog"],
10492
+ ["tt", "projects"],
10493
+ ["tt", "clients"],
10494
+ ["tt", "categories"]
10495
+ ]
10496
+ },
10497
+ shelly: {
10498
+ storageKey: "shelly-query-cache:v1",
10499
+ tokenKey: "shelly_access_token",
10500
+ workspaceKey: "shelly_active_workspace_id",
10501
+ allow: [
10502
+ ["shelly", "skills"],
10503
+ ["shelly", "crews"],
10504
+ ["shelly", "mcp-servers"],
10505
+ ["shelly", "models"]
10506
+ ]
10507
+ }
10508
+ };
10509
+
10275
10510
  // src/sync/create-cross-app-sync.ts
10276
10511
  function createCrossAppSync(config) {
10277
10512
  const pollMs = config.pollMs ?? 5e3;
@@ -10415,6 +10650,74 @@ function createLiveRefresh(sync) {
10415
10650
  return { subscribeLiveRefresh, hasPendingCrossAppSync };
10416
10651
  }
10417
10652
 
10653
+ // src/sync/use-suite-revisions.ts
10654
+ var import_react = require("react");
10655
+ function nextRevisionTick(seen, ready, data) {
10656
+ if (!data) return { seen, ready, changed: [] };
10657
+ const next = { ...seen };
10658
+ if (!ready) {
10659
+ for (const [topic, value] of Object.entries(data)) {
10660
+ if (typeof value === "number") next[topic] = value;
10661
+ }
10662
+ return { seen: next, ready: true, changed: [] };
10663
+ }
10664
+ const changed = [];
10665
+ for (const [topic, value] of Object.entries(data)) {
10666
+ if (typeof value !== "number" || next[topic] === value) continue;
10667
+ next[topic] = value;
10668
+ changed.push(topic);
10669
+ }
10670
+ return { seen: next, ready: true, changed };
10671
+ }
10672
+ function useSuiteRevisions(options) {
10673
+ const loadRef = (0, import_react.useRef)(options.load);
10674
+ const onTopicRef = (0, import_react.useRef)(options.onTopic);
10675
+ loadRef.current = options.load;
10676
+ onTopicRef.current = options.onTopic;
10677
+ const pollMs = options.pollMs ?? 15e3;
10678
+ const scope = options.scope ?? "";
10679
+ const seenScope = (0, import_react.useRef)(null);
10680
+ (0, import_react.useEffect)(() => {
10681
+ if (!options.enabled || typeof window === "undefined") return;
10682
+ const switching = seenScope.current !== null && seenScope.current !== scope;
10683
+ seenScope.current = scope;
10684
+ let seen = {};
10685
+ let ready = switching;
10686
+ let timer;
10687
+ const tick = async () => {
10688
+ let data = null;
10689
+ try {
10690
+ data = await loadRef.current();
10691
+ } catch {
10692
+ return;
10693
+ }
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
+ };
10699
+ const stop = () => {
10700
+ if (timer !== void 0) window.clearInterval(timer);
10701
+ timer = void 0;
10702
+ };
10703
+ const start = () => {
10704
+ if (timer !== void 0) return;
10705
+ void tick();
10706
+ timer = window.setInterval(() => void tick(), pollMs);
10707
+ };
10708
+ const onVisibility = () => {
10709
+ if (document.visibilityState === "visible") start();
10710
+ else stop();
10711
+ };
10712
+ if (document.visibilityState === "visible") start();
10713
+ document.addEventListener("visibilitychange", onVisibility);
10714
+ return () => {
10715
+ stop();
10716
+ document.removeEventListener("visibilitychange", onVisibility);
10717
+ };
10718
+ }, [options.enabled, pollMs, scope]);
10719
+ }
10720
+
10418
10721
  // src/notifications/create-notifications-client.ts
10419
10722
  function createNotificationsClient(config) {
10420
10723
  const { api, parseApiError } = config;
@@ -10560,6 +10863,47 @@ function createWebPushClient(config) {
10560
10863
  return { supported, getState, enable, disable };
10561
10864
  }
10562
10865
 
10866
+ // src/notifications/use-email-notification-preference.ts
10867
+ var import_react2 = require("react");
10868
+ var EMAIL_PREFERENCE_PATH = "/api/notifications/email/";
10869
+ function useEmailNotificationPreference(api, parseApiError) {
10870
+ const [enabled, setEnabled] = (0, import_react2.useState)(false);
10871
+ const [busy, setBusy] = (0, import_react2.useState)(false);
10872
+ const [error, setError] = (0, import_react2.useState)("");
10873
+ (0, import_react2.useEffect)(() => {
10874
+ let cancelled = false;
10875
+ api.get(EMAIL_PREFERENCE_PATH).then(async (res) => {
10876
+ if (!res.ok || cancelled) return;
10877
+ const data = await res.json();
10878
+ if (!cancelled) setEnabled(Boolean(data.enabled));
10879
+ }).catch(() => {
10880
+ });
10881
+ return () => {
10882
+ cancelled = true;
10883
+ };
10884
+ }, [api]);
10885
+ const onChange = (0, import_react2.useCallback)(
10886
+ (next) => {
10887
+ setBusy(true);
10888
+ setError("");
10889
+ setEnabled(next);
10890
+ api.patch(EMAIL_PREFERENCE_PATH, { enabled: next }).then(async (res) => {
10891
+ if (!res.ok) {
10892
+ setEnabled(!next);
10893
+ setError(await parseApiError(res));
10894
+ }
10895
+ }).catch((err) => {
10896
+ setEnabled(!next);
10897
+ setError(
10898
+ err instanceof Error ? err.message : "Could not save email notifications."
10899
+ );
10900
+ }).finally(() => setBusy(false));
10901
+ },
10902
+ [api, parseApiError]
10903
+ );
10904
+ return { enabled, busy, error, onChange };
10905
+ }
10906
+
10563
10907
  // src/ai/suite-ai-job.ts
10564
10908
  function activeSuiteAiJobs(jobs) {
10565
10909
  return jobs.filter(
@@ -10641,12 +10985,12 @@ function createJsonLd(config) {
10641
10985
  }
10642
10986
 
10643
10987
  // src/hooks/use-is-night.ts
10644
- var import_react = require("react");
10988
+ var import_react3 = require("react");
10645
10989
  var NIGHT_START_HOUR = 18;
10646
10990
  var NIGHT_END_HOUR = 6;
10647
10991
  function useIsNight() {
10648
- const [isNight, setIsNight] = (0, import_react.useState)(false);
10649
- (0, import_react.useEffect)(() => {
10992
+ const [isNight, setIsNight] = (0, import_react3.useState)(false);
10993
+ (0, import_react3.useEffect)(() => {
10650
10994
  const check = () => {
10651
10995
  const hour = (/* @__PURE__ */ new Date()).getHours();
10652
10996
  return hour >= NIGHT_START_HOUR || hour < NIGHT_END_HOUR;
@@ -10671,6 +11015,8 @@ function useIsNight() {
10671
11015
  ERROR_RETRYABLE,
10672
11016
  FREE_PLAN_SEAT_HELP,
10673
11017
  ORGANISATION_SEAT_HELP,
11018
+ SUITE_APP_QUERY_CACHES,
11019
+ SUITE_QUERY_CACHE_MAX_AGE_MS,
10674
11020
  TIDES_ACCESS_HELP,
10675
11021
  TURTLETIME_ACCESS_HELP,
10676
11022
  WEBHOOK_ACTIVITY_ACTIONS,
@@ -10698,9 +11044,12 @@ function useIsNight() {
10698
11044
  formatAppError,
10699
11045
  hasAppAccess,
10700
11046
  isErrorCode,
11047
+ nextRevisionTick,
10701
11048
  normalizeError,
10702
11049
  normalizeThrown,
11050
+ persistSuiteQueries,
10703
11051
  planSeatSummary,
11052
+ readJsonSnapshot,
10704
11053
  resolveSubscriptionTier,
10705
11054
  seatTypeDescription,
10706
11055
  seatTypeFromFlags,
@@ -10708,10 +11057,14 @@ function useIsNight() {
10708
11057
  selectedToWebhookActions,
10709
11058
  sortedSuiteAiEvents,
10710
11059
  suiteAiEventSummary,
11060
+ suiteCacheBuster,
10711
11061
  tierAllowsTidesOnlySeats,
10712
11062
  tierAllowsTurtletime,
10713
11063
  tierLabel,
11064
+ useEmailNotificationPreference,
10714
11065
  useIsNight,
10715
- webhookActionsToSelected
11066
+ useSuiteRevisions,
11067
+ webhookActionsToSelected,
11068
+ writeJsonSnapshot
10716
11069
  });
10717
11070
  //# sourceMappingURL=index.js.map