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