@xiboplayer/cache 0.5.7 → 0.5.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.
@@ -1,30 +1,32 @@
1
1
  /**
2
- * Widget HTML caching — preprocesses widget HTML and stores in Cache API
2
+ * Widget HTML processing — preprocesses widget HTML and stores via REST
3
3
  *
4
4
  * Handles:
5
5
  * - <base> tag injection for relative path resolution
6
- * - CMS signed URL → local cache path rewriting
6
+ * - CMS signed URL → local store path rewriting
7
7
  * - CSS font URL rewriting and font file caching
8
8
  * - Interactive Control hostAddress rewriting
9
9
  * - CSS object-position fix for CMS template alignment
10
10
  *
11
11
  * Runs on the main thread (needs window.location for URL construction).
12
- * Uses Cache API directlythe SW also serves from the same cache.
12
+ * Stores content via PUT /store/... no Cache API needed.
13
13
  */
14
14
 
15
15
  import { createLogger } from '@xiboplayer/utils';
16
- import { rewriteUrlForProxy } from './download-manager.js';
16
+ import { toProxyUrl } from './download-manager.js';
17
17
 
18
18
  const log = createLogger('Cache');
19
- const CACHE_NAME = 'xibo-media-v1';
20
19
 
21
20
  // Dynamic base path for multi-variant deployment (pwa, pwa-xmds, pwa-xlr)
22
21
  const BASE = (typeof window !== 'undefined')
23
22
  ? window.location.pathname.replace(/\/[^/]*$/, '').replace(/\/$/, '') || '/player/pwa'
24
23
  : '/player/pwa';
25
24
 
25
+ // Dedup concurrent static resource fetches (two widgets both need bundle.min.js)
26
+ const _pendingStatic = new Map(); // filename → Promise<void>
27
+
26
28
  /**
27
- * Store widget HTML in cache for iframe loading
29
+ * Store widget HTML in ContentStore for iframe loading
28
30
  * @param {string} layoutId - Layout ID
29
31
  * @param {string} regionId - Region ID
30
32
  * @param {string} mediaId - Media ID
@@ -33,7 +35,6 @@ const BASE = (typeof window !== 'undefined')
33
35
  */
34
36
  export async function cacheWidgetHtml(layoutId, regionId, mediaId, html) {
35
37
  const cacheKey = `${BASE}/cache/widget/${layoutId}/${regionId}/${mediaId}`;
36
- const cache = await caches.open(CACHE_NAME);
37
38
 
38
39
  // Inject <base> tag to fix relative paths for widget dependencies
39
40
  // Widget HTML has relative paths like "bundle.min.js" that should resolve to cache/media/
@@ -52,7 +53,7 @@ export async function cacheWidgetHtml(layoutId, regionId, mediaId, html) {
52
53
  }
53
54
  }
54
55
 
55
- // Rewrite absolute CMS signed URLs to local cache paths
56
+ // Rewrite absolute CMS signed URLs to local store paths
56
57
  // Matches: https://cms/xmds.php?file=... or https://cms/pwa/file?file=...
57
58
  // These absolute URLs bypass the <base> tag entirely, causing slow CMS fetches
58
59
  const cmsUrlRegex = /https?:\/\/[^"'\s)]+(?:xmds\.php|pwa\/file)\?[^"'\s)]*file=([^&"'\s)]+)[^"'\s)]*/g;
@@ -65,8 +66,6 @@ export async function cacheWidgetHtml(layoutId, regionId, mediaId, html) {
65
66
  });
66
67
 
67
68
  // Inject CSS default for object-position to suppress CMS template warning
68
- // CMS global-elements.xml uses {{alignId}} {{valignId}} which produces
69
- // invalid CSS (empty value) when alignment is not configured
70
69
  const cssFixTag = '<style>img,video{object-position:center center}</style>';
