@nexushub/client 0.7.7 → 0.7.9
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/cli.cjs +423 -107
- package/dist/index.cjs +109 -59
- package/dist/index.d.cts +21 -12
- package/dist/index.d.ts +21 -12
- package/dist/index.js +109 -59
- package/dist/react.cjs +109 -59
- package/dist/react.d.cts +21 -12
- package/dist/react.d.ts +21 -12
- package/dist/react.js +109 -59
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -936,7 +936,7 @@ var ContentEngine = class {
|
|
|
936
936
|
});
|
|
937
937
|
this.requestBatcher = new RequestBatcher(20, 50);
|
|
938
938
|
this.circuitBreaker = new CircuitBreaker();
|
|
939
|
-
this.defaultRevalidate = config.revalidateTime
|
|
939
|
+
this.defaultRevalidate = config.revalidateTime ?? false;
|
|
940
940
|
this.cacheStrategy = config.cacheStrategy || "memory";
|
|
941
941
|
if (!this.isServer) {
|
|
942
942
|
window.addEventListener("beforeunload", this.cleanup.bind(this));
|
|
@@ -1140,9 +1140,16 @@ var ContentEngine = class {
|
|
|
1140
1140
|
await this.rateLimiter.checkLimit();
|
|
1141
1141
|
const url = `${this.config.apiUrl}/content/${this.config.projectId}/globals`;
|
|
1142
1142
|
const params = include.length > 0 ? `?include=${include.join(",")}` : "";
|
|
1143
|
+
const fetchTags = [
|
|
1144
|
+
CacheTags.project(this.config.projectId),
|
|
1145
|
+
CacheTags.global,
|
|
1146
|
+
...options.tags || []
|
|
1147
|
+
];
|
|
1143
1148
|
const res = await this.fetchWithTimeout(`${url}${params}`, {
|
|
1144
1149
|
method: "GET",
|
|
1145
|
-
headers: this.getHeaders()
|
|
1150
|
+
headers: this.getHeaders(),
|
|
1151
|
+
tags: fetchTags,
|
|
1152
|
+
revalidate
|
|
1146
1153
|
});
|
|
1147
1154
|
if (!res.ok) {
|
|
1148
1155
|
throw new Error(`API Error ${res.status}: ${res.statusText}`);
|
|
@@ -1183,9 +1190,20 @@ var ContentEngine = class {
|
|
|
1183
1190
|
params.append("include", options.include.join(","));
|
|
1184
1191
|
}
|
|
1185
1192
|
const url = `${this.config.apiUrl}/content/${this.config.projectId}/collection/${collectionId}/${itemId}?${params}`;
|
|
1193
|
+
const fetchTags = [
|
|
1194
|
+
CacheTags.project(this.config.projectId),
|
|
1195
|
+
CacheTags.collection(collectionId),
|
|
1196
|
+
`item_${itemId}`,
|
|
1197
|
+
...options.tags || []
|
|
1198
|
+
];
|
|
1186
1199
|
const res = await this.fetchWithTimeout(url, {
|
|
1187
1200
|
method: "GET",
|
|
1188
|
-
headers: this.getHeaders()
|
|
1201
|
+
headers: this.getHeaders(),
|
|
1202
|
+
tags: fetchTags,
|
|
1203
|
+
// ?? not || — see the constructor comment on defaultRevalidate for
|
|
1204
|
+
// why: an explicit `revalidate: 0` on this call must not be
|
|
1205
|
+
// discarded in favor of the engine's default.
|
|
1206
|
+
revalidate: options.revalidate ?? this.defaultRevalidate
|
|
1189
1207
|
});
|
|
1190
1208
|
if (!res.ok) {
|
|
1191
1209
|
throw new Error(`API Error ${res.status}: ${res.statusText}`);
|
|
@@ -1193,13 +1211,8 @@ var ContentEngine = class {
|
|
|
1193
1211
|
const json = await res.json();
|
|
1194
1212
|
const data = json.data || json;
|
|
1195
1213
|
this.writeCache(cacheKey, data, {
|
|
1196
|
-
revalidate: options.revalidate
|
|
1197
|
-
tags:
|
|
1198
|
-
CacheTags.project(this.config.projectId),
|
|
1199
|
-
CacheTags.collection(collectionId),
|
|
1200
|
-
`item_${itemId}`,
|
|
1201
|
-
...options.tags || []
|
|
1202
|
-
]
|
|
1214
|
+
revalidate: options.revalidate ?? this.defaultRevalidate,
|
|
1215
|
+
tags: fetchTags
|
|
1203
1216
|
});
|
|
1204
1217
|
return data;
|
|
1205
1218
|
});
|
|
@@ -1242,19 +1255,49 @@ var ContentEngine = class {
|
|
|
1242
1255
|
}
|
|
1243
1256
|
/**
|
|
1244
1257
|
* Robust Real-time Subscriptions (SSE) with Auto-Reconnect
|
|
1258
|
+
*
|
|
1259
|
+
* ⚠️ SCOPE — READ BEFORE RELYING ON THIS FOR "instant" UPDATES:
|
|
1260
|
+
* This connects from the BROWSER TAB it's called in, and on message it
|
|
1261
|
+
* only clears THIS SDK INSTANCE's in-memory `memoryCache` (via
|
|
1262
|
+
* invalidateCache below) in THAT browser tab's JS heap. In a typical
|
|
1263
|
+
* Next.js deployment — and Cloudflare specifically, which is stateless
|
|
1264
|
+
* per-request at the edge — that is a different process/isolate than the
|
|
1265
|
+
* one that will render the NEXT server request for this content. So:
|
|
1266
|
+
* - ✅ Useful for: a client component that reads from `nexus.content`
|
|
1267
|
+
* directly in the browser and re-renders in place without a page
|
|
1268
|
+
* navigation (e.g. a live-updating dashboard widget).
|
|
1269
|
+
* - ❌ NOT sufficient for: "user A edits content in the CMS, user B's
|
|
1270
|
+
* already-loaded page updates" via a server-rendered page. That
|
|
1271
|
+
* requires the backend's /api/revalidate webhook (see
|
|
1272
|
+
* content.service.ts `pingNextRevalidateWebhook`) to have actually
|
|
1273
|
+
* cleared the *server's* Data Cache, so the NEXT navigation or
|
|
1274
|
+
* server request picks up fresh data. This SSE channel does not
|
|
1275
|
+
* replace that — it's a complementary, browser-local optimization.
|
|
1276
|
+
* If your symptom was "stale content after editing," fix the webhook
|
|
1277
|
+
* wiring first; treat this method as an enhancement layered on top.
|
|
1245
1278
|
*/
|
|
1246
1279
|
subscribeToUpdates(callback) {
|
|
1247
1280
|
if (this.isServer || typeof EventSource === "undefined") {
|
|
1248
|
-
|
|
1281
|
+
if (this.config.debug) {
|
|
1282
|
+
console.warn(
|
|
1283
|
+
"[NexusHub] subscribeToUpdates() called in a non-browser environment (server render or SSR pass) \u2014 this is expected and safely skipped; EventSource only makes sense client-side."
|
|
1284
|
+
);
|
|
1285
|
+
}
|
|
1249
1286
|
return () => {
|
|
1250
1287
|
};
|
|
1251
1288
|
}
|
|
1289
|
+
if (!this.config.apiKey) {
|
|
1290
|
+
console.warn(
|
|
1291
|
+
"[NexusHub] subscribeToUpdates(): no apiKey configured, the SSE connection will likely be rejected by the backend. Set NEXT_PUBLIC_NEXUS_KEY."
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1252
1294
|
let eventSource = null;
|
|
1253
1295
|
let retryCount = 0;
|
|
1254
1296
|
let isClosed = false;
|
|
1297
|
+
const MAX_RETRY_DELAY_MS = 3e4;
|
|
1255
1298
|
const connect = () => {
|
|
1256
1299
|
if (isClosed) return;
|
|
1257
|
-
const url = `${this.config.apiUrl}/content/${this.config.projectId}/updates?key=${this.config.apiKey}`;
|
|
1300
|
+
const url = `${this.config.apiUrl}/content/${this.config.projectId}/updates?key=${this.config.apiKey ?? ""}`;
|
|
1258
1301
|
eventSource = new EventSource(url);
|
|
1259
1302
|
eventSource.onopen = () => {
|
|
1260
1303
|
retryCount = 0;
|
|
@@ -1262,27 +1305,40 @@ var ContentEngine = class {
|
|
|
1262
1305
|
console.log("[NexusHub] \u{1F7E2} Real-time stream connected");
|
|
1263
1306
|
};
|
|
1264
1307
|
eventSource.onmessage = (event) => {
|
|
1308
|
+
let data;
|
|
1265
1309
|
try {
|
|
1266
|
-
|
|
1267
|
-
if (data.type === "content.updated" && data.slug) {
|
|
1268
|
-
this.invalidateCache([CacheTags.content(data.slug)]);
|
|
1269
|
-
}
|
|
1270
|
-
if (data.type === "collection.updated") {
|
|
1271
|
-
this.invalidateCache([CacheTags.collection(data.collectionId)]);
|
|
1272
|
-
}
|
|
1273
|
-
callback(data);
|
|
1310
|
+
data = JSON.parse(event.data);
|
|
1274
1311
|
} catch (e) {
|
|
1275
1312
|
console.error("[NexusHub] SSE Parse Error", e);
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
if (!data || typeof data.type !== "string") {
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
if (data.type === "content.updated" && data.slug) {
|
|
1319
|
+
this.invalidateCache([CacheTags.content(data.slug)]);
|
|
1320
|
+
}
|
|
1321
|
+
if (data.type === "collection.updated" && data.collectionId) {
|
|
1322
|
+
this.invalidateCache([CacheTags.collection(data.collectionId)]);
|
|
1276
1323
|
}
|
|
1324
|
+
if (data.type === "schema.updated") {
|
|
1325
|
+
this.clearCache();
|
|
1326
|
+
}
|
|
1327
|
+
callback(data);
|
|
1277
1328
|
};
|
|
1278
1329
|
eventSource.onerror = () => {
|
|
1279
1330
|
eventSource?.close();
|
|
1280
1331
|
if (isClosed) return;
|
|
1281
|
-
const timeout = Math.min(
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
`[NexusHub] \u{1F534} Stream disconnected. Reconnecting in ${timeout}ms...`
|
|
1332
|
+
const timeout = Math.min(
|
|
1333
|
+
1e3 * Math.pow(2, retryCount),
|
|
1334
|
+
MAX_RETRY_DELAY_MS
|
|
1285
1335
|
);
|
|
1336
|
+
retryCount++;
|
|
1337
|
+
if (this.config.debug) {
|
|
1338
|
+
console.warn(
|
|
1339
|
+
`[NexusHub] \u{1F534} Stream disconnected. Reconnecting in ${timeout}ms...`
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1286
1342
|
setTimeout(connect, timeout);
|
|
1287
1343
|
};
|
|
1288
1344
|
};
|
|
@@ -1293,9 +1349,6 @@ var ContentEngine = class {
|
|
|
1293
1349
|
};
|
|
1294
1350
|
}
|
|
1295
1351
|
// --- CACHE MANAGEMENT ---
|
|
1296
|
-
/**
|
|
1297
|
-
* Check all caches in order of speed
|
|
1298
|
-
*/
|
|
1299
1352
|
async checkCaches(key, forceRefresh, includeMetadata) {
|
|
1300
1353
|
if (forceRefresh) return null;
|
|
1301
1354
|
if (this.cacheStrategy === "memory") {
|
|
@@ -1352,9 +1405,6 @@ var ContentEngine = class {
|
|
|
1352
1405
|
this.browserCache.set(key, data, numericRevalidate * 1e3);
|
|
1353
1406
|
}
|
|
1354
1407
|
}
|
|
1355
|
-
/**
|
|
1356
|
-
* Invalidate cache by tags
|
|
1357
|
-
*/
|
|
1358
1408
|
invalidateCache(tags) {
|
|
1359
1409
|
this.memoryCache.invalidateByTags(tags);
|
|
1360
1410
|
if (this.config.debug) {
|
|
@@ -1363,9 +1413,6 @@ var ContentEngine = class {
|
|
|
1363
1413
|
);
|
|
1364
1414
|
}
|
|
1365
1415
|
}
|
|
1366
|
-
/**
|
|
1367
|
-
* Clear all caches
|
|
1368
|
-
*/
|
|
1369
1416
|
clearCache() {
|
|
1370
1417
|
this.memoryCache.clear();
|
|
1371
1418
|
if (this.browserCache) {
|
|
@@ -1375,9 +1422,6 @@ var ContentEngine = class {
|
|
|
1375
1422
|
console.log("[NexusHub] \u{1F9F9} Cleared all caches");
|
|
1376
1423
|
}
|
|
1377
1424
|
}
|
|
1378
|
-
/**
|
|
1379
|
-
* Get cache statistics
|
|
1380
|
-
*/
|
|
1381
1425
|
getCacheStats() {
|
|
1382
1426
|
const stats = {
|
|
1383
1427
|
memory: this.memoryCache.getStats(),
|
|
@@ -1391,14 +1435,16 @@ var ContentEngine = class {
|
|
|
1391
1435
|
// --- REQUEST METHODS ---
|
|
1392
1436
|
async fetchPage(slug, cacheKey, tags, revalidate, forceRefresh) {
|
|
1393
1437
|
const url = `${this.config.apiUrl}/content/${this.config.projectId}/page/${slug}`;
|
|
1394
|
-
if (this.config.debug) {
|
|
1395
|
-
console.log(`[NexusHub] \u{1F310} Fetching: ${url}`);
|
|
1396
|
-
}
|
|
1397
1438
|
const expiresAt = revalidate === false ? Infinity : Date.now() + revalidate * 1e3;
|
|
1439
|
+
const fetchTags = [
|
|
1440
|
+
CacheTags.project(this.config.projectId),
|
|
1441
|
+
CacheTags.content(slug),
|
|
1442
|
+
...tags
|
|
1443
|
+
];
|
|
1398
1444
|
const res = await this.fetchWithTimeout(url, {
|
|
1399
1445
|
method: "GET",
|
|
1400
1446
|
headers: this.getHeaders(),
|
|
1401
|
-
tags,
|
|
1447
|
+
tags: fetchTags,
|
|
1402
1448
|
revalidate
|
|
1403
1449
|
});
|
|
1404
1450
|
if (!res.ok) {
|
|
@@ -1417,29 +1463,27 @@ var ContentEngine = class {
|
|
|
1417
1463
|
timestamp: Date.now(),
|
|
1418
1464
|
etag: etag || void 0,
|
|
1419
1465
|
expiresAt,
|
|
1420
|
-
tags:
|
|
1466
|
+
tags: fetchTags
|
|
1421
1467
|
}
|
|
1422
1468
|
};
|
|
1423
1469
|
this.writeCache(cacheKey, cacheEntry.data, {
|
|
1424
1470
|
revalidate,
|
|
1425
|
-
tags:
|
|
1426
|
-
CacheTags.project(this.config.projectId),
|
|
1427
|
-
CacheTags.content(slug),
|
|
1428
|
-
...tags
|
|
1429
|
-
]
|
|
1471
|
+
tags: fetchTags
|
|
1430
1472
|
});
|
|
1431
1473
|
return cacheEntry;
|
|
1432
1474
|
}
|
|
1433
1475
|
async fetchCollection(collectionId, query, cacheKey, tags, revalidate) {
|
|
1434
1476
|
const params = buildQueryString(query);
|
|
1435
1477
|
const url = `${this.config.apiUrl}/content/${this.config.projectId}/collection/${collectionId}?${params}`;
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1478
|
+
const fetchTags = [
|
|
1479
|
+
CacheTags.project(this.config.projectId),
|
|
1480
|
+
CacheTags.collection(collectionId),
|
|
1481
|
+
...tags
|
|
1482
|
+
];
|
|
1439
1483
|
const res = await this.fetchWithTimeout(url, {
|
|
1440
1484
|
method: "GET",
|
|
1441
1485
|
headers: this.getHeaders(),
|
|
1442
|
-
tags,
|
|
1486
|
+
tags: fetchTags,
|
|
1443
1487
|
revalidate
|
|
1444
1488
|
});
|
|
1445
1489
|
if (!res.ok) {
|
|
@@ -1454,16 +1498,12 @@ var ContentEngine = class {
|
|
|
1454
1498
|
timestamp: Date.now(),
|
|
1455
1499
|
etag: etag || void 0,
|
|
1456
1500
|
expiresAt,
|
|
1457
|
-
tags:
|
|
1501
|
+
tags: fetchTags
|
|
1458
1502
|
}
|
|
1459
1503
|
};
|
|
1460
1504
|
this.writeCache(cacheKey, cacheEntry.data, {
|
|
1461
1505
|
revalidate,
|
|
1462
|
-
tags:
|
|
1463
|
-
CacheTags.project(this.config.projectId),
|
|
1464
|
-
CacheTags.collection(collectionId),
|
|
1465
|
-
...tags
|
|
1466
|
-
]
|
|
1506
|
+
tags: fetchTags
|
|
1467
1507
|
});
|
|
1468
1508
|
return cacheEntry;
|
|
1469
1509
|
}
|
|
@@ -1523,8 +1563,11 @@ var ContentEngine = class {
|
|
|
1523
1563
|
const controller = new AbortController();
|
|
1524
1564
|
const id = setTimeout(() => controller.abort(), timeout);
|
|
1525
1565
|
const nextConfig = this.isServer ? {
|
|
1526
|
-
cache: "force-cache",
|
|
1527
|
-
next: {
|
|
1566
|
+
cache: revalidate === 0 ? "no-store" : "force-cache",
|
|
1567
|
+
next: {
|
|
1568
|
+
tags,
|
|
1569
|
+
revalidate: revalidate === false ? false : revalidate
|
|
1570
|
+
}
|
|
1528
1571
|
} : {};
|
|
1529
1572
|
try {
|
|
1530
1573
|
const response = await fetch(url, {
|
|
@@ -2547,13 +2590,20 @@ function getSelector(el, depth = 0) {
|
|
|
2547
2590
|
}
|
|
2548
2591
|
|
|
2549
2592
|
// src/client.ts
|
|
2593
|
+
var DEFAULT_REVALIDATE_SECONDS = 60;
|
|
2550
2594
|
var NexusClient = class {
|
|
2551
2595
|
constructor(config) {
|
|
2552
2596
|
const fullConfig = getFullConfig(config);
|
|
2553
2597
|
this.config = {
|
|
2554
2598
|
debug: config?.debug ?? false,
|
|
2555
2599
|
cacheStrategy: config?.cacheStrategy ?? "memory",
|
|
2556
|
-
|
|
2600
|
+
// Only fall through to the safe default when revalidateTime is
|
|
2601
|
+
// genuinely *unset* (undefined). An explicit `false` from the caller
|
|
2602
|
+
// is a deliberate "never revalidate" choice and must be respected,
|
|
2603
|
+
// not silently upgraded — `??` (not `||`) is required here so that
|
|
2604
|
+
// `0` (revalidate on every request) also passes through untouched
|
|
2605
|
+
// instead of being treated as falsy.
|
|
2606
|
+
revalidateTime: config?.revalidateTime ?? DEFAULT_REVALIDATE_SECONDS,
|
|
2557
2607
|
timeout: config?.timeout ?? 1e4,
|
|
2558
2608
|
retries: config?.retries ?? 3,
|
|
2559
2609
|
...fullConfig
|
package/dist/index.d.cts
CHANGED
|
@@ -119,6 +119,7 @@ declare class ContentEngine {
|
|
|
119
119
|
include?: string[];
|
|
120
120
|
revalidate?: number | false;
|
|
121
121
|
forceRefresh?: boolean;
|
|
122
|
+
tags?: string[];
|
|
122
123
|
}): Promise<T>;
|
|
123
124
|
/**
|
|
124
125
|
* Get a single item from a collection (Uses Request Batching)
|
|
@@ -146,24 +147,32 @@ declare class ContentEngine {
|
|
|
146
147
|
prefetch(urls: string[]): Promise<void>;
|
|
147
148
|
/**
|
|
148
149
|
* Robust Real-time Subscriptions (SSE) with Auto-Reconnect
|
|
150
|
+
*
|
|
151
|
+
* ⚠️ SCOPE — READ BEFORE RELYING ON THIS FOR "instant" UPDATES:
|
|
152
|
+
* This connects from the BROWSER TAB it's called in, and on message it
|
|
153
|
+
* only clears THIS SDK INSTANCE's in-memory `memoryCache` (via
|
|
154
|
+
* invalidateCache below) in THAT browser tab's JS heap. In a typical
|
|
155
|
+
* Next.js deployment — and Cloudflare specifically, which is stateless
|
|
156
|
+
* per-request at the edge — that is a different process/isolate than the
|
|
157
|
+
* one that will render the NEXT server request for this content. So:
|
|
158
|
+
* - ✅ Useful for: a client component that reads from `nexus.content`
|
|
159
|
+
* directly in the browser and re-renders in place without a page
|
|
160
|
+
* navigation (e.g. a live-updating dashboard widget).
|
|
161
|
+
* - ❌ NOT sufficient for: "user A edits content in the CMS, user B's
|
|
162
|
+
* already-loaded page updates" via a server-rendered page. That
|
|
163
|
+
* requires the backend's /api/revalidate webhook (see
|
|
164
|
+
* content.service.ts `pingNextRevalidateWebhook`) to have actually
|
|
165
|
+
* cleared the *server's* Data Cache, so the NEXT navigation or
|
|
166
|
+
* server request picks up fresh data. This SSE channel does not
|
|
167
|
+
* replace that — it's a complementary, browser-local optimization.
|
|
168
|
+
* If your symptom was "stale content after editing," fix the webhook
|
|
169
|
+
* wiring first; treat this method as an enhancement layered on top.
|
|
149
170
|
*/
|
|
150
171
|
subscribeToUpdates(callback: (data: any) => void): () => void;
|
|
151
|
-
/**
|
|
152
|
-
* Check all caches in order of speed
|
|
153
|
-
*/
|
|
154
172
|
private checkCaches;
|
|
155
173
|
private writeCache;
|
|
156
|
-
/**
|
|
157
|
-
* Invalidate cache by tags
|
|
158
|
-
*/
|
|
159
174
|
invalidateCache(tags: string[]): void;
|
|
160
|
-
/**
|
|
161
|
-
* Clear all caches
|
|
162
|
-
*/
|
|
163
175
|
clearCache(): void;
|
|
164
|
-
/**
|
|
165
|
-
* Get cache statistics
|
|
166
|
-
*/
|
|
167
176
|
getCacheStats(): {
|
|
168
177
|
memory: {
|
|
169
178
|
size: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -119,6 +119,7 @@ declare class ContentEngine {
|
|
|
119
119
|
include?: string[];
|
|
120
120
|
revalidate?: number | false;
|
|
121
121
|
forceRefresh?: boolean;
|
|
122
|
+
tags?: string[];
|
|
122
123
|
}): Promise<T>;
|
|
123
124
|
/**
|
|
124
125
|
* Get a single item from a collection (Uses Request Batching)
|
|
@@ -146,24 +147,32 @@ declare class ContentEngine {
|
|
|
146
147
|
prefetch(urls: string[]): Promise<void>;
|
|
147
148
|
/**
|
|
148
149
|
* Robust Real-time Subscriptions (SSE) with Auto-Reconnect
|
|
150
|
+
*
|
|
151
|
+
* ⚠️ SCOPE — READ BEFORE RELYING ON THIS FOR "instant" UPDATES:
|
|
152
|
+
* This connects from the BROWSER TAB it's called in, and on message it
|
|
153
|
+
* only clears THIS SDK INSTANCE's in-memory `memoryCache` (via
|
|
154
|
+
* invalidateCache below) in THAT browser tab's JS heap. In a typical
|
|
155
|
+
* Next.js deployment — and Cloudflare specifically, which is stateless
|
|
156
|
+
* per-request at the edge — that is a different process/isolate than the
|
|
157
|
+
* one that will render the NEXT server request for this content. So:
|
|
158
|
+
* - ✅ Useful for: a client component that reads from `nexus.content`
|
|
159
|
+
* directly in the browser and re-renders in place without a page
|
|
160
|
+
* navigation (e.g. a live-updating dashboard widget).
|
|
161
|
+
* - ❌ NOT sufficient for: "user A edits content in the CMS, user B's
|
|
162
|
+
* already-loaded page updates" via a server-rendered page. That
|
|
163
|
+
* requires the backend's /api/revalidate webhook (see
|
|
164
|
+
* content.service.ts `pingNextRevalidateWebhook`) to have actually
|
|
165
|
+
* cleared the *server's* Data Cache, so the NEXT navigation or
|
|
166
|
+
* server request picks up fresh data. This SSE channel does not
|
|
167
|
+
* replace that — it's a complementary, browser-local optimization.
|
|
168
|
+
* If your symptom was "stale content after editing," fix the webhook
|
|
169
|
+
* wiring first; treat this method as an enhancement layered on top.
|
|
149
170
|
*/
|
|
150
171
|
subscribeToUpdates(callback: (data: any) => void): () => void;
|
|
151
|
-
/**
|
|
152
|
-
* Check all caches in order of speed
|
|
153
|
-
*/
|
|
154
172
|
private checkCaches;
|
|
155
173
|
private writeCache;
|
|
156
|
-
/**
|
|
157
|
-
* Invalidate cache by tags
|
|
158
|
-
*/
|
|
159
174
|
invalidateCache(tags: string[]): void;
|
|
160
|
-
/**
|
|
161
|
-
* Clear all caches
|
|
162
|
-
*/
|
|
163
175
|
clearCache(): void;
|
|
164
|
-
/**
|
|
165
|
-
* Get cache statistics
|
|
166
|
-
*/
|
|
167
176
|
getCacheStats(): {
|
|
168
177
|
memory: {
|
|
169
178
|
size: number;
|