stx-router 0.2.814 → 0.2.816

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.
Files changed (2) hide show
  1. package/dist/client.js +35 -3
  2. package/package.json +1 -1
package/dist/client.js CHANGED
@@ -3269,7 +3269,7 @@ else {
3269
3269
  // save data, and not for a link marked data-stx-prefetch="false" or
3270
3270
  // "hover" (hover and touch still prefetch everything).
3271
3271
  var sightObserver=null;
3272
- var sightCount=0;
3272
+ var sightCount=0;var sightBytes=0;var sightDocBytes;
3273
3273
  // Prefetching is for the next page. It must not compete with the paint of
3274
3274
  // this one, so sight and eager prefetch wait for first contentful paint
3275
3275
  // (stacksjs/stacks#3082's measurement: two 600 KB documents were already in flight
@@ -3298,6 +3298,22 @@ else {
3298
3298
  if(!o.prefetch||!o.cache||o.prefetchVisible===false||typeof IntersectionObserver!=='function')return;
3299
3299
  afterFirstPaint(observeLinksNow);
3300
3300
  }
3301
+ // How many bytes of other pages one page load may fetch on sight. An author
3302
+ // sets prefetchBudgetBytes; otherwise it is the size of the document this
3303
+ // origin actually served, read once from navigation timing, so an app of
3304
+ // small pages still prefetches freely and an app of large ones stops early.
3305
+ // Zero means the browser did not tell us, and only the count cap applies.
3306
+ function sightBudget(){
3307
+ if(typeof o.prefetchBudgetBytes==='number')return o.prefetchBudgetBytes;
3308
+ if(sightDocBytes===undefined){
3309
+ sightDocBytes=0;
3310
+ try{
3311
+ var nav=performance.getEntriesByType('navigation')[0];
3312
+ if(nav&&typeof nav.decodedBodySize==='number')sightDocBytes=nav.decodedBodySize;
3313
+ }catch(e){}
3314
+ }
3315
+ return sightDocBytes;
3316
+ }
3301
3317
  function observeLinksNow(){
3302
3318
  var c=navigator.connection;
3303
3319
  if(c&&(c.saveData||/2g/.test(c.effectiveType||'')))return;
@@ -3306,11 +3322,17 @@ else {
3306
3322
  if(!en.isIntersecting)return;
3307
3323
  sightObserver.unobserve(en.target);
3308
3324
  if(sightCount>=o.prefetchVisibleMax)return;
3325
+ // The count alone caps documents, not bytes, and this router navigates
3326
+ // by fetching a whole server-rendered page rather than a route chunk.
3327
+ // Twelve chunks is modest; twelve pages was 1.4 MB against a 717 KB
3328
+ // document on the Stacks dashboard. Stop on the budget as well.
3329
+ var budget=sightBudget();
3330
+ if(budget>0&&sightBytes>=budget)return;
3309
3331
  sightCount++;
3310
3332
  prefetchLink(en.target,false,true);
3311
3333
  });
3312
3334
  });
3313
- sightCount=0;
3335
+ sightCount=0;sightBytes=0;
3314
3336
  var root=activeRoot()||document.body;
3315
3337
  if(!root||!root.querySelectorAll)return;
3316
3338
  root.querySelectorAll('[data-stx-link][href]').forEach(function(a){
@@ -3645,7 +3667,14 @@ else {
3645
3667
  }
3646
3668
  var eager=link.getAttribute('data-stx-prefetch')==='eager';
3647
3669
  var wantsFragment=shouldUseFragmentResponse();
3648
- prefetching[key]=queuePrefetch(key,function(){return fetch(href,{priority:'low',headers:prefetchHeaders(wantsFragment)}).then(function(r){
3670
+ prefetching[key]=queuePrefetch(key,function(){
3671
+ // Charged here, where the job is dequeued, and not where the link came
3672
+ // into view: a viewport full of links is ONE observer callback, so every
3673
+ // job would be queued before the first byte had been counted and the
3674
+ // budget would never bite. Two prefetches run at a time, so by the time
3675
+ // this one starts the pages before it have been weighed.
3676
+ if(glance){var spent=sightBudget();if(spent>0&&sightBytes>=spent)return}
3677
+ return fetch(href,{priority:'low',headers:prefetchHeaders(wantsFragment)}).then(function(r){
3649
3678
  return readPrefetchResponse(r,wantsFragment);
3650
3679
  }).then(function(result){
3651
3680
  // A page in another layout cannot be swapped in as a fragment: the tap
@@ -3663,6 +3692,9 @@ else {
3663
3692
  if(eager)keepPage(key,result);
3664
3693
  }
3665
3694
  if(result){
3695
+ // Only what the viewport asked for is charged to the sight budget; a
3696
+ // hover or a touch is a person saying they want that page.
3697
+ if(glance&&result.html)sightBytes+=result.html.length;
3666
3698
  if(!glance)loadModuleRegistries(result.html,href);
3667
3699
  // Its stylesheet too, so the tap never waits on cssLoadTimeout for a
3668
3700
  // sheet it could have had all along. Added unapplied (media=print),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "stx-router",
3
3
  "type": "module",
4
- "version": "0.2.814",
4
+ "version": "0.2.816",
5
5
  "description": "File-based router for STX — .stx template discovery, nested layouts, typed route params, middleware, and client-side SPA navigation.",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",