71
70
  if (!modifiedHtml.includes('object-position:center center')) {
72
71
  if (modifiedHtml.includes('</head>')) {
@@ -77,9 +76,6 @@ export async function cacheWidgetHtml(layoutId, regionId, mediaId, html) {
77
76
  }
78
77
 
79
78
  // Rewrite Interactive Control hostAddress to SW-interceptable path
80
- // The IC library uses hostAddress + '/info', '/trigger', etc.
81
- // Original: hostAddress: "https://cms.example.com" → XHR to /info goes to CMS (fails)
82
- // Rewritten: hostAddress: "/player/pwa/ic" → XHR to /player/pwa/ic/info (intercepted by SW)
83
79
  modifiedHtml = modifiedHtml.replace(
84
80
  /hostAddress\s*:\s*["']https?:\/\/[^"']+["']/g,
85
81
  `hostAddress: "${BASE}/ic"`
@@ -87,32 +83,26 @@ export async function cacheWidgetHtml(layoutId, regionId, mediaId, html) {
87
83
 
88
84
  log.info('Injected base tag and rewrote CMS/data URLs in widget HTML');
89
85
 
90
- // Construct full URL for cache storage
91
- const cacheUrl = new URL(cacheKey, window.location.origin);
92
-
93
- const response = new Response(modifiedHtml, {
94
- headers: {
95
- 'Content-Type': 'text/html; charset=utf-8',
96
- 'Access-Control-Allow-Origin': '*'
97
- }
98
- });
99
-
100
- await cache.put(cacheUrl, response);
101
- log.info(`Stored widget HTML at ${cacheKey} (${modifiedHtml.length} bytes)`);
102
-
103
- // Fetch and cache static resources (shared Cache API - accessible from main thread and SW)
86
+ // Store static resources FIRST widget iframe loads immediately after HTML is stored,
87
+ // and its <script>/<link> tags will 404 if deps aren't ready yet
104
88
  if (staticResources.length > 0) {
105
- const STATIC_CACHE_NAME = 'xibo-static-v1';
106
- const staticCache = await caches.open(STATIC_CACHE_NAME);
89
+ await Promise.all(staticResources.map(({ filename, originalUrl }) => {
90
+ // Dedup: if another widget is already fetching the same resource, wait for it
91
+ if (_pendingStatic.has(filename)) {
92
+ return _pendingStatic.get(filename);
93
+ }
107
94
 
108
- await Promise.all(staticResources.map(async ({ filename, originalUrl }) => {
109
- const staticKey = `${BASE}/cache/static/${filename}`;
110
- const existing = await staticCache.match(staticKey);
111
- if (existing) return; // Already cached
95
+ const work = (async () => {
96
+ // Check if already stored
97
+ try {
98
+ const headResp = await fetch(`/store/static/${filename}`, { method: 'HEAD' });
99
+ if (headResp.ok) return; // Already stored
100
+ } catch { /* proceed to fetch */ }
112
101
 
113
102
  try {
114
- const resp = await fetch(rewriteUrlForProxy(originalUrl));
103
+ const resp = await fetch(toProxyUrl(originalUrl));
115
104
  if (!resp.ok) {
105
+ resp.body?.cancel();
116
106
  log.warn(`Failed to fetch static resource: ${filename} (HTTP ${resp.status})`);
117
107
  return;
118
108
  }
@@ -127,7 +117,7 @@ export async function cacheWidgetHtml(layoutId, regionId, mediaId, html) {
127
117
  'svg': 'image/svg+xml'
128
118
  }[ext] || 'application/octet-stream';
129
119
 
130
- // For CSS files, rewrite font URLs and cache referenced font files
120
+ // For CSS files, rewrite font URLs and store referenced font files
131
121
  if (ext === 'css') {
132
122
  let cssText = await resp.text();
133
123
  const fontResources = [];
@@ -138,20 +128,26 @@ export async function cacheWidgetHtml(layoutId, regionId, mediaId, html) {
138
128
  return `url(${quote}${BASE}/cache/static/${encodeURIComponent(fontFilename)}${quote})`;
139
129
  });
140
130
 
141
- await staticCache.put(staticKey, new Response(cssText, {
142
- headers: { 'Content-Type': 'text/css' }
143
- }));
144
- log.info(`Cached CSS with ${fontResources.length} rewritten font URLs: ${filename}`);
131
+ const cssResp = await fetch(`/store/static/${filename}`, {
132
+ method: 'PUT',
133
+ headers: { 'Content-Type': 'text/css' },
134
+ body: cssText,
135
+ });
136
+ cssResp.body?.cancel();
137
+ log.info(`Stored CSS with ${fontResources.length} rewritten font URLs: ${filename}`);
145
138
 
146
- // Fetch and cache referenced font files
139
+ // Fetch and store referenced font files
147
140
  await Promise.all(fontResources.map(async ({ filename: fontFile, originalUrl: fontUrl }) => {
148
- const fontKey = `${BASE}/cache/static/${encodeURIComponent(fontFile)}`;
149
- const existingFont = await staticCache.match(fontKey);
150
- if (existingFont) return; // Already cached (by SW or previous widget)
141
+ // Check if already stored
142
+ try {
143
+ const headResp = await fetch(`/store/static/${encodeURIComponent(fontFile)}`, { method: 'HEAD' });
144
+ if (headResp.ok) return;
145
+ } catch { /* proceed */ }
151
146
 
152
147
  try {
153
- const fontResp = await fetch(rewriteUrlForProxy(fontUrl));
148
+ const fontResp = await fetch(toProxyUrl(fontUrl));
154
149
  if (!fontResp.ok) {
150
+ fontResp.body?.cancel();
155
151
  log.warn(`Failed to fetch font: ${fontFile} (HTTP ${fontResp.status})`);
156
152
  return;
157
153
  }
@@ -164,26 +160,46 @@ export async function cacheWidgetHtml(layoutId, regionId, mediaId, html) {
164
160
  'svg': 'image/svg+xml'
165
161
  }[fontExt] || 'application/octet-stream';
166
162
 
167
- await staticCache.put(fontKey, new Response(fontBlob, {
168
- headers: { 'Content-Type': fontContentType }
169
- }));
170
- log.info(`Cached font: ${fontFile} (${fontContentType}, ${fontBlob.size} bytes)`);
163
+ const fontPutResp = await fetch(`/store/static/${encodeURIComponent(fontFile)}`, {
164
+ method: 'PUT',
165
+ headers: { 'Content-Type': fontContentType },
166
+ body: fontBlob,
167
+ });
168
+ fontPutResp.body?.cancel();
169
+ log.info(`Stored font: ${fontFile} (${fontContentType}, ${fontBlob.size} bytes)`);
171
170
  } catch (fontErr) {
172
- log.warn(`Failed to cache font: ${fontFile}`, fontErr);
171
+ log.warn(`Failed to store font: ${fontFile}`, fontErr);
173
172
  }
174
173
  }));
175
174
  } else {
176
175
  const blob = await resp.blob();
177
- await staticCache.put(staticKey, new Response(blob, {
178
- headers: { 'Content-Type': contentType }
179
- }));
180
- log.info(`Cached static resource: ${filename} (${contentType}, ${blob.size} bytes)`);
176
+ const staticResp = await fetch(`/store/static/${filename}`, {
177
+ method: 'PUT',
178
+ headers: { 'Content-Type': contentType },
179
+ body: blob,
180
+ });
181
+ staticResp.body?.cancel();
182
+ log.info(`Stored static resource: ${filename} (${contentType}, ${blob.size} bytes)`);
181
183
  }
182
184
  } catch (error) {
183
- log.warn(`Failed to cache static resource: ${filename}`, error);
185
+ log.warn(`Failed to store static resource: ${filename}`, error);
184
186
  }
187
+ })();
188
+
189
+ _pendingStatic.set(filename, work);
190
+ return work.finally(() => _pendingStatic.delete(filename));
185
191
  }));
186
192
  }
187
193
 
194
+ // Store widget HTML AFTER all static deps are ready — iframe loads instantly on store,
195
+ // so bundle.min.js/fonts.css/fonts must already be in the ContentStore
196
+ const putResp = await fetch(`/store/widget/${layoutId}/${regionId}/${mediaId}`, {
197
+ method: 'PUT',
198
+ headers: { 'Content-Type': 'text/html; charset=utf-8' },
199
+ body: modifiedHtml,
200
+ });
201
+ putResp.body?.cancel();
202
+ log.info(`Stored widget HTML at ${cacheKey} (${modifiedHtml.length} bytes)`);
203
+
188
204
  return cacheKey;
189
205
  }
@@ -9,8 +9,8 @@
9
9
  * 1. CMS getResource returns HTML with signed URLs for bundle.min.js, fonts.css
10
10
  * 2. SW download manager may cache this raw HTML before the main thread processes it
11
11
  * 3. cacheWidgetHtml must rewrite CMS URLs → /cache/static/ and fetch the resources
12
- * 4. Widget iframe loads, SW serves bundle.min.js and fonts.css from static cache
13
- * 5. bundle.min.js runs getWidgetData → $.ajax("193.json") → SW serves from media cache
12
+ * 4. Widget iframe loads, SW serves bundle.min.js and fonts.css from ContentStore
13
+ * 5. bundle.min.js runs getWidgetData → $.ajax("193.json") → SW serves from store
14
14
  */
15
15
 
16
16
  import { describe, it, expect, beforeEach, vi } from 'vitest';
@@ -23,10 +23,6 @@ const SIGNED_PARAMS = 'displayId=152&type=P&itemId=1&X-Amz-Algorithm=AWS4-HMAC-S
23
23
 
24
24
  /**
25
25
  * Simulates actual CMS RSS ticker widget HTML (layout 472, region 223, widget 193).
26
- * The CMS generates this via getResource — it includes:
27
- * - Signed URLs for bundle.min.js and fonts.css (must be rewritten)
28
- * - Relative data URL "193.json" resolved via <base> tag
29
- * - Interactive Control (xiboIC) with hostAddress pointing at CMS
30
26
  */
31
27
  function makeRssTickerHtml() {
32
28
  return `<!DOCTYPE html>
@@ -79,50 +75,61 @@ function makeClockWidgetHtml() {
79
75
  </html>`;
80
76
  }
81
77
 
82
- // --- Mock Cache API ---
83
-
84
- const cacheStore = new Map();
85
- const mockCache = {
86
- put: vi.fn(async (url, response) => {
87
- const key = typeof url === 'string' ? url : url.toString();
88
- const text = await response.clone().text();
89
- cacheStore.set(key, text);
90
- }),
91
- match: vi.fn(async (key) => {
92
- const url = typeof key === 'string' ? key : (key.url || key.toString());
93
- const text = cacheStore.get(url);
94
- return text ? new Response(text) : undefined;
95
- }),
96
- };
78
+ // --- Mock: track PUT /store/... calls via fetch() ---
97
79
 
80
+ /** Map of storeKey → body text, populated by the fetch mock */
81
+ const storeContents = new Map();
98
82
  const fetchedUrls = [];
99
- global.fetch = vi.fn(async (url) => {
100
- fetchedUrls.push(url);
101
- if (url.includes('bundle.min.js')) {
102
- return new Response('var xiboIC = { init: function(){} };', { status: 200 });
103
- }
104
- if (url.includes('fonts.css')) {
105
- return new Response(`@font-face { font-family: "Poppins"; src: url("${CMS_BASE}/pwa/file?file=Poppins-Regular.ttf&${SIGNED_PARAMS}"); }`, { status: 200 });
106
- }
107
- if (url.includes('.ttf') || url.includes('.woff')) {
108
- return new Response(new Blob([new Uint8Array(100)]), { status: 200 });
109
- }
110
- return new Response('', { status: 404 });
111
- });
112
83
 
113
- global.caches = {
114
- open: vi.fn(async () => mockCache),
115
- };
84
+ function createFetchMock() {
85
+ return vi.fn(async (url, opts) => {
86
+ fetchedUrls.push(url);
87
+
88
+ // PUT /store/... — store content
89
+ if (opts?.method === 'PUT' && url.startsWith('/store/')) {
90
+ const body = typeof opts.body === 'string' ? opts.body : await opts.body?.text?.() || '';
91
+ // Use full URL path as key (e.g. /store/widget/472/223/193)
92
+ storeContents.set(url, body);
93
+ return new Response(JSON.stringify({ ok: true }), { status: 200 });
94
+ }
95
+
96
+ // HEAD /store/... — check existence
97
+ if (opts?.method === 'HEAD' && url.startsWith('/store/')) {
98
+ return new Response(null, { status: 404 }); // nothing pre-stored
99
+ }
100
+
101
+ // Proxy fetch for CMS resources
102
+ if (url.includes('bundle.min.js')) {
103
+ return new Response('var xiboIC = { init: function(){} };', { status: 200 });
104
+ }
105
+ if (url.includes('fonts.css')) {
106
+ return new Response(`@font-face { font-family: "Poppins"; src: url("${CMS_BASE}/pwa/file?file=Poppins-Regular.ttf&${SIGNED_PARAMS}"); }`, { status: 200 });
107
+ }
108
+ if (url.includes('.ttf') || url.includes('.woff')) {
109
+ return new Response(new Blob([new Uint8Array(100)]), { status: 200 });
110
+ }
111
+ return new Response('', { status: 404 });
112
+ });
113
+ }
116
114
 
117
115
  // --- Tests ---
118
116
 
119
117
  describe('cacheWidgetHtml', () => {
120
118
  beforeEach(() => {
121
- cacheStore.clear();
122
- vi.clearAllMocks();
119
+ storeContents.clear();
123
120
  fetchedUrls.length = 0;
121
+ vi.clearAllMocks();
122
+ global.fetch = createFetchMock();
124
123
  });
125
124
 
125
+ // Helper: get stored widget HTML (first widget entry)
126
+ function getStoredWidget() {
127
+ for (const [key, value] of storeContents) {
128
+ if (key.startsWith('/store/widget/')) return value;
129
+ }
130
+ return undefined;
131
+ }
132
+
126
133
  // --- Base tag injection ---
127
134
 
128
135
  describe('base tag injection', () => {
@@ -130,7 +137,7 @@ describe('cacheWidgetHtml', () => {
130
137
  const html = '<html><head><title>Widget</title></head><body>content</body></html>';
131
138
  await cacheWidgetHtml('472', '223', '193', html);
132
139
 
133
- const stored = cacheStore.values().next().value;
140
+ const stored = getStoredWidget();
134
141
  expect(stored).toContain('<base href=');
135
142
  expect(stored).toContain('/cache/media/">');
136
143
  });
@@ -139,7 +146,7 @@ describe('cacheWidgetHtml', () => {
139
146
  const html = '<div>no head tag</div>';
140
147
  await cacheWidgetHtml('472', '223', '193', html);
141
148
 
142
- const stored = cacheStore.values().next().value;
149
+ const stored = getStoredWidget();
143
150
  expect(stored).toContain('<base href=');
144
151
  });
145
152
  });
@@ -150,7 +157,7 @@ describe('cacheWidgetHtml', () => {
150
157
  it('rewrites bundle.min.js and fonts.css signed URLs', async () => {
151
158
  await cacheWidgetHtml('472', '223', '193', makeRssTickerHtml());
152
159
 
153
- const stored = cacheStore.values().next().value;
160
+ const stored = getStoredWidget();
154
161
  expect(stored).not.toContain(CMS_BASE);
155
162
  expect(stored).toContain('/cache/static/bundle.min.js');
156
163
  expect(stored).toContain('/cache/static/fonts.css');
@@ -159,7 +166,7 @@ describe('cacheWidgetHtml', () => {
159
166
  it('preserves the data URL (193.json) for SW interception', async () => {
160
167
  await cacheWidgetHtml('472', '223', '193', makeRssTickerHtml());
161
168
 
162
- const stored = cacheStore.values().next().value;
169
+ const stored = getStoredWidget();
163
170
  // 193.json is relative — resolved by <base> tag, not rewritten
164
171
  expect(stored).toContain('"193.json"');
165
172
  });
@@ -167,7 +174,7 @@ describe('cacheWidgetHtml', () => {
167
174
  it('rewrites xiboIC hostAddress from CMS to local path', async () => {
168
175
  await cacheWidgetHtml('472', '223', '193', makeRssTickerHtml());
169
176
 
170
- const stored = cacheStore.values().next().value;
177
+ const stored = getStoredWidget();
171
178
  expect(stored).not.toContain(`hostAddress: "${CMS_BASE}"`);
172
179
  expect(stored).toContain('/ic"');
173
180
  });
@@ -181,11 +188,11 @@ describe('cacheWidgetHtml', () => {
181
188
  expect(fontsFetched).toBe(true);
182
189
  });
183
190
 
184
- it('stores processed HTML at correct cache key', async () => {
191
+ it('stores processed HTML at correct store key', async () => {
185
192
  await cacheWidgetHtml('472', '223', '193', makeRssTickerHtml());
186
193
 
187
- const keys = [...cacheStore.keys()];
188
- const widgetKey = keys.find(k => k.includes('/cache/widget/472/223/193'));
194
+ const keys = [...storeContents.keys()];
195
+ const widgetKey = keys.find(k => k.includes('/store/widget/472/223/193'));
189
196
  expect(widgetKey).toBeTruthy();
190
197
  });
191
198
 
@@ -204,18 +211,18 @@ describe('cacheWidgetHtml', () => {
204
211
  it('rewrites CMS URLs and preserves relative PDF path', async () => {
205
212
  await cacheWidgetHtml('472', '221', '190', makePdfWidgetHtml());
206
213
 
207
- const stored = cacheStore.values().next().value;
214
+ const stored = getStoredWidget();
208
215
  expect(stored).not.toContain(CMS_BASE);
209
216
  expect(stored).toContain('/cache/static/bundle.min.js');
210
217
  // PDF data attribute "11.pdf" is relative — resolved by <base> tag
211
218
  expect(stored).toContain('"11.pdf"');
212
219
  });
213
220
 
214
- it('stores at correct widget cache key with layout/region/media IDs', async () => {
221
+ it('stores at correct widget store key with layout/region/media IDs', async () => {
215
222
  await cacheWidgetHtml('472', '221', '190', makePdfWidgetHtml());
216
223
 
217
- const keys = [...cacheStore.keys()];
218
- expect(keys.some(k => k.includes('/cache/widget/472/221/190'))).toBe(true);
224
+ const keys = [...storeContents.keys()];
225
+ expect(keys.some(k => k.includes('/store/widget/472/221/190'))).toBe(true);
219
226
  });
220
227
  });
221
228
 
@@ -225,7 +232,7 @@ describe('cacheWidgetHtml', () => {
225
232
  it('rewrites xmds.php signed URLs to local cache paths', async () => {
226
233
  await cacheWidgetHtml('1', '1', '1', makeClockWidgetHtml());
227
234
 
228
- const stored = cacheStore.values().next().value;
235
+ const stored = getStoredWidget();
229
236
  expect(stored).not.toContain('xmds.php');
230
237
  expect(stored).toContain('/cache/static/bundle.min.js');
231
238
  expect(stored).toContain('/cache/static/fonts.css');
@@ -237,10 +244,11 @@ describe('cacheWidgetHtml', () => {
237
244
  describe('idempotency', () => {
238
245
  it('does not add duplicate <base> tags on re-processing', async () => {
239
246
  await cacheWidgetHtml('472', '223', '193', makeRssTickerHtml());
240
- const firstPass = cacheStore.values().next().value;
247
+ const firstPass = getStoredWidget();
241
248
 
249
+ storeContents.clear();
242
250
  await cacheWidgetHtml('472', '223', '193', firstPass);
243
- const secondPass = cacheStore.values().next().value;
251
+ const secondPass = getStoredWidget();
244
252
 
245
253
  expect(secondPass).toBe(firstPass);
246
254
  });
@@ -248,10 +256,11 @@ describe('cacheWidgetHtml', () => {
248
256
  it('does not add duplicate CSS fix tags on re-processing', async () => {
249
257
  const html = '<html><head></head><body></body></html>';
250
258
  await cacheWidgetHtml('472', '223', '193', html);
251
- const firstPass = cacheStore.values().next().value;
259
+ const firstPass = getStoredWidget();
252
260
 
261
+ storeContents.clear();
253
262
  await cacheWidgetHtml('472', '223', '193', firstPass);
254
- const secondPass = cacheStore.values().next().value;
263
+ const secondPass = getStoredWidget();
255
264
 
256
265
  const baseCount = (secondPass.match(/<base /g) || []).length;
257
266
  const styleCount = (secondPass.match(/object-position:center center/g) || []).length;
@@ -270,7 +279,7 @@ describe('cacheWidgetHtml', () => {
270
279
  // Main thread finds it in cache and re-processes
271
280
  await cacheWidgetHtml('472', '223', '193', rawCmsHtml);
272
281
 
273
- const stored = cacheStore.values().next().value;
282
+ const stored = getStoredWidget();
274
283
  // CMS URLs must be rewritten even though HTML came from cache
275
284
  expect(stored).not.toContain(CMS_BASE);
276
285
  expect(stored).toContain('/cache/static/bundle.min.js');
@@ -283,15 +292,15 @@ describe('cacheWidgetHtml', () => {
283
292
  await cacheWidgetHtml('472', '221', '190', makePdfWidgetHtml());
284
293
  await cacheWidgetHtml('472', '223', '193', makeRssTickerHtml());
285
294
 
286
- const keys = [...cacheStore.keys()];
287
- const pdfKey = keys.find(k => k.includes('/cache/widget/472/221/190'));
288
- const rssKey = keys.find(k => k.includes('/cache/widget/472/223/193'));
295
+ const keys = [...storeContents.keys()];
296
+ const pdfKey = keys.find(k => k.includes('/store/widget/472/221/190'));
297
+ const rssKey = keys.find(k => k.includes('/store/widget/472/223/193'));
289
298
  expect(pdfKey).toBeTruthy();
290
299
  expect(rssKey).toBeTruthy();
291
300
 
292
301
  // Both should have CMS URLs rewritten
293
- expect(cacheStore.get(pdfKey)).not.toContain(CMS_BASE);
294
- expect(cacheStore.get(rssKey)).not.toContain(CMS_BASE);
302
+ expect(storeContents.get(pdfKey)).not.toContain(CMS_BASE);
303
+ expect(storeContents.get(rssKey)).not.toContain(CMS_BASE);
295
304
  });
296
305
  });
297
306
  });