@nexushub/client 1.1.6 → 1.1.7

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/react.js CHANGED
@@ -829,14 +829,13 @@ var ContentEngine = class {
829
829
  this.localCache = new LocalCacheProxy();
830
830
  this.memoryCache = new MemoryCache({
831
831
  maxSize: 500,
832
- ttl: 5 * 60 * 1e3
833
- // 5 minutes
832
+ ttl: 60 * 1e3
834
833
  });
835
834
  if (!this.isServer) {
836
835
  this.browserCache = new BrowserCache(config.projectId);
837
836
  }
838
837
  this.rateLimiter = new RateLimiter({
839
- maxRequests: config.debug ? 100 : 50,
838
+ maxRequests: config.debug ? 200 : 100,
840
839
  timeWindow: 6e4
841
840
  });
842
841
  this.backoff = new ExponentialBackoff({
@@ -853,15 +852,11 @@ var ContentEngine = class {
853
852
  window.addEventListener("beforeunload", this.cleanup.bind(this));
854
853
  }
855
854
  }
856
- /** Update runtime configuration without exposing internal mutation. */
857
855
  updateConfig(config) {
858
856
  this.config = config;
859
857
  this.defaultRevalidate = config.revalidateTime ?? false;
860
858
  this.cacheStrategy = config.cacheStrategy || "memory";
861
859
  }
862
- /**
863
- * Fetch a Single Page with full strategy pipeline
864
- */
865
860
  async getPage(slug, options = {}) {
866
861
  const { result, duration } = measurePerformance(
867
862
  `getPage("${slug}")`,
@@ -883,66 +878,66 @@ var ContentEngine = class {
883
878
  includeMetadata = false
884
879
  } = options;
885
880
  const cacheKey = `page:${slug}`;
886
- const cached = await this.checkCaches(
887
- cacheKey,
888
- forceRefresh,
889
- includeMetadata
890
- );
891
- if (cached) {
892
- if (this.config.debug) console.log(`[NexusHub] \u26A1 Cache hit: ${slug}`);
893
- return includeMetadata ? cached : cached.data;
881
+ if (!this.isServer && !forceRefresh && this.cacheStrategy === "memory") {
882
+ const cached = this.memoryCache.get(cacheKey);
883
+ if (cached && this.isCacheValid(cached.metadata)) {
884
+ return includeMetadata ? cached : cached.data;
885
+ }
886
+ }
887
+ if (!forceRefresh && process.env.NODE_ENV === "development") {
888
+ try {
889
+ const local = await this.localCache.getPage(slug);
890
+ if (local) {
891
+ return includeMetadata ? {
892
+ data: local,
893
+ metadata: {
894
+ timestamp: Date.now(),
895
+ expiresAt: Infinity,
896
+ tags: []
897
+ }
898
+ } : local;
899
+ }
900
+ } catch {
901
+ }
894
902
  }
895
903
  if (this.circuitBreaker.isOpen()) {
896
- throw new Error(`[NexusHub] Circuit open. API is unavailable.`);
904
+ throw new Error("[NexusHub] Circuit open. API is unavailable.");
897
905
  }
898
906
  try {
899
907
  await this.rateLimiter.checkLimit();
900
- const data = await this.backoff.execute(
901
- async () => {
902
- return this.fetchPage(
903
- slug,
904
- cacheKey,
905
- tags,
906
- revalidate,
907
- forceRefresh
908
- );
909
- },
910
- (attempt, delay, error) => {
911
- if (this.config.debug) {
912
- console.log(
913
- `[NexusHub] \u{1F504} Retry ${attempt} for '${slug}' after ${delay}ms: ${error.message}`
914
- );
915
- }
916
- }
917
- );
908
+ const data = await this.backoff.execute(async () => {
909
+ return this.fetchPage(
910
+ slug,
911
+ cacheKey,
912
+ tags,
913
+ revalidate,
914
+ forceRefresh
915
+ );
916
+ });
918
917
  this.circuitBreaker.recordSuccess();
919
918
  return includeMetadata ? data : data.data;
920
919
  } catch (error) {
921
920
  this.circuitBreaker.recordFailure();
922
- const staleCache = this.memoryCache.get(cacheKey);
923
- if (staleCache && !forceRefresh) {
924
- console.warn(`[NexusHub] \u26A0\uFE0F Serving stale content for '${slug}'`);
925
- return includeMetadata ? staleCache : staleCache.data;
921
+ if (process.env.NODE_ENV === "development") {
922
+ try {
923
+ const local = await this.localCache.getPage(slug);
924
+ if (local) {
925
+ return includeMetadata ? {
926
+ data: local,
927
+ metadata: {
928
+ timestamp: Date.now(),
929
+ expiresAt: Infinity,
930
+ tags: []
931
+ }
932
+ } : local;
933
+ }
934
+ } catch {
935
+ }
926
936
  }
927
937
  throw this.normalizeError(error, `Failed to fetch page '${slug}'`);
928
938
  }
929
939
  }
930
- /**
931
- * Fetch a Collection (Optimized)
932
- */
933
940
  async getCollection(collectionId, query = {}, options = {}) {
934
- const { result, duration } = measurePerformance(
935
- `getCollection("${collectionId}")`,
936
- () => this._getCollection(collectionId, query, options)
937
- );
938
- if (this.config.debug && duration > 100) {
939
- console.warn(
940
- `[NexusHub] \u26A0\uFE0F getCollection("${collectionId}") took ${duration.toFixed(2)}ms`
941
- );
942
- }
943
- return result;
944
- }
945
- async _getCollection(collectionId, query = {}, options = {}) {
946
941
  const normalizedQuery = normalizeQuery(query);
947
942
  const {
948
943
  revalidate = this.defaultRevalidate,
@@ -952,20 +947,13 @@ var ContentEngine = class {
952
947
  } = options;
953
948
  const queryString = buildQueryString(normalizedQuery);
954
949
  const cacheKey = `collection:${collectionId}:${queryString}`;
955
- const cached = await this.checkCaches(
956
- cacheKey,
957
- forceRefresh,
958
- includeMetadata
959
- );
960
- if (cached) {
961
- if (this.config.debug) {
962
- console.log(
963
- `[NexusHub] \u26A1 Served collection '${collectionId}' from memory cache.`
964
- );
950
+ if (!this.isServer && !forceRefresh && this.cacheStrategy === "memory") {
951
+ const cached = this.memoryCache.get(cacheKey);
952
+ if (cached && this.isCacheValid(cached.metadata)) {
953
+ return includeMetadata ? cached : cached.data;
965
954
  }
966
- return includeMetadata ? cached : cached.data;
967
955
  }
968
- if (!forceRefresh) {
956
+ if (!forceRefresh && process.env.NODE_ENV === "development") {
969
957
  try {
970
958
  const localCollection = await this.localCache.getCollection(collectionId);
971
959
  if (localCollection) {
@@ -973,21 +961,14 @@ var ContentEngine = class {
973
961
  localCollection,
974
962
  normalizedQuery
975
963
  );
976
- if (this.config.debug) {
977
- console.log(
978
- `[NexusHub] \u{1F4C1} Compiled collection '${collectionId}' from local offline binaries.`
979
- );
980
- }
981
- this.memoryCache.set(cacheKey, result, {
982
- tags: [...tags, CacheTags.collection(collectionId)],
983
- revalidate: revalidate === false ? void 0 : revalidate
984
- });
985
964
  return result;
986
965
  }
987
966
  } catch {
988
967
  }
989
968
  }
990
- if (this.circuitBreaker.isOpen()) throw new Error("Circuit open");
969
+ if (this.circuitBreaker.isOpen()) {
970
+ throw new Error("[NexusHub] Circuit open. API is unavailable.");
971
+ }
991
972
  try {
992
973
  await this.rateLimiter.checkLimit();
993
974
  const result = await this.backoff.execute(async () => {
@@ -996,19 +977,25 @@ var ContentEngine = class {
996
977
  normalizedQuery,
997
978
  cacheKey,
998
979
  tags,
999
- revalidate
980
+ revalidate,
981
+ forceRefresh
1000
982
  );
1001
983
  });
1002
984
  this.circuitBreaker.recordSuccess();
1003
985
  return includeMetadata ? result : result.data;
1004
986
  } catch (error) {
1005
987
  this.circuitBreaker.recordFailure();
1006
- const staleCache = this.memoryCache.get(cacheKey);
1007
- if (staleCache && !forceRefresh) {
1008
- console.warn(
1009
- `[NexusHub] \u26A0\uFE0F Using stale cache for collection '${collectionId}'`
1010
- );
1011
- return includeMetadata ? staleCache : staleCache.data;
988
+ if (process.env.NODE_ENV === "development") {
989
+ try {
990
+ const localCollection = await this.localCache.getCollection(collectionId);
991
+ if (localCollection) {
992
+ return this.applyLocalQuery(
993
+ localCollection,
994
+ normalizedQuery
995
+ );
996
+ }
997
+ } catch {
998
+ }
1012
999
  }
1013
1000
  throw this.normalizeError(
1014
1001
  error,
@@ -1016,9 +1003,6 @@ var ContentEngine = class {
1016
1003
  );
1017
1004
  }
1018
1005
  }
1019
- /**
1020
- * Fetch Global Settings
1021
- */
1022
1006
  async getGlobals(options = {}) {
1023
1007
  const {
1024
1008
  include = [],
@@ -1026,28 +1010,16 @@ var ContentEngine = class {
1026
1010
  forceRefresh = false
1027
1011
  } = options;
1028
1012
  const cacheKey = `globals:${include.join(",")}`;
1029
- if (!forceRefresh && this.cacheStrategy === "memory") {
1013
+ if (!this.isServer && !forceRefresh && this.cacheStrategy === "memory") {
1030
1014
  const cached = this.memoryCache.get(cacheKey);
1031
1015
  if (cached && this.isCacheValid(cached.metadata)) {
1032
- if (this.config.debug)
1033
- console.log("[NexusHub] \u26A1 Served globals from memory cache.");
1034
1016
  return cached.data;
1035
1017
  }
1036
1018
  }
1037
- if (!forceRefresh && !this.isServer && this.cacheStrategy === "localStorage" && this.browserCache) {
1038
- const cached = this.browserCache.get(cacheKey);
1039
- if (cached) {
1040
- return cached;
1041
- }
1042
- }
1043
- if (!forceRefresh) {
1019
+ if (!forceRefresh && process.env.NODE_ENV === "development") {
1044
1020
  try {
1045
1021
  const localGlobals = await this.localCache.getGlobals();
1046
1022
  if (localGlobals) {
1047
- this.memoryCache.set(cacheKey, localGlobals, {
1048
- tags: [CacheTags.global],
1049
- revalidate: revalidate === false ? void 0 : revalidate
1050
- });
1051
1023
  return localGlobals;
1052
1024
  }
1053
1025
  } catch {
@@ -1066,80 +1038,79 @@ var ContentEngine = class {
1066
1038
  method: "GET",
1067
1039
  headers: this.getHeaders(),
1068
1040
  tags: fetchTags,
1069
- revalidate
1041
+ revalidate,
1042
+ forceRefresh
1070
1043
  });
1071
1044
  if (!res.ok) {
1072
- if (res.status === 408) {
1073
- throw new Error("Request timeout");
1074
- }
1075
1045
  throw new Error(`API Error ${res.status}: ${res.statusText}`);
1076
1046
  }
1077
1047
  const json = await res.json();
1078
1048
  const data = json.data || json;
1079
- this.writeCache(cacheKey, data, {
1080
- revalidate,
1081
- tags: [CacheTags.project(this.config.projectId), CacheTags.global]
1082
- });
1049
+ if (!this.isServer) {
1050
+ this.writeCache(cacheKey, data, {
1051
+ revalidate,
1052
+ tags: fetchTags
1053
+ });
1054
+ }
1083
1055
  return data;
1084
1056
  } catch (error) {
1085
- const staleCache = this.memoryCache.get(cacheKey);
1086
- if (staleCache && !forceRefresh) {
1087
- console.warn("[NexusHub] \u26A0\uFE0F Using stale cache for globals");
1088
- return staleCache.data;
1057
+ if (process.env.NODE_ENV === "development") {
1058
+ try {
1059
+ const localGlobals = await this.localCache.getGlobals();
1060
+ if (localGlobals) {
1061
+ return localGlobals;
1062
+ }
1063
+ } catch {
1064
+ }
1089
1065
  }
1090
1066
  throw this.normalizeError(error, "Failed to fetch globals");
1091
1067
  }
1092
1068
  }
1093
- /**
1094
- * Get a single item from a collection (Uses Request Batching)
1095
- */
1096
1069
  async getItem(collectionId, itemId, options = {}) {
1070
+ const { forceRefresh = false } = options;
1097
1071
  const cacheKey = `item:${collectionId}:${itemId}`;
1098
- if (this.cacheStrategy === "memory") {
1072
+ if (!this.isServer && !forceRefresh && this.cacheStrategy === "memory") {
1099
1073
  const cached = this.memoryCache.get(cacheKey);
1100
1074
  if (cached && this.isCacheValid(cached.metadata)) {
1101
- if (this.config.debug) {
1102
- console.log(`[NexusHub] \u26A1 Served item '${itemId}' from cache.`);
1103
- }
1104
1075
  return cached.data;
1105
1076
  }
1106
1077
  }
1107
- return this.requestBatcher.schedule(cacheKey, async () => {
1108
- const params = new URLSearchParams();
1109
- if (options.include?.length) {
1110
- params.append("include", options.include.join(","));
1111
- }
1112
- const url = `${this.config.apiUrl}/content/${this.config.projectId}/collection/${collectionId}/${itemId}?${params}`;
1113
- const fetchTags = [
1114
- CacheTags.project(this.config.projectId),
1115
- CacheTags.collection(collectionId),
1116
- `item_${itemId}`,
1117
- ...options.tags || []
1118
- ];
1119
- const res = await this.fetchWithTimeout(url, {
1120
- method: "GET",
1121
- headers: this.getHeaders(),
1122
- tags: fetchTags,
1123
- revalidate: options.revalidate ?? this.defaultRevalidate
1124
- });
1125
- if (!res.ok) {
1126
- if (res.status === 408) {
1127
- throw new Error("Request timeout");
1078
+ return this.requestBatcher.schedule(
1079
+ forceRefresh ? `${cacheKey}:refresh:${Date.now()}` : cacheKey,
1080
+ async () => {
1081
+ const params = new URLSearchParams();
1082
+ if (options.include?.length) {
1083
+ params.append("include", options.include.join(","));
1128
1084
  }
1129
- throw new Error(`API Error ${res.status}: ${res.statusText}`);
1085
+ const url = `${this.config.apiUrl}/content/${this.config.projectId}/collection/${collectionId}/${itemId}?${params}`;
1086
+ const fetchTags = [
1087
+ CacheTags.project(this.config.projectId),
1088
+ CacheTags.collection(collectionId),
1089
+ `item_${itemId}`,
1090
+ ...options.tags || []
1091
+ ];
1092
+ const res = await this.fetchWithTimeout(url, {
1093
+ method: "GET",
1094
+ headers: this.getHeaders(),
1095
+ tags: fetchTags,
1096
+ revalidate: options.revalidate ?? this.defaultRevalidate,
1097
+ forceRefresh
1098
+ });
1099
+ if (!res.ok) {
1100
+ throw new Error(`API Error ${res.status}: ${res.statusText}`);
1101
+ }
1102
+ const json = await res.json();
1103
+ const data = json.data || json;
1104
+ if (!this.isServer) {
1105
+ this.writeCache(cacheKey, data, {
1106
+ revalidate: options.revalidate ?? this.defaultRevalidate,
1107
+ tags: fetchTags
1108
+ });
1109
+ }
1110
+ return data;
1130
1111
  }
1131
- const json = await res.json();
1132
- const data = json.data || json;
1133
- this.writeCache(cacheKey, data, {
1134
- revalidate: options.revalidate ?? this.defaultRevalidate,
1135
- tags: fetchTags
1136
- });
1137
- return data;
1138
- });
1112
+ );
1139
1113
  }
1140
- /**
1141
- * Search across collections
1142
- */
1143
1114
  async search(query, options = {}) {
1144
1115
  const params = new URLSearchParams({
1145
1116
  q: query,
@@ -1152,65 +1123,61 @@ var ContentEngine = class {
1152
1123
  params.append("fields", options.fields.join(","));
1153
1124
  }
1154
1125
  const url = `${this.config.apiUrl}/content/${this.config.projectId}/search?${params}`;
1126
+ const fetchTags = [
1127
+ CacheTags.project(this.config.projectId),
1128
+ ...(options.collections || []).map(
1129
+ (collection) => CacheTags.collection(collection)
1130
+ )
1131
+ ];
1155
1132
  const res = await this.fetchWithTimeout(url, {
1156
1133
  method: "GET",
1157
- headers: this.getHeaders()
1134
+ headers: this.getHeaders(),
1135
+ tags: fetchTags,
1136
+ revalidate: options.revalidate ?? this.defaultRevalidate,
1137
+ forceRefresh: options.forceRefresh ?? false
1158
1138
  });
1159
1139
  if (!res.ok) {
1160
- if (res.status === 408) {
1161
- throw new Error("Request timeout");
1162
- }
1163
1140
  throw new Error(`API Error ${res.status}: ${res.statusText}`);
1164
1141
  }
1165
1142
  return await res.json();
1166
1143
  }
1167
- /**
1168
- * Prefetch content
1169
- */
1170
1144
  async prefetch(urls) {
1171
1145
  if (typeof window !== "undefined" && "requestIdleCallback" in window) {
1172
1146
  requestIdleCallback(async () => {
1173
1147
  await Promise.allSettled(
1174
- urls.map((url) => fetch(url, { priority: "low" }))
1148
+ urls.map(
1149
+ (url) => fetch(url, {
1150
+ priority: "low"
1151
+ })
1152
+ )
1175
1153
  );
1176
1154
  });
1177
1155
  }
1178
1156
  }
1179
- /**
1180
- * Robust Real-time Subscriptions (SSE) with Auto-Reconnect
1181
- */
1182
1157
  subscribeToUpdates(callback) {
1183
1158
  if (this.isServer || typeof EventSource === "undefined") {
1184
- if (this.config.debug) {
1185
- console.warn(
1186
- "[NexusHub] subscribeToUpdates() called in a non-browser environment."
1187
- );
1188
- }
1189
1159
  return () => {
1190
1160
  };
1191
1161
  }
1192
1162
  if (!this.config.apiKey) {
1193
- console.warn("[NexusHub] subscribeToUpdates(): no apiKey configured.");
1163
+ return () => {
1164
+ };
1194
1165
  }
1195
1166
  let eventSource = null;
1196
1167
  let retryCount = 0;
1197
1168
  let isClosed = false;
1198
- const MAX_RETRY_DELAY_MS = 3e4;
1199
1169
  const connect = () => {
1200
1170
  if (isClosed) return;
1201
1171
  const url = `${this.config.apiUrl}/content/${this.config.projectId}/updates?key=${this.config.apiKey ?? ""}`;
1202
1172
  eventSource = new EventSource(url);
1203
1173
  eventSource.onopen = () => {
1204
1174
  retryCount = 0;
1205
- if (this.config.debug)
1206
- console.log("[NexusHub] \u{1F7E2} Real-time stream connected");
1207
1175
  };
1208
1176
  eventSource.onmessage = (event) => {
1209
1177
  let data;
1210
1178
  try {
1211
1179
  data = JSON.parse(event.data);
1212
- } catch (e) {
1213
- console.error("[NexusHub] SSE Parse Error", e);
1180
+ } catch {
1214
1181
  return;
1215
1182
  }
1216
1183
  if (!data || typeof data.type !== "string") {
@@ -1222,6 +1189,9 @@ var ContentEngine = class {
1222
1189
  if (data.type === "collection.updated" && data.collectionId) {
1223
1190
  this.invalidateCache([CacheTags.collection(data.collectionId)]);
1224
1191
  }
1192
+ if (data.type === "globals.updated") {
1193
+ this.invalidateCache([CacheTags.global]);
1194
+ }
1225
1195
  if (data.type === "schema.updated") {
1226
1196
  this.clearCache();
1227
1197
  }
@@ -1230,16 +1200,8 @@ var ContentEngine = class {
1230
1200
  eventSource.onerror = () => {
1231
1201
  eventSource?.close();
1232
1202
  if (isClosed) return;
1233
- const timeout = Math.min(
1234
- 1e3 * Math.pow(2, retryCount),
1235
- MAX_RETRY_DELAY_MS
1236
- );
1203
+ const timeout = Math.min(1e3 * Math.pow(2, retryCount), 3e4);
1237
1204
  retryCount++;
1238
- if (this.config.debug) {
1239
- console.warn(
1240
- `[NexusHub] \u{1F534} Stream disconnected. Reconnecting in ${timeout}ms...`
1241
- );
1242
- }
1243
1205
  setTimeout(connect, timeout);
1244
1206
  };
1245
1207
  };
@@ -1249,70 +1211,19 @@ var ContentEngine = class {
1249
1211
  eventSource?.close();
1250
1212
  };
1251
1213
  }
1252
- // --- CACHE MANAGEMENT ---
1253
- async checkCaches(key, forceRefresh, includeMetadata) {
1254
- if (forceRefresh) return null;
1255
- if (this.cacheStrategy === "memory") {
1256
- const cached = this.memoryCache.get(key);
1257
- if (cached && this.isCacheValid(cached.metadata)) {
1258
- return cached;
1259
- }
1260
- }
1261
- if (!this.isServer && this.cacheStrategy === "localStorage" && this.browserCache) {
1262
- const cached = this.browserCache.get(key);
1263
- if (cached) {
1264
- return {
1265
- data: cached,
1266
- metadata: {
1267
- timestamp: 0,
1268
- expiresAt: Infinity,
1269
- tags: [],
1270
- etag: void 0
1271
- }
1272
- };
1273
- }
1274
- }
1275
- if (key.startsWith("page:")) {
1276
- const slug = key.split(":")[1];
1277
- try {
1278
- const local = await this.localCache.getPage(slug);
1279
- if (local && (process.env.NODE_ENV === "development" || !this.config.apiKey)) {
1280
- return {
1281
- data: local,
1282
- metadata: {
1283
- timestamp: Date.now(),
1284
- expiresAt: Infinity,
1285
- tags: [
1286
- CacheTags.content(slug),
1287
- CacheTags.project(this.config.projectId)
1288
- ],
1289
- etag: void 0
1290
- }
1291
- };
1292
- }
1293
- } catch {
1294
- }
1295
- }
1296
- return null;
1297
- }
1298
1214
  writeCache(key, data, options) {
1299
- const numericRevalidate = options.revalidate === false ? 31536e5 : options.revalidate;
1215
+ const ttlSeconds = options.revalidate === false ? 60 : Math.max(1, options.revalidate);
1300
1216
  if (this.cacheStrategy === "memory") {
1301
1217
  this.memoryCache.set(key, data, {
1302
1218
  tags: options.tags,
1303
- revalidate: numericRevalidate
1219
+ ttl: ttlSeconds * 1e3
1304
1220
  });
1305
1221
  }
1306
- if (!this.isServer && this.cacheStrategy === "localStorage" && this.browserCache) {
1307
- this.browserCache.set(key, data, numericRevalidate * 1e3);
1308
- }
1309
1222
  }
1310
1223
  invalidateCache(tags) {
1311
1224
  this.memoryCache.invalidateByTags(tags);
1312
- if (this.config.debug) {
1313
- console.log(
1314
- `[NexusHub] \u267B\uFE0F Invalidated cache for tags: ${tags.join(", ")}`
1315
- );
1225
+ if (this.browserCache) {
1226
+ this.browserCache.clear();
1316
1227
  }
1317
1228
  }
1318
1229
  clearCache() {
@@ -1320,24 +1231,17 @@ var ContentEngine = class {
1320
1231
  if (this.browserCache) {
1321
1232
  this.browserCache.clear();
1322
1233
  }
1323
- if (this.config.debug) {
1324
- console.log("[NexusHub] \u{1F9F9} Cleared all caches");
1325
- }
1326
1234
  }
1327
1235
  getCacheStats() {
1328
- const stats = {
1236
+ return {
1329
1237
  memory: this.memoryCache.getStats(),
1330
- local: { loaded: this.localCache.isLoaded() }
1238
+ local: {
1239
+ loaded: this.localCache.isLoaded()
1240
+ }
1331
1241
  };
1332
- if (this.browserCache) {
1333
- stats.browser = { size: 0 };
1334
- }
1335
- return stats;
1336
1242
  }
1337
- // --- REQUEST METHODS ---
1338
1243
  async fetchPage(slug, cacheKey, tags, revalidate, forceRefresh) {
1339
1244
  const url = `${this.config.apiUrl}/content/${this.config.projectId}/page/${slug}`;
1340
- const expiresAt = revalidate === false ? Infinity : Date.now() + revalidate * 1e3;
1341
1245
  const fetchTags = [
1342
1246
  CacheTags.project(this.config.projectId),
1343
1247
  CacheTags.content(slug),
@@ -1347,13 +1251,12 @@ var ContentEngine = class {
1347
1251
  method: "GET",
1348
1252
  headers: this.getHeaders(),
1349
1253
  tags: fetchTags,
1350
- revalidate
1254
+ revalidate,
1255
+ forceRefresh
1351
1256
  });
1352
1257
  if (!res.ok) {
1353
1258
  if (res.status === 404) {
1354
- throw new Error(
1355
- `Page '${slug}' not found. Check your Dashboard or Seed data.`
1356
- );
1259
+ throw new Error(`Page '${slug}' not found.`);
1357
1260
  }
1358
1261
  if (res.status === 408) {
1359
1262
  throw new Error("Request timeout");
@@ -1367,17 +1270,19 @@ var ContentEngine = class {
1367
1270
  metadata: {
1368
1271
  timestamp: Date.now(),
1369
1272
  etag: etag || void 0,
1370
- expiresAt,
1273
+ expiresAt: revalidate === false ? Infinity : Date.now() + revalidate * 1e3,
1371
1274
  tags: fetchTags
1372
1275
  }
1373
1276
  };
1374
- this.writeCache(cacheKey, cacheEntry.data, {
1375
- revalidate,
1376
- tags: fetchTags
1377
- });
1277
+ if (!this.isServer) {
1278
+ this.writeCache(cacheKey, cacheEntry.data, {
1279
+ revalidate,
1280
+ tags: fetchTags
1281
+ });
1282
+ }
1378
1283
  return cacheEntry;
1379
1284
  }
1380
- async fetchCollection(collectionId, query, cacheKey, tags, revalidate) {
1285
+ async fetchCollection(collectionId, query, cacheKey, tags, revalidate, forceRefresh) {
1381
1286
  const params = buildQueryString(query);
1382
1287
  const url = `${this.config.apiUrl}/content/${this.config.projectId}/collection/${collectionId}?${params}`;
1383
1288
  const fetchTags = [
@@ -1389,7 +1294,8 @@ var ContentEngine = class {
1389
1294
  method: "GET",
1390
1295
  headers: this.getHeaders(),
1391
1296
  tags: fetchTags,
1392
- revalidate
1297
+ revalidate,
1298
+ forceRefresh
1393
1299
  });
1394
1300
  if (!res.ok) {
1395
1301
  if (res.status === 408) {
@@ -1399,23 +1305,23 @@ var ContentEngine = class {
1399
1305
  }
1400
1306
  const json = await res.json();
1401
1307
  const etag = res.headers.get("etag");
1402
- const expiresAt = revalidate === false ? Infinity : Date.now() + revalidate * 1e3;
1403
1308
  const cacheEntry = {
1404
1309
  data: json,
1405
1310
  metadata: {
1406
1311
  timestamp: Date.now(),
1407
1312
  etag: etag || void 0,
1408
- expiresAt,
1313
+ expiresAt: revalidate === false ? Infinity : Date.now() + revalidate * 1e3,
1409
1314
  tags: fetchTags
1410
1315
  }
1411
1316
  };
1412
- this.writeCache(cacheKey, cacheEntry.data, {
1413
- revalidate,
1414
- tags: fetchTags
1415
- });
1317
+ if (!this.isServer) {
1318
+ this.writeCache(cacheKey, cacheEntry.data, {
1319
+ revalidate,
1320
+ tags: fetchTags
1321
+ });
1322
+ }
1416
1323
  return cacheEntry;
1417
1324
  }
1418
- // --- HELPER METHODS ---
1419
1325
  applyLocalQuery(items, query) {
1420
1326
  let filtered = [...items];
1421
1327
  if (query.search) {
@@ -1428,7 +1334,9 @@ var ContentEngine = class {
1428
1334
  filtered = filtered.filter((item) => {
1429
1335
  return Object.entries(query.filter).every(([key, value]) => {
1430
1336
  const itemValue = item[key];
1431
- if (itemValue === void 0) return false;
1337
+ if (itemValue === void 0) {
1338
+ return false;
1339
+ }
1432
1340
  if (Array.isArray(value)) {
1433
1341
  return value.includes(itemValue);
1434
1342
  }
@@ -1441,8 +1349,12 @@ var ContentEngine = class {
1441
1349
  const aVal = a[query.sort];
1442
1350
  const bVal = b[query.sort];
1443
1351
  const order = query.order === "asc" ? 1 : -1;
1444
- if (aVal < bVal) return -1 * order;
1445
- if (aVal > bVal) return 1 * order;
1352
+ if (aVal < bVal) {
1353
+ return -1 * order;
1354
+ }
1355
+ if (aVal > bVal) {
1356
+ return 1 * order;
1357
+ }
1446
1358
  return 0;
1447
1359
  });
1448
1360
  }
@@ -1466,15 +1378,18 @@ var ContentEngine = class {
1466
1378
  timeout = this.config.timeout || 1e4,
1467
1379
  tags = [],
1468
1380
  revalidate,
1381
+ forceRefresh = false,
1469
1382
  ...fetchOptions
1470
1383
  } = options;
1471
1384
  const controller = new AbortController();
1472
1385
  const id = setTimeout(() => controller.abort(), timeout);
1473
1386
  const nextConfig = this.isServer ? {
1474
- cache: revalidate === 0 ? "no-store" : "force-cache",
1475
- next: {
1476
- tags,
1477
- revalidate: revalidate === false ? false : revalidate
1387
+ cache: forceRefresh || revalidate === 0 ? "no-store" : "force-cache",
1388
+ ...forceRefresh ? {} : {
1389
+ next: {
1390
+ tags,
1391
+ revalidate: revalidate === false ? false : revalidate
1392
+ }
1478
1393
  }
1479
1394
  } : {};
1480
1395
  try {
@@ -1493,9 +1408,9 @@ var ContentEngine = class {
1493
1408
  getHeaders() {
1494
1409
  const headers = {
1495
1410
  "Content-Type": "application/json",
1496
- "X-Nexus-Client": `client-sdk/${this.config.sdkVersion ?? "1.1.0"}`,
1497
- "X-Nexus-Request-ID": typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `nx_${Date.now()}_${Math.random().toString(36).slice(2)}`,
1498
- "X-Nexus-Project": this.config.projectId
1411
+ "X-GN-Apex-Client": `gnapex-sdk/${this.config.sdkVersion ?? "1.1.6"}`,
1412
+ "X-GN-Apex-Request-ID": typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `nx_${Date.now()}_${Math.random().toString(36).slice(2)}`,
1413
+ "X-GN-Apex-Project": this.config.projectId
1499
1414
  };
1500
1415
  if (this.config.apiKey) {
1501
1416
  headers["Authorization"] = `Bearer ${this.config.apiKey}`;
@@ -1505,10 +1420,6 @@ var ContentEngine = class {
1505
1420
  isCacheValid(metadata) {
1506
1421
  return Date.now() < metadata.expiresAt;
1507
1422
  }
1508
- /**
1509
- * 🛡️ FIX: Normalizes timeout errors (AbortError, 408, or Timeout messages)
1510
- * into clean, standardized "Request timeout" errors.
1511
- */
1512
1423
  normalizeError(error, context) {
1513
1424
  if (error instanceof Error) {
1514
1425
  const msg = error.message.toLowerCase();
@@ -1523,14 +1434,7 @@ var ContentEngine = class {
1523
1434
  }
1524
1435
  return new Error(`${context}: ${String(error)}`);
1525
1436
  }
1526
- cancelRequests() {
1527
- if (this.abortController) {
1528
- this.abortController.abort();
1529
- this.abortController = new AbortController();
1530
- }
1531
- }
1532
1437
  cleanup() {
1533
- this.cancelRequests();
1534
1438
  if (!this.isServer) {
1535
1439
  window.removeEventListener("beforeunload", this.cleanup.bind(this));
1536
1440
  }