@symbo.ls/router 3.14.595 → 3.14.600

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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @symbo.ls/router
2
2
 
3
+ ## 3.14.599
4
+
5
+ ### Patch Changes
6
+
7
+ - Manual patch bump triggered via workflow_dispatch (scope: all).
8
+ No source change behind this bump — released to refresh dist or
9
+ coordinate a cross-package version line.
10
+ - Updated dependencies
11
+ - @symbo.ls/element@3.14.599
12
+ - @symbo.ls/utils@3.14.599
13
+
3
14
  ## 3.14.595
4
15
 
5
16
  ### Patch Changes
package/dist/cjs/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var R=Object.defineProperty;var z=Object.getOwnPropertyDescriptor;var G=Object.getOwnPropertyNames;var Q=Object.prototype.hasOwnProperty;var D=(e,o)=>{for(var r in o)R(e,r,{get:o[r],enumerable:!0})},H=(e,o,r,s)=>{if(o&&typeof o=="object"||typeof o=="function")for(let t of G(o))!Q.call(e,t)&&t!==r&&R(e,t,{get:()=>o[t],enumerable:!(s=z(o,t))||s.enumerable});return e};var J=e=>H(R({},"__esModule",{value:!0}),e);var et={};D(et,{default:()=>tt,getActiveRoute:()=>q,lastLevel:()=>C,lastPathname:()=>w,matchRoute:()=>k,parseQuery:()=>B,parseRoutePattern:()=>A,router:()=>K,runGuards:()=>F});module.exports=J(et);var i=require("@symbo.ls/utils");const S=/^:(.+)/,j=/^\*$/,M=new Map,A=e=>{const o=M.get(e);if(o)return o;const r=e.replace(/^\//,"").split("/"),s=[];let t=!1;for(let l=0;l<r.length;l++){const n=r[l].match(S);n?s.push({index:l,name:n[1]}):j.test(r[l])&&(t=!0)}const a={segments:r,params:s,hasWildcard:t,pattern:e};return M.set(e,a),a},k=(e,o,r=0)=>{const t=e.replace(/^\//,"").split("/").filter(Boolean).slice(r),a="/"+(t[0]||"");if(!o)return{key:null,content:null,params:{},routePath:a};let l=null,n=-1,g={};for(const u in o){if(u==="/*")continue;const x=A(u),E=V(t,x);E>n&&(n=E,l=u,g=X(t,x))}return!l&&o["/*"]&&(l="/*"),{key:l,content:l?o[l]:null,params:g,routePath:a}},V=(e,o)=>{const{segments:r,hasWildcard:s}=o;if(!s&&r.length!==e.length&&r.length!==1&&r.length>e.length)return-1;let t=0;const a=Math.min(r.length,e.length);for(let l=0;l<a;l++)if(r[l]===e[l])t+=3;else if(S.test(r[l]))t+=1;else if(j.test(r[l]))t+=.5;else return-1;return t},X=(e,o)=>{const r={};for(const{index:s,name:t}of o.params)e[s]&&(r[t]=decodeURIComponent(e[s]));return r},B=e=>{if(!e||e==="?")return{};const o={};return new URLSearchParams(e).forEach((s,t)=>{o[t]!==void 0?(Array.isArray(o[t])||(o[t]=[o[t]]),o[t].push(s)):o[t]=s}),o},F=async(e,o)=>{if(!e||!e.length)return!0;for(const r of e){const s=await r(o);if(s===!1)return!1;if(typeof s=="string")return s}return!0},q=(e=0,o)=>{o||(o=typeof i.window<"u"?i.window.location.pathname:"/");const s=o.split("/")[e+1];if(s)return`/${s}`};let w,C=0;const Y=e=>!e||e==="srcdoc"||e==="about:srcdoc"?"/":e,Z={level:C,pushState:!0,initialRender:!1,scrollToTop:!0,scrollToNode:!1,get scrollNode(){return typeof i.document<"u"&&(i.document.scrollingElement||i.document.documentElement)||null},scrollBody:!1,useFragment:!1,updateState:!0,scrollToOffset:0,contentElementKey:"content",scrollToOptions:{behavior:"smooth"},useParamsMatching:!0},K=async(e,o,r={},s={})=>{const t=o||void 0,a=t?.context?.window||i.window,l=t?.context?.document||i.document,n={...Z,...t.context.routerOptions,...s};C=n.lastLevel;const g=t.__ref;(n.contentElementKey!=="content"&&n.contentElementKey!==g.contentElementKey||!g.contentElementKey)&&(g.contentElementKey=n.contentElementKey||"content");const u=g.contentElementKey||n.contentElementKey||"content",x=a.location.origin!=="null"?a.location.origin:"http://localhost";e=Y(e);const E=typeof a.URL=="function"?a.URL:typeof globalThis<"u"&&globalThis.URL||URL,I=new E(x+e),{pathname:f,search:N,hash:y}=I,P=B(N),U=t.node,W=y&&y!==a.location.hash.slice(1),$=f!==w;w=f;let T,O,m,b;if(n.useParamsMatching){const c=k(f,t.routes,n.level);T=c.key,O=c.routePath,m=c.content,b=c.params}else{T=q(n.level,f),O=T;const c=t.routes;m=c?c[T||"/"]||c["/*"]:null,b={}}const v=n.scrollToNode?U:n.scrollNode;if(t.state?.root?.debugging){t.state.root.debugging=!1;return}if(!m){n.onNotFound?n.onNotFound({pathname:f,route:T,element:t}):n.silent||console.warn("[smbls/router] no content matched for path",f,"\u2014 available routes:",Object.keys(t.routes||{}));return}if(n.guards&&n.guards.length){const c={pathname:f,route:T,params:b,query:P,hash:y,element:t,state:r},d=await F(n.guards,c);if(d===!1)return;if(typeof d=="string")return K(d,o,r,{...s,guards:[]})}if(n.pushState)try{a.history.pushState(r,null,f+(N||"")+(y||""))}catch{}if($||!W){const c={route:T,routePath:O,hash:y,debugging:!1};Object.keys(b).length&&(c.params=b),Object.keys(P).length&&(c.query=P),n.updateState&&t.state.update(c,{preventContentUpdate:!0}),u&&n.removeOldElement&&t[u].remove();const d=t.__ref?.origin?.content,L={};if(d)for(const p in d){const h=d[p];p==="__ref"||p==="props"||p==="node"||p==="parent"||p==="key"||(typeof h=="string"||typeof h=="number"||typeof h=="boolean"||typeof h=="object"&&h!==null&&!h.node&&!h.__ref)&&(L[p]=h)}const _={...L,...typeof m=="object"?m:{extends:m}};n.useFragment&&(_.tag="fragment");try{if(typeof t?.set!="function")return;t.set(_,{contentElementKey:u})}catch(p){console.error("[smbls/router] failed to render route content",f,p)}}if(n.scrollToTop&&v?.scrollTo&&v.scrollTo({...n.scrollToOptions||{},top:0,left:0}),n.scrollToNode&&m[u]?.node?.scrollTo&&m[u].node.scrollTo({...n.scrollToOptions||{},top:0,left:0}),y){const c=l.getElementById(y);if(c&&v?.scrollTo){const d=c.getBoundingClientRect().top+U.scrollTop-(n.scrollToOffset||0);v.scrollTo({...n.scrollToOptions||{},top:d,left:0})}}(0,i.triggerEventOn)("routeChanged",t,n)};var tt=K;
1
+ "use strict";var O=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var Q=Object.getOwnPropertyNames;var D=Object.prototype.hasOwnProperty;var H=(e,o)=>{for(var r in o)O(e,r,{get:o[r],enumerable:!0})},J=(e,o,r,s)=>{if(o&&typeof o=="object"||typeof o=="function")for(let t of Q(o))!D.call(e,t)&&t!==r&&O(e,t,{get:()=>o[t],enumerable:!(s=G(o,t))||s.enumerable});return e};var V=e=>J(O({},"__esModule",{value:!0}),e);var ot={};H(ot,{default:()=>et,getActiveRoute:()=>I,lastLevel:()=>_,lastPathname:()=>C,matchRoute:()=>B,parseQuery:()=>F,parseRoutePattern:()=>k,router:()=>K,runGuards:()=>q});module.exports=V(ot);var i=require("@symbo.ls/utils");const j=/^:(.+)/,M=/^\*$/,A=new Map,k=e=>{const o=A.get(e);if(o)return o;const r=e.replace(/^\//,"").split("/"),s=[];let t=!1;for(let l=0;l<r.length;l++){const n=r[l].match(j);n?s.push({index:l,name:n[1]}):M.test(r[l])&&(t=!0)}const a={segments:r,params:s,hasWildcard:t,pattern:e};return A.set(e,a),a},B=(e,o,r=0)=>{const t=e.replace(/^\//,"").split("/").filter(Boolean).slice(r),a="/"+(t[0]||"");if(!o)return{key:null,content:null,params:{},routePath:a};let l=null,n=-1,f={};for(const d in o){if(d==="/*")continue;const x=k(d),R=X(t,x);R>n&&(n=R,l=d,f=Y(t,x))}return!l&&o["/*"]&&(l="/*"),{key:l,content:l?o[l]:null,params:f,routePath:a}},X=(e,o)=>{const{segments:r,hasWildcard:s}=o;if(!s&&r.length!==e.length&&r.length!==1&&r.length>e.length)return-1;let t=0;const a=Math.min(r.length,e.length);for(let l=0;l<a;l++)if(r[l]===e[l])t+=3;else if(j.test(r[l]))t+=1;else if(M.test(r[l]))t+=.5;else return-1;return t},Y=(e,o)=>{const r={};for(const{index:s,name:t}of o.params)e[s]&&(r[t]=decodeURIComponent(e[s]));return r},F=e=>{if(!e||e==="?")return{};const o={};return new URLSearchParams(e).forEach((s,t)=>{o[t]!==void 0?(Array.isArray(o[t])||(o[t]=[o[t]]),o[t].push(s)):o[t]=s}),o},q=async(e,o)=>{if(!e||!e.length)return!0;for(const r of e){const s=await r(o);if(s===!1)return!1;if(typeof s=="string")return s}return!0},I=(e=0,o)=>{o||(o=typeof i.window<"u"?i.window.location.pathname:"/");const s=o.split("/")[e+1];if(s)return`/${s}`};let C,_=0;const Z=e=>!e||e==="srcdoc"||e==="about:srcdoc"?"/":e,tt={level:_,pushState:!0,initialRender:!1,scrollToTop:!0,scrollToNode:!1,get scrollNode(){return typeof i.document<"u"&&(i.document.scrollingElement||i.document.documentElement)||null},scrollBody:!1,useFragment:!1,updateState:!0,scrollToOffset:0,contentElementKey:"content",scrollToOptions:{behavior:"smooth"},useParamsMatching:!0},K=async(e,o,r={},s={})=>{const t=o||void 0,a=t?.context?.window||i.window,l=t?.context?.document||i.document,n={...tt,...t.context.routerOptions,...s};_=n.lastLevel;const f=t.__ref;(n.contentElementKey!=="content"&&n.contentElementKey!==f.contentElementKey||!f.contentElementKey)&&(f.contentElementKey=n.contentElementKey||"content");const d=f.contentElementKey||n.contentElementKey||"content",x=a.location.origin!=="null"?a.location.origin:"http://localhost";e=Z(e);const R=typeof a.URL=="function"?a.URL:typeof globalThis<"u"&&globalThis.URL||URL,W=new R(x+e),{pathname:p,search:N,hash:T}=W,v=F(N),S=t.node,$=T&&T!==a.location.hash.slice(1),U=p!==C;C=p;const P=f.__routerRendered;let h,w,u,b;if(n.useParamsMatching){const c=B(p,t.routes,n.level);h=c.key,w=c.routePath,u=c.content,b=c.params}else{h=I(n.level,p),w=h;const c=t.routes;u=c?c[h||"/"]||c["/*"]:null,b={}}const E=n.scrollToNode?S:n.scrollNode;if(t.state?.root?.debugging){t.state.root.debugging=!1;return}if(!u){n.onNotFound?n.onNotFound({pathname:p,route:h,element:t}):n.silent||console.warn("[smbls/router] no content matched for path",p,"\u2014 available routes:",Object.keys(t.routes||{}));return}if(n.guards&&n.guards.length){const c={pathname:p,route:h,params:b,query:v,hash:T,element:t,state:r},g=await q(n.guards,c);if(g===!1)return;if(typeof g=="string")return K(g,o,r,{...s,guards:[]})}if(n.pushState)try{a.history.pushState(r,null,p+(N||"")+(T||""))}catch{}if(U||!$){const c={route:h,routePath:w,hash:T,debugging:!1};Object.keys(b).length&&(c.params=b),Object.keys(v).length&&(c.query=v),n.updateState&&t.state.update(c,{preventContentUpdate:!0})}const z=!P||P.content!==u||P.route!==h;if(U||z||n.force===!0){d&&n.removeOldElement&&t[d].remove();const c=t.__ref?.origin?.content,g={};if(c)for(const m in c){const y=c[m];m==="__ref"||m==="props"||m==="node"||m==="parent"||m==="key"||(typeof y=="string"||typeof y=="number"||typeof y=="boolean"||typeof y=="object"&&y!==null&&!y.node&&!y.__ref)&&(g[m]=y)}const L={...g,...typeof u=="object"?u:{extends:u}};n.useFragment&&(L.tag="fragment");try{if(typeof t?.set!="function")return;t.set(L,{contentElementKey:d}),f.__routerRendered={content:u,route:h}}catch(m){console.error("[smbls/router] failed to render route content",p,m)}}if(n.scrollToTop&&E?.scrollTo&&E.scrollTo({...n.scrollToOptions||{},top:0,left:0}),n.scrollToNode&&u[d]?.node?.scrollTo&&u[d].node.scrollTo({...n.scrollToOptions||{},top:0,left:0}),T){const c=l.getElementById(T);if(c&&E?.scrollTo){const g=c.getBoundingClientRect().top+S.scrollTop-(n.scrollToOffset||0);E.scrollTo({...n.scrollToOptions||{},top:g,left:0})}}(0,i.triggerEventOn)("routeChanged",t,n)};var et=K;
package/dist/esm/index.js CHANGED
@@ -1 +1 @@
1
- import{document as v,window as R,triggerEventOn as F}from"@symbo.ls/utils";const U=/^:(.+)/,L=/^\*$/,_=new Map,q=e=>{const n=_.get(e);if(n)return n;const r=e.replace(/^\//,"").split("/"),s=[];let t=!1;for(let l=0;l<r.length;l++){const o=r[l].match(U);o?s.push({index:l,name:o[1]}):L.test(r[l])&&(t=!0)}const a={segments:r,params:s,hasWildcard:t,pattern:e};return _.set(e,a),a},I=(e,n,r=0)=>{const t=e.replace(/^\//,"").split("/").filter(Boolean).slice(r),a="/"+(t[0]||"");if(!n)return{key:null,content:null,params:{},routePath:a};let l=null,o=-1,h={};for(const i in n){if(i==="/*")continue;const b=q(i),x=W(t,b);x>o&&(o=x,l=i,h=$(t,b))}return!l&&n["/*"]&&(l="/*"),{key:l,content:l?n[l]:null,params:h,routePath:a}},W=(e,n)=>{const{segments:r,hasWildcard:s}=n;if(!s&&r.length!==e.length&&r.length!==1&&r.length>e.length)return-1;let t=0;const a=Math.min(r.length,e.length);for(let l=0;l<a;l++)if(r[l]===e[l])t+=3;else if(U.test(r[l]))t+=1;else if(L.test(r[l]))t+=.5;else return-1;return t},$=(e,n)=>{const r={};for(const{index:s,name:t}of n.params)e[s]&&(r[t]=decodeURIComponent(e[s]));return r},z=e=>{if(!e||e==="?")return{};const n={};return new URLSearchParams(e).forEach((s,t)=>{n[t]!==void 0?(Array.isArray(n[t])||(n[t]=[n[t]]),n[t].push(s)):n[t]=s}),n},G=async(e,n)=>{if(!e||!e.length)return!0;for(const r of e){const s=await r(n);if(s===!1)return!1;if(typeof s=="string")return s}return!0},Q=(e=0,n)=>{n||(n=typeof R<"u"?R.location.pathname:"/");const s=n.split("/")[e+1];if(s)return`/${s}`};let S,j=0;const D=e=>!e||e==="srcdoc"||e==="about:srcdoc"?"/":e,H={level:j,pushState:!0,initialRender:!1,scrollToTop:!0,scrollToNode:!1,get scrollNode(){return typeof v<"u"&&(v.scrollingElement||v.documentElement)||null},scrollBody:!1,useFragment:!1,updateState:!0,scrollToOffset:0,contentElementKey:"content",scrollToOptions:{behavior:"smooth"},useParamsMatching:!0},M=async(e,n,r={},s={})=>{const t=n||void 0,a=t?.context?.window||R,l=t?.context?.document||v,o={...H,...t.context.routerOptions,...s};j=o.lastLevel;const h=t.__ref;(o.contentElementKey!=="content"&&o.contentElementKey!==h.contentElementKey||!h.contentElementKey)&&(h.contentElementKey=o.contentElementKey||"content");const i=h.contentElementKey||o.contentElementKey||"content",b=a.location.origin!=="null"?a.location.origin:"http://localhost";e=D(e);const x=typeof a.URL=="function"?a.URL:typeof globalThis<"u"&&globalThis.URL||URL,A=new x(b+e),{pathname:u,search:w,hash:g}=A,P=z(w),C=t.node,k=g&&g!==a.location.hash.slice(1),B=u!==S;S=u;let y,O,p,T;if(o.useParamsMatching){const c=I(u,t.routes,o.level);y=c.key,O=c.routePath,p=c.content,T=c.params}else{y=Q(o.level,u),O=y;const c=t.routes;p=c?c[y||"/"]||c["/*"]:null,T={}}const E=o.scrollToNode?C:o.scrollNode;if(t.state?.root?.debugging){t.state.root.debugging=!1;return}if(!p){o.onNotFound?o.onNotFound({pathname:u,route:y,element:t}):o.silent||console.warn("[smbls/router] no content matched for path",u,"\u2014 available routes:",Object.keys(t.routes||{}));return}if(o.guards&&o.guards.length){const c={pathname:u,route:y,params:T,query:P,hash:g,element:t,state:r},f=await G(o.guards,c);if(f===!1)return;if(typeof f=="string")return M(f,n,r,{...s,guards:[]})}if(o.pushState)try{a.history.pushState(r,null,u+(w||"")+(g||""))}catch{}if(B||!k){const c={route:y,routePath:O,hash:g,debugging:!1};Object.keys(T).length&&(c.params=T),Object.keys(P).length&&(c.query=P),o.updateState&&t.state.update(c,{preventContentUpdate:!0}),i&&o.removeOldElement&&t[i].remove();const f=t.__ref?.origin?.content,K={};if(f)for(const d in f){const m=f[d];d==="__ref"||d==="props"||d==="node"||d==="parent"||d==="key"||(typeof m=="string"||typeof m=="number"||typeof m=="boolean"||typeof m=="object"&&m!==null&&!m.node&&!m.__ref)&&(K[d]=m)}const N={...K,...typeof p=="object"?p:{extends:p}};o.useFragment&&(N.tag="fragment");try{if(typeof t?.set!="function")return;t.set(N,{contentElementKey:i})}catch(d){console.error("[smbls/router] failed to render route content",u,d)}}if(o.scrollToTop&&E?.scrollTo&&E.scrollTo({...o.scrollToOptions||{},top:0,left:0}),o.scrollToNode&&p[i]?.node?.scrollTo&&p[i].node.scrollTo({...o.scrollToOptions||{},top:0,left:0}),g){const c=l.getElementById(g);if(c&&E?.scrollTo){const f=c.getBoundingClientRect().top+C.scrollTop-(o.scrollToOffset||0);E.scrollTo({...o.scrollToOptions||{},top:f,left:0})}}F("routeChanged",t,o)};var V=M;export{V as default,Q as getActiveRoute,j as lastLevel,S as lastPathname,I as matchRoute,z as parseQuery,q as parseRoutePattern,M as router,G as runGuards};
1
+ import{document as E,window as O,triggerEventOn as q}from"@symbo.ls/utils";const S=/^:(.+)/,U=/^\*$/,L=new Map,I=o=>{const n=L.get(o);if(n)return n;const r=o.replace(/^\//,"").split("/"),c=[];let t=!1;for(let l=0;l<r.length;l++){const e=r[l].match(S);e?c.push({index:l,name:e[1]}):U.test(r[l])&&(t=!0)}const a={segments:r,params:c,hasWildcard:t,pattern:o};return L.set(o,a),a},W=(o,n,r=0)=>{const t=o.replace(/^\//,"").split("/").filter(Boolean).slice(r),a="/"+(t[0]||"");if(!n)return{key:null,content:null,params:{},routePath:a};let l=null,e=-1,u={};for(const f in n){if(f==="/*")continue;const b=I(f),x=$(t,b);x>e&&(e=x,l=f,u=z(t,b))}return!l&&n["/*"]&&(l="/*"),{key:l,content:l?n[l]:null,params:u,routePath:a}},$=(o,n)=>{const{segments:r,hasWildcard:c}=n;if(!c&&r.length!==o.length&&r.length!==1&&r.length>o.length)return-1;let t=0;const a=Math.min(r.length,o.length);for(let l=0;l<a;l++)if(r[l]===o[l])t+=3;else if(S.test(r[l]))t+=1;else if(U.test(r[l]))t+=.5;else return-1;return t},z=(o,n)=>{const r={};for(const{index:c,name:t}of n.params)o[c]&&(r[t]=decodeURIComponent(o[c]));return r},G=o=>{if(!o||o==="?")return{};const n={};return new URLSearchParams(o).forEach((c,t)=>{n[t]!==void 0?(Array.isArray(n[t])||(n[t]=[n[t]]),n[t].push(c)):n[t]=c}),n},Q=async(o,n)=>{if(!o||!o.length)return!0;for(const r of o){const c=await r(n);if(c===!1)return!1;if(typeof c=="string")return c}return!0},D=(o=0,n)=>{n||(n=typeof O<"u"?O.location.pathname:"/");const c=n.split("/")[o+1];if(c)return`/${c}`};let j,M=0;const H=o=>!o||o==="srcdoc"||o==="about:srcdoc"?"/":o,J={level:M,pushState:!0,initialRender:!1,scrollToTop:!0,scrollToNode:!1,get scrollNode(){return typeof E<"u"&&(E.scrollingElement||E.documentElement)||null},scrollBody:!1,useFragment:!1,updateState:!0,scrollToOffset:0,contentElementKey:"content",scrollToOptions:{behavior:"smooth"},useParamsMatching:!0},A=async(o,n,r={},c={})=>{const t=n||void 0,a=t?.context?.window||O,l=t?.context?.document||E,e={...J,...t.context.routerOptions,...c};M=e.lastLevel;const u=t.__ref;(e.contentElementKey!=="content"&&e.contentElementKey!==u.contentElementKey||!u.contentElementKey)&&(u.contentElementKey=e.contentElementKey||"content");const f=u.contentElementKey||e.contentElementKey||"content",b=a.location.origin!=="null"?a.location.origin:"http://localhost";o=H(o);const x=typeof a.URL=="function"?a.URL:typeof globalThis<"u"&&globalThis.URL||URL,k=new x(b+o),{pathname:d,search:C,hash:y}=k,v=G(C),_=t.node,B=y&&y!==a.location.hash.slice(1),K=d!==j;j=d;const P=u.__routerRendered;let p,w,i,T;if(e.useParamsMatching){const s=W(d,t.routes,e.level);p=s.key,w=s.routePath,i=s.content,T=s.params}else{p=D(e.level,d),w=p;const s=t.routes;i=s?s[p||"/"]||s["/*"]:null,T={}}const R=e.scrollToNode?_:e.scrollNode;if(t.state?.root?.debugging){t.state.root.debugging=!1;return}if(!i){e.onNotFound?e.onNotFound({pathname:d,route:p,element:t}):e.silent||console.warn("[smbls/router] no content matched for path",d,"\u2014 available routes:",Object.keys(t.routes||{}));return}if(e.guards&&e.guards.length){const s={pathname:d,route:p,params:T,query:v,hash:y,element:t,state:r},m=await Q(e.guards,s);if(m===!1)return;if(typeof m=="string")return A(m,n,r,{...c,guards:[]})}if(e.pushState)try{a.history.pushState(r,null,d+(C||"")+(y||""))}catch{}if(K||!B){const s={route:p,routePath:w,hash:y,debugging:!1};Object.keys(T).length&&(s.params=T),Object.keys(v).length&&(s.query=v),e.updateState&&t.state.update(s,{preventContentUpdate:!0})}const F=!P||P.content!==i||P.route!==p;if(K||F||e.force===!0){f&&e.removeOldElement&&t[f].remove();const s=t.__ref?.origin?.content,m={};if(s)for(const h in s){const g=s[h];h==="__ref"||h==="props"||h==="node"||h==="parent"||h==="key"||(typeof g=="string"||typeof g=="number"||typeof g=="boolean"||typeof g=="object"&&g!==null&&!g.node&&!g.__ref)&&(m[h]=g)}const N={...m,...typeof i=="object"?i:{extends:i}};e.useFragment&&(N.tag="fragment");try{if(typeof t?.set!="function")return;t.set(N,{contentElementKey:f}),u.__routerRendered={content:i,route:p}}catch(h){console.error("[smbls/router] failed to render route content",d,h)}}if(e.scrollToTop&&R?.scrollTo&&R.scrollTo({...e.scrollToOptions||{},top:0,left:0}),e.scrollToNode&&i[f]?.node?.scrollTo&&i[f].node.scrollTo({...e.scrollToOptions||{},top:0,left:0}),y){const s=l.getElementById(y);if(s&&R?.scrollTo){const m=s.getBoundingClientRect().top+_.scrollTop-(e.scrollToOffset||0);R.scrollTo({...e.scrollToOptions||{},top:m,left:0})}}q("routeChanged",t,e)};var Y=A;export{Y as default,D as getActiveRoute,M as lastLevel,j as lastPathname,W as matchRoute,G as parseQuery,I as parseRoutePattern,A as router,Q as runGuards};
package/index.js CHANGED
@@ -219,9 +219,17 @@ export const router = async (path, el, state = {}, options = {}) => {
219
219
  const query = parseQuery(search)
220
220
 
221
221
  const rootNode = element.node
222
+ // Captured against `win.location.hash` BEFORE `history.pushState` below
223
+ // mutates it — `hashChanged` only drives the state.update timing gate
224
+ // now (see ROUTER-QUERY-SWAP-1), not the content-swap decision.
222
225
  const hashChanged = hash && hash !== win.location.hash.slice(1)
223
226
  const pathChanged = pathname !== lastPathname
224
227
  lastPathname = pathname
228
+ // APP-PAGE-COLD-LOAD-NOT-FOUND-1: `lastPathname` is MODULE-level, so it
229
+ // answers "was this pathname the last one ANY routed element resolved",
230
+ // never "is the right content on screen for THIS element". Keep the real
231
+ // rendered identity on the element itself — see the swap gate below.
232
+ const lastRendered = ref.__routerRendered
225
233
 
226
234
  // Route matching - support both simple and param-based
227
235
  let route, routePath, content, params
@@ -293,7 +301,42 @@ export const router = async (path, el, state = {}, options = {}) => {
293
301
  { preventContentUpdate: true }
294
302
  )
295
303
  }
304
+ }
296
305
 
306
+ // ROUTER-QUERY-SWAP-1 (fable.md): `content` is matched purely from
307
+ // `pathname` — matchRoute/getActiveRoute never consult `search` or
308
+ // `hash` — so only an actual pathname change can produce different
309
+ // content. This used to be folded into the `pathChanged || !hashChanged`
310
+ // block above, and `!hashChanged` is true for the vast majority of
311
+ // calls (any nav with no hash involved at all, i.e. every plain
312
+ // query-only `router('/x?a=2', root)` call), so the routed subtree was
313
+ // torn down and rebuilt — losing local state and re-running onCreate —
314
+ // on every query-only navigation even though the matched content never
315
+ // changed. Gate the actual element.set() swap on pathChanged alone so a
316
+ // same-path call (query-only OR a genuine same-path hash jump) updates
317
+ // state/query above but leaves the mounted content element identity
318
+ // untouched.
319
+ //
320
+ // APP-PAGE-COLD-LOAD-NOT-FOUND-1 — "did the pathname change" is the wrong
321
+ // question when the ROUTE TABLE can change under a live page. A cold load
322
+ // of a late-registered route (a workspace app injecting its pages after
323
+ // boot) resolves once against an incomplete table, legitimately matches
324
+ // the `/*` catch-all, and burns that pathname: every later re-resolve of
325
+ // the SAME path — including the app loader's own recovery re-dispatch,
326
+ // measured live at +4.0s with the route present — saw `pathChanged`
327
+ // false and skipped the swap, so "Not found" stayed on screen until a
328
+ // navigation away and back moved `lastPathname` twice.
329
+ //
330
+ // Ask the honest question instead: does the resolved content differ from
331
+ // what this element currently renders? That subsumes the pathname test
332
+ // (different path => different match) and keeps ROUTER-QUERY-SWAP-1
333
+ // intact, because a query-only nav re-matches the SAME route entry and so
334
+ // yields the identical `content` reference. `opts.force` stays available
335
+ // for a caller that knows the content object was mutated in place.
336
+ const contentChanged =
337
+ !lastRendered || lastRendered.content !== content || lastRendered.route !== route
338
+ const shouldSwap = pathChanged || contentChanged || opts.force === true
339
+ if (shouldSwap) {
297
340
  if (contentElementKey && opts.removeOldElement) {
298
341
  element[contentElementKey].remove()
299
342
  }
@@ -328,6 +371,11 @@ export const router = async (path, el, state = {}, options = {}) => {
328
371
  // "bail without exploding" contract for dead elements.
329
372
  if (typeof element?.set !== 'function') return
330
373
  element.set(nextContent, { contentElementKey })
374
+ // Recorded only after the swap actually happened, so a dead element
375
+ // (the early return above) or a throw below never marks content as
376
+ // rendered that never reached the DOM — that would re-arm exactly the
377
+ // stuck state this gate exists to clear.
378
+ ref.__routerRendered = { content, route }
331
379
  } catch (err) {
332
380
  console.error('[smbls/router] failed to render route content', pathname, err)
333
381
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbo.ls/router",
3
- "version": "3.14.595",
3
+ "version": "3.14.600",
4
4
  "license": "CC-BY-NC-4.0",
5
5
  "type": "module",
6
6
  "module": "./dist/esm/index.js",
@@ -28,8 +28,8 @@
28
28
  "prepublishOnly": "npm run build"
29
29
  },
30
30
  "dependencies": {
31
- "@symbo.ls/element": "^3.14.595",
32
- "@symbo.ls/utils": "^3.14.595"
31
+ "@symbo.ls/element": "^3.14.599",
32
+ "@symbo.ls/utils": "^3.14.599"
33
33
  },
34
34
  "gitHead": "9fc1b79b41cdc725ca6b24aec64920a599634681",
35
35
  "devDependencies": {