@stacksjs/stx 0.2.343 → 0.2.345

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 (169) hide show
  1. package/dist/binding-manifest.d.ts +32 -0
  2. package/dist/binding-manifest.js +64 -0
  3. package/dist/{chunk-zr1x54bt.js → chunk-05zfez2g.js} +1 -1
  4. package/dist/{chunk-jagf7h9y.js → chunk-0k1mtwby.js} +1 -1
  5. package/dist/{chunk-jh6f1a9y.js → chunk-1c9453w0.js} +1 -1
  6. package/dist/chunk-1qyg1q6d.js +2 -0
  7. package/dist/chunk-1t4ece3v.js +2 -0
  8. package/dist/{chunk-7zxv2631.js → chunk-1vt1h19d.js} +1 -1
  9. package/dist/chunk-1vtdege3.js +2 -0
  10. package/dist/{chunk-stqm4brp.js → chunk-1vv9g6cj.js} +1 -1
  11. package/dist/{chunk-p3awq80d.js → chunk-1yh6pq6b.js} +1 -1
  12. package/dist/{chunk-57dxdvjh.js → chunk-223makqc.js} +1 -1
  13. package/dist/chunk-2fhk4e68.js +2 -0
  14. package/dist/{chunk-wyhb3nb0.js → chunk-34f8c48f.js} +1 -1
  15. package/dist/chunk-3zm4hgcf.js +2 -0
  16. package/dist/{chunk-xgw269cf.js → chunk-4spaszfn.js} +286 -286
  17. package/dist/chunk-57t402mn.js +2 -0
  18. package/dist/{chunk-wdaf98ya.js → chunk-59906baj.js} +1 -1
  19. package/dist/{chunk-z9hb80mk.js → chunk-64n10kpa.js} +1 -1
  20. package/dist/chunk-66tgkvqw.js +2 -0
  21. package/dist/{chunk-daxr6p6q.js → chunk-6fwp7gee.js} +1 -1
  22. package/dist/{chunk-1bfvdjpy.js → chunk-6h3r8v55.js} +1 -1
  23. package/dist/{chunk-safmgrq5.js → chunk-6wmjwbq9.js} +1 -1
  24. package/dist/{chunk-53sjbp3z.js → chunk-72x62kch.js} +2 -2
  25. package/dist/{chunk-amfq5rnz.js → chunk-72yrrggn.js} +2 -2
  26. package/dist/{chunk-2853ye06.js → chunk-77p1kjg2.js} +2 -2
  27. package/dist/chunk-7g5vk4c3.js +2 -0
  28. package/dist/{chunk-7mc1k1tq.js → chunk-7h9fajgs.js} +1 -1
  29. package/dist/{chunk-gvfgrve8.js → chunk-7m9qk7da.js} +1 -1
  30. package/dist/{chunk-3d3x7dwy.js → chunk-7qp3zphs.js} +1 -1
  31. package/dist/{chunk-grnmr3y1.js → chunk-86c461ca.js} +1 -1
  32. package/dist/chunk-86q2h34b.js +2 -0
  33. package/dist/{chunk-7r79j1hs.js → chunk-89mqnh67.js} +1 -1
  34. package/dist/{chunk-5hq25yky.js → chunk-8cc4vdkh.js} +1 -1
  35. package/dist/chunk-8rhjt6fh.js +2 -0
  36. package/dist/{chunk-45ydb29q.js → chunk-8v2k21n4.js} +1 -1
  37. package/dist/{chunk-aqxwwq6w.js → chunk-8y1ve8tc.js} +2 -2
  38. package/dist/{chunk-ag8gdk51.js → chunk-99b5tfxc.js} +1 -1
  39. package/dist/{chunk-4vd0rqfs.js → chunk-9jbssgem.js} +197 -88
  40. package/dist/{chunk-s60eccqk.js → chunk-9nrgjtah.js} +1 -1
  41. package/dist/{chunk-y5sxa2sq.js → chunk-9qtf2b1p.js} +1 -1
  42. package/dist/chunk-aapqf6bj.js +2 -0
  43. package/dist/{chunk-k8skvm0d.js → chunk-at622hg0.js} +1 -1
  44. package/dist/{chunk-1qtd4ex2.js → chunk-avwywzsj.js} +2 -2
  45. package/dist/{chunk-a0ajg9z5.js → chunk-b5hank5e.js} +1 -1
  46. package/dist/chunk-b5mnam29.js +2 -0
  47. package/dist/{chunk-4fafxr8d.js → chunk-bk1zb1xp.js} +1 -1
  48. package/dist/{chunk-hrdd26r4.js → chunk-bqdfbwdf.js} +1 -1
  49. package/dist/{chunk-c7qdnc5d.js → chunk-c30qnb6z.js} +1 -1
  50. package/dist/chunk-c4tf1vpy.js +2 -0
  51. package/dist/{chunk-g0dbtyk7.js → chunk-c8e2deyp.js} +1 -1
  52. package/dist/chunk-cypg8qwn.js +2 -0
  53. package/dist/{chunk-fbmjw0bh.js → chunk-d86q27j8.js} +1 -1
  54. package/dist/{chunk-dj5mqnp1.js → chunk-dgv43ccd.js} +1 -1
  55. package/dist/{chunk-em8cr7wy.js → chunk-dsztk2kp.js} +1 -1
  56. package/dist/{chunk-mwvxhv0r.js → chunk-dxkjvajv.js} +1 -1
  57. package/dist/{chunk-4pg6wbt6.js → chunk-e631svvs.js} +1 -1
  58. package/dist/chunk-e7gs18rc.js +2 -0
  59. package/dist/{chunk-h6y99nak.js → chunk-e7np0h2q.js} +1 -1
  60. package/dist/{chunk-0s3ry262.js → chunk-ejqpq0wc.js} +1 -1
  61. package/dist/chunk-ek2m0kkw.js +2 -0
  62. package/dist/chunk-eqzc88et.js +2 -0
  63. package/dist/chunk-erb294sv.js +2 -0
  64. package/dist/{chunk-36j9qset.js → chunk-esjcyxa9.js} +2 -2
  65. package/dist/{chunk-5azeswj2.js → chunk-evfr2zgg.js} +1 -1
  66. package/dist/{chunk-e53pwshq.js → chunk-fp868yhg.js} +1 -1
  67. package/dist/{chunk-5zemhhwe.js → chunk-gsc2vds7.js} +1 -1
  68. package/dist/{chunk-q6825wwz.js → chunk-gvemapw4.js} +1 -1
  69. package/dist/chunk-h1vax4wg.js +2 -0
  70. package/dist/chunk-hke0k33t.js +2 -0
  71. package/dist/{chunk-zcytk5w9.js → chunk-jekrtm2j.js} +1 -1
  72. package/dist/{chunk-pwhf5kte.js → chunk-jnbg3e4r.js} +1 -1
  73. package/dist/{chunk-brjfzbq9.js → chunk-kfkf9v2d.js} +1 -1
  74. package/dist/chunk-kq30wqp6.js +2 -0
  75. package/dist/{chunk-s7qbba8r.js → chunk-kqzpmwx2.js} +10 -10
  76. package/dist/{chunk-0dpgh7a6.js → chunk-kw9cgyfc.js} +1 -1
  77. package/dist/{chunk-0fp16c1x.js → chunk-m2j5skez.js} +1 -1
  78. package/dist/{chunk-vj3nx26v.js → chunk-mph46gkj.js} +1 -1
  79. package/dist/chunk-mr8ccsnm.js +2 -0
  80. package/dist/{chunk-1h1c2048.js → chunk-msywnv5n.js} +1 -1
  81. package/dist/{chunk-649dfqqz.js → chunk-n5657hm3.js} +1 -1
  82. package/dist/{chunk-t00ga194.js → chunk-njx93b4z.js} +1 -1
  83. package/dist/{chunk-m0xaw0qw.js → chunk-nm60wh6r.js} +1 -1
  84. package/dist/{chunk-8ah5mhst.js → chunk-nqz501eq.js} +2 -2
  85. package/dist/{chunk-hq3phk1a.js → chunk-nrt99ptw.js} +1 -1
  86. package/dist/{chunk-62vng8g8.js → chunk-pajk5680.js} +2 -2
  87. package/dist/{chunk-ey1tsk22.js → chunk-ppzz953j.js} +1 -1
  88. package/dist/{chunk-7yfk9393.js → chunk-qkdcbeaw.js} +1 -1
  89. package/dist/chunk-qr3zcm87.js +2 -0
  90. package/dist/chunk-r38xq7fp.js +33 -0
  91. package/dist/{chunk-tqw1yean.js → chunk-rwwfzkz4.js} +1 -1
  92. package/dist/{chunk-rns7wjy9.js → chunk-rzda6bd6.js} +1 -1
  93. package/dist/{chunk-rzg9pars.js → chunk-san1eq0r.js} +2 -2
  94. package/dist/{chunk-eh3meyd3.js → chunk-tc4pj651.js} +2 -2
  95. package/dist/{chunk-6g8rvnhc.js → chunk-v6e35j4g.js} +1 -1
  96. package/dist/{chunk-wnmegbw0.js → chunk-v7aa7v85.js} +1 -1
  97. package/dist/{chunk-e6rxvz3p.js → chunk-vhg91hnw.js} +1 -1
  98. package/dist/{chunk-k3dtvay1.js → chunk-vhta1be7.js} +1 -1
  99. package/dist/{chunk-tz68qv47.js → chunk-w5j9tbsk.js} +1 -1
  100. package/dist/{chunk-ec18ftcg.js → chunk-wj21f1e0.js} +1 -1
  101. package/dist/{chunk-12qteqk8.js → chunk-ws0282jr.js} +1 -1
  102. package/dist/chunk-x054qzma.js +2 -0
  103. package/dist/{chunk-q9ckfwhh.js → chunk-xj6bcwqa.js} +1 -1
  104. package/dist/chunk-xq0e2y4x.js +2 -0
  105. package/dist/chunk-xrjx7b2v.js +2 -0
  106. package/dist/{chunk-ay336sqb.js → chunk-xvh1k1a4.js} +1 -1
  107. package/dist/{chunk-vaggjvsm.js → chunk-y2f41kaq.js} +1 -1
  108. package/dist/{chunk-1zav2sf1.js → chunk-y6xzr30x.js} +1 -1
  109. package/dist/chunk-ybx8n794.js +2 -0
  110. package/dist/{chunk-yefnrr5z.js → chunk-yca761bm.js} +1 -1
  111. package/dist/{chunk-kgbz86sa.js → chunk-yg5r7bnm.js} +2 -2
  112. package/dist/{chunk-kbah2k8e.js → chunk-z8wb7kbj.js} +2 -2
  113. package/dist/chunk-zkxnarrh.js +2 -0
  114. package/dist/{chunk-hzbty7hz.js → chunk-zmazqcb8.js} +1 -1
  115. package/dist/chunk-zp14ksx1.js +2 -0
  116. package/dist/chunk-zwr4bn6a.js +2 -0
  117. package/dist/cli.js +14 -14
  118. package/dist/craft-entry.js +1 -1
  119. package/dist/craft.js +1 -1
  120. package/dist/database.js +1 -1
  121. package/dist/expressions.d.ts +1 -1
  122. package/dist/expressions.js +15 -3
  123. package/dist/includes.js +2 -2
  124. package/dist/pwa.js +2 -2
  125. package/dist/runtime-globals.d.ts +22 -0
  126. package/dist/runtime-globals.js +46 -0
  127. package/dist/signals.js +193 -84
  128. package/dist/slots.js +3 -3
  129. package/dist/ssg.js +2 -2
  130. package/dist/theme-tokens.d.ts +59 -0
  131. package/dist/theme-tokens.js +83 -17
  132. package/dist/ts-css-config.js +4 -2
  133. package/dist/utils.d.ts +10 -0
  134. package/dist/utils.js +77 -12
  135. package/dist/variable-extractor.d.ts +1 -1
  136. package/dist/variable-extractor.js +23 -3
  137. package/dist/visual-testing.js +1 -1
  138. package/package.json +5 -5
  139. package/dist/chunk-160g7ynw.js +0 -2
  140. package/dist/chunk-16hr5y08.js +0 -2
  141. package/dist/chunk-3h97y3n3.js +0 -2
  142. package/dist/chunk-3j915h2d.js +0 -2
  143. package/dist/chunk-4hkg6kx4.js +0 -2
  144. package/dist/chunk-57494khk.js +0 -2
  145. package/dist/chunk-5xavmr4t.js +0 -2
  146. package/dist/chunk-6rsfkms1.js +0 -2
  147. package/dist/chunk-6xs80tka.js +0 -32
  148. package/dist/chunk-905j49fe.js +0 -2
  149. package/dist/chunk-9czj483k.js +0 -2
  150. package/dist/chunk-9kv0qavn.js +0 -2
  151. package/dist/chunk-bcz8yp2g.js +0 -2
  152. package/dist/chunk-bxefsc7k.js +0 -2
  153. package/dist/chunk-c4kv3r8t.js +0 -2
  154. package/dist/chunk-fytemc96.js +0 -2
  155. package/dist/chunk-g7gc0ten.js +0 -2
  156. package/dist/chunk-gqsxxd81.js +0 -2
  157. package/dist/chunk-gx9z0wmm.js +0 -2
  158. package/dist/chunk-jnxzzzm4.js +0 -2
  159. package/dist/chunk-jy6jwxhs.js +0 -2
  160. package/dist/chunk-khqn40gv.js +0 -2
  161. package/dist/chunk-mhbzrh5y.js +0 -2
  162. package/dist/chunk-p8anntjb.js +0 -2
  163. package/dist/chunk-pwe51myj.js +0 -2
  164. package/dist/chunk-rqqdvnp5.js +0 -2
  165. package/dist/chunk-w1mbrq1g.js +0 -2
  166. package/dist/chunk-w84qe2m6.js +0 -2
  167. package/dist/chunk-wjm7hv5e.js +0 -2
  168. package/dist/chunk-yr7s02f7.js +0 -2
  169. package/dist/chunk-zq3tm2c2.js +0 -2
