@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.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
|
-
|
|
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
|
-
|
|
9494
|
-
|
|
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
|
|
9938
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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,
|
|
10649
|
-
(0,
|
|
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
|
-
|
|
11066
|
+
useSuiteRevisions,
|
|
11067
|
+
webhookActionsToSelected,
|
|
11068
|
+
writeJsonSnapshot
|
|
10716
11069
|
});
|
|
10717
11070
|
//# sourceMappingURL=index.js.map
|