textbrowser 0.54.9 → 0.54.11

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/CHANGES.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # CHANGES to `textbrowser`
2
2
 
3
+ ## 0.54.11
4
+
5
+ - fix: max concurrent prefetches
6
+
7
+ ## 0.54.10
8
+
9
+ - fix: more logging
10
+
3
11
  ## 0.54.9
4
12
 
5
13
  - fix: logging worker issues
@@ -55,10 +55,12 @@ async function activateCallback ({
55
55
  // anyways); also important to avoid conflicts with
56
56
  // already-running versions upon future sw updates
57
57
  log('Activate: Callback called');
58
+ log('Activate: Fetching files manifest', files);
58
59
  const r = await fetch(files);
59
60
  const {groups} = /** @type {import('./utils/WorkInfo.js').FilesObject} */ (
60
61
  await r.json()
61
62
  );
63
+ log('Activate: Manifest loaded; groups:', groups.length);
62
64
 
63
65
  /* eslint-disable jsdoc/reject-any-type -- Generic */
64
66
  /**
@@ -67,6 +69,7 @@ async function activateCallback ({
67
69
  */
68
70
  const addJSONFetch = (arr, path) => {
69
71
  /* eslint-enable jsdoc/reject-any-type -- Generic */
72
+ log('Activate: Queueing JSON fetch', basePath + path);
70
73
  arr.push(
71
74
  (async () => (await fetch(basePath + path)).json())()
72
75
  );
@@ -89,6 +92,13 @@ async function activateCallback ({
89
92
  const metadataFiles = [];
90
93
  groups.forEach(
91
94
  ({files: fileObjs, metadataBaseDirectory, schemaBaseDirectory}) => {
95
+ log(
96
+ 'Activate: Processing group',
97
+ metadataBaseDirectory,
98
+ schemaBaseDirectory,
99
+ 'files:',
100
+ fileObjs.length
101
+ );
92
102
  fileObjs.forEach(({file: {$ref: filePath}, metadataFile, schemaFile, name}) => {
93
103
  // We don't i18nize the name here
94
104
  dataFileNames.push(name);
@@ -98,6 +108,7 @@ async function activateCallback ({
98
108
  });
99
109
  }
100
110
  );
111
+ log('Activate: Awaiting queued fetches; works:', dataFiles.length);
101
112
  const promises = await Promise.all([
102
113
  ...dataFiles, ...schemaFiles, ...metadataFiles
103
114
  ]);
@@ -109,11 +120,14 @@ async function activateCallback ({
109
120
 
110
121
  log('Activate: Files fetched');
111
122
  const dbName = namespace + '-textbrowser-cache-data';
123
+ log('Activate: Deleting existing database', dbName);
112
124
  indexedDB.deleteDatabase(dbName);
113
125
  return new Promise((resolve, reject) => {
126
+ log('Activate: Opening database', dbName);
114
127
  const req = indexedDB.open(dbName);
115
128
  // @ts-expect-error Ok
116
129
  req.addEventListener('upgradeneeded', ({target: {result: db}}) => {
130
+ log('Activate: Database upgrade needed', dbName);
117
131
  db.onversionchange = () => {
118
132
  db.close();
119
133
  const err = /** @type {Error & {type: string}} */ (new Error('versionchange'));
@@ -122,6 +136,7 @@ async function activateCallback ({
122
136
  };
123
137
  dataFileResponses.forEach(({data: tableRows}, i) => {
124
138
  const dataFileName = dataFileNames[i];
139
+ log('Activate: Creating object store', dataFileName, 'rows:', tableRows.length);
125
140
  const store = db.createObjectStore('files-to-cache-' + dataFileName);
126
141
 
127
142
  const schemaFileResponse = schemaFileResponses[i];
@@ -180,6 +195,12 @@ async function activateCallback ({
180
195
  });
181
196
 
182
197
  const uniqueColumnIndexes = [...new Set(columnIndexes)];
198
+ log(
199
+ 'Activate: Preparing rows for store',
200
+ dataFileName,
201
+ 'unique indexes:',
202
+ uniqueColumnIndexes.length
203
+ );
183
204
 
184
205
  tableRows.forEach((tableRow, j) => {
185
206
  // Todo: Optionally send notice when complete
@@ -203,6 +224,7 @@ async function activateCallback ({
203
224
  // log('objRow', objRow);
204
225
  store.put(objRow, j);
205
226
  });
227
+ log('Activate: Finished object store population', dataFileName);
206
228
  });
207
229
  });
208
230
 
@@ -218,6 +240,7 @@ async function activateCallback ({
218
240
  * @param {Event & {error?: Error}} ev
219
241
  */
220
242
  const onerr = (ev) => {
243
+ log('Activate: Database request error event fired');
221
244
  const error = /** @type {Error & {type: string}} */ (
222
245
  ev.error ?? new Error('dbError')
223
246
  );
package/dist/sw-helper.js CHANGED
@@ -184,7 +184,7 @@ function swHelper (self) {
184
184
  * @returns {Promise<void>}
185
185
  */
186
186
  async function install (time) {
187
- post({type: 'beginInstall'});
187
+ await post({type: 'beginInstall'});
188
188
  log(`Install: Trying, attempt ${time}`);
189
189
  const now = Date.now();
190
190
  const json = await getJSON(/** @type {string} */ (pathToUserJSON));
@@ -249,7 +249,9 @@ function swHelper (self) {
249
249
  }, 10000);
250
250
  // .map((url) => url === 'index.html' ? new Request(url, {cache: 'reload'}) : url)
251
251
  try {
252
- const cachePromises = urlsToPrefetch.map(async (urlToPrefetch, idx) => {
252
+ const maxConcurrentPrefetches = 8;
253
+ const prefetchTimeoutMs = 120000;
254
+ const fetchAndCacheAsset = async (urlToPrefetch, idx) => {
253
255
  // This constructs a new URL object using the service worker's script
254
256
  // location as the base for relative URLs.
255
257
  pendingAssets.add(urlToPrefetch);
@@ -260,8 +262,14 @@ function swHelper (self) {
260
262
  const url = new URL(urlToPrefetch, location.href);
261
263
  url.search += (url.search ? '&' : '?') + 'cache-bust=' + now;
262
264
  const request = new Request(url, {mode: 'no-cors'});
265
+ const controller = new AbortController();
266
+ const timeout = setTimeout(() => {
267
+ controller.abort();
268
+ }, prefetchTimeoutMs);
263
269
  try {
264
- const response = await fetch(request);
270
+ const response = await fetch(request, {
271
+ signal: controller.signal
272
+ });
265
273
  if (response.status >= 400) {
266
274
  throw new Error('request for ' + urlToPrefetch +
267
275
  ' failed with status ' + response.statusText);
@@ -272,15 +280,36 @@ function swHelper (self) {
272
280
  return;
273
281
  } catch (error) {
274
282
  pendingAssets.delete(urlToPrefetch);
283
+ if (
284
+ error && typeof error === 'object' && 'name' in error &&
285
+ error.name === 'AbortError'
286
+ ) {
287
+ error = new Error(
288
+ `Timed out after ${prefetchTimeoutMs}ms while fetching ${urlToPrefetch}`
289
+ );
290
+ }
275
291
  logError(
276
292
  /** @type {Error} */
277
293
  (error),
278
294
  `Not caching ${urlToPrefetch} due to ${error}`
279
295
  );
280
296
  throw error;
297
+ } finally {
298
+ clearTimeout(timeout);
299
+ }
300
+ };
301
+
302
+ let nextAssetIndex = 0;
303
+ const workers = Array.from({
304
+ length: Math.min(maxConcurrentPrefetches, urlsToPrefetch.length)
305
+ }, async () => {
306
+ while (nextAssetIndex < urlsToPrefetch.length) {
307
+ const idx = nextAssetIndex;
308
+ nextAssetIndex++;
309
+ await fetchAndCacheAsset(urlsToPrefetch[idx], idx);
281
310
  }
282
311
  });
283
- await Promise.all(cachePromises);
312
+ await Promise.all(workers);
284
313
  log('Install: Pre-fetching complete.');
285
314
  } catch (error) {
286
315
  logError(
@@ -300,7 +329,7 @@ function swHelper (self) {
300
329
 
301
330
  // Although we only need one client to which to send
302
331
  // arguments, we want to signal phase complete to all
303
- post({type: 'finishedInstall'});
332
+ await post({type: 'finishedInstall'});
304
333
  }
305
334
 
306
335
  /**
@@ -309,7 +338,12 @@ function swHelper (self) {
309
338
  * @returns {Promise<void>}
310
339
  */
311
340
  async function activate (time) {
312
- post({type: 'beginActivate'});
341
+ console.log('Activate event worker callback entered', time);
342
+ await post({
343
+ type: 'log',
344
+ message: `Activate: Event callback entered (attempt ${time})`
345
+ });
346
+ await post({type: 'beginActivate'});
313
347
  log(`Activate: Trying, attempt ${time}`);
314
348
  const [json, cacheNames] = await Promise.all([
315
349
  getJSON(/** @type {string} */ (pathToUserJSON)),
@@ -329,9 +363,12 @@ function swHelper (self) {
329
363
  await activateCallback({namespace, files, basePath, log});
330
364
  log('Activate: Database changes completed');
331
365
 
366
+ await self.clients.claim();
367
+ log('Activate: Clients claimed');
368
+
332
369
  log(`Activate: Posting finished message to clients`);
333
370
  // Signal phase complete to all clients
334
- post({type: 'finishedActivate'});
371
+ await post({type: 'finishedActivate'});
335
372
  }
336
373
 
337
374
  // @ts-expect-error Ok
@@ -348,6 +385,7 @@ function swHelper (self) {
348
385
  });
349
386
 
350
387
  self.addEventListener('activate', (e) => {
388
+ console.log('sw activate event fired');
351
389
  // Erring is of no present use here:
352
390
  // https://github.com/w3c/ServiceWorker/issues/659#issuecomment-384919053
353
391
  e.waitUntil(tryAndRetry(activate, 5 * minutes, 'Error activating'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "textbrowser",
3
- "version": "0.54.9",
3
+ "version": "0.54.11",
4
4
  "description": "Multilinear text browser",
5
5
  "type": "module",
6
6
  "main": "dist/index-es.min.js",
@@ -55,10 +55,12 @@ export default async function activateCallback ({
55
55
  // anyways); also important to avoid conflicts with
56
56
  // already-running versions upon future sw updates
57
57
  log('Activate: Callback called');
58
+ log('Activate: Fetching files manifest', files);
58
59
  const r = await fetch(files);
59
60
  const {groups} = /** @type {import('./utils/WorkInfo.js').FilesObject} */ (
60
61
  await r.json()
61
62
  );
63
+ log('Activate: Manifest loaded; groups:', groups.length);
62
64
 
63
65
  /* eslint-disable jsdoc/reject-any-type -- Generic */
64
66
  /**
@@ -67,6 +69,7 @@ export default async function activateCallback ({
67
69
  */
68
70
  const addJSONFetch = (arr, path) => {
69
71
  /* eslint-enable jsdoc/reject-any-type -- Generic */
72
+ log('Activate: Queueing JSON fetch', basePath + path);
70
73
  arr.push(
71
74
  (async () => (await fetch(basePath + path)).json())()
72
75
  );
@@ -89,6 +92,13 @@ export default async function activateCallback ({
89
92
  const metadataFiles = [];
90
93
  groups.forEach(
91
94
  ({files: fileObjs, metadataBaseDirectory, schemaBaseDirectory}) => {
95
+ log(
96
+ 'Activate: Processing group',
97
+ metadataBaseDirectory,
98
+ schemaBaseDirectory,
99
+ 'files:',
100
+ fileObjs.length
101
+ );
92
102
  fileObjs.forEach(({file: {$ref: filePath}, metadataFile, schemaFile, name}) => {
93
103
  // We don't i18nize the name here
94
104
  dataFileNames.push(name);
@@ -98,6 +108,7 @@ export default async function activateCallback ({
98
108
  });
99
109
  }
100
110
  );
111
+ log('Activate: Awaiting queued fetches; works:', dataFiles.length);
101
112
  const promises = await Promise.all([
102
113
  ...dataFiles, ...schemaFiles, ...metadataFiles
103
114
  ]);
@@ -109,11 +120,14 @@ export default async function activateCallback ({
109
120
 
110
121
  log('Activate: Files fetched');
111
122
  const dbName = namespace + '-textbrowser-cache-data';
123
+ log('Activate: Deleting existing database', dbName);
112
124
  indexedDB.deleteDatabase(dbName);
113
125
  return new Promise((resolve, reject) => {
126
+ log('Activate: Opening database', dbName);
114
127
  const req = indexedDB.open(dbName);
115
128
  // @ts-expect-error Ok
116
129
  req.addEventListener('upgradeneeded', ({target: {result: db}}) => {
130
+ log('Activate: Database upgrade needed', dbName);
117
131
  db.onversionchange = () => {
118
132
  db.close();
119
133
  const err = /** @type {Error & {type: string}} */ (new Error('versionchange'));
@@ -122,6 +136,7 @@ export default async function activateCallback ({
122
136
  };
123
137
  dataFileResponses.forEach(({data: tableRows}, i) => {
124
138
  const dataFileName = dataFileNames[i];
139
+ log('Activate: Creating object store', dataFileName, 'rows:', tableRows.length);
125
140
  const store = db.createObjectStore('files-to-cache-' + dataFileName);
126
141
 
127
142
  const schemaFileResponse = schemaFileResponses[i];
@@ -180,6 +195,12 @@ export default async function activateCallback ({
180
195
  });
181
196
 
182
197
  const uniqueColumnIndexes = [...new Set(columnIndexes)];
198
+ log(
199
+ 'Activate: Preparing rows for store',
200
+ dataFileName,
201
+ 'unique indexes:',
202
+ uniqueColumnIndexes.length
203
+ );
183
204
 
184
205
  tableRows.forEach((tableRow, j) => {
185
206
  // Todo: Optionally send notice when complete
@@ -203,6 +224,7 @@ export default async function activateCallback ({
203
224
  // log('objRow', objRow);
204
225
  store.put(objRow, j);
205
226
  });
227
+ log('Activate: Finished object store population', dataFileName);
206
228
  });
207
229
  });
208
230
 
@@ -218,6 +240,7 @@ export default async function activateCallback ({
218
240
  * @param {Event & {error?: Error}} ev
219
241
  */
220
242
  const onerr = (ev) => {
243
+ log('Activate: Database request error event fired');
221
244
  const error = /** @type {Error & {type: string}} */ (
222
245
  ev.error ?? new Error('dbError')
223
246
  );