package/dist/slots.js CHANGED
@@ -320,9 +320,9 @@ export async function processSlots(template, parsedSlots, context) {
320
320
  }
321
321
  return await renderSlot(slotDef, props, defaultFallback, context);
322
322
  });
323
- const defaultSlotRegex = /<slot\s*(?:\/>|>([\s\S]*?)<\/slot>)/gi;
324
- result = result.replace(defaultSlotRegex, (match, defaultContent) => {
325
- if (match.includes("name="))
323
+ const defaultSlotRegex = /<slot\b((?:"[^"]*"|'[^']*'|[^>"'])*?)\s*(?:\/>|>([\s\S]*?)<\/slot>)/gi;
324
+ result = result.replace(defaultSlotRegex, (match, attrs, defaultContent) => {
325
+ if (/\bname\s*=/.test(attrs || ""))
326
326
  return match;
327
327
  const fallback = defaultContent?.trim() || "";
328
328
  return parsedSlots.default || fallback;
package/dist/ssg.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{Bk as Fq,wk as Uq,xk as Iq}from"./chunk-wnmegbw0.js";import{Ck as Eq,Dk as Tq,Ek as Dq}from"./chunk-c0n802j1.js";import{Zk as _q,al as zq,bl as Aq}from"./chunk-57dxdvjh.js";import"./chunk-2853ye06.js";import"./chunk-fvsefztj.js";import{Jp as P,Sm as Oq,io as l,lo as Hq,rr as n}from"./chunk-xgw269cf.js";import"./chunk-aqxwwq6w.js";import"./chunk-t00ga194.js";import"./chunk-hrdd26r4.js";import"./chunk-7r79j1hs.js";import"./chunk-ay336sqb.js";import{ht as Xq}from"./chunk-6xs80tka.js";import{mt as d}from"./chunk-vaggjvsm.js";import"./chunk-q9ckfwhh.js";import"./chunk-kbah2k8e.js";import"./chunk-k3dtvay1.js";import{Tt as Bq}from"./chunk-eh3meyd3.js";import"./chunk-rzg9pars.js";import"./chunk-1qtd4ex2.js";import"./chunk-ec18ftcg.js";import"./chunk-fbmjw0bh.js";import"./chunk-53sjbp3z.js";import{lw as Yq,mw as Vq,nw as jq,ow as Gq,pw as Lq,qw as Nq}from"./chunk-kgbz86sa.js";import"./chunk-s60eccqk.js";import{Hw as y}from"./chunk-0s3ry262.js";import O from"fs";import j from"path";import{createHash as xq}from"crypto";function yq(q){try{return new Bun.Transpiler({loader:"js",minifyWhitespace:!0}).transformSync(q).replace(/\}(var |let |const |function )/g,"};$1").trim()}catch{return q}}var Pq=/<script\s+type="stx\/island"\s+data-stx-island="([^"]+)"([^>]*)>([\s\S]*?)<\/script>/g;function Cq(q){return Bun.hash(q).toString(16)}function Qq(q,Z={}){if(!q.includes('type="stx/island"'))return{html:q,chunks:[]};let $=new Map,Q=q.replace(Pq,(J,W,z,A)=>{let G=A.trim();if(!G)return J;if(/\bdata-stx-src=/.test(z))return J;if(Z.minify)G=yq(G);let X=Cq(G);if(!$.has(X))$.set(X,G);let B=Z.integrity?` data-stx-integrity="sha384-${xq("sha384").update(G).digest("base64")}"`:"";return`<script type="stx/island" data-stx-island="${W}" data-stx-src="/_stx/islands/${X}.js"${B}${z}></script>`}),K=Array.from($,([J,W])=>({hash:J,code:W}));return{html:Q,chunks:K}}var gq=new Set(["visible","idle"]);function Zq(q){if(!q.includes("data-stx-src="))return q;let Z=new Map,$=/data-stx-scope="([^"]+)"[^>]*?stx-hydrate="([^"]+)"/g,Q;while((Q=$.exec(q))!==null)Z.set(Q[1],Q[2]);let K=new Set,J=/data-stx-island="([^"]+)"[^>]*?data-stx-src="([^"]+)"/g,W;while((W=J.exec(q))!==null)if(gq.has(Z.get(W[1])||""))K.add(W[2]);let z=q.lastIndexOf("</head>");if(K.size===0||z===-1)return q;let A=Array.from(K).filter((G)=>!q.includes(`rel="prefetch" href="${G}"`)).map((G)=>`<link rel="prefetch" href="${G}" as="script">`).join("");if(!A)return q;return q.slice(0,z)+A+q.slice(z)}var fq=["STX_SITE_URL","SITE_URL","URL","VERCEL_URL","CF_PAGES_URL"];function h(q){let Z=q.trim();if(!Z)return null;let $=/^https?:\/\//i.test(Z)?Z:`https://${Z}`;try{return new URL($).origin+new URL($).pathname.replace(/\/$/,"")}catch{return null}}function mq(q){for(let Z of fq){let $=q[Z];if(!$)continue;let Q=h($);if(Q)return{url:Q,source:"env",via:Z}}return null}function $q(q){if(!q||typeof q!=="object")return null;let Z=q,$=Z.site?.url??Z.url??Z.app?.url;return typeof $==="string"?h($):null}async function dq(q){let{existsSync:Z}=await import("fs"),$=await import("path");for(let Q of["site.config.ts","site.config.js",".config/site.config.ts"]){let K=$.resolve(q,Q);if(!Z(K))continue;try{let J=await import(K),W=$q(J.default??J);if(W)return W}catch{}}return null}async function Kq(q={}){let Z=q.cwd??process.cwd(),$=q.env??process.env;if(q.explicit){let W=h(q.explicit);if(W)return{url:W,source:"option"}}let Q=$q(q.stxConfig);if(Q)return{url:Q,source:"stx.config",via:"site.url"};let K=await dq(Z);if(K)return{url:K,source:"site.config",via:"url"};let J=mq($);if(J)return J;return{url:"http://localhost",source:"fallback"}}function Wq(){return["No site URL configured \u2014 sitemap.xml and robots.txt will point at http://localhost."," Set one in stx.config.ts (site: { url: 'https://example.com' }), in site.config.ts,"," via --domain, or with SITE_URL in the build environment."].join(`
2
+ import{Bk as Fq,wk as Uq,xk as Iq}from"./chunk-v7aa7v85.js";import{Ck as Eq,Dk as Tq,Ek as Dq}from"./chunk-c0n802j1.js";import{Zk as _q,al as zq,bl as Aq}from"./chunk-223makqc.js";import"./chunk-77p1kjg2.js";import"./chunk-fvsefztj.js";import{Jp as P,Sm as Oq,io as l,lo as Hq,sr as n}from"./chunk-4spaszfn.js";import"./chunk-8y1ve8tc.js";import"./chunk-njx93b4z.js";import"./chunk-bqdfbwdf.js";import"./chunk-89mqnh67.js";import"./chunk-xvh1k1a4.js";import{it as Xq}from"./chunk-r38xq7fp.js";import{nt as d}from"./chunk-y2f41kaq.js";import"./chunk-xj6bcwqa.js";import"./chunk-z8wb7kbj.js";import"./chunk-vhta1be7.js";import{Ut as Bq}from"./chunk-tc4pj651.js";import"./chunk-san1eq0r.js";import"./chunk-avwywzsj.js";import"./chunk-wj21f1e0.js";import"./chunk-d86q27j8.js";import"./chunk-72x62kch.js";import{rw as Yq,sw as Vq,tw as jq,uw as Gq,vw as Lq,ww as Nq}from"./chunk-yg5r7bnm.js";import"./chunk-9nrgjtah.js";import{Nw as y}from"./chunk-ejqpq0wc.js";import O from"fs";import j from"path";import{createHash as xq}from"crypto";function yq(q){try{return new Bun.Transpiler({loader:"js",minifyWhitespace:!0}).transformSync(q).replace(/\}(var |let |const |function )/g,"};$1").trim()}catch{return q}}var Pq=/<script\s+type="stx\/island"\s+data-stx-island="([^"]+)"([^>]*)>([\s\S]*?)<\/script>/g;function Cq(q){return Bun.hash(q).toString(16)}function Qq(q,Z={}){if(!q.includes('type="stx/island"'))return{html:q,chunks:[]};let $=new Map,Q=q.replace(Pq,(J,W,z,A)=>{let G=A.trim();if(!G)return J;if(/\bdata-stx-src=/.test(z))return J;if(Z.minify)G=yq(G);let X=Cq(G);if(!$.has(X))$.set(X,G);let B=Z.integrity?` data-stx-integrity="sha384-${xq("sha384").update(G).digest("base64")}"`:"";return`<script type="stx/island" data-stx-island="${W}" data-stx-src="/_stx/islands/${X}.js"${B}${z}></script>`}),K=Array.from($,([J,W])=>({hash:J,code:W}));return{html:Q,chunks:K}}var gq=new Set(["visible","idle"]);function Zq(q){if(!q.includes("data-stx-src="))return q;let Z=new Map,$=/data-stx-scope="([^"]+)"[^>]*?stx-hydrate="([^"]+)"/g,Q;while((Q=$.exec(q))!==null)Z.set(Q[1],Q[2]);let K=new Set,J=/data-stx-island="([^"]+)"[^>]*?data-stx-src="([^"]+)"/g,W;while((W=J.exec(q))!==null)if(gq.has(Z.get(W[1])||""))K.add(W[2]);let z=q.lastIndexOf("</head>");if(K.size===0||z===-1)return q;let A=Array.from(K).filter((G)=>!q.includes(`rel="prefetch" href="${G}"`)).map((G)=>`<link rel="prefetch" href="${G}" as="script">`).join("");if(!A)return q;return q.slice(0,z)+A+q.slice(z)}var fq=["STX_SITE_URL","SITE_URL","URL","VERCEL_URL","CF_PAGES_URL"];function h(q){let Z=q.trim();if(!Z)return null;let $=/^https?:\/\//i.test(Z)?Z:`https://${Z}`;try{return new URL($).origin+new URL($).pathname.replace(/\/$/,"")}catch{return null}}function mq(q){for(let Z of fq){let $=q[Z];if(!$)continue;let Q=h($);if(Q)return{url:Q,source:"env",via:Z}}return null}function $q(q){if(!q||typeof q!=="object")return null;let Z=q,$=Z.site?.url??Z.url??Z.app?.url;return typeof $==="string"?h($):null}async function dq(q){let{existsSync:Z}=await import("fs"),$=await import("path");for(let Q of["site.config.ts","site.config.js",".config/site.config.ts"]){let K=$.resolve(q,Q);if(!Z(K))continue;try{let J=await import(K),W=$q(J.default??J);if(W)return W}catch{}}return null}async function Kq(q={}){let Z=q.cwd??process.cwd(),$=q.env??process.env;if(q.explicit){let W=h(q.explicit);if(W)return{url:W,source:"option"}}let Q=$q(q.stxConfig);if(Q)return{url:Q,source:"stx.config",via:"site.url"};let K=await dq(Z);if(K)return{url:K,source:"site.config",via:"url"};let J=mq($);if(J)return J;return{url:"http://localhost",source:"fallback"}}function Wq(){return["No site URL configured \u2014 sitemap.xml and robots.txt will point at http://localhost."," Set one in stx.config.ts (site: { url: 'https://example.com' }), in site.config.ts,"," via --domain, or with SITE_URL in the build environment."].join(`
3
3
  `)}class wq{cacheDir;cache=new Map;loaded=!1;salt;constructor(q,Z=""){this.cacheDir=q,this.salt=Z}async load(){if(this.loaded)return;let q=j.join(this.cacheDir,"build-cache.json");if(await Bun.file(q).exists())try{let Z=JSON.parse(await Bun.file(q).text());for(let[$,Q]of Object.entries(Z))this.cache.set($,Q)}catch{}this.loaded=!0}async save(){await O.promises.mkdir(this.cacheDir,{recursive:!0});let q=j.join(this.cacheDir,"build-cache.json"),Z={};for(let[$,Q]of this.cache)Z[$]=Q;await Bun.write(q,JSON.stringify(Z))}getHash(q,Z){let $=O.statSync(q),Q=`${this.salt}|${q}:${$.mtimeMs}`;for(let J of Z)if(O.existsSync(J)){let W=O.statSync(J);Q+=`:${J}:${W.mtimeMs}`}let K=0;for(let J=0;J<Q.length;J++){let W=Q.charCodeAt(J);K=(K<<5)-K+W,K=K&K}return Math.abs(K).toString(36)}get(q,Z){let $=this.cache.get(q);if(!$)return null;let Q=this.getHash(Z,$.dependencies??[]);if($.hash!==Q)return this.cache.delete(q),null;return $.html}set(q,Z,$,Q){let K=this.getHash(Z,$);this.cache.set(q,{hash:K,html:Q,generatedAt:Date.now(),dependencies:$})}}class Rq{cacheDir;constructor(q){this.cacheDir=q}getFilePath(q){let Z=q.replace(/[^a-zA-Z0-9]/g,"_")+".json";return j.join(this.cacheDir,"isr",Z)}async get(q){let Z=this.getFilePath(q);if(!await Bun.file(Z).exists())return null;try{return JSON.parse(await Bun.file(Z).text())}catch{return null}}async set(q,Z){let $=this.getFilePath(q);await O.promises.mkdir(j.dirname($),{recursive:!0}),await Bun.write($,JSON.stringify(Z))}async invalidate(q){let Z=this.getFilePath(q);if(await Bun.file(Z).exists())await O.promises.unlink(Z)}async invalidatePattern(q){let Z=j.join(this.cacheDir,"isr");if(!O.existsSync(Z))return;let $=await O.promises.readdir(Z);for(let Q of $){let K=Q.replace(/_/g,"/").replace(".json","");if(q.test(K))await O.promises.unlink(j.join(Z,Q))}}async needsRevalidation(q){let Z=await this.get(q);if(!Z)return!0;return Date.now()>Z.revalidateAfter}}async function pq(q){try{let $=(await Bun.file(q).text()).match(/<script\s+server\b[^>]*>([\s\S]*?)<\/script>/i);if(!$)return null;let Q=$[1];if(!Q.includes("getStaticPaths"))return null;let K=Q.match(/(?:export\s+)?(?:async\s+)?function\s+getStaticPaths\s*\([^)]*\)\s*\{([\s\S]*?)\n[\t ]*\}/);if(!K)return null;return await Function(`
