@axcode/axcloud-sdk 0.1.7-dev.bf490b9 → 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.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { createContext, useContext, useState, useRef, useCallback, useEffect, useMemo } from 'react';
1
+ import { createContext, useContext, useState, useRef, useCallback, useEffect, useMemo, useLayoutEffect } from 'react';
2
2
  import { Auth0Provider, useAuth0 } from '@auth0/auth0-react';
3
- import { QueryClient, QueryClientProvider, useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
3
+ import { QueryClientProvider, useQuery, useQueryClient, useMutation, QueryClient } from '@tanstack/react-query';
4
4
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
5
5
 
6
6
  // src/core/errors.ts
@@ -18,7 +18,7 @@ var AxCloudError = class _AxCloudError extends Error {
18
18
  }
19
19
  static fromResponse(status, body) {
20
20
  const details = typeof body === "object" ? body : void 0;
21
- const message = details?.message;
21
+ const message = (typeof details?.message === "string" ? details.message : void 0) || (typeof details?.error === "string" ? details.error : void 0);
22
22
  switch (status) {
23
23
  case 401:
24
24
  return new _AxCloudError(
@@ -344,7 +344,7 @@ function getContentSlotMap(content) {
344
344
  for (const block of content?.blocks || []) {
345
345
  const slot = getBlockSlot(block);
346
346
  if (!slot) continue;
347
- fields[slot] = blockToSlotValue(block);
347
+ if (!(slot in fields)) fields[slot] = blockToSlotValue(block);
348
348
  }
349
349
  return fields;
350
350
  }
@@ -353,11 +353,13 @@ function createContentSlotReader(content) {
353
353
  const value = (key, fallback) => {
354
354
  return key in fields ? fields[key] : fallback;
355
355
  };
356
+ const unsafeHtml = (key, fallback = "") => stringifySlotValue(value(key, fallback));
356
357
  return {
357
358
  fields,
358
359
  value,
359
360
  text: (key, fallback = "") => stringifySlotValue(value(key, fallback)),
360
- html: (key, fallback = "") => stringifySlotValue(value(key, fallback)),
361
+ html: unsafeHtml,
362
+ unsafeHtml,
361
363
  list: (key, fallback = []) => slotValueToList(value(key, fallback)),
362
364
  image: (key, fallback) => slotValueToImage(value(key, fallback))
363
365
  };
@@ -753,25 +755,74 @@ function useAnalyticsRuntime(config) {
753
755
  function useAnalytics() {
754
756
  return useAxCloudContext().analytics;
755
757
  }
756
- var defaultQueryClient = new QueryClient({
757
- defaultOptions: {
758
- queries: {
759
- staleTime: 1e3 * 60 * 5,
760
- // 5 minutes
761
- gcTime: 1e3 * 60 * 30,
762
- // 30 minutes (garbage collection)
763
- retry: 2,
764
- refetchOnWindowFocus: false
765
- }
758
+
759
+ // src/react/auth/auth0Invitation.ts
760
+ var AUTH0_INVITATION_PATTERN = /^[A-Za-z0-9_-]{1,2048}$/;
761
+ function readAuth0InvitationAuthorizationParams(configuredOrganization, search = typeof window === "undefined" ? "" : window.location.search) {
762
+ if (!configuredOrganization || !search) return {};
763
+ const params = new URLSearchParams(search);
764
+ const invitation = params.get("invitation")?.trim();
765
+ const organization = params.get("organization")?.trim();
766
+ if (organization !== configuredOrganization || !invitation || !AUTH0_INVITATION_PATTERN.test(invitation)) {
767
+ return {};
766
768
  }
767
- });
769
+ return { invitation };
770
+ }
771
+ function createDefaultQueryClient() {
772
+ return new QueryClient({
773
+ defaultOptions: {
774
+ queries: {
775
+ staleTime: 1e3 * 60,
776
+ gcTime: 1e3 * 60 * 30,
777
+ retry: (failureCount, error) => {
778
+ if (isAxCloudError(error) && error.status >= 400 && error.status < 500) {
779
+ return false;
780
+ }
781
+ return failureCount < 2;
782
+ },
783
+ refetchOnWindowFocus: true
784
+ }
785
+ }
786
+ });
787
+ }
788
+ var queryClientAxCloudOwners = /* @__PURE__ */ new WeakMap();
789
+ function createQueryClientOwnerSignature(config, subject) {
790
+ return JSON.stringify([
791
+ subject === null ? "public" : "authenticated",
792
+ subject,
793
+ config.orgId,
794
+ config.stage,
795
+ config.apiUrl,
796
+ config.apiKey ?? null,
797
+ config.auth0.domain,
798
+ config.auth0.clientId,
799
+ config.auth0.audience ?? null,
800
+ config.auth0.organization ?? null,
801
+ config.auth0.connection ?? null
802
+ ]);
803
+ }
768
804
  function AxCloudInnerProvider({
769
805
  children,
770
806
  config,
807
+ queryClient,
771
808
  timeout
772
809
  }) {
773
- const { getAccessTokenSilently, isAuthenticated } = useAuth0();
810
+ const { getAccessTokenSilently, isAuthenticated, isLoading, user } = useAuth0();
774
811
  const analytics = useAnalyticsRuntime(config);
812
+ const [, setOwnerGeneration] = useState(0);
813
+ const currentSubject = isLoading ? void 0 : isAuthenticated ? user?.sub : null;
814
+ const currentOwnerSignature = currentSubject === void 0 ? void 0 : createQueryClientOwnerSignature(config, currentSubject);
815
+ const hasKnownOwner = queryClientAxCloudOwners.has(queryClient);
816
+ const ownerNeedsActivation = currentOwnerSignature !== void 0 && (!hasKnownOwner || queryClientAxCloudOwners.get(queryClient) !== currentOwnerSignature);
817
+ const clearContentCache = useCallback(() => {
818
+ queryClient.removeQueries({ queryKey: ["axcloud"] });
819
+ }, [queryClient]);
820
+ useLayoutEffect(() => {
821
+ if (currentOwnerSignature === void 0 || !ownerNeedsActivation) return;
822
+ clearContentCache();
823
+ queryClientAxCloudOwners.set(queryClient, currentOwnerSignature);
824
+ setOwnerGeneration((generation) => generation + 1);
825
+ }, [clearContentCache, currentOwnerSignature, ownerNeedsActivation, queryClient]);
775
826
  const tokenGetterRef = useRef(async () => void 0);
776
827
  tokenGetterRef.current = isAuthenticated ? async () => {
777
828
  try {
@@ -801,12 +852,17 @@ function AxCloudInnerProvider({
801
852
  client,
802
853
  orgId: config.orgId,
803
854
  stage: config.stage,
804
- isPublicMode: Boolean(config.apiKey) && !isAuthenticated,
855
+ isPublicMode: !isAuthenticated,
856
+ isAuthLoading: isLoading,
857
+ isAuthenticated,
858
+ authSubject: isAuthenticated ? user?.sub : void 0,
859
+ clearContentCache,
805
860
  analytics
806
861
  }),
807
- [config, client, isAuthenticated, analytics]
862
+ [config, client, isAuthenticated, isLoading, user?.sub, clearContentCache, analytics]
808
863
  );
809
- return /* @__PURE__ */ jsx(AxCloudContext.Provider, { value: contextValue, children });
864
+ const identityReady = currentOwnerSignature !== void 0 && !ownerNeedsActivation;
865
+ return /* @__PURE__ */ jsx(AxCloudContext.Provider, { value: contextValue, children: identityReady ? children : null });
810
866
  }
811
867
  function AxCloudProvider({
812
868
  children,
@@ -816,9 +872,12 @@ function AxCloudProvider({
816
872
  apiKey,
817
873
  analytics,
818
874
  auth0,
819
- queryClient = defaultQueryClient,
875
+ queryClient: providedQueryClient,
820
876
  timeout
821
877
  }) {
878
+ const [ownedQueryClient] = useState(createDefaultQueryClient);
879
+ const queryClient = providedQueryClient || ownedQueryClient;
880
+ const invitationAuthorizationParams = readAuth0InvitationAuthorizationParams(auth0.organization);
822
881
  const config = useMemo(
823
882
  () => ({
824
883
  orgId,
@@ -838,14 +897,15 @@ function AxCloudProvider({
838
897
  authorizationParams: {
839
898
  redirect_uri: auth0.redirectUri,
840
899
  audience: auth0.audience,
841
- ...auth0.organization ? { organization: auth0.organization } : {}
900
+ ...auth0.organization ? { organization: auth0.organization } : {},
901
+ ...invitationAuthorizationParams
842
902
  },
843
- children: /* @__PURE__ */ jsx(AxCloudInnerProvider, { config, timeout, children })
903
+ children: /* @__PURE__ */ jsx(AxCloudInnerProvider, { config, queryClient, timeout, children })
844
904
  }
845
905
  ) });
846
906
  }
847
907
  function useAuth() {
848
- const { config, client } = useAxCloudContext();
908
+ const { config, client, clearContentCache } = useAxCloudContext();
849
909
  const {
850
910
  isAuthenticated,
851
911
  isLoading,
@@ -858,13 +918,17 @@ function useAuth() {
858
918
  } = useAuth0();
859
919
  const login = useCallback(
860
920
  (options) => {
921
+ const invitationAuthorizationParams = readAuth0InvitationAuthorizationParams(
922
+ config.auth0.organization
923
+ );
861
924
  return loginWithRedirect({
862
925
  appState: {
863
926
  returnTo: options?.returnTo || window.location.pathname
864
927
  },
865
928
  ...config.auth0.organization ? {
866
929
  authorizationParams: {
867
- organization: config.auth0.organization
930
+ organization: config.auth0.organization,
931
+ ...invitationAuthorizationParams
868
932
  }
869
933
  } : {}
870
934
  });
@@ -873,13 +937,14 @@ function useAuth() {
873
937
  );
874
938
  const logout = useCallback(
875
939
  (options) => {
940
+ clearContentCache?.();
876
941
  return auth0Logout({
877
942
  logoutParams: {
878
943
  returnTo: options?.returnTo || window.location.origin
879
944
  }
880
945
  });
881
946
  },
882
- [auth0Logout]
947
+ [auth0Logout, clearContentCache]
883
948
  );
884
949
  const getAccessToken = useCallback(async () => {
885
950
  try {
@@ -1030,24 +1095,167 @@ function useAuth() {
1030
1095
  requestPasswordReset
1031
1096
  };
1032
1097
  }
1098
+ var currentUserQueryKey = (stage, orgId, subject) => ["axcloud", "user", "me", stage, orgId, subject];
1099
+ function asRecord(value) {
1100
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
1101
+ }
1102
+ function asString(value) {
1103
+ return typeof value === "string" && value.trim().length > 0 ? value : null;
1104
+ }
1105
+ function asStringArray(value) {
1106
+ if (!Array.isArray(value)) return [];
1107
+ return Array.from(new Set(value.map(asString).filter((item) => item !== null)));
1108
+ }
1109
+ function normalizeMembershipSections(rawMembership) {
1110
+ const sections = /* @__PURE__ */ new Map();
1111
+ const addSection = (idValue, nameValue, roleValue) => {
1112
+ const id = asString(idValue);
1113
+ if (!id) return;
1114
+ const existing = sections.get(id);
1115
+ sections.set(id, {
1116
+ id,
1117
+ name: asString(nameValue) || existing?.name || id,
1118
+ role: asString(roleValue) || existing?.role || "member"
1119
+ });
1120
+ };
1121
+ const directSections = rawMembership.sections;
1122
+ if (Array.isArray(directSections)) {
1123
+ for (const rawSection of directSections) {
1124
+ if (typeof rawSection === "string") {
1125
+ addSection(rawSection);
1126
+ continue;
1127
+ }
1128
+ const section = asRecord(rawSection);
1129
+ addSection(
1130
+ section.id ?? section.sectionId ?? section.section_id,
1131
+ section.name ?? section.sectionName ?? section.section_name,
1132
+ section.role
1133
+ );
1134
+ }
1135
+ }
1136
+ const sectionMemberships = asRecord(
1137
+ rawMembership.sectionMemberships ?? rawMembership.section_memberships
1138
+ );
1139
+ for (const [sectionId, rawSectionMembership] of Object.entries(sectionMemberships)) {
1140
+ const sectionMembership = asRecord(rawSectionMembership);
1141
+ addSection(
1142
+ sectionId,
1143
+ sectionMembership.name ?? sectionMembership.sectionName ?? sectionMembership.section_name,
1144
+ sectionMembership.role
1145
+ );
1146
+ }
1147
+ for (const sectionId of asStringArray(rawMembership.sectionIds ?? rawMembership.section_ids)) {
1148
+ addSection(sectionId);
1149
+ }
1150
+ const primarySectionId = asString(rawMembership.sectionId ?? rawMembership.section_id);
1151
+ if (primarySectionId) addSection(primarySectionId);
1152
+ const normalized = Array.from(sections.values());
1153
+ if (!primarySectionId) return normalized;
1154
+ return normalized.sort((left, right) => {
1155
+ if (left.id === primarySectionId) return -1;
1156
+ if (right.id === primarySectionId) return 1;
1157
+ return 0;
1158
+ });
1159
+ }
1160
+ function normalizeMembership(rawValue) {
1161
+ const raw = asRecord(rawValue);
1162
+ const role = asString(raw.role) || "member";
1163
+ const roles = asStringArray(raw.roles);
1164
+ const sections = normalizeMembershipSections(raw);
1165
+ const sectionId = asString(raw.sectionId ?? raw.section_id) || sections[0]?.id || null;
1166
+ const joinedAt = asString(raw.joinedAt ?? raw.joined_at);
1167
+ return {
1168
+ role,
1169
+ roles: roles.length > 0 ? roles : [role],
1170
+ title: asString(raw.title),
1171
+ status: asString(raw.status),
1172
+ joinedAt,
1173
+ memberSince: asString(raw.memberSince ?? raw.member_since) || joinedAt,
1174
+ sectionId,
1175
+ sectionIds: sections.map((section) => section.id),
1176
+ sections
1177
+ };
1178
+ }
1179
+ function normalizeCurrentUserResponse(raw, configuredOrgId) {
1180
+ const rawMemberships = asRecord(
1181
+ raw.organizationMemberships ?? raw.organization_memberships
1182
+ );
1183
+ const organizationMemberships = Object.fromEntries(
1184
+ Object.entries(rawMemberships).map(([organizationId2, membership]) => [
1185
+ organizationId2,
1186
+ normalizeMembership(membership)
1187
+ ])
1188
+ );
1189
+ const allOrgIds = Array.from(
1190
+ /* @__PURE__ */ new Set([
1191
+ ...asStringArray(raw.allOrgIds ?? raw.all_org_ids),
1192
+ ...Object.keys(organizationMemberships)
1193
+ ])
1194
+ );
1195
+ const effectiveOrganizationId = asString(
1196
+ raw.effectiveOrganizationId ?? raw.effective_org_id
1197
+ );
1198
+ const primaryOrganizationId = asString(
1199
+ raw.organizationId ?? raw.primary_org_id ?? raw.org_id
1200
+ );
1201
+ const organizationId = effectiveOrganizationId || primaryOrganizationId || (organizationMemberships[configuredOrgId] ? configuredOrgId : null) || allOrgIds[0] || configuredOrgId;
1202
+ const configuredMembership = organizationMemberships[configuredOrgId];
1203
+ const firstName = asString(raw.firstName ?? raw.first_name ?? raw.given_name) || "";
1204
+ const lastName = asString(raw.lastName ?? raw.last_name ?? raw.family_name) || "";
1205
+ const email = asString(raw.email) || "";
1206
+ const createdAt = asString(raw.createdAt ?? raw.created_at) || "";
1207
+ const updatedAt = asString(raw.updatedAt ?? raw.updated_at) || createdAt;
1208
+ const role = configuredMembership?.role || asString(raw.role) || "member";
1209
+ const sectionId = configuredMembership?.sectionId || asString(raw.sectionId ?? raw.section_id);
1210
+ const permissions = asStringArray(raw.permissions ?? raw.effective_permissions);
1211
+ const metadata = asRecord(raw.metadata);
1212
+ return {
1213
+ id: asString(raw.id ?? raw.userId ?? raw.user_id ?? raw.sub) || "",
1214
+ email,
1215
+ name: asString(raw.name) || [firstName, lastName].filter(Boolean).join(" ") || email,
1216
+ firstName,
1217
+ lastName,
1218
+ title: configuredMembership?.title ?? asString(raw.title),
1219
+ phone: asString(raw.phone ?? raw.phoneNumber ?? raw.phone_number),
1220
+ address: asString(raw.address ?? raw.location),
1221
+ avatar: asString(raw.avatar ?? raw.avatarUrl ?? raw.avatar_url ?? raw.picture) || void 0,
1222
+ organizationId,
1223
+ ...sectionId ? { sectionId } : {},
1224
+ role,
1225
+ permissions,
1226
+ ...Object.keys(metadata).length > 0 ? { metadata } : {},
1227
+ createdAt,
1228
+ updatedAt,
1229
+ allOrgIds,
1230
+ organizationMemberships,
1231
+ effectiveOrganizationId,
1232
+ isSuperAdmin: raw.isSuperAdmin === true || raw.is_super_admin === true || asString(raw.global_role) === "super_admin" || role === "super_admin"
1233
+ };
1234
+ }
1033
1235
  function useUser() {
1034
1236
  const { isAuthenticated, isLoading: isAuthLoading, user: auth0User } = useAuth0();
1035
1237
  const { client, stage, orgId } = useAxCloudContext();
1238
+ const queryKey = currentUserQueryKey(stage, orgId, auth0User?.sub);
1036
1239
  const query = useQuery({
1037
- queryKey: ["axcloud", "user", "me", stage, orgId, auth0User?.sub],
1240
+ queryKey,
1038
1241
  queryFn: async () => {
1039
- return client.get("/api/users/me");
1242
+ const response = await client.get("/api/users/me");
1243
+ return normalizeCurrentUserResponse(response, orgId);
1040
1244
  },
1041
1245
  enabled: isAuthenticated && !isAuthLoading,
1042
1246
  staleTime: 1e3 * 60 * 5
1043
1247
  // 5 minutes
1044
1248
  });
1045
1249
  const userData = query.data;
1046
- const userOrgIds = userData?.all_org_ids ?? [];
1250
+ const userOrgIds = userData?.allOrgIds ?? [];
1047
1251
  const isSuperAdmin = userData?.isSuperAdmin === true;
1048
- const hasOrgAccess = userOrgIds.includes(orgId);
1049
- const orgMembership = userData?.organization_memberships?.[orgId];
1252
+ const orgMembership = userData?.organizationMemberships?.[orgId];
1253
+ const hasOrgAccess = userOrgIds.includes(orgId) && orgMembership?.status?.trim().toLowerCase() === "active";
1050
1254
  const roleInOrg = orgMembership?.role ?? null;
1255
+ const sectionId = orgMembership?.sectionId ?? userData?.sectionId ?? null;
1256
+ const sections = orgMembership?.sections?.length ? orgMembership.sections : sectionId ? [{ id: sectionId, name: sectionId, role: roleInOrg ?? "member" }] : [];
1257
+ const section = sections.find((item) => item.id === sectionId) ?? sections[0] ?? null;
1258
+ const memberSince = orgMembership?.memberSince ?? null;
1051
1259
  return {
1052
1260
  user: query.data ?? null,
1053
1261
  isLoading: isAuthLoading || query.isLoading,
@@ -1057,15 +1265,35 @@ function useUser() {
1057
1265
  // Organization access
1058
1266
  hasOrgAccess,
1059
1267
  roleInOrg,
1268
+ organizationMembership: orgMembership ?? null,
1269
+ title: orgMembership?.title ?? query.data?.title ?? null,
1270
+ section,
1271
+ sections,
1272
+ sectionIds: orgMembership?.sectionIds?.length ? orgMembership.sectionIds : sectionId ? [sectionId] : [],
1273
+ joinedAt: orgMembership?.joinedAt ?? null,
1274
+ memberSince,
1060
1275
  // Convenience properties
1061
1276
  isAuthenticated,
1062
- role: query.data?.role ?? null,
1277
+ role: roleInOrg ?? query.data?.role ?? null,
1063
1278
  organizationId: query.data?.organizationId ?? null,
1064
- sectionId: query.data?.sectionId ?? null,
1279
+ sectionId,
1065
1280
  permissions: query.data?.permissions ?? [],
1066
1281
  isSuperAdmin
1067
1282
  };
1068
1283
  }
1284
+ function useUpdateMyProfile() {
1285
+ const { client, stage } = useAxCloudContext();
1286
+ const queryClient = useQueryClient();
1287
+ return useMutation({
1288
+ mutationFn: (updates) => client.patch("/api/users/me/profile", updates),
1289
+ onSuccess: async () => {
1290
+ await queryClient.invalidateQueries({
1291
+ queryKey: ["axcloud", "user", "me", stage],
1292
+ exact: false
1293
+ });
1294
+ }
1295
+ });
1296
+ }
1069
1297
  function useOrg() {
1070
1298
  const { client, orgId, stage, isPublicMode } = useAxCloudContext();
1071
1299
  const usePublicEndpoint = isPublicMode;
@@ -1112,152 +1340,169 @@ function useOrg() {
1112
1340
  modules
1113
1341
  };
1114
1342
  }
1115
- function useNews(options = {}) {
1116
- const { client, orgId, isPublicMode } = useAxCloudContext();
1117
- const {
1118
- page = 1,
1119
- pageSize = 20,
1120
- status = "published",
1121
- visibility,
1122
- sectionId,
1123
- search,
1124
- sortBy = "publishedAt",
1125
- sortOrder = "desc",
1126
- lang,
1127
- publicOnly = false
1128
- } = options;
1129
- const usePublicEndpoint = isPublicMode || publicOnly;
1130
- const query = useQuery({
1131
- queryKey: [
1132
- "axcloud",
1133
- "news",
1134
- orgId,
1135
- usePublicEndpoint,
1136
- page,
1137
- pageSize,
1138
- status,
1139
- visibility,
1140
- sectionId,
1141
- search,
1142
- sortBy,
1143
- sortOrder,
1144
- lang
1145
- ],
1146
- queryFn: async () => {
1147
- const endpoint = usePublicEndpoint ? `/content/public/${orgId}/news` : `/content/members/${orgId}/news`;
1148
- return client.get(endpoint, {
1149
- page,
1150
- pageSize,
1151
- status,
1152
- visibility,
1153
- sectionId,
1154
- search,
1155
- sortBy,
1156
- sortOrder,
1157
- lang
1158
- });
1159
- },
1160
- staleTime: 1e3 * 60 * 5
1161
- // 5 minutes
1162
- });
1163
- return {
1164
- data: query.data?.items ?? [],
1165
- total: query.data?.total ?? 0,
1166
- hasMore: query.data?.hasMore ?? false,
1167
- isLoading: query.isLoading,
1168
- isError: query.isError,
1169
- error: query.error,
1170
- refetch: query.refetch
1171
- };
1343
+
1344
+ // src/react/contentQueryPolicy.ts
1345
+ var CONTENT_STALE_TIME_MS = 6e4;
1346
+ function shouldRetryContentRequest(failureCount, error) {
1347
+ if (isAxCloudError(error) && error.status >= 400 && error.status < 500) {
1348
+ return false;
1349
+ }
1350
+ return failureCount < 2;
1351
+ }
1352
+ function normalizeContentCollectionType(contentType) {
1353
+ switch (contentType.toLowerCase()) {
1354
+ case "news":
1355
+ return "news";
1356
+ case "page":
1357
+ case "pages":
1358
+ return "pages";
1359
+ case "event":
1360
+ case "events":
1361
+ return "events";
1362
+ default:
1363
+ throw new Error(`Unsupported AxCloud content collection type: ${contentType}`);
1364
+ }
1365
+ }
1366
+ function normalizeContentDetailType(contentType) {
1367
+ switch (contentType.toLowerCase()) {
1368
+ case "news":
1369
+ return "news";
1370
+ case "page":
1371
+ case "pages":
1372
+ return "page";
1373
+ case "event":
1374
+ case "events":
1375
+ return "event";
1376
+ default:
1377
+ throw new Error(`Unsupported AxCloud content detail type: ${contentType}`);
1378
+ }
1172
1379
  }
1173
- var MEMBER_ENDPOINT_TYPES = ["news", "pages", "events"];
1174
- var PAGE_CONTENT_TYPES = ["page", "pages"];
1380
+ function getContentAccessState(context, publicOnly) {
1381
+ if (publicOnly) {
1382
+ return { mode: "public", enabled: true, subject: "anonymous" };
1383
+ }
1384
+ const isLoading = context.isAuthLoading ?? false;
1385
+ if (isLoading) {
1386
+ return { mode: "pending", enabled: false, subject: "auth-loading" };
1387
+ }
1388
+ const hasExplicitAuthState = typeof context.isAuthenticated === "boolean";
1389
+ const isAuthenticated = context.isAuthenticated ?? !context.isPublicMode;
1390
+ if (!isAuthenticated) {
1391
+ return { mode: "public", enabled: true, subject: "anonymous" };
1392
+ }
1393
+ if (hasExplicitAuthState && !context.authSubject) {
1394
+ return { mode: "pending", enabled: false, subject: "auth-subject-pending" };
1395
+ }
1396
+ const subject = context.authSubject || "legacy-authenticated-session";
1397
+ return { mode: "member", enabled: Boolean(subject), subject };
1398
+ }
1399
+
1400
+ // src/react/hooks/useContent.ts
1175
1401
  function useContent(contentType, options = {}) {
1176
- const { client, orgId, isPublicMode } = useAxCloudContext();
1402
+ const context = useAxCloudContext();
1403
+ const { client, config, orgId, stage } = context;
1404
+ const collectionType = normalizeContentCollectionType(contentType);
1177
1405
  const {
1178
1406
  publicOnly = false,
1179
1407
  page = 1,
1180
1408
  pageSize = 20,
1181
- status = "published",
1182
- visibility,
1183
- sectionId,
1184
1409
  search,
1185
- sortBy = "createdAt",
1410
+ sortBy = collectionType === "news" ? "publishedAt" : "createdAt",
1186
1411
  sortOrder = "desc",
1412
+ tags,
1187
1413
  lang
1188
1414
  } = options;
1415
+ const access = getContentAccessState(context, publicOnly);
1416
+ const deliveryScope = access.mode === "member" ? "members" : "public";
1417
+ const params = {
1418
+ page,
1419
+ pageSize,
1420
+ search,
1421
+ sort: sortBy,
1422
+ order: sortOrder,
1423
+ tags: tags?.length ? tags.join(",") : void 0,
1424
+ lang
1425
+ };
1189
1426
  const query = useQuery({
1190
1427
  queryKey: [
1191
1428
  "axcloud",
1192
1429
  "content",
1430
+ "collection",
1431
+ stage,
1432
+ config.apiUrl,
1193
1433
  orgId,
1194
- contentType,
1195
- isPublicMode,
1196
- publicOnly,
1197
- page,
1198
- pageSize,
1199
- status,
1200
- visibility,
1201
- sectionId,
1202
- search,
1203
- sortBy,
1204
- sortOrder,
1205
- lang
1434
+ access.mode,
1435
+ access.subject,
1436
+ collectionType,
1437
+ params
1206
1438
  ],
1207
- queryFn: async () => {
1208
- const useMemberEndpoint = !isPublicMode && !publicOnly && MEMBER_ENDPOINT_TYPES.includes(contentType);
1209
- const endpoint = useMemberEndpoint ? `/content/members/${orgId}/${contentType}` : `/content/public/${orgId}/${contentType}`;
1210
- return client.get(endpoint, {
1211
- page,
1212
- pageSize,
1213
- status,
1214
- visibility,
1215
- sectionId,
1216
- search,
1217
- sortBy,
1218
- sortOrder,
1219
- lang
1220
- });
1221
- },
1222
- staleTime: 1e3 * 60 * 5
1223
- // 5 minutes
1439
+ queryFn: () => client.get(
1440
+ `/content/${deliveryScope}/${orgId}/${collectionType}`,
1441
+ params
1442
+ ),
1443
+ enabled: access.enabled,
1444
+ staleTime: CONTENT_STALE_TIME_MS,
1445
+ refetchOnWindowFocus: true,
1446
+ retry: shouldRetryContentRequest
1224
1447
  });
1448
+ const items = query.data?.items ?? [];
1225
1449
  return {
1226
- data: query.data?.items ?? [],
1227
- total: query.data?.total ?? 0,
1450
+ data: items,
1451
+ count: query.data?.count ?? items.length,
1452
+ total: query.data?.total ?? items.length,
1453
+ page: query.data?.page ?? page,
1454
+ pageSize: query.data?.pageSize ?? pageSize,
1228
1455
  hasMore: query.data?.hasMore ?? false,
1229
- isLoading: query.isLoading,
1456
+ nextCursor: query.data?.nextCursor,
1457
+ isLoading: access.mode === "pending" || query.isLoading,
1458
+ isFetching: query.isFetching,
1230
1459
  isError: query.isError,
1231
1460
  error: query.error,
1232
1461
  refetch: query.refetch
1233
1462
  };
1234
1463
  }
1235
1464
  function useContentItem(contentType, idOrSlug, options = {}) {
1236
- const { client, orgId, isPublicMode } = useAxCloudContext();
1465
+ const context = useAxCloudContext();
1466
+ const { client, config, orgId, stage } = context;
1467
+ const detailType = normalizeContentDetailType(contentType);
1237
1468
  const { publicOnly = false, lang } = options;
1238
- const useMemberEndpoint = !isPublicMode && !publicOnly && (MEMBER_ENDPOINT_TYPES.includes(contentType) || PAGE_CONTENT_TYPES.includes(contentType));
1469
+ const access = getContentAccessState(context, publicOnly);
1470
+ const deliveryScope = access.mode === "member" ? "members" : "public";
1239
1471
  const query = useQuery({
1240
- queryKey: ["axcloud", "content", orgId, contentType, idOrSlug, useMemberEndpoint, lang],
1241
- queryFn: async () => {
1242
- const basePath = useMemberEndpoint ? "members" : "public";
1472
+ queryKey: [
1473
+ "axcloud",
1474
+ "content",
1475
+ "item",
1476
+ stage,
1477
+ config.apiUrl,
1478
+ orgId,
1479
+ access.mode,
1480
+ access.subject,
1481
+ detailType,
1482
+ idOrSlug,
1483
+ lang
1484
+ ],
1485
+ queryFn: () => {
1243
1486
  const encodedIdOrSlug = encodeURIComponent(idOrSlug);
1244
- const endpoint = PAGE_CONTENT_TYPES.includes(contentType) ? `/content/${basePath}/${orgId}/page/${encodedIdOrSlug}` : `/content/${basePath}/${orgId}/${contentType}/${encodedIdOrSlug}`;
1487
+ const endpoint = `/content/${deliveryScope}/${orgId}/items/${detailType}/${encodedIdOrSlug}`;
1245
1488
  return lang ? client.get(endpoint, { lang }) : client.get(endpoint);
1246
1489
  },
1247
- enabled: !!idOrSlug,
1248
- staleTime: 1e3 * 60 * 5
1249
- // 5 minutes
1490
+ enabled: access.enabled && Boolean(idOrSlug),
1491
+ staleTime: CONTENT_STALE_TIME_MS,
1492
+ refetchOnWindowFocus: true,
1493
+ retry: shouldRetryContentRequest
1250
1494
  });
1251
1495
  return {
1252
1496
  data: query.data ?? null,
1253
- isLoading: query.isLoading,
1497
+ isLoading: access.mode === "pending" || query.isLoading,
1498
+ isFetching: query.isFetching,
1254
1499
  isError: query.isError,
1255
1500
  error: query.error,
1256
1501
  refetch: query.refetch
1257
1502
  };
1258
1503
  }
1259
1504
  function usePageContent(slug, options = {}) {
1260
- const state = useContentItem("pages", slug, options);
1505
+ const state = useContentItem("page", slug, options);
1261
1506
  const reader = useMemo(() => createContentSlotReader(state.data), [state.data]);
1262
1507
  return {
1263
1508
  ...state,
@@ -1265,6 +1510,11 @@ function usePageContent(slug, options = {}) {
1265
1510
  ...reader
1266
1511
  };
1267
1512
  }
1513
+
1514
+ // src/react/hooks/useNews.ts
1515
+ function useNews(options = {}) {
1516
+ return useContent("news", options);
1517
+ }
1268
1518
  function useMySection(options = {}) {
1269
1519
  const { includeMembers = true } = options;
1270
1520
  const { isAuthenticated } = useAuth0();
@@ -2093,6 +2343,6 @@ function LinkedAccountsList({
2093
2343
  ] });
2094
2344
  }
2095
2345
 
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 };
2346
+ export { AxCloudClient, AxCloudError, AxCloudProvider, LinkedAccountsList, LoginButton, LogoutButton, ModuleGate, ProtectedRoute, castVote, castVoteWithCode, checkEligibility, checkVotingCode, createClient, createContentSlotReader, createFormHelper, fetchAnalyticsPublicConfig, getBallot, getClient, getContentSlotMap, getVotingReceipt, isAllowedAnalyticsEvent, isAxCloudError, isLocalAnalyticsHostname, sanitizeAnalyticsEventData, sanitizeAnalyticsPath, submitForm, useAnalytics, useAnalyticsRuntime, useAuth, useAvatar, useAxCloudContext, useBarnsportActivities, useBarnsportActivity, useBarnsportMunicipalities, useBarnsportSitemap, useBarnsportSport, useBarnsportSports, useBranding, useCodeBallot, useContent, useContentItem, useDocumentDownload, useDocuments, useFormStats, useForms, useLinkedAccounts, useLogo, useMedia, useMediaList, useMemberDocumentDownload, useMemberDocumentFolders, useMemberDocuments, useModuleAccess, useMySection, useNews, useOrg, usePageContent, useSections, useSubmitBarnsportInterest, useUpdateFormSubmission, useUpdateMyProfile, useUser, userHasAllPermissions, userHasAnyPermission, userHasPermission };
2097
2347
  //# sourceMappingURL=index.mjs.map
2098
2348
  //# sourceMappingURL=index.mjs.map