@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/dist/index.js CHANGED
@@ -20,7 +20,7 @@ var AxCloudError = class _AxCloudError extends Error {
20
20
  }
21
21
  static fromResponse(status, body) {
22
22
  const details = typeof body === "object" ? body : void 0;
23
- const message = details?.message;
23
+ const message = (typeof details?.message === "string" ? details.message : void 0) || (typeof details?.error === "string" ? details.error : void 0);
24
24
  switch (status) {
25
25
  case 401:
26
26
  return new _AxCloudError(
@@ -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
- `Request timed out after ${timeoutMs}ms`,
83
- "TIMEOUT_ERROR",
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) {
@@ -245,7 +346,7 @@ function getContentSlotMap(content) {
245
346
  for (const block of content?.blocks || []) {
246
347
  const slot = getBlockSlot(block);
247
348
  if (!slot) continue;
248
- fields[slot] = blockToSlotValue(block);
349
+ if (!(slot in fields)) fields[slot] = blockToSlotValue(block);
249
350
  }
250
351
  return fields;
251
352
  }
@@ -254,11 +355,13 @@ function createContentSlotReader(content) {
254
355
  const value = (key, fallback) => {
255
356
  return key in fields ? fields[key] : fallback;
256
357
  };
358
+ const unsafeHtml = (key, fallback = "") => stringifySlotValue(value(key, fallback));
257
359
  return {
258
360
  fields,
259
361
  value,
260
362
  text: (key, fallback = "") => stringifySlotValue(value(key, fallback)),
261
- html: (key, fallback = "") => stringifySlotValue(value(key, fallback)),
363
+ html: unsafeHtml,
364
+ unsafeHtml,
262
365
  list: (key, fallback = []) => slotValueToList(value(key, fallback)),
263
366
  image: (key, fallback) => slotValueToImage(value(key, fallback))
264
367
  };
@@ -533,24 +636,195 @@ function useAxCloudContext() {
533
636
  }
534
637
  return context;
535
638
  }
536
- var defaultQueryClient = new reactQuery.QueryClient({
537
- defaultOptions: {
538
- queries: {
539
- staleTime: 1e3 * 60 * 5,
540
- // 5 minutes
541
- gcTime: 1e3 * 60 * 30,
542
- // 30 minutes (garbage collection)
543
- retry: 2,
544
- refetchOnWindowFocus: false
639
+ var NAVIGATION_EVENT = "axcloud-sdk:navigation";
640
+ var historySubscriberCount = 0;
641
+ var originalPushState = null;
642
+ var originalReplaceState = null;
643
+ function subscribeToNavigation(callback) {
644
+ if (typeof window === "undefined") return () => void 0;
645
+ if (historySubscriberCount === 0) {
646
+ originalPushState = window.history.pushState.bind(window.history);
647
+ originalReplaceState = window.history.replaceState.bind(window.history);
648
+ window.history.pushState = ((...args) => {
649
+ originalPushState?.(...args);
650
+ window.dispatchEvent(new Event(NAVIGATION_EVENT));
651
+ });
652
+ window.history.replaceState = ((...args) => {
653
+ originalReplaceState?.(...args);
654
+ window.dispatchEvent(new Event(NAVIGATION_EVENT));
655
+ });
656
+ }
657
+ historySubscriberCount += 1;
658
+ window.addEventListener("popstate", callback);
659
+ window.addEventListener(NAVIGATION_EVENT, callback);
660
+ return () => {
661
+ window.removeEventListener("popstate", callback);
662
+ window.removeEventListener(NAVIGATION_EVENT, callback);
663
+ historySubscriberCount = Math.max(0, historySubscriberCount - 1);
664
+ if (historySubscriberCount === 0) {
665
+ if (originalPushState) window.history.pushState = originalPushState;
666
+ if (originalReplaceState) window.history.replaceState = originalReplaceState;
667
+ originalPushState = null;
668
+ originalReplaceState = null;
545
669
  }
670
+ };
671
+ }
672
+ function doNotTrackEnabled(config) {
673
+ if (config.respectDoNotTrack === false || typeof navigator === "undefined") return false;
674
+ return navigator.doNotTrack === "1";
675
+ }
676
+ function useAnalyticsRuntime(config) {
677
+ const [enabled, setEnabled] = react.useState(false);
678
+ const [ready, setReady] = react.useState(false);
679
+ const publicConfigRef = react.useRef(null);
680
+ const optionsRef = react.useRef(config.analytics);
681
+ optionsRef.current = config.analytics;
682
+ const trackPageView = react.useCallback(
683
+ (pathname) => {
684
+ if (!ready || !optionsRef.current || typeof window === "undefined") return false;
685
+ return sendAnalyticsPageView(pathname || window.location.pathname, optionsRef.current);
686
+ },
687
+ [ready]
688
+ );
689
+ const track = react.useCallback(
690
+ (eventName, data) => {
691
+ if (!ready || typeof window === "undefined") return false;
692
+ return sendAnalyticsEvent(eventName, data, publicConfigRef.current?.allowedEvents);
693
+ },
694
+ [ready]
695
+ );
696
+ react.useEffect(() => {
697
+ const options = config.analytics;
698
+ setEnabled(false);
699
+ setReady(false);
700
+ publicConfigRef.current = null;
701
+ if (!options?.enabled || !options.siteKey || typeof window === "undefined") return void 0;
702
+ if (isLocalAnalyticsHostname(window.location.hostname) && !options.trackLocalhost)
703
+ return void 0;
704
+ const controller = new AbortController();
705
+ let cancelled = false;
706
+ let unsubscribe = () => void 0;
707
+ const initialize = async () => {
708
+ try {
709
+ const publicConfig = await fetchAnalyticsPublicConfig({
710
+ apiUrl: config.apiUrl,
711
+ orgId: config.orgId,
712
+ siteKey: options.siteKey,
713
+ stage: config.stage,
714
+ hostname: window.location.hostname,
715
+ signal: controller.signal
716
+ });
717
+ if (cancelled || !publicConfig.enabled || doNotTrackEnabled(publicConfig)) return;
718
+ publicConfigRef.current = publicConfig;
719
+ setEnabled(true);
720
+ await loadAnalyticsScript(publicConfig);
721
+ if (cancelled) return;
722
+ setReady(true);
723
+ if (!publicConfig.autoTrack) {
724
+ const trackCurrentPage = () => {
725
+ sendAnalyticsPageView(window.location.pathname, options);
726
+ };
727
+ trackCurrentPage();
728
+ unsubscribe = subscribeToNavigation(trackCurrentPage);
729
+ }
730
+ } catch (error) {
731
+ if (!cancelled && !(error instanceof Error && error.name === "AbortError")) {
732
+ setEnabled(false);
733
+ setReady(false);
734
+ }
735
+ }
736
+ };
737
+ void initialize();
738
+ return () => {
739
+ cancelled = true;
740
+ controller.abort();
741
+ unsubscribe();
742
+ };
743
+ }, [
744
+ config.analytics?.enabled,
745
+ config.analytics?.siteKey,
746
+ config.analytics?.trackLocalhost,
747
+ config.analytics?.normalizePath,
748
+ config.apiUrl,
749
+ config.orgId,
750
+ config.stage
751
+ ]);
752
+ return react.useMemo(
753
+ () => ({ enabled, ready, track, trackPageView }),
754
+ [enabled, ready, track, trackPageView]
755
+ );
756
+ }
757
+ function useAnalytics() {
758
+ return useAxCloudContext().analytics;
759
+ }
760
+
761
+ // src/react/auth/auth0Invitation.ts
762
+ var AUTH0_INVITATION_PATTERN = /^[A-Za-z0-9_-]{1,2048}$/;
763
+ function readAuth0InvitationAuthorizationParams(configuredOrganization, search = typeof window === "undefined" ? "" : window.location.search) {
764
+ if (!configuredOrganization || !search) return {};
765
+ const params = new URLSearchParams(search);
766
+ const invitation = params.get("invitation")?.trim();
767
+ const organization = params.get("organization")?.trim();
768
+ if (organization !== configuredOrganization || !invitation || !AUTH0_INVITATION_PATTERN.test(invitation)) {
769
+ return {};
546
770
  }
547
- });
771
+ return { invitation };
772
+ }
773
+ function createDefaultQueryClient() {
774
+ return new reactQuery.QueryClient({
775
+ defaultOptions: {
776
+ queries: {
777
+ staleTime: 1e3 * 60,
778
+ gcTime: 1e3 * 60 * 30,
779
+ retry: (failureCount, error) => {
780
+ if (isAxCloudError(error) && error.status >= 400 && error.status < 500) {
781
+ return false;
782
+ }
783
+ return failureCount < 2;
784
+ },
785
+ refetchOnWindowFocus: true
786
+ }
787
+ }
788
+ });
789
+ }
790
+ var queryClientAxCloudOwners = /* @__PURE__ */ new WeakMap();
791
+ function createQueryClientOwnerSignature(config, subject) {
792
+ return JSON.stringify([
793
+ subject === null ? "public" : "authenticated",
794
+ subject,
795
+ config.orgId,
796
+ config.stage,
797
+ config.apiUrl,
798
+ config.apiKey ?? null,
799
+ config.auth0.domain,
800
+ config.auth0.clientId,
801
+ config.auth0.audience ?? null,
802
+ config.auth0.organization ?? null,
803
+ config.auth0.connection ?? null
804
+ ]);
805
+ }
548
806
  function AxCloudInnerProvider({
549
807
  children,
550
808
  config,
809
+ queryClient,
551
810
  timeout
552
811
  }) {
553
- const { getAccessTokenSilently, isAuthenticated } = auth0React.useAuth0();
812
+ const { getAccessTokenSilently, isAuthenticated, isLoading, user } = auth0React.useAuth0();
813
+ const analytics = useAnalyticsRuntime(config);
814
+ const [, setOwnerGeneration] = react.useState(0);
815
+ const currentSubject = isLoading ? void 0 : isAuthenticated ? user?.sub : null;
816
+ const currentOwnerSignature = currentSubject === void 0 ? void 0 : createQueryClientOwnerSignature(config, currentSubject);
817
+ const hasKnownOwner = queryClientAxCloudOwners.has(queryClient);
818
+ const ownerNeedsActivation = currentOwnerSignature !== void 0 && (!hasKnownOwner || queryClientAxCloudOwners.get(queryClient) !== currentOwnerSignature);
819
+ const clearContentCache = react.useCallback(() => {
820
+ queryClient.removeQueries({ queryKey: ["axcloud"] });
821
+ }, [queryClient]);
822
+ react.useLayoutEffect(() => {
823
+ if (currentOwnerSignature === void 0 || !ownerNeedsActivation) return;
824
+ clearContentCache();
825
+ queryClientAxCloudOwners.set(queryClient, currentOwnerSignature);
826
+ setOwnerGeneration((generation) => generation + 1);
827
+ }, [clearContentCache, currentOwnerSignature, ownerNeedsActivation, queryClient]);
554
828
  const tokenGetterRef = react.useRef(async () => void 0);
555
829
  tokenGetterRef.current = isAuthenticated ? async () => {
556
830
  try {
@@ -580,11 +854,17 @@ function AxCloudInnerProvider({
580
854
  client,
581
855
  orgId: config.orgId,
582
856
  stage: config.stage,
583
- isPublicMode: Boolean(config.apiKey) && !isAuthenticated
857
+ isPublicMode: !isAuthenticated,
858
+ isAuthLoading: isLoading,
859
+ isAuthenticated,
860
+ authSubject: isAuthenticated ? user?.sub : void 0,
861
+ clearContentCache,
862
+ analytics
584
863
  }),
585
- [config, client, isAuthenticated]
864
+ [config, client, isAuthenticated, isLoading, user?.sub, clearContentCache, analytics]
586
865
  );
587
- return /* @__PURE__ */ jsxRuntime.jsx(AxCloudContext.Provider, { value: contextValue, children });
866
+ const identityReady = currentOwnerSignature !== void 0 && !ownerNeedsActivation;
867
+ return /* @__PURE__ */ jsxRuntime.jsx(AxCloudContext.Provider, { value: contextValue, children: identityReady ? children : null });
588
868
  }
589
869
  function AxCloudProvider({
590
870
  children,
@@ -592,19 +872,24 @@ function AxCloudProvider({
592
872
  apiUrl,
593
873
  stage = "dev",
594
874
  apiKey,
875
+ analytics,
595
876
  auth0,
596
- queryClient = defaultQueryClient,
877
+ queryClient: providedQueryClient,
597
878
  timeout
598
879
  }) {
880
+ const [ownedQueryClient] = react.useState(createDefaultQueryClient);
881
+ const queryClient = providedQueryClient || ownedQueryClient;
882
+ const invitationAuthorizationParams = readAuth0InvitationAuthorizationParams(auth0.organization);
599
883
  const config = react.useMemo(
600
884
  () => ({
601
885
  orgId,
602
886
  apiUrl,
603
887
  stage,
604
888
  apiKey,
889
+ analytics,
605
890
  auth0
606
891
  }),
607
- [orgId, apiUrl, stage, apiKey, auth0]
892
+ [orgId, apiUrl, stage, apiKey, analytics, auth0]
608
893
  );
609
894
  return /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsxRuntime.jsx(
610
895
  auth0React.Auth0Provider,
@@ -613,14 +898,16 @@ function AxCloudProvider({
613
898
  clientId: auth0.clientId,
614
899
  authorizationParams: {
615
900
  redirect_uri: auth0.redirectUri,
616
- audience: auth0.audience
901
+ audience: auth0.audience,
902
+ ...auth0.organization ? { organization: auth0.organization } : {},
903
+ ...invitationAuthorizationParams
617
904
  },
618
- children: /* @__PURE__ */ jsxRuntime.jsx(AxCloudInnerProvider, { config, timeout, children })
905
+ children: /* @__PURE__ */ jsxRuntime.jsx(AxCloudInnerProvider, { config, queryClient, timeout, children })
619
906
  }
620
907
  ) });
621
908
  }
622
909
  function useAuth() {
623
- const { config } = useAxCloudContext();
910
+ const { config, client, clearContentCache } = useAxCloudContext();
624
911
  const {
625
912
  isAuthenticated,
626
913
  isLoading,
@@ -633,23 +920,33 @@ function useAuth() {
633
920
  } = auth0React.useAuth0();
634
921
  const login = react.useCallback(
635
922
  (options) => {
923
+ const invitationAuthorizationParams = readAuth0InvitationAuthorizationParams(
924
+ config.auth0.organization
925
+ );
636
926
  return loginWithRedirect({
637
927
  appState: {
638
928
  returnTo: options?.returnTo || window.location.pathname
639
- }
929
+ },
930
+ ...config.auth0.organization ? {
931
+ authorizationParams: {
932
+ organization: config.auth0.organization,
933
+ ...invitationAuthorizationParams
934
+ }
935
+ } : {}
640
936
  });
641
937
  },
642
- [loginWithRedirect]
938
+ [config.auth0.organization, loginWithRedirect]
643
939
  );
644
940
  const logout = react.useCallback(
645
941
  (options) => {
942
+ clearContentCache?.();
646
943
  return auth0Logout({
647
944
  logoutParams: {
648
945
  returnTo: options?.returnTo || window.location.origin
649
946
  }
650
947
  });
651
948
  },
652
- [auth0Logout]
949
+ [auth0Logout, clearContentCache]
653
950
  );
654
951
  const getAccessToken = react.useCallback(async () => {
655
952
  try {
@@ -658,6 +955,60 @@ function useAuth() {
658
955
  return void 0;
659
956
  }
660
957
  }, [getAccessTokenSilently]);
958
+ const refreshUser = react.useCallback(async () => {
959
+ await getAccessTokenSilently({ cacheMode: "off" });
960
+ const claims = await getIdTokenClaims();
961
+ return claims?.email_verified === true;
962
+ }, [getAccessTokenSilently, getIdTokenClaims]);
963
+ const requestEmailVerification = react.useCallback(
964
+ async (options) => {
965
+ if (!isAuthenticated || !auth0User) {
966
+ return {
967
+ success: false,
968
+ status: "failed",
969
+ message: "You must be signed in to verify your email."
970
+ };
971
+ }
972
+ if (auth0User.email_verified === true) {
973
+ return {
974
+ success: true,
975
+ status: "already_verified",
976
+ message: "Your email address is already verified."
977
+ };
978
+ }
979
+ try {
980
+ const response = await client.post("/email/identity/verification-email", {
981
+ return_to: options?.returnTo || window.location.href
982
+ });
983
+ const data = response.data || {};
984
+ return {
985
+ success: true,
986
+ status: data.status || "sent",
987
+ message: data.message || "A verification email has been sent.",
988
+ ...data.retry_after_seconds !== void 0 ? { retryAfterSeconds: data.retry_after_seconds } : {}
989
+ };
990
+ } catch (requestError) {
991
+ if (isAxCloudError(requestError) && requestError.status === 429) {
992
+ const responseDetails = requestError.details?.details;
993
+ const retryAfterSeconds = Number(
994
+ responseDetails && typeof responseDetails === "object" ? responseDetails.retry_after_seconds : void 0
995
+ );
996
+ return {
997
+ success: false,
998
+ status: "rate_limited",
999
+ message: "Please wait before requesting another verification email.",
1000
+ ...Number.isFinite(retryAfterSeconds) ? { retryAfterSeconds } : {}
1001
+ };
1002
+ }
1003
+ return {
1004
+ success: false,
1005
+ status: "failed",
1006
+ message: "Failed to send the verification email. Please try again."
1007
+ };
1008
+ }
1009
+ },
1010
+ [auth0User, client, isAuthenticated]
1011
+ );
661
1012
  const getAuth0Client = react.useCallback(() => {
662
1013
  return {
663
1014
  loginWithRedirect,
@@ -731,35 +1082,182 @@ function useAuth() {
731
1082
  id: auth0User.sub || "",
732
1083
  email: auth0User.email || "",
733
1084
  name: auth0User.name || "",
734
- avatar: auth0User.picture
1085
+ avatar: auth0User.picture,
1086
+ ...typeof auth0User.email_verified === "boolean" ? { emailVerified: auth0User.email_verified } : {}
735
1087
  } : null,
1088
+ needsEmailVerification: isAuthenticated && auth0User?.email_verified === false,
736
1089
  login,
737
1090
  logout,
738
1091
  // Escape hatch for advanced use cases
739
1092
  getAccessToken,
740
1093
  getAuth0Client,
1094
+ refreshUser,
1095
+ requestEmailVerification,
741
1096
  // Password management
742
1097
  requestPasswordReset
743
1098
  };
744
1099
  }
1100
+ var currentUserQueryKey = (stage, orgId, subject) => ["axcloud", "user", "me", stage, orgId, subject];
1101
+ function asRecord(value) {
1102
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
1103
+ }
1104
+ function asString(value) {
1105
+ return typeof value === "string" && value.trim().length > 0 ? value : null;
1106
+ }
1107
+ function asStringArray(value) {
1108
+ if (!Array.isArray(value)) return [];
1109
+ return Array.from(new Set(value.map(asString).filter((item) => item !== null)));
1110
+ }
1111
+ function normalizeMembershipSections(rawMembership) {
1112
+ const sections = /* @__PURE__ */ new Map();
1113
+ const addSection = (idValue, nameValue, roleValue) => {
1114
+ const id = asString(idValue);
1115
+ if (!id) return;
1116
+ const existing = sections.get(id);
1117
+ sections.set(id, {
1118
+ id,
1119
+ name: asString(nameValue) || existing?.name || id,
1120
+ role: asString(roleValue) || existing?.role || "member"
1121
+ });
1122
+ };
1123
+ const directSections = rawMembership.sections;
1124
+ if (Array.isArray(directSections)) {
1125
+ for (const rawSection of directSections) {
1126
+ if (typeof rawSection === "string") {
1127
+ addSection(rawSection);
1128
+ continue;
1129
+ }
1130
+ const section = asRecord(rawSection);
1131
+ addSection(
1132
+ section.id ?? section.sectionId ?? section.section_id,
1133
+ section.name ?? section.sectionName ?? section.section_name,
1134
+ section.role
1135
+ );
1136
+ }
1137
+ }
1138
+ const sectionMemberships = asRecord(
1139
+ rawMembership.sectionMemberships ?? rawMembership.section_memberships
1140
+ );
1141
+ for (const [sectionId, rawSectionMembership] of Object.entries(sectionMemberships)) {
1142
+ const sectionMembership = asRecord(rawSectionMembership);
1143
+ addSection(
1144
+ sectionId,
1145
+ sectionMembership.name ?? sectionMembership.sectionName ?? sectionMembership.section_name,
1146
+ sectionMembership.role
1147
+ );
1148
+ }
1149
+ for (const sectionId of asStringArray(rawMembership.sectionIds ?? rawMembership.section_ids)) {
1150
+ addSection(sectionId);
1151
+ }
1152
+ const primarySectionId = asString(rawMembership.sectionId ?? rawMembership.section_id);
1153
+ if (primarySectionId) addSection(primarySectionId);
1154
+ const normalized = Array.from(sections.values());
1155
+ if (!primarySectionId) return normalized;
1156
+ return normalized.sort((left, right) => {
1157
+ if (left.id === primarySectionId) return -1;
1158
+ if (right.id === primarySectionId) return 1;
1159
+ return 0;
1160
+ });
1161
+ }
1162
+ function normalizeMembership(rawValue) {
1163
+ const raw = asRecord(rawValue);
1164
+ const role = asString(raw.role) || "member";
1165
+ const roles = asStringArray(raw.roles);
1166
+ const sections = normalizeMembershipSections(raw);
1167
+ const sectionId = asString(raw.sectionId ?? raw.section_id) || sections[0]?.id || null;
1168
+ const joinedAt = asString(raw.joinedAt ?? raw.joined_at);
1169
+ return {
1170
+ role,
1171
+ roles: roles.length > 0 ? roles : [role],
1172
+ title: asString(raw.title),
1173
+ status: asString(raw.status),
1174
+ joinedAt,
1175
+ memberSince: asString(raw.memberSince ?? raw.member_since) || joinedAt,
1176
+ sectionId,
1177
+ sectionIds: sections.map((section) => section.id),
1178
+ sections
1179
+ };
1180
+ }
1181
+ function normalizeCurrentUserResponse(raw, configuredOrgId) {
1182
+ const rawMemberships = asRecord(
1183
+ raw.organizationMemberships ?? raw.organization_memberships
1184
+ );
1185
+ const organizationMemberships = Object.fromEntries(
1186
+ Object.entries(rawMemberships).map(([organizationId2, membership]) => [
1187
+ organizationId2,
1188
+ normalizeMembership(membership)
1189
+ ])
1190
+ );
1191
+ const allOrgIds = Array.from(
1192
+ /* @__PURE__ */ new Set([
1193
+ ...asStringArray(raw.allOrgIds ?? raw.all_org_ids),
1194
+ ...Object.keys(organizationMemberships)
1195
+ ])
1196
+ );
1197
+ const effectiveOrganizationId = asString(
1198
+ raw.effectiveOrganizationId ?? raw.effective_org_id
1199
+ );
1200
+ const primaryOrganizationId = asString(
1201
+ raw.organizationId ?? raw.primary_org_id ?? raw.org_id
1202
+ );
1203
+ const organizationId = effectiveOrganizationId || primaryOrganizationId || (organizationMemberships[configuredOrgId] ? configuredOrgId : null) || allOrgIds[0] || configuredOrgId;
1204
+ const configuredMembership = organizationMemberships[configuredOrgId];
1205
+ const firstName = asString(raw.firstName ?? raw.first_name ?? raw.given_name) || "";
1206
+ const lastName = asString(raw.lastName ?? raw.last_name ?? raw.family_name) || "";
1207
+ const email = asString(raw.email) || "";
1208
+ const createdAt = asString(raw.createdAt ?? raw.created_at) || "";
1209
+ const updatedAt = asString(raw.updatedAt ?? raw.updated_at) || createdAt;
1210
+ const role = configuredMembership?.role || asString(raw.role) || "member";
1211
+ const sectionId = configuredMembership?.sectionId || asString(raw.sectionId ?? raw.section_id);
1212
+ const permissions = asStringArray(raw.permissions ?? raw.effective_permissions);
1213
+ const metadata = asRecord(raw.metadata);
1214
+ return {
1215
+ id: asString(raw.id ?? raw.userId ?? raw.user_id ?? raw.sub) || "",
1216
+ email,
1217
+ name: asString(raw.name) || [firstName, lastName].filter(Boolean).join(" ") || email,
1218
+ firstName,
1219
+ lastName,
1220
+ title: configuredMembership?.title ?? asString(raw.title),
1221
+ phone: asString(raw.phone ?? raw.phoneNumber ?? raw.phone_number),
1222
+ address: asString(raw.address ?? raw.location),
1223
+ avatar: asString(raw.avatar ?? raw.avatarUrl ?? raw.avatar_url ?? raw.picture) || void 0,
1224
+ organizationId,
1225
+ ...sectionId ? { sectionId } : {},
1226
+ role,
1227
+ permissions,
1228
+ ...Object.keys(metadata).length > 0 ? { metadata } : {},
1229
+ createdAt,
1230
+ updatedAt,
1231
+ allOrgIds,
1232
+ organizationMemberships,
1233
+ effectiveOrganizationId,
1234
+ isSuperAdmin: raw.isSuperAdmin === true || raw.is_super_admin === true || asString(raw.global_role) === "super_admin" || role === "super_admin"
1235
+ };
1236
+ }
745
1237
  function useUser() {
746
- const { isAuthenticated, isLoading: isAuthLoading } = auth0React.useAuth0();
1238
+ const { isAuthenticated, isLoading: isAuthLoading, user: auth0User } = auth0React.useAuth0();
747
1239
  const { client, stage, orgId } = useAxCloudContext();
1240
+ const queryKey = currentUserQueryKey(stage, orgId, auth0User?.sub);
748
1241
  const query = reactQuery.useQuery({
749
- queryKey: ["axcloud", "user", "me", stage],
1242
+ queryKey,
750
1243
  queryFn: async () => {
751
- return client.get(`/${stage}/api/users/me`);
1244
+ const response = await client.get("/api/users/me");
1245
+ return normalizeCurrentUserResponse(response, orgId);
752
1246
  },
753
1247
  enabled: isAuthenticated && !isAuthLoading,
754
1248
  staleTime: 1e3 * 60 * 5
755
1249
  // 5 minutes
756
1250
  });
757
1251
  const userData = query.data;
758
- const userOrgIds = userData?.all_org_ids ?? [];
1252
+ const userOrgIds = userData?.allOrgIds ?? [];
759
1253
  const isSuperAdmin = userData?.isSuperAdmin === true;
760
- const hasOrgAccess = isSuperAdmin || userOrgIds.includes(orgId);
761
- const orgMembership = userData?.organization_memberships?.[orgId];
1254
+ const orgMembership = userData?.organizationMemberships?.[orgId];
1255
+ const hasOrgAccess = userOrgIds.includes(orgId) && orgMembership?.status?.trim().toLowerCase() === "active";
762
1256
  const roleInOrg = orgMembership?.role ?? null;
1257
+ const sectionId = orgMembership?.sectionId ?? userData?.sectionId ?? null;
1258
+ const sections = orgMembership?.sections?.length ? orgMembership.sections : sectionId ? [{ id: sectionId, name: sectionId, role: roleInOrg ?? "member" }] : [];
1259
+ const section = sections.find((item) => item.id === sectionId) ?? sections[0] ?? null;
1260
+ const memberSince = orgMembership?.memberSince ?? null;
763
1261
  return {
764
1262
  user: query.data ?? null,
765
1263
  isLoading: isAuthLoading || query.isLoading,
@@ -769,15 +1267,35 @@ function useUser() {
769
1267
  // Organization access
770
1268
  hasOrgAccess,
771
1269
  roleInOrg,
1270
+ organizationMembership: orgMembership ?? null,
1271
+ title: orgMembership?.title ?? query.data?.title ?? null,
1272
+ section,
1273
+ sections,
1274
+ sectionIds: orgMembership?.sectionIds?.length ? orgMembership.sectionIds : sectionId ? [sectionId] : [],
1275
+ joinedAt: orgMembership?.joinedAt ?? null,
1276
+ memberSince,
772
1277
  // Convenience properties
773
1278
  isAuthenticated,
774
- role: query.data?.role ?? null,
1279
+ role: roleInOrg ?? query.data?.role ?? null,
775
1280
  organizationId: query.data?.organizationId ?? null,
776
- sectionId: query.data?.sectionId ?? null,
1281
+ sectionId,
777
1282
  permissions: query.data?.permissions ?? [],
778
1283
  isSuperAdmin
779
1284
  };
780
1285
  }
1286
+ function useUpdateMyProfile() {
1287
+ const { client, stage } = useAxCloudContext();
1288
+ const queryClient = reactQuery.useQueryClient();
1289
+ return reactQuery.useMutation({
1290
+ mutationFn: (updates) => client.patch("/api/users/me/profile", updates),
1291
+ onSuccess: async () => {
1292
+ await queryClient.invalidateQueries({
1293
+ queryKey: ["axcloud", "user", "me", stage],
1294
+ exact: false
1295
+ });
1296
+ }
1297
+ });
1298
+ }
781
1299
  function useOrg() {
782
1300
  const { client, orgId, stage, isPublicMode } = useAxCloudContext();
783
1301
  const usePublicEndpoint = isPublicMode;
@@ -824,152 +1342,169 @@ function useOrg() {
824
1342
  modules
825
1343
  };
826
1344
  }
827
- function useNews(options = {}) {
828
- const { client, orgId, isPublicMode } = useAxCloudContext();
829
- const {
830
- page = 1,
831
- pageSize = 20,
832
- status = "published",
833
- visibility,
834
- sectionId,
835
- search,
836
- sortBy = "publishedAt",
837
- sortOrder = "desc",
838
- lang,
839
- publicOnly = false
840
- } = options;
841
- const usePublicEndpoint = isPublicMode || publicOnly;
842
- const query = reactQuery.useQuery({
843
- queryKey: [
844
- "axcloud",
845
- "news",
846
- orgId,
847
- usePublicEndpoint,
848
- page,
849
- pageSize,
850
- status,
851
- visibility,
852
- sectionId,
853
- search,
854
- sortBy,
855
- sortOrder,
856
- lang
857
- ],
858
- queryFn: async () => {
859
- const endpoint = usePublicEndpoint ? `/content/public/${orgId}/news` : `/content/members/${orgId}/news`;
860
- return client.get(endpoint, {
861
- page,
862
- pageSize,
863
- status,
864
- visibility,
865
- sectionId,
866
- search,
867
- sortBy,
868
- sortOrder,
869
- lang
870
- });
871
- },
872
- staleTime: 1e3 * 60 * 5
873
- // 5 minutes
874
- });
875
- return {
876
- data: query.data?.items ?? [],
877
- total: query.data?.total ?? 0,
878
- hasMore: query.data?.hasMore ?? false,
879
- isLoading: query.isLoading,
880
- isError: query.isError,
881
- error: query.error,
882
- refetch: query.refetch
883
- };
1345
+
1346
+ // src/react/contentQueryPolicy.ts
1347
+ var CONTENT_STALE_TIME_MS = 6e4;
1348
+ function shouldRetryContentRequest(failureCount, error) {
1349
+ if (isAxCloudError(error) && error.status >= 400 && error.status < 500) {
1350
+ return false;
1351
+ }
1352
+ return failureCount < 2;
1353
+ }
1354
+ function normalizeContentCollectionType(contentType) {
1355
+ switch (contentType.toLowerCase()) {
1356
+ case "news":
1357
+ return "news";
1358
+ case "page":
1359
+ case "pages":
1360
+ return "pages";
1361
+ case "event":
1362
+ case "events":
1363
+ return "events";
1364
+ default:
1365
+ throw new Error(`Unsupported AxCloud content collection type: ${contentType}`);
1366
+ }
1367
+ }
1368
+ function normalizeContentDetailType(contentType) {
1369
+ switch (contentType.toLowerCase()) {
1370
+ case "news":
1371
+ return "news";
1372
+ case "page":
1373
+ case "pages":
1374
+ return "page";
1375
+ case "event":
1376
+ case "events":
1377
+ return "event";
1378
+ default:
1379
+ throw new Error(`Unsupported AxCloud content detail type: ${contentType}`);
1380
+ }
1381
+ }
1382
+ function getContentAccessState(context, publicOnly) {
1383
+ if (publicOnly) {
1384
+ return { mode: "public", enabled: true, subject: "anonymous" };
1385
+ }
1386
+ const isLoading = context.isAuthLoading ?? false;
1387
+ if (isLoading) {
1388
+ return { mode: "pending", enabled: false, subject: "auth-loading" };
1389
+ }
1390
+ const hasExplicitAuthState = typeof context.isAuthenticated === "boolean";
1391
+ const isAuthenticated = context.isAuthenticated ?? !context.isPublicMode;
1392
+ if (!isAuthenticated) {
1393
+ return { mode: "public", enabled: true, subject: "anonymous" };
1394
+ }
1395
+ if (hasExplicitAuthState && !context.authSubject) {
1396
+ return { mode: "pending", enabled: false, subject: "auth-subject-pending" };
1397
+ }
1398
+ const subject = context.authSubject || "legacy-authenticated-session";
1399
+ return { mode: "member", enabled: Boolean(subject), subject };
884
1400
  }
885
- var MEMBER_ENDPOINT_TYPES = ["news", "pages", "events"];
886
- var PAGE_CONTENT_TYPES = ["page", "pages"];
1401
+
1402
+ // src/react/hooks/useContent.ts
887
1403
  function useContent(contentType, options = {}) {
888
- const { client, orgId, isPublicMode } = useAxCloudContext();
1404
+ const context = useAxCloudContext();
1405
+ const { client, config, orgId, stage } = context;
1406
+ const collectionType = normalizeContentCollectionType(contentType);
889
1407
  const {
890
1408
  publicOnly = false,
891
1409
  page = 1,
892
1410
  pageSize = 20,
893
- status = "published",
894
- visibility,
895
- sectionId,
896
1411
  search,
897
- sortBy = "createdAt",
1412
+ sortBy = collectionType === "news" ? "publishedAt" : "createdAt",
898
1413
  sortOrder = "desc",
1414
+ tags,
899
1415
  lang
900
1416
  } = options;
1417
+ const access = getContentAccessState(context, publicOnly);
1418
+ const deliveryScope = access.mode === "member" ? "members" : "public";
1419
+ const params = {
1420
+ page,
1421
+ pageSize,
1422
+ search,
1423
+ sort: sortBy,
1424
+ order: sortOrder,
1425
+ tags: tags?.length ? tags.join(",") : void 0,
1426
+ lang
1427
+ };
901
1428
  const query = reactQuery.useQuery({
902
1429
  queryKey: [
903
1430
  "axcloud",
904
1431
  "content",
1432
+ "collection",
1433
+ stage,
1434
+ config.apiUrl,
905
1435
  orgId,
906
- contentType,
907
- isPublicMode,
908
- publicOnly,
909
- page,
910
- pageSize,
911
- status,
912
- visibility,
913
- sectionId,
914
- search,
915
- sortBy,
916
- sortOrder,
917
- lang
1436
+ access.mode,
1437
+ access.subject,
1438
+ collectionType,
1439
+ params
918
1440
  ],
919
- queryFn: async () => {
920
- const useMemberEndpoint = !isPublicMode && !publicOnly && MEMBER_ENDPOINT_TYPES.includes(contentType);
921
- const endpoint = useMemberEndpoint ? `/content/members/${orgId}/${contentType}` : `/content/public/${orgId}/${contentType}`;
922
- return client.get(endpoint, {
923
- page,
924
- pageSize,
925
- status,
926
- visibility,
927
- sectionId,
928
- search,
929
- sortBy,
930
- sortOrder,
931
- lang
932
- });
933
- },
934
- staleTime: 1e3 * 60 * 5
935
- // 5 minutes
1441
+ queryFn: () => client.get(
1442
+ `/content/${deliveryScope}/${orgId}/${collectionType}`,
1443
+ params
1444
+ ),
1445
+ enabled: access.enabled,
1446
+ staleTime: CONTENT_STALE_TIME_MS,
1447
+ refetchOnWindowFocus: true,
1448
+ retry: shouldRetryContentRequest
936
1449
  });
1450
+ const items = query.data?.items ?? [];
937
1451
  return {
938
- data: query.data?.items ?? [],
939
- total: query.data?.total ?? 0,
1452
+ data: items,
1453
+ count: query.data?.count ?? items.length,
1454
+ total: query.data?.total ?? items.length,
1455
+ page: query.data?.page ?? page,
1456
+ pageSize: query.data?.pageSize ?? pageSize,
940
1457
  hasMore: query.data?.hasMore ?? false,
941
- isLoading: query.isLoading,
1458
+ nextCursor: query.data?.nextCursor,
1459
+ isLoading: access.mode === "pending" || query.isLoading,
1460
+ isFetching: query.isFetching,
942
1461
  isError: query.isError,
943
1462
  error: query.error,
944
1463
  refetch: query.refetch
945
1464
  };
946
1465
  }
947
1466
  function useContentItem(contentType, idOrSlug, options = {}) {
948
- const { client, orgId, isPublicMode } = useAxCloudContext();
1467
+ const context = useAxCloudContext();
1468
+ const { client, config, orgId, stage } = context;
1469
+ const detailType = normalizeContentDetailType(contentType);
949
1470
  const { publicOnly = false, lang } = options;
950
- const useMemberEndpoint = !isPublicMode && !publicOnly && (MEMBER_ENDPOINT_TYPES.includes(contentType) || PAGE_CONTENT_TYPES.includes(contentType));
1471
+ const access = getContentAccessState(context, publicOnly);
1472
+ const deliveryScope = access.mode === "member" ? "members" : "public";
951
1473
  const query = reactQuery.useQuery({
952
- queryKey: ["axcloud", "content", orgId, contentType, idOrSlug, useMemberEndpoint, lang],
953
- queryFn: async () => {
954
- const basePath = useMemberEndpoint ? "members" : "public";
1474
+ queryKey: [
1475
+ "axcloud",
1476
+ "content",
1477
+ "item",
1478
+ stage,
1479
+ config.apiUrl,
1480
+ orgId,
1481
+ access.mode,
1482
+ access.subject,
1483
+ detailType,
1484
+ idOrSlug,
1485
+ lang
1486
+ ],
1487
+ queryFn: () => {
955
1488
  const encodedIdOrSlug = encodeURIComponent(idOrSlug);
956
- const endpoint = PAGE_CONTENT_TYPES.includes(contentType) ? `/content/${basePath}/${orgId}/page/${encodedIdOrSlug}` : `/content/${basePath}/${orgId}/${contentType}/${encodedIdOrSlug}`;
1489
+ const endpoint = `/content/${deliveryScope}/${orgId}/items/${detailType}/${encodedIdOrSlug}`;
957
1490
  return lang ? client.get(endpoint, { lang }) : client.get(endpoint);
958
1491
  },
959
- enabled: !!idOrSlug,
960
- staleTime: 1e3 * 60 * 5
961
- // 5 minutes
1492
+ enabled: access.enabled && Boolean(idOrSlug),
1493
+ staleTime: CONTENT_STALE_TIME_MS,
1494
+ refetchOnWindowFocus: true,
1495
+ retry: shouldRetryContentRequest
962
1496
  });
963
1497
  return {
964
1498
  data: query.data ?? null,
965
- isLoading: query.isLoading,
1499
+ isLoading: access.mode === "pending" || query.isLoading,
1500
+ isFetching: query.isFetching,
966
1501
  isError: query.isError,
967
1502
  error: query.error,
968
1503
  refetch: query.refetch
969
1504
  };
970
1505
  }
971
1506
  function usePageContent(slug, options = {}) {
972
- const state = useContentItem("pages", slug, options);
1507
+ const state = useContentItem("page", slug, options);
973
1508
  const reader = react.useMemo(() => createContentSlotReader(state.data), [state.data]);
974
1509
  return {
975
1510
  ...state,
@@ -977,6 +1512,11 @@ function usePageContent(slug, options = {}) {
977
1512
  ...reader
978
1513
  };
979
1514
  }
1515
+
1516
+ // src/react/hooks/useNews.ts
1517
+ function useNews(options = {}) {
1518
+ return useContent("news", options);
1519
+ }
980
1520
  function useMySection(options = {}) {
981
1521
  const { includeMembers = true } = options;
982
1522
  const { isAuthenticated } = auth0React.useAuth0();
@@ -1375,6 +1915,73 @@ function useDocumentDownload(documentId) {
1375
1915
  refetch: query.refetch
1376
1916
  };
1377
1917
  }
1918
+ function useMemberDocuments(options) {
1919
+ const { client, orgId } = useAxCloudContext();
1920
+ const params = buildDocumentParams(options);
1921
+ const folderId = options?.folderId;
1922
+ const query = reactQuery.useQuery({
1923
+ queryKey: ["axcloud", "documents", "member", "list", orgId, options],
1924
+ queryFn: async () => {
1925
+ return client.get(`/documents/members/${orgId}/documents`, params);
1926
+ },
1927
+ staleTime: 1e3 * 60 * 5
1928
+ });
1929
+ const allItems = query.data?.items ?? [];
1930
+ const items = folderId ? allItems.filter((doc) => doc.folder_ids?.includes(folderId)) : allItems;
1931
+ return {
1932
+ documents: items,
1933
+ items,
1934
+ count: folderId ? items.length : query.data?.count ?? 0,
1935
+ isLoading: query.isLoading,
1936
+ isError: query.isError,
1937
+ error: query.error,
1938
+ refetch: query.refetch
1939
+ };
1940
+ }
1941
+ function useMemberDocumentFolders() {
1942
+ const { client, orgId } = useAxCloudContext();
1943
+ const query = reactQuery.useQuery({
1944
+ queryKey: ["axcloud", "documents", "member", "folders", orgId],
1945
+ queryFn: async () => {
1946
+ return client.get(
1947
+ `/documents/members/${orgId}/document-folders`
1948
+ );
1949
+ },
1950
+ staleTime: 1e3 * 60 * 5
1951
+ });
1952
+ return {
1953
+ folders: query.data?.folders ?? [],
1954
+ isLoading: query.isLoading,
1955
+ isError: query.isError,
1956
+ error: query.error,
1957
+ refetch: query.refetch
1958
+ };
1959
+ }
1960
+ function useMemberDocumentDownload(documentId) {
1961
+ const { client, orgId } = useAxCloudContext();
1962
+ const query = reactQuery.useQuery({
1963
+ queryKey: ["axcloud", "documents", "member", "download", orgId, documentId],
1964
+ queryFn: async () => {
1965
+ return client.get(
1966
+ `/documents/members/${orgId}/documents/${encodeURIComponent(documentId || "")}/download`
1967
+ );
1968
+ },
1969
+ enabled: !!documentId,
1970
+ staleTime: 1e3 * 60 * 10,
1971
+ gcTime: 1e3 * 60 * 14
1972
+ });
1973
+ return {
1974
+ url: query.data?.url ?? null,
1975
+ documentUrl: query.data?.url ?? null,
1976
+ filename: query.data?.filename ?? null,
1977
+ mimeType: query.data?.mimeType ?? null,
1978
+ expiresIn: query.data?.expiresIn ?? null,
1979
+ isLoading: query.isLoading,
1980
+ isError: query.isError,
1981
+ error: query.error,
1982
+ refetch: query.refetch
1983
+ };
1984
+ }
1378
1985
  function useBarnsportMunicipalities() {
1379
1986
  const { client, orgId } = useAxCloudContext();
1380
1987
  const query = reactQuery.useQuery({
@@ -1411,6 +2018,24 @@ function useBarnsportSports() {
1411
2018
  refetch: query.refetch
1412
2019
  };
1413
2020
  }
2021
+ function useBarnsportSport(slug) {
2022
+ const { client, orgId } = useAxCloudContext();
2023
+ const query = reactQuery.useQuery({
2024
+ queryKey: ["axcloud", "barnsport", "sport", orgId, slug],
2025
+ queryFn: () => client.get(
2026
+ `/barnsport/public/${orgId}/sports/${slug || ""}`
2027
+ ),
2028
+ enabled: Boolean(slug),
2029
+ staleTime: 1e3 * 60 * 15
2030
+ });
2031
+ return {
2032
+ sport: query.data?.sport ?? null,
2033
+ isLoading: query.isLoading,
2034
+ isError: query.isError,
2035
+ error: query.error,
2036
+ refetch: query.refetch
2037
+ };
2038
+ }
1414
2039
  function useBarnsportActivities(filters = {}) {
1415
2040
  const { client, orgId } = useAxCloudContext();
1416
2041
  const params = { ...filters };
@@ -1735,12 +2360,19 @@ exports.checkVotingCode = checkVotingCode;
1735
2360
  exports.createClient = createClient;
1736
2361
  exports.createContentSlotReader = createContentSlotReader;
1737
2362
  exports.createFormHelper = createFormHelper;
2363
+ exports.fetchAnalyticsPublicConfig = fetchAnalyticsPublicConfig;
1738
2364
  exports.getBallot = getBallot;
1739
2365
  exports.getClient = getClient;
1740
2366
  exports.getContentSlotMap = getContentSlotMap;
1741
2367
  exports.getVotingReceipt = getVotingReceipt;
2368
+ exports.isAllowedAnalyticsEvent = isAllowedAnalyticsEvent;
1742
2369
  exports.isAxCloudError = isAxCloudError;
2370
+ exports.isLocalAnalyticsHostname = isLocalAnalyticsHostname;
2371
+ exports.sanitizeAnalyticsEventData = sanitizeAnalyticsEventData;
2372
+ exports.sanitizeAnalyticsPath = sanitizeAnalyticsPath;
1743
2373
  exports.submitForm = submitForm;
2374
+ exports.useAnalytics = useAnalytics;
2375
+ exports.useAnalyticsRuntime = useAnalyticsRuntime;
1744
2376
  exports.useAuth = useAuth;
1745
2377
  exports.useAvatar = useAvatar;
1746
2378
  exports.useAxCloudContext = useAxCloudContext;
@@ -1748,6 +2380,7 @@ exports.useBarnsportActivities = useBarnsportActivities;
1748
2380
  exports.useBarnsportActivity = useBarnsportActivity;
1749
2381
  exports.useBarnsportMunicipalities = useBarnsportMunicipalities;
1750
2382
  exports.useBarnsportSitemap = useBarnsportSitemap;
2383
+ exports.useBarnsportSport = useBarnsportSport;
1751
2384
  exports.useBarnsportSports = useBarnsportSports;
1752
2385
  exports.useBranding = useBranding;
1753
2386
  exports.useCodeBallot = useCodeBallot;
@@ -1761,6 +2394,9 @@ exports.useLinkedAccounts = useLinkedAccounts;
1761
2394
  exports.useLogo = useLogo;
1762
2395
  exports.useMedia = useMedia;
1763
2396
  exports.useMediaList = useMediaList;
2397
+ exports.useMemberDocumentDownload = useMemberDocumentDownload;
2398
+ exports.useMemberDocumentFolders = useMemberDocumentFolders;
2399
+ exports.useMemberDocuments = useMemberDocuments;
1764
2400
  exports.useModuleAccess = useModuleAccess;
1765
2401
  exports.useMySection = useMySection;
1766
2402
  exports.useNews = useNews;
@@ -1769,6 +2405,7 @@ exports.usePageContent = usePageContent;
1769
2405
  exports.useSections = useSections;
1770
2406
  exports.useSubmitBarnsportInterest = useSubmitBarnsportInterest;
1771
2407
  exports.useUpdateFormSubmission = useUpdateFormSubmission;
2408
+ exports.useUpdateMyProfile = useUpdateMyProfile;
1772
2409
  exports.useUser = useUser;
1773
2410
  exports.userHasAllPermissions = userHasAllPermissions;
1774
2411
  exports.userHasAnyPermission = userHasAnyPermission;