@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.d.mts +126 -1
- package/dist/index.d.ts +126 -1
- package/dist/index.js +373 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +363 -19
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -9417,8 +9417,12 @@ function createAnalyticsClient(config) {
|
|
|
9417
9417
|
defaults,
|
|
9418
9418
|
person_profiles: "identified_only",
|
|
9419
9419
|
autocapture: true,
|
|
9420
|
-
|
|
9421
|
-
|
|
9420
|
+
// Pageviews are captured explicitly via `pageview()` (mount + route
|
|
9421
|
+
// change) so every `$pageview` carries the app/super-properties and the
|
|
9422
|
+
// behaviour is identical across all apps. The SDK's own pageview capture
|
|
9423
|
+
// is disabled to avoid double-counting and `defaults`-dependent behaviour.
|
|
9424
|
+
capture_pageview: false,
|
|
9425
|
+
capture_pageleave: false,
|
|
9422
9426
|
persistence: "localStorage+cookie",
|
|
9423
9427
|
cross_subdomain_cookie: true
|
|
9424
9428
|
});
|
|
@@ -9476,6 +9480,7 @@ function createAnalyticsClient(config) {
|
|
|
9476
9480
|
if (sessionId) headers["X-POSTHOG-SESSION-ID"] = sessionId;
|
|
9477
9481
|
return headers;
|
|
9478
9482
|
}
|
|
9483
|
+
if (isBrowser()) init();
|
|
9479
9484
|
return {
|
|
9480
9485
|
init,
|
|
9481
9486
|
identify,
|
|
@@ -9502,6 +9507,7 @@ function createApiClient(config) {
|
|
|
9502
9507
|
let accessToken = null;
|
|
9503
9508
|
let refreshToken = null;
|
|
9504
9509
|
let activeWorkspaceId = null;
|
|
9510
|
+
const etagBodies = /* @__PURE__ */ new Map();
|
|
9505
9511
|
let authFailureHandler = null;
|
|
9506
9512
|
let authFailureFired = false;
|
|
9507
9513
|
let refreshPromise = null;
|
|
@@ -9593,6 +9599,7 @@ function createApiClient(config) {
|
|
|
9593
9599
|
window.localStorage.removeItem(WORKSPACE_KEY);
|
|
9594
9600
|
}
|
|
9595
9601
|
if (changed) {
|
|
9602
|
+
etagBodies.clear();
|
|
9596
9603
|
config.onWorkspaceChange?.(id2);
|
|
9597
9604
|
if (id2 && previousWorkspaceId) {
|
|
9598
9605
|
track("Workspace Switched", {
|
|
@@ -9626,6 +9633,7 @@ function createApiClient(config) {
|
|
|
9626
9633
|
accessToken = null;
|
|
9627
9634
|
refreshToken = null;
|
|
9628
9635
|
activeWorkspaceId = null;
|
|
9636
|
+
etagBodies.clear();
|
|
9629
9637
|
if (typeof window !== "undefined") {
|
|
9630
9638
|
window.localStorage.removeItem(ACCESS_KEY);
|
|
9631
9639
|
window.localStorage.removeItem(REFRESH_KEY);
|
|
@@ -9713,6 +9721,12 @@ function createApiClient(config) {
|
|
|
9713
9721
|
headers["X-Active-Workspace-Id"] = wsId;
|
|
9714
9722
|
}
|
|
9715
9723
|
applyContextHeaders(headers);
|
|
9724
|
+
const method = (requestInit.method ?? "GET").toUpperCase();
|
|
9725
|
+
const etagKey = `${method}:${wsId ?? ""}:${path}`;
|
|
9726
|
+
if (method === "GET") {
|
|
9727
|
+
const hit = etagBodies.get(etagKey);
|
|
9728
|
+
if (hit) headers["If-None-Match"] = hit.etag;
|
|
9729
|
+
}
|
|
9716
9730
|
const url = path.startsWith("http") ? path : `${API_URL}${path}`;
|
|
9717
9731
|
if (config.enforceSameOrigin && !skipAuth && path.startsWith("http") && !url.startsWith(API_URL)) {
|
|
9718
9732
|
throw new Error("Refusing to send the session to an unexpected origin.");
|
|
@@ -9744,6 +9758,26 @@ function createApiClient(config) {
|
|
|
9744
9758
|
triggerAuthFailure();
|
|
9745
9759
|
}
|
|
9746
9760
|
}
|
|
9761
|
+
if (method === "GET" && res.status === 304) {
|
|
9762
|
+
const hit = etagBodies.get(etagKey);
|
|
9763
|
+
if (hit) {
|
|
9764
|
+
return new Response(hit.body, {
|
|
9765
|
+
status: 200,
|
|
9766
|
+
headers: { "Content-Type": "application/json", ETag: hit.etag }
|
|
9767
|
+
});
|
|
9768
|
+
}
|
|
9769
|
+
}
|
|
9770
|
+
if (method === "GET" && res.ok) {
|
|
9771
|
+
const etag = res.headers.get("ETag");
|
|
9772
|
+
if (etag) {
|
|
9773
|
+
const cachedBody = await res.clone().text();
|
|
9774
|
+
if (etagBodies.size >= 40) {
|
|
9775
|
+
const oldest = etagBodies.keys().next().value;
|
|
9776
|
+
if (oldest) etagBodies.delete(oldest);
|
|
9777
|
+
}
|
|
9778
|
+
etagBodies.set(etagKey, { etag, body: cachedBody });
|
|
9779
|
+
}
|
|
9780
|
+
}
|
|
9747
9781
|
return res;
|
|
9748
9782
|
}
|
|
9749
9783
|
async function parseApiError(res) {
|
|
@@ -9838,8 +9872,6 @@ function createAuthReturnPath(config) {
|
|
|
9838
9872
|
}
|
|
9839
9873
|
}
|
|
9840
9874
|
function readPortalHandoff(fallbackNext = DEFAULT_NEXT) {
|
|
9841
|
-
const cached = cachedHandoff(fallbackNext);
|
|
9842
|
-
if (cached) return cached;
|
|
9843
9875
|
const hash = new URLSearchParams(window.location.hash.replace(/^#/, ""));
|
|
9844
9876
|
const query = new URLSearchParams(window.location.search);
|
|
9845
9877
|
const nextRaw = hash.get("next") || query.get("next");
|
|
@@ -9852,19 +9884,18 @@ function createAuthReturnPath(config) {
|
|
|
9852
9884
|
if (result.code || result.token) {
|
|
9853
9885
|
sessionStorage.setItem(HANDOFF_KEY, JSON.stringify(result));
|
|
9854
9886
|
}
|
|
9855
|
-
return result;
|
|
9887
|
+
return result.code || result.token ? result : cachedHandoff(fallbackNext) ?? result;
|
|
9856
9888
|
}
|
|
9857
9889
|
let redeemInflight = null;
|
|
9890
|
+
let redeemKey = "";
|
|
9858
9891
|
function track(event, properties) {
|
|
9859
9892
|
try {
|
|
9860
9893
|
config.onAnalyticsEvent?.(event, properties);
|
|
9861
9894
|
} catch {
|
|
9862
9895
|
}
|
|
9863
9896
|
}
|
|
9864
|
-
async function
|
|
9865
|
-
|
|
9866
|
-
const { code, token, refresh, next } = readPortalHandoff(fallbackNext);
|
|
9867
|
-
if (!code) return { code: null, token, refresh, next };
|
|
9897
|
+
async function requestSwitchCode(apiUrl, code) {
|
|
9898
|
+
try {
|
|
9868
9899
|
const response = await fetch(`${apiUrl.replace(/\/$/, "")}${REDEEM_PATH}`, {
|
|
9869
9900
|
method: "POST",
|
|
9870
9901
|
headers: {
|
|
@@ -9873,21 +9904,57 @@ function createAuthReturnPath(config) {
|
|
|
9873
9904
|
},
|
|
9874
9905
|
body: JSON.stringify({ code })
|
|
9875
9906
|
});
|
|
9876
|
-
if (!response.ok)
|
|
9907
|
+
if (!response.ok) return "dead";
|
|
9908
|
+
return await response.json();
|
|
9909
|
+
} catch {
|
|
9910
|
+
return "retry";
|
|
9911
|
+
}
|
|
9912
|
+
}
|
|
9913
|
+
async function redeemPortalHandoff(apiUrl, fallbackNext = DEFAULT_NEXT) {
|
|
9914
|
+
const handoff = readPortalHandoff(fallbackNext);
|
|
9915
|
+
const key = JSON.stringify([apiUrl, handoff]);
|
|
9916
|
+
if (redeemInflight && redeemKey === key) {
|
|
9917
|
+
return redeemInflight.then((result) => {
|
|
9918
|
+
if (result.token) sessionStorage.removeItem(HANDOFF_KEY);
|
|
9919
|
+
return result;
|
|
9920
|
+
});
|
|
9921
|
+
}
|
|
9922
|
+
redeemKey = key;
|
|
9923
|
+
const { code, token, refresh, next } = handoff;
|
|
9924
|
+
redeemInflight = (async () => {
|
|
9925
|
+
if (!code) {
|
|
9926
|
+
sessionStorage.removeItem(HANDOFF_KEY);
|
|
9927
|
+
return { code: null, token, refresh, next };
|
|
9928
|
+
}
|
|
9929
|
+
let outcome = await requestSwitchCode(apiUrl, code);
|
|
9930
|
+
if (outcome === "retry") outcome = await requestSwitchCode(apiUrl, code);
|
|
9931
|
+
if (outcome === "retry") {
|
|
9932
|
+
track("Cross-App Switch", { success: false });
|
|
9933
|
+
return { code: null, token: null, refresh: null, next };
|
|
9934
|
+
}
|
|
9935
|
+
if (outcome === "dead") {
|
|
9936
|
+
sessionStorage.removeItem(HANDOFF_KEY);
|
|
9937
|
+
track("Cross-App Switch", { success: false });
|
|
9938
|
+
return { code: null, token: null, refresh: null, next };
|
|
9939
|
+
}
|
|
9940
|
+
const redeemedToken = outcome.token || outcome.access || null;
|
|
9941
|
+
if (!redeemedToken) {
|
|
9942
|
+
sessionStorage.removeItem(HANDOFF_KEY);
|
|
9877
9943
|
track("Cross-App Switch", { success: false });
|
|
9878
9944
|
return { code: null, token: null, refresh: null, next };
|
|
9879
9945
|
}
|
|
9880
|
-
const data = await response.json();
|
|
9881
9946
|
sessionStorage.removeItem(HANDOFF_KEY);
|
|
9882
|
-
|
|
9883
|
-
track("Cross-App Switch", { success: Boolean(redeemedToken) });
|
|
9947
|
+
track("Cross-App Switch", { success: true });
|
|
9884
9948
|
return {
|
|
9885
9949
|
code: null,
|
|
9886
9950
|
token: redeemedToken,
|
|
9887
|
-
refresh:
|
|
9951
|
+
refresh: outcome.refresh || null,
|
|
9888
9952
|
next
|
|
9889
9953
|
};
|
|
9890
|
-
})())
|
|
9954
|
+
})().then((result) => {
|
|
9955
|
+
if (!result.token && redeemKey === key) redeemInflight = null;
|
|
9956
|
+
return result;
|
|
9957
|
+
});
|
|
9891
9958
|
return redeemInflight;
|
|
9892
9959
|
}
|
|
9893
9960
|
function readAuthReturnPath() {
|
|
@@ -10199,6 +10266,165 @@ function createWorkspaceAppearanceApi(config) {
|
|
|
10199
10266
|
};
|
|
10200
10267
|
}
|
|
10201
10268
|
|
|
10269
|
+
// src/cache/persist-query-cache.ts
|
|
10270
|
+
var SUITE_QUERY_CACHE_MAX_AGE_MS = 30 * 60 * 1e3;
|
|
10271
|
+
var MAX_SNAPSHOT_CHARS = 15e5;
|
|
10272
|
+
function suiteCacheBuster(tokenKey, workspaceKey) {
|
|
10273
|
+
if (typeof window === "undefined") return "";
|
|
10274
|
+
let token = null;
|
|
10275
|
+
let workspace = null;
|
|
10276
|
+
try {
|
|
10277
|
+
token = window.localStorage.getItem(tokenKey);
|
|
10278
|
+
workspace = window.localStorage.getItem(workspaceKey) || readCookie("xenide_active_workspace_id");
|
|
10279
|
+
} catch {
|
|
10280
|
+
return "";
|
|
10281
|
+
}
|
|
10282
|
+
const subject = subjectFromAccessToken(token);
|
|
10283
|
+
if (!subject || !workspace) return "";
|
|
10284
|
+
return `${subject}:${workspace}`;
|
|
10285
|
+
}
|
|
10286
|
+
function readJsonSnapshot(key, buster, maxAgeMs) {
|
|
10287
|
+
if (typeof window === "undefined" || !buster) return null;
|
|
10288
|
+
try {
|
|
10289
|
+
const raw = window.localStorage.getItem(key);
|
|
10290
|
+
if (!raw) return null;
|
|
10291
|
+
const parsed = JSON.parse(raw);
|
|
10292
|
+
if (parsed.buster !== buster || typeof parsed.savedAt !== "number") return null;
|
|
10293
|
+
if (Date.now() - parsed.savedAt > maxAgeMs) return null;
|
|
10294
|
+
return parsed.value ?? null;
|
|
10295
|
+
} catch {
|
|
10296
|
+
return null;
|
|
10297
|
+
}
|
|
10298
|
+
}
|
|
10299
|
+
function writeJsonSnapshot(key, buster, value) {
|
|
10300
|
+
if (typeof window === "undefined" || !buster) return;
|
|
10301
|
+
try {
|
|
10302
|
+
const raw = JSON.stringify({ buster, savedAt: Date.now(), value });
|
|
10303
|
+
if (raw.length > MAX_SNAPSHOT_CHARS) return;
|
|
10304
|
+
window.localStorage.setItem(key, raw);
|
|
10305
|
+
} catch {
|
|
10306
|
+
}
|
|
10307
|
+
}
|
|
10308
|
+
function persistSuiteQueries(queryClient, options) {
|
|
10309
|
+
const maxAgeMs = options.maxAgeMs ?? SUITE_QUERY_CACHE_MAX_AGE_MS;
|
|
10310
|
+
restore(queryClient, options, maxAgeMs);
|
|
10311
|
+
let timer = 0;
|
|
10312
|
+
const unsubscribe = queryClient.getQueryCache().subscribe(() => {
|
|
10313
|
+
if (typeof window === "undefined") return;
|
|
10314
|
+
window.clearTimeout(timer);
|
|
10315
|
+
timer = window.setTimeout(() => save(queryClient, options), 400);
|
|
10316
|
+
});
|
|
10317
|
+
return () => {
|
|
10318
|
+
if (typeof window !== "undefined") window.clearTimeout(timer);
|
|
10319
|
+
unsubscribe();
|
|
10320
|
+
};
|
|
10321
|
+
}
|
|
10322
|
+
function restore(queryClient, options, maxAgeMs) {
|
|
10323
|
+
const buster = suiteCacheBuster(options.tokenKey, options.workspaceKey);
|
|
10324
|
+
const stored = readJsonSnapshot(
|
|
10325
|
+
options.storageKey,
|
|
10326
|
+
buster,
|
|
10327
|
+
maxAgeMs
|
|
10328
|
+
);
|
|
10329
|
+
if (!Array.isArray(stored)) return;
|
|
10330
|
+
for (const entry of stored) {
|
|
10331
|
+
if (!entry || !Array.isArray(entry.queryKey) || !allowed(entry.queryKey, options.allow)) {
|
|
10332
|
+
continue;
|
|
10333
|
+
}
|
|
10334
|
+
queryClient.setQueryData(entry.queryKey, entry.data);
|
|
10335
|
+
void queryClient.invalidateQueries({
|
|
10336
|
+
queryKey: entry.queryKey,
|
|
10337
|
+
refetchType: "none"
|
|
10338
|
+
});
|
|
10339
|
+
}
|
|
10340
|
+
}
|
|
10341
|
+
function save(queryClient, options) {
|
|
10342
|
+
const buster = suiteCacheBuster(options.tokenKey, options.workspaceKey);
|
|
10343
|
+
if (!buster) return;
|
|
10344
|
+
const queries = queryClient.getQueryCache().getAll().filter(
|
|
10345
|
+
(query) => query.state.status === "success" && query.state.data !== void 0 && allowed(query.queryKey, options.allow)
|
|
10346
|
+
).map((query) => ({ queryKey: query.queryKey, data: query.state.data }));
|
|
10347
|
+
writeJsonSnapshot(options.storageKey, buster, queries);
|
|
10348
|
+
}
|
|
10349
|
+
function allowed(queryKey, allow) {
|
|
10350
|
+
return allow.some((rule) => {
|
|
10351
|
+
if (typeof rule === "string") return queryKey[0] === rule;
|
|
10352
|
+
return rule.every((part, index) => queryKey[index] === part);
|
|
10353
|
+
});
|
|
10354
|
+
}
|
|
10355
|
+
function readCookie(name) {
|
|
10356
|
+
if (typeof document === "undefined") return null;
|
|
10357
|
+
const prefix = `${name}=`;
|
|
10358
|
+
const entry = document.cookie.split("; ").find((item) => item.startsWith(prefix));
|
|
10359
|
+
if (!entry) return null;
|
|
10360
|
+
try {
|
|
10361
|
+
return decodeURIComponent(entry.slice(prefix.length)) || null;
|
|
10362
|
+
} catch {
|
|
10363
|
+
return entry.slice(prefix.length) || null;
|
|
10364
|
+
}
|
|
10365
|
+
}
|
|
10366
|
+
function subjectFromAccessToken(token) {
|
|
10367
|
+
if (!token) return "";
|
|
10368
|
+
const segment = token.split(".")[1];
|
|
10369
|
+
if (!segment) return "";
|
|
10370
|
+
try {
|
|
10371
|
+
const base64 = segment.replace(/-/g, "+").replace(/_/g, "/");
|
|
10372
|
+
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
10373
|
+
const json = JSON.parse(atob(padded));
|
|
10374
|
+
return typeof json.sub === "string" ? json.sub : "";
|
|
10375
|
+
} catch {
|
|
10376
|
+
return "";
|
|
10377
|
+
}
|
|
10378
|
+
}
|
|
10379
|
+
var SUITE_APP_QUERY_CACHES = {
|
|
10380
|
+
kraken: {
|
|
10381
|
+
storageKey: "kraken-query-cache:v1",
|
|
10382
|
+
tokenKey: "kraken_access_token",
|
|
10383
|
+
workspaceKey: "kraken_active_workspace_id",
|
|
10384
|
+
allow: ["kraken-sidebar"]
|
|
10385
|
+
},
|
|
10386
|
+
shellstack: {
|
|
10387
|
+
storageKey: "shellstack-query-cache:v1",
|
|
10388
|
+
tokenKey: "portal_access_token",
|
|
10389
|
+
workspaceKey: "portal_active_workspace_id",
|
|
10390
|
+
allow: ["calendar-events", "org-workspaces", "organisation", "org-team", "directory"]
|
|
10391
|
+
},
|
|
10392
|
+
tides: {
|
|
10393
|
+
storageKey: "tides-query-cache:v1",
|
|
10394
|
+
tokenKey: "tides_access_token",
|
|
10395
|
+
workspaceKey: "tides_active_workspace_id",
|
|
10396
|
+
allow: [
|
|
10397
|
+
["tides", "projects"],
|
|
10398
|
+
["tides", "clients"],
|
|
10399
|
+
["tides", "workspace-members"],
|
|
10400
|
+
["tides", "workspace-tags"],
|
|
10401
|
+
["tides", "project-statuses"]
|
|
10402
|
+
]
|
|
10403
|
+
},
|
|
10404
|
+
turtletime: {
|
|
10405
|
+
storageKey: "tt-query-cache:v1",
|
|
10406
|
+
tokenKey: "tt_access_token",
|
|
10407
|
+
workspaceKey: "tt_active_workspace_id",
|
|
10408
|
+
allow: [
|
|
10409
|
+
["tt", "tracker-catalog"],
|
|
10410
|
+
["tt", "projects"],
|
|
10411
|
+
["tt", "clients"],
|
|
10412
|
+
["tt", "categories"]
|
|
10413
|
+
]
|
|
10414
|
+
},
|
|
10415
|
+
shelly: {
|
|
10416
|
+
storageKey: "shelly-query-cache:v1",
|
|
10417
|
+
tokenKey: "shelly_access_token",
|
|
10418
|
+
workspaceKey: "shelly_active_workspace_id",
|
|
10419
|
+
allow: [
|
|
10420
|
+
["shelly", "skills"],
|
|
10421
|
+
["shelly", "crews"],
|
|
10422
|
+
["shelly", "mcp-servers"],
|
|
10423
|
+
["shelly", "models"]
|
|
10424
|
+
]
|
|
10425
|
+
}
|
|
10426
|
+
};
|
|
10427
|
+
|
|
10202
10428
|
// src/sync/create-cross-app-sync.ts
|
|
10203
10429
|
function createCrossAppSync(config) {
|
|
10204
10430
|
const pollMs = config.pollMs ?? 5e3;
|
|
@@ -10342,6 +10568,74 @@ function createLiveRefresh(sync) {
|
|
|
10342
10568
|
return { subscribeLiveRefresh, hasPendingCrossAppSync };
|
|
10343
10569
|
}
|
|
10344
10570
|
|
|
10571
|
+
// src/sync/use-suite-revisions.ts
|
|
10572
|
+
import { useEffect, useRef } from "react";
|
|
10573
|
+
function nextRevisionTick(seen, ready, data) {
|
|
10574
|
+
if (!data) return { seen, ready, changed: [] };
|
|
10575
|
+
const next = { ...seen };
|
|
10576
|
+
if (!ready) {
|
|
10577
|
+
for (const [topic, value] of Object.entries(data)) {
|
|
10578
|
+
if (typeof value === "number") next[topic] = value;
|
|
10579
|
+
}
|
|
10580
|
+
return { seen: next, ready: true, changed: [] };
|
|
10581
|
+
}
|
|
10582
|
+
const changed = [];
|
|
10583
|
+
for (const [topic, value] of Object.entries(data)) {
|
|
10584
|
+
if (typeof value !== "number" || next[topic] === value) continue;
|
|
10585
|
+
next[topic] = value;
|
|
10586
|
+
changed.push(topic);
|
|
10587
|
+
}
|
|
10588
|
+
return { seen: next, ready: true, changed };
|
|
10589
|
+
}
|
|
10590
|
+
function useSuiteRevisions(options) {
|
|
10591
|
+
const loadRef = useRef(options.load);
|
|
10592
|
+
const onTopicRef = useRef(options.onTopic);
|
|
10593
|
+
loadRef.current = options.load;
|
|
10594
|
+
onTopicRef.current = options.onTopic;
|
|
10595
|
+
const pollMs = options.pollMs ?? 15e3;
|
|
10596
|
+
const scope = options.scope ?? "";
|
|
10597
|
+
const seenScope = useRef(null);
|
|
10598
|
+
useEffect(() => {
|
|
10599
|
+
if (!options.enabled || typeof window === "undefined") return;
|
|
10600
|
+
const switching = seenScope.current !== null && seenScope.current !== scope;
|
|
10601
|
+
seenScope.current = scope;
|
|
10602
|
+
let seen = {};
|
|
10603
|
+
let ready = switching;
|
|
10604
|
+
let timer;
|
|
10605
|
+
const tick = async () => {
|
|
10606
|
+
let data = null;
|
|
10607
|
+
try {
|
|
10608
|
+
data = await loadRef.current();
|
|
10609
|
+
} catch {
|
|
10610
|
+
return;
|
|
10611
|
+
}
|
|
10612
|
+
const step = nextRevisionTick(seen, ready, data);
|
|
10613
|
+
seen = step.seen;
|
|
10614
|
+
ready = step.ready;
|
|
10615
|
+
for (const topic of step.changed) onTopicRef.current(topic);
|
|
10616
|
+
};
|
|
10617
|
+
const stop = () => {
|
|
10618
|
+
if (timer !== void 0) window.clearInterval(timer);
|
|
10619
|
+
timer = void 0;
|
|
10620
|
+
};
|
|
10621
|
+
const start = () => {
|
|
10622
|
+
if (timer !== void 0) return;
|
|
10623
|
+
void tick();
|
|
10624
|
+
timer = window.setInterval(() => void tick(), pollMs);
|
|
10625
|
+
};
|
|
10626
|
+
const onVisibility = () => {
|
|
10627
|
+
if (document.visibilityState === "visible") start();
|
|
10628
|
+
else stop();
|
|
10629
|
+
};
|
|
10630
|
+
if (document.visibilityState === "visible") start();
|
|
10631
|
+
document.addEventListener("visibilitychange", onVisibility);
|
|
10632
|
+
return () => {
|
|
10633
|
+
stop();
|
|
10634
|
+
document.removeEventListener("visibilitychange", onVisibility);
|
|
10635
|
+
};
|
|
10636
|
+
}, [options.enabled, pollMs, scope]);
|
|
10637
|
+
}
|
|
10638
|
+
|
|
10345
10639
|
// src/notifications/create-notifications-client.ts
|
|
10346
10640
|
function createNotificationsClient(config) {
|
|
10347
10641
|
const { api, parseApiError } = config;
|
|
@@ -10487,6 +10781,47 @@ function createWebPushClient(config) {
|
|
|
10487
10781
|
return { supported, getState, enable, disable };
|
|
10488
10782
|
}
|
|
10489
10783
|
|
|
10784
|
+
// src/notifications/use-email-notification-preference.ts
|
|
10785
|
+
import { useCallback, useEffect as useEffect2, useState } from "react";
|
|
10786
|
+
var EMAIL_PREFERENCE_PATH = "/api/notifications/email/";
|
|
10787
|
+
function useEmailNotificationPreference(api, parseApiError) {
|
|
10788
|
+
const [enabled, setEnabled] = useState(false);
|
|
10789
|
+
const [busy, setBusy] = useState(false);
|
|
10790
|
+
const [error, setError] = useState("");
|
|
10791
|
+
useEffect2(() => {
|
|
10792
|
+
let cancelled = false;
|
|
10793
|
+
api.get(EMAIL_PREFERENCE_PATH).then(async (res) => {
|
|
10794
|
+
if (!res.ok || cancelled) return;
|
|
10795
|
+
const data = await res.json();
|
|
10796
|
+
if (!cancelled) setEnabled(Boolean(data.enabled));
|
|
10797
|
+
}).catch(() => {
|
|
10798
|
+
});
|
|
10799
|
+
return () => {
|
|
10800
|
+
cancelled = true;
|
|
10801
|
+
};
|
|
10802
|
+
}, [api]);
|
|
10803
|
+
const onChange = useCallback(
|
|
10804
|
+
(next) => {
|
|
10805
|
+
setBusy(true);
|
|
10806
|
+
setError("");
|
|
10807
|
+
setEnabled(next);
|
|
10808
|
+
api.patch(EMAIL_PREFERENCE_PATH, { enabled: next }).then(async (res) => {
|
|
10809
|
+
if (!res.ok) {
|
|
10810
|
+
setEnabled(!next);
|
|
10811
|
+
setError(await parseApiError(res));
|
|
10812
|
+
}
|
|
10813
|
+
}).catch((err) => {
|
|
10814
|
+
setEnabled(!next);
|
|
10815
|
+
setError(
|
|
10816
|
+
err instanceof Error ? err.message : "Could not save email notifications."
|
|
10817
|
+
);
|
|
10818
|
+
}).finally(() => setBusy(false));
|
|
10819
|
+
},
|
|
10820
|
+
[api, parseApiError]
|
|
10821
|
+
);
|
|
10822
|
+
return { enabled, busy, error, onChange };
|
|
10823
|
+
}
|
|
10824
|
+
|
|
10490
10825
|
// src/ai/suite-ai-job.ts
|
|
10491
10826
|
function activeSuiteAiJobs(jobs) {
|
|
10492
10827
|
return jobs.filter(
|
|
@@ -10568,12 +10903,12 @@ function createJsonLd(config) {
|
|
|
10568
10903
|
}
|
|
10569
10904
|
|
|
10570
10905
|
// src/hooks/use-is-night.ts
|
|
10571
|
-
import { useEffect, useState } from "react";
|
|
10906
|
+
import { useEffect as useEffect3, useState as useState2 } from "react";
|
|
10572
10907
|
var NIGHT_START_HOUR = 18;
|
|
10573
10908
|
var NIGHT_END_HOUR = 6;
|
|
10574
10909
|
function useIsNight() {
|
|
10575
|
-
const [isNight, setIsNight] =
|
|
10576
|
-
|
|
10910
|
+
const [isNight, setIsNight] = useState2(false);
|
|
10911
|
+
useEffect3(() => {
|
|
10577
10912
|
const check = () => {
|
|
10578
10913
|
const hour = (/* @__PURE__ */ new Date()).getHours();
|
|
10579
10914
|
return hour >= NIGHT_START_HOUR || hour < NIGHT_END_HOUR;
|
|
@@ -10597,6 +10932,8 @@ export {
|
|
|
10597
10932
|
ERROR_RETRYABLE,
|
|
10598
10933
|
FREE_PLAN_SEAT_HELP,
|
|
10599
10934
|
ORGANISATION_SEAT_HELP,
|
|
10935
|
+
SUITE_APP_QUERY_CACHES,
|
|
10936
|
+
SUITE_QUERY_CACHE_MAX_AGE_MS,
|
|
10600
10937
|
TIDES_ACCESS_HELP,
|
|
10601
10938
|
TURTLETIME_ACCESS_HELP,
|
|
10602
10939
|
WEBHOOK_ACTIVITY_ACTIONS,
|
|
@@ -10624,9 +10961,12 @@ export {
|
|
|
10624
10961
|
formatAppError,
|
|
10625
10962
|
hasAppAccess,
|
|
10626
10963
|
isErrorCode,
|
|
10964
|
+
nextRevisionTick,
|
|
10627
10965
|
normalizeError,
|
|
10628
10966
|
normalizeThrown,
|
|
10967
|
+
persistSuiteQueries,
|
|
10629
10968
|
planSeatSummary,
|
|
10969
|
+
readJsonSnapshot,
|
|
10630
10970
|
resolveSubscriptionTier,
|
|
10631
10971
|
seatTypeDescription,
|
|
10632
10972
|
seatTypeFromFlags,
|
|
@@ -10634,10 +10974,14 @@ export {
|
|
|
10634
10974
|
selectedToWebhookActions,
|
|
10635
10975
|
sortedSuiteAiEvents,
|
|
10636
10976
|
suiteAiEventSummary,
|
|
10977
|
+
suiteCacheBuster,
|
|
10637
10978
|
tierAllowsTidesOnlySeats,
|
|
10638
10979
|
tierAllowsTurtletime,
|
|
10639
10980
|
tierLabel,
|
|
10981
|
+
useEmailNotificationPreference,
|
|
10640
10982
|
useIsNight,
|
|
10641
|
-
|
|
10983
|
+
useSuiteRevisions,
|
|
10984
|
+
webhookActionsToSelected,
|
|
10985
|
+
writeJsonSnapshot
|
|
10642
10986
|
};
|
|
10643
10987
|
//# sourceMappingURL=index.mjs.map
|