@stacksjs/actions 0.70.45 → 0.70.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/LICENSE.md +21 -0
  2. package/dist/action.d.ts +104 -17
  3. package/dist/blog.d.ts +12 -0
  4. package/dist/build/frontend-static.d.ts +16 -0
  5. package/dist/chunk-00qp27jy.js +93 -0
  6. package/dist/{chunk-jd1yzkq5.js → chunk-23thftnv.js} +1 -1
  7. package/dist/{chunk-jbvvb45k.js → chunk-4qzb8g3z.js} +1 -1
  8. package/dist/{chunk-s03z9zn3.js → chunk-5dfz9en9.js} +2 -2
  9. package/dist/{chunk-3beht8ja.js → chunk-5hwp0frq.js} +1 -1
  10. package/dist/{chunk-cqedfbaf.js → chunk-5pwpk005.js} +2 -2
  11. package/dist/chunk-604jy79b.js +5 -0
  12. package/dist/{chunk-yh15ydty.js → chunk-63swek9d.js} +2 -2
  13. package/dist/chunk-67am4dae.js +688 -0
  14. package/dist/{chunk-gt77jmb5.js → chunk-8gbewxns.js} +1 -1
  15. package/dist/chunk-8r76qydb.js +240 -0
  16. package/dist/{chunk-zbhjh5sz.js → chunk-9x17v4x2.js} +2 -2
  17. package/dist/chunk-aazd6qpv.js +3 -0
  18. package/dist/{chunk-n2fd1d0m.js → chunk-ag4fy7cx.js} +1 -1
  19. package/dist/{chunk-2qchdrf9.js → chunk-badehrwa.js} +1 -1
  20. package/dist/chunk-dbz9dp2h.js +3 -0
  21. package/dist/{chunk-v0wnrnaa.js → chunk-e8505qz8.js} +2 -2
  22. package/dist/chunk-jv7ry09n.js +3 -0
  23. package/dist/{chunk-w418anbq.js → chunk-jve1p4dz.js} +2 -2
  24. package/dist/chunk-mcwfmqzb.js +283 -0
  25. package/dist/chunk-n24m8prb.js +3 -0
  26. package/dist/{chunk-d5yxdz7e.js → chunk-nm9h5rye.js} +2 -2
  27. package/dist/{chunk-h0hfqr1p.js → chunk-ny1racy0.js} +2 -2
  28. package/dist/{chunk-2gh7kgpc.js → chunk-p601xr03.js} +1 -1
  29. package/dist/{chunk-yvva7qsg.js → chunk-pqnzp7x8.js} +2 -2
  30. package/dist/chunk-rj1cbghx.js +17 -0
  31. package/dist/chunk-rzhs51h1.js +3 -0
  32. package/dist/{chunk-tzx3h4r8.js → chunk-sp5cjdhd.js} +213 -76
  33. package/dist/chunk-tqax3gbq.js +16 -0
  34. package/dist/{chunk-c8qt2xst.js → chunk-vsj8ns4p.js} +1 -1
  35. package/dist/{chunk-zaw01rqz.js → chunk-x8nrbsk1.js} +2 -2
  36. package/dist/dev/dashboard-utils.d.ts +19 -1
  37. package/dist/dev/migrate-watcher.d.ts +1 -0
  38. package/dist/index.d.ts +2 -0
  39. package/dist/make.d.ts +19 -0
  40. package/dist/queue/dlq-purge.d.ts +1 -0
  41. package/dist/queue/dlq-retry.d.ts +1 -0
  42. package/dist/queue/dlq.d.ts +1 -0
  43. package/dist/queue/list.d.ts +1 -0
  44. package/dist/queue/pause.d.ts +1 -0
  45. package/dist/queue/quarantine.d.ts +1 -0
  46. package/dist/queue/resume.d.ts +1 -0
  47. package/dist/queue/unquarantine.d.ts +1 -0
  48. package/dist/src/action.js +1 -1
  49. package/dist/src/actions.js +1 -1
  50. package/dist/src/add.js +1 -1
  51. package/dist/src/ai/request-model-access.js +1 -1
  52. package/dist/src/auth/setup.js +82 -24
  53. package/dist/src/blog.js +102 -0
  54. package/dist/src/build/core.js +1 -1
  55. package/dist/src/build/frontend-static.js +9 -0
  56. package/dist/src/build/views.js +1 -1
  57. package/dist/src/build.js +1 -1
  58. package/dist/src/commit.js +1 -1
  59. package/dist/src/database/seed.js +1 -1
  60. package/dist/src/deploy/index.js +38 -45
  61. package/dist/src/dev/api.js +4 -4
  62. package/dist/src/dev/dashboard-globals.js +1 -1
  63. package/dist/src/dev/dashboard-utils.js +1 -1
  64. package/dist/src/dev/dashboard.js +6 -6
  65. package/dist/src/dev/docs.js +1 -1
  66. package/dist/src/dev/index.js +1 -1
  67. package/dist/src/dev/migrate-watcher.js +2 -0
  68. package/dist/src/dev/views.js +1 -1
  69. package/dist/src/discover-packages.js +1 -1
  70. package/dist/src/examples.js +1 -1
  71. package/dist/src/generate/component-meta.js +1 -1
  72. package/dist/src/generate/env-files.js +1 -1
  73. package/dist/src/generate/ide-helpers.js +1 -1
  74. package/dist/src/generate/index.js +1 -1
  75. package/dist/src/generate/lib-entries.js +1 -1
  76. package/dist/src/generate/vscode-custom-data.js +1 -1
  77. package/dist/src/helpers/component-meta.js +1 -1
  78. package/dist/src/helpers/index.js +1 -1
  79. package/dist/src/helpers/lib-entries.js +1 -1
  80. package/dist/src/helpers/package-json.js +1 -1
  81. package/dist/src/helpers/utils.js +1 -1
  82. package/dist/src/helpers/vscode-custom-data.js +1 -1
  83. package/dist/src/index.js +1 -1
  84. package/dist/src/key-generate.js +1 -1
  85. package/dist/src/make-command.js +1 -1
  86. package/dist/src/make-job.js +1 -1
  87. package/dist/src/make-policy.js +1 -1
  88. package/dist/src/make-resource.js +1 -1
  89. package/dist/src/make.js +1 -1
  90. package/dist/src/migrate/fresh.js +1 -1
  91. package/dist/src/orm/base.js +1 -1
  92. package/dist/src/queue/dlq-purge.js +2 -0
  93. package/dist/src/queue/dlq-retry.js +2 -0
  94. package/dist/src/queue/dlq.js +5 -0
  95. package/dist/src/queue/failed.js +4 -4
  96. package/dist/src/queue/list.js +5 -0
  97. package/dist/src/queue/pause.js +2 -0
  98. package/dist/src/queue/quarantine.js +3 -0
  99. package/dist/src/queue/resume.js +2 -0
  100. package/dist/src/queue/unquarantine.js +2 -0
  101. package/dist/src/release.js +1 -1
  102. package/dist/src/run-action.js +1 -1
  103. package/dist/src/scaffold-crud.js +1 -1
  104. package/dist/src/serve/api.js +1 -1
  105. package/dist/src/setup/index.js +1 -1
  106. package/dist/src/setup/ssl.js +1 -1
  107. package/dist/src/stacks.js +1 -1
  108. package/dist/src/templates.js +1 -1
  109. package/dist/src/types.js +1 -1
  110. package/dist/src/upgrade/dependencies.js +1 -1
  111. package/dist/src/upgrade/framework-utils.js +1 -1
  112. package/dist/src/upgrade/framework.js +5 -5
  113. package/dist/src/upgrade/index.js +1 -1
  114. package/dist/src/upgrade.js +1 -1
  115. package/dist/upgrade/framework-utils.d.ts +48 -0
  116. package/package.json +18 -18
  117. package/dist/chunk-2kaj93p5.js +0 -3
  118. package/dist/chunk-3086k7zk.js +0 -184
  119. package/dist/chunk-4fz2qb8b.js +0 -17
  120. package/dist/chunk-4p0rjazd.js +0 -101
  121. package/dist/chunk-77pta9ww.js +0 -93
  122. package/dist/chunk-d4srk6ma.js +0 -3
  123. package/dist/chunk-h5rff0v9.js +0 -658
  124. package/dist/chunk-kr2k0h74.js +0 -16
  125. package/dist/chunk-kzhgexj4.js +0 -3
  126. package/dist/chunk-sxp97zys.js +0 -3
  127. package/dist/chunk-vek18p55.js +0 -5
