@axcode/axcloud-sdk 0.1.7-dev.87ea138 → 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 +182 -3
- package/dist/index.d.ts +182 -3
- package/dist/index.js +428 -42
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +419 -44
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -30,19 +30,9 @@ var AxCloudError = class _AxCloudError extends Error {
|
|
|
30
30
|
details
|
|
31
31
|
);
|
|
32
32
|
case 403:
|
|
33
|
-
return new _AxCloudError(
|
|
34
|
-
message || "Access denied",
|
|
35
|
-
"FORBIDDEN",
|
|
36
|
-
status,
|
|
37
|
-
details
|
|
38
|
-
);
|
|
33
|
+
return new _AxCloudError(message || "Access denied", "FORBIDDEN", status, details);
|
|
39
34
|
case 404:
|
|
40
|
-
return new _AxCloudError(
|
|
41
|
-
message || "Resource not found",
|
|
42
|
-
"NOT_FOUND",
|
|
43
|
-
status,
|
|
44
|
-
details
|
|
45
|
-
);
|
|
35
|
+
return new _AxCloudError(message || "Resource not found", "NOT_FOUND", status, details);
|
|
46
36
|
case 422:
|
|
47
37
|
return new _AxCloudError(
|
|
48
38
|
message || "Validation failed",
|
|
@@ -52,19 +42,9 @@ var AxCloudError = class _AxCloudError extends Error {
|
|
|
52
42
|
);
|
|
53
43
|
default:
|
|
54
44
|
if (status >= 500) {
|
|
55
|
-
return new _AxCloudError(
|
|
56
|
-
message || "Server error",
|
|
57
|
-
"SERVER_ERROR",
|
|
58
|
-
status,
|
|
59
|
-
details
|
|
60
|
-
);
|
|
45
|
+
return new _AxCloudError(message || "Server error", "SERVER_ERROR", status, details);
|
|
61
46
|
}
|
|
62
|
-
return new _AxCloudError(
|
|
63
|
-
message || "An error occurred",
|
|
64
|
-
"UNKNOWN_ERROR",
|
|
65
|
-
status,
|
|
66
|
-
details
|
|
67
|
-
);
|
|
47
|
+
return new _AxCloudError(message || "An error occurred", "UNKNOWN_ERROR", status, details);
|
|
68
48
|
}
|
|
69
49
|
}
|
|
70
50
|
static networkError(originalError) {
|
|
@@ -78,12 +58,9 @@ var AxCloudError = class _AxCloudError extends Error {
|
|
|
78
58
|
);
|
|
79
59
|
}
|
|
80
60
|
static timeoutError(timeoutMs) {
|
|
81
|
-
return new _AxCloudError(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
0,
|
|
85
|
-
{ timeout: timeoutMs }
|
|
86
|
-
);
|
|
61
|
+
return new _AxCloudError(`Request timed out after ${timeoutMs}ms`, "TIMEOUT_ERROR", 0, {
|
|
62
|
+
timeout: timeoutMs
|
|
63
|
+
});
|
|
87
64
|
}
|
|
88
65
|
};
|
|
89
66
|
function isAxCloudError(error) {
|
|
@@ -238,6 +215,130 @@ function getClient(key) {
|
|
|
238
215
|
throw new Error("AxCloud client not found. Make sure AxCloudProvider is mounted and use useAxCloudContext() to get the client.");
|
|
239
216
|
}
|
|
240
217
|
|
|
218
|
+
// src/core/analytics.ts
|
|
219
|
+
var EVENT_NAME_PATTERN = /^[a-z][a-z0-9_]{1,63}$/;
|
|
220
|
+
var PROPERTY_KEY_PATTERN = /^[a-z][a-z0-9_]{0,47}$/;
|
|
221
|
+
var SENSITIVE_PROPERTY_PATTERN = /(^|_)(auth0|email|name|password|query|search|secret|token|trace|user|userid)($|_)/;
|
|
222
|
+
var EMAIL_VALUE_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
223
|
+
var OPAQUE_SEGMENT_PATTERNS = [
|
|
224
|
+
/^[0-9a-f]{8}-[0-9a-f-]{27,}$/i,
|
|
225
|
+
/^(org|user|section|document|content|poll|invitation|invite|trace|chan)[_-][a-z0-9_-]+$/i,
|
|
226
|
+
/^[a-f0-9]{20,}$/i,
|
|
227
|
+
/^[a-z0-9_-]{32,}$/i
|
|
228
|
+
];
|
|
229
|
+
var scriptLoads = /* @__PURE__ */ new Map();
|
|
230
|
+
function isLocalAnalyticsHostname(hostname) {
|
|
231
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
|
|
232
|
+
}
|
|
233
|
+
function sanitizeAnalyticsPath(pathname, customNormalizer) {
|
|
234
|
+
const rawPath = pathname.split(/[?#]/, 1)[0] || "/";
|
|
235
|
+
const normalizedInput = rawPath.startsWith("/") ? rawPath : `/${rawPath}`;
|
|
236
|
+
const custom = customNormalizer?.(normalizedInput);
|
|
237
|
+
if (custom === null) return null;
|
|
238
|
+
const path = custom ?? normalizedInput;
|
|
239
|
+
const cleanPath = path.split(/[?#]/, 1)[0] || "/";
|
|
240
|
+
const segments = cleanPath.split("/").map((segment) => {
|
|
241
|
+
if (!segment) return segment;
|
|
242
|
+
let decoded = segment;
|
|
243
|
+
try {
|
|
244
|
+
decoded = decodeURIComponent(segment);
|
|
245
|
+
} catch {
|
|
246
|
+
}
|
|
247
|
+
return OPAQUE_SEGMENT_PATTERNS.some((pattern) => pattern.test(decoded)) ? ":id" : segment;
|
|
248
|
+
});
|
|
249
|
+
return segments.join("/") || "/";
|
|
250
|
+
}
|
|
251
|
+
function sanitizeAnalyticsEventData(data) {
|
|
252
|
+
if (!data) return void 0;
|
|
253
|
+
const sanitized = {};
|
|
254
|
+
for (const [rawKey, rawValue] of Object.entries(data).slice(0, 20)) {
|
|
255
|
+
const key = rawKey.trim().toLowerCase();
|
|
256
|
+
if (!PROPERTY_KEY_PATTERN.test(key) || SENSITIVE_PROPERTY_PATTERN.test(key)) continue;
|
|
257
|
+
if (typeof rawValue === "string") {
|
|
258
|
+
const value = rawValue.trim();
|
|
259
|
+
if (!value || value.length > 100 || EMAIL_VALUE_PATTERN.test(value)) continue;
|
|
260
|
+
sanitized[key] = value;
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (typeof rawValue === "number" && Number.isFinite(rawValue)) {
|
|
264
|
+
sanitized[key] = rawValue;
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (typeof rawValue === "boolean") sanitized[key] = rawValue;
|
|
268
|
+
}
|
|
269
|
+
return Object.keys(sanitized).length > 0 ? sanitized : void 0;
|
|
270
|
+
}
|
|
271
|
+
function isAllowedAnalyticsEvent(eventName, allowedEvents) {
|
|
272
|
+
return EVENT_NAME_PATTERN.test(eventName) && Boolean(allowedEvents?.includes(eventName));
|
|
273
|
+
}
|
|
274
|
+
async function fetchAnalyticsPublicConfig(input) {
|
|
275
|
+
const baseUrl = input.apiUrl.replace(/\/$/, "");
|
|
276
|
+
const url = new URL(`${baseUrl}/analytics/public/config`);
|
|
277
|
+
url.searchParams.set("orgId", input.orgId);
|
|
278
|
+
url.searchParams.set("siteKey", input.siteKey);
|
|
279
|
+
url.searchParams.set("stage", input.stage);
|
|
280
|
+
url.searchParams.set("hostname", input.hostname);
|
|
281
|
+
const response = await fetch(url, {
|
|
282
|
+
method: "GET",
|
|
283
|
+
headers: { Accept: "application/json" },
|
|
284
|
+
signal: input.signal
|
|
285
|
+
});
|
|
286
|
+
if (!response.ok) throw new Error(`Analytics configuration returned ${response.status}`);
|
|
287
|
+
return response.json();
|
|
288
|
+
}
|
|
289
|
+
function loadAnalyticsScript(config, doc = document) {
|
|
290
|
+
if (!config.enabled || !config.websiteId || !config.scriptUrl) {
|
|
291
|
+
return Promise.reject(new Error("Analytics tracker configuration is incomplete"));
|
|
292
|
+
}
|
|
293
|
+
const key = `${config.scriptUrl}|${config.websiteId}`;
|
|
294
|
+
const existingPromise = scriptLoads.get(key);
|
|
295
|
+
if (existingPromise) return existingPromise;
|
|
296
|
+
const existing = doc.querySelector(
|
|
297
|
+
`script[data-axcloud-analytics-site="${config.websiteId}"]`
|
|
298
|
+
);
|
|
299
|
+
if (existing && doc.defaultView?.umami) {
|
|
300
|
+
return Promise.resolve();
|
|
301
|
+
}
|
|
302
|
+
const promise = new Promise((resolve, reject) => {
|
|
303
|
+
const script = existing || doc.createElement("script");
|
|
304
|
+
const onLoad = () => resolve();
|
|
305
|
+
const onError = () => reject(new Error("Analytics tracker failed to load"));
|
|
306
|
+
script.addEventListener("load", onLoad, { once: true });
|
|
307
|
+
script.addEventListener("error", onError, { once: true });
|
|
308
|
+
if (!existing) {
|
|
309
|
+
script.src = config.scriptUrl;
|
|
310
|
+
script.defer = true;
|
|
311
|
+
script.dataset.websiteId = config.websiteId;
|
|
312
|
+
script.dataset.axcloudAnalyticsSite = config.websiteId;
|
|
313
|
+
script.dataset.autoTrack = String(config.autoTrack === true);
|
|
314
|
+
if (config.hostUrl) script.dataset.hostUrl = config.hostUrl;
|
|
315
|
+
if (config.domains?.length) script.dataset.domains = config.domains.join(",");
|
|
316
|
+
if (config.respectDoNotTrack !== false) script.dataset.doNotTrack = "true";
|
|
317
|
+
if (config.excludeSearch !== false) script.dataset.excludeSearch = "true";
|
|
318
|
+
if (config.excludeHash !== false) script.dataset.excludeHash = "true";
|
|
319
|
+
doc.head.appendChild(script);
|
|
320
|
+
}
|
|
321
|
+
}).catch((error) => {
|
|
322
|
+
scriptLoads.delete(key);
|
|
323
|
+
throw error;
|
|
324
|
+
});
|
|
325
|
+
scriptLoads.set(key, promise);
|
|
326
|
+
return promise;
|
|
327
|
+
}
|
|
328
|
+
function sendAnalyticsPageView(pathname, options, targetWindow = window) {
|
|
329
|
+
const path = sanitizeAnalyticsPath(pathname, options.normalizePath);
|
|
330
|
+
const umami = targetWindow.umami;
|
|
331
|
+
if (!path || !umami) return false;
|
|
332
|
+
umami.track({ url: path, title: targetWindow.document.title });
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
function sendAnalyticsEvent(eventName, data, allowedEvents, targetWindow = window) {
|
|
336
|
+
const umami = targetWindow.umami;
|
|
337
|
+
if (!umami || !isAllowedAnalyticsEvent(eventName, allowedEvents)) return false;
|
|
338
|
+
umami.track(eventName, sanitizeAnalyticsEventData(data));
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
|
|
241
342
|
// src/core/contentSlots.ts
|
|
242
343
|
var SLOT_ATTRIBUTE_KEYS = ["slot", "field", "key", "name"];
|
|
243
344
|
function getContentSlotMap(content) {
|
|
@@ -533,6 +634,127 @@ function useAxCloudContext() {
|
|
|
533
634
|
}
|
|
534
635
|
return context;
|
|
535
636
|
}
|
|
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;
|
|
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] = react.useState(false);
|
|
676
|
+
const [ready, setReady] = react.useState(false);
|
|
677
|
+
const publicConfigRef = react.useRef(null);
|
|
678
|
+
const optionsRef = react.useRef(config.analytics);
|
|
679
|
+
optionsRef.current = config.analytics;
|
|
680
|
+
const trackPageView = react.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 = react.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
|
+
react.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 react.useMemo(
|
|
751
|
+
() => ({ enabled, ready, track, trackPageView }),
|
|
752
|
+
[enabled, ready, track, trackPageView]
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
function useAnalytics() {
|
|
756
|
+
return useAxCloudContext().analytics;
|
|
757
|
+
}
|
|
536
758
|
var defaultQueryClient = new reactQuery.QueryClient({
|
|
537
759
|
defaultOptions: {
|
|
538
760
|
queries: {
|
|
@@ -551,6 +773,7 @@ function AxCloudInnerProvider({
|
|
|
551
773
|
timeout
|
|
552
774
|
}) {
|
|
553
775
|
const { getAccessTokenSilently, isAuthenticated } = auth0React.useAuth0();
|
|
776
|
+
const analytics = useAnalyticsRuntime(config);
|
|
554
777
|
const tokenGetterRef = react.useRef(async () => void 0);
|
|
555
778
|
tokenGetterRef.current = isAuthenticated ? async () => {
|
|
556
779
|
try {
|
|
@@ -580,9 +803,10 @@ function AxCloudInnerProvider({
|
|
|
580
803
|
client,
|
|
581
804
|
orgId: config.orgId,
|
|
582
805
|
stage: config.stage,
|
|
583
|
-
isPublicMode: Boolean(config.apiKey) && !isAuthenticated
|
|
806
|
+
isPublicMode: Boolean(config.apiKey) && !isAuthenticated,
|
|
807
|
+
analytics
|
|
584
808
|
}),
|
|
585
|
-
[config, client, isAuthenticated]
|
|
809
|
+
[config, client, isAuthenticated, analytics]
|
|
586
810
|
);
|
|
587
811
|
return /* @__PURE__ */ jsxRuntime.jsx(AxCloudContext.Provider, { value: contextValue, children });
|
|
588
812
|
}
|
|
@@ -592,6 +816,7 @@ function AxCloudProvider({
|
|
|
592
816
|
apiUrl,
|
|
593
817
|
stage = "dev",
|
|
594
818
|
apiKey,
|
|
819
|
+
analytics,
|
|
595
820
|
auth0,
|
|
596
821
|
queryClient = defaultQueryClient,
|
|
597
822
|
timeout
|
|
@@ -602,9 +827,10 @@ function AxCloudProvider({
|
|
|
602
827
|
apiUrl,
|
|
603
828
|
stage,
|
|
604
829
|
apiKey,
|
|
830
|
+
analytics,
|
|
605
831
|
auth0
|
|
606
832
|
}),
|
|
607
|
-
[orgId, apiUrl, stage, apiKey, auth0]
|
|
833
|
+
[orgId, apiUrl, stage, apiKey, analytics, auth0]
|
|
608
834
|
);
|
|
609
835
|
return /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
610
836
|
auth0React.Auth0Provider,
|
|
@@ -613,14 +839,15 @@ function AxCloudProvider({
|
|
|
613
839
|
clientId: auth0.clientId,
|
|
614
840
|
authorizationParams: {
|
|
615
841
|
redirect_uri: auth0.redirectUri,
|
|
616
|
-
audience: auth0.audience
|
|
842
|
+
audience: auth0.audience,
|
|
843
|
+
...auth0.organization ? { organization: auth0.organization } : {}
|
|
617
844
|
},
|
|
618
845
|
children: /* @__PURE__ */ jsxRuntime.jsx(AxCloudInnerProvider, { config, timeout, children })
|
|
619
846
|
}
|
|
620
847
|
) });
|
|
621
848
|
}
|
|
622
849
|
function useAuth() {
|
|
623
|
-
const { config } = useAxCloudContext();
|
|
850
|
+
const { config, client } = useAxCloudContext();
|
|
624
851
|
const {
|
|
625
852
|
isAuthenticated,
|
|
626
853
|
isLoading,
|
|
@@ -636,10 +863,15 @@ function useAuth() {
|
|
|
636
863
|
return loginWithRedirect({
|
|
637
864
|
appState: {
|
|
638
865
|
returnTo: options?.returnTo || window.location.pathname
|
|
639
|
-
}
|
|
866
|
+
},
|
|
867
|
+
...config.auth0.organization ? {
|
|
868
|
+
authorizationParams: {
|
|
869
|
+
organization: config.auth0.organization
|
|
870
|
+
}
|
|
871
|
+
} : {}
|
|
640
872
|
});
|
|
641
873
|
},
|
|
642
|
-
[loginWithRedirect]
|
|
874
|
+
[config.auth0.organization, loginWithRedirect]
|
|
643
875
|
);
|
|
644
876
|
const logout = react.useCallback(
|
|
645
877
|
(options) => {
|
|
@@ -658,6 +890,60 @@ function useAuth() {
|
|
|
658
890
|
return void 0;
|
|
659
891
|
}
|
|
660
892
|
}, [getAccessTokenSilently]);
|
|
893
|
+
const refreshUser = react.useCallback(async () => {
|
|
894
|
+
await getAccessTokenSilently({ cacheMode: "off" });
|
|
895
|
+
const claims = await getIdTokenClaims();
|
|
896
|
+
return claims?.email_verified === true;
|
|
897
|
+
}, [getAccessTokenSilently, getIdTokenClaims]);
|
|
898
|
+
const requestEmailVerification = react.useCallback(
|
|
899
|
+
async (options) => {
|
|
900
|
+
if (!isAuthenticated || !auth0User) {
|
|
901
|
+
return {
|
|
902
|
+
success: false,
|
|
903
|
+
status: "failed",
|
|
904
|
+
message: "You must be signed in to verify your email."
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
if (auth0User.email_verified === true) {
|
|
908
|
+
return {
|
|
909
|
+
success: true,
|
|
910
|
+
status: "already_verified",
|
|
911
|
+
message: "Your email address is already verified."
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
try {
|
|
915
|
+
const response = await client.post("/email/identity/verification-email", {
|
|
916
|
+
return_to: options?.returnTo || window.location.href
|
|
917
|
+
});
|
|
918
|
+
const data = response.data || {};
|
|
919
|
+
return {
|
|
920
|
+
success: true,
|
|
921
|
+
status: data.status || "sent",
|
|
922
|
+
message: data.message || "A verification email has been sent.",
|
|
923
|
+
...data.retry_after_seconds !== void 0 ? { retryAfterSeconds: data.retry_after_seconds } : {}
|
|
924
|
+
};
|
|
925
|
+
} catch (requestError) {
|
|
926
|
+
if (isAxCloudError(requestError) && requestError.status === 429) {
|
|
927
|
+
const responseDetails = requestError.details?.details;
|
|
928
|
+
const retryAfterSeconds = Number(
|
|
929
|
+
responseDetails && typeof responseDetails === "object" ? responseDetails.retry_after_seconds : void 0
|
|
930
|
+
);
|
|
931
|
+
return {
|
|
932
|
+
success: false,
|
|
933
|
+
status: "rate_limited",
|
|
934
|
+
message: "Please wait before requesting another verification email.",
|
|
935
|
+
...Number.isFinite(retryAfterSeconds) ? { retryAfterSeconds } : {}
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
return {
|
|
939
|
+
success: false,
|
|
940
|
+
status: "failed",
|
|
941
|
+
message: "Failed to send the verification email. Please try again."
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
},
|
|
945
|
+
[auth0User, client, isAuthenticated]
|
|
946
|
+
);
|
|
661
947
|
const getAuth0Client = react.useCallback(() => {
|
|
662
948
|
return {
|
|
663
949
|
loginWithRedirect,
|
|
@@ -731,24 +1017,28 @@ function useAuth() {
|
|
|
731
1017
|
id: auth0User.sub || "",
|
|
732
1018
|
email: auth0User.email || "",
|
|
733
1019
|
name: auth0User.name || "",
|
|
734
|
-
avatar: auth0User.picture
|
|
1020
|
+
avatar: auth0User.picture,
|
|
1021
|
+
...typeof auth0User.email_verified === "boolean" ? { emailVerified: auth0User.email_verified } : {}
|
|
735
1022
|
} : null,
|
|
1023
|
+
needsEmailVerification: isAuthenticated && auth0User?.email_verified === false,
|
|
736
1024
|
login,
|
|
737
1025
|
logout,
|
|
738
1026
|
// Escape hatch for advanced use cases
|
|
739
1027
|
getAccessToken,
|
|
740
1028
|
getAuth0Client,
|
|
1029
|
+
refreshUser,
|
|
1030
|
+
requestEmailVerification,
|
|
741
1031
|
// Password management
|
|
742
1032
|
requestPasswordReset
|
|
743
1033
|
};
|
|
744
1034
|
}
|
|
745
1035
|
function useUser() {
|
|
746
|
-
const { isAuthenticated, isLoading: isAuthLoading } = auth0React.useAuth0();
|
|
1036
|
+
const { isAuthenticated, isLoading: isAuthLoading, user: auth0User } = auth0React.useAuth0();
|
|
747
1037
|
const { client, stage, orgId } = useAxCloudContext();
|
|
748
1038
|
const query = reactQuery.useQuery({
|
|
749
|
-
queryKey: ["axcloud", "user", "me", stage],
|
|
1039
|
+
queryKey: ["axcloud", "user", "me", stage, orgId, auth0User?.sub],
|
|
750
1040
|
queryFn: async () => {
|
|
751
|
-
return client.get(
|
|
1041
|
+
return client.get("/api/users/me");
|
|
752
1042
|
},
|
|
753
1043
|
enabled: isAuthenticated && !isAuthLoading,
|
|
754
1044
|
staleTime: 1e3 * 60 * 5
|
|
@@ -757,7 +1047,7 @@ function useUser() {
|
|
|
757
1047
|
const userData = query.data;
|
|
758
1048
|
const userOrgIds = userData?.all_org_ids ?? [];
|
|
759
1049
|
const isSuperAdmin = userData?.isSuperAdmin === true;
|
|
760
|
-
const hasOrgAccess =
|
|
1050
|
+
const hasOrgAccess = userOrgIds.includes(orgId);
|
|
761
1051
|
const orgMembership = userData?.organization_memberships?.[orgId];
|
|
762
1052
|
const roleInOrg = orgMembership?.role ?? null;
|
|
763
1053
|
return {
|
|
@@ -1375,6 +1665,73 @@ function useDocumentDownload(documentId) {
|
|
|
1375
1665
|
refetch: query.refetch
|
|
1376
1666
|
};
|
|
1377
1667
|
}
|
|
1668
|
+
function useMemberDocuments(options) {
|
|
1669
|
+
const { client, orgId } = useAxCloudContext();
|
|
1670
|
+
const params = buildDocumentParams(options);
|
|
1671
|
+
const folderId = options?.folderId;
|
|
1672
|
+
const query = reactQuery.useQuery({
|
|
1673
|
+
queryKey: ["axcloud", "documents", "member", "list", orgId, options],
|
|
1674
|
+
queryFn: async () => {
|
|
1675
|
+
return client.get(`/documents/members/${orgId}/documents`, params);
|
|
1676
|
+
},
|
|
1677
|
+
staleTime: 1e3 * 60 * 5
|
|
1678
|
+
});
|
|
1679
|
+
const allItems = query.data?.items ?? [];
|
|
1680
|
+
const items = folderId ? allItems.filter((doc) => doc.folder_ids?.includes(folderId)) : allItems;
|
|
1681
|
+
return {
|
|
1682
|
+
documents: items,
|
|
1683
|
+
items,
|
|
1684
|
+
count: folderId ? items.length : query.data?.count ?? 0,
|
|
1685
|
+
isLoading: query.isLoading,
|
|
1686
|
+
isError: query.isError,
|
|
1687
|
+
error: query.error,
|
|
1688
|
+
refetch: query.refetch
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
function useMemberDocumentFolders() {
|
|
1692
|
+
const { client, orgId } = useAxCloudContext();
|
|
1693
|
+
const query = reactQuery.useQuery({
|
|
1694
|
+
queryKey: ["axcloud", "documents", "member", "folders", orgId],
|
|
1695
|
+
queryFn: async () => {
|
|
1696
|
+
return client.get(
|
|
1697
|
+
`/documents/members/${orgId}/document-folders`
|
|
1698
|
+
);
|
|
1699
|
+
},
|
|
1700
|
+
staleTime: 1e3 * 60 * 5
|
|
1701
|
+
});
|
|
1702
|
+
return {
|
|
1703
|
+
folders: query.data?.folders ?? [],
|
|
1704
|
+
isLoading: query.isLoading,
|
|
1705
|
+
isError: query.isError,
|
|
1706
|
+
error: query.error,
|
|
1707
|
+
refetch: query.refetch
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
function useMemberDocumentDownload(documentId) {
|
|
1711
|
+
const { client, orgId } = useAxCloudContext();
|
|
1712
|
+
const query = reactQuery.useQuery({
|
|
1713
|
+
queryKey: ["axcloud", "documents", "member", "download", orgId, documentId],
|
|
1714
|
+
queryFn: async () => {
|
|
1715
|
+
return client.get(
|
|
1716
|
+
`/documents/members/${orgId}/documents/${encodeURIComponent(documentId || "")}/download`
|
|
1717
|
+
);
|
|
1718
|
+
},
|
|
1719
|
+
enabled: !!documentId,
|
|
1720
|
+
staleTime: 1e3 * 60 * 10,
|
|
1721
|
+
gcTime: 1e3 * 60 * 14
|
|
1722
|
+
});
|
|
1723
|
+
return {
|
|
1724
|
+
url: query.data?.url ?? null,
|
|
1725
|
+
documentUrl: query.data?.url ?? null,
|
|
1726
|
+
filename: query.data?.filename ?? null,
|
|
1727
|
+
mimeType: query.data?.mimeType ?? null,
|
|
1728
|
+
expiresIn: query.data?.expiresIn ?? null,
|
|
1729
|
+
isLoading: query.isLoading,
|
|
1730
|
+
isError: query.isError,
|
|
1731
|
+
error: query.error,
|
|
1732
|
+
refetch: query.refetch
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1378
1735
|
function useBarnsportMunicipalities() {
|
|
1379
1736
|
const { client, orgId } = useAxCloudContext();
|
|
1380
1737
|
const query = reactQuery.useQuery({
|
|
@@ -1411,6 +1768,24 @@ function useBarnsportSports() {
|
|
|
1411
1768
|
refetch: query.refetch
|
|
1412
1769
|
};
|
|
1413
1770
|
}
|
|
1771
|
+
function useBarnsportSport(slug) {
|
|
1772
|
+
const { client, orgId } = useAxCloudContext();
|
|
1773
|
+
const query = reactQuery.useQuery({
|
|
1774
|
+
queryKey: ["axcloud", "barnsport", "sport", orgId, slug],
|
|
1775
|
+
queryFn: () => client.get(
|
|
1776
|
+
`/barnsport/public/${orgId}/sports/${slug || ""}`
|
|
1777
|
+
),
|
|
1778
|
+
enabled: Boolean(slug),
|
|
1779
|
+
staleTime: 1e3 * 60 * 15
|
|
1780
|
+
});
|
|
1781
|
+
return {
|
|
1782
|
+
sport: query.data?.sport ?? null,
|
|
1783
|
+
isLoading: query.isLoading,
|
|
1784
|
+
isError: query.isError,
|
|
1785
|
+
error: query.error,
|
|
1786
|
+
refetch: query.refetch
|
|
1787
|
+
};
|
|
1788
|
+
}
|
|
1414
1789
|
function useBarnsportActivities(filters = {}) {
|
|
1415
1790
|
const { client, orgId } = useAxCloudContext();
|
|
1416
1791
|
const params = { ...filters };
|
|
@@ -1735,12 +2110,19 @@ exports.checkVotingCode = checkVotingCode;
|
|
|
1735
2110
|
exports.createClient = createClient;
|
|
1736
2111
|
exports.createContentSlotReader = createContentSlotReader;
|
|
1737
2112
|
exports.createFormHelper = createFormHelper;
|
|
2113
|
+
exports.fetchAnalyticsPublicConfig = fetchAnalyticsPublicConfig;
|
|
1738
2114
|
exports.getBallot = getBallot;
|
|
1739
2115
|
exports.getClient = getClient;
|
|
1740
2116
|
exports.getContentSlotMap = getContentSlotMap;
|
|
1741
2117
|
exports.getVotingReceipt = getVotingReceipt;
|
|
2118
|
+
exports.isAllowedAnalyticsEvent = isAllowedAnalyticsEvent;
|
|
1742
2119
|
exports.isAxCloudError = isAxCloudError;
|
|
2120
|
+
exports.isLocalAnalyticsHostname = isLocalAnalyticsHostname;
|
|
2121
|
+
exports.sanitizeAnalyticsEventData = sanitizeAnalyticsEventData;
|
|
2122
|
+
exports.sanitizeAnalyticsPath = sanitizeAnalyticsPath;
|
|
1743
2123
|
exports.submitForm = submitForm;
|
|
2124
|
+
exports.useAnalytics = useAnalytics;
|
|
2125
|
+
exports.useAnalyticsRuntime = useAnalyticsRuntime;
|
|
1744
2126
|
exports.useAuth = useAuth;
|
|
1745
2127
|
exports.useAvatar = useAvatar;
|
|
1746
2128
|
exports.useAxCloudContext = useAxCloudContext;
|
|
@@ -1748,6 +2130,7 @@ exports.useBarnsportActivities = useBarnsportActivities;
|
|
|
1748
2130
|
exports.useBarnsportActivity = useBarnsportActivity;
|
|
1749
2131
|
exports.useBarnsportMunicipalities = useBarnsportMunicipalities;
|
|
1750
2132
|
exports.useBarnsportSitemap = useBarnsportSitemap;
|
|
2133
|
+
exports.useBarnsportSport = useBarnsportSport;
|
|
1751
2134
|
exports.useBarnsportSports = useBarnsportSports;
|
|
1752
2135
|
exports.useBranding = useBranding;
|
|
1753
2136
|
exports.useCodeBallot = useCodeBallot;
|
|
@@ -1761,6 +2144,9 @@ exports.useLinkedAccounts = useLinkedAccounts;
|
|
|
1761
2144
|
exports.useLogo = useLogo;
|
|
1762
2145
|
exports.useMedia = useMedia;
|
|
1763
2146
|
exports.useMediaList = useMediaList;
|
|
2147
|
+
exports.useMemberDocumentDownload = useMemberDocumentDownload;
|
|
2148
|
+
exports.useMemberDocumentFolders = useMemberDocumentFolders;
|
|
2149
|
+
exports.useMemberDocuments = useMemberDocuments;
|
|
1764
2150
|
exports.useModuleAccess = useModuleAccess;
|
|
1765
2151
|
exports.useMySection = useMySection;
|
|
1766
2152
|
exports.useNews = useNews;
|