@stacksjs/stx 0.2.123 → 0.2.125

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -4226,7 +4226,10 @@ catch (e) {
4226
4226
  }
4227
4227
  }
4228
4228
  if (el.nodeType === Node.TEXT_NODE) {
4229
- const text = el.textContent;
4229
+ // DOM textContent is specified as a string, but lightweight DOM
4230
+ // implementations and user-created nodes can retain a numeric value
4231
+ // assigned by a binding. Normalize before interpolation detection.
4232
+ const text = el.textContent == null ? '' : String(el.textContent);
4230
4233
  if (text && text.includes('{{')) {
4231
4234
  const parts = text.split(/(\\{\\{[\\s\\S]+?\\}\\})/g);
4232
4235
  if (parts.length > 1) {
@@ -5136,13 +5139,24 @@ else if (typeof value === 'string') {
5136
5139
  var __forComponentSetupFactories = new Map();
5137
5140
 
5138
5141
  function forEachGroupElement(nodes, callback) {
5139
- nodes.forEach(function(node) {
5142
+ function visit(node, insideTemplateContent) {
5140
5143
  if (!node || node.nodeType !== Node.ELEMENT_NODE) return;
5141
- callback(node);
5142
- if (node.querySelectorAll) {
5143
- node.querySelectorAll('*').forEach(function(child) { callback(child); });
5144
- }
5145
- });
5144
+ callback(node, insideTemplateContent);
5145
+
5146
+ // HTMLTemplateElement children live in template.content, outside the
5147
+ // element's ordinary descendant tree. Component roots and setup scripts
5148
+ // wrapped by caller-owned :if/:for directives must still be discoverable
5149
+ // for scope remapping before those templates are cloned.
5150
+ var entersTemplateContent = node.tagName === 'TEMPLATE' && node.content;
5151
+ var childRoot = entersTemplateContent
5152
+ ? node.content
5153
+ : node;
5154
+ Array.from(childRoot.childNodes || []).forEach(function(child) {
5155
+ visit(child, insideTemplateContent || entersTemplateContent);
5156
+ });
5157
+ }
5158
+
5159
+ nodes.forEach(function(node) { visit(node, false); });
5146
5160
  }
5147
5161
 
5148
5162
  // A signal component expanded inside <template :for> carries a compiled
@@ -5190,7 +5204,11 @@ else if (typeof value === 'string') {
5190
5204
  function hydrateComponentScopes(nodes, callerScope) {
5191
5205
  var roots = [];
5192
5206
  var scripts = [];
5193
- forEachGroupElement(nodes, function(node) {
5207
+ forEachGroupElement(nodes, function(node, insideTemplateContent) {
5208
+ // A template's content is inert source material. Remapping must traverse
5209
+ // it, but setup and event hydration belong to the live clones inserted by
5210
+ // bindIf/bindFor, where the correct iteration scope is available.
5211
+ if (insideTemplateContent) return;
5194
5212
  if (node.hasAttribute && node.hasAttribute('data-stx-scope')) roots.push(node);
5195
5213
  if (node.tagName === 'SCRIPT' && node.hasAttribute('data-stx-scoped')) scripts.push(node);
5196
5214
  });
@@ -6020,37 +6038,38 @@ catch (e2) {
6020
6038
  pick.el.__stx_shown_at = performance.now();
6021
6039
  if (!pick.childrenProcessed) {
6022
6040
  pick.childrenProcessed = true;
6023
- // Defer child processing (same rationale as bindIf, CLAUDE.md
6024
- // item 35): keep child effects from subscribing to THIS chain
6025
- // effect's tracked signals.
6026
- (function (branch) {
6027
- setTimeout(function () {
6028
- var childScope = { ...globalHelpers, ...capturedComponentScope, ...(branch.capturedElementScope || {}) };
6029
- // Components compiled inside any branch of an if/else-if/else
6030
- // chain carry scoped setup scripts with them. Branch nodes are
6031
- // detached before those scripts can execute, so simply
6032
- // re-inserting the selected branch leaves component props,
6033
- // events, directives, and projected slot expressions inert.
6034
- // Hydrate those scopes before the normal subtree walk, matching
6035
- // the single-template :if and :for insertion paths.
6036
- var previouslyTrackedNodes = new Set();
6037
- branch.nodes.forEach(function (n) {
6038
- if (n && n.__stx_disposers) previouslyTrackedNodes.add(n);
6039
- });
6040
- hydrateComponentScopes(branch.nodes, childScope);
6041
- branch.nodes.forEach(function (n) {
6042
- if (n.nodeType !== 1) return; // whitespace/text between template children
6043
- // A scoped component receives its disposer in the outer scope
6044
- // walk immediately after processElement returns from binding
6045
- // this conditional. That first pass intentionally stops at
6046
- // :if, so a disposer does not mean its children were hydrated.
6047
- if (previouslyTrackedNodes.has(n) || !n.__stx_disposers)
6048
- processElement(n, childScope);
6049
- n.removeAttribute('x-cloak');
6050
- n.querySelectorAll('[x-cloak]').forEach(function (c) { c.removeAttribute('x-cloak'); });
6051
- });
6052
- }, 0);
6053
- })(pick);
6041
+ // Hydrate before the outer scope walk can see the inserted branch.
6042
+ // Chromium executes scripts cloned from template.content as soon as
6043
+ // they are connected, so deferring this pass allowed the global walk
6044
+ // to process projected slot expressions without the loop caller
6045
+ // scope. peek() keeps this synchronous pass from subscribing child
6046
+ // reads to the current chain effect.
6047
+ peek(function () {
6048
+ var childScope = { ...globalHelpers, ...capturedComponentScope, ...(pick.capturedElementScope || {}) };
6049
+ // Components compiled inside any branch of an if/else-if/else
6050
+ // chain carry scoped setup scripts with them. Branch nodes are
6051
+ // detached before those scripts can execute, so simply
6052
+ // re-inserting the selected branch leaves component props,
6053
+ // events, directives, and projected slot expressions inert.
6054
+ // Hydrate those scopes before the normal subtree walk, matching
6055
+ // the single-template :if and :for insertion paths.
6056
+ var previouslyTrackedNodes = new Set();
6057
+ pick.nodes.forEach(function (n) {
6058
+ if (n && n.__stx_disposers) previouslyTrackedNodes.add(n);
6059
+ });
6060
+ hydrateComponentScopes(pick.nodes, childScope);
6061
+ pick.nodes.forEach(function (n) {
6062
+ if (n.nodeType !== 1) return; // whitespace/text between template children
6063
+ // A scoped component receives its disposer in the outer scope
6064
+ // walk immediately after processElement returns from binding
6065
+ // this conditional. That first pass intentionally stops at
6066
+ // :if, so a disposer does not mean its children were hydrated.
6067
+ if (previouslyTrackedNodes.has(n) || !n.__stx_disposers)
6068
+ processElement(n, childScope);
6069
+ n.removeAttribute('x-cloak');
6070
+ n.querySelectorAll('[x-cloak]').forEach(function (c) { c.removeAttribute('x-cloak'); });
6071
+ });
6072
+ });
6054
6073
  }
6055
6074
  }
6056
6075
 
@@ -6221,10 +6240,13 @@ else if (!value && isInserted) {
6221
6240
  else if (value && isInserted && !childrenProcessed) {
6222
6241
  // Initial template content is cloned before the effect runs. Process
6223
6242
  // it after the condition is confirmed so hidden branches stay inert.
6243
+ // Keep hydration synchronous so the outer scope walk cannot bind
6244
+ // caller-owned slot expressions first, while peek() prevents child
6245
+ // reads from subscribing to this conditional effect.
6224
6246
  childrenProcessed = true;
6225
- setTimeout(function() {
6247
+ peek(function() {
6226
6248
  if (isInserted) processTemplateNodes(currentNodes);
6227
- }, 0);
6249
+ });
6228
6250
  }
6229
6251
  }
6230
6252
  else {
@@ -22422,7 +22444,7 @@ else {
22422
22444
  `)}function QR($,Z){let Y=" ".repeat(Z),X=[];for(let[K,W]of Object.entries($.props))if(W.type==="literal"){let U=typeof W.value==="string"?`"${W.value}"`:String(W.value);X.push(`${K}: ${U}`)}let J=X.length>0?`{ ${X.join(", ")} }`:"{}",Q=$.children.map((K)=>K.type==="text"?`"${K.props.text?.type==="literal"?K.props.text.value:""}"`:QR(K,Z+1)),G=Q.length>0?`,
22423
22445
  ${Y} [${Q.join(`,
22424
22446
  ${Y} `)}]`:"";return`Craft.create("${$.zigType}", ${J}${G})`}function ih($,Z,Y){if(Z.target==="universal"){let X=[];X.push("// Universal entry point"),X.push("");for(let[J,Q]of Y)X.push(`// --- ${J.toUpperCase()} ---`),X.push(`#if ${J.toUpperCase()}`),X.push(Q),X.push("#endif"),X.push("");return X.join(`
22425
- `)}return Y.get(Z.target)||fU($,Z)}function nh($,Z,Y){let X=[];function J(Q){X.push({name:Q.zigType,hash:KR(JSON.stringify(Q.props)),props:Object.keys(Q.props),events:Array.from(Q.events.keys()),slots:[],isNative:Q.zigType.startsWith("craft.")});for(let G of Q.children)J(G)}return J(Z),{version:"1.0.0",components:X,entry:$.id,assets:[],styles:$.styles}}function rh($,Z){return JSON.stringify({version:3,file:"output.zig",sources:["input.stx"],mappings:""})}function GR(){return`id_${++ah}`}function KR($){let Z=0;for(let Y=0;Y<$.length;Y++){let X=$.charCodeAt(Y);Z=(Z<<5)-Z+X,Z=Z&Z}return Math.abs(Z).toString(36)}function sh($){return $.charAt(0).toUpperCase()+$.slice(1)}function oh($){let Z=[],Y=/<script[^>]*>([\s\S]*?)<\/script>/gi,X;while((X=Y.exec($))!==null)Z.push(X[1]);return Z}function th($){let Z=[],Y=/<style[^>]*>([\s\S]*?)<\/style>/gi,X;while((X=Y.exec($))!==null)Z.push(X[1]);return Z}function gU($){let Z=$.type==="component"?1:0;if($.children)for(let Y of $.children)Z+=gU(Y);return Z}function hU($){let Z=$.zigType.startsWith("craft.")?1:0;for(let Y of $.children)Z+=hU(Y);return Z}function WR($){return{start:async()=>{console.log("[craft-compiler] Hot reload server started")},stop:async()=>{console.log("[craft-compiler] Hot reload server stopped")},notifyChange:(Z)=>{console.log(`[craft-compiler] File changed: ${Z}`)}}}var dU,ah=0,eh,$d;var zR=D(()=>{dU=new Map([["craft-button",{name:"Button",zigType:"craft.Button",props:[{name:"variant",type:"string",required:!1,default:"primary"},{name:"disabled",type:"boolean",required:!1,default:!1},{name:"onclick",type:"callback",required:!1}],children:"single"}],["craft-text-input",{name:"TextInput",zigType:"craft.TextInput",props:[{name:"value",type:"string",required:!1,default:""},{name:"placeholder",type:"string",required:!1},{name:"disabled",type:"boolean",required:!1,default:!1},{name:"onchange",type:"callback",required:!1}],children:"none"}],["craft-textarea",{name:"TextArea",zigType:"craft.TextArea",props:[{name:"value",type:"string",required:!1,default:""},{name:"placeholder",type:"string",required:!1},{name:"rows",type:"number",required:!1,default:3},{name:"disabled",type:"boolean",required:!1,default:!1}],children:"none"}],["craft-checkbox",{name:"Checkbox",zigType:"craft.Checkbox",props:[{name:"checked",type:"boolean",required:!1,default:!1},{name:"label",type:"string",required:!1},{name:"disabled",type:"boolean",required:!1,default:!1},{name:"onchange",type:"callback",required:!1}],children:"none"}],["craft-select",{name:"Select",zigType:"craft.Select",props:[{name:"value",type:"string",required:!1},{name:"options",type:"array",required:!0},{name:"disabled",type:"boolean",required:!1,default:!1},{name:"onchange",type:"callback",required:!1}],children:"none"}],["craft-slider",{name:"Slider",zigType:"craft.Slider",props:[{name:"value",type:"number",required:!1,default:0},{name:"min",type:"number",required:!1,default:0},{name:"max",type:"number",required:!1,default:100},{name:"step",type:"number",required:!1,default:1},{name:"onchange",type:"callback",required:!1}],children:"none"}],["craft-toggle",{name:"Toggle",zigType:"craft.Toggle",props:[{name:"checked",type:"boolean",required:!1,default:!1},{name:"disabled",type:"boolean",required:!1,default:!1},{name:"onchange",type:"callback",required:!1}],children:"none"}],["craft-modal",{name:"Modal",zigType:"craft.Modal",props:[{name:"open",type:"boolean",required:!1,default:!1},{name:"title",type:"string",required:!1},{name:"closable",type:"boolean",required:!1,default:!0},{name:"onclose",type:"callback",required:!1}],children:"multiple"}],["craft-alert",{name:"Alert",zigType:"craft.Alert",props:[{name:"variant",type:"string",required:!1,default:"info"},{name:"title",type:"string",required:!1},{name:"dismissible",type:"boolean",required:!1,default:!1}],children:"single"}],["craft-progress",{name:"ProgressBar",zigType:"craft.ProgressBar",props:[{name:"value",type:"number",required:!1,default:0},{name:"max",type:"number",required:!1,default:100},{name:"indeterminate",type:"boolean",required:!1,default:!1}],children:"none"}],["craft-spinner",{name:"Spinner",zigType:"craft.Spinner",props:[{name:"size",type:"number",required:!1,default:24}],children:"none"}],["craft-avatar",{name:"Avatar",zigType:"craft.Avatar",props:[{name:"src",type:"string",required:!1},{name:"alt",type:"string",required:!1},{name:"size",type:"number",required:!1,default:40}],children:"none"}],["craft-badge",{name:"Badge",zigType:"craft.Badge",props:[{name:"variant",type:"string",required:!1,default:"default"}],children:"single"}],["craft-card",{name:"Card",zigType:"craft.Card",props:[{name:"padding",type:"number",required:!1,default:16}],children:"multiple"}],["craft-sidebar",{name:"Sidebar",zigType:"craft.NativeSidebar",props:[{name:"id",type:"string",required:!1},{name:"variant",type:"string",required:!1,default:"desktop"},{name:"material",type:"string",required:!1,default:"sidebar"},{name:"backgroundEffect",type:"string",required:!1,default:"shimmer"},{name:"allowsVibrancy",type:"boolean",required:!1,default:!0},{name:"materialOpacity",type:"number",required:!1,default:0.68},{name:"minWidth",type:"number",required:!1,default:240},{name:"maxWidth",type:"number",required:!1,default:340},{name:"canCollapse",type:"boolean",required:!1,default:!0},{name:"selectedItem",type:"string",required:!1},{name:"sections",type:"array",required:!1}],children:"multiple",platform:["macos"],fallback:"aside"}],["craft-sidebar-section",{name:"SidebarSection",zigType:"craft.NativeSidebarSection",props:[{name:"id",type:"string",required:!0},{name:"title",type:"string",required:!1},{name:"header",type:"string",required:!1}],children:"multiple",platform:["macos"],fallback:"section"}],["craft-sidebar-item",{name:"SidebarItem",zigType:"craft.NativeSidebarItem",props:[{name:"id",type:"string",required:!0},{name:"label",type:"string",required:!0},{name:"icon",type:"string",required:!1},{name:"badge",type:"string",required:!1},{name:"tintColor",type:"color",required:!1}],children:"none",platform:["macos"],fallback:"button"}],["craft-accordion",{name:"Accordion",zigType:"craft.Accordion",props:[{name:"title",type:"string",required:!0},{name:"open",type:"boolean",required:!1,default:!1}],children:"multiple"}],["craft-tabs",{name:"TabView",zigType:"craft.TabView",props:[{name:"activeTab",type:"number",required:!1,default:0},{name:"ontabchange",type:"callback",required:!1}],children:"multiple"}],["craft-tooltip",{name:"Tooltip",zigType:"craft.Tooltip",props:[{name:"content",type:"string",required:!0},{name:"position",type:"string",required:!1,default:"top"}],children:"single"}],["craft-date-picker",{name:"DatePicker",zigType:"craft.DatePicker",props:[{name:"value",type:"string",required:!1},{name:"min",type:"string",required:!1},{name:"max",type:"string",required:!1},{name:"onchange",type:"callback",required:!1}],children:"none"}],["craft-time-picker",{name:"TimePicker",zigType:"craft.TimePicker",props:[{name:"value",type:"string",required:!1},{name:"onchange",type:"callback",required:!1}],children:"none"}],["craft-color-picker",{name:"ColorPicker",zigType:"craft.ColorPicker",props:[{name:"value",type:"color",required:!1,default:"#000000"},{name:"onchange",type:"callback",required:!1}],children:"none"}],["craft-data-table",{name:"DataTable",zigType:"craft.DataTable",props:[{name:"data",type:"array",required:!0},{name:"columns",type:"array",required:!0},{name:"sortable",type:"boolean",required:!1,default:!1},{name:"paginated",type:"boolean",required:!1,default:!1}],children:"none"}],["craft-virtual-list",{name:"VirtualList",zigType:"craft.VirtualList",props:[{name:"items",type:"array",required:!0},{name:"itemHeight",type:"number",required:!1,default:48},{name:"renderItem",type:"callback",required:!0}],children:"none"}],["craft-tree-view",{name:"TreeView",zigType:"craft.TreeView",props:[{name:"data",type:"object",required:!0},{name:"expandable",type:"boolean",required:!1,default:!0}],children:"none"}]]);eh={compileToNative:$R,createHotReloadServer:WR,NATIVE_COMPONENTS:dU},$d=eh});import{spawn as qR}from"child_process";import f0 from"fs";import Zd from"os";import Y0 from"path";import v from"process";import{CLI as Yd}from"@stacksjs/clapp";var E6="0.2.123";j$();jU();AK();kK();r4();EK();OK();JU();var __dirname="/home/runner/work/stx/stx/packages/stx/bin",V0=new Yd("stx");function _R($){let Z=typeof $==="string"?Number.parseInt($,10):$;if(Number.isNaN(Z))return{isValid:!1,error:"Port must be a valid number",suggestion:"Try using a number between 1024 and 65535"};if(Z<1024||Z>65535)return{isValid:!1,error:"Port must be between 1024 and 65535",suggestion:"Try using a port between 1024 and 65535"};return{isValid:!0}}function BR($){try{if(!f0.existsSync($)){let Y=Y0.dirname($),X=Y0.basename($);if(f0.existsSync(Y)){let Q=f0.readdirSync(Y).filter((K)=>K.toLowerCase().includes(X.toLowerCase().substring(0,3))||K.endsWith(".stx")||K.endsWith(".md")),G=Q.length>0?`File not found. Similar files in ${Y}: ${Q.slice(0,3).join(", ")}`:`File not found. Directory ${Y} exists but doesn't contain the specified file.`;return{isValid:!1,error:`File does not exist: ${$}`,suggestion:G}}return{isValid:!1,error:`File does not exist: ${$}`,suggestion:"Check the file path and try again"}}if(f0.statSync($).isDirectory())return{isValid:!1,error:`Expected file but got directory: ${$}`,suggestion:"Specify a .stx or .md file path"};return{isValid:!0}}catch(Z){return{isValid:!1,error:`Cannot access file: ${$}`,suggestion:"Check file permissions and path"}}}function uU($){let Z=typeof $==="string"?Number.parseInt($,10):$;if(Number.isNaN(Z))return{isValid:!1,error:"Timeout must be a valid number",suggestion:"Specify timeout in milliseconds (e.g., 5000 for 5 seconds)"};if(Z<100)return{isValid:!1,error:"Timeout too short (minimum 100ms)",suggestion:"Use a timeout of at least 100ms"};if(Z>300000)return{isValid:!1,error:"Timeout too long (maximum 5 minutes)",suggestion:"Use a timeout of less than 300000ms (5 minutes)"};return{isValid:!0}}var e5=($)=>$.includes("*")||$.includes("?")||$.includes("{")||$.includes("["),Xd=($)=>$.endsWith(".stx")||$.endsWith(".md");function t5($,Z=1){if(console.error(`\u274C ${$.error}`),$.suggestion)console.error(`\uD83D\uDCA1 suggestion: ${$.suggestion}`);v.exit(Z)}function Jd($){if(!$)return;let Z;try{Z=JSON.parse($)}catch(Y){console.error(`\u274C --info-plist is not valid JSON: ${Y.message}`),v.exit(1)}if(typeof Z!=="object"||Z===null||Array.isArray(Z))console.error(`\u274C --info-plist must be a JSON object, e.g. '{"NSFocusStatusUsageDescription":"\u2026"}'`),v.exit(1);for(let[Y,X]of Object.entries(Z))if(typeof X!=="string"&&typeof X!=="number"&&typeof X!=="boolean")console.error(`\u274C --info-plist value for "${Y}" must be a string, number or boolean`),v.exit(1);return Z}async function Qd($,Z){try{let Y=["test"],X=[];if($&&$.length>0)for(let W of $)if(e5(W))try{let U=await Array.fromAsync(new Bun.Glob(W).scan({onlyFiles:!0,absolute:!0}));if(U.length===0)console.warn(`Warning: No files found matching pattern: ${W}`);else if(X.push(...U),Z.verbose)console.log(`Found ${U.length} file(s) matching pattern: ${W}`)}catch(U){return console.error(`Error expanding glob pattern ${W}:`,U),!1}else X.push(W);if(X.length===0)return console.error("Error: No test files found matching the provided patterns."),!1;if(Y.push(...X),Z.filter)Y.push("--pattern",Z.filter);if(Z.timeout)Y.push("--timeout",Z.timeout.toString());if(Z.reporter&&Z.reporter!=="default")if(Z.reporter==="junit"){let W=Y0.join(Zd.tmpdir(),`stx-test-report-${Date.now()}.xml`);Y.push("--reporter",Z.reporter),Y.push("--reporter-outfile",W)}else console.warn(`Warning: Reporter '${Z.reporter}' is not supported. Using default reporter.`);if(Z.coverage)Y.push("--coverage");if(Z.watch)Y.push("--watch");Y.push("--preload",Y0.join(__dirname,"../../../test-utils/happy-dom.ts"));let J={...v.env,STX_TEST_MODE:"true",STX_TEST_UI:Z.ui?"true":"false",STX_TEST_VERBOSE:Z.verbose?"true":"false"},Q="bun";if(Z.verbose)console.log(`Running: ${Q} ${Y.join(" ")}`);let G=performance.now(),K=qR(Q,Y,{stdio:"inherit",shell:!1,env:J});return new Promise((W)=>{K.on("close",(U)=>{let _=((performance.now()-G)/1000).toFixed(2);if(U===0)W(!0);else W(!1)}),v.on("SIGINT",()=>{console.log(`
22447
+ `)}return Y.get(Z.target)||fU($,Z)}function nh($,Z,Y){let X=[];function J(Q){X.push({name:Q.zigType,hash:KR(JSON.stringify(Q.props)),props:Object.keys(Q.props),events:Array.from(Q.events.keys()),slots:[],isNative:Q.zigType.startsWith("craft.")});for(let G of Q.children)J(G)}return J(Z),{version:"1.0.0",components:X,entry:$.id,assets:[],styles:$.styles}}function rh($,Z){return JSON.stringify({version:3,file:"output.zig",sources:["input.stx"],mappings:""})}function GR(){return`id_${++ah}`}function KR($){let Z=0;for(let Y=0;Y<$.length;Y++){let X=$.charCodeAt(Y);Z=(Z<<5)-Z+X,Z=Z&Z}return Math.abs(Z).toString(36)}function sh($){return $.charAt(0).toUpperCase()+$.slice(1)}function oh($){let Z=[],Y=/<script[^>]*>([\s\S]*?)<\/script>/gi,X;while((X=Y.exec($))!==null)Z.push(X[1]);return Z}function th($){let Z=[],Y=/<style[^>]*>([\s\S]*?)<\/style>/gi,X;while((X=Y.exec($))!==null)Z.push(X[1]);return Z}function gU($){let Z=$.type==="component"?1:0;if($.children)for(let Y of $.children)Z+=gU(Y);return Z}function hU($){let Z=$.zigType.startsWith("craft.")?1:0;for(let Y of $.children)Z+=hU(Y);return Z}function WR($){return{start:async()=>{console.log("[craft-compiler] Hot reload server started")},stop:async()=>{console.log("[craft-compiler] Hot reload server stopped")},notifyChange:(Z)=>{console.log(`[craft-compiler] File changed: ${Z}`)}}}var dU,ah=0,eh,$d;var zR=D(()=>{dU=new Map([["craft-button",{name:"Button",zigType:"craft.Button",props:[{name:"variant",type:"string",required:!1,default:"primary"},{name:"disabled",type:"boolean",required:!1,default:!1},{name:"onclick",type:"callback",required:!1}],children:"single"}],["craft-text-input",{name:"TextInput",zigType:"craft.TextInput",props:[{name:"value",type:"string",required:!1,default:""},{name:"placeholder",type:"string",required:!1},{name:"disabled",type:"boolean",required:!1,default:!1},{name:"onchange",type:"callback",required:!1}],children:"none"}],["craft-textarea",{name:"TextArea",zigType:"craft.TextArea",props:[{name:"value",type:"string",required:!1,default:""},{name:"placeholder",type:"string",required:!1},{name:"rows",type:"number",required:!1,default:3},{name:"disabled",type:"boolean",required:!1,default:!1}],children:"none"}],["craft-checkbox",{name:"Checkbox",zigType:"craft.Checkbox",props:[{name:"checked",type:"boolean",required:!1,default:!1},{name:"label",type:"string",required:!1},{name:"disabled",type:"boolean",required:!1,default:!1},{name:"onchange",type:"callback",required:!1}],children:"none"}],["craft-select",{name:"Select",zigType:"craft.Select",props:[{name:"value",type:"string",required:!1},{name:"options",type:"array",required:!0},{name:"disabled",type:"boolean",required:!1,default:!1},{name:"onchange",type:"callback",required:!1}],children:"none"}],["craft-slider",{name:"Slider",zigType:"craft.Slider",props:[{name:"value",type:"number",required:!1,default:0},{name:"min",type:"number",required:!1,default:0},{name:"max",type:"number",required:!1,default:100},{name:"step",type:"number",required:!1,default:1},{name:"onchange",type:"callback",required:!1}],children:"none"}],["craft-toggle",{name:"Toggle",zigType:"craft.Toggle",props:[{name:"checked",type:"boolean",required:!1,default:!1},{name:"disabled",type:"boolean",required:!1,default:!1},{name:"onchange",type:"callback",required:!1}],children:"none"}],["craft-modal",{name:"Modal",zigType:"craft.Modal",props:[{name:"open",type:"boolean",required:!1,default:!1},{name:"title",type:"string",required:!1},{name:"closable",type:"boolean",required:!1,default:!0},{name:"onclose",type:"callback",required:!1}],children:"multiple"}],["craft-alert",{name:"Alert",zigType:"craft.Alert",props:[{name:"variant",type:"string",required:!1,default:"info"},{name:"title",type:"string",required:!1},{name:"dismissible",type:"boolean",required:!1,default:!1}],children:"single"}],["craft-progress",{name:"ProgressBar",zigType:"craft.ProgressBar",props:[{name:"value",type:"number",required:!1,default:0},{name:"max",type:"number",required:!1,default:100},{name:"indeterminate",type:"boolean",required:!1,default:!1}],children:"none"}],["craft-spinner",{name:"Spinner",zigType:"craft.Spinner",props:[{name:"size",type:"number",required:!1,default:24}],children:"none"}],["craft-avatar",{name:"Avatar",zigType:"craft.Avatar",props:[{name:"src",type:"string",required:!1},{name:"alt",type:"string",required:!1},{name:"size",type:"number",required:!1,default:40}],children:"none"}],["craft-badge",{name:"Badge",zigType:"craft.Badge",props:[{name:"variant",type:"string",required:!1,default:"default"}],children:"single"}],["craft-card",{name:"Card",zigType:"craft.Card",props:[{name:"padding",type:"number",required:!1,default:16}],children:"multiple"}],["craft-sidebar",{name:"Sidebar",zigType:"craft.NativeSidebar",props:[{name:"id",type:"string",required:!1},{name:"variant",type:"string",required:!1,default:"desktop"},{name:"material",type:"string",required:!1,default:"sidebar"},{name:"backgroundEffect",type:"string",required:!1,default:"shimmer"},{name:"allowsVibrancy",type:"boolean",required:!1,default:!0},{name:"materialOpacity",type:"number",required:!1,default:0.68},{name:"minWidth",type:"number",required:!1,default:240},{name:"maxWidth",type:"number",required:!1,default:340},{name:"canCollapse",type:"boolean",required:!1,default:!0},{name:"selectedItem",type:"string",required:!1},{name:"sections",type:"array",required:!1}],children:"multiple",platform:["macos"],fallback:"aside"}],["craft-sidebar-section",{name:"SidebarSection",zigType:"craft.NativeSidebarSection",props:[{name:"id",type:"string",required:!0},{name:"title",type:"string",required:!1},{name:"header",type:"string",required:!1}],children:"multiple",platform:["macos"],fallback:"section"}],["craft-sidebar-item",{name:"SidebarItem",zigType:"craft.NativeSidebarItem",props:[{name:"id",type:"string",required:!0},{name:"label",type:"string",required:!0},{name:"icon",type:"string",required:!1},{name:"badge",type:"string",required:!1},{name:"tintColor",type:"color",required:!1}],children:"none",platform:["macos"],fallback:"button"}],["craft-accordion",{name:"Accordion",zigType:"craft.Accordion",props:[{name:"title",type:"string",required:!0},{name:"open",type:"boolean",required:!1,default:!1}],children:"multiple"}],["craft-tabs",{name:"TabView",zigType:"craft.TabView",props:[{name:"activeTab",type:"number",required:!1,default:0},{name:"ontabchange",type:"callback",required:!1}],children:"multiple"}],["craft-tooltip",{name:"Tooltip",zigType:"craft.Tooltip",props:[{name:"content",type:"string",required:!0},{name:"position",type:"string",required:!1,default:"top"}],children:"single"}],["craft-date-picker",{name:"DatePicker",zigType:"craft.DatePicker",props:[{name:"value",type:"string",required:!1},{name:"min",type:"string",required:!1},{name:"max",type:"string",required:!1},{name:"onchange",type:"callback",required:!1}],children:"none"}],["craft-time-picker",{name:"TimePicker",zigType:"craft.TimePicker",props:[{name:"value",type:"string",required:!1},{name:"onchange",type:"callback",required:!1}],children:"none"}],["craft-color-picker",{name:"ColorPicker",zigType:"craft.ColorPicker",props:[{name:"value",type:"color",required:!1,default:"#000000"},{name:"onchange",type:"callback",required:!1}],children:"none"}],["craft-data-table",{name:"DataTable",zigType:"craft.DataTable",props:[{name:"data",type:"array",required:!0},{name:"columns",type:"array",required:!0},{name:"sortable",type:"boolean",required:!1,default:!1},{name:"paginated",type:"boolean",required:!1,default:!1}],children:"none"}],["craft-virtual-list",{name:"VirtualList",zigType:"craft.VirtualList",props:[{name:"items",type:"array",required:!0},{name:"itemHeight",type:"number",required:!1,default:48},{name:"renderItem",type:"callback",required:!0}],children:"none"}],["craft-tree-view",{name:"TreeView",zigType:"craft.TreeView",props:[{name:"data",type:"object",required:!0},{name:"expandable",type:"boolean",required:!1,default:!0}],children:"none"}]]);eh={compileToNative:$R,createHotReloadServer:WR,NATIVE_COMPONENTS:dU},$d=eh});import{spawn as qR}from"child_process";import f0 from"fs";import Zd from"os";import Y0 from"path";import v from"process";import{CLI as Yd}from"@stacksjs/clapp";var E6="0.2.125";j$();jU();AK();kK();r4();EK();OK();JU();var __dirname="/home/runner/work/stx/stx/packages/stx/bin",V0=new Yd("stx");function _R($){let Z=typeof $==="string"?Number.parseInt($,10):$;if(Number.isNaN(Z))return{isValid:!1,error:"Port must be a valid number",suggestion:"Try using a number between 1024 and 65535"};if(Z<1024||Z>65535)return{isValid:!1,error:"Port must be between 1024 and 65535",suggestion:"Try using a port between 1024 and 65535"};return{isValid:!0}}function BR($){try{if(!f0.existsSync($)){let Y=Y0.dirname($),X=Y0.basename($);if(f0.existsSync(Y)){let Q=f0.readdirSync(Y).filter((K)=>K.toLowerCase().includes(X.toLowerCase().substring(0,3))||K.endsWith(".stx")||K.endsWith(".md")),G=Q.length>0?`File not found. Similar files in ${Y}: ${Q.slice(0,3).join(", ")}`:`File not found. Directory ${Y} exists but doesn't contain the specified file.`;return{isValid:!1,error:`File does not exist: ${$}`,suggestion:G}}return{isValid:!1,error:`File does not exist: ${$}`,suggestion:"Check the file path and try again"}}if(f0.statSync($).isDirectory())return{isValid:!1,error:`Expected file but got directory: ${$}`,suggestion:"Specify a .stx or .md file path"};return{isValid:!0}}catch(Z){return{isValid:!1,error:`Cannot access file: ${$}`,suggestion:"Check file permissions and path"}}}function uU($){let Z=typeof $==="string"?Number.parseInt($,10):$;if(Number.isNaN(Z))return{isValid:!1,error:"Timeout must be a valid number",suggestion:"Specify timeout in milliseconds (e.g., 5000 for 5 seconds)"};if(Z<100)return{isValid:!1,error:"Timeout too short (minimum 100ms)",suggestion:"Use a timeout of at least 100ms"};if(Z>300000)return{isValid:!1,error:"Timeout too long (maximum 5 minutes)",suggestion:"Use a timeout of less than 300000ms (5 minutes)"};return{isValid:!0}}var e5=($)=>$.includes("*")||$.includes("?")||$.includes("{")||$.includes("["),Xd=($)=>$.endsWith(".stx")||$.endsWith(".md");function t5($,Z=1){if(console.error(`\u274C ${$.error}`),$.suggestion)console.error(`\uD83D\uDCA1 suggestion: ${$.suggestion}`);v.exit(Z)}function Jd($){if(!$)return;let Z;try{Z=JSON.parse($)}catch(Y){console.error(`\u274C --info-plist is not valid JSON: ${Y.message}`),v.exit(1)}if(typeof Z!=="object"||Z===null||Array.isArray(Z))console.error(`\u274C --info-plist must be a JSON object, e.g. '{"NSFocusStatusUsageDescription":"\u2026"}'`),v.exit(1);for(let[Y,X]of Object.entries(Z))if(typeof X!=="string"&&typeof X!=="number"&&typeof X!=="boolean")console.error(`\u274C --info-plist value for "${Y}" must be a string, number or boolean`),v.exit(1);return Z}async function Qd($,Z){try{let Y=["test"],X=[];if($&&$.length>0)for(let W of $)if(e5(W))try{let U=await Array.fromAsync(new Bun.Glob(W).scan({onlyFiles:!0,absolute:!0}));if(U.length===0)console.warn(`Warning: No files found matching pattern: ${W}`);else if(X.push(...U),Z.verbose)console.log(`Found ${U.length} file(s) matching pattern: ${W}`)}catch(U){return console.error(`Error expanding glob pattern ${W}:`,U),!1}else X.push(W);if(X.length===0)return console.error("Error: No test files found matching the provided patterns."),!1;if(Y.push(...X),Z.filter)Y.push("--pattern",Z.filter);if(Z.timeout)Y.push("--timeout",Z.timeout.toString());if(Z.reporter&&Z.reporter!=="default")if(Z.reporter==="junit"){let W=Y0.join(Zd.tmpdir(),`stx-test-report-${Date.now()}.xml`);Y.push("--reporter",Z.reporter),Y.push("--reporter-outfile",W)}else console.warn(`Warning: Reporter '${Z.reporter}' is not supported. Using default reporter.`);if(Z.coverage)Y.push("--coverage");if(Z.watch)Y.push("--watch");Y.push("--preload",Y0.join(__dirname,"../../../test-utils/happy-dom.ts"));let J={...v.env,STX_TEST_MODE:"true",STX_TEST_UI:Z.ui?"true":"false",STX_TEST_VERBOSE:Z.verbose?"true":"false"},Q="bun";if(Z.verbose)console.log(`Running: ${Q} ${Y.join(" ")}`);let G=performance.now(),K=qR(Q,Y,{stdio:"inherit",shell:!1,env:J});return new Promise((W)=>{K.on("close",(U)=>{let _=((performance.now()-G)/1000).toFixed(2);if(U===0)W(!0);else W(!1)}),v.on("SIGINT",()=>{console.log(`
22426
22448
  Test run interrupted`),K.kill("SIGINT"),v.exit(1)})})}catch(Y){return console.error("Error running tests:",Y instanceof Error?Y.message:String(Y)),!1}}var Gd=v.argv.length>=3&&(Xd(v.argv[2])&&f0.existsSync(v.argv[2])||e5(v.argv[2]));if(Gd){let $=v.argv[2],Z={port:3000,watch:!0,cache:!0,markdown:{syntaxHighlighting:{enabled:!0,serverSide:!0,defaultTheme:"github-dark",highlightUnknownLanguages:!0}}};for(let Y=3;Y<v.argv.length;Y++){let X=v.argv[Y];if(X==="--port"&&Y+1<v.argv.length){let J=_R(v.argv[++Y]);if(!J.isValid)t5(J);Z.port=Number.parseInt(v.argv[Y],10)}else if(X==="--no-watch")Z.watch=!1;else if(X==="--highlight-theme"&&Y+1<v.argv.length){if(!Z.markdown)Z.markdown={};if(!Z.markdown.syntaxHighlighting)Z.markdown.syntaxHighlighting={};Z.markdown.syntaxHighlighting.defaultTheme=v.argv[++Y]}else if(X==="--no-highlight"){if(!Z.markdown)Z.markdown={};if(!Z.markdown.syntaxHighlighting)Z.markdown.syntaxHighlighting={};Z.markdown.syntaxHighlighting.enabled=!1}else if(X==="--no-highlight-unknown"){if(!Z.markdown)Z.markdown={};if(!Z.markdown.syntaxHighlighting)Z.markdown.syntaxHighlighting={};Z.markdown.syntaxHighlighting.highlightUnknownLanguages=!1}else if(X==="--no-cache")Z.cache=!1;else if(X==="--native")Z.native=!0}if(e5($))try{let Y=await Array.fromAsync(new Bun.Glob($).scan({onlyFiles:!0,absolute:!0})),X=$.endsWith(".stx")?Y.filter((K)=>K.endsWith(".stx")):$.endsWith(".md")?Y.filter((K)=>K.endsWith(".md")):Y.filter((K)=>K.endsWith(".stx")||K.endsWith(".md")),J=$.split("*")[0].replace(/\/$/,"")||".",Q=Y0.resolve(J);try{let K=new Bun.Glob("**/*.stx"),U=(await Array.fromAsync(K.scan({cwd:Q,onlyFiles:!0,absolute:!0}))).filter((z)=>/\[.+\]/.test(Y0.basename(z)));for(let z of U)if(!X.includes(z))X.push(z)}catch{}if(X.length===0)console.error(`Error: No STX or Markdown files found matching pattern: ${$}`),v.exit(1);if(!await w3(X,Z))v.exit(1)}catch(Y){console.error("Failed to process files:",Y),v.exit(1)}else N3($,Z).catch((Y)=>{console.error("Failed to start dev server:",Y),v.exit(1)})}else{let $=function(X,J){let Q=[];for(let G=0;G<=J.length;G++)Q[G]=[G];for(let G=0;G<=X.length;G++)Q[0][G]=G;for(let G=1;G<=J.length;G++)for(let K=1;K<=X.length;K++)if(J.charAt(G-1)===X.charAt(K-1))Q[G][K]=Q[G-1][K-1];else Q[G][K]=Math.min(Q[G-1][K-1]+1,Q[G][K-1]+1,Q[G-1][K]+1);return Q[J.length][X.length]};V0.command("docs","Generate documentation for components, templates, and directives").option("--output <dir>","Output directory for documentation",{default:"docs"}).option("--format <format>","Documentation format (markdown, html, json)",{default:"markdown"}).option("--components-dir <dir>","Components directory",{default:"components"}).option("--templates-dir <dir>","Templates directory",{default:"."}).option("--no-components","Disable components documentation").option("--no-templates","Disable templates documentation").option("--no-directives","Disable directives documentation").option("--extra-content <content>","Extra content to include in documentation").example("stx docs --output docs --format html").action(async(X)=>{try{if(await FK(X))console.log("Documentation generated successfully."),v.exit(0);else console.error("Documentation generation failed."),v.exit(1)}catch(J){console.error("Error generating documentation:",J),v.exit(1)}}),V0.command("iconify <command>","Generate Iconify icon packages").option("--output <dir>","Output directory for generated packages",{default:"packages"}).option("--icons <icons>","Comma-separated list of specific icons to generate").example("stx iconify list").example("stx iconify generate mdi").example("stx iconify generate lucide --icons home,settings,user").action(async(X,J)=>{try{let{fetchCollections:Q,generatePackage:G}=await import("@stacksjs/iconify-generator");if(X==="list"){console.log(`
22427
22449
  \uD83D\uDCDA Fetching available icon collections...
22428
22450
  `);let K=await Q(),W=Object.entries(K).sort((U,z)=>z[1].total-U[1].total);console.log(`Available collections:
package/dist/craft.js CHANGED
@@ -3986,7 +3986,10 @@ catch (e) {
3986
3986
  }
3987
3987
  }
3988
3988
  if (el.nodeType === Node.TEXT_NODE) {
3989
- const text = el.textContent;
3989
+ // DOM textContent is specified as a string, but lightweight DOM
3990
+ // implementations and user-created nodes can retain a numeric value
3991
+ // assigned by a binding. Normalize before interpolation detection.
3992
+ const text = el.textContent == null ? '' : String(el.textContent);
3990
3993
  if (text && text.includes('{{')) {
3991
3994
  const parts = text.split(/(\\{\\{[\\s\\S]+?\\}\\})/g);
3992
3995
  if (parts.length > 1) {
@@ -4896,13 +4899,24 @@ else if (typeof value === 'string') {
4896
4899
  var __forComponentSetupFactories = new Map();
4897
4900
 
4898
4901
  function forEachGroupElement(nodes, callback) {
4899
- nodes.forEach(function(node) {
4902
+ function visit(node, insideTemplateContent) {
4900
4903
  if (!node || node.nodeType !== Node.ELEMENT_NODE) return;
4901
- callback(node);
4902
- if (node.querySelectorAll) {
4903
- node.querySelectorAll('*').forEach(function(child) { callback(child); });
4904
- }
4905
- });
4904
+ callback(node, insideTemplateContent);
4905
+
4906
+ // HTMLTemplateElement children live in template.content, outside the
4907
+ // element's ordinary descendant tree. Component roots and setup scripts
4908
+ // wrapped by caller-owned :if/:for directives must still be discoverable
4909
+ // for scope remapping before those templates are cloned.
4910
+ var entersTemplateContent = node.tagName === 'TEMPLATE' && node.content;
4911
+ var childRoot = entersTemplateContent
4912
+ ? node.content
4913
+ : node;
4914
+ Array.from(childRoot.childNodes || []).forEach(function(child) {
4915
+ visit(child, insideTemplateContent || entersTemplateContent);
4916
+ });
4917
+ }
4918
+
4919
+ nodes.forEach(function(node) { visit(node, false); });
4906
4920
  }
4907
4921
 
4908
4922
  // A signal component expanded inside <template :for> carries a compiled
@@ -4950,7 +4964,11 @@ else if (typeof value === 'string') {
4950
4964
  function hydrateComponentScopes(nodes, callerScope) {
4951
4965
  var roots = [];
4952
4966
  var scripts = [];
4953
- forEachGroupElement(nodes, function(node) {
4967
+ forEachGroupElement(nodes, function(node, insideTemplateContent) {
4968
+ // A template's content is inert source material. Remapping must traverse
4969
+ // it, but setup and event hydration belong to the live clones inserted by
4970
+ // bindIf/bindFor, where the correct iteration scope is available.
4971
+ if (insideTemplateContent) return;
4954
4972
  if (node.hasAttribute && node.hasAttribute('data-stx-scope')) roots.push(node);
4955
4973
  if (node.tagName === 'SCRIPT' && node.hasAttribute('data-stx-scoped')) scripts.push(node);
4956
4974
  });
@@ -5780,37 +5798,38 @@ catch (e2) {
5780
5798
  pick.el.__stx_shown_at = performance.now();
5781
5799
  if (!pick.childrenProcessed) {
5782
5800
  pick.childrenProcessed = true;
5783
- // Defer child processing (same rationale as bindIf, CLAUDE.md
5784
- // item 35): keep child effects from subscribing to THIS chain
5785
- // effect's tracked signals.
5786
- (function (branch) {
5787
- setTimeout(function () {
5788
- var childScope = { ...globalHelpers, ...capturedComponentScope, ...(branch.capturedElementScope || {}) };
5789
- // Components compiled inside any branch of an if/else-if/else
5790
- // chain carry scoped setup scripts with them. Branch nodes are
5791
- // detached before those scripts can execute, so simply
5792
- // re-inserting the selected branch leaves component props,
5793
- // events, directives, and projected slot expressions inert.
5794
- // Hydrate those scopes before the normal subtree walk, matching
5795
- // the single-template :if and :for insertion paths.
5796
- var previouslyTrackedNodes = new Set();
5797
- branch.nodes.forEach(function (n) {
5798
- if (n && n.__stx_disposers) previouslyTrackedNodes.add(n);
5799
- });
5800
- hydrateComponentScopes(branch.nodes, childScope);
5801
- branch.nodes.forEach(function (n) {
5802
- if (n.nodeType !== 1) return; // whitespace/text between template children
5803
- // A scoped component receives its disposer in the outer scope
5804
- // walk immediately after processElement returns from binding
5805
- // this conditional. That first pass intentionally stops at
5806
- // :if, so a disposer does not mean its children were hydrated.
5807
- if (previouslyTrackedNodes.has(n) || !n.__stx_disposers)
5808
- processElement(n, childScope);
5809
- n.removeAttribute('x-cloak');
5810
- n.querySelectorAll('[x-cloak]').forEach(function (c) { c.removeAttribute('x-cloak'); });
5811
- });
5812
- }, 0);
5813
- })(pick);
5801
+ // Hydrate before the outer scope walk can see the inserted branch.
5802
+ // Chromium executes scripts cloned from template.content as soon as
5803
+ // they are connected, so deferring this pass allowed the global walk
5804
+ // to process projected slot expressions without the loop caller
5805
+ // scope. peek() keeps this synchronous pass from subscribing child
5806
+ // reads to the current chain effect.
5807
+ peek(function () {
5808
+ var childScope = { ...globalHelpers, ...capturedComponentScope, ...(pick.capturedElementScope || {}) };
5809
+ // Components compiled inside any branch of an if/else-if/else
5810
+ // chain carry scoped setup scripts with them. Branch nodes are
5811
+ // detached before those scripts can execute, so simply
5812
+ // re-inserting the selected branch leaves component props,
5813
+ // events, directives, and projected slot expressions inert.
5814
+ // Hydrate those scopes before the normal subtree walk, matching
5815
+ // the single-template :if and :for insertion paths.
5816
+ var previouslyTrackedNodes = new Set();
5817
+ pick.nodes.forEach(function (n) {
5818
+ if (n && n.__stx_disposers) previouslyTrackedNodes.add(n);
5819
+ });
5820
+ hydrateComponentScopes(pick.nodes, childScope);
5821
+ pick.nodes.forEach(function (n) {
5822
+ if (n.nodeType !== 1) return; // whitespace/text between template children
5823
+ // A scoped component receives its disposer in the outer scope
5824
+ // walk immediately after processElement returns from binding
5825
+ // this conditional. That first pass intentionally stops at
5826
+ // :if, so a disposer does not mean its children were hydrated.
5827
+ if (previouslyTrackedNodes.has(n) || !n.__stx_disposers)
5828
+ processElement(n, childScope);
5829
+ n.removeAttribute('x-cloak');
5830
+ n.querySelectorAll('[x-cloak]').forEach(function (c) { c.removeAttribute('x-cloak'); });
5831
+ });
5832
+ });
5814
5833
  }
5815
5834
  }
5816
5835
 
@@ -5981,10 +6000,13 @@ else if (!value && isInserted) {
5981
6000
  else if (value && isInserted && !childrenProcessed) {
5982
6001
  // Initial template content is cloned before the effect runs. Process
5983
6002
  // it after the condition is confirmed so hidden branches stay inert.
6003
+ // Keep hydration synchronous so the outer scope walk cannot bind
6004
+ // caller-owned slot expressions first, while peek() prevents child
6005
+ // reads from subscribing to this conditional effect.
5984
6006
  childrenProcessed = true;
5985
- setTimeout(function() {
6007
+ peek(function() {
5986
6008
  if (isInserted) processTemplateNodes(currentNodes);
5987
- }, 0);
6009
+ });
5988
6010
  }
5989
6011
  }
5990
6012
  else {
package/dist/pwa.js CHANGED
@@ -3895,7 +3895,10 @@ catch (e) {
3895
3895
  }
3896
3896
  }
3897
3897
  if (el.nodeType === Node.TEXT_NODE) {
3898
- const text = el.textContent;
3898
+ // DOM textContent is specified as a string, but lightweight DOM
3899
+ // implementations and user-created nodes can retain a numeric value
3900
+ // assigned by a binding. Normalize before interpolation detection.
3901
+ const text = el.textContent == null ? '' : String(el.textContent);
3899
3902
  if (text && text.includes('{{')) {
3900
3903
  const parts = text.split(/(\\{\\{[\\s\\S]+?\\}\\})/g);
3901
3904
  if (parts.length > 1) {
@@ -4805,13 +4808,24 @@ else if (typeof value === 'string') {
4805
4808
  var __forComponentSetupFactories = new Map();
4806
4809
 
4807
4810
  function forEachGroupElement(nodes, callback) {
4808
- nodes.forEach(function(node) {
4811
+ function visit(node, insideTemplateContent) {
4809
4812
  if (!node || node.nodeType !== Node.ELEMENT_NODE) return;
4810
- callback(node);
4811
- if (node.querySelectorAll) {
4812
- node.querySelectorAll('*').forEach(function(child) { callback(child); });
4813
- }
4814
- });
4813
+ callback(node, insideTemplateContent);
4814
+
4815
+ // HTMLTemplateElement children live in template.content, outside the
4816
+ // element's ordinary descendant tree. Component roots and setup scripts
4817
+ // wrapped by caller-owned :if/:for directives must still be discoverable
4818
+ // for scope remapping before those templates are cloned.
4819
+ var entersTemplateContent = node.tagName === 'TEMPLATE' && node.content;
4820
+ var childRoot = entersTemplateContent
4821
+ ? node.content
4822
+ : node;
4823
+ Array.from(childRoot.childNodes || []).forEach(function(child) {
4824
+ visit(child, insideTemplateContent || entersTemplateContent);
4825
+ });
4826
+ }
4827
+
4828
+ nodes.forEach(function(node) { visit(node, false); });
4815
4829
  }
4816
4830
 
4817
4831
  // A signal component expanded inside <template :for> carries a compiled
@@ -4859,7 +4873,11 @@ else if (typeof value === 'string') {
4859
4873
  function hydrateComponentScopes(nodes, callerScope) {
4860
4874
  var roots = [];
4861
4875
  var scripts = [];
4862
- forEachGroupElement(nodes, function(node) {
4876
+ forEachGroupElement(nodes, function(node, insideTemplateContent) {
4877
+ // A template's content is inert source material. Remapping must traverse
4878
+ // it, but setup and event hydration belong to the live clones inserted by
4879
+ // bindIf/bindFor, where the correct iteration scope is available.
4880
+ if (insideTemplateContent) return;
4863
4881
  if (node.hasAttribute && node.hasAttribute('data-stx-scope')) roots.push(node);
4864
4882
  if (node.tagName === 'SCRIPT' && node.hasAttribute('data-stx-scoped')) scripts.push(node);
4865
4883
  });
@@ -5689,37 +5707,38 @@ catch (e2) {
5689
5707
  pick.el.__stx_shown_at = performance.now();
5690
5708
  if (!pick.childrenProcessed) {
5691
5709
  pick.childrenProcessed = true;
5692
- // Defer child processing (same rationale as bindIf, CLAUDE.md
5693
- // item 35): keep child effects from subscribing to THIS chain
5694
- // effect's tracked signals.
5695
- (function (branch) {
5696
- setTimeout(function () {
5697
- var childScope = { ...globalHelpers, ...capturedComponentScope, ...(branch.capturedElementScope || {}) };
5698
- // Components compiled inside any branch of an if/else-if/else
5699
- // chain carry scoped setup scripts with them. Branch nodes are
5700
- // detached before those scripts can execute, so simply
5701
- // re-inserting the selected branch leaves component props,
5702
- // events, directives, and projected slot expressions inert.
5703
- // Hydrate those scopes before the normal subtree walk, matching
5704
- // the single-template :if and :for insertion paths.
5705
- var previouslyTrackedNodes = new Set();
5706
- branch.nodes.forEach(function (n) {
5707
- if (n && n.__stx_disposers) previouslyTrackedNodes.add(n);
5708
- });
5709
- hydrateComponentScopes(branch.nodes, childScope);
5710
- branch.nodes.forEach(function (n) {
5711
- if (n.nodeType !== 1) return; // whitespace/text between template children
5712
- // A scoped component receives its disposer in the outer scope
5713
- // walk immediately after processElement returns from binding
5714
- // this conditional. That first pass intentionally stops at
5715
- // :if, so a disposer does not mean its children were hydrated.
5716
- if (previouslyTrackedNodes.has(n) || !n.__stx_disposers)
5717
- processElement(n, childScope);
5718
- n.removeAttribute('x-cloak');
5719
- n.querySelectorAll('[x-cloak]').forEach(function (c) { c.removeAttribute('x-cloak'); });
5720
- });
5721
- }, 0);
5722
- })(pick);
5710
+ // Hydrate before the outer scope walk can see the inserted branch.
5711
+ // Chromium executes scripts cloned from template.content as soon as
5712
+ // they are connected, so deferring this pass allowed the global walk
5713
+ // to process projected slot expressions without the loop caller
5714
+ // scope. peek() keeps this synchronous pass from subscribing child
5715
+ // reads to the current chain effect.
5716
+ peek(function () {
5717
+ var childScope = { ...globalHelpers, ...capturedComponentScope, ...(pick.capturedElementScope || {}) };
5718
+ // Components compiled inside any branch of an if/else-if/else
5719
+ // chain carry scoped setup scripts with them. Branch nodes are
5720
+ // detached before those scripts can execute, so simply
5721
+ // re-inserting the selected branch leaves component props,
5722
+ // events, directives, and projected slot expressions inert.
5723
+ // Hydrate those scopes before the normal subtree walk, matching
5724
+ // the single-template :if and :for insertion paths.
5725
+ var previouslyTrackedNodes = new Set();
5726
+ pick.nodes.forEach(function (n) {
5727
+ if (n && n.__stx_disposers) previouslyTrackedNodes.add(n);
5728
+ });
5729
+ hydrateComponentScopes(pick.nodes, childScope);
5730
+ pick.nodes.forEach(function (n) {
5731
+ if (n.nodeType !== 1) return; // whitespace/text between template children
5732
+ // A scoped component receives its disposer in the outer scope
5733
+ // walk immediately after processElement returns from binding
5734
+ // this conditional. That first pass intentionally stops at
5735
+ // :if, so a disposer does not mean its children were hydrated.
5736
+ if (previouslyTrackedNodes.has(n) || !n.__stx_disposers)
5737
+ processElement(n, childScope);
5738
+ n.removeAttribute('x-cloak');
5739
+ n.querySelectorAll('[x-cloak]').forEach(function (c) { c.removeAttribute('x-cloak'); });
5740
+ });
5741
+ });
5723
5742
  }
5724
5743
  }
5725
5744
 
@@ -5890,10 +5909,13 @@ else if (!value && isInserted) {
5890
5909
  else if (value && isInserted && !childrenProcessed) {
5891
5910
  // Initial template content is cloned before the effect runs. Process
5892
5911
  // it after the condition is confirmed so hidden branches stay inert.
5912
+ // Keep hydration synchronous so the outer scope walk cannot bind
5913
+ // caller-owned slot expressions first, while peek() prevents child
5914
+ // reads from subscribing to this conditional effect.
5893
5915
  childrenProcessed = true;
5894
- setTimeout(function() {
5916
+ peek(function() {
5895
5917
  if (isInserted) processTemplateNodes(currentNodes);
5896
- }, 0);
5918
+ });
5897
5919
  }
5898
5920
  }
5899
5921
  else {
package/dist/signals.js CHANGED
@@ -1970,7 +1970,10 @@ catch (e) {
1970
1970
  }
1971
1971
  }
1972
1972
  if (el.nodeType === Node.TEXT_NODE) {
1973
- const text = el.textContent;
1973
+ // DOM textContent is specified as a string, but lightweight DOM
1974
+ // implementations and user-created nodes can retain a numeric value
1975
+ // assigned by a binding. Normalize before interpolation detection.
1976
+ const text = el.textContent == null ? '' : String(el.textContent);
1974
1977
  if (text && text.includes('{{')) {
1975
1978
  const parts = text.split(/(\\{\\{[\\s\\S]+?\\}\\})/g);
1976
1979
  if (parts.length > 1) {
@@ -2880,13 +2883,24 @@ else if (typeof value === 'string') {
2880
2883
  var __forComponentSetupFactories = new Map();
2881
2884
 
2882
2885
  function forEachGroupElement(nodes, callback) {
2883
- nodes.forEach(function(node) {
2886
+ function visit(node, insideTemplateContent) {
2884
2887
  if (!node || node.nodeType !== Node.ELEMENT_NODE) return;
2885
- callback(node);
2886
- if (node.querySelectorAll) {
2887
- node.querySelectorAll('*').forEach(function(child) { callback(child); });
2888
- }
2889
- });
2888
+ callback(node, insideTemplateContent);
2889
+
2890
+ // HTMLTemplateElement children live in template.content, outside the
2891
+ // element's ordinary descendant tree. Component roots and setup scripts
2892
+ // wrapped by caller-owned :if/:for directives must still be discoverable
2893
+ // for scope remapping before those templates are cloned.
2894
+ var entersTemplateContent = node.tagName === 'TEMPLATE' && node.content;
2895
+ var childRoot = entersTemplateContent
2896
+ ? node.content
2897
+ : node;
2898
+ Array.from(childRoot.childNodes || []).forEach(function(child) {
2899
+ visit(child, insideTemplateContent || entersTemplateContent);
2900
+ });
2901
+ }
2902
+
2903
+ nodes.forEach(function(node) { visit(node, false); });
2890
2904
  }
2891
2905
 
2892
2906
  // A signal component expanded inside <template :for> carries a compiled
@@ -2934,7 +2948,11 @@ else if (typeof value === 'string') {
2934
2948
  function hydrateComponentScopes(nodes, callerScope) {
2935
2949
  var roots = [];
2936
2950
  var scripts = [];
2937
- forEachGroupElement(nodes, function(node) {
2951
+ forEachGroupElement(nodes, function(node, insideTemplateContent) {
2952
+ // A template's content is inert source material. Remapping must traverse
2953
+ // it, but setup and event hydration belong to the live clones inserted by
2954
+ // bindIf/bindFor, where the correct iteration scope is available.
2955
+ if (insideTemplateContent) return;
2938
2956
  if (node.hasAttribute && node.hasAttribute('data-stx-scope')) roots.push(node);
2939
2957
  if (node.tagName === 'SCRIPT' && node.hasAttribute('data-stx-scoped')) scripts.push(node);
2940
2958
  });
@@ -3764,37 +3782,38 @@ catch (e2) {
3764
3782
  pick.el.__stx_shown_at = performance.now();
3765
3783
  if (!pick.childrenProcessed) {
3766
3784
  pick.childrenProcessed = true;
3767
- // Defer child processing (same rationale as bindIf, CLAUDE.md
3768
- // item 35): keep child effects from subscribing to THIS chain
3769
- // effect's tracked signals.
3770
- (function (branch) {
3771
- setTimeout(function () {
3772
- var childScope = { ...globalHelpers, ...capturedComponentScope, ...(branch.capturedElementScope || {}) };
3773
- // Components compiled inside any branch of an if/else-if/else
3774
- // chain carry scoped setup scripts with them. Branch nodes are
3775
- // detached before those scripts can execute, so simply
3776
- // re-inserting the selected branch leaves component props,
3777
- // events, directives, and projected slot expressions inert.
3778
- // Hydrate those scopes before the normal subtree walk, matching
3779
- // the single-template :if and :for insertion paths.
3780
- var previouslyTrackedNodes = new Set();
3781
- branch.nodes.forEach(function (n) {
3782
- if (n && n.__stx_disposers) previouslyTrackedNodes.add(n);
3783
- });
3784
- hydrateComponentScopes(branch.nodes, childScope);
3785
- branch.nodes.forEach(function (n) {
3786
- if (n.nodeType !== 1) return; // whitespace/text between template children
3787
- // A scoped component receives its disposer in the outer scope
3788
- // walk immediately after processElement returns from binding
3789
- // this conditional. That first pass intentionally stops at
3790
- // :if, so a disposer does not mean its children were hydrated.
3791
- if (previouslyTrackedNodes.has(n) || !n.__stx_disposers)
3792
- processElement(n, childScope);
3793
- n.removeAttribute('x-cloak');
3794
- n.querySelectorAll('[x-cloak]').forEach(function (c) { c.removeAttribute('x-cloak'); });
3795
- });
3796
- }, 0);
3797
- })(pick);
3785
+ // Hydrate before the outer scope walk can see the inserted branch.
3786
+ // Chromium executes scripts cloned from template.content as soon as
3787
+ // they are connected, so deferring this pass allowed the global walk
3788
+ // to process projected slot expressions without the loop caller
3789
+ // scope. peek() keeps this synchronous pass from subscribing child
3790
+ // reads to the current chain effect.
3791
+ peek(function () {
3792
+ var childScope = { ...globalHelpers, ...capturedComponentScope, ...(pick.capturedElementScope || {}) };
3793
+ // Components compiled inside any branch of an if/else-if/else
3794
+ // chain carry scoped setup scripts with them. Branch nodes are
3795
+ // detached before those scripts can execute, so simply
3796
+ // re-inserting the selected branch leaves component props,
3797
+ // events, directives, and projected slot expressions inert.
3798
+ // Hydrate those scopes before the normal subtree walk, matching
3799
+ // the single-template :if and :for insertion paths.
3800
+ var previouslyTrackedNodes = new Set();
3801
+ pick.nodes.forEach(function (n) {
3802
+ if (n && n.__stx_disposers) previouslyTrackedNodes.add(n);
3803
+ });
3804
+ hydrateComponentScopes(pick.nodes, childScope);
3805
+ pick.nodes.forEach(function (n) {
3806
+ if (n.nodeType !== 1) return; // whitespace/text between template children
3807
+ // A scoped component receives its disposer in the outer scope
3808
+ // walk immediately after processElement returns from binding
3809
+ // this conditional. That first pass intentionally stops at
3810
+ // :if, so a disposer does not mean its children were hydrated.
3811
+ if (previouslyTrackedNodes.has(n) || !n.__stx_disposers)
3812
+ processElement(n, childScope);
3813
+ n.removeAttribute('x-cloak');
3814
+ n.querySelectorAll('[x-cloak]').forEach(function (c) { c.removeAttribute('x-cloak'); });
3815
+ });
3816
+ });
3798
3817
  }
3799
3818
  }
3800
3819
 
@@ -3965,10 +3984,13 @@ else if (!value && isInserted) {
3965
3984
  else if (value && isInserted && !childrenProcessed) {
3966
3985
  // Initial template content is cloned before the effect runs. Process
3967
3986
  // it after the condition is confirmed so hidden branches stay inert.
3987
+ // Keep hydration synchronous so the outer scope walk cannot bind
3988
+ // caller-owned slot expressions first, while peek() prevents child
3989
+ // reads from subscribing to this conditional effect.
3968
3990
  childrenProcessed = true;
3969
- setTimeout(function() {
3991
+ peek(function() {
3970
3992
  if (isInserted) processTemplateNodes(currentNodes);
3971
- }, 0);
3993
+ });
3972
3994
  }
3973
3995
  }
3974
3996
  else {
package/dist/ssg.js CHANGED
@@ -2704,7 +2704,10 @@ catch (e) {
2704
2704
  }
2705
2705
  }
2706
2706
  if (el.nodeType === Node.TEXT_NODE) {
2707
- const text = el.textContent;
2707
+ // DOM textContent is specified as a string, but lightweight DOM
2708
+ // implementations and user-created nodes can retain a numeric value
2709
+ // assigned by a binding. Normalize before interpolation detection.
2710
+ const text = el.textContent == null ? '' : String(el.textContent);
2708
2711
  if (text && text.includes('{{')) {
2709
2712
  const parts = text.split(/(\\{\\{[\\s\\S]+?\\}\\})/g);
2710
2713
  if (parts.length > 1) {
@@ -3614,13 +3617,24 @@ else if (typeof value === 'string') {
3614
3617
  var __forComponentSetupFactories = new Map();
3615
3618
 
3616
3619
  function forEachGroupElement(nodes, callback) {
3617
- nodes.forEach(function(node) {
3620
+ function visit(node, insideTemplateContent) {
3618
3621
  if (!node || node.nodeType !== Node.ELEMENT_NODE) return;
3619
- callback(node);
3620
- if (node.querySelectorAll) {
3621
- node.querySelectorAll('*').forEach(function(child) { callback(child); });
3622
- }
3623
- });
3622
+ callback(node, insideTemplateContent);
3623
+
3624
+ // HTMLTemplateElement children live in template.content, outside the
3625
+ // element's ordinary descendant tree. Component roots and setup scripts
3626
+ // wrapped by caller-owned :if/:for directives must still be discoverable
3627
+ // for scope remapping before those templates are cloned.
3628
+ var entersTemplateContent = node.tagName === 'TEMPLATE' && node.content;
3629
+ var childRoot = entersTemplateContent
3630
+ ? node.content
3631
+ : node;
3632
+ Array.from(childRoot.childNodes || []).forEach(function(child) {
3633
+ visit(child, insideTemplateContent || entersTemplateContent);
3634
+ });
3635
+ }
3636
+
3637
+ nodes.forEach(function(node) { visit(node, false); });
3624
3638
  }
3625
3639
 
3626
3640
  // A signal component expanded inside <template :for> carries a compiled
@@ -3668,7 +3682,11 @@ else if (typeof value === 'string') {
3668
3682
  function hydrateComponentScopes(nodes, callerScope) {
3669
3683
  var roots = [];
3670
3684
  var scripts = [];
3671
- forEachGroupElement(nodes, function(node) {
3685
+ forEachGroupElement(nodes, function(node, insideTemplateContent) {
3686
+ // A template's content is inert source material. Remapping must traverse
3687
+ // it, but setup and event hydration belong to the live clones inserted by
3688
+ // bindIf/bindFor, where the correct iteration scope is available.
3689
+ if (insideTemplateContent) return;
3672
3690
  if (node.hasAttribute && node.hasAttribute('data-stx-scope')) roots.push(node);
3673
3691
  if (node.tagName === 'SCRIPT' && node.hasAttribute('data-stx-scoped')) scripts.push(node);
3674
3692
  });
@@ -4498,37 +4516,38 @@ catch (e2) {
4498
4516
  pick.el.__stx_shown_at = performance.now();
4499
4517
  if (!pick.childrenProcessed) {
4500
4518
  pick.childrenProcessed = true;
4501
- // Defer child processing (same rationale as bindIf, CLAUDE.md
4502
- // item 35): keep child effects from subscribing to THIS chain
4503
- // effect's tracked signals.
4504
- (function (branch) {
4505
- setTimeout(function () {
4506
- var childScope = { ...globalHelpers, ...capturedComponentScope, ...(branch.capturedElementScope || {}) };
4507
- // Components compiled inside any branch of an if/else-if/else
4508
- // chain carry scoped setup scripts with them. Branch nodes are
4509
- // detached before those scripts can execute, so simply
4510
- // re-inserting the selected branch leaves component props,
4511
- // events, directives, and projected slot expressions inert.
4512
- // Hydrate those scopes before the normal subtree walk, matching
4513
- // the single-template :if and :for insertion paths.
4514
- var previouslyTrackedNodes = new Set();
4515
- branch.nodes.forEach(function (n) {
4516
- if (n && n.__stx_disposers) previouslyTrackedNodes.add(n);
4517
- });
4518
- hydrateComponentScopes(branch.nodes, childScope);
4519
- branch.nodes.forEach(function (n) {
4520
- if (n.nodeType !== 1) return; // whitespace/text between template children
4521
- // A scoped component receives its disposer in the outer scope
4522
- // walk immediately after processElement returns from binding
4523
- // this conditional. That first pass intentionally stops at
4524
- // :if, so a disposer does not mean its children were hydrated.
4525
- if (previouslyTrackedNodes.has(n) || !n.__stx_disposers)
4526
- processElement(n, childScope);
4527
- n.removeAttribute('x-cloak');
4528
- n.querySelectorAll('[x-cloak]').forEach(function (c) { c.removeAttribute('x-cloak'); });
4529
- });
4530
- }, 0);
4531
- })(pick);
4519
+ // Hydrate before the outer scope walk can see the inserted branch.
4520
+ // Chromium executes scripts cloned from template.content as soon as
4521
+ // they are connected, so deferring this pass allowed the global walk
4522
+ // to process projected slot expressions without the loop caller
4523
+ // scope. peek() keeps this synchronous pass from subscribing child
4524
+ // reads to the current chain effect.
4525
+ peek(function () {
4526
+ var childScope = { ...globalHelpers, ...capturedComponentScope, ...(pick.capturedElementScope || {}) };
4527
+ // Components compiled inside any branch of an if/else-if/else
4528
+ // chain carry scoped setup scripts with them. Branch nodes are
4529
+ // detached before those scripts can execute, so simply
4530
+ // re-inserting the selected branch leaves component props,
4531
+ // events, directives, and projected slot expressions inert.
4532
+ // Hydrate those scopes before the normal subtree walk, matching
4533
+ // the single-template :if and :for insertion paths.
4534
+ var previouslyTrackedNodes = new Set();
4535
+ pick.nodes.forEach(function (n) {
4536
+ if (n && n.__stx_disposers) previouslyTrackedNodes.add(n);
4537
+ });
4538
+ hydrateComponentScopes(pick.nodes, childScope);
4539
+ pick.nodes.forEach(function (n) {
4540
+ if (n.nodeType !== 1) return; // whitespace/text between template children
4541
+ // A scoped component receives its disposer in the outer scope
4542
+ // walk immediately after processElement returns from binding
4543
+ // this conditional. That first pass intentionally stops at
4544
+ // :if, so a disposer does not mean its children were hydrated.
4545
+ if (previouslyTrackedNodes.has(n) || !n.__stx_disposers)
4546
+ processElement(n, childScope);
4547
+ n.removeAttribute('x-cloak');
4548
+ n.querySelectorAll('[x-cloak]').forEach(function (c) { c.removeAttribute('x-cloak'); });
4549
+ });
4550
+ });
4532
4551
  }
4533
4552
  }
4534
4553
 
@@ -4699,10 +4718,13 @@ else if (!value && isInserted) {
4699
4718
  else if (value && isInserted && !childrenProcessed) {
4700
4719
  // Initial template content is cloned before the effect runs. Process
4701
4720
  // it after the condition is confirmed so hidden branches stay inert.
4721
+ // Keep hydration synchronous so the outer scope walk cannot bind
4722
+ // caller-owned slot expressions first, while peek() prevents child
4723
+ // reads from subscribing to this conditional effect.
4702
4724
  childrenProcessed = true;
4703
- setTimeout(function() {
4725
+ peek(function() {
4704
4726
  if (isInserted) processTemplateNodes(currentNodes);
4705
- }, 0);
4727
+ });
4706
4728
  }
4707
4729
  }
4708
4730
  else {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/stx",
3
3
  "type": "module",
4
- "version": "0.2.123",
4
+ "version": "0.2.125",
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
  "@cwcss/crosswind": "^0.2.12",
119
119
  "@stacksjs/clapp": "^0.2.0",
120
- "@stacksjs/desktop": "0.2.123",
121
- "@stacksjs/sanitizer": "0.2.123",
120
+ "@stacksjs/desktop": "0.2.125",
121
+ "@stacksjs/sanitizer": "0.2.125",
122
122
  "@stacksjs/ts-i18n": "^0.1.11",
123
- "bun-plugin-stx": "0.2.123",
123
+ "bun-plugin-stx": "0.2.125",
124
124
  "bunfig": "^0.15.15",
125
- "stx-router": "0.2.123",
125
+ "stx-router": "0.2.125",
126
126
  "ts-broadcasting": "^0.0.9",
127
127
  "ts-images": "^0.1.9",
128
128
  "ts-syntax-highlighter": "^0.2.1"