@@ -1,15 +1,31 @@
1
1
  // @bun
2
- import"./chunk-xg9xscqz.js";import"./chunk-jbvvb45k.js";import U from"fs";import Z from"path";import Q from"fs";import j from"path";import F from"path";import D from"fs";import L from"path";import T from"fs";import H from"path";function S(G,B){let I=["// Auto-generated by STX - do not edit",'declare module "stx/routes" {'," interface RouteMap {"];for(let J of G){let K=[];for(let V of J.params){let X=J.pattern.includes(`:${V}*`)?"string[]":"string";K.push(`${V}: ${X}`)}let O=K.join("; ");I.push(` '${J.pattern}': { ${O} }`)}I.push(" }","}",""),Q.mkdirSync(B,{recursive:!0}),Q.writeFileSync(j.join(B,"route-types.d.ts"),I.join(`
3
- `))}function b(G,B,I){let J=process.cwd(),K=j.relative(J,I),O=["components","layouts","partials"],V=G.filter((W)=>!O.some((Y)=>W.pattern.startsWith(`/${Y}/`)||W.pattern===`/${Y}`)),X=["// Auto-generated by stx \u2014 do not edit",`// Source: ${K||"pages"}/`,"","export const routes = ["];for(let W of V){let Y=j.relative(J,W.filePath).replace(/\\/g,"/"),M=JSON.stringify(W.params),R=W.layout?`, layout: '${j.relative(J,W.layout).replace(/\\/g,"/")}'`:"";X.push(` { pattern: '${W.pattern}', filePath: '${Y}', isDynamic: ${W.isDynamic}, params: ${M}${R} },`)}X.push("] as const",""),X.push("export type RouteName = typeof routes[number]['pattern']",""),Q.mkdirSync(B,{recursive:!0}),Q.writeFileSync(j.join(B,"routes.ts"),X.join(`
4
- `))}function v(G,B){let J=F.relative(B,G).replace(/\.(stx|md)$/,"");if(J=J.split(F.sep).join("/"),J==="index")return"/";if(J.endsWith("/index"))J=J.slice(0,-6);return J=J.replace(/\[\[([^\]]+)\]\]/g,":$1?"),J=J.replace(/\[([^\]]+)\]/g,":$1"),J=J.replace(/:\.\.\.([^/]+)/g,":$1*"),`/${J}`}function w(G){let B=[],I=G.replace(/[.+^${}()|[\]\\]/g,"\\$&");return I=I.replace(/\/:([^/]+)\?/g,(J,K)=>{return B.push(K),"(?:/([^/]+))?"}),I=I.replace(/:([^/]+)\*/g,(J,K)=>{return B.push(K),"(.+)"}),I=I.replace(/:([^/]+)/g,(J,K)=>{return B.push(K),"([^/]+)"}),I=`^${I}$`,{regex:new RegExp(I),params:B}}function m(G,B){let I=G===""?"/":G;for(let J of B){let K=I.match(J.regex);if(K){let O={};return J.params.forEach((V,X)=>{O[V]=K[X+1]}),{route:J,params:O}}}return null}function P(G,B){let I=[],J=L.dirname(G);while(J.startsWith(B)||J===B){let K=L.join(J,"_layout.stx");if(D.existsSync(K))I.unshift(K);if(J===B)break;J=L.dirname(J)}return I}function N(G,B){let I=[];if(!U.existsSync(G))return I;let J=U.readdirSync(G,{withFileTypes:!0});for(let K of J){let O=Z.join(G,K.name);if(K.isDirectory()){if(K.name.startsWith("."))continue;I.push(...N(O,B))}else if(K.isFile()){let V=Z.extname(K.name);if(B.includes(V)){if(K.name.startsWith("_")&&K.name!=="_layout.stx")continue;if(K.name==="_layout.stx")continue;I.push(O)}}}return I}class C{routes;pagesDir;config;constructor(G,B={}){this.config=B,this.pagesDir=Z.resolve(G,B.pagesDir||"pages");let I=B.extensions||[".stx"],J=N(this.pagesDir,I);this.routes=J.map((O)=>{let V=v(O,this.pagesDir),{regex:X,params:W}=w(V),Y=B.layouts!==!1?P(O,this.pagesDir):[];return{pattern:V,regex:X,params:W,filePath:O,isDynamic:W.length>0,layout:Y.length>0?Y[Y.length-1]:void 0}}),this.routes.sort((O,V)=>{if(!O.isDynamic&&V.isDynamic)return-1;if(O.isDynamic&&!V.isDynamic)return 1;let X=O.pattern.split("/").length,W=V.pattern.split("/").length;if(X!==W)return W-X;return O.pattern.localeCompare(V.pattern)});let K=Z.join(G,".stx");S(this.routes,K),b(this.routes,K,this.pagesDir)}match(G){let B=G===""?"/":G;for(let I of this.routes){let J=B.match(I.regex);if(J){let K={};return I.params.forEach((O,V)=>{K[O]=J[V+1]}),{route:I,params:K}}}return null}resolve(G,B){let I=this.routes.find((K)=>K.pattern===G||K.filePath.includes(G));if(!I)return"#not-found";let J=I.pattern;if(B)for(let[K,O]of Object.entries(B))J=J.replace(`:${K}`,encodeURIComponent(O));return J}addRoute(G){this.routes.push(G),this.routes.sort((B,I)=>{if(!B.isDynamic&&I.isDynamic)return-1;if(B.isDynamic&&!I.isDynamic)return 1;return B.pattern.localeCompare(I.pattern)})}getLayout(G){return G.layout??null}}function i(G,B){return new C(G,B)}function o(G,B){let I=[`${B}.stx`,"error.stx"];for(let J of I){let K=Z.join(G,J);if(U.existsSync(K))return K}return null}function t(G,B){return G.map((I)=>{let J=Z.relative(B,I.filePath),K=I.isDynamic?" (dynamic)":"";return`${I.pattern} \u2192 ${J}${K}`})}var $={},_="";function r(G){_=G.replace(/\/$/,"")}function a(G,B,I={}){$[G]={path:B,name:G,params:I}}function e(G){for(let[B,I]of Object.entries(G))if(typeof I==="string")$[B]={path:I,name:B};else $[B]={...I,name:B}}function BB(G,B={},I=!1){let J=$[G];if(!J)return console.warn(`Route [${G}] not defined.`),"#undefined-route";let K={...J.params,...B},O=J.path;O=O.replace(/:(\w+)/g,(X,W)=>{let Y=K[W];if(Y===void 0)return console.warn(`Missing parameter [${W}] for route [${G}].`),`:${W}`;return delete K[W],encodeURIComponent(String(Y))});let V=Object.entries(K);if(V.length>0){let X=V.map(([W,Y])=>`${encodeURIComponent(W)}=${encodeURIComponent(String(Y))}`).join("&");O+=(O.includes("?")?"&":"?")+X}return I&&_?`${_}${O}`:O}function GB(){for(let G of Object.keys($))delete $[G];_=""}function KB(G,B={}){return{type:"redirect",path:G,options:{replace:B.replace??!1,external:B.external??!1,redirectCode:B.redirectCode??302}}}function OB(G){return{type:"abort",error:typeof G==="string"?{statusCode:500,message:G}:G}}function VB(G,B={}){return{handler:G,mode:B.mode??"universal"}}function z(G){let B={};if(!G)return B;let I=G.split(";");for(let J of I){let[K,...O]=J.split("=");if(K){let V=K.trim(),X=O.join("=").trim();if(V)B[V]=decodeURIComponent(X)}}return B}function E(G,B,I={}){let J=`${encodeURIComponent(G)}=${encodeURIComponent(B)}`;if(I.expires){let K=typeof I.expires==="number"?new Date(Date.now()+I.expires*1000):I.expires;J+=`; Expires=${K.toUTCString()}`}if(I.path)J+=`; Path=${I.path}`;if(I.domain)J+=`; Domain=${I.domain}`;if(I.secure)J+="; Secure";if(I.httpOnly)J+="; HttpOnly";if(I.sameSite)J+=`; SameSite=${I.sameSite}`;return J}function k(G,B){let I=G.headers.get("cookie")||"",J=z(I);return{get(K){return J[K]},set(K,O,V={}){J[K]=O,B.append("Set-Cookie",E(K,O,V))},delete(K){delete J[K],B.append("Set-Cookie",E(K,"",{expires:new Date(0),path:"/"}))},getAll(){return{...J}}}}function x(){return{get(G){if(typeof document>"u")return;return z(document.cookie)[G]},set(G,B,I={}){if(typeof document>"u")return;document.cookie=E(G,B,I)},delete(G){if(typeof document>"u")return;document.cookie=E(G,"",{expires:new Date(0),path:"/"})},getAll(){if(typeof document>"u")return{};return z(document.cookie)}}}function y(){let G=typeof window<"u"&&typeof window.localStorage<"u";return{get(B){if(!G)return null;try{return window.localStorage.getItem(B)}catch{return null}},set(B,I){if(!G)return;try{window.localStorage.setItem(B,I)}catch{}},remove(B){if(!G)return;try{window.localStorage.removeItem(B)}catch{}},isAvailable(){return G}}}var A=new Map;function q(G,B){A.set(G,B)}function f(G){return A.get(G)}function WB(G){return A.has(G)}function XB(){A.clear()}function YB(){return Array.from(A.keys())}async function ZB(G,B="middleware"){let I=H.resolve(G,B);if(!T.existsSync(I))return;let J=T.readdirSync(I);for(let K of J){if(!K.endsWith(".ts")&&!K.endsWith(".js"))continue;if(K.includes(".test.")||K.includes(".spec."))continue;let O=K.replace(/\.(ts|js)$/,""),V=H.join(I,K);try{let W=(await import(V)).default;if(W&&typeof W.handler==="function")q(O,W);else if(typeof W==="function")q(O,{handler:W,mode:"universal"})}catch(X){console.warn(`Failed to load middleware '${O}' from ${V}:`,X)}}}function g(G){let B={};if(!G)return B;let I=new URLSearchParams(G);for(let[J,K]of I)B[J]=K;return B}function $B(G,B,I,J=""){return{path:G,params:B,query:g(J),fullPath:J?`${G}?${J}`:G,meta:I}}function jB(G,B,I){let J=typeof window>"u",K=!J,O=new Headers;return{to:G,from:B,isClient:K,isServer:J,cookies:J&&I?k(I,O):x(),storage:y(),state:{},request:I,responseHeaders:O}}async function AB(G,B){let I=Array.isArray(G)?G:[G],J=B.isServer;for(let K of I){let O=f(K);if(!O){console.warn(`Middleware '${K}' not found`);continue}if(O.mode==="server"&&!J)continue;if(O.mode==="client"&&J)continue;try{let V=await O.handler(B);if(V){if(V.type==="redirect")return{passed:!1,redirect:V,state:B.state,responseHeaders:B.responseHeaders};if(V.type==="abort")return{passed:!1,abort:V,state:B.state,responseHeaders:B.responseHeaders}}}catch(V){return console.error(`Error in middleware '${K}':`,V),{passed:!1,abort:{type:"abort",error:{statusCode:500,message:V instanceof Error?V.message:"Middleware error"}},state:B.state,responseHeaders:B.responseHeaders}}}return{passed:!0,state:B.state,responseHeaders:B.responseHeaders}}function QB(){return`
2
+ import{a as b,b as v}from"./chunk-dbz9dp2h.js";import"./chunk-xg9xscqz.js";import"./chunk-4qzb8g3z.js";import T from"fs";import $ from"path";import _ from"fs";import G from"path";import q from"path";import x from"fs";import L from"path";import N from"fs";import C from"path";function w(I,B){let J=["// Auto-generated by STX - do not edit",'declare module "stx/routes" {'," interface RouteMap {"];for(let K of I){let O=[];for(let V of K.params){let Y=K.pattern.includes(`:${V}*`)?"string[]":"string";O.push(`${V}: ${Y}`)}let W=O.join("; ");J.push(` '${K.pattern}': { ${W} }`)}J.push(" }","}",""),_.mkdirSync(B,{recursive:!0}),_.writeFileSync(G.join(B,"route-types.d.ts"),J.join(`
3
+ `))}function D(I,B,J){let K=process.cwd(),O=G.relative(K,J),W=["components","layouts","partials"],V=I.filter((X)=>!W.some((Z)=>X.pattern.startsWith(`/${Z}/`)||X.pattern===`/${Z}`)),Y=["// Auto-generated by stx \u2014 do not edit",`// Source: ${O||"pages"}/`,"","export const routes = ["];for(let X of V){let Z=G.relative(K,X.filePath).replace(/\\/g,"/"),z=JSON.stringify(X.params),j=X.layout?`, layout: '${G.relative(K,X.layout).replace(/\\/g,"/")}'`:"";Y.push(` { pattern: '${X.pattern}', filePath: '${Z}', isDynamic: ${X.isDynamic}, params: ${z}${j} },`)}Y.push("] as const",""),Y.push("export type RouteName = typeof routes[number]['pattern']",""),_.mkdirSync(B,{recursive:!0}),_.writeFileSync(G.join(B,"routes.ts"),Y.join(`
4
+ `))}function P(I,B){let K=q.relative(B,I).replace(/\.(stx|md)$/,"");if(K=K.split(q.sep).join("/"),K==="index")return"/";if(K.endsWith("/index"))K=K.slice(0,-6);return K=K.replace(/\[\[([^\]]+)\]\]/g,":$1?"),K=K.replace(/\[([^\]]+)\]/g,":$1"),K=K.replace(/:\.\.\.([^/]+)/g,":$1*"),`/${K}`}function k(I){let B=[],J=I.replace(/[.+^${}()|[\]\\]/g,"\\$&");return J=J.replace(/\/:([^/]+)\?/g,(K,O)=>{return B.push(O),"(?:/([^/]+))?"}),J=J.replace(/:([^/]+)\*/g,(K,O)=>{return B.push(O),"(.+)"}),J=J.replace(/:([^/]+)/g,(K,O)=>{return B.push(O),"([^/]+)"}),J=`^${J}$`,{regex:new RegExp(J),params:B}}function o(I,B){let J=I===""?"/":I;for(let K of B){let O=J.match(K.regex);if(O){let W={};return K.params.forEach((V,Y)=>{W[V]=O[Y+1]}),{route:K,params:W}}}return null}function y(I,B){let J=[],K=L.dirname(I);while(K.startsWith(B)||K===B){let O=L.join(K,"_layout.stx");if(x.existsSync(O))J.unshift(O);if(K===B)break;K=L.dirname(K)}return J}function R(I,B){let J=[];if(!T.existsSync(I))return J;let K=T.readdirSync(I,{withFileTypes:!0});for(let O of K){let W=$.join(I,O.name);if(O.isDirectory()){if(O.name.startsWith("."))continue;J.push(...R(W,B))}else if(O.isFile()){let V=$.extname(O.name);if(B.includes(V)){if(O.name.startsWith("_")&&O.name!=="_layout.stx")continue;if(O.name==="_layout.stx")continue;J.push(W)}}}return J}class S{routes;pagesDir;pagesDirs;config;constructor(I,B={}){this.config=B;let J=B.pagesDirs&&B.pagesDirs.length>0?B.pagesDirs:[B.pagesDir||"pages"];this.pagesDirs=J.map((V)=>$.resolve(I,V)),this.pagesDir=this.pagesDirs[0];let K=B.extensions||[".stx"],O=new Set;this.routes=[];for(let V of this.pagesDirs){let Y=R(V,K);for(let X of Y){let Z=P(X,V);if(O.has(Z))continue;O.add(Z);let{regex:z,params:j}=k(Z),F=B.layouts!==!1?y(X,V):[];this.routes.push({pattern:Z,regex:z,params:j,filePath:X,isDynamic:j.length>0,layout:F.length>0?F[F.length-1]:void 0})}}this.routes.sort((V,Y)=>{if(!V.isDynamic&&Y.isDynamic)return-1;if(V.isDynamic&&!Y.isDynamic)return 1;let X=V.pattern.split("/").length,Z=Y.pattern.split("/").length;if(X!==Z)return Z-X;return V.pattern.localeCompare(Y.pattern)});let W=$.join(I,".stx");w(this.routes,W),D(this.routes,W,this.pagesDir)}match(I){let B=I===""?"/":I;for(let J of this.routes){let K=B.match(J.regex);if(K){let O={};return J.params.forEach((W,V)=>{O[W]=K[V+1]}),{route:J,params:O}}}return null}resolve(I,B){let J=this.routes.find((O)=>O.pattern===I||O.filePath.includes(I));if(!J)return"#not-found";let K=J.pattern;if(B)for(let[O,W]of Object.entries(B))K=K.replace(`:${O}`,encodeURIComponent(W));return K}addRoute(I){this.routes.push(I),this.routes.sort((B,J)=>{if(!B.isDynamic&&J.isDynamic)return-1;if(B.isDynamic&&!J.isDynamic)return 1;return B.pattern.localeCompare(J.pattern)})}getLayout(I){return I.layout??null}}function a(I,B){return new S(I,B)}function e(I,B){let J=[`${B}.stx`,"error.stx"];for(let K of J){let O=$.join(I,K);if(T.existsSync(O))return O}return null}function BB(I,B){return I.map((J)=>{let K=$.relative(B,J.filePath),O=J.isDynamic?" (dynamic)":"";return`${J.pattern} \u2192 ${K}${O}`})}var A={},E="";function IB(I){E=I.replace(/\/$/,"")}function JB(I,B,J={}){A[I]={path:B,name:I,params:J}}function KB(I){for(let[B,J]of Object.entries(I))if(typeof J==="string")A[B]={path:J,name:B};else A[B]={...J,name:B}}function OB(I,B={},J=!1){let K=A[I];if(!K)return console.warn(`Route [${I}] not defined.`),"#undefined-route";let O={...K.params,...B},W=K.path;W=W.replace(/:(\w+)/g,(Y,X)=>{let Z=O[X];if(Z===void 0)return console.warn(`Missing parameter [${X}] for route [${I}].`),`:${X}`;return delete O[X],encodeURIComponent(String(Z))});let V=Object.entries(O);if(V.length>0){let Y=V.map(([X,Z])=>`${encodeURIComponent(X)}=${encodeURIComponent(String(Z))}`).join("&");W+=(W.includes("?")?"&":"?")+Y}return J&&E?`${E}${W}`:W}function VB(){for(let I of Object.keys(A))delete A[I];E=""}function YB(I,B={}){return{type:"redirect",path:I,options:{replace:B.replace??!1,external:B.external??!1,redirectCode:B.redirectCode??302}}}function ZB(I){return{type:"abort",error:typeof I==="string"?{statusCode:500,message:I}:I}}function $B(I,B={}){return{handler:I,mode:B.mode??"universal"}}function H(I){let B={};if(!I)return B;let J=I.split(";");for(let K of J){let[O,...W]=K.split("=");if(O){let V=O.trim(),Y=W.join("=").trim();if(V)B[V]=decodeURIComponent(Y)}}return B}function U(I,B,J={}){let K=`${encodeURIComponent(I)}=${encodeURIComponent(B)}`;if(J.expires){let O=typeof J.expires==="number"?new Date(Date.now()+J.expires*1000):J.expires;K+=`; Expires=${O.toUTCString()}`}if(J.path)K+=`; Path=${J.path}`;if(J.domain)K+=`; Domain=${J.domain}`;if(J.secure)K+="; Secure";if(J.httpOnly)K+="; HttpOnly";if(J.sameSite)K+=`; SameSite=${J.sameSite}`;return K}function f(I,B){let J=I.headers.get("cookie")||"",K=H(J);return{get(O){return K[O]},set(O,W,V={}){K[O]=W,B.append("Set-Cookie",U(O,W,V))},delete(O){delete K[O],B.append("Set-Cookie",U(O,"",{expires:new Date(0),path:"/"}))},getAll(){return{...K}}}}function g(){return{get(I){if(typeof document>"u")return;return H(document.cookie)[I]},set(I,B,J={}){if(typeof document>"u")return;document.cookie=U(I,B,J)},delete(I){if(typeof document>"u")return;document.cookie=U(I,"",{expires:new Date(0),path:"/"})},getAll(){if(typeof document>"u")return{};return H(document.cookie)}}}function h(){let I=typeof window<"u"&&typeof window.localStorage<"u";return{get(B){if(!I)return null;try{return window.localStorage.getItem(B)}catch{return null}},set(B,J){if(!I)return;try{window.localStorage.setItem(B,J)}catch{}},remove(B){if(!I)return;try{window.localStorage.removeItem(B)}catch{}},isAvailable(){return I}}}var Q=new Map;function M(I,B){Q.set(I,B)}function c(I){return Q.get(I)}function AB(I){return Q.has(I)}function GB(){Q.clear()}function QB(){return Array.from(Q.keys())}async function jB(I,B="middleware"){let J=C.resolve(I,B);if(!N.existsSync(J))return;let K=N.readdirSync(J);for(let O of K){if(!O.endsWith(".ts")&&!O.endsWith(".js"))continue;if(O.includes(".test.")||O.includes(".spec."))continue;let W=O.replace(/\.(ts|js)$/,""),V=C.join(J,O);try{let X=(await import(V)).default;if(X&&typeof X.handler==="function")M(W,X);else if(typeof X==="function")M(W,{handler:X,mode:"universal"})}catch(Y){console.warn(`Failed to load middleware '${W}' from ${V}:`,Y)}}}function d(I){let B={};if(!I)return B;let J=new URLSearchParams(I);for(let[K,O]of J)B[K]=O;return B}function _B(I,B,J,K=""){return{path:I,params:B,query:d(K),fullPath:K?`${I}?${K}`:I,meta:J}}function EB(I,B,J){let K=typeof window>"u",O=!K,W=new Headers;return{to:I,from:B,isClient:O,isServer:K,cookies:K&&J?f(J,W):g(),storage:h(),state:{},request:J,responseHeaders:W}}async function UB(I,B){let J=Array.isArray(I)?I:[I],K=B.isServer;for(let O of J){let W=c(O);if(!W){console.warn(`Middleware '${O}' not found`);continue}if(W.mode==="server"&&!K)continue;if(W.mode==="client"&&K)continue;try{let V=await W.handler(B);if(V){if(V.type==="redirect")return{passed:!1,redirect:V,state:B.state,responseHeaders:B.responseHeaders};if(V.type==="abort")return{passed:!1,abort:V,state:B.state,responseHeaders:B.responseHeaders}}}catch(V){return console.error(`Error in middleware '${O}':`,V),{passed:!1,abort:{type:"abort",error:{statusCode:500,message:V instanceof Error?V.message:"Middleware error"}},state:B.state,responseHeaders:B.responseHeaders}}}return{passed:!0,state:B.state,responseHeaders:B.responseHeaders}}function zB(){return`
5
5
  ;(function(){
6
6
  'use strict';
7
- if(window.__stxRouter)return;
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;
14
+ var debug=!!o.debug;
15
+ function log(){if(debug&&typeof console!=='undefined'&&console.log)console.log.apply(console,arguments)}
16
+ function runViewTransition(callback){
17
+ if(!o.viewTransitions||!document.startViewTransition)return false;
18
+ try{
19
+ var transition=document.startViewTransition(callback);
20
+ var onAbort=function(err){log('[router] view transition aborted:',err&&err.message?err.message:err)};
21
+ if(transition&&transition.finished&&transition.finished.catch)transition.finished.catch(onAbort);
22
+ if(transition&&transition.updateCallbackDone&&transition.updateCallbackDone.catch)transition.updateCallbackDone.catch(onAbort);
23
+ return true;
24
+ }catch(err){
25
+ log('[router] view transition unavailable:',err&&err.message?err.message:err);
26
+ return false;
27
+ }
28
+ }
13
29
 
14
30
  // \u2500\u2500 Progress bar (0\u2192100% at top of viewport) \u2500\u2500
15
31
  // Native loading indicator baked into the router. Starts when navigation
@@ -52,6 +68,40 @@ import"./chunk-xg9xscqz.js";import"./chunk-jbvvb45k.js";import U from"fs";import
52
68
  var prefetching={};
53
69
  var isNavigating=false;
54
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
+
55
105
  // Extract top-level CSS blocks (rules AND @media blocks with nested braces).
56
106
  // The old regex ([^{}]+{[^{}]*}) silently dropped @media blocks because
57
107
  // it couldn't handle nested braces \u2014 causing responsive classes (lg:, md:)
@@ -101,6 +151,7 @@ import"./chunk-xg9xscqz.js";import"./chunk-jbvvb45k.js";import U from"fs";import
101
151
  // Layout group detection \u2014 layouts in the same group share a <main> container.
102
152
  // Only truly different layout groups trigger a full page reload.
103
153
  var layoutCache={};
154
+ var layoutGroupCache={};
104
155
  // Track executed script hashes to prevent redeclaration errors on navigation.
105
156
  // Layout-level scripts (theme, nav setup) execute on initial page load and
106
157
  // should NOT re-execute when navigating to another page with the same layout.
@@ -114,33 +165,63 @@ import"./chunk-xg9xscqz.js";import"./chunk-jbvvb45k.js";import U from"fs";import
114
165
  var text=s.textContent||'';
115
166
  if(text.trim()&&!s.hasAttribute('src'))executedScriptHashes[hashScript(text)]=1;
116
167
  });
117
- function getLayoutGroup(layout){
168
+ function cacheKey(url){
169
+ var u=new URL(url,location.origin);
170
+ return u.pathname+u.search;
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
+ }
178
+ function defaultLayoutGroup(layout){
118
179
  if(!layout)return 'app';
119
- if(layout.indexOf('auth')!==-1||layout.indexOf('guest')!==-1)return 'auth';
120
- return 'app';
180
+ var clean=String(layout).replace(/\\\\/g,'/');
181
+ var parts=clean.split('/').filter(Boolean);
182
+ var idx=parts.lastIndexOf('layouts');
183
+ var part=idx>=0?parts[idx+1]:parts[parts.length-1];
184
+ return part?part.replace(/\\.stx$/i,''):'app';
185
+ }
186
+ function getCurrentLayoutGroup(){
187
+ var meta=document.querySelector('meta[name="stx-layout-group"]');
188
+ if(meta&&meta.getAttribute('content'))return meta.getAttribute('content');
189
+ var layout=document.querySelector('meta[name="stx-layout"]');
190
+ return defaultLayoutGroup(layout?layout.getAttribute('content'):'');
191
+ }
192
+ function getDocLayoutGroup(doc,layout){
193
+ var meta=doc&&doc.querySelector?doc.querySelector('meta[name="stx-layout-group"]'):null;
194
+ return meta&&meta.getAttribute('content')?meta.getAttribute('content'):defaultLayoutGroup(layout||'');
121
195
  }
122
- function checkLayoutChange(newLayout,targetUrl){
196
+ function checkLayoutChange(newLayout,targetUrl,newGroup){
123
197
  var currentLayout=document.querySelector('meta[name="stx-layout"]');
124
198
  var curLayoutName=currentLayout?currentLayout.getAttribute('content'):'';
125
- var curGroup=getLayoutGroup(curLayoutName);
126
- var newGroup=getLayoutGroup(newLayout);
127
- console.log('[router] layout check: current='+curLayoutName+'('+curGroup+') new='+(newLayout||'')+'('+newGroup+')');
199
+ var curGroup=getCurrentLayoutGroup();
200
+ var nextGroup=newGroup||defaultLayoutGroup(newLayout);
201
+ log('[router] layout check: current='+curLayoutName+'('+curGroup+') new='+(newLayout||'')+'('+nextGroup+')');
128
202
  // Different layout GROUP (e.g. app \u2192 auth): full reload
129
- if(curGroup!==newGroup){
130
- console.log('[router] layout group change:',curGroup,'\u2192',newGroup,'\u2014 full reload to:',targetUrl);
203
+ if(curGroup!==nextGroup){
204
+ log('[router] layout group change:',curGroup,'\u2192',nextGroup,'\u2014 full reload to:',targetUrl);
131
205
  return true;
132
206
  }
133
207
  // Same group but different SPECIFIC layout (e.g. layouts/app \u2192 layouts/coach):
134
208
  // full body swap so nav, sidebar, and other layout-level elements update
135
209
  if(curLayoutName && newLayout && curLayoutName!==newLayout){
136
- console.log('[router] layout name change:',curLayoutName,'\u2192',newLayout,'\u2014 full body swap');
210
+ log('[router] layout name change:',curLayoutName,'\u2192',newLayout,'\u2014 full body swap');
137
211
  return true;
138
212
  }
139
213
  return false;
140
214
  }
141
215
 
216
+ function shouldUseFragmentResponse(){
217
+ return containerSel==='main'||containerSel==='[data-stx-content]';
218
+ }
219
+
142
220
  function navigate(url,pushState,force){
143
- console.log('[router] navigate() called:',url,'isNavigating:',isNavigating);
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);
224
+ log('[router] navigate() called:',url,'isNavigating:',isNavigating);
144
225
  if(isNavigating)return;
145
226
  var t=new URL(url,location.origin);
146
227
 
@@ -153,27 +234,36 @@ import"./chunk-xg9xscqz.js";import"./chunk-jbvvb45k.js";import U from"fs";import
153
234
  return;
154
235
  }
155
236
 
156
- if(t.href===location.href&&!t.hash&&!force)return;
237
+ // Skip if user clicks a link to the page they're already on. We
238
+ // deliberately allow popstate (pushState===false) through because
239
+ // the browser has already updated location.href to the popped
240
+ // entry \u2014 without this allowance, hitting back would early-return
241
+ // and the visible page content would be left frozen on the
242
+ // forward-navigation page.
243
+ if(pushState!==false&&t.href===location.href&&!t.hash&&!force)return;
157
244
 
158
245
  isNavigating=true;
159
246
  document.body.classList.add(o.loadingClass);
160
247
  startProgress();
161
248
 
162
249
  var targetHref=t.href;
163
- var targetPath=t.pathname;
250
+ var targetPath=cacheKey(url);
164
251
  var targetHash=t.hash;
165
252
 
166
253
  function done(){isNavigating=false;document.body.classList.remove(o.loadingClass);finishProgress()}
167
254
 
168
255
  if(o.cache&&cache[targetPath]&&!force){
169
- if(checkLayoutChange(layoutCache[targetPath],url)){
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);
259
+ if(checkLayoutChange(layoutCache[targetPath],url,layoutGroupCache[targetPath])){
170
260
  // Layout changed \u2014 fetch full page and do full document swap
171
- console.log('[router] cache hit but layout changed \u2014 fetching full page');
261
+ log('[router] cache hit but layout changed \u2014 fetching full page');
172
262
  fetch(url,{headers:{'Accept':'text/html'}}).then(function(r){
173
263
  if(!r.ok)throw new Error(r.status);
174
264
  return r.text();
175
265
  }).then(function(html){
176
- console.log('[router] full page fetched from cache path, len:',html.length);
266
+ log('[router] full page fetched from cache path, len:',html.length);
177
267
  swap(html,targetPath,pushState,targetHash);
178
268
  }).catch(function(err){
179
269
  console.error('[router] full page fetch error:',err);
@@ -185,29 +275,33 @@ import"./chunk-xg9xscqz.js";import"./chunk-jbvvb45k.js";import U from"fs";import
185
275
  done();
186
276
  }
187
277
  else {
188
- if(force&&cache[targetPath])delete cache[targetPath];
189
- // First fetch as fragment (SPA mode)
190
- fetch(url,{headers:{'X-STX-Router':'true','Accept':'text/html'}}).then(function(r){
278
+ if(force&&cache[targetPath])evictCache(targetPath);
279
+ // First fetch as fragment (SPA mode) when the configured container is
280
+ // the page content. Custom app-shell containers need full documents so
281
+ // the router does not inject a <main> fragment into the wrong element.
282
+ var wantsFragment=shouldUseFragmentResponse();
283
+ fetch(url,{headers:wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'}}).then(function(r){
191
284
  if(!r.ok)throw new Error(r.status);
192
- var isFragment=r.headers.get('X-STX-Fragment')==='true';
285
+ var isFragment=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';
193
286
  var newLayout=r.headers.get('X-STX-Layout')||'';
287
+ var newGroup=r.headers.get('X-STX-Layout-Group')||'';
194
288
  // Layout change? Fetch the FULL page (no X-STX-Router header) and do full document swap
195
- if(isFragment&&checkLayoutChange(newLayout,url)){
196
- console.log('[router] layout change \u2014 fetching full page for document swap');
289
+ if(isFragment&&checkLayoutChange(newLayout,url,newGroup)){
290
+ log('[router] layout change \u2014 fetching full page for document swap');
197
291
  return fetch(url,{headers:{'Accept':'text/html'}}).then(function(fullRes){
198
- console.log('[router] full page fetched:',fullRes.status,'ok:',fullRes.ok);
292
+ log('[router] full page fetched:',fullRes.status,'ok:',fullRes.ok);
199
293
  if(!fullRes.ok)throw new Error(fullRes.status);
200
294
  return fullRes.text().then(function(html){
201
- console.log('[router] full page html length:',html.length);
202
- return{html:html,isFragment:false,layout:newLayout};
295
+ log('[router] full page html length:',html.length);
296
+ return{html:html,isFragment:false,layout:newLayout,layoutGroup:newGroup};
203
297
  });
204
298
  });
205
299
  }
206
- return r.text().then(function(html){return{html:html,isFragment:isFragment,layout:newLayout}});
300
+ return r.text().then(function(html){return{html:html,isFragment:isFragment,layout:newLayout,layoutGroup:newGroup}});
207
301
  }).then(function(result){
208
302
  if(!result)return;
209
303
  if(result.isFragment)result.html='<!--stx-fragment-->'+result.html;
210
- if(o.cache){cache[targetPath]=result.html;layoutCache[targetPath]=result.layout}
304
+ if(o.cache)setCache(targetPath,result.html,result.layout,result.layoutGroup);
211
305
  swap(result.html,targetPath,pushState,targetHash);
212
306
  }).catch(function(err){
213
307
  console.error('[router] fetch error:',err);
@@ -220,8 +314,8 @@ else {
220
314
  var isFragment=html.indexOf('<!--stx-fragment-->')===0;
221
315
  if(isFragment)html=html.slice('<!--stx-fragment-->'.length);
222
316
  var currentContent=getContainer();
223
- console.log('[router] swap: isFragment='+isFragment+' container='+!!currentContent+' htmlLen='+html.length);
224
- if(!currentContent){console.log('[router] no container \u2014 falling back');location.href=url;return}
317
+ log('[router] swap: isFragment='+isFragment+' container='+!!currentContent+' tag='+(currentContent&&currentContent.tagName)+' selector='+containerSel+' htmlLen='+html.length);
318
+ if(!currentContent){log('[router] no container \u2014 falling back');location.href=url;return}
225
319
 
226
320
  // Fragment mode: server returned just the page content (no document wrapper)
227
321
  if(isFragment){
@@ -276,25 +370,47 @@ else {
276
370
  else if(hash){var el=document.querySelector(hash);if(el)el.scrollIntoView({behavior:'smooth'})}
277
371
  window.dispatchEvent(new CustomEvent('stx:navigate',{detail:{url:url}}));
278
372
  // Execute page scripts FIRST \u2014 they define setup functions and set _latestSetup
279
- console.log('[router] frag scripts:', fragScripts.length);
373
+ log('[router] frag scripts:', fragScripts.length);
280
374
  document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});
281
375
  fragScripts.forEach(function(code){
282
- console.log('[router] exec script len:', code.length, 'has __stx_setup:', code.indexOf('__stx_setup')>-1);
283
- var ns=document.createElement('script');
284
- // Wrap in block scope to prevent const/let collisions on re-navigation.
285
- // Without this, navigating away and back throws "Identifier has already
286
- // been declared" because the previous page's top-level const/let are
287
- // still in the global scope.
376
+ log('[router] exec script len:', code.length, 'has __stx_setup:', code.indexOf('__stx_setup')>-1);
377
+ // Skip scripts that were already executed (layout-level partials
378
+ // like theme.stx, stores.stx, nav.stx). Their top-level const/let
379
+ // declarations would throw "Identifier has already been declared"
380
+ // on re-execution. Setup functions (__stx_setup_) must always
381
+ // re-execute because each page has its own setup.
382
+ var h=hashScript(code);
383
+ var isSetup=code.indexOf('__stx_setup_')!==-1;
384
+ // Compute isAlreadyScoped first so the dedup check can exempt
385
+ // self-contained IIFE scripts. data-stx-scoped scope IIFEs
386
+ // (the (function(){ ... })() shape) must re-execute on every
387
+ // navigation. They own their own scope and won't collide
388
+ // with prior runs, AND _cleanupContainer deleted their
389
+ // entry from window.stx._scopes on the way out. Without
390
+ // this exemption, navigating away and back leaves the page
391
+ // leaf with no scope registered: bindIf/@event never re-bind
392
+ // and all :if branches stay visible at once.
393
+ // Mirrors the full-doc swap path's runAlways escape below.
288
394
  var isAlreadyScoped=code.trimStart().charAt(0)==='('||code.trimStart().charAt(0)===';'||code.indexOf('window.stx.mount')>-1;
289
- ns.textContent=isAlreadyScoped?code:'{'+code+'}';
395
+ if(!isSetup&&!isAlreadyScoped&&executedScriptHashes[h]){
396
+ log('[router] skipping already-executed script (hash dedup)');
397
+ return;
398
+ }
399
+ executedScriptHashes[h]=1;
400
+ var ns=document.createElement('script');
401
+ var hasImport=code.indexOf('import ')!==-1;
402
+ if(hasImport){
403
+ ns.type='module';
404
+ }
405
+ ns.textContent=(hasImport||isAlreadyScoped)?code:'{'+code+'}';
290
406
  ns.setAttribute('data-stx-page','');
291
407
  document.body.appendChild(ns);
292
408
  });
293
- console.log('[router] scripts done. _latestSetup:', !!window.stx._latestSetup);
409
+ log('[router] scripts done. _latestSetup:', !!window.stx._latestSetup);
294
410
  // THEN fire stx:load \u2014 now _latestSetup is set and processElement has the right scope
295
411
  window.dispatchEvent(new Event('stx:load'));
296
412
  }
297
- if(o.viewTransitions&&document.startViewTransition){document.startViewTransition(doFragSwap)}
413
+ if(runViewTransition(doFragSwap)){}
298
414
  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)}
299
415
  return;
300
416
  }
@@ -362,12 +478,14 @@ else {
362
478
  if(newBody){
363
479
  var newMeta=doc.querySelector('meta[name="stx-layout"]');
364
480
  var curMeta=document.querySelector('meta[name="stx-layout"]');
365
- var curGroup=getLayoutGroup(curMeta?curMeta.getAttribute('content'):'');
366
- var newGroup=getLayoutGroup(newMeta?newMeta.getAttribute('content'):'');
367
- isLayoutChange=curGroup!==newGroup;
481
+ var curLayout=curMeta?curMeta.getAttribute('content'):'';
482
+ var newLayout=newMeta?newMeta.getAttribute('content'):'';
483
+ var curGroup=getCurrentLayoutGroup();
484
+ var newGroup=getDocLayoutGroup(doc,newLayout);
485
+ isLayoutChange=curGroup!==newGroup||(curLayout&&newLayout&&curLayout!==newLayout);
368
486
  }
369
487
  if(isLayoutChange&&newBody){
370
- console.log('[router] full body swap for layout change');
488
+ log('[router] full body swap for layout change');
371
489
  // Replace entire body content \u2014 layout chrome and all
372
490
  var bodyHTML=newBody.innerHTML.replace(new RegExp('<scr'+'ipt\\\\b[^>]*>[\\\\s\\\\S]*?<\\\\/scr'+'ipt\\\\s*>','gi'),'');
373
491
  document.body.innerHTML=bodyHTML;
@@ -378,6 +496,10 @@ else {
378
496
  var freshMeta=doc.querySelector('meta[name="stx-layout"]');
379
497
  if(oldMeta&&freshMeta)oldMeta.setAttribute('content',freshMeta.getAttribute('content')||'');
380
498
  else if(freshMeta){var m=document.createElement('meta');m.name='stx-layout';m.content=freshMeta.getAttribute('content')||'';document.head.appendChild(m)}
499
+ var oldGroupMeta=document.querySelector('meta[name="stx-layout-group"]');
500
+ var freshGroupMeta=doc.querySelector('meta[name="stx-layout-group"]');
501
+ if(oldGroupMeta&&freshGroupMeta)oldGroupMeta.setAttribute('content',freshGroupMeta.getAttribute('content')||'');
502
+ else if(freshGroupMeta){var gm=document.createElement('meta');gm.name='stx-layout-group';gm.content=freshGroupMeta.getAttribute('content')||'';document.head.appendChild(gm)}
381
503
  // Update container reference for script execution below
382
504
  currentContent=document.querySelector(containerSel)||document.querySelector('main')||document.body;
383
505
  } else {
@@ -408,6 +530,9 @@ else {
408
530
  document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});
409
531
 
410
532
  var scripts=[];
533
+ function addScript(text, runAlways){
534
+ scripts.push({text:text,runAlways:!!runAlways});
535
+ }
411
536
  if(isLayoutChange){
412
537
  // Layout change: collect ALL scripts from the new document body
413
538
  // (layout scripts, component mounts, setup functions \u2014 everything)
@@ -420,7 +545,7 @@ else {
420
545
  // Skip the signals runtime IIFE \u2014 it's already loaded
421
546
  if(text.indexOf("'use strict';var cloakStyle")!==-1)return;
422
547
  if(text.indexOf('__stxRouter')!==-1)return;
423
- scripts.push(text);
548
+ addScript(text,true);
424
549
  });
425
550
  }
426
551
  // Also collect setup functions from <head>
@@ -428,7 +553,7 @@ else {
428
553
  var text=s.textContent||'';
429
554
  if(s.hasAttribute('src'))return;
430
555
  if(!text.trim())return;
431
- if(text.indexOf('__stx_setup_')!==-1)scripts.push(text);
556
+ if(text.indexOf('__stx_setup_')!==-1)addScript(text,true);
432
557
  });
433
558
  } else {
434
559
  // Same layout: collect scripts from the container
@@ -436,26 +561,27 @@ else {
436
561
  var text=s.textContent||'';
437
562
  if(s.hasAttribute('src'))return;
438
563
  if(!text.trim())return;
439
- scripts.push(text);
564
+ addScript(text,true);
440
565
  });
441
- // Collect setup scripts from <head> AND <body> (outside container).
566
+ // Collect page scripts from <head> AND <body> (outside container).
442
567
  // SSG builds place the setup function in <body> before <main>, not
443
568
  // in <head>. Without this, SPA navigation on static sites never
444
569
  // runs the setup and reactive data stays empty.
445
570
  var seenSetups={};
446
- scripts.forEach(function(t){if(t.indexOf('__stx_setup_')!==-1)seenSetups[t.substring(0,80)]=1});
571
+ scripts.forEach(function(entry){var t=entry.text;if(t.indexOf('__stx_setup_')!==-1)seenSetups[t.substring(0,80)]=1});
447
572
  doc.querySelectorAll('script').forEach(function(s){
448
573
  var text=s.textContent||'';
449
574
  if(s.hasAttribute('src'))return;
450
575
  if(!text.trim())return;
451
- if(text.indexOf('__stx_setup_')===-1)return;
452
576
  // Skip signals runtime and router (they contain __stx_setup references but aren't setup functions)
453
577
  if(text.indexOf("'use strict';var cloakStyle")!==-1)return;
454
578
  if(text.indexOf('__stxRouter')!==-1)return;
579
+ if(newContent.contains(s))return;
580
+ if(text.indexOf('__stx_setup_')===-1&&!s.hasAttribute('data-stx-scoped')&&!s.hasAttribute('data-stx-page'))return;
455
581
  var key=text.substring(0,80);
456
582
  if(seenSetups[key])return;
457
583
  seenSetups[key]=1;
458
- scripts.push(text);
584
+ addScript(text,true);
459
585
  });
460
586
  }
461
587
 
@@ -485,7 +611,9 @@ else {
485
611
 
486
612
  // Execute page scripts FIRST \u2014 they define setup functions and set _latestSetup
487
613
  function execScripts(){
488
- scripts.forEach(function(text){
614
+ scripts.forEach(function(entry){
615
+ var text=typeof entry==='string'?entry:entry.text;
616
+ var runAlways=typeof entry==='string'?false:entry.runAlways;
489
617
  // Skip scripts that were already executed (layout-level partials
490
618
  // like theme.stx). Their top-level const/function declarations
491
619
  // would throw "Identifier has already been declared" on re-execution.
@@ -495,7 +623,7 @@ else {
495
623
  var h=hashScript(text);
496
624
  var isSetup=text.indexOf('__stx_setup_')!==-1;
497
625
  var hasImport=text.indexOf('import ')!==-1;
498
- if(!isSetup&&executedScriptHashes[h])return;
626
+ if(!runAlways&&!isSetup&&executedScriptHashes[h])return;
499
627
  executedScriptHashes[h]=1;
500
628
  // Wrap scripts with import statements as modules (Bug 3 fix)
501
629
  var ns=document.createElement('script');
@@ -505,8 +633,11 @@ else {
505
633
  // Wrap in block scope to prevent const/let collisions on re-navigation.
506
634
  // Top-level const/let in <script> tags are global-scoped in browsers
507
635
  // and throw "Identifier has already been declared" on re-execution.
636
+ // Skip the wrap for module scripts \u2014 they get their own scope from
637
+ // ESM, and top-level 'import' is illegal inside a block, which
638
+ // would throw SyntaxError before the script ever runs.
508
639
  var alreadyScoped=text.trimStart().charAt(0)==='('||text.trimStart().charAt(0)===';'||text.indexOf('window.stx.mount')>-1;
509
- ns.textContent=alreadyScoped?text:'{'+text+'}';
640
+ ns.textContent=(hasImport||alreadyScoped)?text:'{'+text+'}';
510
641
  ns.setAttribute('data-stx-page','');
511
642
  document.body.appendChild(ns);
512
643
  });
@@ -522,8 +653,7 @@ else {
522
653
  }
523
654
  }
524
655
 
525
- if(o.viewTransitions&&document.startViewTransition){
526
- document.startViewTransition(doSwap);
656
+ if(runViewTransition(doSwap)){
527
657
  }
528
658
  else {
529
659
  // Fallback fade for browsers without View Transitions API
@@ -545,6 +675,7 @@ else {
545
675
  if(href.startsWith('http')||href.startsWith('#')||href.startsWith('mailto:')||href.startsWith('tel:')||href.startsWith('javascript:'))return false;
546
676
  if(link.target==='_blank')return false;
547
677
  if(link.hasAttribute('data-stx-no-router')||link.hasAttribute('data-no-router')||link.hasAttribute('download'))return false;
678
+ if(href.startsWith('?'))return true;
548
679
  if(href===location.pathname)return false;
549
680
  if(!getContainer())return false;
550
681
  return true;
@@ -565,13 +696,13 @@ else {
565
696
  }
566
697
  if(!link){return}
567
698
  var href=link.getAttribute('href');
568
- console.log('[router] click intercepted:',href,'container:',!!getContainer(),'defaultPrevented:',e.defaultPrevented);
569
- if(!href||href.startsWith('http')||href.startsWith('#')||href.startsWith('mailto:')||href.startsWith('tel:')){console.log('[router] skipped:',href);return}
699
+ log('[router] click intercepted:',href,'container:',!!getContainer(),'defaultPrevented:',e.defaultPrevented);
700
+ if(!href||href.startsWith('http')||href.startsWith('#')||href.startsWith('mailto:')||href.startsWith('tel:')){log('[router] skipped:',href);return}
570
701
  if(link.target==='_blank'||link.hasAttribute('download'))return;
571
702
  e.preventDefault();
572
703
  e.stopPropagation();
573
- console.log('[router] navigating to:',href);
574
- navigate(href);
704
+ log('[router] navigating to:',href);
705
+ navigate(withCurrentLocale(href));
575
706
  },true);
576
707
 
577
708
  // \u2500\u2500 Back/forward \u2500\u2500
@@ -585,16 +716,19 @@ else {
585
716
  if(!e.target||!e.target.closest)return;
586
717
  var link=e.target.closest('[data-stx-link]');
587
718
  if(!link)return;
588
- var href=link.getAttribute('href');
589
- if(cache[href]||prefetching[href])return;
590
- prefetching[href]=true;
591
- fetch(href,{headers:{'X-STX-Router':'true','Accept':'text/html'}}).then(function(r){
592
- var isFrag=r.headers.get('X-STX-Fragment')==='true';
719
+ var href=withCurrentLocale(link.getAttribute('href'));
720
+ var key=cacheKey(href);
721
+ if(cache[key]||prefetching[key])return;
722
+ prefetching[key]=true;
723
+ var wantsFragment=shouldUseFragmentResponse();
724
+ fetch(href,{headers:wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'}}).then(function(r){
725
+ var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';
593
726
  var pLayout=r.headers.get('X-STX-Layout')||'';
594
- return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout}});
727
+ var pGroup=r.headers.get('X-STX-Layout-Group')||'';
728
+ return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}});
595
729
  }).then(function(result){
596
- if(o.cache){cache[href]=result.html;layoutCache[href]=result.layout}
597
- }).catch(function(){}).finally(function(){delete prefetching[href]});
730
+ if(o.cache)setCache(key,result.html,result.layout,result.layoutGroup);
731
+ }).catch(function(){}).finally(function(){delete prefetching[key]});
598
732
  },true);
599
733
  }
600
734
 
@@ -687,16 +821,19 @@ else {
687
821
  navigate:navigate,
688
822
  navigateTo:navigate,
689
823
  prefetch:function(url){
690
- if(!cache[url]){
691
- fetch(url,{headers:{'X-STX-Router':'true'}}).then(function(r){var isFrag=r.headers.get('X-STX-Fragment')==='true';return r.text().then(function(html){return isFrag?'<!--stx-fragment-->'+html:html})}).then(function(html){cache[url]=html}).catch(function(){});
824
+ var key=cacheKey(url);
825
+ if(!cache[key]){
826
+ var wantsFragment=shouldUseFragmentResponse();
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(){});
692
828
  }
693
829
  },
694
- clearCache:function(){for(var k in cache)delete cache[k]},
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},
695
831
  cache:cache,
696
832
  swap:swap,
697
833
  updateNav:updateNav
698
834
  };
699
835
 
836
+ router.__rev=ROUTER_REV;
700
837
  window.__stxRouter=router;
701
838
  window.stxRouter=router;
702
839
  if(window.stx)window.stx.router=router;
@@ -711,4 +848,4 @@ else {
711
848
 
712
849
  if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);else init();
713
850
  })();
714
- `}export{r as setAppUrl,AB as runMiddleware,BB as route,P as resolveLayoutChain,GB as resetRoutes,q as registerMiddleware,w as patternToRegex,KB as navigateTo,m as matchRoute,ZB as loadMiddlewareFromDirectory,WB as hasMiddleware,QB as getRouterScript,YB as getMiddlewareNames,f as getMiddleware,S as generateRouteTypes,b as generateRouteManifest,t as formatRoutes,o as findErrorPage,v as filePathToPattern,e as defineRoutes,a as defineRoute,VB as defineMiddleware,y as createStorageManager,k as createServerCookieManager,i as createRouter,$B as createRouteLocation,jB as createMiddlewareContext,x as createClientCookieManager,XB as clearMiddleware,OB as abortNavigation,C as Router};
851
+ `}export{IB as setAppUrl,UB as runMiddleware,OB as route,y as resolveLayoutChain,VB as resetRoutes,M as registerMiddleware,k as patternToRegex,YB as navigateTo,o as matchRoute,jB as loadMiddlewareFromDirectory,AB as hasMiddleware,zB as getRouterScript,QB as getMiddlewareNames,c as getMiddleware,w as generateRouteTypes,D as generateRouteManifest,BB as formatRoutes,e as findErrorPage,P as filePathToPattern,v as extractLayoutMetadata,b as deriveLayoutGroup,KB as defineRoutes,JB as defineRoute,$B as defineMiddleware,h as createStorageManager,f as createServerCookieManager,a as createRouter,_B as createRouteLocation,EB as createMiddlewareContext,g as createClientCookieManager,GB as clearMiddleware,ZB as abortNavigation,S as Router};