4
4
  return (async function getStaticPaths() {
5
5
  ${K[1]}
@@ -41,7 +41,7 @@ ${q.map((Q)=>{let K=new URL(Q.loc,Z).href,J=` <url>
41
41
  ${$.join(`
42
42
  `)}
43
43
  </channel>
44
- </rss>`}async function aq(q={}){let Z=Date.now();Aq();let $=await n(process.cwd()),Q=$.build||{},K=await Kq({explicit:q.domain||Q.domain,stxConfig:$});if(K.source==="fallback"&&(q.sitemap??Q.sitemap??!0))console.warn(Wq());let J={router:q.router??$?.router??{},pagesDir:q.pagesDir||Q.pagesDir||"pages",outputDir:q.outputDir||Q.outputDir||"dist",baseUrl:q.baseUrl||Q.baseUrl||"/",domain:K.url,revalidate:q.revalidate??Q.revalidate??!1,sitemap:q.sitemap??Q.sitemap??!0,sitemapExclude:q.sitemapExclude||Q.sitemapExclude||[],robots:q.robots??Q.robots??!0,rss:q.rss??Q.rss??!1,concurrency:q.concurrency||Q.concurrency||10,cache:q.cache??Q.cache??!0,cacheDir:q.cacheDir||Q.cacheDir||d(process.cwd(),"ssg-cache"),contentLoaders:q.contentLoaders||[],hooks:q.hooks||{},minify:q.minify??Q.minify??!0,generate404:q.generate404??Q.generate404??!0,publicDir:q.publicDir||Q.publicDir||"public",trailingSlash:q.trailingSlash??Q.trailingSlash??!1,cleanOutput:q.cleanOutput??Q.cleanOutput??!0,cacheSalt:q.cacheSalt??"",partialsDir:q.partialsDir||$?.partialsDir||"",componentsDir:q.componentsDir||$?.componentsDir||"",failOnIncludeError:q.failOnIncludeError??!0,chunkIslands:q.chunkIslands===!0,prefetchIslands:q.prefetchIslands===!0,integrityIslands:q.integrityIslands===!0},W={totalPages:0,successCount:0,failedCount:0,cachedCount:0,buildTime:0,pages:[],errors:[]},z=new Set,A=0,G={},X={};await J.hooks.onBuildStart?.();try{if(J.cleanOutput&&O.existsSync(J.outputDir))await O.promises.rm(J.outputDir,{recursive:!0});await O.promises.mkdir(J.outputDir,{recursive:!0});let L=J.cacheSalt;if(!L){let{resolveUserCssConfig:Y,fingerprintConfig:N}=await import("./chunk-pwhf5kte.js");try{L=N(await Y(process.cwd()))}catch{}}let{prepareImageDelivery:_}=await import("./chunk-4pg6wbt6.js"),F=await _(J.publicDir,J.outputDir);if(F.count>0)console.log(`Optimized ${F.count} image(s) for responsive delivery`),L=[L,F.fingerprint].filter(Boolean).join(":");let C=new wq(J.cacheDir,L);if(J.cache)await C.load();try{let{generateCss:Y}=await import("./chunk-pwhf5kte.js");await Y('<div class="bg-black"></div>')}catch{}Yq();let kq=$?.routeMiddleware?.dir||"middleware";await jq(process.cwd(),kq);let p=Vq();if(p.length>0)console.log(`Loaded ${p.length} middleware: ${p.join(", ")}`);let bq=$?.routeMiddleware?.global||[];Fq($?.layoutsDir,d(process.cwd()));let i=l(".",{pagesDir:J.pagesDir,pagesDirs:$?._layerPageDirs});console.log(`Found ${i.length} routes`);let g=[];for(let Y of i){let N=await uq(Y);for(let H of N)g.push({route:Y,url:H.url,params:H.params,props:H.props,revalidate:H.revalidate??J.revalidate})}W.totalPages=g.length,console.log(`Generating ${W.totalPages} pages...`);let b=[],s=[];for(let Y=0;Y<g.length;Y+=J.concurrency)s.push(g.slice(Y,Y+J.concurrency));for(let Y of s)await Promise.all(Y.map(async(N)=>{let{route:H,url:V,params:S,props:Sq,revalidate:a}=N;try{await J.hooks.onPageStart?.(V);let w=await Bun.file(H.filePath).text(),R=JJ(w),vq=R?.middleware?Array.isArray(R.middleware)?R.middleware:[R.middleware]:[],t=[...bq,...vq];if(t.length>0){let D=Gq(V,S,R||{}),M=Lq(D,null,void 0,{isServer:!0}),E=await Nq(t,M);if(E.redirect){let U=typeof E.redirect==="string"?E.redirect:E.redirect.path||E.redirect,x=QJ(String(U),V),k=ZJ(V,J);await O.promises.mkdir(j.dirname(k),{recursive:!0}),await Bun.write(k,x),W.successCount++,W.pages.push({route:V,outputPath:k,size:Buffer.byteLength(x),cached:!1}),b.push({loc:V,lastmod:new Date().toISOString()}),console.log(` \u21B3 ${V} \u2192 redirect to ${U}`);return}if(E.abort){let U=typeof E.abort==="string"?E.abort:E.abort?.error?.message||"aborted";console.warn(` \u21B3 ${V} \u2014 middleware aborted: ${U}`);return}}let e=w.match(/definePageMeta\s*\(\s*\{[\s\S]*?validate\s*:\s*((?:\([^)]*\)|[^,}])*(?:\{[\s\S]*?\})?)/m);if(e)try{if(!await Function("route",`
44
+ </rss>`}async function aq(q={}){let Z=Date.now();Aq();let $=await n(process.cwd()),Q=$.build||{},K=await Kq({explicit:q.domain||Q.domain,stxConfig:$});if(K.source==="fallback"&&(q.sitemap??Q.sitemap??!0))console.warn(Wq());let J={router:q.router??$?.router??{},pagesDir:q.pagesDir||Q.pagesDir||"pages",outputDir:q.outputDir||Q.outputDir||"dist",baseUrl:q.baseUrl||Q.baseUrl||"/",domain:K.url,revalidate:q.revalidate??Q.revalidate??!1,sitemap:q.sitemap??Q.sitemap??!0,sitemapExclude:q.sitemapExclude||Q.sitemapExclude||[],robots:q.robots??Q.robots??!0,rss:q.rss??Q.rss??!1,concurrency:q.concurrency||Q.concurrency||10,cache:q.cache??Q.cache??!0,cacheDir:q.cacheDir||Q.cacheDir||d(process.cwd(),"ssg-cache"),contentLoaders:q.contentLoaders||[],hooks:q.hooks||{},minify:q.minify??Q.minify??!0,generate404:q.generate404??Q.generate404??!0,publicDir:q.publicDir||Q.publicDir||"public",trailingSlash:q.trailingSlash??Q.trailingSlash??!1,cleanOutput:q.cleanOutput??Q.cleanOutput??!0,cacheSalt:q.cacheSalt??"",partialsDir:q.partialsDir||$?.partialsDir||"",componentsDir:q.componentsDir||$?.componentsDir||"",failOnIncludeError:q.failOnIncludeError??!0,chunkIslands:q.chunkIslands===!0,prefetchIslands:q.prefetchIslands===!0,integrityIslands:q.integrityIslands===!0},W={totalPages:0,successCount:0,failedCount:0,cachedCount:0,buildTime:0,pages:[],errors:[]},z=new Set,A=0,G={},X={};await J.hooks.onBuildStart?.();try{if(J.cleanOutput&&O.existsSync(J.outputDir))await O.promises.rm(J.outputDir,{recursive:!0});await O.promises.mkdir(J.outputDir,{recursive:!0});let L=J.cacheSalt;if(!L){let{resolveUserCssConfig:Y,fingerprintConfig:N}=await import("./chunk-jnbg3e4r.js");try{L=N(await Y(process.cwd()))}catch{}}let{prepareImageDelivery:_}=await import("./chunk-e631svvs.js"),F=await _(J.publicDir,J.outputDir);if(F.count>0)console.log(`Optimized ${F.count} image(s) for responsive delivery`),L=[L,F.fingerprint].filter(Boolean).join(":");let C=new wq(J.cacheDir,L);if(J.cache)await C.load();try{let{generateCss:Y}=await import("./chunk-jnbg3e4r.js");await Y('<div class="bg-black"></div>')}catch{}Yq();let kq=$?.routeMiddleware?.dir||"middleware";await jq(process.cwd(),kq);let p=Vq();if(p.length>0)console.log(`Loaded ${p.length} middleware: ${p.join(", ")}`);let bq=$?.routeMiddleware?.global||[];Fq($?.layoutsDir,d(process.cwd()));let i=l(".",{pagesDir:J.pagesDir,pagesDirs:$?._layerPageDirs});console.log(`Found ${i.length} routes`);let g=[];for(let Y of i){let N=await uq(Y);for(let H of N)g.push({route:Y,url:H.url,params:H.params,props:H.props,revalidate:H.revalidate??J.revalidate})}W.totalPages=g.length,console.log(`Generating ${W.totalPages} pages...`);let b=[],s=[];for(let Y=0;Y<g.length;Y+=J.concurrency)s.push(g.slice(Y,Y+J.concurrency));for(let Y of s)await Promise.all(Y.map(async(N)=>{let{route:H,url:V,params:S,props:Sq,revalidate:a}=N;try{await J.hooks.onPageStart?.(V);let w=await Bun.file(H.filePath).text(),R=JJ(w),vq=R?.middleware?Array.isArray(R.middleware)?R.middleware:[R.middleware]:[],t=[...bq,...vq];if(t.length>0){let D=Gq(V,S,R||{}),M=Lq(D,null,void 0,{isServer:!0}),E=await Nq(t,M);if(E.redirect){let U=typeof E.redirect==="string"?E.redirect:E.redirect.path||E.redirect,x=QJ(String(U),V),k=ZJ(V,J);await O.promises.mkdir(j.dirname(k),{recursive:!0}),await Bun.write(k,x),W.successCount++,W.pages.push({route:V,outputPath:k,size:Buffer.byteLength(x),cached:!1}),b.push({loc:V,lastmod:new Date().toISOString()}),console.log(` \u21B3 ${V} \u2192 redirect to ${U}`);return}if(E.abort){let U=typeof E.abort==="string"?E.abort:E.abort?.error?.message||"aborted";console.warn(` \u21B3 ${V} \u2014 middleware aborted: ${U}`);return}}let e=w.match(/definePageMeta\s*\(\s*\{[\s\S]*?validate\s*:\s*((?:\([^)]*\)|[^,}])*(?:\{[\s\S]*?\})?)/m);if(e)try{if(!await Function("route",`
45
45
  const validate = ${e[1]};
46
46
  return validate(route);
47
47
  `)({params:S})){console.warn(`Validation failed for ${V}, skipping...`);return}}catch{}let I=null,qq=!1;if(J.cache){if(I=C.get(V,H.filePath),I)qq=!0,W.cachedCount++}if(!I){let D=await Mq(H,S,Sq,J,$);if(I=D.html,J.failOnIncludeError&&D.includeFailures.length>0)throw Error(`${D.includeFailures.length} include(s) failed to resolve, so this page would have shipped with an error banner where the markup should be:
@@ -29,6 +29,23 @@
29
29
  * pairing was the dominant one, and the defaults stay in step with the palette
30
30
  * because they are resolved from it rather than copied.
31
31
  *
32
+ * ## Why `neutral` and not `gray`
33
+ *
34
+ * The STEP each neutral role names was measured from the library; the FAMILY
35
+ * is a house decision. `neutral` is achromatic (chroma 0 at every step) while
36
+ * `gray` carries a blue tint (chroma 0.027–0.034 through the mid range), and
37
+ * the two agree on lightness to within about 1% at every step. So the choice
38
+ * is a pure saturation one and changes no contrast ratio.
39
+ *
40
+ * These were gray-backed, which left the library mixing both: 103 `neutral-*`
41
+ * uses in components against gray-backed roles. Everything is `neutral` now -
42
+ * the roles, and the 35 literal shades that could not move onto a role - so
43
+ * there is one neutral in the library rather than two that differ only by a
44
+ * tint nobody chose (stacksjs/stx#1993).
45
+ *
46
+ * Flipping this back is a one-line edit per role, and the rest of the
47
+ * vocabulary is unaffected: the status hues are their own families.
48
+ *
32
49
  * ## How dark mode works
33
50
  *
34
51
  * Each token resolves to `var(--stx-<name>, <light value>)`, and
@@ -85,4 +102,46 @@ export declare function semanticColors(palette: Palette): Record<string, string>
85
102
  export declare function semanticTokenCSS(palette: Palette): string;
86
103
  /** Every variable a role token reads, for documentation and tooling. */
87
104
  export declare function semanticTokenNames(): string[];
105
+ /**
106
+ * Shape roles — the radius a thing has because of what it IS.
107
+ *
108
+ * Colour was only half of what made the library unadoptable. An app whose
109
+ * buttons are pills got `rounded-md`, and `className` is no escape hatch for a
110
+ * radius for exactly the reason it was not one for a colour: `rounded-md` and
111
+ * `rounded-full` are single-class selectors of equal specificity, so the winner
112
+ * is whichever lands later in the generated stylesheet, which a call site
113
+ * cannot control (stacksjs/stx#1993).
114
+ *
115
+ * Three roles, from what the library actually uses: `rounded-md` on controls
116
+ * (54 uses), `rounded-lg` on panels (24), and `rounded-full` where something is
117
+ * genuinely a pill (34, of which the circular ones - avatars, dots, spinners -
118
+ * keep `rounded-full`, because a circle is not a theming decision).
119
+ *
120
+ * So `--stx-radius-control: 9999px` turns every button, input and select in the
121
+ * library into a pill, and nothing else moves.
122
+ */
123
+ export declare const SHAPE_TOKENS: Record<string, {
124
+ value: string;
125
+ description: string;
126
+ }>;
127
+ /** The CSS custom property that backs a shape role. */
128
+ export declare function shapeVariable(name: string): string;
129
+ /**
130
+ * The `theme.borderRadius` entries to merge in.
131
+ *
132
+ * Same shape as {@link semanticColors}: each is `var(--stx-radius-<name>,
133
+ * <stock value>)`, so `rounded-control` is an ordinary utility - directional
134
+ * variants like `rounded-t-control` included - and an app moves it by setting
135
+ * the variable.
136
+ */
137
+ export declare function shapeRadii(): Record<string, string>;
138
+ /**
139
+ * The `:root` block for the shape roles.
140
+ *
141
+ * No `.dark` counterpart: a radius does not change with the colour scheme, and
142
+ * emitting an empty dark block would invite someone to fill it in.
143
+ */
144
+ export declare function shapeTokenCSS(): string;
145
+ /** Every variable a shape role reads, for documentation and tooling. */
146
+ export declare function shapeTokenNames(): string[];
88
147
  export {};
@@ -1,23 +1,62 @@
1
1
  export const SEMANTIC_TOKENS = {
2
- fg: { light: "gray-900", dark: "gray-100", description: "Primary text" },
3
- "fg-strong": { light: "gray-700", dark: "gray-300", description: "Labels, secondary headings" },
4
- "fg-muted": { light: "gray-600", dark: "gray-400", description: "Supporting text" },
5
- "fg-soft": { light: "gray-500", dark: "gray-400", description: "De-emphasised text" },
6
- "fg-subtle": { light: "gray-400", dark: "gray-500", description: "Placeholders, disabled text" },
7
- surface: { light: "gray-50", dark: "gray-800", description: "Page and panel background" },
8
- "surface-raised": { light: "gray-100", dark: "gray-700", description: "Cards, popovers" },
9
- "surface-sunken": { light: "gray-200", dark: "gray-700", description: "Wells, track backgrounds" },
10
- line: { light: "gray-200", dark: "gray-700", description: "Default border, divider" },
11
- "line-strong": { light: "gray-300", dark: "gray-600", description: "Input border, focus ring track" },
12
- accent: { light: "indigo-600", dark: "indigo-400", description: "Primary action, selected state" },
13
- info: { light: "blue-600", dark: "blue-400", description: "Informational emphasis, links" },
2
+ fg: { light: "neutral-900", dark: "neutral-100", description: "Primary text" },
3
+ "fg-strong": { light: "neutral-700", dark: "neutral-300", description: "Labels, secondary headings" },
4
+ "fg-muted": { light: "neutral-600", dark: "neutral-400", description: "Supporting text" },
5
+ "fg-soft": { light: "neutral-500", dark: "neutral-400", description: "De-emphasised text" },
6
+ "fg-subtle": { light: "neutral-400", dark: "neutral-500", description: "Placeholders, disabled text" },
7
+ surface: { light: "neutral-50", dark: "neutral-800", description: "Page and panel background" },
8
+ "surface-raised": { light: "neutral-100", dark: "neutral-700", description: "Cards, popovers" },
9
+ "surface-sunken": { light: "neutral-200", dark: "neutral-700", description: "Wells, track backgrounds" },
10
+ panel: { light: "white", dark: "neutral-800", description: "Card, menu, dialog surface" },
11
+ field: { light: "white", dark: "neutral-700", description: "Form control surface" },
12
+ "surface-hover": { light: "neutral-100", dark: "neutral-700", description: "Row or item highlight on a page or panel" },
13
+ "surface-raised-hover": { light: "neutral-200", dark: "neutral-600", description: "Raised surface, hovered" },
14
+ "surface-sunken-hover": { light: "neutral-300", dark: "neutral-600", description: "Sunken surface, hovered" },
15
+ "field-hover": { light: "neutral-50", dark: "neutral-600", description: "Form control surface, hovered" },
16
+ inverse: { light: "neutral-900", dark: "neutral-700", description: "Tooltip bubble, inverted surface" },
17
+ "inverse-ink": { light: "white", dark: "white", description: "Text on an inverted surface" },
18
+ line: { light: "neutral-200", dark: "neutral-700", description: "Default border, divider" },
19
+ "line-strong": { light: "neutral-300", dark: "neutral-600", description: "Input border, focus ring track" },
20
+ accent: { light: "blue-600", dark: "blue-400", description: "Primary action, selected state" },
21
+ info: { light: "cyan-600", dark: "cyan-400", description: "Informational emphasis, links" },
14
22
  danger: { light: "red-600", dark: "red-400", description: "Errors, destructive actions" },
15
23
  success: { light: "green-600", dark: "green-400", description: "Confirmation" },
16
- warning: { light: "yellow-500", dark: "yellow-400", description: "Caution" },
17
- "accent-solid": { light: "indigo-600", dark: "indigo-500", description: "Primary button fill" },
18
- "info-solid": { light: "blue-600", dark: "blue-500", description: "Informational fill" },
19
- "danger-solid": { light: "red-600", dark: "red-500", description: "Destructive button fill" },
20
- "success-solid": { light: "green-600", dark: "green-500", description: "Confirmation fill" }
24
+ warning: { light: "yellow-600", dark: "yellow-400", description: "Caution" },
25
+ "accent-solid": { light: "blue-500", dark: "blue-600", description: "Primary button fill" },
26
+ "info-solid": { light: "cyan-500", dark: "cyan-600", description: "Informational fill" },
27
+ "danger-solid": { light: "red-500", dark: "red-600", description: "Destructive button fill" },
28
+ "success-solid": { light: "green-500", dark: "green-600", description: "Confirmation fill" },
29
+ "warning-solid": { light: "yellow-500", dark: "yellow-600", description: "Caution fill" },
30
+ "accent-solid-hover": { light: "blue-600", dark: "blue-700", description: "Primary button fill, hovered" },
31
+ "info-solid-hover": { light: "cyan-600", dark: "cyan-700", description: "Informational fill, hovered" },
32
+ "danger-solid-hover": { light: "red-600", dark: "red-700", description: "Destructive fill, hovered" },
33
+ "success-solid-hover": { light: "green-600", dark: "green-700", description: "Confirmation fill, hovered" },
34
+ "warning-solid-hover": { light: "yellow-600", dark: "yellow-700", description: "Caution fill, hovered" },
35
+ "accent-ink": { light: "white", dark: "white", description: "Text on a primary fill" },
36
+ "info-ink": { light: "white", dark: "white", description: "Text on an informational fill" },
37
+ "danger-ink": { light: "white", dark: "white", description: "Text on a destructive fill" },
38
+ "success-ink": { light: "white", dark: "white", description: "Text on a confirmation fill" },
39
+ "warning-ink": { light: "neutral-900", dark: "neutral-900", description: "Text on a caution fill" },
40
+ "accent-soft": { light: "blue-100", dark: "blue-900", description: "Tinted primary fill" },
41
+ "accent-soft-ink": { light: "blue-800", dark: "blue-200", description: "Text on a tinted primary fill" },
42
+ "info-soft": { light: "cyan-100", dark: "cyan-900", description: "Tinted informational fill" },
43
+ "info-soft-ink": { light: "cyan-800", dark: "cyan-200", description: "Text on a tinted informational fill" },
44
+ "danger-soft": { light: "red-100", dark: "red-900", description: "Tinted destructive fill" },
45
+ "danger-soft-ink": { light: "red-800", dark: "red-200", description: "Text on a tinted destructive fill" },
46
+ "success-soft": { light: "green-100", dark: "green-900", description: "Tinted confirmation fill" },
47
+ "success-soft-ink": { light: "green-800", dark: "green-200", description: "Text on a tinted confirmation fill" },
48
+ "warning-soft": { light: "yellow-100", dark: "yellow-900", description: "Tinted caution fill" },
49
+ "warning-soft-ink": { light: "yellow-800", dark: "yellow-200", description: "Text on a tinted caution fill" },
50
+ secondary: { light: "purple-600", dark: "purple-400", description: "Secondary emphasis" },
51
+ "secondary-solid": { light: "purple-500", dark: "purple-600", description: "Secondary fill" },
52
+ "secondary-solid-hover": { light: "purple-600", dark: "purple-700", description: "Secondary fill, hovered" },
53
+ "secondary-ink": { light: "white", dark: "white", description: "Text on a secondary fill" },
54
+ "secondary-soft": { light: "purple-100", dark: "purple-900", description: "Tinted secondary fill" },
55
+ "secondary-soft-ink": { light: "purple-800", dark: "purple-200", description: "Text on a tinted secondary fill" },
56
+ "danger-fg": { light: "red-900", dark: "red-300", description: "Text in a field with an error" },
57
+ "danger-fg-subtle": { light: "red-300", dark: "red-500", description: "Placeholder in a field with an error" },
58
+ "danger-line": { light: "red-300", dark: "red-600", description: "Border of a field with an error" },
59
+ "danger-focus": { light: "red-500", dark: "red-400", description: "Focus ring of a field with an error" }
21
60
  };
22
61
  export function tokenVariable(name) {
23
62
  return `--stx-${name}`;
@@ -69,3 +108,30 @@ export function semanticTokenCSS(palette) {
69
108
  export function semanticTokenNames() {
70
109
  return Object.keys(SEMANTIC_TOKENS).map(tokenVariable);
71
110
  }
111
+ export const SHAPE_TOKENS = {
112
+ control: { value: "0.375rem", description: "Buttons, inputs, selects \u2014 rounded-md" },
113
+ panel: { value: "0.5rem", description: "Cards, dialogs, menus \u2014 rounded-lg" },
114
+ pill: { value: "9999px", description: "Chips and badges, which are pill-shaped by intent" }
115
+ };
116
+ export function shapeVariable(name) {
117
+ return `--stx-radius-${name}`;
118
+ }
119
+ export function shapeRadii() {
120
+ const radii = {};
121
+ for (const [name, token] of Object.entries(SHAPE_TOKENS))
122
+ radii[name] = `var(${shapeVariable(name)}, ${token.value})`;
123
+ return radii;
124
+ }
125
+ export function shapeTokenCSS() {
126
+ return [
127
+ "/* stx shape tokens \u2014 a radius by what the thing is. */",
128
+ ":root {",
129
+ ...Object.entries(SHAPE_TOKENS).map(([name, token]) => ` ${shapeVariable(name)}: ${token.value};`),
130
+ "}",
131
+ ""
132
+ ].join(`
133
+ `);
134
+ }
135
+ export function shapeTokenNames() {
136
+ return Object.keys(SHAPE_TOKENS).map(shapeVariable);
137
+ }
@@ -1,4 +1,4 @@
1
- import { semanticColors, semanticTokenCSS } from "./theme-tokens.js";
1
+ import { semanticColors, semanticTokenCSS, shapeRadii, shapeTokenCSS } from "./theme-tokens.js";
2
2
  function isPlainObject(value) {
3
3
  return typeof value === "object" && value !== null && !Array.isArray(value);
4
4
  }
@@ -11,6 +11,8 @@ export function deepMergeThemes(target, source) {
11
11
  export function mergeCssConfig(base = {}, user = {}) {
12
12
  const baseTheme = base.theme || {}, userTheme = user.theme || {}, { extend: userExtend, ...userThemeKeys } = userTheme, { extend: baseExtend, ...baseThemeKeys } = baseTheme, basePalette = baseThemeKeys.colors || {};
13
13
  baseThemeKeys.colors = { ...basePalette, ...semanticColors(basePalette) };
14
+ const baseRadii = baseThemeKeys.borderRadius || {};
15
+ baseThemeKeys.borderRadius = { ...baseRadii, ...shapeRadii() };
14
16
  const theme = deepMergeThemes(baseThemeKeys, userThemeKeys), extend = deepMergeThemes(baseExtend || {}, userExtend || {});
15
17
  if (Object.keys(extend).length > 0)
16
18
  theme.extend = extend;
@@ -39,6 +41,6 @@ export function mergeCssConfig(base = {}, user = {}) {
39
41
  shortcuts,
40
42
  includePreflight: user.preflight !== !1,
41
43
  minify: user.minify === !0,
42
- tokenCSS: semanticTokenCSS(basePalette)
44
+ tokenCSS: semanticTokenCSS(basePalette) + shapeTokenCSS()
43
45
  };
44
46
  }
package/dist/utils.d.ts CHANGED
@@ -28,6 +28,16 @@ export declare function getSharedTranspiler(options: ConstructorParameters<typeo
28
28
  * Transpile TypeScript code to JavaScript using Bun (sync version)
29
29
  */
30
30
  export declare function transpileTypeScript(code: string): string;
31
+ /**
32
+ * Put `attrs` on the markup's own root element, or return null to say a
33
+ * wrapper is fine.
34
+ *
35
+ * Returns null unless the markup is a single element of a kind whose parent
36
+ * would reject a wrapper: leading comments and whitespace are skipped, the tag
37
+ * name is checked against the set above, and the element has to be the only one
38
+ * of its kind at the top level, closing at the end.
39
+ */
40
+ export declare function scopeOnRootElement(html: string, attrs: string): string | null;
31
41
  /**
32
42
  * Does a project-authored component file exist for `componentPath`?
33
43
  *
package/dist/utils.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  replayComponentClientFactories,
9
9
  snapshotComponentClientFactoryCounts
10
10
  } from "./component-client-factories.js";
11
- import { interpolateScriptExpressions, processExpressions, usesSignalsInScript } from "./expressions.js";
11
+ import { evaluateExpression, interpolateScriptExpressions, processExpressions, usesSignalsInScript } from "./expressions.js";
12
12
  import { COMPONENT_SCOPE_LOCAL_GLOBALS, buildRuntimeGlobalsDestructure } from "./runtime-globals.js";
13
13
  import { contentKey, depsUnchanged, renderMemo, snapshotDeps } from "./render-memo.js";
14
14
  import { transformStoreImports } from "./store-imports.js";
@@ -400,6 +400,36 @@ function applyDestructuredPropDefaults(componentContent, ctx) {
400
400
  }
401
401
  } catch {}
402
402
  }
403
+ const WRAPPER_HOISTED_OUT_BY_PARSER = new Set([
404
+ "tr",
405
+ "td",
406
+ "th",
407
+ "thead",
408
+ "tbody",
409
+ "tfoot",
410
+ "caption",
411
+ "colgroup",
412
+ "col",
413
+ "option",
414
+ "optgroup",
415
+ "li",
416
+ "dt",
417
+ "dd"
418
+ ]);
419
+ export function scopeOnRootElement(html, attrs) {
420
+ const opening = html.match(/^(\s*(?:(?:<!--[\s\S]*?-->|\x00STX_HTML_COMMENT_\d+\x00)\s*)*)<([a-zA-Z][a-zA-Z0-9-]*)/s);
421
+ if (!opening)
422
+ return null;
423
+ const tag = opening[2].toLowerCase();
424
+ if (!WRAPPER_HOISTED_OUT_BY_PARSER.has(tag))
425
+ return null;
426
+ if ((html.match(new RegExp(`<${tag}(?=[\\s/>])`, "gi")) ?? []).length !== 1)
427
+ return null;
428
+ if (!new RegExp(`</${tag}>\\s*$`, "i").test(html))
429
+ return null;
430
+ const insertAt = opening[0].length;
431
+ return html.slice(0, insertAt) + attrs + html.slice(insertAt);
432
+ }
403
433
  export async function userComponentFileExists(componentPath, componentsDir, parentContext, parentFilePath, options) {
404
434
  const baseName = componentPath.endsWith(".stx") ? componentPath.slice(0, -4) : componentPath, kebabToPascal = (str) => str.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(""), pascalToKebab = (str) => str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), importedComponents = parentContext.__importedComponents;
405
435
  if (importedComponents) {
@@ -459,6 +489,42 @@ export function slotContentHasExpressions(slotContent) {
459
489
  const withoutScripts = slotContent.replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<style[\s\S]*?<\/style>/gi, "");
460
490
  return /\{\{[\s\S]*?\}\}|\{!![\s\S]*?!!\}/.test(withoutScripts);
461
491
  }
492
+ function interpolateStyleExpressions(css, context, filePath) {
493
+ if (!css.includes("{{") && !css.includes("{!!"))
494
+ return css;
495
+ const insert = (expression, raw) => {
496
+ let value;
497
+ try {
498
+ value = evaluateExpression(expression, context);
499
+ } catch {
500
+ return raw;
501
+ }
502
+ if (value === void 0 || value === null)
503
+ return "";
504
+ const text = String(value);
505
+ if (/<\/style/i.test(text)) {
506
+ console.warn(`[stx] a <style> expression in ${filePath ?? "<unknown>"} evaluated to CSS containing a closing style tag, which would end the element early, so it was dropped rather than emitted.`);
507
+ return "";
508
+ }
509
+ return text;
510
+ };
511
+ return css.replace(/\{!!([\s\S]*?)!!\}/g, (raw, expression) => insert(expression, raw)).replace(/\{\{([\s\S]*?)\}\}/g, (raw, expression) => insert(expression, raw));
512
+ }
513
+ const reportedUnresolvedComponents = new Set;
514
+ function reportUnresolvedComponent(componentPath, parentFilePath, triedPaths, cause) {
515
+ const key = `${componentPath}\x00${parentFilePath}\x00${cause ?? ""}`;
516
+ if (!process.env.STX_DEBUG && reportedUnresolvedComponents.has(key))
517
+ return;
518
+ reportedUnresolvedComponents.add(key);
519
+ const where = parentFilePath ? ` used in ${parentFilePath}` : "", why = cause ? ` Cause: ${cause}.` : "", searched = triedPaths.length > 0 ? `
520
+ searched:
521
+ ${triedPaths.map((tried) => ` - ${tried}`).join(`
522
+ `)}` : "";
523
+ console.error(`[stx] component <${componentPath}> could not be resolved${where}, so it rendered as nothing.${why}${searched}`);
524
+ }
525
+ function unresolvedComponentMarkup(componentPath) {
526
+ return `<!-- stx: component "${componentPath.replace(/--+/g, "-")}" could not be resolved; see the server log -->`;
527
+ }
462
528
  export function componentClientSignalNames(scripts) {
463
529
  const names = new Set, declaration = /\b(?:const|let|var)\s+([a-zA-Z_$][\w$]*)\s*=\s*(?:state|derived|computed|ref|reactive|signal|useModel|useReactiveProp|useLocalStorage|useSessionStorage|useCookie|useDebouncedValue)\s*(?:<[^>]*>)?\s*\(/g;
464
530
  for (const script of scripts) {
@@ -610,11 +676,8 @@ export async function renderComponentWithSlot(componentPath, props, slotContent,
610
676
  componentResolutionCache.set(resolutionCacheKey, componentFilePath);
611
677
  }
612
678
  if (!componentFilePath || !await fileExists(componentFilePath)) {
613
- const searchInfo = triedPaths.length > 0 ? `
614
- Searched paths:
615
- ${triedPaths.map((p) => ` - ${p}`).join(`
616
- `)}` : "";
617
- return `[Error loading component: ENOENT: no such file or directory, open '${componentPath}']${searchInfo}`;
679
+ reportUnresolvedComponent(componentPath, parentFilePath, triedPaths);
680
+ return unresolvedComponentMarkup(componentPath);
618
681
  }
619
682
  dependencies.add(componentFilePath);
620
683
  let componentContent;
@@ -625,7 +688,9 @@ ${triedPaths.map((p) => ` - ${p}`).join(`
625
688
  componentContent = await Bun.file(componentFilePath).text();
626
689
  componentsCache.set(componentFilePath, componentContent);
627
690
  } catch (error) {
628
- return `[Error loading component: ${error instanceof Error ? error.message : String(error)}]`;
691
+ const message = error instanceof Error ? error.message : String(error);
692
+ reportUnresolvedComponent(componentPath, parentFilePath, [componentFilePath], message);
693
+ return unresolvedComponentMarkup(componentPath);
629
694
  }
630
695
  const internalKeysToPreserve = new Set([
631
696
  "__originalFilePath",
@@ -727,16 +792,16 @@ ${triedPaths.map((p) => ` - ${p}`).join(`
727
792
  let cleanContent = content.replace(/^\s*const\s*\{[^}]+\}\s*=\s*window\.stx\s*;?\s*\n?/gm, "");
728
793
  const hasMustache = cleanContent.includes("{{") && cleanContent.includes("}}"), hasRawMarker = cleanContent.includes("{!!") && cleanContent.includes("!!}");
729
794
  if (hasMustache || hasRawMarker)
730
- cleanContent = interpolateScriptExpressions(cleanContent, componentContext);
795
+ cleanContent = interpolateScriptExpressions(cleanContent, componentContext, componentFilePath);
731
796
  const cleanAttrs = attrs.replace(/\s*\bts\b/g, "").replace(/\s*\blang\s*=\s*["']?(ts|typescript)["']?/gi, "");
732
797
  clientScripts.push(`<script${cleanAttrs}>${cleanContent}</script>`);
733
798
  }
734
799
  }
735
- const styleMatch = componentContent.match(/<style\b([^>]*)>([\s\S]*?)<\/style>/i), styleAttrs = styleMatch ? styleMatch[1] : "", styleContent = styleMatch ? styleMatch[2] : "";
800
+ const styleMatch = stashScriptElements(componentContent).output.match(/<style\b([^>]*)>([\s\S]*?)<\/style>/i), styleAttrs = styleMatch ? styleMatch[1] : "", styleContent = styleMatch ? styleMatch[2] : "";
736
801
  let preservedStyle = "";
737
802
  const hasScopedStyle = styleAttrs && /\bscoped\b/i.test(styleAttrs);
738
803
  if (styleMatch)
739
- preservedStyle = `<style${styleAttrs}>${styleContent}</style>`;
804
+ preservedStyle = `<style${styleAttrs}>${interpolateStyleExpressions(styleContent, componentContext, componentFilePath)}</style>`;
740
805
  let templateContent = workingContent;
741
806
  templateContent = templateContent.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "");
742
807
  if (styleMatch)
@@ -788,8 +853,8 @@ ${triedPaths.map((p) => ` - ${p}`).join(`
788
853
  try {
789
854
  propsJson = ` data-stx-props="${escapeAttr(JSON.stringify(props))}"`;
790
855
  } catch {}
791
- const hydrateJson = hydrateTrigger ? ` stx-hydrate="${escapeAttr(hydrateTrigger)}"` : "";
792
- output = `<div data-stx-scope="${scopeId}"${propsJson}${hydrateJson}>${result}</div>`;
856
+ const hydrateJson = hydrateTrigger ? ` stx-hydrate="${escapeAttr(hydrateTrigger)}"` : "", scopeAttrs = ` data-stx-scope="${scopeId}"${propsJson}${hydrateJson}`;
857
+ output = scopeOnRootElement(result, scopeAttrs) ?? `<div${scopeAttrs}>${result}</div>`;
793
858
  const scopedScripts = await Promise.all(clientScripts.map(async (script) => {
794
859
  const scriptMatch = script.match(/<script([^>]*)>([\s\S]*)<\/script>/i);
795
860
  if (!scriptMatch)
@@ -19,7 +19,7 @@
19
19
  * generated `await import('stx')` rejected and the shadowing binding was never
20
20
  * created. The failure needed a real install to appear.
21
21
  */
22
- export declare const STX_ENGINE_BINDING_NAMES: readonly ['module', 'exports', 'require', 'props', '$props', 'defineProps', 'withDefaults', 'defineClientPayload', 'useServerData', 'useRuntimeConfig', 'useServerRuntimeConfig', 'state', 'derived', 'effect', 'batch', 'onMount', 'onDestroy', 'definePageMeta', 'useRoute', 'useRouter', 'useHead', 'useSeoMeta', 'setResponseStatus', 'setResponseHeader', 'notFound', 'ref', 'reactive', 'computed', 'watch', 'onMounted', 'onUnmounted', 'nextTick', 'defineEmits', 'defineExpose', 'defineSlots', 'provide', 'inject', 'useColorMode', 'useDark', 'useMediaQuery', 'useScrollLock', 'usePreferredDark', 'usePreferredLight', 'usePreferredReducedMotion', 'usePreferredContrast', 'window', 'document', 'console', 'confirm', 'alert', 'fetch', 'params'];
22
+ export declare const STX_ENGINE_BINDING_NAMES: readonly ['module', 'exports', 'require', 'props', '$props', '$bool', 'defineProps', 'withDefaults', 'defineClientPayload', 'useServerData', 'useRuntimeConfig', 'useServerRuntimeConfig', 'state', 'derived', 'effect', 'batch', 'onMount', 'onDestroy', 'definePageMeta', 'useRoute', 'useRouter', 'useHead', 'useSeoMeta', 'setResponseStatus', 'setResponseHeader', 'notFound', 'ref', 'reactive', 'computed', 'watch', 'onMounted', 'onUnmounted', 'nextTick', 'defineEmits', 'defineExpose', 'defineSlots', 'provide', 'inject', 'useColorMode', 'useDark', 'useMediaQuery', 'useScrollLock', 'usePreferredDark', 'usePreferredLight', 'usePreferredReducedMotion', 'usePreferredContrast', 'window', 'document', 'console', 'confirm', 'alert', 'fetch', 'params'];
23
23
  /**
24
24
  * Strip TypeScript-specific syntax to make it executable as JavaScript
25
25
  *
@@ -10,6 +10,7 @@ export const STX_ENGINE_BINDING_NAMES = [
10
10
  "require",
11
11
  "props",
12
12
  "$props",
13
+ "$bool",
13
14
  "defineProps",
14
15
  "withDefaults",
15
16
  "defineClientPayload",
@@ -286,6 +287,7 @@ export function isMissingBindingFailure(message) {
286
287
  return !1;
287
288
  return !/\b(document|window|navigator|localStorage|sessionStorage|self)\b/.test(message);
288
289
  }
290
+ const reportedServerScriptFailures = new Set;
289
291
  export async function extractVariables(scriptContent, context, filePath, options = {}) {
290
292
  if (!scriptContent.trim())
291
293
  return;
@@ -351,6 +353,19 @@ export async function extractVariables(scriptContent, context, filePath, options
351
353
  };
352
354
  for (const [key, value] of Object.entries(propsObj))
353
355
  Object.defineProperty($props, key, { value, enumerable: !0, configurable: !0, writable: !0 });
356
+ const $bool = (value, fallback = !1) => {
357
+ if (value === void 0 || value === null)
358
+ return fallback;
359
+ if (typeof value === "string") {
360
+ const text = value.trim().toLowerCase();
361
+ if (text === "" || text === "true" || text === "1" || text === "on" || text === "yes")
362
+ return !0;
363
+ if (text === "false" || text === "0" || text === "off" || text === "no")
364
+ return !1;
365
+ return !0;
366
+ }
367
+ return Boolean(value);
368
+ };
354
369
  for (const shadowed of ["name", "length", "caller", "arguments"]) {
355
370
  if (Object.prototype.hasOwnProperty.call(propsObj, shadowed))
356
371
  continue;
@@ -540,7 +555,7 @@ export async function extractVariables(scriptContent, context, filePath, options
540
555
  const hostRuntimeConfig = context.__stx_options?.runtimeConfig;
541
556
  if (hostRuntimeConfig || /\buse(?:Server)?RuntimeConfig\b/.test(jsContent))
542
557
  await prepareRuntimeConfig(context, filePath, hostRuntimeConfig);
543
- const result = await withRuntimeConfig(context.__stx_runtime_config ?? currentRuntimeConfig() ?? resolveRuntimeConfig(), () => withServerData(context, () => scriptFn(module, exports, requireFn, propsObj, $props, defineProps, withDefaults, defineClientPayload, useServerData, useRuntimeConfig, useServerRuntimeConfig, state, derived, effect, batch, onMount, onDestroy, definePageMeta, useRoute, useRouter, useHead, useSeoMeta, responseApi.setResponseStatus, responseApi.setResponseHeader, responseApi.notFound, ref, reactive, computed, watch, onMounted, onUnmounted, nextTick, defineEmits, defineExpose, defineSlots, provide, inject, useColorMode, useDark, useMediaQuery, useScrollLock, usePreferredDark, usePreferredLight, usePreferredReducedMotion, usePreferredContrast, windowProxy, mockDocument, mockConsole, mockWindow.confirm, mockWindow.alert, mockWindow.fetch, paramsObj, ...propArgValues, ...scriptContextValues)));
558
+ const result = await withRuntimeConfig(context.__stx_runtime_config ?? currentRuntimeConfig() ?? resolveRuntimeConfig(), () => withServerData(context, () => scriptFn(module, exports, requireFn, propsObj, $props, $bool, defineProps, withDefaults, defineClientPayload, useServerData, useRuntimeConfig, useServerRuntimeConfig, state, derived, effect, batch, onMount, onDestroy, definePageMeta, useRoute, useRouter, useHead, useSeoMeta, responseApi.setResponseStatus, responseApi.setResponseHeader, responseApi.notFound, ref, reactive, computed, watch, onMounted, onUnmounted, nextTick, defineEmits, defineExpose, defineSlots, provide, inject, useColorMode, useDark, useMediaQuery, useScrollLock, usePreferredDark, usePreferredLight, usePreferredReducedMotion, usePreferredContrast, windowProxy, mockDocument, mockConsole, mockWindow.confirm, mockWindow.alert, mockWindow.fetch, paramsObj, ...propArgValues, ...scriptContextValues)));
544
559
  if (options.preserveExisting) {
545
560
  for (const [key, value] of Object.entries(result))
546
561
  if (!(key in context) || context[key] === void 0)
@@ -560,8 +575,13 @@ export async function extractVariables(scriptContent, context, filePath, options
560
575
  console.warn(`[stx] server <script> in ${filePath ?? "<unknown>"} does not parse, so every variable in that script is undefined. Cause: ${msg}`);
561
576
  else if (isMissingBindingFailure(msg))
562
577
  console.warn(`[stx] server <script> in ${filePath ?? "<unknown>"} names something that does not exist, so every variable in that script is undefined. Optional chaining does not help here: an undefined identifier throws before the chain is reached. Cause: ${msg}`);
563
- else if (process.env.STX_DEBUG)
564
- console.warn(`[stx] server <script> did not execute in ${filePath ?? "<unknown>"} \u2014 falling back to static extraction. Cause: ${msg}`);
578
+ else {
579
+ const seenKey = `${filePath ?? "<unknown>"}\x00${msg}`;
580
+ if (process.env.STX_DEBUG || !reportedServerScriptFailures.has(seenKey)) {
581
+ reportedServerScriptFailures.add(seenKey);
582
+ console.warn(`[stx] server <script> in ${filePath ?? "<unknown>"} threw, so every variable in that script is undefined and anything that reads one renders empty. Cause: ${msg}`);
583
+ }
584
+ }
565
585
  try {
566
586
  await fallbackVariableExtraction(jsContent, context, filePath);
567
587
  } catch (fallbackError) {
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import"./chunk-0s3ry262.js";import B from"fs";import $ from"path";import k from"crypto";var V=[{name:"mobile",width:375,height:667,isMobile:!0,hasTouch:!0},{name:"tablet",width:768,height:1024,isMobile:!0,hasTouch:!0},{name:"desktop",width:1280,height:800,isMobile:!1,hasTouch:!1},{name:"wide",width:1920,height:1080,isMobile:!1,hasTouch:!1}];function J(N={}){let Y={snapshotDir:N.snapshotDir||"__snapshots__",screenshotDir:N.screenshotDir||"__screenshots__",updateSnapshots:N.updateSnapshots||process.env.UPDATE_SNAPSHOTS==="true",threshold:N.threshold??0.01,viewports:N.viewports||V,generateDiff:N.generateDiff??!0,diffDir:N.diffDir||"__diffs__",compare:N.compare||b,ci:N.ci??process.env.CI==="true",retries:N.retries??(N.ci?3:1)};return{async snapshot(M,X){let q=$.join(Y.snapshotDir,`${X}.html`),G=C(M),Q=K(G);if(await Bun.file(q).exists()){let W=await Bun.file(q).text(),Z=K(C(W));if(Q===Z)return{passed:!0,snapshotPath:q,message:"Snapshot matches"};if(Y.updateSnapshots)return await B.promises.mkdir($.dirname(q),{recursive:!0}),await Bun.write(q,G),{passed:!0,snapshotPath:q,message:"Snapshot updated"};let F=$.join(Y.diffDir,`${X}.actual.html`),U=$.join(Y.diffDir,`${X}.diff.html`);return await B.promises.mkdir(Y.diffDir,{recursive:!0}),await Bun.write(F,G),await Bun.write(U,I(W,G)),{passed:!1,snapshotPath:q,actualPath:F,diffPath:U,message:"Snapshot does not match"}}return await B.promises.mkdir($.dirname(q),{recursive:!0}),await Bun.write(q,G),{passed:!0,snapshotPath:q,message:"New snapshot created"}},async snapshotJson(M,X){let q=$.join(Y.snapshotDir,`${X}.json`),G=JSON.stringify(M,null,2);if(await Bun.file(q).exists()){let Q=await Bun.file(q).text();if(G===Q)return{passed:!0,snapshotPath:q};if(Y.updateSnapshots)return await Bun.write(q,G),{passed:!0,snapshotPath:q,message:"Snapshot updated"};return{passed:!1,snapshotPath:q,message:`JSON snapshot mismatch:
2
+ import"./chunk-ejqpq0wc.js";import B from"fs";import $ from"path";import k from"crypto";var V=[{name:"mobile",width:375,height:667,isMobile:!0,hasTouch:!0},{name:"tablet",width:768,height:1024,isMobile:!0,hasTouch:!0},{name:"desktop",width:1280,height:800,isMobile:!1,hasTouch:!1},{name:"wide",width:1920,height:1080,isMobile:!1,hasTouch:!1}];function J(N={}){let Y={snapshotDir:N.snapshotDir||"__snapshots__",screenshotDir:N.screenshotDir||"__screenshots__",updateSnapshots:N.updateSnapshots||process.env.UPDATE_SNAPSHOTS==="true",threshold:N.threshold??0.01,viewports:N.viewports||V,generateDiff:N.generateDiff??!0,diffDir:N.diffDir||"__diffs__",compare:N.compare||b,ci:N.ci??process.env.CI==="true",retries:N.retries??(N.ci?3:1)};return{async snapshot(M,X){let q=$.join(Y.snapshotDir,`${X}.html`),G=C(M),Q=K(G);if(await Bun.file(q).exists()){let W=await Bun.file(q).text(),Z=K(C(W));if(Q===Z)return{passed:!0,snapshotPath:q,message:"Snapshot matches"};if(Y.updateSnapshots)return await B.promises.mkdir($.dirname(q),{recursive:!0}),await Bun.write(q,G),{passed:!0,snapshotPath:q,message:"Snapshot updated"};let F=$.join(Y.diffDir,`${X}.actual.html`),U=$.join(Y.diffDir,`${X}.diff.html`);return await B.promises.mkdir(Y.diffDir,{recursive:!0}),await Bun.write(F,G),await Bun.write(U,I(W,G)),{passed:!1,snapshotPath:q,actualPath:F,diffPath:U,message:"Snapshot does not match"}}return await B.promises.mkdir($.dirname(q),{recursive:!0}),await Bun.write(q,G),{passed:!0,snapshotPath:q,message:"New snapshot created"}},async snapshotJson(M,X){let q=$.join(Y.snapshotDir,`${X}.json`),G=JSON.stringify(M,null,2);if(await Bun.file(q).exists()){let Q=await Bun.file(q).text();if(G===Q)return{passed:!0,snapshotPath:q};if(Y.updateSnapshots)return await Bun.write(q,G),{passed:!0,snapshotPath:q,message:"Snapshot updated"};return{passed:!1,snapshotPath:q,message:`JSON snapshot mismatch:
3
3
  ${z(Q,G)}`}}return await B.promises.mkdir($.dirname(q),{recursive:!0}),await Bun.write(q,G),{passed:!0,snapshotPath:q,message:"New snapshot created"}},matchInlineSnapshot(M,X){let q=C(M);if(X===void 0)return{passed:!0,expected:q};let G=C(X);return{passed:q===G,expected:q}},getConfig(){return Y}}}function D(N={}){let Y=J(N),M=Y.getConfig();return{async compareScreenshots(X,q,G="desktop"){let Q=$.join(M.screenshotDir,G,`${X}.png`),W=$.join(M.diffDir,G,`${X}.actual.png`),Z=$.join(M.diffDir,G,`${X}.diff.png`);if(await Bun.file(Q).exists()){let F=await Bun.file(Q).arrayBuffer(),U=Buffer.from(F),_=await M.compare(q,U);if(_.match||_.diffPercentage<=M.threshold)return{viewport:G,passed:!0,baselinePath:Q};if(M.updateSnapshots)return await B.promises.mkdir($.dirname(Q),{recursive:!0}),await Bun.write(Q,q),{viewport:G,passed:!0,baselinePath:Q};if(await B.promises.mkdir($.dirname(W),{recursive:!0}),await Bun.write(W,q),_.diffImage&&M.generateDiff)await Bun.write(Z,_.diffImage);return{viewport:G,passed:!1,baselinePath:Q,actualPath:W,diffPath:M.generateDiff?Z:void 0,diffPercentage:_.diffPercentage}}return await B.promises.mkdir($.dirname(Q),{recursive:!0}),await Bun.write(Q,q),{viewport:G,passed:!0,baselinePath:Q}},async testViewports(X,q,G=M.viewports){let Q=[];for(let W of G){let Z=null;for(let F=0;F<M.retries;F++)try{let U=await q(W),_=await this.compareScreenshots(X,U,W.name);Q.push(_),Z=null;break}catch(U){if(Z=U,F<M.retries-1)await new Promise((_)=>setTimeout(_,100*(F+1)))}if(Z)Q.push({viewport:W.name,passed:!1,baselinePath:"",diffPercentage:100})}return Q},getSnapshotTester(){return Y}}}function L(N={}){let M=D(N).getSnapshotTester(),X=new Map;return{addStory(q){X.set(`${q.component}/${q.name}`,q)},addStories(q){for(let G of q)this.addStory(G)},getStories(){return Array.from(X.values())},getStoriesForComponent(q){return Array.from(X.values()).filter((G)=>G.component===q)},async testStory(q){let G=X.get(q);if(!G)return{name:q,passed:!1,duration:0,snapshots:[],screenshots:[],errors:[Error(`Story not found: ${q}`)]};let Q=Date.now(),W={name:q,passed:!0,duration:0,snapshots:[],screenshots:[],errors:[]};try{let Z=await G.render(),F=await M.snapshot(Z,q.replace(/\//g,"-"));if(W.snapshots.push(F),!F.passed)W.passed=!1}catch(Z){W.passed=!1,W.errors.push(Z)}return W.duration=Date.now()-Q,W},async testAllStories(){let q=[];for(let G of X.keys()){let Q=await this.testStory(G);q.push(Q)}return q},async generateStoryIndex(){let q={};for(let G of X.values()){if(!q[G.component])q[G.component]=[];q[G.component].push({name:G.name,args:G.args})}return JSON.stringify(q,null,2)}}}async function S(N,Y={}){let M={outputDir:Y.outputDir||"visual-test-report",format:Y.format||"html",title:Y.title||"Visual Test Report",includeScreenshots:Y.includeScreenshots??!0};await B.promises.mkdir(M.outputDir,{recursive:!0});let X=N.length,q=N.filter((F)=>F.passed).length,G=X-q,Q=N.reduce((F,U)=>F+U.duration,0);if(M.format==="json"){let F={title:M.title,summary:{total:X,passed:q,failed:G,duration:Q},results:N,generatedAt:new Date().toISOString()},U=$.join(M.outputDir,"report.json");return await Bun.write(U,JSON.stringify(F,null,2)),U}if(M.format==="markdown"){let F=`# ${M.title}
4
4
 
5
5
  `;F+=`## Summary
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/stx",
3
3
  "type": "module",
4
- "version": "0.2.343",
4
+ "version": "0.2.345",
5
5
  "description": "A performant UI Framework. Powered by Bun.",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",
@@ -117,12 +117,12 @@
117
117
  "dependencies": {
118
118
  "@stacksjs/ts-css": "^0.3.2",
119
119
  "@stacksjs/clapp": "^0.2.16",
120
- "@stacksjs/desktop": "0.2.343",
121
- "@stacksjs/sanitizer": "0.2.343",
120
+ "@stacksjs/desktop": "0.2.345",
121
+ "@stacksjs/sanitizer": "0.2.345",
122
122
  "@stacksjs/ts-i18n": "^0.1.11",
123
- "bun-plugin-stx": "0.2.343",
123
+ "bun-plugin-stx": "0.2.345",
124
124
  "bunfig": "^0.15.19",
125
- "stx-router": "0.2.343",
125
+ "stx-router": "0.2.345",
126
126
  "ts-broadcasting": "^0.0.10",
127
127
  "ts-images": "^0.2.22",
128
128
  "ts-syntax-highlighter": "^0.2.1"
@@ -1,2 +0,0 @@
1
- // @bun
2
- import{hs as a,is as b}from"./chunk-t00ga194.js";import"./chunk-0s3ry262.js";export{b as resolveStxTargets,a as looksLikeGlob};
@@ -1,2 +0,0 @@
1
- // @bun
2
- import{$l as c,cm as d,dm as e,em as f,fm as g,gm as h,hm as i,im as j,jm as k,km as l,lm as m,mm as n}from"./chunk-xgw269cf.js";import"./chunk-aqxwwq6w.js";import"./chunk-t00ga194.js";import{js as a,ks as b}from"./chunk-hrdd26r4.js";import"./chunk-7r79j1hs.js";import"./chunk-ay336sqb.js";import"./chunk-6xs80tka.js";import"./chunk-vaggjvsm.js";import"./chunk-q9ckfwhh.js";import"./chunk-kbah2k8e.js";import"./chunk-k3dtvay1.js";import"./chunk-eh3meyd3.js";import"./chunk-rzg9pars.js";import"./chunk-1qtd4ex2.js";import"./chunk-ec18ftcg.js";import"./chunk-fbmjw0bh.js";import"./chunk-53sjbp3z.js";import"./chunk-kgbz86sa.js";import"./chunk-s60eccqk.js";import"./chunk-0s3ry262.js";export{h as transformAutoImports,b as stripCommentsAndLiterals,e as renderVendorStyleTags,i as referencesName,n as processClientScript,g as injectBrowserCoreAutoImports,m as generateServerDataBridge,k as generateBrowserAutoImportDestructuring,j as generateAutoImportDestructuring,a as findInterpolationEnd,l as extractBridgeData,d as extractAndStripCssImports,f as STX_AUTO_IMPORTS,c as BROWSER_CORE_IMPORTS};
@@ -1,2 +0,0 @@
1
- // @bun
2
- import{$p as k,Ho as a,Jo as b,Lo as c,Mo as d,Vp as e,Wp as f,Xp as g,Yp as h,Zp as i,_p as j,aq as l,bq as m,cq as n,dq as o,eq as p,fq as q,gq as r,hq as s,iq as t,jq as u,kq as v,lq as w,mq as x}from"./chunk-xgw269cf.js";import"./chunk-aqxwwq6w.js";import"./chunk-t00ga194.js";import"./chunk-hrdd26r4.js";import"./chunk-7r79j1hs.js";import"./chunk-ay336sqb.js";import"./chunk-6xs80tka.js";import"./chunk-vaggjvsm.js";import"./chunk-q9ckfwhh.js";import"./chunk-kbah2k8e.js";import"./chunk-k3dtvay1.js";import"./chunk-eh3meyd3.js";import"./chunk-rzg9pars.js";import"./chunk-1qtd4ex2.js";import"./chunk-ec18ftcg.js";import"./chunk-fbmjw0bh.js";import"./chunk-53sjbp3z.js";import"./chunk-kgbz86sa.js";import"./chunk-s60eccqk.js";import"./chunk-0s3ry262.js";export{e as validateTemplate,g as validateDirective,n as userComponentFileExists,m as transpileTypeScript,p as slotContentHasExpressions,k as shouldTranspileTypeScript,t as resolveTemplatePath,r as renderComponentWithSlot,j as isJavaScriptScript,o as isHydrateTrigger,d as hasVariables,h as hasDirectives,u as getSourceLineInfo,l as getSharedTranspiler,f as getPositionInfo,x as getComponentCacheStats,s as fileExists,a as extractVariables,c as extractScriptFromTemplate,i as extractDirectiveNames,v as createDetailedErrorMessage,b as convertToCommonJS,q as componentClientSignalNames,w as clearComponentCache};
@@ -1,2 +0,0 @@
1
- // @bun
2
- import{Ue as a}from"./chunk-e6rxvz3p.js";import"./chunk-57atgbyq.js";import"./chunk-xgw269cf.js";import"./chunk-aqxwwq6w.js";import"./chunk-t00ga194.js";import"./chunk-hrdd26r4.js";import"./chunk-7r79j1hs.js";import"./chunk-ay336sqb.js";import"./chunk-6xs80tka.js";import"./chunk-vaggjvsm.js";import"./chunk-q9ckfwhh.js";import"./chunk-kbah2k8e.js";import"./chunk-k3dtvay1.js";import"./chunk-eh3meyd3.js";import"./chunk-rzg9pars.js";import"./chunk-1qtd4ex2.js";import"./chunk-ec18ftcg.js";import"./chunk-fbmjw0bh.js";import"./chunk-53sjbp3z.js";import"./chunk-kgbz86sa.js";import"./chunk-s60eccqk.js";import"./chunk-0s3ry262.js";export{a as buildApp};
@@ -1,2 +0,0 @@
1
- // @bun
2
- import{mo as a,no as b,oo as c,po as d,qo as e,ro as f,so as g,to as h,uo as i,vo as j,wo as k,xo as l,yo as m}from"./chunk-xgw269cf.js";import"./chunk-aqxwwq6w.js";import"./chunk-t00ga194.js";import"./chunk-hrdd26r4.js";import"./chunk-7r79j1hs.js";import"./chunk-ay336sqb.js";import"./chunk-6xs80tka.js";import"./chunk-vaggjvsm.js";import"./chunk-q9ckfwhh.js";import"./chunk-kbah2k8e.js";import"./chunk-k3dtvay1.js";import"./chunk-eh3meyd3.js";import"./chunk-rzg9pars.js";import"./chunk-1qtd4ex2.js";import"./chunk-ec18ftcg.js";import"./chunk-fbmjw0bh.js";import"./chunk-53sjbp3z.js";import"./chunk-kgbz86sa.js";import"./chunk-s60eccqk.js";import"./chunk-0s3ry262.js";export{m as stripStxRuntimeImports,b as scanScriptTags,l as rewriteStxImportSpecifiers,j as processSignals,h as processScriptSetup,f as preEvalLiteralReactiveIfs,c as parseLoopExpression,d as hasSignalsSyntax,k as findMarkupIndexOutsideScripts,i as extractExports,g as convertSignalLoopsToAttributes,e as convertSignalDirectivesToAttributes,a as clearScriptScanCache};
@@ -1,2 +0,0 @@
1
- // @bun
2
- import{Qo as a,Ro as b,So as c,To as d,Uo as e,Vo as f,Wo as g}from"./chunk-xgw269cf.js";import"./chunk-aqxwwq6w.js";import"./chunk-t00ga194.js";import"./chunk-hrdd26r4.js";import"./chunk-7r79j1hs.js";import"./chunk-ay336sqb.js";import"./chunk-6xs80tka.js";import"./chunk-vaggjvsm.js";import"./chunk-q9ckfwhh.js";import"./chunk-kbah2k8e.js";import"./chunk-k3dtvay1.js";import"./chunk-eh3meyd3.js";import"./chunk-rzg9pars.js";import"./chunk-1qtd4ex2.js";import"./chunk-ec18ftcg.js";import"./chunk-fbmjw0bh.js";import"./chunk-53sjbp3z.js";import"./chunk-kgbz86sa.js";import"./chunk-s60eccqk.js";import"./chunk-0s3ry262.js";export{a as resetUnscopedSignalWarnings,g as processStackReplacements,f as processStackPushDirectives,e as processIncludes,b as partialsCache,c as onceStore,d as clearOnceStore};
@@ -1,2 +0,0 @@
1
- // @bun
2
- import{$k as c,Zk as a,_k as b,al as d,bl as e,cl as f,dl as g,el as h,fl as i,gl as j,hl as k,il as l,jl as m,kl as n,ll as o}from"./chunk-57dxdvjh.js";import"./chunk-2853ye06.js";import"./chunk-fvsefztj.js";import"./chunk-xgw269cf.js";import"./chunk-aqxwwq6w.js";import"./chunk-t00ga194.js";import"./chunk-hrdd26r4.js";import"./chunk-7r79j1hs.js";import"./chunk-ay336sqb.js";import"./chunk-6xs80tka.js";import"./chunk-vaggjvsm.js";import"./chunk-q9ckfwhh.js";import"./chunk-kbah2k8e.js";import"./chunk-k3dtvay1.js";import"./chunk-eh3meyd3.js";import"./chunk-rzg9pars.js";import"./chunk-1qtd4ex2.js";import"./chunk-ec18ftcg.js";import"./chunk-fbmjw0bh.js";import"./chunk-53sjbp3z.js";import"./chunk-kgbz86sa.js";import"./chunk-s60eccqk.js";import"./chunk-0s3ry262.js";export{o as tempBuildName,b as shouldLogBundlerDiagnostics,f as setBundlerFallbackMode,l as rewriteRegistryImports,g as resolveBundlerFallback,j as registryIdForPath,c as recordBundleFailure,m as queueClientBundleBuild,k as normalizeRegistrySpecifiers,h as hasUserImports,d as getBundleFailures,a as clientBundleDependencies,e as clearBundleFailures,n as bundleClientScript,i as MODULE_SPECIFIER_PREFIX};