@nexushub/client 1.1.6 → 1.1.8

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