stx-router 0.2.66 → 0.2.69
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/client.js +59 -10
- package/dist/client.js.map +3 -3
- package/dist/index.js +59 -10
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -4,10 +4,11 @@ function getRouterScript() {
|
|
|
4
4
|
return `
|
|
5
5
|
;(function(){
|
|
6
6
|
'use strict';
|
|
7
|
-
|
|
7
|
+
var ROUTER_REV=3;
|
|
8
|
+
if(window.__stxRouter&&window.__stxRouter.__rev===ROUTER_REV)return;
|
|
8
9
|
|
|
9
10
|
// \u2500\u2500 Configuration \u2500\u2500
|
|
10
|
-
var defaults={container:'main',loadingClass:'stx-navigating',viewTransitions:true,cache:true,scrollToTop:true,prefetch:true,progress:true,progressColor:'#78dce8',progressHeight:'2px',interceptAllLinks:false};
|
|
11
|
+
var defaults={container:'main',loadingClass:'stx-navigating',viewTransitions:true,cache:true,scrollToTop:true,prefetch:true,progress:true,progressColor:'#78dce8',progressHeight:'2px',interceptAllLinks:false,prefetchCacheMax:50};
|
|
11
12
|
var o=Object.assign({},defaults,window.__stxRouterConfig||{},window.STX_ROUTER_OPTIONS||{});
|
|
12
13
|
var containerSel=o.container;
|
|
13
14
|
var debug=!!o.debug;
|
|
@@ -67,6 +68,40 @@ function getRouterScript() {
|
|
|
67
68
|
var prefetching={};
|
|
68
69
|
var isNavigating=false;
|
|
69
70
|
|
|
71
|
+
// LRU bookkeeping for cache/layoutCache/layoutGroupCache. Pre-fix
|
|
72
|
+
// (stacksjs/stx#1719) these were plain objects with no eviction, so
|
|
73
|
+
// hover-prefetch on a sidebar of 200 internal links retained ~10MB
|
|
74
|
+
// of HTML strings for the page's lifetime. cacheOrder is a most-
|
|
75
|
+
// recently-used-last list of keys; setCache pushes new entries and
|
|
76
|
+
// evicts the oldest when length exceeds o.prefetchCacheMax (default
|
|
77
|
+
// 50). touchCache promotes a key on cache-hit. We use a plain array
|
|
78
|
+
// rather than a Map to keep the diff localized and the runtime
|
|
79
|
+
// hand-minified shape consistent.
|
|
80
|
+
var cacheOrder=[];
|
|
81
|
+
function setCache(key,html,layout,group){
|
|
82
|
+
if(!(key in cache))cacheOrder.push(key);
|
|
83
|
+
cache[key]=html;
|
|
84
|
+
layoutCache[key]=layout;
|
|
85
|
+
layoutGroupCache[key]=group;
|
|
86
|
+
while(cacheOrder.length>o.prefetchCacheMax){
|
|
87
|
+
var oldest=cacheOrder.shift();
|
|
88
|
+
delete cache[oldest];
|
|
89
|
+
delete layoutCache[oldest];
|
|
90
|
+
delete layoutGroupCache[oldest];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function touchCache(key){
|
|
94
|
+
var i=cacheOrder.indexOf(key);
|
|
95
|
+
if(i>=0){cacheOrder.splice(i,1);cacheOrder.push(key)}
|
|
96
|
+
}
|
|
97
|
+
function evictCache(key){
|
|
98
|
+
var i=cacheOrder.indexOf(key);
|
|
99
|
+
if(i>=0)cacheOrder.splice(i,1);
|
|
100
|
+
delete cache[key];
|
|
101
|
+
delete layoutCache[key];
|
|
102
|
+
delete layoutGroupCache[key];
|
|
103
|
+
}
|
|
104
|
+
|
|
70
105
|
// Extract top-level CSS blocks (rules AND @media blocks with nested braces).
|
|
71
106
|
// The old regex ([^{}]+{[^{}]*}) silently dropped @media blocks because
|
|
72
107
|
// it couldn't handle nested braces \u2014 causing responsive classes (lg:, md:)
|
|
@@ -134,6 +169,12 @@ function getRouterScript() {
|
|
|
134
169
|
var u=new URL(url,location.origin);
|
|
135
170
|
return u.pathname+u.search;
|
|
136
171
|
}
|
|
172
|
+
// Keep SPA fetches inside the active locale (/en/... when viewing English).
|
|
173
|
+
function withCurrentLocale(href){
|
|
174
|
+
var d=window.__stxI18n;
|
|
175
|
+
if(d&&typeof d.localizeHref==='function')return d.localizeHref(href);
|
|
176
|
+
return href;
|
|
177
|
+
}
|
|
137
178
|
function defaultLayoutGroup(layout){
|
|
138
179
|
if(!layout)return 'app';
|
|
139
180
|
var clean=String(layout).replace(/\\\\/g,'/');
|
|
@@ -177,6 +218,9 @@ function getRouterScript() {
|
|
|
177
218
|
}
|
|
178
219
|
|
|
179
220
|
function navigate(url,pushState,force){
|
|
221
|
+
// Lang-picker passes force=true with an already-localized path (/en/...).
|
|
222
|
+
// Re-localizing would map it back to the *current* locale and no-op.
|
|
223
|
+
if(!force) url=withCurrentLocale(url);
|
|
180
224
|
log('[router] navigate() called:',url,'isNavigating:',isNavigating);
|
|
181
225
|
if(isNavigating)return;
|
|
182
226
|
var t=new URL(url,location.origin);
|
|
@@ -209,6 +253,9 @@ function getRouterScript() {
|
|
|
209
253
|
function done(){isNavigating=false;document.body.classList.remove(o.loadingClass);finishProgress()}
|
|
210
254
|
|
|
211
255
|
if(o.cache&&cache[targetPath]&&!force){
|
|
256
|
+
// Cache hit \u2192 promote in LRU order so this entry survives eviction
|
|
257
|
+
// longer than other entries that haven't been touched.
|
|
258
|
+
touchCache(targetPath);
|
|
212
259
|
if(checkLayoutChange(layoutCache[targetPath],url,layoutGroupCache[targetPath])){
|
|
213
260
|
// Layout changed \u2014 fetch full page and do full document swap
|
|
214
261
|
log('[router] cache hit but layout changed \u2014 fetching full page');
|
|
@@ -228,7 +275,7 @@ function getRouterScript() {
|
|
|
228
275
|
done();
|
|
229
276
|
}
|
|
230
277
|
else {
|
|
231
|
-
if(force&&cache[targetPath])
|
|
278
|
+
if(force&&cache[targetPath])evictCache(targetPath);
|
|
232
279
|
// First fetch as fragment (SPA mode) when the configured container is
|
|
233
280
|
// the page content. Custom app-shell containers need full documents so
|
|
234
281
|
// the router does not inject a <main> fragment into the wrong element.
|
|
@@ -254,7 +301,7 @@ else {
|
|
|
254
301
|
}).then(function(result){
|
|
255
302
|
if(!result)return;
|
|
256
303
|
if(result.isFragment)result.html='<!--stx-fragment-->'+result.html;
|
|
257
|
-
if(o.cache)
|
|
304
|
+
if(o.cache)setCache(targetPath,result.html,result.layout,result.layoutGroup);
|
|
258
305
|
swap(result.html,targetPath,pushState,targetHash);
|
|
259
306
|
}).catch(function(err){
|
|
260
307
|
console.error('[router] fetch error:',err);
|
|
@@ -628,6 +675,7 @@ else {
|
|
|
628
675
|
if(href.startsWith('http')||href.startsWith('#')||href.startsWith('mailto:')||href.startsWith('tel:')||href.startsWith('javascript:'))return false;
|
|
629
676
|
if(link.target==='_blank')return false;
|
|
630
677
|
if(link.hasAttribute('data-stx-no-router')||link.hasAttribute('data-no-router')||link.hasAttribute('download'))return false;
|
|
678
|
+
if(href.startsWith('?'))return true;
|
|
631
679
|
if(href===location.pathname)return false;
|
|
632
680
|
if(!getContainer())return false;
|
|
633
681
|
return true;
|
|
@@ -654,7 +702,7 @@ else {
|
|
|
654
702
|
e.preventDefault();
|
|
655
703
|
e.stopPropagation();
|
|
656
704
|
log('[router] navigating to:',href);
|
|
657
|
-
navigate(href);
|
|
705
|
+
navigate(withCurrentLocale(href));
|
|
658
706
|
},true);
|
|
659
707
|
|
|
660
708
|
// \u2500\u2500 Back/forward \u2500\u2500
|
|
@@ -668,7 +716,7 @@ else {
|
|
|
668
716
|
if(!e.target||!e.target.closest)return;
|
|
669
717
|
var link=e.target.closest('[data-stx-link]');
|
|
670
718
|
if(!link)return;
|
|
671
|
-
var href=link.getAttribute('href');
|
|
719
|
+
var href=withCurrentLocale(link.getAttribute('href'));
|
|
672
720
|
var key=cacheKey(href);
|
|
673
721
|
if(cache[key]||prefetching[key])return;
|
|
674
722
|
prefetching[key]=true;
|
|
@@ -679,7 +727,7 @@ else {
|
|
|
679
727
|
var pGroup=r.headers.get('X-STX-Layout-Group')||'';
|
|
680
728
|
return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}});
|
|
681
729
|
}).then(function(result){
|
|
682
|
-
if(o.cache)
|
|
730
|
+
if(o.cache)setCache(key,result.html,result.layout,result.layoutGroup);
|
|
683
731
|
}).catch(function(){}).finally(function(){delete prefetching[key]});
|
|
684
732
|
},true);
|
|
685
733
|
}
|
|
@@ -776,15 +824,16 @@ else {
|
|
|
776
824
|
var key=cacheKey(url);
|
|
777
825
|
if(!cache[key]){
|
|
778
826
|
var wantsFragment=shouldUseFragmentResponse();
|
|
779
|
-
fetch(url,{headers:wantsFragment?{'X-STX-Router':'true'}:{'Accept':'text/html'}}).then(function(r){var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';var pLayout=r.headers.get('X-STX-Layout')||'';var pGroup=r.headers.get('X-STX-Layout-Group')||'';return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}})}).then(function(result){
|
|
827
|
+
fetch(url,{headers:wantsFragment?{'X-STX-Router':'true'}:{'Accept':'text/html'}}).then(function(r){var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';var pLayout=r.headers.get('X-STX-Layout')||'';var pGroup=r.headers.get('X-STX-Layout-Group')||'';return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}})}).then(function(result){setCache(key,result.html,result.layout,result.layoutGroup)}).catch(function(){});
|
|
780
828
|
}
|
|
781
829
|
},
|
|
782
|
-
clearCache:function(){for(var k in cache)delete cache[k];for(var lk in layoutCache)delete layoutCache[lk];for(var gk in layoutGroupCache)delete layoutGroupCache[gk]},
|
|
830
|
+
clearCache:function(){for(var k in cache)delete cache[k];for(var lk in layoutCache)delete layoutCache[lk];for(var gk in layoutGroupCache)delete layoutGroupCache[gk];cacheOrder.length=0},
|
|
783
831
|
cache:cache,
|
|
784
832
|
swap:swap,
|
|
785
833
|
updateNav:updateNav
|
|
786
834
|
};
|
|
787
835
|
|
|
836
|
+
router.__rev=ROUTER_REV;
|
|
788
837
|
window.__stxRouter=router;
|
|
789
838
|
window.stxRouter=router;
|
|
790
839
|
if(window.stx)window.stx.router=router;
|
|
@@ -805,4 +854,4 @@ export {
|
|
|
805
854
|
getRouterScript
|
|
806
855
|
};
|
|
807
856
|
|
|
808
|
-
//# debugId=
|
|
857
|
+
//# debugId=EBA4E6561C5FCAFB64756E2164756E21
|
package/dist/client.js.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/client.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/* eslint-disable prefer-const, no-console, style/max-statements-per-line, general/prefer-template */\n/**\n * STX Router - Canonical SPA Router\n *\n * Single source of truth for client-side navigation.\n * Injected via @stxRouter directive or auto-loaded with the signals runtime.\n *\n * Features:\n * - Click interception for internal links\n * - History API pushState navigation\n * - View Transitions API with CSS fade fallback\n * - <head> style swapping (prevents unstyled flash)\n * - Smart script filtering (skip signals runtime, layout guards)\n * - Page prefetching on hover\n * - Response caching (5-minute TTL)\n * - Active link class management (data-stx-link)\n * - Loading indicator bar\n * - Configurable via window.STX_ROUTER_OPTIONS or window.__stxRouterConfig\n */\nexport function getRouterScript(): string {\n return `\n;(function(){\n 'use strict';\n if(window.__stxRouter)return;\n\n // ── Configuration ──\n var defaults={container:'main',loadingClass:'stx-navigating',viewTransitions:true,cache:true,scrollToTop:true,prefetch:true,progress:true,progressColor:'#78dce8',progressHeight:'2px',interceptAllLinks:false};\n var o=Object.assign({},defaults,window.__stxRouterConfig||{},window.STX_ROUTER_OPTIONS||{});\n var containerSel=o.container;\n var debug=!!o.debug;\n function log(){if(debug&&typeof console!=='undefined'&&console.log)console.log.apply(console,arguments)}\n function runViewTransition(callback){\n if(!o.viewTransitions||!document.startViewTransition)return false;\n try{\n var transition=document.startViewTransition(callback);\n var onAbort=function(err){log('[router] view transition aborted:',err&&err.message?err.message:err)};\n if(transition&&transition.finished&&transition.finished.catch)transition.finished.catch(onAbort);\n if(transition&&transition.updateCallbackDone&&transition.updateCallbackDone.catch)transition.updateCallbackDone.catch(onAbort);\n return true;\n }catch(err){\n log('[router] view transition unavailable:',err&&err.message?err.message:err);\n return false;\n }\n }\n\n // ── Progress bar (0→100% at top of viewport) ──\n // Native loading indicator baked into the router. Starts when navigation\n // begins, trickles up to ~90% during fetch, snaps to 100% + fades on\n // completion. Opt-out via window.__stxRouterConfig={progress:false}.\n var progEl=null;\n var progVal=0;\n var progTimer=null;\n function setProgress(v){\n progVal=v<0?0:(v>100?100:v);\n if(!progEl)return;\n progEl.style.opacity=progVal>0?'1':'0';\n progEl.style.transform='scaleX('+(progVal/100)+')';\n }\n function startProgress(){\n if(!o.progress||!progEl)return;\n if(progTimer){clearInterval(progTimer);progTimer=null}\n setProgress(8);\n progTimer=setInterval(function(){\n if(progVal>=90)return;\n // Asymptotic trickle — fast at first, slower near 90%\n var inc=Math.max(0.4,(90-progVal)*0.08);\n setProgress(progVal+inc);\n },160);\n }\n function finishProgress(){\n if(!o.progress||!progEl)return;\n if(progTimer){clearInterval(progTimer);progTimer=null}\n setProgress(100);\n // Fade out, then reset. Delays chosen so the \"full\" state is briefly\n // visible before the bar disappears.\n setTimeout(function(){\n if(!progEl)return;\n progEl.style.opacity='0';\n setTimeout(function(){progVal=0;if(progEl)progEl.style.transform='scaleX(0)'},260);\n },180);\n }\n\n var cache={};\n var prefetching={};\n var isNavigating=false;\n\n // Extract top-level CSS blocks (rules AND @media blocks with nested braces).\n // The old regex ([^{}]+\\{[^{}]*\\}) silently dropped @media blocks because\n // it couldn't handle nested braces — causing responsive classes (lg:, md:)\n // to vanish after SPA navigation.\n function extractCssBlocks(css){\n var blocks=[];\n var i=0;\n var len=css.length;\n while(i<len){\n // Skip whitespace\n while(i<len&&(css[i]===' '||css[i]==='\\\\n'||css[i]==='\\\\r'||css[i]==='\\\\t'))i++;\n if(i>=len)break;\n // Find the start of a block (first {)\n var blockStart=i;\n var bracePos=css.indexOf('{',i);\n if(bracePos===-1)break;\n // Walk forward tracking brace depth to find the matching close\n var depth=0;\n var j=bracePos;\n while(j<len){\n if(css[j]==='{')depth++;\n else if(css[j]==='}'){depth--;if(depth===0){j++;break}}\n j++;\n }\n if(depth!==0)break;\n blocks.push(css.slice(blockStart,j).trim());\n i=j;\n }\n return blocks;\n }\n\n function mergeCrosswindCSS(existing,incoming){\n var blocks=extractCssBlocks(incoming);\n var newBlocks=[];\n for(var bi=0;bi<blocks.length;bi++){\n if(existing.indexOf(blocks[bi])===-1)newBlocks.push(blocks[bi]);\n }\n if(newBlocks.length>0)return existing+'\\\\n'+newBlocks.join('\\\\n');\n return null;\n }\n\n function getContainer(){\n return document.querySelector(containerSel)||document.querySelector('[data-stx-content]')||document.querySelector('main');\n }\n\n // ── Navigation ──\n // Layout group detection — layouts in the same group share a <main> container.\n // Only truly different layout groups trigger a full page reload.\n var layoutCache={};\n var layoutGroupCache={};\n // Track executed script hashes to prevent redeclaration errors on navigation.\n // Layout-level scripts (theme, nav setup) execute on initial page load and\n // should NOT re-execute when navigating to another page with the same layout.\n var executedScriptHashes={};\n function hashScript(code){\n var h=0;for(var i=0;i<code.length;i++){h=((h<<5)-h)+code.charCodeAt(i);h|=0}\n return h;\n }\n // Record scripts from the initial page load\n document.querySelectorAll('script').forEach(function(s){\n var text=s.textContent||'';\n if(text.trim()&&!s.hasAttribute('src'))executedScriptHashes[hashScript(text)]=1;\n });\n function cacheKey(url){\n var u=new URL(url,location.origin);\n return u.pathname+u.search;\n }\n function defaultLayoutGroup(layout){\n if(!layout)return 'app';\n var clean=String(layout).replace(/\\\\\\\\/g,'/');\n var parts=clean.split('/').filter(Boolean);\n var idx=parts.lastIndexOf('layouts');\n var part=idx>=0?parts[idx+1]:parts[parts.length-1];\n return part?part.replace(/\\\\.stx$/i,''):'app';\n }\n function getCurrentLayoutGroup(){\n var meta=document.querySelector('meta[name=\"stx-layout-group\"]');\n if(meta&&meta.getAttribute('content'))return meta.getAttribute('content');\n var layout=document.querySelector('meta[name=\"stx-layout\"]');\n return defaultLayoutGroup(layout?layout.getAttribute('content'):'');\n }\n function getDocLayoutGroup(doc,layout){\n var meta=doc&&doc.querySelector?doc.querySelector('meta[name=\"stx-layout-group\"]'):null;\n return meta&&meta.getAttribute('content')?meta.getAttribute('content'):defaultLayoutGroup(layout||'');\n }\n function checkLayoutChange(newLayout,targetUrl,newGroup){\n var currentLayout=document.querySelector('meta[name=\"stx-layout\"]');\n var curLayoutName=currentLayout?currentLayout.getAttribute('content'):'';\n var curGroup=getCurrentLayoutGroup();\n var nextGroup=newGroup||defaultLayoutGroup(newLayout);\n log('[router] layout check: current='+curLayoutName+'('+curGroup+') new='+(newLayout||'')+'('+nextGroup+')');\n // Different layout GROUP (e.g. app → auth): full reload\n if(curGroup!==nextGroup){\n log('[router] layout group change:',curGroup,'→',nextGroup,'— full reload to:',targetUrl);\n return true;\n }\n // Same group but different SPECIFIC layout (e.g. layouts/app → layouts/coach):\n // full body swap so nav, sidebar, and other layout-level elements update\n if(curLayoutName && newLayout && curLayoutName!==newLayout){\n log('[router] layout name change:',curLayoutName,'→',newLayout,'— full body swap');\n return true;\n }\n return false;\n }\n\n function shouldUseFragmentResponse(){\n return containerSel==='main'||containerSel==='[data-stx-content]';\n }\n\n function navigate(url,pushState,force){\n log('[router] navigate() called:',url,'isNavigating:',isNavigating);\n if(isNavigating)return;\n var t=new URL(url,location.origin);\n\n if(t.origin!==location.origin){location.href=url;return}\n\n if(t.pathname===location.pathname&&t.hash){\n if(pushState!==false)history.pushState({},'',t.href);\n var el=document.querySelector(t.hash);\n if(el)el.scrollIntoView({behavior:'smooth'});\n return;\n }\n\n // Skip if user clicks a link to the page they're already on. We\n // deliberately allow popstate (pushState===false) through because\n // the browser has already updated location.href to the popped\n // entry — without this allowance, hitting back would early-return\n // and the visible page content would be left frozen on the\n // forward-navigation page.\n if(pushState!==false&&t.href===location.href&&!t.hash&&!force)return;\n\n isNavigating=true;\n document.body.classList.add(o.loadingClass);\n startProgress();\n\n var targetHref=t.href;\n var targetPath=cacheKey(url);\n var targetHash=t.hash;\n\n function done(){isNavigating=false;document.body.classList.remove(o.loadingClass);finishProgress()}\n\n if(o.cache&&cache[targetPath]&&!force){\n if(checkLayoutChange(layoutCache[targetPath],url,layoutGroupCache[targetPath])){\n // Layout changed — fetch full page and do full document swap\n log('[router] cache hit but layout changed — fetching full page');\n fetch(url,{headers:{'Accept':'text/html'}}).then(function(r){\n if(!r.ok)throw new Error(r.status);\n return r.text();\n }).then(function(html){\n log('[router] full page fetched from cache path, len:',html.length);\n swap(html,targetPath,pushState,targetHash);\n }).catch(function(err){\n console.error('[router] full page fetch error:',err);\n location.href=url;\n }).finally(done);\n return;\n }\n swap(cache[targetPath],targetPath,pushState,targetHash);\n done();\n }\nelse {\n if(force&&cache[targetPath])delete cache[targetPath];\n // First fetch as fragment (SPA mode) when the configured container is\n // the page content. Custom app-shell containers need full documents so\n // the router does not inject a <main> fragment into the wrong element.\n var wantsFragment=shouldUseFragmentResponse();\n fetch(url,{headers:wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'}}).then(function(r){\n if(!r.ok)throw new Error(r.status);\n var isFragment=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';\n var newLayout=r.headers.get('X-STX-Layout')||'';\n var newGroup=r.headers.get('X-STX-Layout-Group')||'';\n // Layout change? Fetch the FULL page (no X-STX-Router header) and do full document swap\n if(isFragment&&checkLayoutChange(newLayout,url,newGroup)){\n log('[router] layout change — fetching full page for document swap');\n return fetch(url,{headers:{'Accept':'text/html'}}).then(function(fullRes){\n log('[router] full page fetched:',fullRes.status,'ok:',fullRes.ok);\n if(!fullRes.ok)throw new Error(fullRes.status);\n return fullRes.text().then(function(html){\n log('[router] full page html length:',html.length);\n return{html:html,isFragment:false,layout:newLayout,layoutGroup:newGroup};\n });\n });\n }\n return r.text().then(function(html){return{html:html,isFragment:isFragment,layout:newLayout,layoutGroup:newGroup}});\n }).then(function(result){\n if(!result)return;\n if(result.isFragment)result.html='<!--stx-fragment-->'+result.html;\n if(o.cache){cache[targetPath]=result.html;layoutCache[targetPath]=result.layout;layoutGroupCache[targetPath]=result.layoutGroup}\n swap(result.html,targetPath,pushState,targetHash);\n }).catch(function(err){\n console.error('[router] fetch error:',err);\n location.href=url;\n }).finally(done);\n }\n }\n\n function swap(html,url,pushState,hash){\n var isFragment=html.indexOf('<!--stx-fragment-->')===0;\n if(isFragment)html=html.slice('<!--stx-fragment-->'.length);\n var currentContent=getContainer();\n log('[router] swap: isFragment='+isFragment+' container='+!!currentContent+' tag='+(currentContent&¤tContent.tagName)+' selector='+containerSel+' htmlLen='+html.length);\n if(!currentContent){log('[router] no container — falling back');location.href=url;return}\n\n // Fragment mode: server returned just the page content (no document wrapper)\n if(isFragment){\n if(window.stx&&window.stx._cleanupContainer)window.stx._cleanupContainer(currentContent);\n function doFragSwap(){\n // Extract scripts from fragment before injecting HTML\n var fragScripts=[];\n var fragStyles=[];\n var fragCrosswindCSS=null;\n var cleanFrag=html.replace(new RegExp('<scr'+'ipt\\\\\\\\b[^>]*>([\\\\\\\\s\\\\\\\\S]*?)<\\\\\\\\/scr'+'ipt>','gi'),function(m,code){\n if(code&&code.trim())fragScripts.push(code);\n return '';\n });\n cleanFrag=cleanFrag.replace(new RegExp('<sty'+'le\\\\\\\\b([^>]*)>([\\\\\\\\s\\\\\\\\S]*?)<\\\\\\\\/sty'+'le>','gi'),function(m,attrs,css){\n if(attrs.indexOf('data-crosswind')!==-1){\n fragCrosswindCSS=css;\n }else{\n fragStyles.push({attrs:attrs,css:css});\n }\n return '';\n });\n // Remove old page styles (not crosswind — that gets merged)\n document.querySelectorAll('style[data-stx-page]').forEach(function(s){s.remove()});\n // Merge crosswind CSS from fragment into existing crosswind style\n if(fragCrosswindCSS){\n var curCrosswind=document.querySelector('head style[data-crosswind]');\n if(curCrosswind){\n var merged=mergeCrosswindCSS(curCrosswind.textContent||'',fragCrosswindCSS);\n if(merged)curCrosswind.textContent=merged;\n }else{\n var cw=document.createElement('style');\n cw.setAttribute('data-crosswind','generated');\n cw.textContent=fragCrosswindCSS;\n document.head.appendChild(cw);\n }\n }\n // Add new page styles\n fragStyles.forEach(function(s){\n var el=document.createElement('style');\n el.textContent=s.css;\n el.setAttribute('data-stx-page','');\n document.head.appendChild(el);\n });\n // Swap content\n currentContent.innerHTML=cleanFrag;\n // Remove old page scripts\n document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});\n if(pushState!==false)history.pushState({},'',url+(hash||''));\n updateNav(url);\n updateActiveLinks();\n if(o.scrollToTop&&!hash)window.scrollTo({top:0,behavior:'instant'});\n else if(hash){var el=document.querySelector(hash);if(el)el.scrollIntoView({behavior:'smooth'})}\n window.dispatchEvent(new CustomEvent('stx:navigate',{detail:{url:url}}));\n // Execute page scripts FIRST — they define setup functions and set _latestSetup\n log('[router] frag scripts:', fragScripts.length);\n document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});\n fragScripts.forEach(function(code){\n log('[router] exec script len:', code.length, 'has __stx_setup:', code.indexOf('__stx_setup')>-1);\n // Skip scripts that were already executed (layout-level partials\n // like theme.stx, stores.stx, nav.stx). Their top-level const/let\n // declarations would throw \"Identifier has already been declared\"\n // on re-execution. Setup functions (__stx_setup_) must always\n // re-execute because each page has its own setup.\n var h=hashScript(code);\n var isSetup=code.indexOf('__stx_setup_')!==-1;\n // Compute isAlreadyScoped first so the dedup check can exempt\n // self-contained IIFE scripts. data-stx-scoped scope IIFEs\n // (the (function(){ ... })() shape) must re-execute on every\n // navigation. They own their own scope and won't collide\n // with prior runs, AND _cleanupContainer deleted their\n // entry from window.stx._scopes on the way out. Without\n // this exemption, navigating away and back leaves the page\n // leaf with no scope registered: bindIf/@event never re-bind\n // and all :if branches stay visible at once.\n // Mirrors the full-doc swap path's runAlways escape below.\n var isAlreadyScoped=code.trimStart().charAt(0)==='('||code.trimStart().charAt(0)===';'||code.indexOf('window.stx.mount')>-1;\n if(!isSetup&&!isAlreadyScoped&&executedScriptHashes[h]){\n log('[router] skipping already-executed script (hash dedup)');\n return;\n }\n executedScriptHashes[h]=1;\n var ns=document.createElement('script');\n var hasImport=code.indexOf('import ')!==-1;\n if(hasImport){\n ns.type='module';\n }\n ns.textContent=(hasImport||isAlreadyScoped)?code:'{'+code+'}';\n ns.setAttribute('data-stx-page','');\n document.body.appendChild(ns);\n });\n log('[router] scripts done. _latestSetup:', !!window.stx._latestSetup);\n // THEN fire stx:load — now _latestSetup is set and processElement has the right scope\n window.dispatchEvent(new Event('stx:load'));\n }\n if(runViewTransition(doFragSwap)){}\n else{currentContent.style.transition='opacity 0.12s ease-out';currentContent.style.opacity='0';setTimeout(function(){doFragSwap();currentContent.style.opacity='1';setTimeout(function(){currentContent.style.transition=''},150)},120)}\n return;\n }\n\n // Full document mode: parse with DOMParser and extract container content\n var parser=new DOMParser();\n var doc=parser.parseFromString(html,'text/html');\n var newContent=doc.querySelector(containerSel)||doc.querySelector('[data-stx-content]')||doc.querySelector('main');\n if(!newContent){location.href=url;return}\n\n // Clean up existing signals/effects\n if(window.stx&&window.stx._cleanupContainer){\n window.stx._cleanupContainer(currentContent);\n }\n\n function doSwap(){\n // ── Swap <head> styles ──\n // Inject new styles FIRST, then remove old to prevent unstyled flash\n var keepIds={'stx-view-transitions':1,'stx-r-css':1};\n var curStyles=document.querySelectorAll('head style');\n var newStyles=doc.querySelectorAll('head style');\n\n // Merge crosswind styles instead of replacing — persistent elements\n // (nav, footer) outside <main> still need their utility classes\n var curCrosswind=document.querySelector('head style[data-crosswind]');\n var newCrosswind=null;\n newStyles.forEach(function(s){if(s.getAttribute('data-crosswind'))newCrosswind=s});\n\n if(curCrosswind&&newCrosswind){\n var merged=mergeCrosswindCSS(curCrosswind.textContent||'',newCrosswind.textContent||'');\n if(merged)curCrosswind.textContent=merged;\n }\n\n var incoming=[];\n newStyles.forEach(function(s){\n if(!keepIds[s.id]&&!s.getAttribute('data-crosswind')){\n var ns=document.createElement('style');\n ns.textContent=s.textContent;\n ns.setAttribute('data-stx-incoming','');\n document.head.appendChild(ns);\n incoming.push(ns);\n }\n });\n\n // If no existing crosswind but new page has one, add it\n if(!curCrosswind&&newCrosswind){\n var ns=document.createElement('style');\n ns.textContent=newCrosswind.textContent;\n ns.setAttribute('data-crosswind',newCrosswind.getAttribute('data-crosswind'));\n document.head.appendChild(ns);\n }\n\n // Remove old styles (except persistent ones, crosswind, and incoming)\n curStyles.forEach(function(s){\n if(!keepIds[s.id]&&!s.hasAttribute('data-stx-incoming')&&!s.hasAttribute('data-crosswind'))s.remove();\n });\n\n incoming.forEach(function(s){s.removeAttribute('data-stx-incoming')});\n\n // ── Swap content ──\n // For layout changes: swap the entire <body> to replace layout chrome (nav, footer, etc.)\n // For same-layout: swap only the container (<main>)\n var newBody=doc.querySelector('body');\n var isLayoutChange=false;\n if(newBody){\n var newMeta=doc.querySelector('meta[name=\"stx-layout\"]');\n var curMeta=document.querySelector('meta[name=\"stx-layout\"]');\n var curLayout=curMeta?curMeta.getAttribute('content'):'';\n var newLayout=newMeta?newMeta.getAttribute('content'):'';\n var curGroup=getCurrentLayoutGroup();\n var newGroup=getDocLayoutGroup(doc,newLayout);\n isLayoutChange=curGroup!==newGroup||(curLayout&&newLayout&&curLayout!==newLayout);\n }\n if(isLayoutChange&&newBody){\n log('[router] full body swap for layout change');\n // Replace entire body content — layout chrome and all\n var bodyHTML=newBody.innerHTML.replace(new RegExp('<scr'+'ipt\\\\\\\\b[^>]*>[\\\\\\\\s\\\\\\\\S]*?<\\\\\\\\/scr'+'ipt\\\\\\\\s*>','gi'),'');\n document.body.innerHTML=bodyHTML;\n // Copy body attributes (class, data-stx, etc.)\n Array.from(newBody.attributes).forEach(function(attr){document.body.setAttribute(attr.name,attr.value)});\n // Update layout meta tag\n var oldMeta=document.querySelector('meta[name=\"stx-layout\"]');\n var freshMeta=doc.querySelector('meta[name=\"stx-layout\"]');\n if(oldMeta&&freshMeta)oldMeta.setAttribute('content',freshMeta.getAttribute('content')||'');\n else if(freshMeta){var m=document.createElement('meta');m.name='stx-layout';m.content=freshMeta.getAttribute('content')||'';document.head.appendChild(m)}\n var oldGroupMeta=document.querySelector('meta[name=\"stx-layout-group\"]');\n var freshGroupMeta=doc.querySelector('meta[name=\"stx-layout-group\"]');\n if(oldGroupMeta&&freshGroupMeta)oldGroupMeta.setAttribute('content',freshGroupMeta.getAttribute('content')||'');\n else if(freshGroupMeta){var gm=document.createElement('meta');gm.name='stx-layout-group';gm.content=freshGroupMeta.getAttribute('content')||'';document.head.appendChild(gm)}\n // Update container reference for script execution below\n currentContent=document.querySelector(containerSel)||document.querySelector('main')||document.body;\n } else {\n // Same layout — swap only container content\n var cleanHTML=newContent.innerHTML.replace(new RegExp('<scr'+'ipt\\\\\\\\b[^>]*>[\\\\\\\\s\\\\\\\\S]*?<\\\\\\\\/scr'+'ipt\\\\\\\\s*>','gi'),'');\n currentContent.innerHTML=cleanHTML;\n }\n\n // ── Load new external <head> scripts ──\n var loadedSrcs={};\n document.querySelectorAll('head script[src]').forEach(function(s){loadedSrcs[s.src]=1});\n var extPromises=[];\n doc.querySelectorAll('head script[src]').forEach(function(s){\n var src=new URL(s.getAttribute('src'),location.origin).href;\n if(loadedSrcs[src])return;\n loadedSrcs[src]=1;\n extPromises.push(new Promise(function(resolve,reject){\n var ns=document.createElement('script');\n ns.src=src;\n ns.onload=resolve;\n ns.onerror=reject;\n document.head.appendChild(ns);\n }));\n });\n\n // ── Script re-execution ──\n // Remove previously injected page scripts\n document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});\n\n var scripts=[];\n function addScript(text, runAlways){\n scripts.push({text:text,runAlways:!!runAlways});\n }\n if(isLayoutChange){\n // Layout change: collect ALL scripts from the new document body\n // (layout scripts, component mounts, setup functions — everything)\n var newBodyEl=doc.querySelector('body');\n if(newBodyEl){\n newBodyEl.querySelectorAll('script').forEach(function(s){\n var text=s.textContent||'';\n if(s.hasAttribute('src'))return;\n if(!text.trim())return;\n // Skip the signals runtime IIFE — it's already loaded\n if(text.indexOf(\"'use strict';var cloakStyle\")!==-1)return;\n if(text.indexOf('__stxRouter')!==-1)return;\n addScript(text,true);\n });\n }\n // Also collect setup functions from <head>\n doc.querySelectorAll('head script').forEach(function(s){\n var text=s.textContent||'';\n if(s.hasAttribute('src'))return;\n if(!text.trim())return;\n if(text.indexOf('__stx_setup_')!==-1)addScript(text,true);\n });\n } else {\n // Same layout: collect scripts from the container\n newContent.querySelectorAll('script').forEach(function(s){\n var text=s.textContent||'';\n if(s.hasAttribute('src'))return;\n if(!text.trim())return;\n addScript(text,true);\n });\n // Collect page scripts from <head> AND <body> (outside container).\n // SSG builds place the setup function in <body> before <main>, not\n // in <head>. Without this, SPA navigation on static sites never\n // runs the setup and reactive data stays empty.\n var seenSetups={};\n scripts.forEach(function(entry){var t=entry.text;if(t.indexOf('__stx_setup_')!==-1)seenSetups[t.substring(0,80)]=1});\n doc.querySelectorAll('script').forEach(function(s){\n var text=s.textContent||'';\n if(s.hasAttribute('src'))return;\n if(!text.trim())return;\n // Skip signals runtime and router (they contain __stx_setup references but aren't setup functions)\n if(text.indexOf(\"'use strict';var cloakStyle\")!==-1)return;\n if(text.indexOf('__stxRouter')!==-1)return;\n if(newContent.contains(s))return;\n if(text.indexOf('__stx_setup_')===-1&&!s.hasAttribute('data-stx-scoped')&&!s.hasAttribute('data-stx-page'))return;\n var key=text.substring(0,80);\n if(seenSetups[key])return;\n seenSetups[key]=1;\n addScript(text,true);\n });\n }\n\n // Push history state (before active link updates so location.pathname is current)\n if(pushState!==false)history.pushState({},'',url+(hash||''));\n\n // Update active nav links\n updateNav(url);\n updateActiveLinks();\n\n // Scroll\n if(o.scrollToTop&&!hash)window.scrollTo({top:0,behavior:'instant'});\n else if(hash){var el=document.querySelector(hash);if(el)el.scrollIntoView({behavior:'smooth'})}\n\n // Update title\n var newTitle=doc.querySelector('title');\n if(newTitle)document.title=newTitle.textContent;\n\n // Update <html lang> from the destination doc so screen readers,\n // CSS :lang() selectors, and any i18n picker that mirrors\n // document.documentElement.lang stay accurate after SPA hops.\n if(doc.documentElement&&doc.documentElement.lang){\n document.documentElement.lang=doc.documentElement.lang;\n }\n\n window.dispatchEvent(new CustomEvent('stx:navigate',{detail:{url:url}}));\n\n // Execute page scripts FIRST — they define setup functions and set _latestSetup\n function execScripts(){\n scripts.forEach(function(entry){\n var text=typeof entry==='string'?entry:entry.text;\n var runAlways=typeof entry==='string'?false:entry.runAlways;\n // Skip scripts that were already executed (layout-level partials\n // like theme.stx). Their top-level const/function declarations\n // would throw \"Identifier has already been declared\" on re-execution.\n // Exception: setup functions (__stx_setup_) must always re-execute\n // because each page has its own setup, and import statements\n // need special handling.\n var h=hashScript(text);\n var isSetup=text.indexOf('__stx_setup_')!==-1;\n var hasImport=text.indexOf('import ')!==-1;\n if(!runAlways&&!isSetup&&executedScriptHashes[h])return;\n executedScriptHashes[h]=1;\n // Wrap scripts with import statements as modules (Bug 3 fix)\n var ns=document.createElement('script');\n if(hasImport){\n ns.type='module';\n }\n // Wrap in block scope to prevent const/let collisions on re-navigation.\n // Top-level const/let in <script> tags are global-scoped in browsers\n // and throw \"Identifier has already been declared\" on re-execution.\n // Skip the wrap for module scripts — they get their own scope from\n // ESM, and top-level 'import' is illegal inside a block, which\n // would throw SyntaxError before the script ever runs.\n var alreadyScoped=text.trimStart().charAt(0)==='('||text.trimStart().charAt(0)===';'||text.indexOf('window.stx.mount')>-1;\n ns.textContent=(hasImport||alreadyScoped)?text:'{'+text+'}';\n ns.setAttribute('data-stx-page','');\n document.body.appendChild(ns);\n });\n // THEN fire stx:load — now _latestSetup is set and processElement has the right scope\n window.dispatchEvent(new Event('stx:load'));\n }\n\n if(extPromises.length>0){\n Promise.all(extPromises).then(execScripts).catch(execScripts);\n }\nelse {\n execScripts();\n }\n }\n\n if(runViewTransition(doSwap)){\n }\nelse {\n // Fallback fade for browsers without View Transitions API\n currentContent.style.transition='opacity 0.12s ease-out';\n currentContent.style.opacity='0';\n setTimeout(function(){\n doSwap();\n currentContent.style.opacity='1';\n setTimeout(function(){currentContent.style.transition=''},150);\n },120);\n }\n }\n\n // ── Link interception ──\n function shouldIntercept(link){\n if(!link)return false;\n var href=link.getAttribute('href');\n if(!href)return false;\n if(href.startsWith('http')||href.startsWith('#')||href.startsWith('mailto:')||href.startsWith('tel:')||href.startsWith('javascript:'))return false;\n if(link.target==='_blank')return false;\n if(link.hasAttribute('data-stx-no-router')||link.hasAttribute('data-no-router')||link.hasAttribute('download'))return false;\n if(href===location.pathname)return false;\n if(!getContainer())return false;\n return true;\n }\n\n // SPA click handling. By default only [data-stx-link] elements get\n // SPA navigation; regular <a href> does native full page reload. Set\n // interceptAllLinks:true (window.__stxRouterConfig or build-time\n // config) to intercept any same-origin anchor — used by static-site\n // mode where every page is part of the same SPA shell.\n document.addEventListener('click',function(e){\n if(e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||e.button!==0)return;\n if(!e.target||!e.target.closest)return;\n var link=e.target.closest('[data-stx-link]');\n if(!link&&o.interceptAllLinks){\n var anchor=e.target.closest('a[href]');\n if(anchor&&shouldIntercept(anchor))link=anchor;\n }\n if(!link){return}\n var href=link.getAttribute('href');\n log('[router] click intercepted:',href,'container:',!!getContainer(),'defaultPrevented:',e.defaultPrevented);\n if(!href||href.startsWith('http')||href.startsWith('#')||href.startsWith('mailto:')||href.startsWith('tel:')){log('[router] skipped:',href);return}\n if(link.target==='_blank'||link.hasAttribute('download'))return;\n e.preventDefault();\n e.stopPropagation();\n log('[router] navigating to:',href);\n navigate(href);\n },true);\n\n // ── Back/forward ──\n window.addEventListener('popstate',function(){\n navigate(location.pathname+location.search+location.hash,false);\n });\n\n // ── Prefetch on hover ──\n if(o.prefetch){\n document.addEventListener('mouseover',function(e){\n if(!e.target||!e.target.closest)return;\n var link=e.target.closest('[data-stx-link]');\n if(!link)return;\n var href=link.getAttribute('href');\n var key=cacheKey(href);\n if(cache[key]||prefetching[key])return;\n prefetching[key]=true;\n var wantsFragment=shouldUseFragmentResponse();\n fetch(href,{headers:wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'}}).then(function(r){\n var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';\n var pLayout=r.headers.get('X-STX-Layout')||'';\n var pGroup=r.headers.get('X-STX-Layout-Group')||'';\n return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}});\n }).then(function(result){\n if(o.cache){cache[key]=result.html;layoutCache[key]=result.layout;layoutGroupCache[key]=result.layoutGroup}\n }).catch(function(){}).finally(function(){delete prefetching[key]});\n },true);\n }\n\n // ── Active link management ──\n function updateNav(url){\n document.querySelectorAll('nav a[href], #mobileNav a[href], [data-stx-nav] a[href]').forEach(function(a){\n var href=a.getAttribute('href');\n if(!href||href.startsWith('#')||href.startsWith('http'))return;\n var isActive=(href===url)||(href==='/'&&url==='/');\n if(a.hasAttribute('data-stx-link')){\n var ac=a.getAttribute('data-stx-active-class')||'active';\n if(isActive)ac.split(' ').forEach(function(cls){if(cls)a.classList.add(cls)});else ac.split(' ').forEach(function(cls){if(cls)a.classList.remove(cls)});\n }\n });\n }\n\n function updateActiveLinks(){\n // Update active classes on <stx-link> elements (and legacy data-stx-link)\n var links=document.querySelectorAll('[data-stx-link]');\n var cur=location.pathname;\n links.forEach(function(link){\n var href=link.getAttribute('to')||link.getAttribute('href')||'';\n var ac=link.getAttribute('active-class')||link.getAttribute('data-stx-active-class')||'active';\n var eac=link.getAttribute('exact-active-class')||link.getAttribute('data-stx-exact-active-class')||'exact-active';\n ac.split(' ').forEach(function(cls){if(cls)link.classList.remove(cls)});\n eac.split(' ').forEach(function(cls){if(cls)link.classList.remove(cls)});\n var isExact=cur===href;\n var isActive=href!=='/'?cur.startsWith(href):cur==='/';\n if(isExact)eac.split(' ').forEach(function(cls){if(cls)link.classList.add(cls)});\n if(isActive)ac.split(' ').forEach(function(cls){if(cls)link.classList.add(cls)});\n });\n }\n\n // ── Progress bar DOM + style ──\n // Injects a fixed-position element at the top of the viewport plus the\n // minimal CSS for its transform/opacity transitions. Kept idempotent so\n // repeated init() calls (e.g. after full-body swaps) don't duplicate.\n // Also ships the default View Transitions fade+slide CSS when\n // viewTransitions is enabled and the browser supports it. Apps can\n // override by defining more specific ::view-transition-* rules later in\n // the cascade, or opt out entirely with viewTransitions:false.\n function injectStyles(){\n if(!document.getElementById('stx-r-css')){\n var s=document.createElement('style');s.id='stx-r-css';\n // Strip characters that could escape our CSS block and inject new\n // declarations: ; { } ( ) \" ' \\\\ < > plus whitespace control chars.\n // This keeps the value safe to concat into a stylesheet even if a\n // caller wires the config from an untrusted source.\n var sanitize=function(v){return String(v).replace(/[;{}()\"'\\\\\\\\<>\\\\n\\\\r\\\\t]/g,'')};\n var pc=sanitize(o.progressColor);\n var ph=sanitize(o.progressHeight);\n var css='.stx-navigating{cursor:wait}.stx-navigating a,.stx-navigating button{pointer-events:none}#stx-router-progress{position:fixed;top:0;left:0;right:0;height:'+ph+';background:'+pc+';box-shadow:0 0 8px '+pc+',0 0 4px '+pc+';transform:scaleX(0);transform-origin:left;transition:transform .18s ease-out,opacity .26s ease;opacity:0;pointer-events:none;z-index:999999}';\n if(o.viewTransitions&&'startViewTransition' in document){\n var dur=(o.viewTransitionDuration||220)+'ms';\n var ease=o.viewTransitionEasing||'cubic-bezier(0.16, 1, 0.3, 1)';\n css+='::view-transition-old(root),::view-transition-new(root){animation-duration:'+dur+';animation-timing-function:'+ease+'}';\n css+='::view-transition-old(root){animation-name:stx-r-fade-out}::view-transition-new(root){animation-name:stx-r-fade-in}';\n css+='@keyframes stx-r-fade-out{from{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-4px)}}';\n css+='@keyframes stx-r-fade-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}';\n css+='@media (prefers-reduced-motion: reduce){::view-transition-old(root),::view-transition-new(root){animation-duration:0s;animation-name:none}}';\n }\n s.textContent=css;\n document.head.appendChild(s);\n }\n if(o.progress&&!document.getElementById('stx-router-progress')){\n var el=document.createElement('div');\n el.id='stx-router-progress';\n el.setAttribute('role','progressbar');\n el.setAttribute('aria-hidden','true');\n // Append to <html>, not <body>: when the router swaps the body\n // (container:'body' for full-page static-site SPAs) the progress\n // element gets wiped along with it. <html> stays put across\n // swaps, so the bar survives multi-hop navigation.\n document.documentElement.appendChild(el);\n progEl=el;\n } else if(o.progress){\n progEl=document.getElementById('stx-router-progress');\n }\n }\n\n function injectViewTransitionCSS(){\n if(document.getElementById('stx-view-transitions'))return;\n var s=document.createElement('style');s.id='stx-view-transitions';\n s.textContent='::view-transition-group(root){animation:none}::view-transition-old(root){animation:none}::view-transition-new(root){animation:none}main,#app-content,[data-stx-content]{view-transition-name:stx-content}::view-transition-old(stx-content){animation:stx-fade-out .15s ease-out both}::view-transition-new(stx-content){animation:stx-fade-in .15s ease-in .1s both}@keyframes stx-fade-out{from{opacity:1}to{opacity:0}}@keyframes stx-fade-in{from{opacity:0}to{opacity:1}}::view-transition{background:transparent}::view-transition-group(stx-content){background:inherit;overflow:hidden}';\n (document.head||document.documentElement).appendChild(s);\n }\n\n // ── Public API ──\n var router={\n navigate:navigate,\n navigateTo:navigate,\n prefetch:function(url){\n var key=cacheKey(url);\n if(!cache[key]){\n var wantsFragment=shouldUseFragmentResponse();\n fetch(url,{headers:wantsFragment?{'X-STX-Router':'true'}:{'Accept':'text/html'}}).then(function(r){var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';var pLayout=r.headers.get('X-STX-Layout')||'';var pGroup=r.headers.get('X-STX-Layout-Group')||'';return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}})}).then(function(result){cache[key]=result.html;layoutCache[key]=result.layout;layoutGroupCache[key]=result.layoutGroup}).catch(function(){});\n }\n },\n clearCache:function(){for(var k in cache)delete cache[k];for(var lk in layoutCache)delete layoutCache[lk];for(var gk in layoutGroupCache)delete layoutGroupCache[gk]},\n cache:cache,\n swap:swap,\n updateNav:updateNav\n };\n\n window.__stxRouter=router;\n window.stxRouter=router;\n if(window.stx)window.stx.router=router;\n\n // ── Initialize ──\n function init(){\n injectStyles();\n injectViewTransitionCSS();\n updateActiveLinks();\n updateNav(location.pathname);\n }\n\n if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);else init();\n})();\n`\n}\n"
|
|
5
|
+
"/* eslint-disable prefer-const, no-console, style/max-statements-per-line, general/prefer-template */\n/**\n * STX Router - Canonical SPA Router\n *\n * Single source of truth for client-side navigation.\n * Injected via @stxRouter directive or auto-loaded with the signals runtime.\n *\n * Features:\n * - Click interception for internal links\n * - History API pushState navigation\n * - View Transitions API with CSS fade fallback\n * - <head> style swapping (prevents unstyled flash)\n * - Smart script filtering (skip signals runtime, layout guards)\n * - Page prefetching on hover\n * - Response caching (5-minute TTL)\n * - Active link class management (data-stx-link)\n * - Loading indicator bar\n * - Configurable via window.STX_ROUTER_OPTIONS or window.__stxRouterConfig\n */\nexport function getRouterScript(): string {\n return `\n;(function(){\n 'use strict';\n var ROUTER_REV=3;\n if(window.__stxRouter&&window.__stxRouter.__rev===ROUTER_REV)return;\n\n // ── Configuration ──\n var defaults={container:'main',loadingClass:'stx-navigating',viewTransitions:true,cache:true,scrollToTop:true,prefetch:true,progress:true,progressColor:'#78dce8',progressHeight:'2px',interceptAllLinks:false,prefetchCacheMax:50};\n var o=Object.assign({},defaults,window.__stxRouterConfig||{},window.STX_ROUTER_OPTIONS||{});\n var containerSel=o.container;\n var debug=!!o.debug;\n function log(){if(debug&&typeof console!=='undefined'&&console.log)console.log.apply(console,arguments)}\n function runViewTransition(callback){\n if(!o.viewTransitions||!document.startViewTransition)return false;\n try{\n var transition=document.startViewTransition(callback);\n var onAbort=function(err){log('[router] view transition aborted:',err&&err.message?err.message:err)};\n if(transition&&transition.finished&&transition.finished.catch)transition.finished.catch(onAbort);\n if(transition&&transition.updateCallbackDone&&transition.updateCallbackDone.catch)transition.updateCallbackDone.catch(onAbort);\n return true;\n }catch(err){\n log('[router] view transition unavailable:',err&&err.message?err.message:err);\n return false;\n }\n }\n\n // ── Progress bar (0→100% at top of viewport) ──\n // Native loading indicator baked into the router. Starts when navigation\n // begins, trickles up to ~90% during fetch, snaps to 100% + fades on\n // completion. Opt-out via window.__stxRouterConfig={progress:false}.\n var progEl=null;\n var progVal=0;\n var progTimer=null;\n function setProgress(v){\n progVal=v<0?0:(v>100?100:v);\n if(!progEl)return;\n progEl.style.opacity=progVal>0?'1':'0';\n progEl.style.transform='scaleX('+(progVal/100)+')';\n }\n function startProgress(){\n if(!o.progress||!progEl)return;\n if(progTimer){clearInterval(progTimer);progTimer=null}\n setProgress(8);\n progTimer=setInterval(function(){\n if(progVal>=90)return;\n // Asymptotic trickle — fast at first, slower near 90%\n var inc=Math.max(0.4,(90-progVal)*0.08);\n setProgress(progVal+inc);\n },160);\n }\n function finishProgress(){\n if(!o.progress||!progEl)return;\n if(progTimer){clearInterval(progTimer);progTimer=null}\n setProgress(100);\n // Fade out, then reset. Delays chosen so the \"full\" state is briefly\n // visible before the bar disappears.\n setTimeout(function(){\n if(!progEl)return;\n progEl.style.opacity='0';\n setTimeout(function(){progVal=0;if(progEl)progEl.style.transform='scaleX(0)'},260);\n },180);\n }\n\n var cache={};\n var prefetching={};\n var isNavigating=false;\n\n // LRU bookkeeping for cache/layoutCache/layoutGroupCache. Pre-fix\n // (stacksjs/stx#1719) these were plain objects with no eviction, so\n // hover-prefetch on a sidebar of 200 internal links retained ~10MB\n // of HTML strings for the page's lifetime. cacheOrder is a most-\n // recently-used-last list of keys; setCache pushes new entries and\n // evicts the oldest when length exceeds o.prefetchCacheMax (default\n // 50). touchCache promotes a key on cache-hit. We use a plain array\n // rather than a Map to keep the diff localized and the runtime\n // hand-minified shape consistent.\n var cacheOrder=[];\n function setCache(key,html,layout,group){\n if(!(key in cache))cacheOrder.push(key);\n cache[key]=html;\n layoutCache[key]=layout;\n layoutGroupCache[key]=group;\n while(cacheOrder.length>o.prefetchCacheMax){\n var oldest=cacheOrder.shift();\n delete cache[oldest];\n delete layoutCache[oldest];\n delete layoutGroupCache[oldest];\n }\n }\n function touchCache(key){\n var i=cacheOrder.indexOf(key);\n if(i>=0){cacheOrder.splice(i,1);cacheOrder.push(key)}\n }\n function evictCache(key){\n var i=cacheOrder.indexOf(key);\n if(i>=0)cacheOrder.splice(i,1);\n delete cache[key];\n delete layoutCache[key];\n delete layoutGroupCache[key];\n }\n\n // Extract top-level CSS blocks (rules AND @media blocks with nested braces).\n // The old regex ([^{}]+\\{[^{}]*\\}) silently dropped @media blocks because\n // it couldn't handle nested braces — causing responsive classes (lg:, md:)\n // to vanish after SPA navigation.\n function extractCssBlocks(css){\n var blocks=[];\n var i=0;\n var len=css.length;\n while(i<len){\n // Skip whitespace\n while(i<len&&(css[i]===' '||css[i]==='\\\\n'||css[i]==='\\\\r'||css[i]==='\\\\t'))i++;\n if(i>=len)break;\n // Find the start of a block (first {)\n var blockStart=i;\n var bracePos=css.indexOf('{',i);\n if(bracePos===-1)break;\n // Walk forward tracking brace depth to find the matching close\n var depth=0;\n var j=bracePos;\n while(j<len){\n if(css[j]==='{')depth++;\n else if(css[j]==='}'){depth--;if(depth===0){j++;break}}\n j++;\n }\n if(depth!==0)break;\n blocks.push(css.slice(blockStart,j).trim());\n i=j;\n }\n return blocks;\n }\n\n function mergeCrosswindCSS(existing,incoming){\n var blocks=extractCssBlocks(incoming);\n var newBlocks=[];\n for(var bi=0;bi<blocks.length;bi++){\n if(existing.indexOf(blocks[bi])===-1)newBlocks.push(blocks[bi]);\n }\n if(newBlocks.length>0)return existing+'\\\\n'+newBlocks.join('\\\\n');\n return null;\n }\n\n function getContainer(){\n return document.querySelector(containerSel)||document.querySelector('[data-stx-content]')||document.querySelector('main');\n }\n\n // ── Navigation ──\n // Layout group detection — layouts in the same group share a <main> container.\n // Only truly different layout groups trigger a full page reload.\n var layoutCache={};\n var layoutGroupCache={};\n // Track executed script hashes to prevent redeclaration errors on navigation.\n // Layout-level scripts (theme, nav setup) execute on initial page load and\n // should NOT re-execute when navigating to another page with the same layout.\n var executedScriptHashes={};\n function hashScript(code){\n var h=0;for(var i=0;i<code.length;i++){h=((h<<5)-h)+code.charCodeAt(i);h|=0}\n return h;\n }\n // Record scripts from the initial page load\n document.querySelectorAll('script').forEach(function(s){\n var text=s.textContent||'';\n if(text.trim()&&!s.hasAttribute('src'))executedScriptHashes[hashScript(text)]=1;\n });\n function cacheKey(url){\n var u=new URL(url,location.origin);\n return u.pathname+u.search;\n }\n // Keep SPA fetches inside the active locale (/en/... when viewing English).\n function withCurrentLocale(href){\n var d=window.__stxI18n;\n if(d&&typeof d.localizeHref==='function')return d.localizeHref(href);\n return href;\n }\n function defaultLayoutGroup(layout){\n if(!layout)return 'app';\n var clean=String(layout).replace(/\\\\\\\\/g,'/');\n var parts=clean.split('/').filter(Boolean);\n var idx=parts.lastIndexOf('layouts');\n var part=idx>=0?parts[idx+1]:parts[parts.length-1];\n return part?part.replace(/\\\\.stx$/i,''):'app';\n }\n function getCurrentLayoutGroup(){\n var meta=document.querySelector('meta[name=\"stx-layout-group\"]');\n if(meta&&meta.getAttribute('content'))return meta.getAttribute('content');\n var layout=document.querySelector('meta[name=\"stx-layout\"]');\n return defaultLayoutGroup(layout?layout.getAttribute('content'):'');\n }\n function getDocLayoutGroup(doc,layout){\n var meta=doc&&doc.querySelector?doc.querySelector('meta[name=\"stx-layout-group\"]'):null;\n return meta&&meta.getAttribute('content')?meta.getAttribute('content'):defaultLayoutGroup(layout||'');\n }\n function checkLayoutChange(newLayout,targetUrl,newGroup){\n var currentLayout=document.querySelector('meta[name=\"stx-layout\"]');\n var curLayoutName=currentLayout?currentLayout.getAttribute('content'):'';\n var curGroup=getCurrentLayoutGroup();\n var nextGroup=newGroup||defaultLayoutGroup(newLayout);\n log('[router] layout check: current='+curLayoutName+'('+curGroup+') new='+(newLayout||'')+'('+nextGroup+')');\n // Different layout GROUP (e.g. app → auth): full reload\n if(curGroup!==nextGroup){\n log('[router] layout group change:',curGroup,'→',nextGroup,'— full reload to:',targetUrl);\n return true;\n }\n // Same group but different SPECIFIC layout (e.g. layouts/app → layouts/coach):\n // full body swap so nav, sidebar, and other layout-level elements update\n if(curLayoutName && newLayout && curLayoutName!==newLayout){\n log('[router] layout name change:',curLayoutName,'→',newLayout,'— full body swap');\n return true;\n }\n return false;\n }\n\n function shouldUseFragmentResponse(){\n return containerSel==='main'||containerSel==='[data-stx-content]';\n }\n\n function navigate(url,pushState,force){\n // Lang-picker passes force=true with an already-localized path (/en/...).\n // Re-localizing would map it back to the *current* locale and no-op.\n if(!force) url=withCurrentLocale(url);\n log('[router] navigate() called:',url,'isNavigating:',isNavigating);\n if(isNavigating)return;\n var t=new URL(url,location.origin);\n\n if(t.origin!==location.origin){location.href=url;return}\n\n if(t.pathname===location.pathname&&t.hash){\n if(pushState!==false)history.pushState({},'',t.href);\n var el=document.querySelector(t.hash);\n if(el)el.scrollIntoView({behavior:'smooth'});\n return;\n }\n\n // Skip if user clicks a link to the page they're already on. We\n // deliberately allow popstate (pushState===false) through because\n // the browser has already updated location.href to the popped\n // entry — without this allowance, hitting back would early-return\n // and the visible page content would be left frozen on the\n // forward-navigation page.\n if(pushState!==false&&t.href===location.href&&!t.hash&&!force)return;\n\n isNavigating=true;\n document.body.classList.add(o.loadingClass);\n startProgress();\n\n var targetHref=t.href;\n var targetPath=cacheKey(url);\n var targetHash=t.hash;\n\n function done(){isNavigating=false;document.body.classList.remove(o.loadingClass);finishProgress()}\n\n if(o.cache&&cache[targetPath]&&!force){\n // Cache hit → promote in LRU order so this entry survives eviction\n // longer than other entries that haven't been touched.\n touchCache(targetPath);\n if(checkLayoutChange(layoutCache[targetPath],url,layoutGroupCache[targetPath])){\n // Layout changed — fetch full page and do full document swap\n log('[router] cache hit but layout changed — fetching full page');\n fetch(url,{headers:{'Accept':'text/html'}}).then(function(r){\n if(!r.ok)throw new Error(r.status);\n return r.text();\n }).then(function(html){\n log('[router] full page fetched from cache path, len:',html.length);\n swap(html,targetPath,pushState,targetHash);\n }).catch(function(err){\n console.error('[router] full page fetch error:',err);\n location.href=url;\n }).finally(done);\n return;\n }\n swap(cache[targetPath],targetPath,pushState,targetHash);\n done();\n }\nelse {\n if(force&&cache[targetPath])evictCache(targetPath);\n // First fetch as fragment (SPA mode) when the configured container is\n // the page content. Custom app-shell containers need full documents so\n // the router does not inject a <main> fragment into the wrong element.\n var wantsFragment=shouldUseFragmentResponse();\n fetch(url,{headers:wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'}}).then(function(r){\n if(!r.ok)throw new Error(r.status);\n var isFragment=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';\n var newLayout=r.headers.get('X-STX-Layout')||'';\n var newGroup=r.headers.get('X-STX-Layout-Group')||'';\n // Layout change? Fetch the FULL page (no X-STX-Router header) and do full document swap\n if(isFragment&&checkLayoutChange(newLayout,url,newGroup)){\n log('[router] layout change — fetching full page for document swap');\n return fetch(url,{headers:{'Accept':'text/html'}}).then(function(fullRes){\n log('[router] full page fetched:',fullRes.status,'ok:',fullRes.ok);\n if(!fullRes.ok)throw new Error(fullRes.status);\n return fullRes.text().then(function(html){\n log('[router] full page html length:',html.length);\n return{html:html,isFragment:false,layout:newLayout,layoutGroup:newGroup};\n });\n });\n }\n return r.text().then(function(html){return{html:html,isFragment:isFragment,layout:newLayout,layoutGroup:newGroup}});\n }).then(function(result){\n if(!result)return;\n if(result.isFragment)result.html='<!--stx-fragment-->'+result.html;\n if(o.cache)setCache(targetPath,result.html,result.layout,result.layoutGroup);\n swap(result.html,targetPath,pushState,targetHash);\n }).catch(function(err){\n console.error('[router] fetch error:',err);\n location.href=url;\n }).finally(done);\n }\n }\n\n function swap(html,url,pushState,hash){\n var isFragment=html.indexOf('<!--stx-fragment-->')===0;\n if(isFragment)html=html.slice('<!--stx-fragment-->'.length);\n var currentContent=getContainer();\n log('[router] swap: isFragment='+isFragment+' container='+!!currentContent+' tag='+(currentContent&¤tContent.tagName)+' selector='+containerSel+' htmlLen='+html.length);\n if(!currentContent){log('[router] no container — falling back');location.href=url;return}\n\n // Fragment mode: server returned just the page content (no document wrapper)\n if(isFragment){\n if(window.stx&&window.stx._cleanupContainer)window.stx._cleanupContainer(currentContent);\n function doFragSwap(){\n // Extract scripts from fragment before injecting HTML\n var fragScripts=[];\n var fragStyles=[];\n var fragCrosswindCSS=null;\n var cleanFrag=html.replace(new RegExp('<scr'+'ipt\\\\\\\\b[^>]*>([\\\\\\\\s\\\\\\\\S]*?)<\\\\\\\\/scr'+'ipt>','gi'),function(m,code){\n if(code&&code.trim())fragScripts.push(code);\n return '';\n });\n cleanFrag=cleanFrag.replace(new RegExp('<sty'+'le\\\\\\\\b([^>]*)>([\\\\\\\\s\\\\\\\\S]*?)<\\\\\\\\/sty'+'le>','gi'),function(m,attrs,css){\n if(attrs.indexOf('data-crosswind')!==-1){\n fragCrosswindCSS=css;\n }else{\n fragStyles.push({attrs:attrs,css:css});\n }\n return '';\n });\n // Remove old page styles (not crosswind — that gets merged)\n document.querySelectorAll('style[data-stx-page]').forEach(function(s){s.remove()});\n // Merge crosswind CSS from fragment into existing crosswind style\n if(fragCrosswindCSS){\n var curCrosswind=document.querySelector('head style[data-crosswind]');\n if(curCrosswind){\n var merged=mergeCrosswindCSS(curCrosswind.textContent||'',fragCrosswindCSS);\n if(merged)curCrosswind.textContent=merged;\n }else{\n var cw=document.createElement('style');\n cw.setAttribute('data-crosswind','generated');\n cw.textContent=fragCrosswindCSS;\n document.head.appendChild(cw);\n }\n }\n // Add new page styles\n fragStyles.forEach(function(s){\n var el=document.createElement('style');\n el.textContent=s.css;\n el.setAttribute('data-stx-page','');\n document.head.appendChild(el);\n });\n // Swap content\n currentContent.innerHTML=cleanFrag;\n // Remove old page scripts\n document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});\n if(pushState!==false)history.pushState({},'',url+(hash||''));\n updateNav(url);\n updateActiveLinks();\n if(o.scrollToTop&&!hash)window.scrollTo({top:0,behavior:'instant'});\n else if(hash){var el=document.querySelector(hash);if(el)el.scrollIntoView({behavior:'smooth'})}\n window.dispatchEvent(new CustomEvent('stx:navigate',{detail:{url:url}}));\n // Execute page scripts FIRST — they define setup functions and set _latestSetup\n log('[router] frag scripts:', fragScripts.length);\n document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});\n fragScripts.forEach(function(code){\n log('[router] exec script len:', code.length, 'has __stx_setup:', code.indexOf('__stx_setup')>-1);\n // Skip scripts that were already executed (layout-level partials\n // like theme.stx, stores.stx, nav.stx). Their top-level const/let\n // declarations would throw \"Identifier has already been declared\"\n // on re-execution. Setup functions (__stx_setup_) must always\n // re-execute because each page has its own setup.\n var h=hashScript(code);\n var isSetup=code.indexOf('__stx_setup_')!==-1;\n // Compute isAlreadyScoped first so the dedup check can exempt\n // self-contained IIFE scripts. data-stx-scoped scope IIFEs\n // (the (function(){ ... })() shape) must re-execute on every\n // navigation. They own their own scope and won't collide\n // with prior runs, AND _cleanupContainer deleted their\n // entry from window.stx._scopes on the way out. Without\n // this exemption, navigating away and back leaves the page\n // leaf with no scope registered: bindIf/@event never re-bind\n // and all :if branches stay visible at once.\n // Mirrors the full-doc swap path's runAlways escape below.\n var isAlreadyScoped=code.trimStart().charAt(0)==='('||code.trimStart().charAt(0)===';'||code.indexOf('window.stx.mount')>-1;\n if(!isSetup&&!isAlreadyScoped&&executedScriptHashes[h]){\n log('[router] skipping already-executed script (hash dedup)');\n return;\n }\n executedScriptHashes[h]=1;\n var ns=document.createElement('script');\n var hasImport=code.indexOf('import ')!==-1;\n if(hasImport){\n ns.type='module';\n }\n ns.textContent=(hasImport||isAlreadyScoped)?code:'{'+code+'}';\n ns.setAttribute('data-stx-page','');\n document.body.appendChild(ns);\n });\n log('[router] scripts done. _latestSetup:', !!window.stx._latestSetup);\n // THEN fire stx:load — now _latestSetup is set and processElement has the right scope\n window.dispatchEvent(new Event('stx:load'));\n }\n if(runViewTransition(doFragSwap)){}\n else{currentContent.style.transition='opacity 0.12s ease-out';currentContent.style.opacity='0';setTimeout(function(){doFragSwap();currentContent.style.opacity='1';setTimeout(function(){currentContent.style.transition=''},150)},120)}\n return;\n }\n\n // Full document mode: parse with DOMParser and extract container content\n var parser=new DOMParser();\n var doc=parser.parseFromString(html,'text/html');\n var newContent=doc.querySelector(containerSel)||doc.querySelector('[data-stx-content]')||doc.querySelector('main');\n if(!newContent){location.href=url;return}\n\n // Clean up existing signals/effects\n if(window.stx&&window.stx._cleanupContainer){\n window.stx._cleanupContainer(currentContent);\n }\n\n function doSwap(){\n // ── Swap <head> styles ──\n // Inject new styles FIRST, then remove old to prevent unstyled flash\n var keepIds={'stx-view-transitions':1,'stx-r-css':1};\n var curStyles=document.querySelectorAll('head style');\n var newStyles=doc.querySelectorAll('head style');\n\n // Merge crosswind styles instead of replacing — persistent elements\n // (nav, footer) outside <main> still need their utility classes\n var curCrosswind=document.querySelector('head style[data-crosswind]');\n var newCrosswind=null;\n newStyles.forEach(function(s){if(s.getAttribute('data-crosswind'))newCrosswind=s});\n\n if(curCrosswind&&newCrosswind){\n var merged=mergeCrosswindCSS(curCrosswind.textContent||'',newCrosswind.textContent||'');\n if(merged)curCrosswind.textContent=merged;\n }\n\n var incoming=[];\n newStyles.forEach(function(s){\n if(!keepIds[s.id]&&!s.getAttribute('data-crosswind')){\n var ns=document.createElement('style');\n ns.textContent=s.textContent;\n ns.setAttribute('data-stx-incoming','');\n document.head.appendChild(ns);\n incoming.push(ns);\n }\n });\n\n // If no existing crosswind but new page has one, add it\n if(!curCrosswind&&newCrosswind){\n var ns=document.createElement('style');\n ns.textContent=newCrosswind.textContent;\n ns.setAttribute('data-crosswind',newCrosswind.getAttribute('data-crosswind'));\n document.head.appendChild(ns);\n }\n\n // Remove old styles (except persistent ones, crosswind, and incoming)\n curStyles.forEach(function(s){\n if(!keepIds[s.id]&&!s.hasAttribute('data-stx-incoming')&&!s.hasAttribute('data-crosswind'))s.remove();\n });\n\n incoming.forEach(function(s){s.removeAttribute('data-stx-incoming')});\n\n // ── Swap content ──\n // For layout changes: swap the entire <body> to replace layout chrome (nav, footer, etc.)\n // For same-layout: swap only the container (<main>)\n var newBody=doc.querySelector('body');\n var isLayoutChange=false;\n if(newBody){\n var newMeta=doc.querySelector('meta[name=\"stx-layout\"]');\n var curMeta=document.querySelector('meta[name=\"stx-layout\"]');\n var curLayout=curMeta?curMeta.getAttribute('content'):'';\n var newLayout=newMeta?newMeta.getAttribute('content'):'';\n var curGroup=getCurrentLayoutGroup();\n var newGroup=getDocLayoutGroup(doc,newLayout);\n isLayoutChange=curGroup!==newGroup||(curLayout&&newLayout&&curLayout!==newLayout);\n }\n if(isLayoutChange&&newBody){\n log('[router] full body swap for layout change');\n // Replace entire body content — layout chrome and all\n var bodyHTML=newBody.innerHTML.replace(new RegExp('<scr'+'ipt\\\\\\\\b[^>]*>[\\\\\\\\s\\\\\\\\S]*?<\\\\\\\\/scr'+'ipt\\\\\\\\s*>','gi'),'');\n document.body.innerHTML=bodyHTML;\n // Copy body attributes (class, data-stx, etc.)\n Array.from(newBody.attributes).forEach(function(attr){document.body.setAttribute(attr.name,attr.value)});\n // Update layout meta tag\n var oldMeta=document.querySelector('meta[name=\"stx-layout\"]');\n var freshMeta=doc.querySelector('meta[name=\"stx-layout\"]');\n if(oldMeta&&freshMeta)oldMeta.setAttribute('content',freshMeta.getAttribute('content')||'');\n else if(freshMeta){var m=document.createElement('meta');m.name='stx-layout';m.content=freshMeta.getAttribute('content')||'';document.head.appendChild(m)}\n var oldGroupMeta=document.querySelector('meta[name=\"stx-layout-group\"]');\n var freshGroupMeta=doc.querySelector('meta[name=\"stx-layout-group\"]');\n if(oldGroupMeta&&freshGroupMeta)oldGroupMeta.setAttribute('content',freshGroupMeta.getAttribute('content')||'');\n else if(freshGroupMeta){var gm=document.createElement('meta');gm.name='stx-layout-group';gm.content=freshGroupMeta.getAttribute('content')||'';document.head.appendChild(gm)}\n // Update container reference for script execution below\n currentContent=document.querySelector(containerSel)||document.querySelector('main')||document.body;\n } else {\n // Same layout — swap only container content\n var cleanHTML=newContent.innerHTML.replace(new RegExp('<scr'+'ipt\\\\\\\\b[^>]*>[\\\\\\\\s\\\\\\\\S]*?<\\\\\\\\/scr'+'ipt\\\\\\\\s*>','gi'),'');\n currentContent.innerHTML=cleanHTML;\n }\n\n // ── Load new external <head> scripts ──\n var loadedSrcs={};\n document.querySelectorAll('head script[src]').forEach(function(s){loadedSrcs[s.src]=1});\n var extPromises=[];\n doc.querySelectorAll('head script[src]').forEach(function(s){\n var src=new URL(s.getAttribute('src'),location.origin).href;\n if(loadedSrcs[src])return;\n loadedSrcs[src]=1;\n extPromises.push(new Promise(function(resolve,reject){\n var ns=document.createElement('script');\n ns.src=src;\n ns.onload=resolve;\n ns.onerror=reject;\n document.head.appendChild(ns);\n }));\n });\n\n // ── Script re-execution ──\n // Remove previously injected page scripts\n document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});\n\n var scripts=[];\n function addScript(text, runAlways){\n scripts.push({text:text,runAlways:!!runAlways});\n }\n if(isLayoutChange){\n // Layout change: collect ALL scripts from the new document body\n // (layout scripts, component mounts, setup functions — everything)\n var newBodyEl=doc.querySelector('body');\n if(newBodyEl){\n newBodyEl.querySelectorAll('script').forEach(function(s){\n var text=s.textContent||'';\n if(s.hasAttribute('src'))return;\n if(!text.trim())return;\n // Skip the signals runtime IIFE — it's already loaded\n if(text.indexOf(\"'use strict';var cloakStyle\")!==-1)return;\n if(text.indexOf('__stxRouter')!==-1)return;\n addScript(text,true);\n });\n }\n // Also collect setup functions from <head>\n doc.querySelectorAll('head script').forEach(function(s){\n var text=s.textContent||'';\n if(s.hasAttribute('src'))return;\n if(!text.trim())return;\n if(text.indexOf('__stx_setup_')!==-1)addScript(text,true);\n });\n } else {\n // Same layout: collect scripts from the container\n newContent.querySelectorAll('script').forEach(function(s){\n var text=s.textContent||'';\n if(s.hasAttribute('src'))return;\n if(!text.trim())return;\n addScript(text,true);\n });\n // Collect page scripts from <head> AND <body> (outside container).\n // SSG builds place the setup function in <body> before <main>, not\n // in <head>. Without this, SPA navigation on static sites never\n // runs the setup and reactive data stays empty.\n var seenSetups={};\n scripts.forEach(function(entry){var t=entry.text;if(t.indexOf('__stx_setup_')!==-1)seenSetups[t.substring(0,80)]=1});\n doc.querySelectorAll('script').forEach(function(s){\n var text=s.textContent||'';\n if(s.hasAttribute('src'))return;\n if(!text.trim())return;\n // Skip signals runtime and router (they contain __stx_setup references but aren't setup functions)\n if(text.indexOf(\"'use strict';var cloakStyle\")!==-1)return;\n if(text.indexOf('__stxRouter')!==-1)return;\n if(newContent.contains(s))return;\n if(text.indexOf('__stx_setup_')===-1&&!s.hasAttribute('data-stx-scoped')&&!s.hasAttribute('data-stx-page'))return;\n var key=text.substring(0,80);\n if(seenSetups[key])return;\n seenSetups[key]=1;\n addScript(text,true);\n });\n }\n\n // Push history state (before active link updates so location.pathname is current)\n if(pushState!==false)history.pushState({},'',url+(hash||''));\n\n // Update active nav links\n updateNav(url);\n updateActiveLinks();\n\n // Scroll\n if(o.scrollToTop&&!hash)window.scrollTo({top:0,behavior:'instant'});\n else if(hash){var el=document.querySelector(hash);if(el)el.scrollIntoView({behavior:'smooth'})}\n\n // Update title\n var newTitle=doc.querySelector('title');\n if(newTitle)document.title=newTitle.textContent;\n\n // Update <html lang> from the destination doc so screen readers,\n // CSS :lang() selectors, and any i18n picker that mirrors\n // document.documentElement.lang stay accurate after SPA hops.\n if(doc.documentElement&&doc.documentElement.lang){\n document.documentElement.lang=doc.documentElement.lang;\n }\n\n window.dispatchEvent(new CustomEvent('stx:navigate',{detail:{url:url}}));\n\n // Execute page scripts FIRST — they define setup functions and set _latestSetup\n function execScripts(){\n scripts.forEach(function(entry){\n var text=typeof entry==='string'?entry:entry.text;\n var runAlways=typeof entry==='string'?false:entry.runAlways;\n // Skip scripts that were already executed (layout-level partials\n // like theme.stx). Their top-level const/function declarations\n // would throw \"Identifier has already been declared\" on re-execution.\n // Exception: setup functions (__stx_setup_) must always re-execute\n // because each page has its own setup, and import statements\n // need special handling.\n var h=hashScript(text);\n var isSetup=text.indexOf('__stx_setup_')!==-1;\n var hasImport=text.indexOf('import ')!==-1;\n if(!runAlways&&!isSetup&&executedScriptHashes[h])return;\n executedScriptHashes[h]=1;\n // Wrap scripts with import statements as modules (Bug 3 fix)\n var ns=document.createElement('script');\n if(hasImport){\n ns.type='module';\n }\n // Wrap in block scope to prevent const/let collisions on re-navigation.\n // Top-level const/let in <script> tags are global-scoped in browsers\n // and throw \"Identifier has already been declared\" on re-execution.\n // Skip the wrap for module scripts — they get their own scope from\n // ESM, and top-level 'import' is illegal inside a block, which\n // would throw SyntaxError before the script ever runs.\n var alreadyScoped=text.trimStart().charAt(0)==='('||text.trimStart().charAt(0)===';'||text.indexOf('window.stx.mount')>-1;\n ns.textContent=(hasImport||alreadyScoped)?text:'{'+text+'}';\n ns.setAttribute('data-stx-page','');\n document.body.appendChild(ns);\n });\n // THEN fire stx:load — now _latestSetup is set and processElement has the right scope\n window.dispatchEvent(new Event('stx:load'));\n }\n\n if(extPromises.length>0){\n Promise.all(extPromises).then(execScripts).catch(execScripts);\n }\nelse {\n execScripts();\n }\n }\n\n if(runViewTransition(doSwap)){\n }\nelse {\n // Fallback fade for browsers without View Transitions API\n currentContent.style.transition='opacity 0.12s ease-out';\n currentContent.style.opacity='0';\n setTimeout(function(){\n doSwap();\n currentContent.style.opacity='1';\n setTimeout(function(){currentContent.style.transition=''},150);\n },120);\n }\n }\n\n // ── Link interception ──\n function shouldIntercept(link){\n if(!link)return false;\n var href=link.getAttribute('href');\n if(!href)return false;\n if(href.startsWith('http')||href.startsWith('#')||href.startsWith('mailto:')||href.startsWith('tel:')||href.startsWith('javascript:'))return false;\n if(link.target==='_blank')return false;\n if(link.hasAttribute('data-stx-no-router')||link.hasAttribute('data-no-router')||link.hasAttribute('download'))return false;\n if(href.startsWith('?'))return true;\n if(href===location.pathname)return false;\n if(!getContainer())return false;\n return true;\n }\n\n // SPA click handling. By default only [data-stx-link] elements get\n // SPA navigation; regular <a href> does native full page reload. Set\n // interceptAllLinks:true (window.__stxRouterConfig or build-time\n // config) to intercept any same-origin anchor — used by static-site\n // mode where every page is part of the same SPA shell.\n document.addEventListener('click',function(e){\n if(e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||e.button!==0)return;\n if(!e.target||!e.target.closest)return;\n var link=e.target.closest('[data-stx-link]');\n if(!link&&o.interceptAllLinks){\n var anchor=e.target.closest('a[href]');\n if(anchor&&shouldIntercept(anchor))link=anchor;\n }\n if(!link){return}\n var href=link.getAttribute('href');\n log('[router] click intercepted:',href,'container:',!!getContainer(),'defaultPrevented:',e.defaultPrevented);\n if(!href||href.startsWith('http')||href.startsWith('#')||href.startsWith('mailto:')||href.startsWith('tel:')){log('[router] skipped:',href);return}\n if(link.target==='_blank'||link.hasAttribute('download'))return;\n e.preventDefault();\n e.stopPropagation();\n log('[router] navigating to:',href);\n navigate(withCurrentLocale(href));\n },true);\n\n // ── Back/forward ──\n window.addEventListener('popstate',function(){\n navigate(location.pathname+location.search+location.hash,false);\n });\n\n // ── Prefetch on hover ──\n if(o.prefetch){\n document.addEventListener('mouseover',function(e){\n if(!e.target||!e.target.closest)return;\n var link=e.target.closest('[data-stx-link]');\n if(!link)return;\n var href=withCurrentLocale(link.getAttribute('href'));\n var key=cacheKey(href);\n if(cache[key]||prefetching[key])return;\n prefetching[key]=true;\n var wantsFragment=shouldUseFragmentResponse();\n fetch(href,{headers:wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'}}).then(function(r){\n var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';\n var pLayout=r.headers.get('X-STX-Layout')||'';\n var pGroup=r.headers.get('X-STX-Layout-Group')||'';\n return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}});\n }).then(function(result){\n if(o.cache)setCache(key,result.html,result.layout,result.layoutGroup);\n }).catch(function(){}).finally(function(){delete prefetching[key]});\n },true);\n }\n\n // ── Active link management ──\n function updateNav(url){\n document.querySelectorAll('nav a[href], #mobileNav a[href], [data-stx-nav] a[href]').forEach(function(a){\n var href=a.getAttribute('href');\n if(!href||href.startsWith('#')||href.startsWith('http'))return;\n var isActive=(href===url)||(href==='/'&&url==='/');\n if(a.hasAttribute('data-stx-link')){\n var ac=a.getAttribute('data-stx-active-class')||'active';\n if(isActive)ac.split(' ').forEach(function(cls){if(cls)a.classList.add(cls)});else ac.split(' ').forEach(function(cls){if(cls)a.classList.remove(cls)});\n }\n });\n }\n\n function updateActiveLinks(){\n // Update active classes on <stx-link> elements (and legacy data-stx-link)\n var links=document.querySelectorAll('[data-stx-link]');\n var cur=location.pathname;\n links.forEach(function(link){\n var href=link.getAttribute('to')||link.getAttribute('href')||'';\n var ac=link.getAttribute('active-class')||link.getAttribute('data-stx-active-class')||'active';\n var eac=link.getAttribute('exact-active-class')||link.getAttribute('data-stx-exact-active-class')||'exact-active';\n ac.split(' ').forEach(function(cls){if(cls)link.classList.remove(cls)});\n eac.split(' ').forEach(function(cls){if(cls)link.classList.remove(cls)});\n var isExact=cur===href;\n var isActive=href!=='/'?cur.startsWith(href):cur==='/';\n if(isExact)eac.split(' ').forEach(function(cls){if(cls)link.classList.add(cls)});\n if(isActive)ac.split(' ').forEach(function(cls){if(cls)link.classList.add(cls)});\n });\n }\n\n // ── Progress bar DOM + style ──\n // Injects a fixed-position element at the top of the viewport plus the\n // minimal CSS for its transform/opacity transitions. Kept idempotent so\n // repeated init() calls (e.g. after full-body swaps) don't duplicate.\n // Also ships the default View Transitions fade+slide CSS when\n // viewTransitions is enabled and the browser supports it. Apps can\n // override by defining more specific ::view-transition-* rules later in\n // the cascade, or opt out entirely with viewTransitions:false.\n function injectStyles(){\n if(!document.getElementById('stx-r-css')){\n var s=document.createElement('style');s.id='stx-r-css';\n // Strip characters that could escape our CSS block and inject new\n // declarations: ; { } ( ) \" ' \\\\ < > plus whitespace control chars.\n // This keeps the value safe to concat into a stylesheet even if a\n // caller wires the config from an untrusted source.\n var sanitize=function(v){return String(v).replace(/[;{}()\"'\\\\\\\\<>\\\\n\\\\r\\\\t]/g,'')};\n var pc=sanitize(o.progressColor);\n var ph=sanitize(o.progressHeight);\n var css='.stx-navigating{cursor:wait}.stx-navigating a,.stx-navigating button{pointer-events:none}#stx-router-progress{position:fixed;top:0;left:0;right:0;height:'+ph+';background:'+pc+';box-shadow:0 0 8px '+pc+',0 0 4px '+pc+';transform:scaleX(0);transform-origin:left;transition:transform .18s ease-out,opacity .26s ease;opacity:0;pointer-events:none;z-index:999999}';\n if(o.viewTransitions&&'startViewTransition' in document){\n var dur=(o.viewTransitionDuration||220)+'ms';\n var ease=o.viewTransitionEasing||'cubic-bezier(0.16, 1, 0.3, 1)';\n css+='::view-transition-old(root),::view-transition-new(root){animation-duration:'+dur+';animation-timing-function:'+ease+'}';\n css+='::view-transition-old(root){animation-name:stx-r-fade-out}::view-transition-new(root){animation-name:stx-r-fade-in}';\n css+='@keyframes stx-r-fade-out{from{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-4px)}}';\n css+='@keyframes stx-r-fade-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}';\n css+='@media (prefers-reduced-motion: reduce){::view-transition-old(root),::view-transition-new(root){animation-duration:0s;animation-name:none}}';\n }\n s.textContent=css;\n document.head.appendChild(s);\n }\n if(o.progress&&!document.getElementById('stx-router-progress')){\n var el=document.createElement('div');\n el.id='stx-router-progress';\n el.setAttribute('role','progressbar');\n el.setAttribute('aria-hidden','true');\n // Append to <html>, not <body>: when the router swaps the body\n // (container:'body' for full-page static-site SPAs) the progress\n // element gets wiped along with it. <html> stays put across\n // swaps, so the bar survives multi-hop navigation.\n document.documentElement.appendChild(el);\n progEl=el;\n } else if(o.progress){\n progEl=document.getElementById('stx-router-progress');\n }\n }\n\n function injectViewTransitionCSS(){\n if(document.getElementById('stx-view-transitions'))return;\n var s=document.createElement('style');s.id='stx-view-transitions';\n s.textContent='::view-transition-group(root){animation:none}::view-transition-old(root){animation:none}::view-transition-new(root){animation:none}main,#app-content,[data-stx-content]{view-transition-name:stx-content}::view-transition-old(stx-content){animation:stx-fade-out .15s ease-out both}::view-transition-new(stx-content){animation:stx-fade-in .15s ease-in .1s both}@keyframes stx-fade-out{from{opacity:1}to{opacity:0}}@keyframes stx-fade-in{from{opacity:0}to{opacity:1}}::view-transition{background:transparent}::view-transition-group(stx-content){background:inherit;overflow:hidden}';\n (document.head||document.documentElement).appendChild(s);\n }\n\n // ── Public API ──\n var router={\n navigate:navigate,\n navigateTo:navigate,\n prefetch:function(url){\n var key=cacheKey(url);\n if(!cache[key]){\n var wantsFragment=shouldUseFragmentResponse();\n fetch(url,{headers:wantsFragment?{'X-STX-Router':'true'}:{'Accept':'text/html'}}).then(function(r){var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';var pLayout=r.headers.get('X-STX-Layout')||'';var pGroup=r.headers.get('X-STX-Layout-Group')||'';return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}})}).then(function(result){setCache(key,result.html,result.layout,result.layoutGroup)}).catch(function(){});\n }\n },\n clearCache:function(){for(var k in cache)delete cache[k];for(var lk in layoutCache)delete layoutCache[lk];for(var gk in layoutGroupCache)delete layoutGroupCache[gk];cacheOrder.length=0},\n cache:cache,\n swap:swap,\n updateNav:updateNav\n };\n\n router.__rev=ROUTER_REV;\n window.__stxRouter=router;\n window.stxRouter=router;\n if(window.stx)window.stx.router=router;\n\n // ── Initialize ──\n function init(){\n injectStyles();\n injectViewTransitionCSS();\n updateActiveLinks();\n updateNav(location.pathname);\n }\n\n if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);else init();\n})();\n`\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;AAmBO,SAAS,eAAe,GAAW;AAAA,EACxC,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";;AAmBO,SAAS,eAAe,GAAW;AAAA,EACxC,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
|
|
8
|
+
"debugId": "EBA4E6561C5FCAFB64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/index.js
CHANGED
|
@@ -4,10 +4,11 @@ function getRouterScript() {
|
|
|
4
4
|
return `
|
|
5
5
|
;(function(){
|
|
6
6
|
'use strict';
|
|
7
|
-
|
|
7
|
+
var ROUTER_REV=3;
|
|
8
|
+
if(window.__stxRouter&&window.__stxRouter.__rev===ROUTER_REV)return;
|
|
8
9
|
|
|
9
10
|
// \u2500\u2500 Configuration \u2500\u2500
|
|
10
|
-
var defaults={container:'main',loadingClass:'stx-navigating',viewTransitions:true,cache:true,scrollToTop:true,prefetch:true,progress:true,progressColor:'#78dce8',progressHeight:'2px',interceptAllLinks:false};
|
|
11
|
+
var defaults={container:'main',loadingClass:'stx-navigating',viewTransitions:true,cache:true,scrollToTop:true,prefetch:true,progress:true,progressColor:'#78dce8',progressHeight:'2px',interceptAllLinks:false,prefetchCacheMax:50};
|
|
11
12
|
var o=Object.assign({},defaults,window.__stxRouterConfig||{},window.STX_ROUTER_OPTIONS||{});
|
|
12
13
|
var containerSel=o.container;
|
|
13
14
|
var debug=!!o.debug;
|
|
@@ -67,6 +68,40 @@ function getRouterScript() {
|
|
|
67
68
|
var prefetching={};
|
|
68
69
|
var isNavigating=false;
|
|
69
70
|
|
|
71
|
+
// LRU bookkeeping for cache/layoutCache/layoutGroupCache. Pre-fix
|
|
72
|
+
// (stacksjs/stx#1719) these were plain objects with no eviction, so
|
|
73
|
+
// hover-prefetch on a sidebar of 200 internal links retained ~10MB
|
|
74
|
+
// of HTML strings for the page's lifetime. cacheOrder is a most-
|
|
75
|
+
// recently-used-last list of keys; setCache pushes new entries and
|
|
76
|
+
// evicts the oldest when length exceeds o.prefetchCacheMax (default
|
|
77
|
+
// 50). touchCache promotes a key on cache-hit. We use a plain array
|
|
78
|
+
// rather than a Map to keep the diff localized and the runtime
|
|
79
|
+
// hand-minified shape consistent.
|
|
80
|
+
var cacheOrder=[];
|
|
81
|
+
function setCache(key,html,layout,group){
|
|
82
|
+
if(!(key in cache))cacheOrder.push(key);
|
|
83
|
+
cache[key]=html;
|
|
84
|
+
layoutCache[key]=layout;
|
|
85
|
+
layoutGroupCache[key]=group;
|
|
86
|
+
while(cacheOrder.length>o.prefetchCacheMax){
|
|
87
|
+
var oldest=cacheOrder.shift();
|
|
88
|
+
delete cache[oldest];
|
|
89
|
+
delete layoutCache[oldest];
|
|
90
|
+
delete layoutGroupCache[oldest];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function touchCache(key){
|
|
94
|
+
var i=cacheOrder.indexOf(key);
|
|
95
|
+
if(i>=0){cacheOrder.splice(i,1);cacheOrder.push(key)}
|
|
96
|
+
}
|
|
97
|
+
function evictCache(key){
|
|
98
|
+
var i=cacheOrder.indexOf(key);
|
|
99
|
+
if(i>=0)cacheOrder.splice(i,1);
|
|
100
|
+
delete cache[key];
|
|
101
|
+
delete layoutCache[key];
|
|
102
|
+
delete layoutGroupCache[key];
|
|
103
|
+
}
|
|
104
|
+
|
|
70
105
|
// Extract top-level CSS blocks (rules AND @media blocks with nested braces).
|
|
71
106
|
// The old regex ([^{}]+{[^{}]*}) silently dropped @media blocks because
|
|
72
107
|
// it couldn't handle nested braces \u2014 causing responsive classes (lg:, md:)
|
|
@@ -134,6 +169,12 @@ function getRouterScript() {
|
|
|
134
169
|
var u=new URL(url,location.origin);
|
|
135
170
|
return u.pathname+u.search;
|
|
136
171
|
}
|
|
172
|
+
// Keep SPA fetches inside the active locale (/en/... when viewing English).
|
|
173
|
+
function withCurrentLocale(href){
|
|
174
|
+
var d=window.__stxI18n;
|
|
175
|
+
if(d&&typeof d.localizeHref==='function')return d.localizeHref(href);
|
|
176
|
+
return href;
|
|
177
|
+
}
|
|
137
178
|
function defaultLayoutGroup(layout){
|
|
138
179
|
if(!layout)return 'app';
|
|
139
180
|
var clean=String(layout).replace(/\\\\/g,'/');
|
|
@@ -177,6 +218,9 @@ function getRouterScript() {
|
|
|
177
218
|
}
|
|
178
219
|
|
|
179
220
|
function navigate(url,pushState,force){
|
|
221
|
+
// Lang-picker passes force=true with an already-localized path (/en/...).
|
|
222
|
+
// Re-localizing would map it back to the *current* locale and no-op.
|
|
223
|
+
if(!force) url=withCurrentLocale(url);
|
|
180
224
|
log('[router] navigate() called:',url,'isNavigating:',isNavigating);
|
|
181
225
|
if(isNavigating)return;
|
|
182
226
|
var t=new URL(url,location.origin);
|
|
@@ -209,6 +253,9 @@ function getRouterScript() {
|
|
|
209
253
|
function done(){isNavigating=false;document.body.classList.remove(o.loadingClass);finishProgress()}
|
|
210
254
|
|
|
211
255
|
if(o.cache&&cache[targetPath]&&!force){
|
|
256
|
+
// Cache hit \u2192 promote in LRU order so this entry survives eviction
|
|
257
|
+
// longer than other entries that haven't been touched.
|
|
258
|
+
touchCache(targetPath);
|
|
212
259
|
if(checkLayoutChange(layoutCache[targetPath],url,layoutGroupCache[targetPath])){
|
|
213
260
|
// Layout changed \u2014 fetch full page and do full document swap
|
|
214
261
|
log('[router] cache hit but layout changed \u2014 fetching full page');
|
|
@@ -228,7 +275,7 @@ function getRouterScript() {
|
|
|
228
275
|
done();
|
|
229
276
|
}
|
|
230
277
|
else {
|
|
231
|
-
if(force&&cache[targetPath])
|
|
278
|
+
if(force&&cache[targetPath])evictCache(targetPath);
|
|
232
279
|
// First fetch as fragment (SPA mode) when the configured container is
|
|
233
280
|
// the page content. Custom app-shell containers need full documents so
|
|
234
281
|
// the router does not inject a <main> fragment into the wrong element.
|
|
@@ -254,7 +301,7 @@ else {
|
|
|
254
301
|
}).then(function(result){
|
|
255
302
|
if(!result)return;
|
|
256
303
|
if(result.isFragment)result.html='<!--stx-fragment-->'+result.html;
|
|
257
|
-
if(o.cache)
|
|
304
|
+
if(o.cache)setCache(targetPath,result.html,result.layout,result.layoutGroup);
|
|
258
305
|
swap(result.html,targetPath,pushState,targetHash);
|
|
259
306
|
}).catch(function(err){
|
|
260
307
|
console.error('[router] fetch error:',err);
|
|
@@ -628,6 +675,7 @@ else {
|
|
|
628
675
|
if(href.startsWith('http')||href.startsWith('#')||href.startsWith('mailto:')||href.startsWith('tel:')||href.startsWith('javascript:'))return false;
|
|
629
676
|
if(link.target==='_blank')return false;
|
|
630
677
|
if(link.hasAttribute('data-stx-no-router')||link.hasAttribute('data-no-router')||link.hasAttribute('download'))return false;
|
|
678
|
+
if(href.startsWith('?'))return true;
|
|
631
679
|
if(href===location.pathname)return false;
|
|
632
680
|
if(!getContainer())return false;
|
|
633
681
|
return true;
|
|
@@ -654,7 +702,7 @@ else {
|
|
|
654
702
|
e.preventDefault();
|
|
655
703
|
e.stopPropagation();
|
|
656
704
|
log('[router] navigating to:',href);
|
|
657
|
-
navigate(href);
|
|
705
|
+
navigate(withCurrentLocale(href));
|
|
658
706
|
},true);
|
|
659
707
|
|
|
660
708
|
// \u2500\u2500 Back/forward \u2500\u2500
|
|
@@ -668,7 +716,7 @@ else {
|
|
|
668
716
|
if(!e.target||!e.target.closest)return;
|
|
669
717
|
var link=e.target.closest('[data-stx-link]');
|
|
670
718
|
if(!link)return;
|
|
671
|
-
var href=link.getAttribute('href');
|
|
719
|
+
var href=withCurrentLocale(link.getAttribute('href'));
|
|
672
720
|
var key=cacheKey(href);
|
|
673
721
|
if(cache[key]||prefetching[key])return;
|
|
674
722
|
prefetching[key]=true;
|
|
@@ -679,7 +727,7 @@ else {
|
|
|
679
727
|
var pGroup=r.headers.get('X-STX-Layout-Group')||'';
|
|
680
728
|
return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}});
|
|
681
729
|
}).then(function(result){
|
|
682
|
-
if(o.cache)
|
|
730
|
+
if(o.cache)setCache(key,result.html,result.layout,result.layoutGroup);
|
|
683
731
|
}).catch(function(){}).finally(function(){delete prefetching[key]});
|
|
684
732
|
},true);
|
|
685
733
|
}
|
|
@@ -776,15 +824,16 @@ else {
|
|
|
776
824
|
var key=cacheKey(url);
|
|
777
825
|
if(!cache[key]){
|
|
778
826
|
var wantsFragment=shouldUseFragmentResponse();
|
|
779
|
-
fetch(url,{headers:wantsFragment?{'X-STX-Router':'true'}:{'Accept':'text/html'}}).then(function(r){var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';var pLayout=r.headers.get('X-STX-Layout')||'';var pGroup=r.headers.get('X-STX-Layout-Group')||'';return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}})}).then(function(result){
|
|
827
|
+
fetch(url,{headers:wantsFragment?{'X-STX-Router':'true'}:{'Accept':'text/html'}}).then(function(r){var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';var pLayout=r.headers.get('X-STX-Layout')||'';var pGroup=r.headers.get('X-STX-Layout-Group')||'';return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}})}).then(function(result){setCache(key,result.html,result.layout,result.layoutGroup)}).catch(function(){});
|
|
780
828
|
}
|
|
781
829
|
},
|
|
782
|
-
clearCache:function(){for(var k in cache)delete cache[k];for(var lk in layoutCache)delete layoutCache[lk];for(var gk in layoutGroupCache)delete layoutGroupCache[gk]},
|
|
830
|
+
clearCache:function(){for(var k in cache)delete cache[k];for(var lk in layoutCache)delete layoutCache[lk];for(var gk in layoutGroupCache)delete layoutGroupCache[gk];cacheOrder.length=0},
|
|
783
831
|
cache:cache,
|
|
784
832
|
swap:swap,
|
|
785
833
|
updateNav:updateNav
|
|
786
834
|
};
|
|
787
835
|
|
|
836
|
+
router.__rev=ROUTER_REV;
|
|
788
837
|
window.__stxRouter=router;
|
|
789
838
|
window.stxRouter=router;
|
|
790
839
|
if(window.stx)window.stx.router=router;
|
|
@@ -1432,4 +1481,4 @@ export {
|
|
|
1432
1481
|
Router
|
|
1433
1482
|
};
|
|
1434
1483
|
|
|
1435
|
-
//# debugId=
|
|
1484
|
+
//# debugId=6F4C4DEF36A29C6664756E2164756E21
|
package/dist/index.js.map
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/client.ts", "../src/codegen.ts", "../src/matcher.ts", "../src/nested-layouts.ts", "../src/file-router.ts", "../src/named-routes.ts", "../src/middleware.ts", "../src/layout-metadata.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/* eslint-disable prefer-const, no-console, style/max-statements-per-line, general/prefer-template */\n/**\n * STX Router - Canonical SPA Router\n *\n * Single source of truth for client-side navigation.\n * Injected via @stxRouter directive or auto-loaded with the signals runtime.\n *\n * Features:\n * - Click interception for internal links\n * - History API pushState navigation\n * - View Transitions API with CSS fade fallback\n * - <head> style swapping (prevents unstyled flash)\n * - Smart script filtering (skip signals runtime, layout guards)\n * - Page prefetching on hover\n * - Response caching (5-minute TTL)\n * - Active link class management (data-stx-link)\n * - Loading indicator bar\n * - Configurable via window.STX_ROUTER_OPTIONS or window.__stxRouterConfig\n */\nexport function getRouterScript(): string {\n return `\n;(function(){\n 'use strict';\n if(window.__stxRouter)return;\n\n // ── Configuration ──\n var defaults={container:'main',loadingClass:'stx-navigating',viewTransitions:true,cache:true,scrollToTop:true,prefetch:true,progress:true,progressColor:'#78dce8',progressHeight:'2px',interceptAllLinks:false};\n var o=Object.assign({},defaults,window.__stxRouterConfig||{},window.STX_ROUTER_OPTIONS||{});\n var containerSel=o.container;\n var debug=!!o.debug;\n function log(){if(debug&&typeof console!=='undefined'&&console.log)console.log.apply(console,arguments)}\n function runViewTransition(callback){\n if(!o.viewTransitions||!document.startViewTransition)return false;\n try{\n var transition=document.startViewTransition(callback);\n var onAbort=function(err){log('[router] view transition aborted:',err&&err.message?err.message:err)};\n if(transition&&transition.finished&&transition.finished.catch)transition.finished.catch(onAbort);\n if(transition&&transition.updateCallbackDone&&transition.updateCallbackDone.catch)transition.updateCallbackDone.catch(onAbort);\n return true;\n }catch(err){\n log('[router] view transition unavailable:',err&&err.message?err.message:err);\n return false;\n }\n }\n\n // ── Progress bar (0→100% at top of viewport) ──\n // Native loading indicator baked into the router. Starts when navigation\n // begins, trickles up to ~90% during fetch, snaps to 100% + fades on\n // completion. Opt-out via window.__stxRouterConfig={progress:false}.\n var progEl=null;\n var progVal=0;\n var progTimer=null;\n function setProgress(v){\n progVal=v<0?0:(v>100?100:v);\n if(!progEl)return;\n progEl.style.opacity=progVal>0?'1':'0';\n progEl.style.transform='scaleX('+(progVal/100)+')';\n }\n function startProgress(){\n if(!o.progress||!progEl)return;\n if(progTimer){clearInterval(progTimer);progTimer=null}\n setProgress(8);\n progTimer=setInterval(function(){\n if(progVal>=90)return;\n // Asymptotic trickle — fast at first, slower near 90%\n var inc=Math.max(0.4,(90-progVal)*0.08);\n setProgress(progVal+inc);\n },160);\n }\n function finishProgress(){\n if(!o.progress||!progEl)return;\n if(progTimer){clearInterval(progTimer);progTimer=null}\n setProgress(100);\n // Fade out, then reset. Delays chosen so the \"full\" state is briefly\n // visible before the bar disappears.\n setTimeout(function(){\n if(!progEl)return;\n progEl.style.opacity='0';\n setTimeout(function(){progVal=0;if(progEl)progEl.style.transform='scaleX(0)'},260);\n },180);\n }\n\n var cache={};\n var prefetching={};\n var isNavigating=false;\n\n // Extract top-level CSS blocks (rules AND @media blocks with nested braces).\n // The old regex ([^{}]+\\{[^{}]*\\}) silently dropped @media blocks because\n // it couldn't handle nested braces — causing responsive classes (lg:, md:)\n // to vanish after SPA navigation.\n function extractCssBlocks(css){\n var blocks=[];\n var i=0;\n var len=css.length;\n while(i<len){\n // Skip whitespace\n while(i<len&&(css[i]===' '||css[i]==='\\\\n'||css[i]==='\\\\r'||css[i]==='\\\\t'))i++;\n if(i>=len)break;\n // Find the start of a block (first {)\n var blockStart=i;\n var bracePos=css.indexOf('{',i);\n if(bracePos===-1)break;\n // Walk forward tracking brace depth to find the matching close\n var depth=0;\n var j=bracePos;\n while(j<len){\n if(css[j]==='{')depth++;\n else if(css[j]==='}'){depth--;if(depth===0){j++;break}}\n j++;\n }\n if(depth!==0)break;\n blocks.push(css.slice(blockStart,j).trim());\n i=j;\n }\n return blocks;\n }\n\n function mergeCrosswindCSS(existing,incoming){\n var blocks=extractCssBlocks(incoming);\n var newBlocks=[];\n for(var bi=0;bi<blocks.length;bi++){\n if(existing.indexOf(blocks[bi])===-1)newBlocks.push(blocks[bi]);\n }\n if(newBlocks.length>0)return existing+'\\\\n'+newBlocks.join('\\\\n');\n return null;\n }\n\n function getContainer(){\n return document.querySelector(containerSel)||document.querySelector('[data-stx-content]')||document.querySelector('main');\n }\n\n // ── Navigation ──\n // Layout group detection — layouts in the same group share a <main> container.\n // Only truly different layout groups trigger a full page reload.\n var layoutCache={};\n var layoutGroupCache={};\n // Track executed script hashes to prevent redeclaration errors on navigation.\n // Layout-level scripts (theme, nav setup) execute on initial page load and\n // should NOT re-execute when navigating to another page with the same layout.\n var executedScriptHashes={};\n function hashScript(code){\n var h=0;for(var i=0;i<code.length;i++){h=((h<<5)-h)+code.charCodeAt(i);h|=0}\n return h;\n }\n // Record scripts from the initial page load\n document.querySelectorAll('script').forEach(function(s){\n var text=s.textContent||'';\n if(text.trim()&&!s.hasAttribute('src'))executedScriptHashes[hashScript(text)]=1;\n });\n function cacheKey(url){\n var u=new URL(url,location.origin);\n return u.pathname+u.search;\n }\n function defaultLayoutGroup(layout){\n if(!layout)return 'app';\n var clean=String(layout).replace(/\\\\\\\\/g,'/');\n var parts=clean.split('/').filter(Boolean);\n var idx=parts.lastIndexOf('layouts');\n var part=idx>=0?parts[idx+1]:parts[parts.length-1];\n return part?part.replace(/\\\\.stx$/i,''):'app';\n }\n function getCurrentLayoutGroup(){\n var meta=document.querySelector('meta[name=\"stx-layout-group\"]');\n if(meta&&meta.getAttribute('content'))return meta.getAttribute('content');\n var layout=document.querySelector('meta[name=\"stx-layout\"]');\n return defaultLayoutGroup(layout?layout.getAttribute('content'):'');\n }\n function getDocLayoutGroup(doc,layout){\n var meta=doc&&doc.querySelector?doc.querySelector('meta[name=\"stx-layout-group\"]'):null;\n return meta&&meta.getAttribute('content')?meta.getAttribute('content'):defaultLayoutGroup(layout||'');\n }\n function checkLayoutChange(newLayout,targetUrl,newGroup){\n var currentLayout=document.querySelector('meta[name=\"stx-layout\"]');\n var curLayoutName=currentLayout?currentLayout.getAttribute('content'):'';\n var curGroup=getCurrentLayoutGroup();\n var nextGroup=newGroup||defaultLayoutGroup(newLayout);\n log('[router] layout check: current='+curLayoutName+'('+curGroup+') new='+(newLayout||'')+'('+nextGroup+')');\n // Different layout GROUP (e.g. app → auth): full reload\n if(curGroup!==nextGroup){\n log('[router] layout group change:',curGroup,'→',nextGroup,'— full reload to:',targetUrl);\n return true;\n }\n // Same group but different SPECIFIC layout (e.g. layouts/app → layouts/coach):\n // full body swap so nav, sidebar, and other layout-level elements update\n if(curLayoutName && newLayout && curLayoutName!==newLayout){\n log('[router] layout name change:',curLayoutName,'→',newLayout,'— full body swap');\n return true;\n }\n return false;\n }\n\n function shouldUseFragmentResponse(){\n return containerSel==='main'||containerSel==='[data-stx-content]';\n }\n\n function navigate(url,pushState,force){\n log('[router] navigate() called:',url,'isNavigating:',isNavigating);\n if(isNavigating)return;\n var t=new URL(url,location.origin);\n\n if(t.origin!==location.origin){location.href=url;return}\n\n if(t.pathname===location.pathname&&t.hash){\n if(pushState!==false)history.pushState({},'',t.href);\n var el=document.querySelector(t.hash);\n if(el)el.scrollIntoView({behavior:'smooth'});\n return;\n }\n\n // Skip if user clicks a link to the page they're already on. We\n // deliberately allow popstate (pushState===false) through because\n // the browser has already updated location.href to the popped\n // entry — without this allowance, hitting back would early-return\n // and the visible page content would be left frozen on the\n // forward-navigation page.\n if(pushState!==false&&t.href===location.href&&!t.hash&&!force)return;\n\n isNavigating=true;\n document.body.classList.add(o.loadingClass);\n startProgress();\n\n var targetHref=t.href;\n var targetPath=cacheKey(url);\n var targetHash=t.hash;\n\n function done(){isNavigating=false;document.body.classList.remove(o.loadingClass);finishProgress()}\n\n if(o.cache&&cache[targetPath]&&!force){\n if(checkLayoutChange(layoutCache[targetPath],url,layoutGroupCache[targetPath])){\n // Layout changed — fetch full page and do full document swap\n log('[router] cache hit but layout changed — fetching full page');\n fetch(url,{headers:{'Accept':'text/html'}}).then(function(r){\n if(!r.ok)throw new Error(r.status);\n return r.text();\n }).then(function(html){\n log('[router] full page fetched from cache path, len:',html.length);\n swap(html,targetPath,pushState,targetHash);\n }).catch(function(err){\n console.error('[router] full page fetch error:',err);\n location.href=url;\n }).finally(done);\n return;\n }\n swap(cache[targetPath],targetPath,pushState,targetHash);\n done();\n }\nelse {\n if(force&&cache[targetPath])delete cache[targetPath];\n // First fetch as fragment (SPA mode) when the configured container is\n // the page content. Custom app-shell containers need full documents so\n // the router does not inject a <main> fragment into the wrong element.\n var wantsFragment=shouldUseFragmentResponse();\n fetch(url,{headers:wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'}}).then(function(r){\n if(!r.ok)throw new Error(r.status);\n var isFragment=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';\n var newLayout=r.headers.get('X-STX-Layout')||'';\n var newGroup=r.headers.get('X-STX-Layout-Group')||'';\n // Layout change? Fetch the FULL page (no X-STX-Router header) and do full document swap\n if(isFragment&&checkLayoutChange(newLayout,url,newGroup)){\n log('[router] layout change — fetching full page for document swap');\n return fetch(url,{headers:{'Accept':'text/html'}}).then(function(fullRes){\n log('[router] full page fetched:',fullRes.status,'ok:',fullRes.ok);\n if(!fullRes.ok)throw new Error(fullRes.status);\n return fullRes.text().then(function(html){\n log('[router] full page html length:',html.length);\n return{html:html,isFragment:false,layout:newLayout,layoutGroup:newGroup};\n });\n });\n }\n return r.text().then(function(html){return{html:html,isFragment:isFragment,layout:newLayout,layoutGroup:newGroup}});\n }).then(function(result){\n if(!result)return;\n if(result.isFragment)result.html='<!--stx-fragment-->'+result.html;\n if(o.cache){cache[targetPath]=result.html;layoutCache[targetPath]=result.layout;layoutGroupCache[targetPath]=result.layoutGroup}\n swap(result.html,targetPath,pushState,targetHash);\n }).catch(function(err){\n console.error('[router] fetch error:',err);\n location.href=url;\n }).finally(done);\n }\n }\n\n function swap(html,url,pushState,hash){\n var isFragment=html.indexOf('<!--stx-fragment-->')===0;\n if(isFragment)html=html.slice('<!--stx-fragment-->'.length);\n var currentContent=getContainer();\n log('[router] swap: isFragment='+isFragment+' container='+!!currentContent+' tag='+(currentContent&¤tContent.tagName)+' selector='+containerSel+' htmlLen='+html.length);\n if(!currentContent){log('[router] no container — falling back');location.href=url;return}\n\n // Fragment mode: server returned just the page content (no document wrapper)\n if(isFragment){\n if(window.stx&&window.stx._cleanupContainer)window.stx._cleanupContainer(currentContent);\n function doFragSwap(){\n // Extract scripts from fragment before injecting HTML\n var fragScripts=[];\n var fragStyles=[];\n var fragCrosswindCSS=null;\n var cleanFrag=html.replace(new RegExp('<scr'+'ipt\\\\\\\\b[^>]*>([\\\\\\\\s\\\\\\\\S]*?)<\\\\\\\\/scr'+'ipt>','gi'),function(m,code){\n if(code&&code.trim())fragScripts.push(code);\n return '';\n });\n cleanFrag=cleanFrag.replace(new RegExp('<sty'+'le\\\\\\\\b([^>]*)>([\\\\\\\\s\\\\\\\\S]*?)<\\\\\\\\/sty'+'le>','gi'),function(m,attrs,css){\n if(attrs.indexOf('data-crosswind')!==-1){\n fragCrosswindCSS=css;\n }else{\n fragStyles.push({attrs:attrs,css:css});\n }\n return '';\n });\n // Remove old page styles (not crosswind — that gets merged)\n document.querySelectorAll('style[data-stx-page]').forEach(function(s){s.remove()});\n // Merge crosswind CSS from fragment into existing crosswind style\n if(fragCrosswindCSS){\n var curCrosswind=document.querySelector('head style[data-crosswind]');\n if(curCrosswind){\n var merged=mergeCrosswindCSS(curCrosswind.textContent||'',fragCrosswindCSS);\n if(merged)curCrosswind.textContent=merged;\n }else{\n var cw=document.createElement('style');\n cw.setAttribute('data-crosswind','generated');\n cw.textContent=fragCrosswindCSS;\n document.head.appendChild(cw);\n }\n }\n // Add new page styles\n fragStyles.forEach(function(s){\n var el=document.createElement('style');\n el.textContent=s.css;\n el.setAttribute('data-stx-page','');\n document.head.appendChild(el);\n });\n // Swap content\n currentContent.innerHTML=cleanFrag;\n // Remove old page scripts\n document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});\n if(pushState!==false)history.pushState({},'',url+(hash||''));\n updateNav(url);\n updateActiveLinks();\n if(o.scrollToTop&&!hash)window.scrollTo({top:0,behavior:'instant'});\n else if(hash){var el=document.querySelector(hash);if(el)el.scrollIntoView({behavior:'smooth'})}\n window.dispatchEvent(new CustomEvent('stx:navigate',{detail:{url:url}}));\n // Execute page scripts FIRST — they define setup functions and set _latestSetup\n log('[router] frag scripts:', fragScripts.length);\n document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});\n fragScripts.forEach(function(code){\n log('[router] exec script len:', code.length, 'has __stx_setup:', code.indexOf('__stx_setup')>-1);\n // Skip scripts that were already executed (layout-level partials\n // like theme.stx, stores.stx, nav.stx). Their top-level const/let\n // declarations would throw \"Identifier has already been declared\"\n // on re-execution. Setup functions (__stx_setup_) must always\n // re-execute because each page has its own setup.\n var h=hashScript(code);\n var isSetup=code.indexOf('__stx_setup_')!==-1;\n // Compute isAlreadyScoped first so the dedup check can exempt\n // self-contained IIFE scripts. data-stx-scoped scope IIFEs\n // (the (function(){ ... })() shape) must re-execute on every\n // navigation. They own their own scope and won't collide\n // with prior runs, AND _cleanupContainer deleted their\n // entry from window.stx._scopes on the way out. Without\n // this exemption, navigating away and back leaves the page\n // leaf with no scope registered: bindIf/@event never re-bind\n // and all :if branches stay visible at once.\n // Mirrors the full-doc swap path's runAlways escape below.\n var isAlreadyScoped=code.trimStart().charAt(0)==='('||code.trimStart().charAt(0)===';'||code.indexOf('window.stx.mount')>-1;\n if(!isSetup&&!isAlreadyScoped&&executedScriptHashes[h]){\n log('[router] skipping already-executed script (hash dedup)');\n return;\n }\n executedScriptHashes[h]=1;\n var ns=document.createElement('script');\n var hasImport=code.indexOf('import ')!==-1;\n if(hasImport){\n ns.type='module';\n }\n ns.textContent=(hasImport||isAlreadyScoped)?code:'{'+code+'}';\n ns.setAttribute('data-stx-page','');\n document.body.appendChild(ns);\n });\n log('[router] scripts done. _latestSetup:', !!window.stx._latestSetup);\n // THEN fire stx:load — now _latestSetup is set and processElement has the right scope\n window.dispatchEvent(new Event('stx:load'));\n }\n if(runViewTransition(doFragSwap)){}\n else{currentContent.style.transition='opacity 0.12s ease-out';currentContent.style.opacity='0';setTimeout(function(){doFragSwap();currentContent.style.opacity='1';setTimeout(function(){currentContent.style.transition=''},150)},120)}\n return;\n }\n\n // Full document mode: parse with DOMParser and extract container content\n var parser=new DOMParser();\n var doc=parser.parseFromString(html,'text/html');\n var newContent=doc.querySelector(containerSel)||doc.querySelector('[data-stx-content]')||doc.querySelector('main');\n if(!newContent){location.href=url;return}\n\n // Clean up existing signals/effects\n if(window.stx&&window.stx._cleanupContainer){\n window.stx._cleanupContainer(currentContent);\n }\n\n function doSwap(){\n // ── Swap <head> styles ──\n // Inject new styles FIRST, then remove old to prevent unstyled flash\n var keepIds={'stx-view-transitions':1,'stx-r-css':1};\n var curStyles=document.querySelectorAll('head style');\n var newStyles=doc.querySelectorAll('head style');\n\n // Merge crosswind styles instead of replacing — persistent elements\n // (nav, footer) outside <main> still need their utility classes\n var curCrosswind=document.querySelector('head style[data-crosswind]');\n var newCrosswind=null;\n newStyles.forEach(function(s){if(s.getAttribute('data-crosswind'))newCrosswind=s});\n\n if(curCrosswind&&newCrosswind){\n var merged=mergeCrosswindCSS(curCrosswind.textContent||'',newCrosswind.textContent||'');\n if(merged)curCrosswind.textContent=merged;\n }\n\n var incoming=[];\n newStyles.forEach(function(s){\n if(!keepIds[s.id]&&!s.getAttribute('data-crosswind')){\n var ns=document.createElement('style');\n ns.textContent=s.textContent;\n ns.setAttribute('data-stx-incoming','');\n document.head.appendChild(ns);\n incoming.push(ns);\n }\n });\n\n // If no existing crosswind but new page has one, add it\n if(!curCrosswind&&newCrosswind){\n var ns=document.createElement('style');\n ns.textContent=newCrosswind.textContent;\n ns.setAttribute('data-crosswind',newCrosswind.getAttribute('data-crosswind'));\n document.head.appendChild(ns);\n }\n\n // Remove old styles (except persistent ones, crosswind, and incoming)\n curStyles.forEach(function(s){\n if(!keepIds[s.id]&&!s.hasAttribute('data-stx-incoming')&&!s.hasAttribute('data-crosswind'))s.remove();\n });\n\n incoming.forEach(function(s){s.removeAttribute('data-stx-incoming')});\n\n // ── Swap content ──\n // For layout changes: swap the entire <body> to replace layout chrome (nav, footer, etc.)\n // For same-layout: swap only the container (<main>)\n var newBody=doc.querySelector('body');\n var isLayoutChange=false;\n if(newBody){\n var newMeta=doc.querySelector('meta[name=\"stx-layout\"]');\n var curMeta=document.querySelector('meta[name=\"stx-layout\"]');\n var curLayout=curMeta?curMeta.getAttribute('content'):'';\n var newLayout=newMeta?newMeta.getAttribute('content'):'';\n var curGroup=getCurrentLayoutGroup();\n var newGroup=getDocLayoutGroup(doc,newLayout);\n isLayoutChange=curGroup!==newGroup||(curLayout&&newLayout&&curLayout!==newLayout);\n }\n if(isLayoutChange&&newBody){\n log('[router] full body swap for layout change');\n // Replace entire body content — layout chrome and all\n var bodyHTML=newBody.innerHTML.replace(new RegExp('<scr'+'ipt\\\\\\\\b[^>]*>[\\\\\\\\s\\\\\\\\S]*?<\\\\\\\\/scr'+'ipt\\\\\\\\s*>','gi'),'');\n document.body.innerHTML=bodyHTML;\n // Copy body attributes (class, data-stx, etc.)\n Array.from(newBody.attributes).forEach(function(attr){document.body.setAttribute(attr.name,attr.value)});\n // Update layout meta tag\n var oldMeta=document.querySelector('meta[name=\"stx-layout\"]');\n var freshMeta=doc.querySelector('meta[name=\"stx-layout\"]');\n if(oldMeta&&freshMeta)oldMeta.setAttribute('content',freshMeta.getAttribute('content')||'');\n else if(freshMeta){var m=document.createElement('meta');m.name='stx-layout';m.content=freshMeta.getAttribute('content')||'';document.head.appendChild(m)}\n var oldGroupMeta=document.querySelector('meta[name=\"stx-layout-group\"]');\n var freshGroupMeta=doc.querySelector('meta[name=\"stx-layout-group\"]');\n if(oldGroupMeta&&freshGroupMeta)oldGroupMeta.setAttribute('content',freshGroupMeta.getAttribute('content')||'');\n else if(freshGroupMeta){var gm=document.createElement('meta');gm.name='stx-layout-group';gm.content=freshGroupMeta.getAttribute('content')||'';document.head.appendChild(gm)}\n // Update container reference for script execution below\n currentContent=document.querySelector(containerSel)||document.querySelector('main')||document.body;\n } else {\n // Same layout — swap only container content\n var cleanHTML=newContent.innerHTML.replace(new RegExp('<scr'+'ipt\\\\\\\\b[^>]*>[\\\\\\\\s\\\\\\\\S]*?<\\\\\\\\/scr'+'ipt\\\\\\\\s*>','gi'),'');\n currentContent.innerHTML=cleanHTML;\n }\n\n // ── Load new external <head> scripts ──\n var loadedSrcs={};\n document.querySelectorAll('head script[src]').forEach(function(s){loadedSrcs[s.src]=1});\n var extPromises=[];\n doc.querySelectorAll('head script[src]').forEach(function(s){\n var src=new URL(s.getAttribute('src'),location.origin).href;\n if(loadedSrcs[src])return;\n loadedSrcs[src]=1;\n extPromises.push(new Promise(function(resolve,reject){\n var ns=document.createElement('script');\n ns.src=src;\n ns.onload=resolve;\n ns.onerror=reject;\n document.head.appendChild(ns);\n }));\n });\n\n // ── Script re-execution ──\n // Remove previously injected page scripts\n document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});\n\n var scripts=[];\n function addScript(text, runAlways){\n scripts.push({text:text,runAlways:!!runAlways});\n }\n if(isLayoutChange){\n // Layout change: collect ALL scripts from the new document body\n // (layout scripts, component mounts, setup functions — everything)\n var newBodyEl=doc.querySelector('body');\n if(newBodyEl){\n newBodyEl.querySelectorAll('script').forEach(function(s){\n var text=s.textContent||'';\n if(s.hasAttribute('src'))return;\n if(!text.trim())return;\n // Skip the signals runtime IIFE — it's already loaded\n if(text.indexOf(\"'use strict';var cloakStyle\")!==-1)return;\n if(text.indexOf('__stxRouter')!==-1)return;\n addScript(text,true);\n });\n }\n // Also collect setup functions from <head>\n doc.querySelectorAll('head script').forEach(function(s){\n var text=s.textContent||'';\n if(s.hasAttribute('src'))return;\n if(!text.trim())return;\n if(text.indexOf('__stx_setup_')!==-1)addScript(text,true);\n });\n } else {\n // Same layout: collect scripts from the container\n newContent.querySelectorAll('script').forEach(function(s){\n var text=s.textContent||'';\n if(s.hasAttribute('src'))return;\n if(!text.trim())return;\n addScript(text,true);\n });\n // Collect page scripts from <head> AND <body> (outside container).\n // SSG builds place the setup function in <body> before <main>, not\n // in <head>. Without this, SPA navigation on static sites never\n // runs the setup and reactive data stays empty.\n var seenSetups={};\n scripts.forEach(function(entry){var t=entry.text;if(t.indexOf('__stx_setup_')!==-1)seenSetups[t.substring(0,80)]=1});\n doc.querySelectorAll('script').forEach(function(s){\n var text=s.textContent||'';\n if(s.hasAttribute('src'))return;\n if(!text.trim())return;\n // Skip signals runtime and router (they contain __stx_setup references but aren't setup functions)\n if(text.indexOf(\"'use strict';var cloakStyle\")!==-1)return;\n if(text.indexOf('__stxRouter')!==-1)return;\n if(newContent.contains(s))return;\n if(text.indexOf('__stx_setup_')===-1&&!s.hasAttribute('data-stx-scoped')&&!s.hasAttribute('data-stx-page'))return;\n var key=text.substring(0,80);\n if(seenSetups[key])return;\n seenSetups[key]=1;\n addScript(text,true);\n });\n }\n\n // Push history state (before active link updates so location.pathname is current)\n if(pushState!==false)history.pushState({},'',url+(hash||''));\n\n // Update active nav links\n updateNav(url);\n updateActiveLinks();\n\n // Scroll\n if(o.scrollToTop&&!hash)window.scrollTo({top:0,behavior:'instant'});\n else if(hash){var el=document.querySelector(hash);if(el)el.scrollIntoView({behavior:'smooth'})}\n\n // Update title\n var newTitle=doc.querySelector('title');\n if(newTitle)document.title=newTitle.textContent;\n\n // Update <html lang> from the destination doc so screen readers,\n // CSS :lang() selectors, and any i18n picker that mirrors\n // document.documentElement.lang stay accurate after SPA hops.\n if(doc.documentElement&&doc.documentElement.lang){\n document.documentElement.lang=doc.documentElement.lang;\n }\n\n window.dispatchEvent(new CustomEvent('stx:navigate',{detail:{url:url}}));\n\n // Execute page scripts FIRST — they define setup functions and set _latestSetup\n function execScripts(){\n scripts.forEach(function(entry){\n var text=typeof entry==='string'?entry:entry.text;\n var runAlways=typeof entry==='string'?false:entry.runAlways;\n // Skip scripts that were already executed (layout-level partials\n // like theme.stx). Their top-level const/function declarations\n // would throw \"Identifier has already been declared\" on re-execution.\n // Exception: setup functions (__stx_setup_) must always re-execute\n // because each page has its own setup, and import statements\n // need special handling.\n var h=hashScript(text);\n var isSetup=text.indexOf('__stx_setup_')!==-1;\n var hasImport=text.indexOf('import ')!==-1;\n if(!runAlways&&!isSetup&&executedScriptHashes[h])return;\n executedScriptHashes[h]=1;\n // Wrap scripts with import statements as modules (Bug 3 fix)\n var ns=document.createElement('script');\n if(hasImport){\n ns.type='module';\n }\n // Wrap in block scope to prevent const/let collisions on re-navigation.\n // Top-level const/let in <script> tags are global-scoped in browsers\n // and throw \"Identifier has already been declared\" on re-execution.\n // Skip the wrap for module scripts — they get their own scope from\n // ESM, and top-level 'import' is illegal inside a block, which\n // would throw SyntaxError before the script ever runs.\n var alreadyScoped=text.trimStart().charAt(0)==='('||text.trimStart().charAt(0)===';'||text.indexOf('window.stx.mount')>-1;\n ns.textContent=(hasImport||alreadyScoped)?text:'{'+text+'}';\n ns.setAttribute('data-stx-page','');\n document.body.appendChild(ns);\n });\n // THEN fire stx:load — now _latestSetup is set and processElement has the right scope\n window.dispatchEvent(new Event('stx:load'));\n }\n\n if(extPromises.length>0){\n Promise.all(extPromises).then(execScripts).catch(execScripts);\n }\nelse {\n execScripts();\n }\n }\n\n if(runViewTransition(doSwap)){\n }\nelse {\n // Fallback fade for browsers without View Transitions API\n currentContent.style.transition='opacity 0.12s ease-out';\n currentContent.style.opacity='0';\n setTimeout(function(){\n doSwap();\n currentContent.style.opacity='1';\n setTimeout(function(){currentContent.style.transition=''},150);\n },120);\n }\n }\n\n // ── Link interception ──\n function shouldIntercept(link){\n if(!link)return false;\n var href=link.getAttribute('href');\n if(!href)return false;\n if(href.startsWith('http')||href.startsWith('#')||href.startsWith('mailto:')||href.startsWith('tel:')||href.startsWith('javascript:'))return false;\n if(link.target==='_blank')return false;\n if(link.hasAttribute('data-stx-no-router')||link.hasAttribute('data-no-router')||link.hasAttribute('download'))return false;\n if(href===location.pathname)return false;\n if(!getContainer())return false;\n return true;\n }\n\n // SPA click handling. By default only [data-stx-link] elements get\n // SPA navigation; regular <a href> does native full page reload. Set\n // interceptAllLinks:true (window.__stxRouterConfig or build-time\n // config) to intercept any same-origin anchor — used by static-site\n // mode where every page is part of the same SPA shell.\n document.addEventListener('click',function(e){\n if(e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||e.button!==0)return;\n if(!e.target||!e.target.closest)return;\n var link=e.target.closest('[data-stx-link]');\n if(!link&&o.interceptAllLinks){\n var anchor=e.target.closest('a[href]');\n if(anchor&&shouldIntercept(anchor))link=anchor;\n }\n if(!link){return}\n var href=link.getAttribute('href');\n log('[router] click intercepted:',href,'container:',!!getContainer(),'defaultPrevented:',e.defaultPrevented);\n if(!href||href.startsWith('http')||href.startsWith('#')||href.startsWith('mailto:')||href.startsWith('tel:')){log('[router] skipped:',href);return}\n if(link.target==='_blank'||link.hasAttribute('download'))return;\n e.preventDefault();\n e.stopPropagation();\n log('[router] navigating to:',href);\n navigate(href);\n },true);\n\n // ── Back/forward ──\n window.addEventListener('popstate',function(){\n navigate(location.pathname+location.search+location.hash,false);\n });\n\n // ── Prefetch on hover ──\n if(o.prefetch){\n document.addEventListener('mouseover',function(e){\n if(!e.target||!e.target.closest)return;\n var link=e.target.closest('[data-stx-link]');\n if(!link)return;\n var href=link.getAttribute('href');\n var key=cacheKey(href);\n if(cache[key]||prefetching[key])return;\n prefetching[key]=true;\n var wantsFragment=shouldUseFragmentResponse();\n fetch(href,{headers:wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'}}).then(function(r){\n var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';\n var pLayout=r.headers.get('X-STX-Layout')||'';\n var pGroup=r.headers.get('X-STX-Layout-Group')||'';\n return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}});\n }).then(function(result){\n if(o.cache){cache[key]=result.html;layoutCache[key]=result.layout;layoutGroupCache[key]=result.layoutGroup}\n }).catch(function(){}).finally(function(){delete prefetching[key]});\n },true);\n }\n\n // ── Active link management ──\n function updateNav(url){\n document.querySelectorAll('nav a[href], #mobileNav a[href], [data-stx-nav] a[href]').forEach(function(a){\n var href=a.getAttribute('href');\n if(!href||href.startsWith('#')||href.startsWith('http'))return;\n var isActive=(href===url)||(href==='/'&&url==='/');\n if(a.hasAttribute('data-stx-link')){\n var ac=a.getAttribute('data-stx-active-class')||'active';\n if(isActive)ac.split(' ').forEach(function(cls){if(cls)a.classList.add(cls)});else ac.split(' ').forEach(function(cls){if(cls)a.classList.remove(cls)});\n }\n });\n }\n\n function updateActiveLinks(){\n // Update active classes on <stx-link> elements (and legacy data-stx-link)\n var links=document.querySelectorAll('[data-stx-link]');\n var cur=location.pathname;\n links.forEach(function(link){\n var href=link.getAttribute('to')||link.getAttribute('href')||'';\n var ac=link.getAttribute('active-class')||link.getAttribute('data-stx-active-class')||'active';\n var eac=link.getAttribute('exact-active-class')||link.getAttribute('data-stx-exact-active-class')||'exact-active';\n ac.split(' ').forEach(function(cls){if(cls)link.classList.remove(cls)});\n eac.split(' ').forEach(function(cls){if(cls)link.classList.remove(cls)});\n var isExact=cur===href;\n var isActive=href!=='/'?cur.startsWith(href):cur==='/';\n if(isExact)eac.split(' ').forEach(function(cls){if(cls)link.classList.add(cls)});\n if(isActive)ac.split(' ').forEach(function(cls){if(cls)link.classList.add(cls)});\n });\n }\n\n // ── Progress bar DOM + style ──\n // Injects a fixed-position element at the top of the viewport plus the\n // minimal CSS for its transform/opacity transitions. Kept idempotent so\n // repeated init() calls (e.g. after full-body swaps) don't duplicate.\n // Also ships the default View Transitions fade+slide CSS when\n // viewTransitions is enabled and the browser supports it. Apps can\n // override by defining more specific ::view-transition-* rules later in\n // the cascade, or opt out entirely with viewTransitions:false.\n function injectStyles(){\n if(!document.getElementById('stx-r-css')){\n var s=document.createElement('style');s.id='stx-r-css';\n // Strip characters that could escape our CSS block and inject new\n // declarations: ; { } ( ) \" ' \\\\ < > plus whitespace control chars.\n // This keeps the value safe to concat into a stylesheet even if a\n // caller wires the config from an untrusted source.\n var sanitize=function(v){return String(v).replace(/[;{}()\"'\\\\\\\\<>\\\\n\\\\r\\\\t]/g,'')};\n var pc=sanitize(o.progressColor);\n var ph=sanitize(o.progressHeight);\n var css='.stx-navigating{cursor:wait}.stx-navigating a,.stx-navigating button{pointer-events:none}#stx-router-progress{position:fixed;top:0;left:0;right:0;height:'+ph+';background:'+pc+';box-shadow:0 0 8px '+pc+',0 0 4px '+pc+';transform:scaleX(0);transform-origin:left;transition:transform .18s ease-out,opacity .26s ease;opacity:0;pointer-events:none;z-index:999999}';\n if(o.viewTransitions&&'startViewTransition' in document){\n var dur=(o.viewTransitionDuration||220)+'ms';\n var ease=o.viewTransitionEasing||'cubic-bezier(0.16, 1, 0.3, 1)';\n css+='::view-transition-old(root),::view-transition-new(root){animation-duration:'+dur+';animation-timing-function:'+ease+'}';\n css+='::view-transition-old(root){animation-name:stx-r-fade-out}::view-transition-new(root){animation-name:stx-r-fade-in}';\n css+='@keyframes stx-r-fade-out{from{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-4px)}}';\n css+='@keyframes stx-r-fade-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}';\n css+='@media (prefers-reduced-motion: reduce){::view-transition-old(root),::view-transition-new(root){animation-duration:0s;animation-name:none}}';\n }\n s.textContent=css;\n document.head.appendChild(s);\n }\n if(o.progress&&!document.getElementById('stx-router-progress')){\n var el=document.createElement('div');\n el.id='stx-router-progress';\n el.setAttribute('role','progressbar');\n el.setAttribute('aria-hidden','true');\n // Append to <html>, not <body>: when the router swaps the body\n // (container:'body' for full-page static-site SPAs) the progress\n // element gets wiped along with it. <html> stays put across\n // swaps, so the bar survives multi-hop navigation.\n document.documentElement.appendChild(el);\n progEl=el;\n } else if(o.progress){\n progEl=document.getElementById('stx-router-progress');\n }\n }\n\n function injectViewTransitionCSS(){\n if(document.getElementById('stx-view-transitions'))return;\n var s=document.createElement('style');s.id='stx-view-transitions';\n s.textContent='::view-transition-group(root){animation:none}::view-transition-old(root){animation:none}::view-transition-new(root){animation:none}main,#app-content,[data-stx-content]{view-transition-name:stx-content}::view-transition-old(stx-content){animation:stx-fade-out .15s ease-out both}::view-transition-new(stx-content){animation:stx-fade-in .15s ease-in .1s both}@keyframes stx-fade-out{from{opacity:1}to{opacity:0}}@keyframes stx-fade-in{from{opacity:0}to{opacity:1}}::view-transition{background:transparent}::view-transition-group(stx-content){background:inherit;overflow:hidden}';\n (document.head||document.documentElement).appendChild(s);\n }\n\n // ── Public API ──\n var router={\n navigate:navigate,\n navigateTo:navigate,\n prefetch:function(url){\n var key=cacheKey(url);\n if(!cache[key]){\n var wantsFragment=shouldUseFragmentResponse();\n fetch(url,{headers:wantsFragment?{'X-STX-Router':'true'}:{'Accept':'text/html'}}).then(function(r){var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';var pLayout=r.headers.get('X-STX-Layout')||'';var pGroup=r.headers.get('X-STX-Layout-Group')||'';return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}})}).then(function(result){cache[key]=result.html;layoutCache[key]=result.layout;layoutGroupCache[key]=result.layoutGroup}).catch(function(){});\n }\n },\n clearCache:function(){for(var k in cache)delete cache[k];for(var lk in layoutCache)delete layoutCache[lk];for(var gk in layoutGroupCache)delete layoutGroupCache[gk]},\n cache:cache,\n swap:swap,\n updateNav:updateNav\n };\n\n window.__stxRouter=router;\n window.stxRouter=router;\n if(window.stx)window.stx.router=router;\n\n // ── Initialize ──\n function init(){\n injectStyles();\n injectViewTransitionCSS();\n updateActiveLinks();\n updateNav(location.pathname);\n }\n\n if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);else init();\n})();\n`\n}\n",
|
|
5
|
+
"/* eslint-disable prefer-const, no-console, style/max-statements-per-line, general/prefer-template */\n/**\n * STX Router - Canonical SPA Router\n *\n * Single source of truth for client-side navigation.\n * Injected via @stxRouter directive or auto-loaded with the signals runtime.\n *\n * Features:\n * - Click interception for internal links\n * - History API pushState navigation\n * - View Transitions API with CSS fade fallback\n * - <head> style swapping (prevents unstyled flash)\n * - Smart script filtering (skip signals runtime, layout guards)\n * - Page prefetching on hover\n * - Response caching (5-minute TTL)\n * - Active link class management (data-stx-link)\n * - Loading indicator bar\n * - Configurable via window.STX_ROUTER_OPTIONS or window.__stxRouterConfig\n */\nexport function getRouterScript(): string {\n return `\n;(function(){\n 'use strict';\n var ROUTER_REV=3;\n if(window.__stxRouter&&window.__stxRouter.__rev===ROUTER_REV)return;\n\n // ── Configuration ──\n var defaults={container:'main',loadingClass:'stx-navigating',viewTransitions:true,cache:true,scrollToTop:true,prefetch:true,progress:true,progressColor:'#78dce8',progressHeight:'2px',interceptAllLinks:false,prefetchCacheMax:50};\n var o=Object.assign({},defaults,window.__stxRouterConfig||{},window.STX_ROUTER_OPTIONS||{});\n var containerSel=o.container;\n var debug=!!o.debug;\n function log(){if(debug&&typeof console!=='undefined'&&console.log)console.log.apply(console,arguments)}\n function runViewTransition(callback){\n if(!o.viewTransitions||!document.startViewTransition)return false;\n try{\n var transition=document.startViewTransition(callback);\n var onAbort=function(err){log('[router] view transition aborted:',err&&err.message?err.message:err)};\n if(transition&&transition.finished&&transition.finished.catch)transition.finished.catch(onAbort);\n if(transition&&transition.updateCallbackDone&&transition.updateCallbackDone.catch)transition.updateCallbackDone.catch(onAbort);\n return true;\n }catch(err){\n log('[router] view transition unavailable:',err&&err.message?err.message:err);\n return false;\n }\n }\n\n // ── Progress bar (0→100% at top of viewport) ──\n // Native loading indicator baked into the router. Starts when navigation\n // begins, trickles up to ~90% during fetch, snaps to 100% + fades on\n // completion. Opt-out via window.__stxRouterConfig={progress:false}.\n var progEl=null;\n var progVal=0;\n var progTimer=null;\n function setProgress(v){\n progVal=v<0?0:(v>100?100:v);\n if(!progEl)return;\n progEl.style.opacity=progVal>0?'1':'0';\n progEl.style.transform='scaleX('+(progVal/100)+')';\n }\n function startProgress(){\n if(!o.progress||!progEl)return;\n if(progTimer){clearInterval(progTimer);progTimer=null}\n setProgress(8);\n progTimer=setInterval(function(){\n if(progVal>=90)return;\n // Asymptotic trickle — fast at first, slower near 90%\n var inc=Math.max(0.4,(90-progVal)*0.08);\n setProgress(progVal+inc);\n },160);\n }\n function finishProgress(){\n if(!o.progress||!progEl)return;\n if(progTimer){clearInterval(progTimer);progTimer=null}\n setProgress(100);\n // Fade out, then reset. Delays chosen so the \"full\" state is briefly\n // visible before the bar disappears.\n setTimeout(function(){\n if(!progEl)return;\n progEl.style.opacity='0';\n setTimeout(function(){progVal=0;if(progEl)progEl.style.transform='scaleX(0)'},260);\n },180);\n }\n\n var cache={};\n var prefetching={};\n var isNavigating=false;\n\n // LRU bookkeeping for cache/layoutCache/layoutGroupCache. Pre-fix\n // (stacksjs/stx#1719) these were plain objects with no eviction, so\n // hover-prefetch on a sidebar of 200 internal links retained ~10MB\n // of HTML strings for the page's lifetime. cacheOrder is a most-\n // recently-used-last list of keys; setCache pushes new entries and\n // evicts the oldest when length exceeds o.prefetchCacheMax (default\n // 50). touchCache promotes a key on cache-hit. We use a plain array\n // rather than a Map to keep the diff localized and the runtime\n // hand-minified shape consistent.\n var cacheOrder=[];\n function setCache(key,html,layout,group){\n if(!(key in cache))cacheOrder.push(key);\n cache[key]=html;\n layoutCache[key]=layout;\n layoutGroupCache[key]=group;\n while(cacheOrder.length>o.prefetchCacheMax){\n var oldest=cacheOrder.shift();\n delete cache[oldest];\n delete layoutCache[oldest];\n delete layoutGroupCache[oldest];\n }\n }\n function touchCache(key){\n var i=cacheOrder.indexOf(key);\n if(i>=0){cacheOrder.splice(i,1);cacheOrder.push(key)}\n }\n function evictCache(key){\n var i=cacheOrder.indexOf(key);\n if(i>=0)cacheOrder.splice(i,1);\n delete cache[key];\n delete layoutCache[key];\n delete layoutGroupCache[key];\n }\n\n // Extract top-level CSS blocks (rules AND @media blocks with nested braces).\n // The old regex ([^{}]+\\{[^{}]*\\}) silently dropped @media blocks because\n // it couldn't handle nested braces — causing responsive classes (lg:, md:)\n // to vanish after SPA navigation.\n function extractCssBlocks(css){\n var blocks=[];\n var i=0;\n var len=css.length;\n while(i<len){\n // Skip whitespace\n while(i<len&&(css[i]===' '||css[i]==='\\\\n'||css[i]==='\\\\r'||css[i]==='\\\\t'))i++;\n if(i>=len)break;\n // Find the start of a block (first {)\n var blockStart=i;\n var bracePos=css.indexOf('{',i);\n if(bracePos===-1)break;\n // Walk forward tracking brace depth to find the matching close\n var depth=0;\n var j=bracePos;\n while(j<len){\n if(css[j]==='{')depth++;\n else if(css[j]==='}'){depth--;if(depth===0){j++;break}}\n j++;\n }\n if(depth!==0)break;\n blocks.push(css.slice(blockStart,j).trim());\n i=j;\n }\n return blocks;\n }\n\n function mergeCrosswindCSS(existing,incoming){\n var blocks=extractCssBlocks(incoming);\n var newBlocks=[];\n for(var bi=0;bi<blocks.length;bi++){\n if(existing.indexOf(blocks[bi])===-1)newBlocks.push(blocks[bi]);\n }\n if(newBlocks.length>0)return existing+'\\\\n'+newBlocks.join('\\\\n');\n return null;\n }\n\n function getContainer(){\n return document.querySelector(containerSel)||document.querySelector('[data-stx-content]')||document.querySelector('main');\n }\n\n // ── Navigation ──\n // Layout group detection — layouts in the same group share a <main> container.\n // Only truly different layout groups trigger a full page reload.\n var layoutCache={};\n var layoutGroupCache={};\n // Track executed script hashes to prevent redeclaration errors on navigation.\n // Layout-level scripts (theme, nav setup) execute on initial page load and\n // should NOT re-execute when navigating to another page with the same layout.\n var executedScriptHashes={};\n function hashScript(code){\n var h=0;for(var i=0;i<code.length;i++){h=((h<<5)-h)+code.charCodeAt(i);h|=0}\n return h;\n }\n // Record scripts from the initial page load\n document.querySelectorAll('script').forEach(function(s){\n var text=s.textContent||'';\n if(text.trim()&&!s.hasAttribute('src'))executedScriptHashes[hashScript(text)]=1;\n });\n function cacheKey(url){\n var u=new URL(url,location.origin);\n return u.pathname+u.search;\n }\n // Keep SPA fetches inside the active locale (/en/... when viewing English).\n function withCurrentLocale(href){\n var d=window.__stxI18n;\n if(d&&typeof d.localizeHref==='function')return d.localizeHref(href);\n return href;\n }\n function defaultLayoutGroup(layout){\n if(!layout)return 'app';\n var clean=String(layout).replace(/\\\\\\\\/g,'/');\n var parts=clean.split('/').filter(Boolean);\n var idx=parts.lastIndexOf('layouts');\n var part=idx>=0?parts[idx+1]:parts[parts.length-1];\n return part?part.replace(/\\\\.stx$/i,''):'app';\n }\n function getCurrentLayoutGroup(){\n var meta=document.querySelector('meta[name=\"stx-layout-group\"]');\n if(meta&&meta.getAttribute('content'))return meta.getAttribute('content');\n var layout=document.querySelector('meta[name=\"stx-layout\"]');\n return defaultLayoutGroup(layout?layout.getAttribute('content'):'');\n }\n function getDocLayoutGroup(doc,layout){\n var meta=doc&&doc.querySelector?doc.querySelector('meta[name=\"stx-layout-group\"]'):null;\n return meta&&meta.getAttribute('content')?meta.getAttribute('content'):defaultLayoutGroup(layout||'');\n }\n function checkLayoutChange(newLayout,targetUrl,newGroup){\n var currentLayout=document.querySelector('meta[name=\"stx-layout\"]');\n var curLayoutName=currentLayout?currentLayout.getAttribute('content'):'';\n var curGroup=getCurrentLayoutGroup();\n var nextGroup=newGroup||defaultLayoutGroup(newLayout);\n log('[router] layout check: current='+curLayoutName+'('+curGroup+') new='+(newLayout||'')+'('+nextGroup+')');\n // Different layout GROUP (e.g. app → auth): full reload\n if(curGroup!==nextGroup){\n log('[router] layout group change:',curGroup,'→',nextGroup,'— full reload to:',targetUrl);\n return true;\n }\n // Same group but different SPECIFIC layout (e.g. layouts/app → layouts/coach):\n // full body swap so nav, sidebar, and other layout-level elements update\n if(curLayoutName && newLayout && curLayoutName!==newLayout){\n log('[router] layout name change:',curLayoutName,'→',newLayout,'— full body swap');\n return true;\n }\n return false;\n }\n\n function shouldUseFragmentResponse(){\n return containerSel==='main'||containerSel==='[data-stx-content]';\n }\n\n function navigate(url,pushState,force){\n // Lang-picker passes force=true with an already-localized path (/en/...).\n // Re-localizing would map it back to the *current* locale and no-op.\n if(!force) url=withCurrentLocale(url);\n log('[router] navigate() called:',url,'isNavigating:',isNavigating);\n if(isNavigating)return;\n var t=new URL(url,location.origin);\n\n if(t.origin!==location.origin){location.href=url;return}\n\n if(t.pathname===location.pathname&&t.hash){\n if(pushState!==false)history.pushState({},'',t.href);\n var el=document.querySelector(t.hash);\n if(el)el.scrollIntoView({behavior:'smooth'});\n return;\n }\n\n // Skip if user clicks a link to the page they're already on. We\n // deliberately allow popstate (pushState===false) through because\n // the browser has already updated location.href to the popped\n // entry — without this allowance, hitting back would early-return\n // and the visible page content would be left frozen on the\n // forward-navigation page.\n if(pushState!==false&&t.href===location.href&&!t.hash&&!force)return;\n\n isNavigating=true;\n document.body.classList.add(o.loadingClass);\n startProgress();\n\n var targetHref=t.href;\n var targetPath=cacheKey(url);\n var targetHash=t.hash;\n\n function done(){isNavigating=false;document.body.classList.remove(o.loadingClass);finishProgress()}\n\n if(o.cache&&cache[targetPath]&&!force){\n // Cache hit → promote in LRU order so this entry survives eviction\n // longer than other entries that haven't been touched.\n touchCache(targetPath);\n if(checkLayoutChange(layoutCache[targetPath],url,layoutGroupCache[targetPath])){\n // Layout changed — fetch full page and do full document swap\n log('[router] cache hit but layout changed — fetching full page');\n fetch(url,{headers:{'Accept':'text/html'}}).then(function(r){\n if(!r.ok)throw new Error(r.status);\n return r.text();\n }).then(function(html){\n log('[router] full page fetched from cache path, len:',html.length);\n swap(html,targetPath,pushState,targetHash);\n }).catch(function(err){\n console.error('[router] full page fetch error:',err);\n location.href=url;\n }).finally(done);\n return;\n }\n swap(cache[targetPath],targetPath,pushState,targetHash);\n done();\n }\nelse {\n if(force&&cache[targetPath])evictCache(targetPath);\n // First fetch as fragment (SPA mode) when the configured container is\n // the page content. Custom app-shell containers need full documents so\n // the router does not inject a <main> fragment into the wrong element.\n var wantsFragment=shouldUseFragmentResponse();\n fetch(url,{headers:wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'}}).then(function(r){\n if(!r.ok)throw new Error(r.status);\n var isFragment=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';\n var newLayout=r.headers.get('X-STX-Layout')||'';\n var newGroup=r.headers.get('X-STX-Layout-Group')||'';\n // Layout change? Fetch the FULL page (no X-STX-Router header) and do full document swap\n if(isFragment&&checkLayoutChange(newLayout,url,newGroup)){\n log('[router] layout change — fetching full page for document swap');\n return fetch(url,{headers:{'Accept':'text/html'}}).then(function(fullRes){\n log('[router] full page fetched:',fullRes.status,'ok:',fullRes.ok);\n if(!fullRes.ok)throw new Error(fullRes.status);\n return fullRes.text().then(function(html){\n log('[router] full page html length:',html.length);\n return{html:html,isFragment:false,layout:newLayout,layoutGroup:newGroup};\n });\n });\n }\n return r.text().then(function(html){return{html:html,isFragment:isFragment,layout:newLayout,layoutGroup:newGroup}});\n }).then(function(result){\n if(!result)return;\n if(result.isFragment)result.html='<!--stx-fragment-->'+result.html;\n if(o.cache)setCache(targetPath,result.html,result.layout,result.layoutGroup);\n swap(result.html,targetPath,pushState,targetHash);\n }).catch(function(err){\n console.error('[router] fetch error:',err);\n location.href=url;\n }).finally(done);\n }\n }\n\n function swap(html,url,pushState,hash){\n var isFragment=html.indexOf('<!--stx-fragment-->')===0;\n if(isFragment)html=html.slice('<!--stx-fragment-->'.length);\n var currentContent=getContainer();\n log('[router] swap: isFragment='+isFragment+' container='+!!currentContent+' tag='+(currentContent&¤tContent.tagName)+' selector='+containerSel+' htmlLen='+html.length);\n if(!currentContent){log('[router] no container — falling back');location.href=url;return}\n\n // Fragment mode: server returned just the page content (no document wrapper)\n if(isFragment){\n if(window.stx&&window.stx._cleanupContainer)window.stx._cleanupContainer(currentContent);\n function doFragSwap(){\n // Extract scripts from fragment before injecting HTML\n var fragScripts=[];\n var fragStyles=[];\n var fragCrosswindCSS=null;\n var cleanFrag=html.replace(new RegExp('<scr'+'ipt\\\\\\\\b[^>]*>([\\\\\\\\s\\\\\\\\S]*?)<\\\\\\\\/scr'+'ipt>','gi'),function(m,code){\n if(code&&code.trim())fragScripts.push(code);\n return '';\n });\n cleanFrag=cleanFrag.replace(new RegExp('<sty'+'le\\\\\\\\b([^>]*)>([\\\\\\\\s\\\\\\\\S]*?)<\\\\\\\\/sty'+'le>','gi'),function(m,attrs,css){\n if(attrs.indexOf('data-crosswind')!==-1){\n fragCrosswindCSS=css;\n }else{\n fragStyles.push({attrs:attrs,css:css});\n }\n return '';\n });\n // Remove old page styles (not crosswind — that gets merged)\n document.querySelectorAll('style[data-stx-page]').forEach(function(s){s.remove()});\n // Merge crosswind CSS from fragment into existing crosswind style\n if(fragCrosswindCSS){\n var curCrosswind=document.querySelector('head style[data-crosswind]');\n if(curCrosswind){\n var merged=mergeCrosswindCSS(curCrosswind.textContent||'',fragCrosswindCSS);\n if(merged)curCrosswind.textContent=merged;\n }else{\n var cw=document.createElement('style');\n cw.setAttribute('data-crosswind','generated');\n cw.textContent=fragCrosswindCSS;\n document.head.appendChild(cw);\n }\n }\n // Add new page styles\n fragStyles.forEach(function(s){\n var el=document.createElement('style');\n el.textContent=s.css;\n el.setAttribute('data-stx-page','');\n document.head.appendChild(el);\n });\n // Swap content\n currentContent.innerHTML=cleanFrag;\n // Remove old page scripts\n document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});\n if(pushState!==false)history.pushState({},'',url+(hash||''));\n updateNav(url);\n updateActiveLinks();\n if(o.scrollToTop&&!hash)window.scrollTo({top:0,behavior:'instant'});\n else if(hash){var el=document.querySelector(hash);if(el)el.scrollIntoView({behavior:'smooth'})}\n window.dispatchEvent(new CustomEvent('stx:navigate',{detail:{url:url}}));\n // Execute page scripts FIRST — they define setup functions and set _latestSetup\n log('[router] frag scripts:', fragScripts.length);\n document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});\n fragScripts.forEach(function(code){\n log('[router] exec script len:', code.length, 'has __stx_setup:', code.indexOf('__stx_setup')>-1);\n // Skip scripts that were already executed (layout-level partials\n // like theme.stx, stores.stx, nav.stx). Their top-level const/let\n // declarations would throw \"Identifier has already been declared\"\n // on re-execution. Setup functions (__stx_setup_) must always\n // re-execute because each page has its own setup.\n var h=hashScript(code);\n var isSetup=code.indexOf('__stx_setup_')!==-1;\n // Compute isAlreadyScoped first so the dedup check can exempt\n // self-contained IIFE scripts. data-stx-scoped scope IIFEs\n // (the (function(){ ... })() shape) must re-execute on every\n // navigation. They own their own scope and won't collide\n // with prior runs, AND _cleanupContainer deleted their\n // entry from window.stx._scopes on the way out. Without\n // this exemption, navigating away and back leaves the page\n // leaf with no scope registered: bindIf/@event never re-bind\n // and all :if branches stay visible at once.\n // Mirrors the full-doc swap path's runAlways escape below.\n var isAlreadyScoped=code.trimStart().charAt(0)==='('||code.trimStart().charAt(0)===';'||code.indexOf('window.stx.mount')>-1;\n if(!isSetup&&!isAlreadyScoped&&executedScriptHashes[h]){\n log('[router] skipping already-executed script (hash dedup)');\n return;\n }\n executedScriptHashes[h]=1;\n var ns=document.createElement('script');\n var hasImport=code.indexOf('import ')!==-1;\n if(hasImport){\n ns.type='module';\n }\n ns.textContent=(hasImport||isAlreadyScoped)?code:'{'+code+'}';\n ns.setAttribute('data-stx-page','');\n document.body.appendChild(ns);\n });\n log('[router] scripts done. _latestSetup:', !!window.stx._latestSetup);\n // THEN fire stx:load — now _latestSetup is set and processElement has the right scope\n window.dispatchEvent(new Event('stx:load'));\n }\n if(runViewTransition(doFragSwap)){}\n else{currentContent.style.transition='opacity 0.12s ease-out';currentContent.style.opacity='0';setTimeout(function(){doFragSwap();currentContent.style.opacity='1';setTimeout(function(){currentContent.style.transition=''},150)},120)}\n return;\n }\n\n // Full document mode: parse with DOMParser and extract container content\n var parser=new DOMParser();\n var doc=parser.parseFromString(html,'text/html');\n var newContent=doc.querySelector(containerSel)||doc.querySelector('[data-stx-content]')||doc.querySelector('main');\n if(!newContent){location.href=url;return}\n\n // Clean up existing signals/effects\n if(window.stx&&window.stx._cleanupContainer){\n window.stx._cleanupContainer(currentContent);\n }\n\n function doSwap(){\n // ── Swap <head> styles ──\n // Inject new styles FIRST, then remove old to prevent unstyled flash\n var keepIds={'stx-view-transitions':1,'stx-r-css':1};\n var curStyles=document.querySelectorAll('head style');\n var newStyles=doc.querySelectorAll('head style');\n\n // Merge crosswind styles instead of replacing — persistent elements\n // (nav, footer) outside <main> still need their utility classes\n var curCrosswind=document.querySelector('head style[data-crosswind]');\n var newCrosswind=null;\n newStyles.forEach(function(s){if(s.getAttribute('data-crosswind'))newCrosswind=s});\n\n if(curCrosswind&&newCrosswind){\n var merged=mergeCrosswindCSS(curCrosswind.textContent||'',newCrosswind.textContent||'');\n if(merged)curCrosswind.textContent=merged;\n }\n\n var incoming=[];\n newStyles.forEach(function(s){\n if(!keepIds[s.id]&&!s.getAttribute('data-crosswind')){\n var ns=document.createElement('style');\n ns.textContent=s.textContent;\n ns.setAttribute('data-stx-incoming','');\n document.head.appendChild(ns);\n incoming.push(ns);\n }\n });\n\n // If no existing crosswind but new page has one, add it\n if(!curCrosswind&&newCrosswind){\n var ns=document.createElement('style');\n ns.textContent=newCrosswind.textContent;\n ns.setAttribute('data-crosswind',newCrosswind.getAttribute('data-crosswind'));\n document.head.appendChild(ns);\n }\n\n // Remove old styles (except persistent ones, crosswind, and incoming)\n curStyles.forEach(function(s){\n if(!keepIds[s.id]&&!s.hasAttribute('data-stx-incoming')&&!s.hasAttribute('data-crosswind'))s.remove();\n });\n\n incoming.forEach(function(s){s.removeAttribute('data-stx-incoming')});\n\n // ── Swap content ──\n // For layout changes: swap the entire <body> to replace layout chrome (nav, footer, etc.)\n // For same-layout: swap only the container (<main>)\n var newBody=doc.querySelector('body');\n var isLayoutChange=false;\n if(newBody){\n var newMeta=doc.querySelector('meta[name=\"stx-layout\"]');\n var curMeta=document.querySelector('meta[name=\"stx-layout\"]');\n var curLayout=curMeta?curMeta.getAttribute('content'):'';\n var newLayout=newMeta?newMeta.getAttribute('content'):'';\n var curGroup=getCurrentLayoutGroup();\n var newGroup=getDocLayoutGroup(doc,newLayout);\n isLayoutChange=curGroup!==newGroup||(curLayout&&newLayout&&curLayout!==newLayout);\n }\n if(isLayoutChange&&newBody){\n log('[router] full body swap for layout change');\n // Replace entire body content — layout chrome and all\n var bodyHTML=newBody.innerHTML.replace(new RegExp('<scr'+'ipt\\\\\\\\b[^>]*>[\\\\\\\\s\\\\\\\\S]*?<\\\\\\\\/scr'+'ipt\\\\\\\\s*>','gi'),'');\n document.body.innerHTML=bodyHTML;\n // Copy body attributes (class, data-stx, etc.)\n Array.from(newBody.attributes).forEach(function(attr){document.body.setAttribute(attr.name,attr.value)});\n // Update layout meta tag\n var oldMeta=document.querySelector('meta[name=\"stx-layout\"]');\n var freshMeta=doc.querySelector('meta[name=\"stx-layout\"]');\n if(oldMeta&&freshMeta)oldMeta.setAttribute('content',freshMeta.getAttribute('content')||'');\n else if(freshMeta){var m=document.createElement('meta');m.name='stx-layout';m.content=freshMeta.getAttribute('content')||'';document.head.appendChild(m)}\n var oldGroupMeta=document.querySelector('meta[name=\"stx-layout-group\"]');\n var freshGroupMeta=doc.querySelector('meta[name=\"stx-layout-group\"]');\n if(oldGroupMeta&&freshGroupMeta)oldGroupMeta.setAttribute('content',freshGroupMeta.getAttribute('content')||'');\n else if(freshGroupMeta){var gm=document.createElement('meta');gm.name='stx-layout-group';gm.content=freshGroupMeta.getAttribute('content')||'';document.head.appendChild(gm)}\n // Update container reference for script execution below\n currentContent=document.querySelector(containerSel)||document.querySelector('main')||document.body;\n } else {\n // Same layout — swap only container content\n var cleanHTML=newContent.innerHTML.replace(new RegExp('<scr'+'ipt\\\\\\\\b[^>]*>[\\\\\\\\s\\\\\\\\S]*?<\\\\\\\\/scr'+'ipt\\\\\\\\s*>','gi'),'');\n currentContent.innerHTML=cleanHTML;\n }\n\n // ── Load new external <head> scripts ──\n var loadedSrcs={};\n document.querySelectorAll('head script[src]').forEach(function(s){loadedSrcs[s.src]=1});\n var extPromises=[];\n doc.querySelectorAll('head script[src]').forEach(function(s){\n var src=new URL(s.getAttribute('src'),location.origin).href;\n if(loadedSrcs[src])return;\n loadedSrcs[src]=1;\n extPromises.push(new Promise(function(resolve,reject){\n var ns=document.createElement('script');\n ns.src=src;\n ns.onload=resolve;\n ns.onerror=reject;\n document.head.appendChild(ns);\n }));\n });\n\n // ── Script re-execution ──\n // Remove previously injected page scripts\n document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});\n\n var scripts=[];\n function addScript(text, runAlways){\n scripts.push({text:text,runAlways:!!runAlways});\n }\n if(isLayoutChange){\n // Layout change: collect ALL scripts from the new document body\n // (layout scripts, component mounts, setup functions — everything)\n var newBodyEl=doc.querySelector('body');\n if(newBodyEl){\n newBodyEl.querySelectorAll('script').forEach(function(s){\n var text=s.textContent||'';\n if(s.hasAttribute('src'))return;\n if(!text.trim())return;\n // Skip the signals runtime IIFE — it's already loaded\n if(text.indexOf(\"'use strict';var cloakStyle\")!==-1)return;\n if(text.indexOf('__stxRouter')!==-1)return;\n addScript(text,true);\n });\n }\n // Also collect setup functions from <head>\n doc.querySelectorAll('head script').forEach(function(s){\n var text=s.textContent||'';\n if(s.hasAttribute('src'))return;\n if(!text.trim())return;\n if(text.indexOf('__stx_setup_')!==-1)addScript(text,true);\n });\n } else {\n // Same layout: collect scripts from the container\n newContent.querySelectorAll('script').forEach(function(s){\n var text=s.textContent||'';\n if(s.hasAttribute('src'))return;\n if(!text.trim())return;\n addScript(text,true);\n });\n // Collect page scripts from <head> AND <body> (outside container).\n // SSG builds place the setup function in <body> before <main>, not\n // in <head>. Without this, SPA navigation on static sites never\n // runs the setup and reactive data stays empty.\n var seenSetups={};\n scripts.forEach(function(entry){var t=entry.text;if(t.indexOf('__stx_setup_')!==-1)seenSetups[t.substring(0,80)]=1});\n doc.querySelectorAll('script').forEach(function(s){\n var text=s.textContent||'';\n if(s.hasAttribute('src'))return;\n if(!text.trim())return;\n // Skip signals runtime and router (they contain __stx_setup references but aren't setup functions)\n if(text.indexOf(\"'use strict';var cloakStyle\")!==-1)return;\n if(text.indexOf('__stxRouter')!==-1)return;\n if(newContent.contains(s))return;\n if(text.indexOf('__stx_setup_')===-1&&!s.hasAttribute('data-stx-scoped')&&!s.hasAttribute('data-stx-page'))return;\n var key=text.substring(0,80);\n if(seenSetups[key])return;\n seenSetups[key]=1;\n addScript(text,true);\n });\n }\n\n // Push history state (before active link updates so location.pathname is current)\n if(pushState!==false)history.pushState({},'',url+(hash||''));\n\n // Update active nav links\n updateNav(url);\n updateActiveLinks();\n\n // Scroll\n if(o.scrollToTop&&!hash)window.scrollTo({top:0,behavior:'instant'});\n else if(hash){var el=document.querySelector(hash);if(el)el.scrollIntoView({behavior:'smooth'})}\n\n // Update title\n var newTitle=doc.querySelector('title');\n if(newTitle)document.title=newTitle.textContent;\n\n // Update <html lang> from the destination doc so screen readers,\n // CSS :lang() selectors, and any i18n picker that mirrors\n // document.documentElement.lang stay accurate after SPA hops.\n if(doc.documentElement&&doc.documentElement.lang){\n document.documentElement.lang=doc.documentElement.lang;\n }\n\n window.dispatchEvent(new CustomEvent('stx:navigate',{detail:{url:url}}));\n\n // Execute page scripts FIRST — they define setup functions and set _latestSetup\n function execScripts(){\n scripts.forEach(function(entry){\n var text=typeof entry==='string'?entry:entry.text;\n var runAlways=typeof entry==='string'?false:entry.runAlways;\n // Skip scripts that were already executed (layout-level partials\n // like theme.stx). Their top-level const/function declarations\n // would throw \"Identifier has already been declared\" on re-execution.\n // Exception: setup functions (__stx_setup_) must always re-execute\n // because each page has its own setup, and import statements\n // need special handling.\n var h=hashScript(text);\n var isSetup=text.indexOf('__stx_setup_')!==-1;\n var hasImport=text.indexOf('import ')!==-1;\n if(!runAlways&&!isSetup&&executedScriptHashes[h])return;\n executedScriptHashes[h]=1;\n // Wrap scripts with import statements as modules (Bug 3 fix)\n var ns=document.createElement('script');\n if(hasImport){\n ns.type='module';\n }\n // Wrap in block scope to prevent const/let collisions on re-navigation.\n // Top-level const/let in <script> tags are global-scoped in browsers\n // and throw \"Identifier has already been declared\" on re-execution.\n // Skip the wrap for module scripts — they get their own scope from\n // ESM, and top-level 'import' is illegal inside a block, which\n // would throw SyntaxError before the script ever runs.\n var alreadyScoped=text.trimStart().charAt(0)==='('||text.trimStart().charAt(0)===';'||text.indexOf('window.stx.mount')>-1;\n ns.textContent=(hasImport||alreadyScoped)?text:'{'+text+'}';\n ns.setAttribute('data-stx-page','');\n document.body.appendChild(ns);\n });\n // THEN fire stx:load — now _latestSetup is set and processElement has the right scope\n window.dispatchEvent(new Event('stx:load'));\n }\n\n if(extPromises.length>0){\n Promise.all(extPromises).then(execScripts).catch(execScripts);\n }\nelse {\n execScripts();\n }\n }\n\n if(runViewTransition(doSwap)){\n }\nelse {\n // Fallback fade for browsers without View Transitions API\n currentContent.style.transition='opacity 0.12s ease-out';\n currentContent.style.opacity='0';\n setTimeout(function(){\n doSwap();\n currentContent.style.opacity='1';\n setTimeout(function(){currentContent.style.transition=''},150);\n },120);\n }\n }\n\n // ── Link interception ──\n function shouldIntercept(link){\n if(!link)return false;\n var href=link.getAttribute('href');\n if(!href)return false;\n if(href.startsWith('http')||href.startsWith('#')||href.startsWith('mailto:')||href.startsWith('tel:')||href.startsWith('javascript:'))return false;\n if(link.target==='_blank')return false;\n if(link.hasAttribute('data-stx-no-router')||link.hasAttribute('data-no-router')||link.hasAttribute('download'))return false;\n if(href.startsWith('?'))return true;\n if(href===location.pathname)return false;\n if(!getContainer())return false;\n return true;\n }\n\n // SPA click handling. By default only [data-stx-link] elements get\n // SPA navigation; regular <a href> does native full page reload. Set\n // interceptAllLinks:true (window.__stxRouterConfig or build-time\n // config) to intercept any same-origin anchor — used by static-site\n // mode where every page is part of the same SPA shell.\n document.addEventListener('click',function(e){\n if(e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||e.button!==0)return;\n if(!e.target||!e.target.closest)return;\n var link=e.target.closest('[data-stx-link]');\n if(!link&&o.interceptAllLinks){\n var anchor=e.target.closest('a[href]');\n if(anchor&&shouldIntercept(anchor))link=anchor;\n }\n if(!link){return}\n var href=link.getAttribute('href');\n log('[router] click intercepted:',href,'container:',!!getContainer(),'defaultPrevented:',e.defaultPrevented);\n if(!href||href.startsWith('http')||href.startsWith('#')||href.startsWith('mailto:')||href.startsWith('tel:')){log('[router] skipped:',href);return}\n if(link.target==='_blank'||link.hasAttribute('download'))return;\n e.preventDefault();\n e.stopPropagation();\n log('[router] navigating to:',href);\n navigate(withCurrentLocale(href));\n },true);\n\n // ── Back/forward ──\n window.addEventListener('popstate',function(){\n navigate(location.pathname+location.search+location.hash,false);\n });\n\n // ── Prefetch on hover ──\n if(o.prefetch){\n document.addEventListener('mouseover',function(e){\n if(!e.target||!e.target.closest)return;\n var link=e.target.closest('[data-stx-link]');\n if(!link)return;\n var href=withCurrentLocale(link.getAttribute('href'));\n var key=cacheKey(href);\n if(cache[key]||prefetching[key])return;\n prefetching[key]=true;\n var wantsFragment=shouldUseFragmentResponse();\n fetch(href,{headers:wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'}}).then(function(r){\n var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';\n var pLayout=r.headers.get('X-STX-Layout')||'';\n var pGroup=r.headers.get('X-STX-Layout-Group')||'';\n return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}});\n }).then(function(result){\n if(o.cache)setCache(key,result.html,result.layout,result.layoutGroup);\n }).catch(function(){}).finally(function(){delete prefetching[key]});\n },true);\n }\n\n // ── Active link management ──\n function updateNav(url){\n document.querySelectorAll('nav a[href], #mobileNav a[href], [data-stx-nav] a[href]').forEach(function(a){\n var href=a.getAttribute('href');\n if(!href||href.startsWith('#')||href.startsWith('http'))return;\n var isActive=(href===url)||(href==='/'&&url==='/');\n if(a.hasAttribute('data-stx-link')){\n var ac=a.getAttribute('data-stx-active-class')||'active';\n if(isActive)ac.split(' ').forEach(function(cls){if(cls)a.classList.add(cls)});else ac.split(' ').forEach(function(cls){if(cls)a.classList.remove(cls)});\n }\n });\n }\n\n function updateActiveLinks(){\n // Update active classes on <stx-link> elements (and legacy data-stx-link)\n var links=document.querySelectorAll('[data-stx-link]');\n var cur=location.pathname;\n links.forEach(function(link){\n var href=link.getAttribute('to')||link.getAttribute('href')||'';\n var ac=link.getAttribute('active-class')||link.getAttribute('data-stx-active-class')||'active';\n var eac=link.getAttribute('exact-active-class')||link.getAttribute('data-stx-exact-active-class')||'exact-active';\n ac.split(' ').forEach(function(cls){if(cls)link.classList.remove(cls)});\n eac.split(' ').forEach(function(cls){if(cls)link.classList.remove(cls)});\n var isExact=cur===href;\n var isActive=href!=='/'?cur.startsWith(href):cur==='/';\n if(isExact)eac.split(' ').forEach(function(cls){if(cls)link.classList.add(cls)});\n if(isActive)ac.split(' ').forEach(function(cls){if(cls)link.classList.add(cls)});\n });\n }\n\n // ── Progress bar DOM + style ──\n // Injects a fixed-position element at the top of the viewport plus the\n // minimal CSS for its transform/opacity transitions. Kept idempotent so\n // repeated init() calls (e.g. after full-body swaps) don't duplicate.\n // Also ships the default View Transitions fade+slide CSS when\n // viewTransitions is enabled and the browser supports it. Apps can\n // override by defining more specific ::view-transition-* rules later in\n // the cascade, or opt out entirely with viewTransitions:false.\n function injectStyles(){\n if(!document.getElementById('stx-r-css')){\n var s=document.createElement('style');s.id='stx-r-css';\n // Strip characters that could escape our CSS block and inject new\n // declarations: ; { } ( ) \" ' \\\\ < > plus whitespace control chars.\n // This keeps the value safe to concat into a stylesheet even if a\n // caller wires the config from an untrusted source.\n var sanitize=function(v){return String(v).replace(/[;{}()\"'\\\\\\\\<>\\\\n\\\\r\\\\t]/g,'')};\n var pc=sanitize(o.progressColor);\n var ph=sanitize(o.progressHeight);\n var css='.stx-navigating{cursor:wait}.stx-navigating a,.stx-navigating button{pointer-events:none}#stx-router-progress{position:fixed;top:0;left:0;right:0;height:'+ph+';background:'+pc+';box-shadow:0 0 8px '+pc+',0 0 4px '+pc+';transform:scaleX(0);transform-origin:left;transition:transform .18s ease-out,opacity .26s ease;opacity:0;pointer-events:none;z-index:999999}';\n if(o.viewTransitions&&'startViewTransition' in document){\n var dur=(o.viewTransitionDuration||220)+'ms';\n var ease=o.viewTransitionEasing||'cubic-bezier(0.16, 1, 0.3, 1)';\n css+='::view-transition-old(root),::view-transition-new(root){animation-duration:'+dur+';animation-timing-function:'+ease+'}';\n css+='::view-transition-old(root){animation-name:stx-r-fade-out}::view-transition-new(root){animation-name:stx-r-fade-in}';\n css+='@keyframes stx-r-fade-out{from{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-4px)}}';\n css+='@keyframes stx-r-fade-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}';\n css+='@media (prefers-reduced-motion: reduce){::view-transition-old(root),::view-transition-new(root){animation-duration:0s;animation-name:none}}';\n }\n s.textContent=css;\n document.head.appendChild(s);\n }\n if(o.progress&&!document.getElementById('stx-router-progress')){\n var el=document.createElement('div');\n el.id='stx-router-progress';\n el.setAttribute('role','progressbar');\n el.setAttribute('aria-hidden','true');\n // Append to <html>, not <body>: when the router swaps the body\n // (container:'body' for full-page static-site SPAs) the progress\n // element gets wiped along with it. <html> stays put across\n // swaps, so the bar survives multi-hop navigation.\n document.documentElement.appendChild(el);\n progEl=el;\n } else if(o.progress){\n progEl=document.getElementById('stx-router-progress');\n }\n }\n\n function injectViewTransitionCSS(){\n if(document.getElementById('stx-view-transitions'))return;\n var s=document.createElement('style');s.id='stx-view-transitions';\n s.textContent='::view-transition-group(root){animation:none}::view-transition-old(root){animation:none}::view-transition-new(root){animation:none}main,#app-content,[data-stx-content]{view-transition-name:stx-content}::view-transition-old(stx-content){animation:stx-fade-out .15s ease-out both}::view-transition-new(stx-content){animation:stx-fade-in .15s ease-in .1s both}@keyframes stx-fade-out{from{opacity:1}to{opacity:0}}@keyframes stx-fade-in{from{opacity:0}to{opacity:1}}::view-transition{background:transparent}::view-transition-group(stx-content){background:inherit;overflow:hidden}';\n (document.head||document.documentElement).appendChild(s);\n }\n\n // ── Public API ──\n var router={\n navigate:navigate,\n navigateTo:navigate,\n prefetch:function(url){\n var key=cacheKey(url);\n if(!cache[key]){\n var wantsFragment=shouldUseFragmentResponse();\n fetch(url,{headers:wantsFragment?{'X-STX-Router':'true'}:{'Accept':'text/html'}}).then(function(r){var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';var pLayout=r.headers.get('X-STX-Layout')||'';var pGroup=r.headers.get('X-STX-Layout-Group')||'';return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}})}).then(function(result){setCache(key,result.html,result.layout,result.layoutGroup)}).catch(function(){});\n }\n },\n clearCache:function(){for(var k in cache)delete cache[k];for(var lk in layoutCache)delete layoutCache[lk];for(var gk in layoutGroupCache)delete layoutGroupCache[gk];cacheOrder.length=0},\n cache:cache,\n swap:swap,\n updateNav:updateNav\n };\n\n router.__rev=ROUTER_REV;\n window.__stxRouter=router;\n window.stxRouter=router;\n if(window.stx)window.stx.router=router;\n\n // ── Initialize ──\n function init(){\n injectStyles();\n injectViewTransitionCSS();\n updateActiveLinks();\n updateNav(location.pathname);\n }\n\n if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);else init();\n})();\n`\n}\n",
|
|
6
6
|
"import fs from 'node:fs'\nimport path from 'node:path'\nimport type { Route } from './types'\n\nexport function generateRouteTypes(routes: Route[], outputDir: string): void {\n const lines = [\n '// Auto-generated by STX - do not edit',\n 'declare module \"stx/routes\" {',\n ' interface RouteMap {',\n ]\n\n for (const route of routes) {\n const paramEntries: string[] = []\n for (const p of route.params) {\n const type = route.pattern.includes(`:${p}*`) ? 'string[]' : 'string'\n paramEntries.push(`${p}: ${type}`)\n }\n const paramsStr = paramEntries.join('; ')\n lines.push(` '${route.pattern}': { ${paramsStr} }`)\n }\n\n lines.push(' }', '}', '')\n\n fs.mkdirSync(outputDir, { recursive: true })\n fs.writeFileSync(path.join(outputDir, 'route-types.d.ts'), lines.join('\\n'))\n}\n\n/**\n * Generate a route manifest file (.stx/routes.ts).\n *\n * Produces a TypeScript module exporting the full route array with patterns,\n * file paths, params, and layout references. Similar to Nuxt's .nuxt/routes.mjs.\n */\nexport function generateRouteManifest(routes: Route[], outputDir: string, pagesDir: string): void {\n const cwd = process.cwd()\n const relativePages = path.relative(cwd, pagesDir)\n\n // Filter out non-page files (components, layouts, partials)\n const excludeDirs = ['components', 'layouts', 'partials']\n const pageRoutes = routes.filter(r =>\n !excludeDirs.some(dir => r.pattern.startsWith(`/${dir}/`) || r.pattern === `/${dir}`),\n )\n\n const lines = [\n '// Auto-generated by stx — do not edit',\n `// Source: ${relativePages || 'pages'}/`,\n '',\n 'export const routes = [',\n ]\n\n for (const route of pageRoutes) {\n const relFile = path.relative(cwd, route.filePath).replace(/\\\\/g, '/')\n const params = JSON.stringify(route.params)\n const layoutStr = route.layout\n ? `, layout: '${path.relative(cwd, route.layout).replace(/\\\\/g, '/')}'`\n : ''\n lines.push(` { pattern: '${route.pattern}', filePath: '${relFile}', isDynamic: ${route.isDynamic}, params: ${params}${layoutStr} },`)\n }\n\n lines.push('] as const', '')\n lines.push('export type RouteName = typeof routes[number][\\'pattern\\']', '')\n\n fs.mkdirSync(outputDir, { recursive: true })\n fs.writeFileSync(path.join(outputDir, 'routes.ts'), lines.join('\\n'))\n}\n",
|
|
7
7
|
"import path from 'node:path'\n\nexport function filePathToPattern(filePath: string, pagesDir: string): string {\n const relativePath = path.relative(pagesDir, filePath)\n let route = relativePath.replace(/\\.(stx|md)$/, '')\n\n route = route.split(path.sep).join('/')\n\n if (route === 'index') {\n return '/'\n }\n if (route.endsWith('/index')) {\n route = route.slice(0, -6)\n }\n\n // Convert [[param]] to :param? (optional) — BEFORE required params\n route = route.replace(/\\[\\[([^\\]]+)\\]\\]/g, ':$1?')\n\n // Convert [param] to :param\n route = route.replace(/\\[([^\\]]+)\\]/g, ':$1')\n\n // Convert [...param] to :param* (catch-all)\n route = route.replace(/:\\.\\.\\.([^/]+)/g, ':$1*')\n\n return `/${route}`\n}\n\nexport function patternToRegex(pattern: string): { regex: RegExp, params: string[] } {\n const params: string[] = []\n\n let regexStr = pattern.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n\n // Handle optional params (/:param?)\n regexStr = regexStr.replace(/\\/:([^/]+)\\?/g, (_, name) => {\n params.push(name)\n return '(?:/([^/]+))?'\n })\n\n // Handle catch-all params (:param*)\n regexStr = regexStr.replace(/:([^/]+)\\*/g, (_, name) => {\n params.push(name)\n return '(.+)'\n })\n\n // Handle regular params (:param)\n regexStr = regexStr.replace(/:([^/]+)/g, (_, name) => {\n params.push(name)\n return '([^/]+)'\n })\n\n regexStr = `^${regexStr}$`\n\n return { regex: new RegExp(regexStr), params }\n}\n\nexport function matchRoute(pathname: string, routes: { pattern: string, regex: RegExp, params: string[] }[]): { route: typeof routes[0], params: Record<string, string> } | null {\n const normalizedPath = pathname === '' ? '/' : pathname\n\n for (const route of routes) {\n const match = normalizedPath.match(route.regex)\n if (match) {\n const params: Record<string, string> = {}\n route.params.forEach((name, index) => {\n params[name] = match[index + 1]\n })\n return { route, params }\n }\n }\n\n return null\n}\n",
|
|
8
8
|
"import fs from 'node:fs'\nimport path from 'node:path'\n\nexport function resolveLayoutChain(routeFilePath: string, pagesDir: string): string[] {\n const layouts: string[] = []\n let dir = path.dirname(routeFilePath)\n\n // Walk up from the route's directory to the pages root, collecting _layout.stx files\n while (dir.startsWith(pagesDir) || dir === pagesDir) {\n const layoutFile = path.join(dir, '_layout.stx')\n if (fs.existsSync(layoutFile)) {\n layouts.unshift(layoutFile) // Prepend so root layout is first\n }\n\n if (dir === pagesDir) break\n dir = path.dirname(dir)\n }\n\n return layouts\n}\n",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"import fs from 'node:fs'\nimport path from 'node:path'\nimport type {\n AbortNavigationResult,\n CookieManager,\n CookieOptions,\n MiddlewareContext,\n MiddlewareMode,\n MiddlewareOptions,\n MiddlewareResult,\n NavigateToOptions,\n NavigateToResult,\n NavigationError,\n NavigationResult,\n RouteLocation,\n RouteMiddlewareDefinition,\n RouteMiddlewareHandler,\n StorageManager,\n} from './types'\n\n// =============================================================================\n// Navigation Helpers\n// =============================================================================\n\nexport function navigateTo(navPath: string, options: NavigateToOptions = {}): NavigateToResult {\n return {\n type: 'redirect',\n path: navPath,\n options: {\n replace: options.replace ?? false,\n external: options.external ?? false,\n redirectCode: options.redirectCode ?? 302,\n },\n }\n}\n\nexport function abortNavigation(error: string | NavigationError): AbortNavigationResult {\n const normalizedError: NavigationError\n = typeof error === 'string' ? { statusCode: 500, message: error } : error\n\n return {\n type: 'abort',\n error: normalizedError,\n }\n}\n\n// =============================================================================\n// Middleware Definition\n// =============================================================================\n\nexport function defineMiddleware(\n handler: RouteMiddlewareHandler,\n options: MiddlewareOptions = {},\n): RouteMiddlewareDefinition {\n return {\n handler,\n mode: options.mode ?? 'universal',\n }\n}\n\n// =============================================================================\n// Cookie Manager\n// =============================================================================\n\nfunction parseCookies(cookieHeader: string): Record<string, string> {\n const cookies: Record<string, string> = {}\n if (!cookieHeader) return cookies\n\n const pairs = cookieHeader.split(';')\n for (const pair of pairs) {\n const [name, ...rest] = pair.split('=')\n if (name) {\n const key = name.trim()\n const value = rest.join('=').trim()\n if (key) {\n cookies[key] = decodeURIComponent(value)\n }\n }\n }\n return cookies\n}\n\nfunction serializeCookie(name: string, value: string, options: CookieOptions = {}): string {\n let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`\n\n if (options.expires) {\n const expires\n = typeof options.expires === 'number'\n ? new Date(Date.now() + options.expires * 1000)\n : options.expires\n cookie += `; Expires=${expires.toUTCString()}`\n }\n\n if (options.path) cookie += `; Path=${options.path}`\n if (options.domain) cookie += `; Domain=${options.domain}`\n if (options.secure) cookie += '; Secure'\n if (options.httpOnly) cookie += '; HttpOnly'\n if (options.sameSite) cookie += `; SameSite=${options.sameSite}`\n\n return cookie\n}\n\nexport function createServerCookieManager(\n request: Request,\n responseHeaders: Headers,\n): CookieManager {\n const cookieHeader = request.headers.get('cookie') || ''\n const cookies = parseCookies(cookieHeader)\n\n return {\n get(name: string): string | undefined {\n return cookies[name]\n },\n set(name: string, value: string, options: CookieOptions = {}): void {\n cookies[name] = value\n responseHeaders.append('Set-Cookie', serializeCookie(name, value, options))\n },\n delete(name: string): void {\n delete cookies[name]\n responseHeaders.append(\n 'Set-Cookie',\n serializeCookie(name, '', { expires: new Date(0), path: '/' }),\n )\n },\n getAll(): Record<string, string> {\n return { ...cookies }\n },\n }\n}\n\nexport function createClientCookieManager(): CookieManager {\n return {\n get(name: string): string | undefined {\n if (typeof document === 'undefined') return undefined\n const cookies = parseCookies(document.cookie)\n return cookies[name]\n },\n set(name: string, value: string, options: CookieOptions = {}): void {\n if (typeof document === 'undefined') return\n document.cookie = serializeCookie(name, value, options)\n },\n delete(name: string): void {\n if (typeof document === 'undefined') return\n document.cookie = serializeCookie(name, '', { expires: new Date(0), path: '/' })\n },\n getAll(): Record<string, string> {\n if (typeof document === 'undefined') return {}\n return parseCookies(document.cookie)\n },\n }\n}\n\n// =============================================================================\n// Storage Manager\n// =============================================================================\n\nexport function createStorageManager(): StorageManager {\n const available\n = typeof window !== 'undefined' && typeof window.localStorage !== 'undefined'\n\n return {\n get(key: string): string | null {\n if (!available) return null\n try { return window.localStorage.getItem(key) }\n catch { return null }\n },\n set(key: string, value: string): void {\n if (!available) return\n try { window.localStorage.setItem(key, value) }\n catch { /* storage full or blocked */ }\n },\n remove(key: string): void {\n if (!available) return\n try { window.localStorage.removeItem(key) }\n catch { /* ignore */ }\n },\n isAvailable(): boolean {\n return available\n },\n }\n}\n\n// =============================================================================\n// Middleware Registry\n// =============================================================================\n\nconst middlewareRegistry = new Map<string, RouteMiddlewareDefinition>()\n\nexport function registerMiddleware(name: string, middleware: RouteMiddlewareDefinition): void {\n middlewareRegistry.set(name, middleware)\n}\n\nexport function getMiddleware(name: string): RouteMiddlewareDefinition | undefined {\n return middlewareRegistry.get(name)\n}\n\nexport function hasMiddleware(name: string): boolean {\n return middlewareRegistry.has(name)\n}\n\nexport function clearMiddleware(): void {\n middlewareRegistry.clear()\n}\n\nexport function getMiddlewareNames(): string[] {\n return Array.from(middlewareRegistry.keys())\n}\n\n// =============================================================================\n// Middleware Loading\n// =============================================================================\n\nexport async function loadMiddlewareFromDirectory(\n baseDir: string,\n middlewareDir: string = 'middleware',\n): Promise<void> {\n const fullPath = path.resolve(baseDir, middlewareDir)\n\n if (!fs.existsSync(fullPath)) {\n return\n }\n\n const files = fs.readdirSync(fullPath)\n\n for (const file of files) {\n if (!file.endsWith('.ts') && !file.endsWith('.js')) continue\n if (file.includes('.test.') || file.includes('.spec.')) continue\n\n const name = file.replace(/\\.(ts|js)$/, '')\n const filePath = path.join(fullPath, file)\n\n try {\n const mod = await import(filePath)\n const mw = mod.default\n\n if (mw && typeof mw.handler === 'function') {\n registerMiddleware(name, mw)\n }\n else if (typeof mw === 'function') {\n registerMiddleware(name, { handler: mw, mode: 'universal' })\n }\n }\n catch (error) {\n console.warn(`Failed to load middleware '${name}' from ${filePath}:`, error)\n }\n }\n}\n\n// =============================================================================\n// Middleware Context & Execution\n// =============================================================================\n\nfunction parseQuery(search: string): Record<string, string> {\n const query: Record<string, string> = {}\n if (!search) return query\n\n const params = new URLSearchParams(search)\n for (const [key, value] of params) {\n query[key] = value\n }\n return query\n}\n\nexport function createRouteLocation(\n pathname: string,\n params: Record<string, string>,\n meta: Record<string, unknown>,\n search: string = '',\n): RouteLocation {\n return {\n path: pathname,\n params,\n query: parseQuery(search),\n fullPath: search ? `${pathname}?${search}` : pathname,\n meta,\n }\n}\n\nexport function createMiddlewareContext(\n to: RouteLocation,\n from: RouteLocation | null,\n request?: Request,\n): MiddlewareContext {\n const isServer = typeof window === 'undefined'\n const isClient = !isServer\n const responseHeaders = new Headers()\n\n return {\n to,\n from,\n isClient,\n isServer,\n cookies: isServer && request\n ? createServerCookieManager(request, responseHeaders)\n : createClientCookieManager(),\n storage: createStorageManager(),\n state: {},\n request,\n responseHeaders,\n }\n}\n\nexport async function runMiddleware(\n middlewareNames: string | string[],\n context: MiddlewareContext,\n): Promise<MiddlewareResult> {\n const names = Array.isArray(middlewareNames) ? middlewareNames : [middlewareNames]\n const isServer = context.isServer\n\n for (const name of names) {\n const middleware = getMiddleware(name)\n\n if (!middleware) {\n console.warn(`Middleware '${name}' not found`)\n continue\n }\n\n if (middleware.mode === 'server' && !isServer) continue\n if (middleware.mode === 'client' && isServer) continue\n\n try {\n const result = await middleware.handler(context)\n\n if (result) {\n if (result.type === 'redirect') {\n return {\n passed: false,\n redirect: result,\n state: context.state,\n responseHeaders: context.responseHeaders,\n }\n }\n\n if (result.type === 'abort') {\n return {\n passed: false,\n abort: result,\n state: context.state,\n responseHeaders: context.responseHeaders,\n }\n }\n }\n }\n catch (error) {\n console.error(`Error in middleware '${name}':`, error)\n return {\n passed: false,\n abort: {\n type: 'abort',\n error: {\n statusCode: 500,\n message: error instanceof Error ? error.message : 'Middleware error',\n },\n },\n state: context.state,\n responseHeaders: context.responseHeaders,\n }\n }\n }\n\n return {\n passed: true,\n state: context.state,\n responseHeaders: context.responseHeaders,\n }\n}\n",
|
|
12
12
|
"export interface LayoutMetadata {\n layout: string\n group: string\n}\n\nexport function deriveLayoutGroup(layoutPath: string): string {\n const normalized = layoutPath.replace(/\\\\/g, '/')\n const segments = normalized.split('/').filter(Boolean)\n const layoutsIndex = segments.lastIndexOf('layouts')\n const layoutSegment = layoutsIndex >= 0 ? segments[layoutsIndex + 1] : segments.at(-1)\n\n if (!layoutSegment)\n return 'app'\n\n return layoutSegment.replace(/\\.stx$/i, '') || 'app'\n}\n\nexport function extractLayoutMetadata(html: string): LayoutMetadata {\n const layoutMeta = html.match(/<meta\\b[^>]*name=[\"']stx-layout[\"'][^>]*content=[\"']([^\"']+)[\"'][^>]*>/i)\n const groupMeta = html.match(/<meta\\b[^>]*name=[\"']stx-layout-group[\"'][^>]*content=[\"']([^\"']+)[\"'][^>]*>/i)\n const layoutComment = html.match(/<!--\\s*stx-layout:\\s*([^ ]+)\\s*-->/i)\n const layout = layoutMeta?.[1] || layoutComment?.[1] || ''\n\n return {\n layout,\n group: groupMeta?.[1] || deriveLayoutGroup(layout),\n }\n}\n"
|
|
13
13
|
],
|
|
14
|
-
"mappings": ";;AAmBO,SAAS,eAAe,GAAW;AAAA,EACxC,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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",
|
|
15
|
-
"debugId": "
|
|
14
|
+
"mappings": ";;AAmBO,SAAS,eAAe,GAAW;AAAA,EACxC,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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",
|
|
15
|
+
"debugId": "6F4C4DEF36A29C6664756E2164756E21",
|
|
16
16
|
"names": []
|
|
17
17
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stx-router",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.69",
|
|
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",
|