@sola-air-ui/core 1.1.0 → 1.1.1

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/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # @sola-air-ui/core
2
+
3
+ Zero-VDOM reactivity engine for [Sola AIR](https://sola-air.dev) — signals, derived values, effects, component lifecycle, and the `$intent` primitive. This is the runtime that compiled `.sola` components import; most apps won't call it directly (use `@sola-air-ui/compiler` or the `sola-air` meta-package instead), but it's published standalone for anyone embedding Sola-compiled output in a custom build pipeline.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @sola-air-ui/core
9
+ ```
10
+
11
+ ## What's in here
12
+
13
+ - **Signals** — `createSignal`, `createDerived`, `createEffect`, `flushSync`
14
+ - **Lifecycle** — `onMount`, `onDestroy`, `pushContext`/`popContext` (component-scoped mount/destroy queues)
15
+ - **Data & intent** — `createData`, `createIntent`, `configureIntent` (ambient AI state resolution, with SSE streaming support)
16
+ - **IIFE build** — `@sola-air-ui/core/iife` exports a pre-bundled `dist/sola-core.iife.js` for no-bundler environments (e.g. embedding in a CMS widget or a `<script>` tag), guarded so multiple copies on one page share a single instance via `window.SolaCore`.
17
+
18
+ ## Usage
19
+
20
+ Compiled `.sola` output imports these directly:
21
+
22
+ ```js
23
+ import { createSignal, createDerived, onMount } from '@sola-air-ui/core';
24
+
25
+ const [count, setCount] = createSignal(0);
26
+ const doubled = createDerived(() => count() * 2);
27
+
28
+ onMount(() => {
29
+ console.log('mounted, doubled =', doubled());
30
+ });
31
+ ```
32
+
33
+ ## Component lifecycle contract
34
+
35
+ `pushContext()` / `popContext(ctx)` scope `onMount`/`onDestroy` callbacks to one component instance. If your own runtime code manually mounts nested components, always flush a component's mounts/destroys with the **specific context object** `pushContext()` returned for it — not by relying on whichever context happens to be globally "active":
36
+
37
+ ```js
38
+ const ctx = pushContext();
39
+ onMount(() => { /* ... */ });
40
+ // ...build DOM, possibly mounting nested child components...
41
+ __flush_mounts(ctx); // pass ctx explicitly
42
+ ```
43
+
44
+ This matters because a mounted child component's own context stays on the stack (it isn't popped until that child unmounts), so relying on implicit "current" context after nested mounts silently drops the parent's own mount callbacks. `@sola-air-ui/compiler`-generated code already does this correctly.
45
+
46
+ ## License
47
+
48
+ MIT — see the [repo root](https://github.com/rbm3267/sola-air) for the full license and [changelog](https://github.com/rbm3267/sola-air/blob/main/CHANGELOG.md).
@@ -332,19 +332,19 @@ var SolaCore = (() => {
332
332
  activeContext.destroys.push(fn);
333
333
  }
334
334
  }
335
- function __flush_mounts() {
336
- if (activeContext && activeContext.mounts.length > 0) {
337
- const cbs = [...activeContext.mounts];
338
- activeContext.mounts = [];
335
+ function __flush_mounts(ctx = activeContext) {
336
+ if (ctx && ctx.mounts.length > 0) {
337
+ const cbs = [...ctx.mounts];
338
+ ctx.mounts = [];
339
339
  for (const cb of cbs) {
340
340
  cb();
341
341
  }
342
342
  }
343
343
  }
344
- function __flush_destroys() {
345
- if (activeContext && activeContext.destroys.length > 0) {
346
- const cbs = [...activeContext.destroys];
347
- activeContext.destroys = [];
344
+ function __flush_destroys(ctx = activeContext) {
345
+ if (ctx && ctx.destroys.length > 0) {
346
+ const cbs = [...ctx.destroys];
347
+ ctx.destroys = [];
348
348
  for (const cb of cbs) {
349
349
  cb();
350
350
  }
@@ -1,4 +1,4 @@
1
1
  /* @sola-air-ui/core v1.0.2 | MIT */
2
- var SolaCore=(()=>{var I=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var $=Object.prototype.hasOwnProperty;var N=(s,t)=>{for(var e in t)I(s,e,{get:t[e],enumerable:!0})},O=(s,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of L(t))!$.call(s,i)&&i!==e&&I(s,i,{get:()=>t[i],enumerable:!(n=R(t,i))||n.enumerable});return s};var W=s=>O(I({},"__esModule",{value:!0}),s);var et={};N(et,{SolaSentinel:()=>E,__flush_destroys:()=>j,__flush_mounts:()=>V,configureData:()=>Y,configureIntent:()=>G,createData:()=>Z,createDerived:()=>U,createEffect:()=>D,createIntent:()=>z,createSentinel:()=>C,createSignal:()=>S,createTopicSignal:()=>tt,flushSync:()=>M,onDestroy:()=>k,onMount:()=>B,popContext:()=>P,pushContext:()=>X,signalMesh:()=>T});function y(){return typeof performance<"u"?performance.now():Date.now()}var E=class{constructor(t="default",e={}){this.name=t,this.thresholdMs=e.thresholdMs||600,this.maxRageClicks=e.maxRageClicks||3,this.clickHistory=[],this.subscribers=new Set,this.frictionEvents=[],this.flowIndex=99.8,this.fieldHistory=[],this.lastActivityAt=0,this.lastSuggestedAt=-1/0,this.idleThresholdMs=e.idleThresholdMs??1500,this.minSuggestIntervalMs=e.minSuggestIntervalMs??8e3,this.minEventsForSuggestion=e.minEventsForSuggestion??2}recordClick(t,e="button"){let n=y();this.clickHistory.push({actionId:t,target:e,timestamp:n}),this.clickHistory=this.clickHistory.filter(r=>n-r.timestamp<2e3);let i=this.clickHistory.filter(r=>r.actionId===t&&n-r.timestamp<this.thresholdMs);i.length>=this.maxRageClicks&&this.triggerFrictionAlert({type:"RAGE_CLICK",actionId:t,target:e,count:i.length,timestamp:n,severity:"HIGH",message:`Rage-click burst: ${i.length} taps in ${Math.round(n-i[0].timestamp)}ms`})}recordSignalDrop(t,e){this.triggerFrictionAlert({type:"SIGNAL_TIMEOUT",topic:t,error:e?.message||String(e),timestamp:y(),severity:"CRITICAL",message:`Signal channel "${t}" breached SLA timeout (504 Gateway Stall)`})}triggerFrictionAlert(t){this.frictionEvents.unshift(t),this.frictionEvents.length>50&&this.frictionEvents.pop(),this._recomputeFlowIndex(t.timestamp),this.subscribers.forEach(e=>{try{e(t,this)}catch(n){console.error(n)}})}onFriction(t){return this.subscribers.add(t),()=>this.subscribers.delete(t)}_recomputeFlowIndex(t=y()){let e=99.8,n=this.frictionEvents.filter(r=>t-r.timestamp<12e4);e-=n.reduce((r,o)=>{let c=o.severity==="CRITICAL"?6:o.severity==="HIGH"?3.8:2,l=Math.max(.3,1-(t-o.timestamp)/12e4);return r+c*l},0);let i=this.fieldHistory.filter(r=>r.type==="focus");if(i.length>0){let r=i.filter(o=>o.revisit).length/i.length;e-=r*15}if(this.fieldHistory.length>=3){let r=[];for(let l=1;l<this.fieldHistory.length;l++)r.push(this.fieldHistory[l].timestamp-this.fieldHistory[l-1].timestamp);let o=r.reduce((l,u)=>l+u,0)/r.length,c=r.reduce((l,u)=>l+(u-o)**2,0)/r.length;e-=Math.min(10,Math.sqrt(c)/500)}return this.flowIndex=Math.max(0,Math.min(99.8,Number(e.toFixed(1)))),this.flowIndex}_pushFieldEvent(t){this.fieldHistory.push(t),this.fieldHistory=this.fieldHistory.filter(e=>t.timestamp-e.timestamp<6e4).slice(-50),this.lastActivityAt=t.timestamp,this._recomputeFlowIndex(t.timestamp)}recordFieldFocus(t,e=y()){let n=this.fieldHistory.some(i=>i.type==="blur"&&i.fieldId===t);return this._pushFieldEvent({type:"focus",fieldId:t,revisit:n,timestamp:e}),n}recordFieldBlur(t,e,n=y()){let i=String(e??""),r=i.length>200?i.slice(-200):i;this._pushFieldEvent({type:"blur",fieldId:t,valuePreview:r,valueLength:i.length,timestamp:n})}checkSignificance(t=y()){return this.fieldHistory.length<this.minEventsForSuggestion||this.lastActivityAt<=this.lastSuggestedAt||t-this.lastActivityAt<this.idleThresholdMs||t-this.lastSuggestedAt<this.minSuggestIntervalMs?!1:(this.lastSuggestedAt=t,!0)}buildPrompt(){return this.fieldHistory.length===0?null:["You are an ambient UX assistant embedded in a form.","Recent user activity, oldest first:",...this.fieldHistory.map(e=>e.type==="focus"?e.revisit?`User returned to field "${e.fieldId}".`:`User focused field "${e.fieldId}".`:e.valueLength===0?`User left field "${e.fieldId}" empty.`:`Field "${e.fieldId}" now contains: "${e.valuePreview}"`),"","Based only on this activity, suggest exactly one concise, specific next-step action the user might want.",'Respond as compact JSON only: {"label": string (<=60 chars), "action": string (<=140 chars), "confidence": number 0-1}.','If nothing useful can be suggested, respond {"label": null}.'].join(`
3
- `)}};function C(s,t){return new E(s,t)}var p=[],v=new Set,x=!1;function M(){for(;v.size>0;){let s=[...v];v.clear();for(let t of s)t.execute()}x=!1}function H(){x||(x=!0,queueMicrotask(M))}function S(s){let t=s,e=new Set;return[()=>{let r=p[p.length-1];return r&&(e.add(r),r.dependencies.add(e)),t},r=>{if(t!==r){t=r;for(let o of[...e])v.add(o);H()}}]}function D(s){let t={execute(){e(),p.push(t);try{s()}finally{p.pop()}},dependencies:new Set,cleanup:e};function e(){for(let n of t.dependencies)n.delete(t);t.dependencies.clear()}p.push(t);try{s()}finally{p.pop()}return e}function U(s){let t,e=!0,n=new Set,i=new Set,r={execute(){if(!e){e=!0;for(let c of[...n])v.add(c);H()}},dependencies:i,cleanup(){for(let c of i)c.delete(r);i.clear()}};return()=>{let c=p[p.length-1];if(c&&(n.add(c),c.dependencies.add(n)),e){r.cleanup(),i=new Set,r.dependencies=i,p.push(r);try{t=s()}finally{p.pop()}e=!1}return t}}var b=[],f=null;function X(){let s={mounts:[],destroys:[]};return b.push(s),f=s,s}function P(s){let t=b.lastIndexOf(s);t!==-1&&b.splice(t,1),f=b.length>0?b[b.length-1]:null}function B(s){f?f.mounts.push(s):s()}function k(s){f&&f.destroys.push(s)}function V(){if(f&&f.mounts.length>0){let s=[...f.mounts];f.mounts=[];for(let t of s)t()}}function j(){if(f&&f.destroys.length>0){let s=[...f.destroys];f.destroys=[];for(let t of s)t()}}var q={provider:"local",endpoint:"/api/intent",model:"gemini-2.5-flash",stream:!1},_={...q};function G(s){_={..._,...s}}async function J(s,t,e,n){let i=s.body.getReader(),r=new TextDecoder,o="";try{for(;;){let{done:c,value:l}=await i.read();if(c)break;o+=r.decode(l,{stream:!0});let u=o.split(`
4
- `);o=u.pop();for(let a of u){if(!a.startsWith("data: "))continue;let d=a.slice(6).trim();if(d==="[DONE]"){e();return}try{let g=JSON.parse(d),h=g.token??g.delta??g.content??"";h&&t(h)}catch{d&&t(d)}}}e()}catch(c){c.name!=="AbortError"&&n(c)}}function z(s,t={}){let e={..._,...t},[n,i]=S(t.initial??null),[r,o]=S(!1),[c,l]=S(null),u=null;k(()=>{u&&u.abort()}),D(()=>{let d=typeof s=="function"?s():s;if(!d)return;u&&u.abort(),u=new AbortController,i(null),l(null),o(!0);let g=JSON.stringify({messages:[{role:"user",content:d}],model:e.model,provider:e.provider,stream:e.stream});fetch(e.endpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:g,signal:u.signal}).then(h=>{if(!h.ok)throw new Error(`Intent failed: ${h.status}`);if(e.stream){let m="";return J(h,w=>{m+=w,i(m)},()=>o(!1),w=>{l(w.message),o(!1)})}return h.json().then(m=>{m?.components?.length>0?i(m.components[0]):m?.result!=null?i(m.result):i(m),o(!1)})}).catch(h=>{h.name!=="AbortError"&&(console.error("[Sola Intent Error]",h),l(h.message),o(!1))})});let a=n;return a.loading=r,a.error=c,a}var K={relayEndpoint:"http://localhost:4040/api/query",refresh:null},A={...K};function Y(s){A={...A,...s}}function Q(s){if(!s)return null;let t=s.match(/^(\d+)(s|m|h)$/);if(!t)return null;let e=parseInt(t[1]);switch(t[2]){case"s":return e*1e3;case"m":return e*60*1e3;case"h":return e*3600*1e3}return null}function Z(s,t={}){let e={...A,...t},[n,i]=S({loading:!0,data:null,error:null}),r=null,o=null;function c(){r&&r.abort(),r=new AbortController,i({loading:!0,data:n().data,error:null}),fetch(e.relayEndpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({source:s,query:e.query||null,filters:e.filters||null,sort:e.sort||null,limit:e.limit||null,offset:e.offset||null}),signal:r.signal}).then(a=>{if(!a.ok)throw new Error(`Data fetch failed: ${a.status}`);return a.json()}).then(a=>{i({loading:!1,data:a.rows||a,error:null})}).catch(a=>{a.name!=="AbortError"&&(console.error("[Sola Data Error]",a),i({loading:!1,data:null,error:a.message}))})}c();let l=Q(e.refresh);l&&(o=setInterval(c,l));let u=()=>n();return u.refetch=c,u.stop=()=>{o&&clearInterval(o),r&&r.abort()},u}var F=class{constructor(){this.topics=new Map,this.telemetrySubscribers=new Set,this.cycleStack=new Set}topic(t,e){if(!this.topics.has(t)){let[o,c]=S(e);this.topics.set(t,{read:o,write:c,value:e,subscribers:new Set})}let n=this.topics.get(t);return[()=>n.read(),(o,c="signal")=>{let l=typeof o=="function"?o(n.value):o;if(n.value===l)return;if(this.cycleStack.has(t)){console.warn(`[Sola Signal Mesh] Cycle detected on topic "${t}". Aborting cyclic dispatch.`);return}let u=n.value;n.value=l,n.write(l);let a={topic:t,value:l,prevValue:u,timestamp:typeof performance<"u"?performance.now():Date.now(),originWidgetId:c};this.telemetrySubscribers.forEach(d=>{try{d(a)}catch(g){console.error(g)}}),this.cycleStack.add(t);try{n.subscribers.forEach(d=>{try{d(l,a)}catch(g){console.error(g)}})}finally{this.cycleStack.delete(t)}}]}subscribe(t,e){this.topics.has(t)||this.topic(t,void 0);let n=this.topics.get(t);return n.subscribers.add(e),()=>n.subscribers.delete(e)}onTelemetry(t){return this.telemetrySubscribers.add(t),()=>this.telemetrySubscribers.delete(t)}},T=new F,tt=(s,t)=>T.topic(s,t);return W(et);})();
2
+ var SolaCore=(()=>{var I=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var $=Object.prototype.hasOwnProperty;var N=(s,t)=>{for(var e in t)I(s,e,{get:t[e],enumerable:!0})},O=(s,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of L(t))!$.call(s,i)&&i!==e&&I(s,i,{get:()=>t[i],enumerable:!(n=R(t,i))||n.enumerable});return s};var W=s=>O(I({},"__esModule",{value:!0}),s);var et={};N(et,{SolaSentinel:()=>E,__flush_destroys:()=>j,__flush_mounts:()=>V,configureData:()=>Y,configureIntent:()=>G,createData:()=>Z,createDerived:()=>U,createEffect:()=>D,createIntent:()=>z,createSentinel:()=>C,createSignal:()=>S,createTopicSignal:()=>tt,flushSync:()=>M,onDestroy:()=>k,onMount:()=>B,popContext:()=>P,pushContext:()=>X,signalMesh:()=>T});function y(){return typeof performance<"u"?performance.now():Date.now()}var E=class{constructor(t="default",e={}){this.name=t,this.thresholdMs=e.thresholdMs||600,this.maxRageClicks=e.maxRageClicks||3,this.clickHistory=[],this.subscribers=new Set,this.frictionEvents=[],this.flowIndex=99.8,this.fieldHistory=[],this.lastActivityAt=0,this.lastSuggestedAt=-1/0,this.idleThresholdMs=e.idleThresholdMs??1500,this.minSuggestIntervalMs=e.minSuggestIntervalMs??8e3,this.minEventsForSuggestion=e.minEventsForSuggestion??2}recordClick(t,e="button"){let n=y();this.clickHistory.push({actionId:t,target:e,timestamp:n}),this.clickHistory=this.clickHistory.filter(r=>n-r.timestamp<2e3);let i=this.clickHistory.filter(r=>r.actionId===t&&n-r.timestamp<this.thresholdMs);i.length>=this.maxRageClicks&&this.triggerFrictionAlert({type:"RAGE_CLICK",actionId:t,target:e,count:i.length,timestamp:n,severity:"HIGH",message:`Rage-click burst: ${i.length} taps in ${Math.round(n-i[0].timestamp)}ms`})}recordSignalDrop(t,e){this.triggerFrictionAlert({type:"SIGNAL_TIMEOUT",topic:t,error:e?.message||String(e),timestamp:y(),severity:"CRITICAL",message:`Signal channel "${t}" breached SLA timeout (504 Gateway Stall)`})}triggerFrictionAlert(t){this.frictionEvents.unshift(t),this.frictionEvents.length>50&&this.frictionEvents.pop(),this._recomputeFlowIndex(t.timestamp),this.subscribers.forEach(e=>{try{e(t,this)}catch(n){console.error(n)}})}onFriction(t){return this.subscribers.add(t),()=>this.subscribers.delete(t)}_recomputeFlowIndex(t=y()){let e=99.8,n=this.frictionEvents.filter(r=>t-r.timestamp<12e4);e-=n.reduce((r,o)=>{let l=o.severity==="CRITICAL"?6:o.severity==="HIGH"?3.8:2,c=Math.max(.3,1-(t-o.timestamp)/12e4);return r+l*c},0);let i=this.fieldHistory.filter(r=>r.type==="focus");if(i.length>0){let r=i.filter(o=>o.revisit).length/i.length;e-=r*15}if(this.fieldHistory.length>=3){let r=[];for(let c=1;c<this.fieldHistory.length;c++)r.push(this.fieldHistory[c].timestamp-this.fieldHistory[c-1].timestamp);let o=r.reduce((c,u)=>c+u,0)/r.length,l=r.reduce((c,u)=>c+(u-o)**2,0)/r.length;e-=Math.min(10,Math.sqrt(l)/500)}return this.flowIndex=Math.max(0,Math.min(99.8,Number(e.toFixed(1)))),this.flowIndex}_pushFieldEvent(t){this.fieldHistory.push(t),this.fieldHistory=this.fieldHistory.filter(e=>t.timestamp-e.timestamp<6e4).slice(-50),this.lastActivityAt=t.timestamp,this._recomputeFlowIndex(t.timestamp)}recordFieldFocus(t,e=y()){let n=this.fieldHistory.some(i=>i.type==="blur"&&i.fieldId===t);return this._pushFieldEvent({type:"focus",fieldId:t,revisit:n,timestamp:e}),n}recordFieldBlur(t,e,n=y()){let i=String(e??""),r=i.length>200?i.slice(-200):i;this._pushFieldEvent({type:"blur",fieldId:t,valuePreview:r,valueLength:i.length,timestamp:n})}checkSignificance(t=y()){return this.fieldHistory.length<this.minEventsForSuggestion||this.lastActivityAt<=this.lastSuggestedAt||t-this.lastActivityAt<this.idleThresholdMs||t-this.lastSuggestedAt<this.minSuggestIntervalMs?!1:(this.lastSuggestedAt=t,!0)}buildPrompt(){return this.fieldHistory.length===0?null:["You are an ambient UX assistant embedded in a form.","Recent user activity, oldest first:",...this.fieldHistory.map(e=>e.type==="focus"?e.revisit?`User returned to field "${e.fieldId}".`:`User focused field "${e.fieldId}".`:e.valueLength===0?`User left field "${e.fieldId}" empty.`:`Field "${e.fieldId}" now contains: "${e.valuePreview}"`),"","Based only on this activity, suggest exactly one concise, specific next-step action the user might want.",'Respond as compact JSON only: {"label": string (<=60 chars), "action": string (<=140 chars), "confidence": number 0-1}.','If nothing useful can be suggested, respond {"label": null}.'].join(`
3
+ `)}};function C(s,t){return new E(s,t)}var h=[],v=new Set,_=!1;function M(){for(;v.size>0;){let s=[...v];v.clear();for(let t of s)t.execute()}_=!1}function H(){_||(_=!0,queueMicrotask(M))}function S(s){let t=s,e=new Set;return[()=>{let r=h[h.length-1];return r&&(e.add(r),r.dependencies.add(e)),t},r=>{if(t!==r){t=r;for(let o of[...e])v.add(o);H()}}]}function D(s){let t={execute(){e(),h.push(t);try{s()}finally{h.pop()}},dependencies:new Set,cleanup:e};function e(){for(let n of t.dependencies)n.delete(t);t.dependencies.clear()}h.push(t);try{s()}finally{h.pop()}return e}function U(s){let t,e=!0,n=new Set,i=new Set,r={execute(){if(!e){e=!0;for(let l of[...n])v.add(l);H()}},dependencies:i,cleanup(){for(let l of i)l.delete(r);i.clear()}};return()=>{let l=h[h.length-1];if(l&&(n.add(l),l.dependencies.add(n)),e){r.cleanup(),i=new Set,r.dependencies=i,h.push(r);try{t=s()}finally{h.pop()}e=!1}return t}}var b=[],m=null;function X(){let s={mounts:[],destroys:[]};return b.push(s),m=s,s}function P(s){let t=b.lastIndexOf(s);t!==-1&&b.splice(t,1),m=b.length>0?b[b.length-1]:null}function B(s){m?m.mounts.push(s):s()}function k(s){m&&m.destroys.push(s)}function V(s=m){if(s&&s.mounts.length>0){let t=[...s.mounts];s.mounts=[];for(let e of t)e()}}function j(s=m){if(s&&s.destroys.length>0){let t=[...s.destroys];s.destroys=[];for(let e of t)e()}}var q={provider:"local",endpoint:"/api/intent",model:"gemini-2.5-flash",stream:!1},x={...q};function G(s){x={...x,...s}}async function J(s,t,e,n){let i=s.body.getReader(),r=new TextDecoder,o="";try{for(;;){let{done:l,value:c}=await i.read();if(l)break;o+=r.decode(c,{stream:!0});let u=o.split(`
4
+ `);o=u.pop();for(let a of u){if(!a.startsWith("data: "))continue;let f=a.slice(6).trim();if(f==="[DONE]"){e();return}try{let p=JSON.parse(f),d=p.token??p.delta??p.content??"";d&&t(d)}catch{f&&t(f)}}}e()}catch(l){l.name!=="AbortError"&&n(l)}}function z(s,t={}){let e={...x,...t},[n,i]=S(t.initial??null),[r,o]=S(!1),[l,c]=S(null),u=null;k(()=>{u&&u.abort()}),D(()=>{let f=typeof s=="function"?s():s;if(!f)return;u&&u.abort(),u=new AbortController,i(null),c(null),o(!0);let p=JSON.stringify({messages:[{role:"user",content:f}],model:e.model,provider:e.provider,stream:e.stream});fetch(e.endpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:p,signal:u.signal}).then(d=>{if(!d.ok)throw new Error(`Intent failed: ${d.status}`);if(e.stream){let g="";return J(d,w=>{g+=w,i(g)},()=>o(!1),w=>{c(w.message),o(!1)})}return d.json().then(g=>{g?.components?.length>0?i(g.components[0]):g?.result!=null?i(g.result):i(g),o(!1)})}).catch(d=>{d.name!=="AbortError"&&(console.error("[Sola Intent Error]",d),c(d.message),o(!1))})});let a=n;return a.loading=r,a.error=l,a}var K={relayEndpoint:"http://localhost:4040/api/query",refresh:null},A={...K};function Y(s){A={...A,...s}}function Q(s){if(!s)return null;let t=s.match(/^(\d+)(s|m|h)$/);if(!t)return null;let e=parseInt(t[1]);switch(t[2]){case"s":return e*1e3;case"m":return e*60*1e3;case"h":return e*3600*1e3}return null}function Z(s,t={}){let e={...A,...t},[n,i]=S({loading:!0,data:null,error:null}),r=null,o=null;function l(){r&&r.abort(),r=new AbortController,i({loading:!0,data:n().data,error:null}),fetch(e.relayEndpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({source:s,query:e.query||null,filters:e.filters||null,sort:e.sort||null,limit:e.limit||null,offset:e.offset||null}),signal:r.signal}).then(a=>{if(!a.ok)throw new Error(`Data fetch failed: ${a.status}`);return a.json()}).then(a=>{i({loading:!1,data:a.rows||a,error:null})}).catch(a=>{a.name!=="AbortError"&&(console.error("[Sola Data Error]",a),i({loading:!1,data:null,error:a.message}))})}l();let c=Q(e.refresh);c&&(o=setInterval(l,c));let u=()=>n();return u.refetch=l,u.stop=()=>{o&&clearInterval(o),r&&r.abort()},u}var F=class{constructor(){this.topics=new Map,this.telemetrySubscribers=new Set,this.cycleStack=new Set}topic(t,e){if(!this.topics.has(t)){let[o,l]=S(e);this.topics.set(t,{read:o,write:l,value:e,subscribers:new Set})}let n=this.topics.get(t);return[()=>n.read(),(o,l="signal")=>{let c=typeof o=="function"?o(n.value):o;if(n.value===c)return;if(this.cycleStack.has(t)){console.warn(`[Sola Signal Mesh] Cycle detected on topic "${t}". Aborting cyclic dispatch.`);return}let u=n.value;n.value=c,n.write(c);let a={topic:t,value:c,prevValue:u,timestamp:typeof performance<"u"?performance.now():Date.now(),originWidgetId:l};this.telemetrySubscribers.forEach(f=>{try{f(a)}catch(p){console.error(p)}}),this.cycleStack.add(t);try{n.subscribers.forEach(f=>{try{f(c,a)}catch(p){console.error(p)}})}finally{this.cycleStack.delete(t)}}]}subscribe(t,e){this.topics.has(t)||this.topic(t,void 0);let n=this.topics.get(t);return n.subscribers.add(e),()=>n.subscribers.delete(e)}onTelemetry(t){return this.telemetrySubscribers.add(t),()=>this.telemetrySubscribers.delete(t)}},T=new F,tt=(s,t)=>T.topic(s,t);return W(et);})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sola-air-ui/core",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Zero-VDOM reactivity engine — signals, effects, lifecycle, and intent primitives",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/src/index.js CHANGED
@@ -178,22 +178,34 @@ export function onDestroy(fn) {
178
178
  }
179
179
  }
180
180
 
181
- // Called by compiled mount() function to flush instance mounts
182
- export function __flush_mounts() {
183
- if (activeContext && activeContext.mounts.length > 0) {
184
- const cbs = [...activeContext.mounts];
185
- activeContext.mounts = [];
181
+ // Called by compiled mount() function to flush instance mounts.
182
+ //
183
+ // Takes the specific context to flush (the one `pushContext()` returned for
184
+ // THIS component instance) rather than trusting the module-global
185
+ // `activeContext`. If a nested child component mounts during this
186
+ // component's own DOM construction, the child's own pushContext() call
187
+ // reassigns `activeContext` to the child's context and never pops it back
188
+ // (a mounted child stays on the stack until it unmounts) — so by the time
189
+ // the parent reaches its own flush call, `activeContext` no longer points
190
+ // at the parent. Falls back to `activeContext` when called with no
191
+ // argument, for compiled bundles built before this fix.
192
+ export function __flush_mounts(ctx = activeContext) {
193
+ if (ctx && ctx.mounts.length > 0) {
194
+ const cbs = [...ctx.mounts];
195
+ ctx.mounts = [];
186
196
  for (const cb of cbs) {
187
197
  cb();
188
198
  }
189
199
  }
190
200
  }
191
201
 
192
- // Called when a component is torn down
193
- export function __flush_destroys() {
194
- if (activeContext && activeContext.destroys.length > 0) {
195
- const cbs = [...activeContext.destroys];
196
- activeContext.destroys = [];
202
+ // Called when a component is torn down. Same explicit-context fix as
203
+ // __flush_mounts above — see that comment for why activeContext alone
204
+ // isn't reliable once nested components are involved.
205
+ export function __flush_destroys(ctx = activeContext) {
206
+ if (ctx && ctx.destroys.length > 0) {
207
+ const cbs = [...ctx.destroys];
208
+ ctx.destroys = [];
197
209
  for (const cb of cbs) {
198
210
  cb();
199
211
  }