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