@axcode/axcloud-sdk 0.1.7-dev.580a922 → 0.1.7-dev.bf490b9
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 +200 -124
- package/dist/index.d.mts +78 -3
- package/dist/index.d.ts +78 -3
- package/dist/index.js +339 -42
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +334 -44
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext, useContext,
|
|
1
|
+
import { createContext, useContext, useState, useRef, useCallback, useEffect, useMemo } from 'react';
|
|
2
2
|
import { Auth0Provider, useAuth0 } from '@auth0/auth0-react';
|
|
3
3
|
import { QueryClient, QueryClientProvider, useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
|
|
4
4
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
@@ -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) {
|
|
@@ -531,6 +632,127 @@ function useAxCloudContext() {
|
|
|
531
632
|
}
|
|
532
633
|
return context;
|
|
533
634
|
}
|
|
635
|
+
var NAVIGATION_EVENT = "axcloud-sdk:navigation";
|
|
636
|
+
var historySubscriberCount = 0;
|
|
637
|
+
var originalPushState = null;
|
|
638
|
+
var originalReplaceState = null;
|
|
639
|
+
function subscribeToNavigation(callback) {
|
|
640
|
+
if (typeof window === "undefined") return () => void 0;
|
|
641
|
+
if (historySubscriberCount === 0) {
|
|
642
|
+
originalPushState = window.history.pushState.bind(window.history);
|
|
643
|
+
originalReplaceState = window.history.replaceState.bind(window.history);
|
|
644
|
+
window.history.pushState = ((...args) => {
|
|
645
|
+
originalPushState?.(...args);
|
|
646
|
+
window.dispatchEvent(new Event(NAVIGATION_EVENT));
|
|
647
|
+
});
|
|
648
|
+
window.history.replaceState = ((...args) => {
|
|
649
|
+
originalReplaceState?.(...args);
|
|
650
|
+
window.dispatchEvent(new Event(NAVIGATION_EVENT));
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
historySubscriberCount += 1;
|
|
654
|
+
window.addEventListener("popstate", callback);
|
|
655
|
+
window.addEventListener(NAVIGATION_EVENT, callback);
|
|
656
|
+
return () => {
|
|
657
|
+
window.removeEventListener("popstate", callback);
|
|
658
|
+
window.removeEventListener(NAVIGATION_EVENT, callback);
|
|
659
|
+
historySubscriberCount = Math.max(0, historySubscriberCount - 1);
|
|
660
|
+
if (historySubscriberCount === 0) {
|
|
661
|
+
if (originalPushState) window.history.pushState = originalPushState;
|
|
662
|
+
if (originalReplaceState) window.history.replaceState = originalReplaceState;
|
|
663
|
+
originalPushState = null;
|
|
664
|
+
originalReplaceState = null;
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
function doNotTrackEnabled(config) {
|
|
669
|
+
if (config.respectDoNotTrack === false || typeof navigator === "undefined") return false;
|
|
670
|
+
return navigator.doNotTrack === "1";
|
|
671
|
+
}
|
|
672
|
+
function useAnalyticsRuntime(config) {
|
|
673
|
+
const [enabled, setEnabled] = useState(false);
|
|
674
|
+
const [ready, setReady] = useState(false);
|
|
675
|
+
const publicConfigRef = useRef(null);
|
|
676
|
+
const optionsRef = useRef(config.analytics);
|
|
677
|
+
optionsRef.current = config.analytics;
|
|
678
|
+
const trackPageView = useCallback(
|
|
679
|
+
(pathname) => {
|
|
680
|
+
if (!ready || !optionsRef.current || typeof window === "undefined") return false;
|
|
681
|
+
return sendAnalyticsPageView(pathname || window.location.pathname, optionsRef.current);
|
|
682
|
+
},
|
|
683
|
+
[ready]
|
|
684
|
+
);
|
|
685
|
+
const track = useCallback(
|
|
686
|
+
(eventName, data) => {
|
|
687
|
+
if (!ready || typeof window === "undefined") return false;
|
|
688
|
+
return sendAnalyticsEvent(eventName, data, publicConfigRef.current?.allowedEvents);
|
|
689
|
+
},
|
|
690
|
+
[ready]
|
|
691
|
+
);
|
|
692
|
+
useEffect(() => {
|
|
693
|
+
const options = config.analytics;
|
|
694
|
+
setEnabled(false);
|
|
695
|
+
setReady(false);
|
|
696
|
+
publicConfigRef.current = null;
|
|
697
|
+
if (!options?.enabled || !options.siteKey || typeof window === "undefined") return void 0;
|
|
698
|
+
if (isLocalAnalyticsHostname(window.location.hostname) && !options.trackLocalhost)
|
|
699
|
+
return void 0;
|
|
700
|
+
const controller = new AbortController();
|
|
701
|
+
let cancelled = false;
|
|
702
|
+
let unsubscribe = () => void 0;
|
|
703
|
+
const initialize = async () => {
|
|
704
|
+
try {
|
|
705
|
+
const publicConfig = await fetchAnalyticsPublicConfig({
|
|
706
|
+
apiUrl: config.apiUrl,
|
|
707
|
+
orgId: config.orgId,
|
|
708
|
+
siteKey: options.siteKey,
|
|
709
|
+
stage: config.stage,
|
|
710
|
+
hostname: window.location.hostname,
|
|
711
|
+
signal: controller.signal
|
|
712
|
+
});
|
|
713
|
+
if (cancelled || !publicConfig.enabled || doNotTrackEnabled(publicConfig)) return;
|
|
714
|
+
publicConfigRef.current = publicConfig;
|
|
715
|
+
setEnabled(true);
|
|
716
|
+
await loadAnalyticsScript(publicConfig);
|
|
717
|
+
if (cancelled) return;
|
|
718
|
+
setReady(true);
|
|
719
|
+
if (!publicConfig.autoTrack) {
|
|
720
|
+
const trackCurrentPage = () => {
|
|
721
|
+
sendAnalyticsPageView(window.location.pathname, options);
|
|
722
|
+
};
|
|
723
|
+
trackCurrentPage();
|
|
724
|
+
unsubscribe = subscribeToNavigation(trackCurrentPage);
|
|
725
|
+
}
|
|
726
|
+
} catch (error) {
|
|
727
|
+
if (!cancelled && !(error instanceof Error && error.name === "AbortError")) {
|
|
728
|
+
setEnabled(false);
|
|
729
|
+
setReady(false);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
void initialize();
|
|
734
|
+
return () => {
|
|
735
|
+
cancelled = true;
|
|
736
|
+
controller.abort();
|
|
737
|
+
unsubscribe();
|
|
738
|
+
};
|
|
739
|
+
}, [
|
|
740
|
+
config.analytics?.enabled,
|
|
741
|
+
config.analytics?.siteKey,
|
|
742
|
+
config.analytics?.trackLocalhost,
|
|
743
|
+
config.analytics?.normalizePath,
|
|
744
|
+
config.apiUrl,
|
|
745
|
+
config.orgId,
|
|
746
|
+
config.stage
|
|
747
|
+
]);
|
|
748
|
+
return useMemo(
|
|
749
|
+
() => ({ enabled, ready, track, trackPageView }),
|
|
750
|
+
[enabled, ready, track, trackPageView]
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
function useAnalytics() {
|
|
754
|
+
return useAxCloudContext().analytics;
|
|
755
|
+
}
|
|
534
756
|
var defaultQueryClient = new QueryClient({
|
|
535
757
|
defaultOptions: {
|
|
536
758
|
queries: {
|
|
@@ -549,6 +771,7 @@ function AxCloudInnerProvider({
|
|
|
549
771
|
timeout
|
|
550
772
|
}) {
|
|
551
773
|
const { getAccessTokenSilently, isAuthenticated } = useAuth0();
|
|
774
|
+
const analytics = useAnalyticsRuntime(config);
|
|
552
775
|
const tokenGetterRef = useRef(async () => void 0);
|
|
553
776
|
tokenGetterRef.current = isAuthenticated ? async () => {
|
|
554
777
|
try {
|
|
@@ -578,9 +801,10 @@ function AxCloudInnerProvider({
|
|
|
578
801
|
client,
|
|
579
802
|
orgId: config.orgId,
|
|
580
803
|
stage: config.stage,
|
|
581
|
-
isPublicMode: Boolean(config.apiKey) && !isAuthenticated
|
|
804
|
+
isPublicMode: Boolean(config.apiKey) && !isAuthenticated,
|
|
805
|
+
analytics
|
|
582
806
|
}),
|
|
583
|
-
[config, client, isAuthenticated]
|
|
807
|
+
[config, client, isAuthenticated, analytics]
|
|
584
808
|
);
|
|
585
809
|
return /* @__PURE__ */ jsx(AxCloudContext.Provider, { value: contextValue, children });
|
|
586
810
|
}
|
|
@@ -590,6 +814,7 @@ function AxCloudProvider({
|
|
|
590
814
|
apiUrl,
|
|
591
815
|
stage = "dev",
|
|
592
816
|
apiKey,
|
|
817
|
+
analytics,
|
|
593
818
|
auth0,
|
|
594
819
|
queryClient = defaultQueryClient,
|
|
595
820
|
timeout
|
|
@@ -600,9 +825,10 @@ function AxCloudProvider({
|
|
|
600
825
|
apiUrl,
|
|
601
826
|
stage,
|
|
602
827
|
apiKey,
|
|
828
|
+
analytics,
|
|
603
829
|
auth0
|
|
604
830
|
}),
|
|
605
|
-
[orgId, apiUrl, stage, apiKey, auth0]
|
|
831
|
+
[orgId, apiUrl, stage, apiKey, analytics, auth0]
|
|
606
832
|
);
|
|
607
833
|
return /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx(
|
|
608
834
|
Auth0Provider,
|
|
@@ -611,14 +837,15 @@ function AxCloudProvider({
|
|
|
611
837
|
clientId: auth0.clientId,
|
|
612
838
|
authorizationParams: {
|
|
613
839
|
redirect_uri: auth0.redirectUri,
|
|
614
|
-
audience: auth0.audience
|
|
840
|
+
audience: auth0.audience,
|
|
841
|
+
...auth0.organization ? { organization: auth0.organization } : {}
|
|
615
842
|
},
|
|
616
843
|
children: /* @__PURE__ */ jsx(AxCloudInnerProvider, { config, timeout, children })
|
|
617
844
|
}
|
|
618
845
|
) });
|
|
619
846
|
}
|
|
620
847
|
function useAuth() {
|
|
621
|
-
const { config } = useAxCloudContext();
|
|
848
|
+
const { config, client } = useAxCloudContext();
|
|
622
849
|
const {
|
|
623
850
|
isAuthenticated,
|
|
624
851
|
isLoading,
|
|
@@ -634,10 +861,15 @@ function useAuth() {
|
|
|
634
861
|
return loginWithRedirect({
|
|
635
862
|
appState: {
|
|
636
863
|
returnTo: options?.returnTo || window.location.pathname
|
|
637
|
-
}
|
|
864
|
+
},
|
|
865
|
+
...config.auth0.organization ? {
|
|
866
|
+
authorizationParams: {
|
|
867
|
+
organization: config.auth0.organization
|
|
868
|
+
}
|
|
869
|
+
} : {}
|
|
638
870
|
});
|
|
639
871
|
},
|
|
640
|
-
[loginWithRedirect]
|
|
872
|
+
[config.auth0.organization, loginWithRedirect]
|
|
641
873
|
);
|
|
642
874
|
const logout = useCallback(
|
|
643
875
|
(options) => {
|
|
@@ -656,6 +888,60 @@ function useAuth() {
|
|
|
656
888
|
return void 0;
|
|
657
889
|
}
|
|
658
890
|
}, [getAccessTokenSilently]);
|
|
891
|
+
const refreshUser = useCallback(async () => {
|
|
892
|
+
await getAccessTokenSilently({ cacheMode: "off" });
|
|
893
|
+
const claims = await getIdTokenClaims();
|
|
894
|
+
return claims?.email_verified === true;
|
|
895
|
+
}, [getAccessTokenSilently, getIdTokenClaims]);
|
|
896
|
+
const requestEmailVerification = useCallback(
|
|
897
|
+
async (options) => {
|
|
898
|
+
if (!isAuthenticated || !auth0User) {
|
|
899
|
+
return {
|
|
900
|
+
success: false,
|
|
901
|
+
status: "failed",
|
|
902
|
+
message: "You must be signed in to verify your email."
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
if (auth0User.email_verified === true) {
|
|
906
|
+
return {
|
|
907
|
+
success: true,
|
|
908
|
+
status: "already_verified",
|
|
909
|
+
message: "Your email address is already verified."
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
try {
|
|
913
|
+
const response = await client.post("/email/identity/verification-email", {
|
|
914
|
+
return_to: options?.returnTo || window.location.href
|
|
915
|
+
});
|
|
916
|
+
const data = response.data || {};
|
|
917
|
+
return {
|
|
918
|
+
success: true,
|
|
919
|
+
status: data.status || "sent",
|
|
920
|
+
message: data.message || "A verification email has been sent.",
|
|
921
|
+
...data.retry_after_seconds !== void 0 ? { retryAfterSeconds: data.retry_after_seconds } : {}
|
|
922
|
+
};
|
|
923
|
+
} catch (requestError) {
|
|
924
|
+
if (isAxCloudError(requestError) && requestError.status === 429) {
|
|
925
|
+
const responseDetails = requestError.details?.details;
|
|
926
|
+
const retryAfterSeconds = Number(
|
|
927
|
+
responseDetails && typeof responseDetails === "object" ? responseDetails.retry_after_seconds : void 0
|
|
928
|
+
);
|
|
929
|
+
return {
|
|
930
|
+
success: false,
|
|
931
|
+
status: "rate_limited",
|
|
932
|
+
message: "Please wait before requesting another verification email.",
|
|
933
|
+
...Number.isFinite(retryAfterSeconds) ? { retryAfterSeconds } : {}
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
return {
|
|
937
|
+
success: false,
|
|
938
|
+
status: "failed",
|
|
939
|
+
message: "Failed to send the verification email. Please try again."
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
},
|
|
943
|
+
[auth0User, client, isAuthenticated]
|
|
944
|
+
);
|
|
659
945
|
const getAuth0Client = useCallback(() => {
|
|
660
946
|
return {
|
|
661
947
|
loginWithRedirect,
|
|
@@ -729,24 +1015,28 @@ function useAuth() {
|
|
|
729
1015
|
id: auth0User.sub || "",
|
|
730
1016
|
email: auth0User.email || "",
|
|
731
1017
|
name: auth0User.name || "",
|
|
732
|
-
avatar: auth0User.picture
|
|
1018
|
+
avatar: auth0User.picture,
|
|
1019
|
+
...typeof auth0User.email_verified === "boolean" ? { emailVerified: auth0User.email_verified } : {}
|
|
733
1020
|
} : null,
|
|
1021
|
+
needsEmailVerification: isAuthenticated && auth0User?.email_verified === false,
|
|
734
1022
|
login,
|
|
735
1023
|
logout,
|
|
736
1024
|
// Escape hatch for advanced use cases
|
|
737
1025
|
getAccessToken,
|
|
738
1026
|
getAuth0Client,
|
|
1027
|
+
refreshUser,
|
|
1028
|
+
requestEmailVerification,
|
|
739
1029
|
// Password management
|
|
740
1030
|
requestPasswordReset
|
|
741
1031
|
};
|
|
742
1032
|
}
|
|
743
1033
|
function useUser() {
|
|
744
|
-
const { isAuthenticated, isLoading: isAuthLoading } = useAuth0();
|
|
1034
|
+
const { isAuthenticated, isLoading: isAuthLoading, user: auth0User } = useAuth0();
|
|
745
1035
|
const { client, stage, orgId } = useAxCloudContext();
|
|
746
1036
|
const query = useQuery({
|
|
747
|
-
queryKey: ["axcloud", "user", "me", stage],
|
|
1037
|
+
queryKey: ["axcloud", "user", "me", stage, orgId, auth0User?.sub],
|
|
748
1038
|
queryFn: async () => {
|
|
749
|
-
return client.get(
|
|
1039
|
+
return client.get("/api/users/me");
|
|
750
1040
|
},
|
|
751
1041
|
enabled: isAuthenticated && !isAuthLoading,
|
|
752
1042
|
staleTime: 1e3 * 60 * 5
|
|
@@ -755,7 +1045,7 @@ function useUser() {
|
|
|
755
1045
|
const userData = query.data;
|
|
756
1046
|
const userOrgIds = userData?.all_org_ids ?? [];
|
|
757
1047
|
const isSuperAdmin = userData?.isSuperAdmin === true;
|
|
758
|
-
const hasOrgAccess =
|
|
1048
|
+
const hasOrgAccess = userOrgIds.includes(orgId);
|
|
759
1049
|
const orgMembership = userData?.organization_memberships?.[orgId];
|
|
760
1050
|
const roleInOrg = orgMembership?.role ?? null;
|
|
761
1051
|
return {
|
|
@@ -1803,6 +2093,6 @@ function LinkedAccountsList({
|
|
|
1803
2093
|
] });
|
|
1804
2094
|
}
|
|
1805
2095
|
|
|
1806
|
-
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, 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, useUser, userHasAllPermissions, userHasAnyPermission, userHasPermission };
|
|
2096
|
+
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, useUser, userHasAllPermissions, userHasAnyPermission, userHasPermission };
|
|
1807
2097
|
//# sourceMappingURL=index.mjs.map
|
|
1808
2098
|
//# sourceMappingURL=index.mjs.map
|