@axcode/axcloud-sdk 0.1.7-dev.87ea138 → 0.1.7-dev.cc5cc69
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/README.md +265 -144
- package/dist/index.d.mts +291 -92
- package/dist/index.d.ts +291 -92
- package/dist/index.js +808 -171
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +799 -174
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -1
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createContext, useContext,
|
|
1
|
+
import { createContext, useContext, useState, useRef, useCallback, useEffect, useMemo, useLayoutEffect } from 'react';
|
|
2
2
|
import { Auth0Provider, useAuth0 } from '@auth0/auth0-react';
|
|
3
|
-
import {
|
|
3
|
+
import { QueryClientProvider, useQuery, useQueryClient, useMutation, QueryClient } from '@tanstack/react-query';
|
|
4
4
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
5
5
|
|
|
6
6
|
// src/core/errors.ts
|
|
@@ -18,7 +18,7 @@ var AxCloudError = class _AxCloudError extends Error {
|
|
|
18
18
|
}
|
|
19
19
|
static fromResponse(status, body) {
|
|
20
20
|
const details = typeof body === "object" ? body : void 0;
|
|
21
|
-
const message = details?.message;
|
|
21
|
+
const message = (typeof details?.message === "string" ? details.message : void 0) || (typeof details?.error === "string" ? details.error : void 0);
|
|
22
22
|
switch (status) {
|
|
23
23
|
case 401:
|
|
24
24
|
return new _AxCloudError(
|
|
@@ -28,19 +28,9 @@ var AxCloudError = class _AxCloudError extends Error {
|
|
|
28
28
|
details
|
|
29
29
|
);
|
|
30
30
|
case 403:
|
|
31
|
-
return new _AxCloudError(
|
|
32
|
-
message || "Access denied",
|
|
33
|
-
"FORBIDDEN",
|
|
34
|
-
status,
|
|
35
|
-
details
|
|
36
|
-
);
|
|
31
|
+
return new _AxCloudError(message || "Access denied", "FORBIDDEN", status, details);
|
|
37
32
|
case 404:
|
|
38
|
-
return new _AxCloudError(
|
|
39
|
-
message || "Resource not found",
|
|
40
|
-
"NOT_FOUND",
|
|
41
|
-
status,
|
|
42
|
-
details
|
|
43
|
-
);
|
|
33
|
+
return new _AxCloudError(message || "Resource not found", "NOT_FOUND", status, details);
|
|
44
34
|
case 422:
|
|
45
35
|
return new _AxCloudError(
|
|
46
36
|
message || "Validation failed",
|
|
@@ -50,19 +40,9 @@ var AxCloudError = class _AxCloudError extends Error {
|
|
|
50
40
|
);
|
|
51
41
|
default:
|
|
52
42
|
if (status >= 500) {
|
|
53
|
-
return new _AxCloudError(
|
|
54
|
-
message || "Server error",
|
|
55
|
-
"SERVER_ERROR",
|
|
56
|
-
status,
|
|
57
|
-
details
|
|
58
|
-
);
|
|
43
|
+
return new _AxCloudError(message || "Server error", "SERVER_ERROR", status, details);
|
|
59
44
|
}
|
|
60
|
-
return new _AxCloudError(
|
|
61
|
-
message || "An error occurred",
|
|
62
|
-
"UNKNOWN_ERROR",
|
|
63
|
-
status,
|
|
64
|
-
details
|
|
65
|
-
);
|
|
45
|
+
return new _AxCloudError(message || "An error occurred", "UNKNOWN_ERROR", status, details);
|
|
66
46
|
}
|
|
67
47
|
}
|
|
68
48
|
static networkError(originalError) {
|
|
@@ -76,12 +56,9 @@ var AxCloudError = class _AxCloudError extends Error {
|
|
|
76
56
|
);
|
|
77
57
|
}
|
|
78
58
|
static timeoutError(timeoutMs) {
|
|
79
|
-
return new _AxCloudError(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
0,
|
|
83
|
-
{ timeout: timeoutMs }
|
|
84
|
-
);
|
|
59
|
+
return new _AxCloudError(`Request timed out after ${timeoutMs}ms`, "TIMEOUT_ERROR", 0, {
|
|
60
|
+
timeout: timeoutMs
|
|
61
|
+
});
|
|
85
62
|
}
|
|
86
63
|
};
|
|
87
64
|
function isAxCloudError(error) {
|
|
@@ -236,6 +213,130 @@ function getClient(key) {
|
|
|
236
213
|
throw new Error("AxCloud client not found. Make sure AxCloudProvider is mounted and use useAxCloudContext() to get the client.");
|
|
237
214
|
}
|
|
238
215
|
|
|
216
|
+
// src/core/analytics.ts
|
|
217
|
+
var EVENT_NAME_PATTERN = /^[a-z][a-z0-9_]{1,63}$/;
|
|
218
|
+
var PROPERTY_KEY_PATTERN = /^[a-z][a-z0-9_]{0,47}$/;
|
|
219
|
+
var SENSITIVE_PROPERTY_PATTERN = /(^|_)(auth0|email|name|password|query|search|secret|token|trace|user|userid)($|_)/;
|
|
220
|
+
var EMAIL_VALUE_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
221
|
+
var OPAQUE_SEGMENT_PATTERNS = [
|
|
222
|
+
/^[0-9a-f]{8}-[0-9a-f-]{27,}$/i,
|
|
223
|
+
/^(org|user|section|document|content|poll|invitation|invite|trace|chan)[_-][a-z0-9_-]+$/i,
|
|
224
|
+
/^[a-f0-9]{20,}$/i,
|
|
225
|
+
/^[a-z0-9_-]{32,}$/i
|
|
226
|
+
];
|
|
227
|
+
var scriptLoads = /* @__PURE__ */ new Map();
|
|
228
|
+
function isLocalAnalyticsHostname(hostname) {
|
|
229
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
|
|
230
|
+
}
|
|
231
|
+
function sanitizeAnalyticsPath(pathname, customNormalizer) {
|
|
232
|
+
const rawPath = pathname.split(/[?#]/, 1)[0] || "/";
|
|
233
|
+
const normalizedInput = rawPath.startsWith("/") ? rawPath : `/${rawPath}`;
|
|
234
|
+
const custom = customNormalizer?.(normalizedInput);
|
|
235
|
+
if (custom === null) return null;
|
|
236
|
+
const path = custom ?? normalizedInput;
|
|
237
|
+
const cleanPath = path.split(/[?#]/, 1)[0] || "/";
|
|
238
|
+
const segments = cleanPath.split("/").map((segment) => {
|
|
239
|
+
if (!segment) return segment;
|
|
240
|
+
let decoded = segment;
|
|
241
|
+
try {
|
|
242
|
+
decoded = decodeURIComponent(segment);
|
|
243
|
+
} catch {
|
|
244
|
+
}
|
|
245
|
+
return OPAQUE_SEGMENT_PATTERNS.some((pattern) => pattern.test(decoded)) ? ":id" : segment;
|
|
246
|
+
});
|
|
247
|
+
return segments.join("/") || "/";
|
|
248
|
+
}
|
|
249
|
+
function sanitizeAnalyticsEventData(data) {
|
|
250
|
+
if (!data) return void 0;
|
|
251
|
+
const sanitized = {};
|
|
252
|
+
for (const [rawKey, rawValue] of Object.entries(data).slice(0, 20)) {
|
|
253
|
+
const key = rawKey.trim().toLowerCase();
|
|
254
|
+
if (!PROPERTY_KEY_PATTERN.test(key) || SENSITIVE_PROPERTY_PATTERN.test(key)) continue;
|
|
255
|
+
if (typeof rawValue === "string") {
|
|
256
|
+
const value = rawValue.trim();
|
|
257
|
+
if (!value || value.length > 100 || EMAIL_VALUE_PATTERN.test(value)) continue;
|
|
258
|
+
sanitized[key] = value;
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
if (typeof rawValue === "number" && Number.isFinite(rawValue)) {
|
|
262
|
+
sanitized[key] = rawValue;
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
if (typeof rawValue === "boolean") sanitized[key] = rawValue;
|
|
266
|
+
}
|
|
267
|
+
return Object.keys(sanitized).length > 0 ? sanitized : void 0;
|
|
268
|
+
}
|
|
269
|
+
function isAllowedAnalyticsEvent(eventName, allowedEvents) {
|
|
270
|
+
return EVENT_NAME_PATTERN.test(eventName) && Boolean(allowedEvents?.includes(eventName));
|
|
271
|
+
}
|
|
272
|
+
async function fetchAnalyticsPublicConfig(input) {
|
|
273
|
+
const baseUrl = input.apiUrl.replace(/\/$/, "");
|
|
274
|
+
const url = new URL(`${baseUrl}/analytics/public/config`);
|
|
275
|
+
url.searchParams.set("orgId", input.orgId);
|
|
276
|
+
url.searchParams.set("siteKey", input.siteKey);
|
|
277
|
+
url.searchParams.set("stage", input.stage);
|
|
278
|
+
url.searchParams.set("hostname", input.hostname);
|
|
279
|
+
const response = await fetch(url, {
|
|
280
|
+
method: "GET",
|
|
281
|
+
headers: { Accept: "application/json" },
|
|
282
|
+
signal: input.signal
|
|
283
|
+
});
|
|
284
|
+
if (!response.ok) throw new Error(`Analytics configuration returned ${response.status}`);
|
|
285
|
+
return response.json();
|
|
286
|
+
}
|
|
287
|
+
function loadAnalyticsScript(config, doc = document) {
|
|
288
|
+
if (!config.enabled || !config.websiteId || !config.scriptUrl) {
|
|
289
|
+
return Promise.reject(new Error("Analytics tracker configuration is incomplete"));
|
|
290
|
+
}
|
|
291
|
+
const key = `${config.scriptUrl}|${config.websiteId}`;
|
|
292
|
+
const existingPromise = scriptLoads.get(key);
|
|
293
|
+
if (existingPromise) return existingPromise;
|
|
294
|
+
const existing = doc.querySelector(
|
|
295
|
+
`script[data-axcloud-analytics-site="${config.websiteId}"]`
|
|
296
|
+
);
|
|
297
|
+
if (existing && doc.defaultView?.umami) {
|
|
298
|
+
return Promise.resolve();
|
|
299
|
+
}
|
|
300
|
+
const promise = new Promise((resolve, reject) => {
|
|
301
|
+
const script = existing || doc.createElement("script");
|
|
302
|
+
const onLoad = () => resolve();
|
|
303
|
+
const onError = () => reject(new Error("Analytics tracker failed to load"));
|
|
304
|
+
script.addEventListener("load", onLoad, { once: true });
|
|
305
|
+
script.addEventListener("error", onError, { once: true });
|
|
306
|
+
if (!existing) {
|
|
307
|
+
script.src = config.scriptUrl;
|
|
308
|
+
script.defer = true;
|
|
309
|
+
script.dataset.websiteId = config.websiteId;
|
|
310
|
+
script.dataset.axcloudAnalyticsSite = config.websiteId;
|
|
311
|
+
script.dataset.autoTrack = String(config.autoTrack === true);
|
|
312
|
+
if (config.hostUrl) script.dataset.hostUrl = config.hostUrl;
|
|
313
|
+
if (config.domains?.length) script.dataset.domains = config.domains.join(",");
|
|
314
|
+
if (config.respectDoNotTrack !== false) script.dataset.doNotTrack = "true";
|
|
315
|
+
if (config.excludeSearch !== false) script.dataset.excludeSearch = "true";
|
|
316
|
+
if (config.excludeHash !== false) script.dataset.excludeHash = "true";
|
|
317
|
+
doc.head.appendChild(script);
|
|
318
|
+
}
|
|
319
|
+
}).catch((error) => {
|
|
320
|
+
scriptLoads.delete(key);
|
|
321
|
+
throw error;
|
|
322
|
+
});
|
|
323
|
+
scriptLoads.set(key, promise);
|
|
324
|
+
return promise;
|
|
325
|
+
}
|
|
326
|
+
function sendAnalyticsPageView(pathname, options, targetWindow = window) {
|
|
327
|
+
const path = sanitizeAnalyticsPath(pathname, options.normalizePath);
|
|
328
|
+
const umami = targetWindow.umami;
|
|
329
|
+
if (!path || !umami) return false;
|
|
330
|
+
umami.track({ url: path, title: targetWindow.document.title });
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
function sendAnalyticsEvent(eventName, data, allowedEvents, targetWindow = window) {
|
|
334
|
+
const umami = targetWindow.umami;
|
|
335
|
+
if (!umami || !isAllowedAnalyticsEvent(eventName, allowedEvents)) return false;
|
|
336
|
+
umami.track(eventName, sanitizeAnalyticsEventData(data));
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
|
|
239
340
|
// src/core/contentSlots.ts
|
|
240
341
|
var SLOT_ATTRIBUTE_KEYS = ["slot", "field", "key", "name"];
|
|
241
342
|
function getContentSlotMap(content) {
|
|
@@ -243,7 +344,7 @@ function getContentSlotMap(content) {
|
|
|
243
344
|
for (const block of content?.blocks || []) {
|
|
244
345
|
const slot = getBlockSlot(block);
|
|
245
346
|
if (!slot) continue;
|
|
246
|
-
fields[slot] = blockToSlotValue(block);
|
|
347
|
+
if (!(slot in fields)) fields[slot] = blockToSlotValue(block);
|
|
247
348
|
}
|
|
248
349
|
return fields;
|
|
249
350
|
}
|
|
@@ -252,11 +353,13 @@ function createContentSlotReader(content) {
|
|
|
252
353
|
const value = (key, fallback) => {
|
|
253
354
|
return key in fields ? fields[key] : fallback;
|
|
254
355
|
};
|
|
356
|
+
const unsafeHtml = (key, fallback = "") => stringifySlotValue(value(key, fallback));
|
|
255
357
|
return {
|
|
256
358
|
fields,
|
|
257
359
|
value,
|
|
258
360
|
text: (key, fallback = "") => stringifySlotValue(value(key, fallback)),
|
|
259
|
-
html:
|
|
361
|
+
html: unsafeHtml,
|
|
362
|
+
unsafeHtml,
|
|
260
363
|
list: (key, fallback = []) => slotValueToList(value(key, fallback)),
|
|
261
364
|
image: (key, fallback) => slotValueToImage(value(key, fallback))
|
|
262
365
|
};
|
|
@@ -531,24 +634,195 @@ function useAxCloudContext() {
|
|
|
531
634
|
}
|
|
532
635
|
return context;
|
|
533
636
|
}
|
|
534
|
-
var
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
637
|
+
var NAVIGATION_EVENT = "axcloud-sdk:navigation";
|
|
638
|
+
var historySubscriberCount = 0;
|
|
639
|
+
var originalPushState = null;
|
|
640
|
+
var originalReplaceState = null;
|
|
641
|
+
function subscribeToNavigation(callback) {
|
|
642
|
+
if (typeof window === "undefined") return () => void 0;
|
|
643
|
+
if (historySubscriberCount === 0) {
|
|
644
|
+
originalPushState = window.history.pushState.bind(window.history);
|
|
645
|
+
originalReplaceState = window.history.replaceState.bind(window.history);
|
|
646
|
+
window.history.pushState = ((...args) => {
|
|
647
|
+
originalPushState?.(...args);
|
|
648
|
+
window.dispatchEvent(new Event(NAVIGATION_EVENT));
|
|
649
|
+
});
|
|
650
|
+
window.history.replaceState = ((...args) => {
|
|
651
|
+
originalReplaceState?.(...args);
|
|
652
|
+
window.dispatchEvent(new Event(NAVIGATION_EVENT));
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
historySubscriberCount += 1;
|
|
656
|
+
window.addEventListener("popstate", callback);
|
|
657
|
+
window.addEventListener(NAVIGATION_EVENT, callback);
|
|
658
|
+
return () => {
|
|
659
|
+
window.removeEventListener("popstate", callback);
|
|
660
|
+
window.removeEventListener(NAVIGATION_EVENT, callback);
|
|
661
|
+
historySubscriberCount = Math.max(0, historySubscriberCount - 1);
|
|
662
|
+
if (historySubscriberCount === 0) {
|
|
663
|
+
if (originalPushState) window.history.pushState = originalPushState;
|
|
664
|
+
if (originalReplaceState) window.history.replaceState = originalReplaceState;
|
|
665
|
+
originalPushState = null;
|
|
666
|
+
originalReplaceState = null;
|
|
543
667
|
}
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
function doNotTrackEnabled(config) {
|
|
671
|
+
if (config.respectDoNotTrack === false || typeof navigator === "undefined") return false;
|
|
672
|
+
return navigator.doNotTrack === "1";
|
|
673
|
+
}
|
|
674
|
+
function useAnalyticsRuntime(config) {
|
|
675
|
+
const [enabled, setEnabled] = useState(false);
|
|
676
|
+
const [ready, setReady] = useState(false);
|
|
677
|
+
const publicConfigRef = useRef(null);
|
|
678
|
+
const optionsRef = useRef(config.analytics);
|
|
679
|
+
optionsRef.current = config.analytics;
|
|
680
|
+
const trackPageView = useCallback(
|
|
681
|
+
(pathname) => {
|
|
682
|
+
if (!ready || !optionsRef.current || typeof window === "undefined") return false;
|
|
683
|
+
return sendAnalyticsPageView(pathname || window.location.pathname, optionsRef.current);
|
|
684
|
+
},
|
|
685
|
+
[ready]
|
|
686
|
+
);
|
|
687
|
+
const track = useCallback(
|
|
688
|
+
(eventName, data) => {
|
|
689
|
+
if (!ready || typeof window === "undefined") return false;
|
|
690
|
+
return sendAnalyticsEvent(eventName, data, publicConfigRef.current?.allowedEvents);
|
|
691
|
+
},
|
|
692
|
+
[ready]
|
|
693
|
+
);
|
|
694
|
+
useEffect(() => {
|
|
695
|
+
const options = config.analytics;
|
|
696
|
+
setEnabled(false);
|
|
697
|
+
setReady(false);
|
|
698
|
+
publicConfigRef.current = null;
|
|
699
|
+
if (!options?.enabled || !options.siteKey || typeof window === "undefined") return void 0;
|
|
700
|
+
if (isLocalAnalyticsHostname(window.location.hostname) && !options.trackLocalhost)
|
|
701
|
+
return void 0;
|
|
702
|
+
const controller = new AbortController();
|
|
703
|
+
let cancelled = false;
|
|
704
|
+
let unsubscribe = () => void 0;
|
|
705
|
+
const initialize = async () => {
|
|
706
|
+
try {
|
|
707
|
+
const publicConfig = await fetchAnalyticsPublicConfig({
|
|
708
|
+
apiUrl: config.apiUrl,
|
|
709
|
+
orgId: config.orgId,
|
|
710
|
+
siteKey: options.siteKey,
|
|
711
|
+
stage: config.stage,
|
|
712
|
+
hostname: window.location.hostname,
|
|
713
|
+
signal: controller.signal
|
|
714
|
+
});
|
|
715
|
+
if (cancelled || !publicConfig.enabled || doNotTrackEnabled(publicConfig)) return;
|
|
716
|
+
publicConfigRef.current = publicConfig;
|
|
717
|
+
setEnabled(true);
|
|
718
|
+
await loadAnalyticsScript(publicConfig);
|
|
719
|
+
if (cancelled) return;
|
|
720
|
+
setReady(true);
|
|
721
|
+
if (!publicConfig.autoTrack) {
|
|
722
|
+
const trackCurrentPage = () => {
|
|
723
|
+
sendAnalyticsPageView(window.location.pathname, options);
|
|
724
|
+
};
|
|
725
|
+
trackCurrentPage();
|
|
726
|
+
unsubscribe = subscribeToNavigation(trackCurrentPage);
|
|
727
|
+
}
|
|
728
|
+
} catch (error) {
|
|
729
|
+
if (!cancelled && !(error instanceof Error && error.name === "AbortError")) {
|
|
730
|
+
setEnabled(false);
|
|
731
|
+
setReady(false);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
};
|
|
735
|
+
void initialize();
|
|
736
|
+
return () => {
|
|
737
|
+
cancelled = true;
|
|
738
|
+
controller.abort();
|
|
739
|
+
unsubscribe();
|
|
740
|
+
};
|
|
741
|
+
}, [
|
|
742
|
+
config.analytics?.enabled,
|
|
743
|
+
config.analytics?.siteKey,
|
|
744
|
+
config.analytics?.trackLocalhost,
|
|
745
|
+
config.analytics?.normalizePath,
|
|
746
|
+
config.apiUrl,
|
|
747
|
+
config.orgId,
|
|
748
|
+
config.stage
|
|
749
|
+
]);
|
|
750
|
+
return useMemo(
|
|
751
|
+
() => ({ enabled, ready, track, trackPageView }),
|
|
752
|
+
[enabled, ready, track, trackPageView]
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
function useAnalytics() {
|
|
756
|
+
return useAxCloudContext().analytics;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// src/react/auth/auth0Invitation.ts
|
|
760
|
+
var AUTH0_INVITATION_PATTERN = /^[A-Za-z0-9_-]{1,2048}$/;
|
|
761
|
+
function readAuth0InvitationAuthorizationParams(configuredOrganization, search = typeof window === "undefined" ? "" : window.location.search) {
|
|
762
|
+
if (!configuredOrganization || !search) return {};
|
|
763
|
+
const params = new URLSearchParams(search);
|
|
764
|
+
const invitation = params.get("invitation")?.trim();
|
|
765
|
+
const organization = params.get("organization")?.trim();
|
|
766
|
+
if (organization !== configuredOrganization || !invitation || !AUTH0_INVITATION_PATTERN.test(invitation)) {
|
|
767
|
+
return {};
|
|
544
768
|
}
|
|
545
|
-
}
|
|
769
|
+
return { invitation };
|
|
770
|
+
}
|
|
771
|
+
function createDefaultQueryClient() {
|
|
772
|
+
return new QueryClient({
|
|
773
|
+
defaultOptions: {
|
|
774
|
+
queries: {
|
|
775
|
+
staleTime: 1e3 * 60,
|
|
776
|
+
gcTime: 1e3 * 60 * 30,
|
|
777
|
+
retry: (failureCount, error) => {
|
|
778
|
+
if (isAxCloudError(error) && error.status >= 400 && error.status < 500) {
|
|
779
|
+
return false;
|
|
780
|
+
}
|
|
781
|
+
return failureCount < 2;
|
|
782
|
+
},
|
|
783
|
+
refetchOnWindowFocus: true
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
var queryClientAxCloudOwners = /* @__PURE__ */ new WeakMap();
|
|
789
|
+
function createQueryClientOwnerSignature(config, subject) {
|
|
790
|
+
return JSON.stringify([
|
|
791
|
+
subject === null ? "public" : "authenticated",
|
|
792
|
+
subject,
|
|
793
|
+
config.orgId,
|
|
794
|
+
config.stage,
|
|
795
|
+
config.apiUrl,
|
|
796
|
+
config.apiKey ?? null,
|
|
797
|
+
config.auth0.domain,
|
|
798
|
+
config.auth0.clientId,
|
|
799
|
+
config.auth0.audience ?? null,
|
|
800
|
+
config.auth0.organization ?? null,
|
|
801
|
+
config.auth0.connection ?? null
|
|
802
|
+
]);
|
|
803
|
+
}
|
|
546
804
|
function AxCloudInnerProvider({
|
|
547
805
|
children,
|
|
548
806
|
config,
|
|
807
|
+
queryClient,
|
|
549
808
|
timeout
|
|
550
809
|
}) {
|
|
551
|
-
const { getAccessTokenSilently, isAuthenticated } = useAuth0();
|
|
810
|
+
const { getAccessTokenSilently, isAuthenticated, isLoading, user } = useAuth0();
|
|
811
|
+
const analytics = useAnalyticsRuntime(config);
|
|
812
|
+
const [, setOwnerGeneration] = useState(0);
|
|
813
|
+
const currentSubject = isLoading ? void 0 : isAuthenticated ? user?.sub : null;
|
|
814
|
+
const currentOwnerSignature = currentSubject === void 0 ? void 0 : createQueryClientOwnerSignature(config, currentSubject);
|
|
815
|
+
const hasKnownOwner = queryClientAxCloudOwners.has(queryClient);
|
|
816
|
+
const ownerNeedsActivation = currentOwnerSignature !== void 0 && (!hasKnownOwner || queryClientAxCloudOwners.get(queryClient) !== currentOwnerSignature);
|
|
817
|
+
const clearContentCache = useCallback(() => {
|
|
818
|
+
queryClient.removeQueries({ queryKey: ["axcloud"] });
|
|
819
|
+
}, [queryClient]);
|
|
820
|
+
useLayoutEffect(() => {
|
|
821
|
+
if (currentOwnerSignature === void 0 || !ownerNeedsActivation) return;
|
|
822
|
+
clearContentCache();
|
|
823
|
+
queryClientAxCloudOwners.set(queryClient, currentOwnerSignature);
|
|
824
|
+
setOwnerGeneration((generation) => generation + 1);
|
|
825
|
+
}, [clearContentCache, currentOwnerSignature, ownerNeedsActivation, queryClient]);
|
|
552
826
|
const tokenGetterRef = useRef(async () => void 0);
|
|
553
827
|
tokenGetterRef.current = isAuthenticated ? async () => {
|
|
554
828
|
try {
|
|
@@ -578,11 +852,17 @@ function AxCloudInnerProvider({
|
|
|
578
852
|
client,
|
|
579
853
|
orgId: config.orgId,
|
|
580
854
|
stage: config.stage,
|
|
581
|
-
isPublicMode:
|
|
855
|
+
isPublicMode: !isAuthenticated,
|
|
856
|
+
isAuthLoading: isLoading,
|
|
857
|
+
isAuthenticated,
|
|
858
|
+
authSubject: isAuthenticated ? user?.sub : void 0,
|
|
859
|
+
clearContentCache,
|
|
860
|
+
analytics
|
|
582
861
|
}),
|
|
583
|
-
[config, client, isAuthenticated]
|
|
862
|
+
[config, client, isAuthenticated, isLoading, user?.sub, clearContentCache, analytics]
|
|
584
863
|
);
|
|
585
|
-
|
|
864
|
+
const identityReady = currentOwnerSignature !== void 0 && !ownerNeedsActivation;
|
|
865
|
+
return /* @__PURE__ */ jsx(AxCloudContext.Provider, { value: contextValue, children: identityReady ? children : null });
|
|
586
866
|
}
|
|
587
867
|
function AxCloudProvider({
|
|
588
868
|
children,
|
|
@@ -590,19 +870,24 @@ function AxCloudProvider({
|
|
|
590
870
|
apiUrl,
|
|
591
871
|
stage = "dev",
|
|
592
872
|
apiKey,
|
|
873
|
+
analytics,
|
|
593
874
|
auth0,
|
|
594
|
-
queryClient
|
|
875
|
+
queryClient: providedQueryClient,
|
|
595
876
|
timeout
|
|
596
877
|
}) {
|
|
878
|
+
const [ownedQueryClient] = useState(createDefaultQueryClient);
|
|
879
|
+
const queryClient = providedQueryClient || ownedQueryClient;
|
|
880
|
+
const invitationAuthorizationParams = readAuth0InvitationAuthorizationParams(auth0.organization);
|
|
597
881
|
const config = useMemo(
|
|
598
882
|
() => ({
|
|
599
883
|
orgId,
|
|
600
884
|
apiUrl,
|
|
601
885
|
stage,
|
|
602
886
|
apiKey,
|
|
887
|
+
analytics,
|
|
603
888
|
auth0
|
|
604
889
|
}),
|
|
605
|
-
[orgId, apiUrl, stage, apiKey, auth0]
|
|
890
|
+
[orgId, apiUrl, stage, apiKey, analytics, auth0]
|
|
606
891
|
);
|
|
607
892
|
return /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx(
|
|
608
893
|
Auth0Provider,
|
|
@@ -611,14 +896,16 @@ function AxCloudProvider({
|
|
|
611
896
|
clientId: auth0.clientId,
|
|
612
897
|
authorizationParams: {
|
|
613
898
|
redirect_uri: auth0.redirectUri,
|
|
614
|
-
audience: auth0.audience
|
|
899
|
+
audience: auth0.audience,
|
|
900
|
+
...auth0.organization ? { organization: auth0.organization } : {},
|
|
901
|
+
...invitationAuthorizationParams
|
|
615
902
|
},
|
|
616
|
-
children: /* @__PURE__ */ jsx(AxCloudInnerProvider, { config, timeout, children })
|
|
903
|
+
children: /* @__PURE__ */ jsx(AxCloudInnerProvider, { config, queryClient, timeout, children })
|
|
617
904
|
}
|
|
618
905
|
) });
|
|
619
906
|
}
|
|
620
907
|
function useAuth() {
|
|
621
|
-
const { config } = useAxCloudContext();
|
|
908
|
+
const { config, client, clearContentCache } = useAxCloudContext();
|
|
622
909
|
const {
|
|
623
910
|
isAuthenticated,
|
|
624
911
|
isLoading,
|
|
@@ -631,23 +918,33 @@ function useAuth() {
|
|
|
631
918
|
} = useAuth0();
|
|
632
919
|
const login = useCallback(
|
|
633
920
|
(options) => {
|
|
921
|
+
const invitationAuthorizationParams = readAuth0InvitationAuthorizationParams(
|
|
922
|
+
config.auth0.organization
|
|
923
|
+
);
|
|
634
924
|
return loginWithRedirect({
|
|
635
925
|
appState: {
|
|
636
926
|
returnTo: options?.returnTo || window.location.pathname
|
|
637
|
-
}
|
|
927
|
+
},
|
|
928
|
+
...config.auth0.organization ? {
|
|
929
|
+
authorizationParams: {
|
|
930
|
+
organization: config.auth0.organization,
|
|
931
|
+
...invitationAuthorizationParams
|
|
932
|
+
}
|
|
933
|
+
} : {}
|
|
638
934
|
});
|
|
639
935
|
},
|
|
640
|
-
[loginWithRedirect]
|
|
936
|
+
[config.auth0.organization, loginWithRedirect]
|
|
641
937
|
);
|
|
642
938
|
const logout = useCallback(
|
|
643
939
|
(options) => {
|
|
940
|
+
clearContentCache?.();
|
|
644
941
|
return auth0Logout({
|
|
645
942
|
logoutParams: {
|
|
646
943
|
returnTo: options?.returnTo || window.location.origin
|
|
647
944
|
}
|
|
648
945
|
});
|
|
649
946
|
},
|
|
650
|
-
[auth0Logout]
|
|
947
|
+
[auth0Logout, clearContentCache]
|
|
651
948
|
);
|
|
652
949
|
const getAccessToken = useCallback(async () => {
|
|
653
950
|
try {
|
|
@@ -656,6 +953,60 @@ function useAuth() {
|
|
|
656
953
|
return void 0;
|
|
657
954
|
}
|
|
658
955
|
}, [getAccessTokenSilently]);
|
|
956
|
+
const refreshUser = useCallback(async () => {
|
|
957
|
+
await getAccessTokenSilently({ cacheMode: "off" });
|
|
958
|
+
const claims = await getIdTokenClaims();
|
|
959
|
+
return claims?.email_verified === true;
|
|
960
|
+
}, [getAccessTokenSilently, getIdTokenClaims]);
|
|
961
|
+
const requestEmailVerification = useCallback(
|
|
962
|
+
async (options) => {
|
|
963
|
+
if (!isAuthenticated || !auth0User) {
|
|
964
|
+
return {
|
|
965
|
+
success: false,
|
|
966
|
+
status: "failed",
|
|
967
|
+
message: "You must be signed in to verify your email."
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
if (auth0User.email_verified === true) {
|
|
971
|
+
return {
|
|
972
|
+
success: true,
|
|
973
|
+
status: "already_verified",
|
|
974
|
+
message: "Your email address is already verified."
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
try {
|
|
978
|
+
const response = await client.post("/email/identity/verification-email", {
|
|
979
|
+
return_to: options?.returnTo || window.location.href
|
|
980
|
+
});
|
|
981
|
+
const data = response.data || {};
|
|
982
|
+
return {
|
|
983
|
+
success: true,
|
|
984
|
+
status: data.status || "sent",
|
|
985
|
+
message: data.message || "A verification email has been sent.",
|
|
986
|
+
...data.retry_after_seconds !== void 0 ? { retryAfterSeconds: data.retry_after_seconds } : {}
|
|
987
|
+
};
|
|
988
|
+
} catch (requestError) {
|
|
989
|
+
if (isAxCloudError(requestError) && requestError.status === 429) {
|
|
990
|
+
const responseDetails = requestError.details?.details;
|
|
991
|
+
const retryAfterSeconds = Number(
|
|
992
|
+
responseDetails && typeof responseDetails === "object" ? responseDetails.retry_after_seconds : void 0
|
|
993
|
+
);
|
|
994
|
+
return {
|
|
995
|
+
success: false,
|
|
996
|
+
status: "rate_limited",
|
|
997
|
+
message: "Please wait before requesting another verification email.",
|
|
998
|
+
...Number.isFinite(retryAfterSeconds) ? { retryAfterSeconds } : {}
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
return {
|
|
1002
|
+
success: false,
|
|
1003
|
+
status: "failed",
|
|
1004
|
+
message: "Failed to send the verification email. Please try again."
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
},
|
|
1008
|
+
[auth0User, client, isAuthenticated]
|
|
1009
|
+
);
|
|
659
1010
|
const getAuth0Client = useCallback(() => {
|
|
660
1011
|
return {
|
|
661
1012
|
loginWithRedirect,
|
|
@@ -729,35 +1080,182 @@ function useAuth() {
|
|
|
729
1080
|
id: auth0User.sub || "",
|
|
730
1081
|
email: auth0User.email || "",
|
|
731
1082
|
name: auth0User.name || "",
|
|
732
|
-
avatar: auth0User.picture
|
|
1083
|
+
avatar: auth0User.picture,
|
|
1084
|
+
...typeof auth0User.email_verified === "boolean" ? { emailVerified: auth0User.email_verified } : {}
|
|
733
1085
|
} : null,
|
|
1086
|
+
needsEmailVerification: isAuthenticated && auth0User?.email_verified === false,
|
|
734
1087
|
login,
|
|
735
1088
|
logout,
|
|
736
1089
|
// Escape hatch for advanced use cases
|
|
737
1090
|
getAccessToken,
|
|
738
1091
|
getAuth0Client,
|
|
1092
|
+
refreshUser,
|
|
1093
|
+
requestEmailVerification,
|
|
739
1094
|
// Password management
|
|
740
1095
|
requestPasswordReset
|
|
741
1096
|
};
|
|
742
1097
|
}
|
|
1098
|
+
var currentUserQueryKey = (stage, orgId, subject) => ["axcloud", "user", "me", stage, orgId, subject];
|
|
1099
|
+
function asRecord(value) {
|
|
1100
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1101
|
+
}
|
|
1102
|
+
function asString(value) {
|
|
1103
|
+
return typeof value === "string" && value.trim().length > 0 ? value : null;
|
|
1104
|
+
}
|
|
1105
|
+
function asStringArray(value) {
|
|
1106
|
+
if (!Array.isArray(value)) return [];
|
|
1107
|
+
return Array.from(new Set(value.map(asString).filter((item) => item !== null)));
|
|
1108
|
+
}
|
|
1109
|
+
function normalizeMembershipSections(rawMembership) {
|
|
1110
|
+
const sections = /* @__PURE__ */ new Map();
|
|
1111
|
+
const addSection = (idValue, nameValue, roleValue) => {
|
|
1112
|
+
const id = asString(idValue);
|
|
1113
|
+
if (!id) return;
|
|
1114
|
+
const existing = sections.get(id);
|
|
1115
|
+
sections.set(id, {
|
|
1116
|
+
id,
|
|
1117
|
+
name: asString(nameValue) || existing?.name || id,
|
|
1118
|
+
role: asString(roleValue) || existing?.role || "member"
|
|
1119
|
+
});
|
|
1120
|
+
};
|
|
1121
|
+
const directSections = rawMembership.sections;
|
|
1122
|
+
if (Array.isArray(directSections)) {
|
|
1123
|
+
for (const rawSection of directSections) {
|
|
1124
|
+
if (typeof rawSection === "string") {
|
|
1125
|
+
addSection(rawSection);
|
|
1126
|
+
continue;
|
|
1127
|
+
}
|
|
1128
|
+
const section = asRecord(rawSection);
|
|
1129
|
+
addSection(
|
|
1130
|
+
section.id ?? section.sectionId ?? section.section_id,
|
|
1131
|
+
section.name ?? section.sectionName ?? section.section_name,
|
|
1132
|
+
section.role
|
|
1133
|
+
);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
const sectionMemberships = asRecord(
|
|
1137
|
+
rawMembership.sectionMemberships ?? rawMembership.section_memberships
|
|
1138
|
+
);
|
|
1139
|
+
for (const [sectionId, rawSectionMembership] of Object.entries(sectionMemberships)) {
|
|
1140
|
+
const sectionMembership = asRecord(rawSectionMembership);
|
|
1141
|
+
addSection(
|
|
1142
|
+
sectionId,
|
|
1143
|
+
sectionMembership.name ?? sectionMembership.sectionName ?? sectionMembership.section_name,
|
|
1144
|
+
sectionMembership.role
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1147
|
+
for (const sectionId of asStringArray(rawMembership.sectionIds ?? rawMembership.section_ids)) {
|
|
1148
|
+
addSection(sectionId);
|
|
1149
|
+
}
|
|
1150
|
+
const primarySectionId = asString(rawMembership.sectionId ?? rawMembership.section_id);
|
|
1151
|
+
if (primarySectionId) addSection(primarySectionId);
|
|
1152
|
+
const normalized = Array.from(sections.values());
|
|
1153
|
+
if (!primarySectionId) return normalized;
|
|
1154
|
+
return normalized.sort((left, right) => {
|
|
1155
|
+
if (left.id === primarySectionId) return -1;
|
|
1156
|
+
if (right.id === primarySectionId) return 1;
|
|
1157
|
+
return 0;
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
function normalizeMembership(rawValue) {
|
|
1161
|
+
const raw = asRecord(rawValue);
|
|
1162
|
+
const role = asString(raw.role) || "member";
|
|
1163
|
+
const roles = asStringArray(raw.roles);
|
|
1164
|
+
const sections = normalizeMembershipSections(raw);
|
|
1165
|
+
const sectionId = asString(raw.sectionId ?? raw.section_id) || sections[0]?.id || null;
|
|
1166
|
+
const joinedAt = asString(raw.joinedAt ?? raw.joined_at);
|
|
1167
|
+
return {
|
|
1168
|
+
role,
|
|
1169
|
+
roles: roles.length > 0 ? roles : [role],
|
|
1170
|
+
title: asString(raw.title),
|
|
1171
|
+
status: asString(raw.status),
|
|
1172
|
+
joinedAt,
|
|
1173
|
+
memberSince: asString(raw.memberSince ?? raw.member_since) || joinedAt,
|
|
1174
|
+
sectionId,
|
|
1175
|
+
sectionIds: sections.map((section) => section.id),
|
|
1176
|
+
sections
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
function normalizeCurrentUserResponse(raw, configuredOrgId) {
|
|
1180
|
+
const rawMemberships = asRecord(
|
|
1181
|
+
raw.organizationMemberships ?? raw.organization_memberships
|
|
1182
|
+
);
|
|
1183
|
+
const organizationMemberships = Object.fromEntries(
|
|
1184
|
+
Object.entries(rawMemberships).map(([organizationId2, membership]) => [
|
|
1185
|
+
organizationId2,
|
|
1186
|
+
normalizeMembership(membership)
|
|
1187
|
+
])
|
|
1188
|
+
);
|
|
1189
|
+
const allOrgIds = Array.from(
|
|
1190
|
+
/* @__PURE__ */ new Set([
|
|
1191
|
+
...asStringArray(raw.allOrgIds ?? raw.all_org_ids),
|
|
1192
|
+
...Object.keys(organizationMemberships)
|
|
1193
|
+
])
|
|
1194
|
+
);
|
|
1195
|
+
const effectiveOrganizationId = asString(
|
|
1196
|
+
raw.effectiveOrganizationId ?? raw.effective_org_id
|
|
1197
|
+
);
|
|
1198
|
+
const primaryOrganizationId = asString(
|
|
1199
|
+
raw.organizationId ?? raw.primary_org_id ?? raw.org_id
|
|
1200
|
+
);
|
|
1201
|
+
const organizationId = effectiveOrganizationId || primaryOrganizationId || (organizationMemberships[configuredOrgId] ? configuredOrgId : null) || allOrgIds[0] || configuredOrgId;
|
|
1202
|
+
const configuredMembership = organizationMemberships[configuredOrgId];
|
|
1203
|
+
const firstName = asString(raw.firstName ?? raw.first_name ?? raw.given_name) || "";
|
|
1204
|
+
const lastName = asString(raw.lastName ?? raw.last_name ?? raw.family_name) || "";
|
|
1205
|
+
const email = asString(raw.email) || "";
|
|
1206
|
+
const createdAt = asString(raw.createdAt ?? raw.created_at) || "";
|
|
1207
|
+
const updatedAt = asString(raw.updatedAt ?? raw.updated_at) || createdAt;
|
|
1208
|
+
const role = configuredMembership?.role || asString(raw.role) || "member";
|
|
1209
|
+
const sectionId = configuredMembership?.sectionId || asString(raw.sectionId ?? raw.section_id);
|
|
1210
|
+
const permissions = asStringArray(raw.permissions ?? raw.effective_permissions);
|
|
1211
|
+
const metadata = asRecord(raw.metadata);
|
|
1212
|
+
return {
|
|
1213
|
+
id: asString(raw.id ?? raw.userId ?? raw.user_id ?? raw.sub) || "",
|
|
1214
|
+
email,
|
|
1215
|
+
name: asString(raw.name) || [firstName, lastName].filter(Boolean).join(" ") || email,
|
|
1216
|
+
firstName,
|
|
1217
|
+
lastName,
|
|
1218
|
+
title: configuredMembership?.title ?? asString(raw.title),
|
|
1219
|
+
phone: asString(raw.phone ?? raw.phoneNumber ?? raw.phone_number),
|
|
1220
|
+
address: asString(raw.address ?? raw.location),
|
|
1221
|
+
avatar: asString(raw.avatar ?? raw.avatarUrl ?? raw.avatar_url ?? raw.picture) || void 0,
|
|
1222
|
+
organizationId,
|
|
1223
|
+
...sectionId ? { sectionId } : {},
|
|
1224
|
+
role,
|
|
1225
|
+
permissions,
|
|
1226
|
+
...Object.keys(metadata).length > 0 ? { metadata } : {},
|
|
1227
|
+
createdAt,
|
|
1228
|
+
updatedAt,
|
|
1229
|
+
allOrgIds,
|
|
1230
|
+
organizationMemberships,
|
|
1231
|
+
effectiveOrganizationId,
|
|
1232
|
+
isSuperAdmin: raw.isSuperAdmin === true || raw.is_super_admin === true || asString(raw.global_role) === "super_admin" || role === "super_admin"
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
743
1235
|
function useUser() {
|
|
744
|
-
const { isAuthenticated, isLoading: isAuthLoading } = useAuth0();
|
|
1236
|
+
const { isAuthenticated, isLoading: isAuthLoading, user: auth0User } = useAuth0();
|
|
745
1237
|
const { client, stage, orgId } = useAxCloudContext();
|
|
1238
|
+
const queryKey = currentUserQueryKey(stage, orgId, auth0User?.sub);
|
|
746
1239
|
const query = useQuery({
|
|
747
|
-
queryKey
|
|
1240
|
+
queryKey,
|
|
748
1241
|
queryFn: async () => {
|
|
749
|
-
|
|
1242
|
+
const response = await client.get("/api/users/me");
|
|
1243
|
+
return normalizeCurrentUserResponse(response, orgId);
|
|
750
1244
|
},
|
|
751
1245
|
enabled: isAuthenticated && !isAuthLoading,
|
|
752
1246
|
staleTime: 1e3 * 60 * 5
|
|
753
1247
|
// 5 minutes
|
|
754
1248
|
});
|
|
755
1249
|
const userData = query.data;
|
|
756
|
-
const userOrgIds = userData?.
|
|
1250
|
+
const userOrgIds = userData?.allOrgIds ?? [];
|
|
757
1251
|
const isSuperAdmin = userData?.isSuperAdmin === true;
|
|
758
|
-
const
|
|
759
|
-
const
|
|
1252
|
+
const orgMembership = userData?.organizationMemberships?.[orgId];
|
|
1253
|
+
const hasOrgAccess = userOrgIds.includes(orgId) && orgMembership?.status?.trim().toLowerCase() === "active";
|
|
760
1254
|
const roleInOrg = orgMembership?.role ?? null;
|
|
1255
|
+
const sectionId = orgMembership?.sectionId ?? userData?.sectionId ?? null;
|
|
1256
|
+
const sections = orgMembership?.sections?.length ? orgMembership.sections : sectionId ? [{ id: sectionId, name: sectionId, role: roleInOrg ?? "member" }] : [];
|
|
1257
|
+
const section = sections.find((item) => item.id === sectionId) ?? sections[0] ?? null;
|
|
1258
|
+
const memberSince = orgMembership?.memberSince ?? null;
|
|
761
1259
|
return {
|
|
762
1260
|
user: query.data ?? null,
|
|
763
1261
|
isLoading: isAuthLoading || query.isLoading,
|
|
@@ -767,15 +1265,35 @@ function useUser() {
|
|
|
767
1265
|
// Organization access
|
|
768
1266
|
hasOrgAccess,
|
|
769
1267
|
roleInOrg,
|
|
1268
|
+
organizationMembership: orgMembership ?? null,
|
|
1269
|
+
title: orgMembership?.title ?? query.data?.title ?? null,
|
|
1270
|
+
section,
|
|
1271
|
+
sections,
|
|
1272
|
+
sectionIds: orgMembership?.sectionIds?.length ? orgMembership.sectionIds : sectionId ? [sectionId] : [],
|
|
1273
|
+
joinedAt: orgMembership?.joinedAt ?? null,
|
|
1274
|
+
memberSince,
|
|
770
1275
|
// Convenience properties
|
|
771
1276
|
isAuthenticated,
|
|
772
|
-
role: query.data?.role ?? null,
|
|
1277
|
+
role: roleInOrg ?? query.data?.role ?? null,
|
|
773
1278
|
organizationId: query.data?.organizationId ?? null,
|
|
774
|
-
sectionId
|
|
1279
|
+
sectionId,
|
|
775
1280
|
permissions: query.data?.permissions ?? [],
|
|
776
1281
|
isSuperAdmin
|
|
777
1282
|
};
|
|
778
1283
|
}
|
|
1284
|
+
function useUpdateMyProfile() {
|
|
1285
|
+
const { client, stage } = useAxCloudContext();
|
|
1286
|
+
const queryClient = useQueryClient();
|
|
1287
|
+
return useMutation({
|
|
1288
|
+
mutationFn: (updates) => client.patch("/api/users/me/profile", updates),
|
|
1289
|
+
onSuccess: async () => {
|
|
1290
|
+
await queryClient.invalidateQueries({
|
|
1291
|
+
queryKey: ["axcloud", "user", "me", stage],
|
|
1292
|
+
exact: false
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
779
1297
|
function useOrg() {
|
|
780
1298
|
const { client, orgId, stage, isPublicMode } = useAxCloudContext();
|
|
781
1299
|
const usePublicEndpoint = isPublicMode;
|
|
@@ -822,152 +1340,169 @@ function useOrg() {
|
|
|
822
1340
|
modules
|
|
823
1341
|
};
|
|
824
1342
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
"
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
refetch: query.refetch
|
|
881
|
-
};
|
|
1343
|
+
|
|
1344
|
+
// src/react/contentQueryPolicy.ts
|
|
1345
|
+
var CONTENT_STALE_TIME_MS = 6e4;
|
|
1346
|
+
function shouldRetryContentRequest(failureCount, error) {
|
|
1347
|
+
if (isAxCloudError(error) && error.status >= 400 && error.status < 500) {
|
|
1348
|
+
return false;
|
|
1349
|
+
}
|
|
1350
|
+
return failureCount < 2;
|
|
1351
|
+
}
|
|
1352
|
+
function normalizeContentCollectionType(contentType) {
|
|
1353
|
+
switch (contentType.toLowerCase()) {
|
|
1354
|
+
case "news":
|
|
1355
|
+
return "news";
|
|
1356
|
+
case "page":
|
|
1357
|
+
case "pages":
|
|
1358
|
+
return "pages";
|
|
1359
|
+
case "event":
|
|
1360
|
+
case "events":
|
|
1361
|
+
return "events";
|
|
1362
|
+
default:
|
|
1363
|
+
throw new Error(`Unsupported AxCloud content collection type: ${contentType}`);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
function normalizeContentDetailType(contentType) {
|
|
1367
|
+
switch (contentType.toLowerCase()) {
|
|
1368
|
+
case "news":
|
|
1369
|
+
return "news";
|
|
1370
|
+
case "page":
|
|
1371
|
+
case "pages":
|
|
1372
|
+
return "page";
|
|
1373
|
+
case "event":
|
|
1374
|
+
case "events":
|
|
1375
|
+
return "event";
|
|
1376
|
+
default:
|
|
1377
|
+
throw new Error(`Unsupported AxCloud content detail type: ${contentType}`);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
function getContentAccessState(context, publicOnly) {
|
|
1381
|
+
if (publicOnly) {
|
|
1382
|
+
return { mode: "public", enabled: true, subject: "anonymous" };
|
|
1383
|
+
}
|
|
1384
|
+
const isLoading = context.isAuthLoading ?? false;
|
|
1385
|
+
if (isLoading) {
|
|
1386
|
+
return { mode: "pending", enabled: false, subject: "auth-loading" };
|
|
1387
|
+
}
|
|
1388
|
+
const hasExplicitAuthState = typeof context.isAuthenticated === "boolean";
|
|
1389
|
+
const isAuthenticated = context.isAuthenticated ?? !context.isPublicMode;
|
|
1390
|
+
if (!isAuthenticated) {
|
|
1391
|
+
return { mode: "public", enabled: true, subject: "anonymous" };
|
|
1392
|
+
}
|
|
1393
|
+
if (hasExplicitAuthState && !context.authSubject) {
|
|
1394
|
+
return { mode: "pending", enabled: false, subject: "auth-subject-pending" };
|
|
1395
|
+
}
|
|
1396
|
+
const subject = context.authSubject || "legacy-authenticated-session";
|
|
1397
|
+
return { mode: "member", enabled: Boolean(subject), subject };
|
|
882
1398
|
}
|
|
883
|
-
|
|
884
|
-
|
|
1399
|
+
|
|
1400
|
+
// src/react/hooks/useContent.ts
|
|
885
1401
|
function useContent(contentType, options = {}) {
|
|
886
|
-
const
|
|
1402
|
+
const context = useAxCloudContext();
|
|
1403
|
+
const { client, config, orgId, stage } = context;
|
|
1404
|
+
const collectionType = normalizeContentCollectionType(contentType);
|
|
887
1405
|
const {
|
|
888
1406
|
publicOnly = false,
|
|
889
1407
|
page = 1,
|
|
890
1408
|
pageSize = 20,
|
|
891
|
-
status = "published",
|
|
892
|
-
visibility,
|
|
893
|
-
sectionId,
|
|
894
1409
|
search,
|
|
895
|
-
sortBy = "createdAt",
|
|
1410
|
+
sortBy = collectionType === "news" ? "publishedAt" : "createdAt",
|
|
896
1411
|
sortOrder = "desc",
|
|
1412
|
+
tags,
|
|
897
1413
|
lang
|
|
898
1414
|
} = options;
|
|
1415
|
+
const access = getContentAccessState(context, publicOnly);
|
|
1416
|
+
const deliveryScope = access.mode === "member" ? "members" : "public";
|
|
1417
|
+
const params = {
|
|
1418
|
+
page,
|
|
1419
|
+
pageSize,
|
|
1420
|
+
search,
|
|
1421
|
+
sort: sortBy,
|
|
1422
|
+
order: sortOrder,
|
|
1423
|
+
tags: tags?.length ? tags.join(",") : void 0,
|
|
1424
|
+
lang
|
|
1425
|
+
};
|
|
899
1426
|
const query = useQuery({
|
|
900
1427
|
queryKey: [
|
|
901
1428
|
"axcloud",
|
|
902
1429
|
"content",
|
|
1430
|
+
"collection",
|
|
1431
|
+
stage,
|
|
1432
|
+
config.apiUrl,
|
|
903
1433
|
orgId,
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
pageSize,
|
|
909
|
-
status,
|
|
910
|
-
visibility,
|
|
911
|
-
sectionId,
|
|
912
|
-
search,
|
|
913
|
-
sortBy,
|
|
914
|
-
sortOrder,
|
|
915
|
-
lang
|
|
1434
|
+
access.mode,
|
|
1435
|
+
access.subject,
|
|
1436
|
+
collectionType,
|
|
1437
|
+
params
|
|
916
1438
|
],
|
|
917
|
-
queryFn:
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
sectionId,
|
|
926
|
-
search,
|
|
927
|
-
sortBy,
|
|
928
|
-
sortOrder,
|
|
929
|
-
lang
|
|
930
|
-
});
|
|
931
|
-
},
|
|
932
|
-
staleTime: 1e3 * 60 * 5
|
|
933
|
-
// 5 minutes
|
|
1439
|
+
queryFn: () => client.get(
|
|
1440
|
+
`/content/${deliveryScope}/${orgId}/${collectionType}`,
|
|
1441
|
+
params
|
|
1442
|
+
),
|
|
1443
|
+
enabled: access.enabled,
|
|
1444
|
+
staleTime: CONTENT_STALE_TIME_MS,
|
|
1445
|
+
refetchOnWindowFocus: true,
|
|
1446
|
+
retry: shouldRetryContentRequest
|
|
934
1447
|
});
|
|
1448
|
+
const items = query.data?.items ?? [];
|
|
935
1449
|
return {
|
|
936
|
-
data:
|
|
937
|
-
|
|
1450
|
+
data: items,
|
|
1451
|
+
count: query.data?.count ?? items.length,
|
|
1452
|
+
total: query.data?.total ?? items.length,
|
|
1453
|
+
page: query.data?.page ?? page,
|
|
1454
|
+
pageSize: query.data?.pageSize ?? pageSize,
|
|
938
1455
|
hasMore: query.data?.hasMore ?? false,
|
|
939
|
-
|
|
1456
|
+
nextCursor: query.data?.nextCursor,
|
|
1457
|
+
isLoading: access.mode === "pending" || query.isLoading,
|
|
1458
|
+
isFetching: query.isFetching,
|
|
940
1459
|
isError: query.isError,
|
|
941
1460
|
error: query.error,
|
|
942
1461
|
refetch: query.refetch
|
|
943
1462
|
};
|
|
944
1463
|
}
|
|
945
1464
|
function useContentItem(contentType, idOrSlug, options = {}) {
|
|
946
|
-
const
|
|
1465
|
+
const context = useAxCloudContext();
|
|
1466
|
+
const { client, config, orgId, stage } = context;
|
|
1467
|
+
const detailType = normalizeContentDetailType(contentType);
|
|
947
1468
|
const { publicOnly = false, lang } = options;
|
|
948
|
-
const
|
|
1469
|
+
const access = getContentAccessState(context, publicOnly);
|
|
1470
|
+
const deliveryScope = access.mode === "member" ? "members" : "public";
|
|
949
1471
|
const query = useQuery({
|
|
950
|
-
queryKey: [
|
|
951
|
-
|
|
952
|
-
|
|
1472
|
+
queryKey: [
|
|
1473
|
+
"axcloud",
|
|
1474
|
+
"content",
|
|
1475
|
+
"item",
|
|
1476
|
+
stage,
|
|
1477
|
+
config.apiUrl,
|
|
1478
|
+
orgId,
|
|
1479
|
+
access.mode,
|
|
1480
|
+
access.subject,
|
|
1481
|
+
detailType,
|
|
1482
|
+
idOrSlug,
|
|
1483
|
+
lang
|
|
1484
|
+
],
|
|
1485
|
+
queryFn: () => {
|
|
953
1486
|
const encodedIdOrSlug = encodeURIComponent(idOrSlug);
|
|
954
|
-
const endpoint =
|
|
1487
|
+
const endpoint = `/content/${deliveryScope}/${orgId}/items/${detailType}/${encodedIdOrSlug}`;
|
|
955
1488
|
return lang ? client.get(endpoint, { lang }) : client.get(endpoint);
|
|
956
1489
|
},
|
|
957
|
-
enabled:
|
|
958
|
-
staleTime:
|
|
959
|
-
|
|
1490
|
+
enabled: access.enabled && Boolean(idOrSlug),
|
|
1491
|
+
staleTime: CONTENT_STALE_TIME_MS,
|
|
1492
|
+
refetchOnWindowFocus: true,
|
|
1493
|
+
retry: shouldRetryContentRequest
|
|
960
1494
|
});
|
|
961
1495
|
return {
|
|
962
1496
|
data: query.data ?? null,
|
|
963
|
-
isLoading: query.isLoading,
|
|
1497
|
+
isLoading: access.mode === "pending" || query.isLoading,
|
|
1498
|
+
isFetching: query.isFetching,
|
|
964
1499
|
isError: query.isError,
|
|
965
1500
|
error: query.error,
|
|
966
1501
|
refetch: query.refetch
|
|
967
1502
|
};
|
|
968
1503
|
}
|
|
969
1504
|
function usePageContent(slug, options = {}) {
|
|
970
|
-
const state = useContentItem("
|
|
1505
|
+
const state = useContentItem("page", slug, options);
|
|
971
1506
|
const reader = useMemo(() => createContentSlotReader(state.data), [state.data]);
|
|
972
1507
|
return {
|
|
973
1508
|
...state,
|
|
@@ -975,6 +1510,11 @@ function usePageContent(slug, options = {}) {
|
|
|
975
1510
|
...reader
|
|
976
1511
|
};
|
|
977
1512
|
}
|
|
1513
|
+
|
|
1514
|
+
// src/react/hooks/useNews.ts
|
|
1515
|
+
function useNews(options = {}) {
|
|
1516
|
+
return useContent("news", options);
|
|
1517
|
+
}
|
|
978
1518
|
function useMySection(options = {}) {
|
|
979
1519
|
const { includeMembers = true } = options;
|
|
980
1520
|
const { isAuthenticated } = useAuth0();
|
|
@@ -1373,6 +1913,73 @@ function useDocumentDownload(documentId) {
|
|
|
1373
1913
|
refetch: query.refetch
|
|
1374
1914
|
};
|
|
1375
1915
|
}
|
|
1916
|
+
function useMemberDocuments(options) {
|
|
1917
|
+
const { client, orgId } = useAxCloudContext();
|
|
1918
|
+
const params = buildDocumentParams(options);
|
|
1919
|
+
const folderId = options?.folderId;
|
|
1920
|
+
const query = useQuery({
|
|
1921
|
+
queryKey: ["axcloud", "documents", "member", "list", orgId, options],
|
|
1922
|
+
queryFn: async () => {
|
|
1923
|
+
return client.get(`/documents/members/${orgId}/documents`, params);
|
|
1924
|
+
},
|
|
1925
|
+
staleTime: 1e3 * 60 * 5
|
|
1926
|
+
});
|
|
1927
|
+
const allItems = query.data?.items ?? [];
|
|
1928
|
+
const items = folderId ? allItems.filter((doc) => doc.folder_ids?.includes(folderId)) : allItems;
|
|
1929
|
+
return {
|
|
1930
|
+
documents: items,
|
|
1931
|
+
items,
|
|
1932
|
+
count: folderId ? items.length : query.data?.count ?? 0,
|
|
1933
|
+
isLoading: query.isLoading,
|
|
1934
|
+
isError: query.isError,
|
|
1935
|
+
error: query.error,
|
|
1936
|
+
refetch: query.refetch
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1939
|
+
function useMemberDocumentFolders() {
|
|
1940
|
+
const { client, orgId } = useAxCloudContext();
|
|
1941
|
+
const query = useQuery({
|
|
1942
|
+
queryKey: ["axcloud", "documents", "member", "folders", orgId],
|
|
1943
|
+
queryFn: async () => {
|
|
1944
|
+
return client.get(
|
|
1945
|
+
`/documents/members/${orgId}/document-folders`
|
|
1946
|
+
);
|
|
1947
|
+
},
|
|
1948
|
+
staleTime: 1e3 * 60 * 5
|
|
1949
|
+
});
|
|
1950
|
+
return {
|
|
1951
|
+
folders: query.data?.folders ?? [],
|
|
1952
|
+
isLoading: query.isLoading,
|
|
1953
|
+
isError: query.isError,
|
|
1954
|
+
error: query.error,
|
|
1955
|
+
refetch: query.refetch
|
|
1956
|
+
};
|
|
1957
|
+
}
|
|
1958
|
+
function useMemberDocumentDownload(documentId) {
|
|
1959
|
+
const { client, orgId } = useAxCloudContext();
|
|
1960
|
+
const query = useQuery({
|
|
1961
|
+
queryKey: ["axcloud", "documents", "member", "download", orgId, documentId],
|
|
1962
|
+
queryFn: async () => {
|
|
1963
|
+
return client.get(
|
|
1964
|
+
`/documents/members/${orgId}/documents/${encodeURIComponent(documentId || "")}/download`
|
|
1965
|
+
);
|
|
1966
|
+
},
|
|
1967
|
+
enabled: !!documentId,
|
|
1968
|
+
staleTime: 1e3 * 60 * 10,
|
|
1969
|
+
gcTime: 1e3 * 60 * 14
|
|
1970
|
+
});
|
|
1971
|
+
return {
|
|
1972
|
+
url: query.data?.url ?? null,
|
|
1973
|
+
documentUrl: query.data?.url ?? null,
|
|
1974
|
+
filename: query.data?.filename ?? null,
|
|
1975
|
+
mimeType: query.data?.mimeType ?? null,
|
|
1976
|
+
expiresIn: query.data?.expiresIn ?? null,
|
|
1977
|
+
isLoading: query.isLoading,
|
|
1978
|
+
isError: query.isError,
|
|
1979
|
+
error: query.error,
|
|
1980
|
+
refetch: query.refetch
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1376
1983
|
function useBarnsportMunicipalities() {
|
|
1377
1984
|
const { client, orgId } = useAxCloudContext();
|
|
1378
1985
|
const query = useQuery({
|
|
@@ -1409,6 +2016,24 @@ function useBarnsportSports() {
|
|
|
1409
2016
|
refetch: query.refetch
|
|
1410
2017
|
};
|
|
1411
2018
|
}
|
|
2019
|
+
function useBarnsportSport(slug) {
|
|
2020
|
+
const { client, orgId } = useAxCloudContext();
|
|
2021
|
+
const query = useQuery({
|
|
2022
|
+
queryKey: ["axcloud", "barnsport", "sport", orgId, slug],
|
|
2023
|
+
queryFn: () => client.get(
|
|
2024
|
+
`/barnsport/public/${orgId}/sports/${slug || ""}`
|
|
2025
|
+
),
|
|
2026
|
+
enabled: Boolean(slug),
|
|
2027
|
+
staleTime: 1e3 * 60 * 15
|
|
2028
|
+
});
|
|
2029
|
+
return {
|
|
2030
|
+
sport: query.data?.sport ?? null,
|
|
2031
|
+
isLoading: query.isLoading,
|
|
2032
|
+
isError: query.isError,
|
|
2033
|
+
error: query.error,
|
|
2034
|
+
refetch: query.refetch
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
1412
2037
|
function useBarnsportActivities(filters = {}) {
|
|
1413
2038
|
const { client, orgId } = useAxCloudContext();
|
|
1414
2039
|
const params = { ...filters };
|
|
@@ -1718,6 +2343,6 @@ function LinkedAccountsList({
|
|
|
1718
2343
|
] });
|
|
1719
2344
|
}
|
|
1720
2345
|
|
|
1721
|
-
export { AxCloudClient, AxCloudError, AxCloudProvider, LinkedAccountsList, LoginButton, LogoutButton, ModuleGate, ProtectedRoute, castVote, castVoteWithCode, checkEligibility, checkVotingCode, createClient, createContentSlotReader, createFormHelper, getBallot, getClient, getContentSlotMap, getVotingReceipt, isAxCloudError, submitForm, useAuth, useAvatar, useAxCloudContext, useBarnsportActivities, useBarnsportActivity, useBarnsportMunicipalities, useBarnsportSitemap, useBarnsportSports, useBranding, useCodeBallot, useContent, useContentItem, useDocumentDownload, useDocuments, useFormStats, useForms, useLinkedAccounts, useLogo, useMedia, useMediaList, useModuleAccess, useMySection, useNews, useOrg, usePageContent, useSections, useSubmitBarnsportInterest, useUpdateFormSubmission, useUser, userHasAllPermissions, userHasAnyPermission, userHasPermission };
|
|
2346
|
+
export { AxCloudClient, AxCloudError, AxCloudProvider, LinkedAccountsList, LoginButton, LogoutButton, ModuleGate, ProtectedRoute, castVote, castVoteWithCode, checkEligibility, checkVotingCode, createClient, createContentSlotReader, createFormHelper, fetchAnalyticsPublicConfig, getBallot, getClient, getContentSlotMap, getVotingReceipt, isAllowedAnalyticsEvent, isAxCloudError, isLocalAnalyticsHostname, sanitizeAnalyticsEventData, sanitizeAnalyticsPath, submitForm, useAnalytics, useAnalyticsRuntime, useAuth, useAvatar, useAxCloudContext, useBarnsportActivities, useBarnsportActivity, useBarnsportMunicipalities, useBarnsportSitemap, useBarnsportSport, useBarnsportSports, useBranding, useCodeBallot, useContent, useContentItem, useDocumentDownload, useDocuments, useFormStats, useForms, useLinkedAccounts, useLogo, useMedia, useMediaList, useMemberDocumentDownload, useMemberDocumentFolders, useMemberDocuments, useModuleAccess, useMySection, useNews, useOrg, usePageContent, useSections, useSubmitBarnsportInterest, useUpdateFormSubmission, useUpdateMyProfile, useUser, userHasAllPermissions, userHasAnyPermission, userHasPermission };
|
|
1722
2347
|
//# sourceMappingURL=index.mjs.map
|
|
1723
2348
|
//# sourceMappingURL=index.mjs.map
|