sprae 6.1.2 → 8.0.0

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/sprae.min.js CHANGED
@@ -1 +1 @@
1
- var e,t,r=Promise.prototype.then.bind(Promise.resolve()),n=new WeakMap,l=class extends Map{#e=new FinalizationRegistry((e=>super.delete(e)));get size(){return[...this].length}constructor(e=[]){super();for(const[t,r]of e)this.set(t,r)}get(e){return super.get(e)?.deref()}set(e,t){let r=super.get(e);return r&&this.#e.unregister(r),r=(e=>n.get(e)||(e=>{const t=new WeakRef(e);return n.set(e,t),t})(e))(t),this.#e.register(t,e,r),super.set(e,r)}},s=new Set,i=new WeakMap,o=new WeakMap,a=new WeakMap,u=Symbol("parent"),c={Array:Array,Object:Object,Number:Number,String:String,Boolean:Boolean,Date:Date,console:console,window:window,document:document,history:history,location:location},f={has:()=>!0,get(t,r){if("symbol"==typeof r)return t[r];if(!(r in t))return t[u]?.[r];if(r in Object.prototype||Array.isArray(t)&&r in Array.prototype&&"length"!==r)return t[r];let n=t[r];if(e){let n=i.get(t);n||i.set(t,n={}),n[r]?n[r].includes(e)||n[r].push(e):n[r]=[e]}if(n&&n.constructor===Object||Array.isArray(n)){let e=o.get(n);return e||o.set(n,e=new Proxy(n,f)),e}return n},set(e,t,r){if(!(t in e)&&e[u]&&t in e[u])return e[u][t]=r;if(!Array.isArray(e)&&Object.is(e[t],r))return!0;e[t]=r;let n=i.get(e)?.[t];if(n)for(let e of n)s.add(e);return h(),!0},deleteProperty:(e,t)=>(e[t]=void 0,delete e[t],!0)},p=(e,t)=>{if(o.has(e))return o.get(e);if(a.has(e))return e;let r=new Proxy(e,f);o.set(e,r),a.set(r,e);let n=Object.getOwnPropertyDescriptors(e);for(let t in n){let l=n[t];l.get&&l.get&&(l.get=l.get.bind(r),Object.defineProperty(e,t,l))}return e[u]=t?p(t):c,r},y=t=>{const r=()=>{let n=e;e=r,t(),e=n};return r(),r},h=()=>{t||(t=!0,r((()=>{for(let e of s)e.call();s.clear(),t=!1})))},d={},g={};d.if=(e,t)=>{let r=document.createTextNode(""),n=[j(e,t,":if")],l=[e],s=e;for(;(s=e.nextElementSibling)&&s.hasAttribute(":else");)s.removeAttribute(":else"),(t=s.getAttribute(":if"))?(s.removeAttribute(":if"),s.remove(),l.push(s),n.push(j(e,t,":else :if"))):(s.remove(),l.push(s),n.push((()=>1)));return e.replaceWith(s=r),e=>{let t=n.findIndex((t=>t(e)));l[t]!=s&&((s[m]||s).replaceWith(s=l[t]||r),O(s,e))}};var m=Symbol(":each");d.each=(e,t)=>{let r=function(e){let t=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,r=e.match(/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/);if(!r)return;let n=r[2].trim(),l=r[1].replace(/^\s*\(|\)\s*$/g,"").trim(),s=l.match(t);return s?[l.replace(t,"").trim(),s[1].trim(),n]:[l,"",n]}(t);if(!r)return E(new Error,e,t,":each");const n=e[m]=document.createTextNode("");e.replaceWith(n);const s=j(e,r[2],":each"),i=e.getAttribute(":key"),o=i?j(null,i,":each"):null;e.removeAttribute(":key");const a=e.getAttribute(":ref"),u=new l,c=new l;let f=[];return l=>{let i=s(l);i?"number"==typeof i?i=Array.from({length:i},((e,t)=>[t+1,t])):Array.isArray(i)?i=i.map(((e,t)=>[t+1,e])):"object"==typeof i?i=Object.entries(i):E(Error("Bad list value"),e,t,":each"):i=[];let y=[],h=[];for(let[t,n]of i){let s,i,f=o?.({[r[0]]:n,[r[1]]:t});null==f?s=e.cloneNode(!0):(s=c.get(f))||c.set(f,s=e.cloneNode(!0)),y.push(s),null!=f&&(i=u.get(f))?i[r[0]]=n:(i=p({[r[0]]:n,[a||""]:null,[r[1]]:t},l),null!=f&&u.set(f,i)),h.push(i)}!function(e,t,r,n){const l=new Map,s=new Map;let i,o;for(i=0;i<t.length;i++)l.set(t[i],i);for(i=0;i<r.length;i++)s.set(r[i],i);for(i=o=0;i!==t.length||o!==r.length;){var a=t[i],u=r[o];if(null===a)i++;else if(r.length<=o)e.removeChild(t[i]),i++;else if(t.length<=i)e.insertBefore(u,t[i]||n),o++;else if(a===u)i++,o++;else{var c=s.get(a),f=l.get(u);void 0===c?(e.removeChild(t[i]),i++):void 0===f?(e.insertBefore(u,t[i]||n),o++):(e.insertBefore(t[f],t[i]||n),t[f]=null,f>i+1&&i++,o++)}}}(n.parentNode,f,y,n),f=y;for(let e=0;e<y.length;e++)O(y[e],h[e])}},d.with=(e,t,r)=>{const n=j(e,t,":with")(r);O(e,p(n,r))},d.ref=(e,t,r)=>{r[t]=e},g.render=(e,t,r)=>{let n=j(e,t,":render")(r);n||E(new Error("Template not found"),e,t,":render");let l=n.content.cloneNode(!0);e.replaceChildren(l),O(e,r)},g.id=(e,t)=>{let r=j(e,t,":id");return t=>{return n=r(t),e.id=n||0===n?n:"";var n}},g.class=(e,t)=>{let r=j(e,t,":class"),n=e.getAttribute("class");return t=>{let l=r(t),s=[n];l&&("string"==typeof l?s.push(l):Array.isArray(l)?s.push(...l):s.push(...Object.entries(l).map((([e,t])=>t?e:"")))),(s=s.filter(Boolean).join(" "))?e.setAttribute("class",s):e.removeAttribute("class")}},g.style=(e,t)=>{let r=j(e,t,":style"),n=e.getAttribute("style")||"";return n.endsWith(";")||(n+="; "),t=>{let l=r(t);if("string"==typeof l)e.setAttribute("style",n+l);else{e.setAttribute("style",n);for(let t in l)e.style.setProperty(t,l[t])}}},g.text=(e,t)=>{let r=j(e,t,":text");return t=>{let n=r(t);e.textContent=null==n?"":n}},g[""]=(e,t)=>{let r=j(e,t,":");if(r)return t=>{let n=r(t);for(let t in n)W(e,t.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(e=>"-"+e.toLowerCase())),n[t])}},g.value=(e,t)=>{let r,n,l=j(e,t,":value"),s="text"===e.type||""===e.type?t=>e.setAttribute("value",e.value=null==t?"":t):"TEXTAREA"===e.tagName||"text"===e.type||""===e.type?t=>(r=e.selectionStart,n=e.selectionEnd,e.setAttribute("value",e.value=null==t?"":t),r&&e.setSelectionRange(r,n)):"checkbox"===e.type?t=>(e.value=t?"on":"",W(e,"checked",t)):"select-one"===e.type?t=>{for(let t in e.options)t.removeAttribute("selected");e.value=t,e.selectedOptions[0]?.setAttribute("selected","")}:t=>e.value=t;return e=>s(l(e))};var b=(e,t,r,n)=>{let l=n.startsWith("on")&&n.slice(2),s=j(e,t,":"+n);if(s)return l?t=>{let r=s(t)||(()=>{});return v(e,l,r)}:t=>W(e,n,s(t))},v=(e,t,r)=>{if(!r)return;const n={evt:"",target:e,test:()=>!0};n.evt=(t.startsWith("on")?t.slice(2):t).replace(/\.(\w+)?-?([-\w]+)?/g,((e,t,r="")=>(n.test=A[t]?.(n,...r.split("-"))||n.test,"")));const{evt:l,target:s,test:i,defer:o,stop:a,prevent:u,...c}=n;o&&(r=o(r));const f=e=>i(e)&&(a&&e.stopPropagation(),u&&e.preventDefault(),r.call(s,e));return s.addEventListener(l,f,c),()=>s.removeEventListener(l,f,c)},A={prevent(e){e.prevent=!0},stop(e){e.stop=!0},once(e){e.once=!0},passive(e){e.passive=!0},capture(e){e.capture=!0},window(e){e.target=window},document(e){e.target=document},throttle(e,t){e.defer=e=>k(e,t?Number(t)||0:108)},debounce(e,t){e.defer=e=>x(e,t?Number(t)||0:108)},outside:e=>t=>{let r=e.target;return!(r.contains(t.target)||!1===t.target.isConnected||r.offsetWidth<1&&r.offsetHeight<1)},self:e=>t=>t.target===e.target,ctrl:(e,...t)=>e=>w.ctrl(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),shift:(e,...t)=>e=>w.shift(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),alt:(e,...t)=>e=>w.alt(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),meta:(e,...t)=>e=>w.meta(e)&&t.every((t=>w[t]?w[t](e):e.key===t)),arrow:e=>w.arrow,enter:e=>w.enter,escape:e=>w.escape,tab:e=>w.tab,space:e=>w.space,backspace:e=>w.backspace,delete:e=>w.delete,digit:e=>w.digit,letter:e=>w.letter,character:e=>w.character},w={ctrl:e=>e.ctrlKey||"Control"===e.key||"Ctrl"===e.key,shift:e=>e.shiftKey||"Shift"===e.key,alt:e=>e.altKey||"Alt"===e.key,meta:e=>e.metaKey||"Meta"===e.key||"Command"===e.key,arrow:e=>e.key.startsWith("Arrow"),enter:e=>"Enter"===e.key,escape:e=>e.key.startsWith("Esc"),tab:e=>"Tab"===e.key,space:e=>" "===e.key||"Space"===e.key||" "===e.key,backspace:e=>"Backspace"===e.key,delete:e=>"Delete"===e.key,digit:e=>/^\d$/.test(e.key),letter:e=>/^[a-zA-Z]$/.test(e.key),character:e=>/^\S$/.test(e.key)},k=(e,t)=>{let r,n,l=s=>{r=!0,setTimeout((()=>{if(r=!1,n)return n=!1,l(s),e(s)}),t)};return t=>r?n=!0:(l(t),e(t))},x=(e,t)=>{let r;return n=>{clearTimeout(r),r=setTimeout((()=>{r=null,e(n)}),t)}},W=(e,t,r)=>{null==r||!1===r?e.removeAttribute(t):e.setAttribute(t,!0===r?"":"number"==typeof r||"string"==typeof r?r:"")},S={};function j(e,t,r){let n=S[t];if(!n)try{n=S[t]=new Function("__scope",`with (__scope) { return ${t.trim()} };`)}catch(n){return E(n,e,t,r)}return l=>{let s;try{s=n.call(e,l)}catch(n){return E(n,e,t,r)}return s}}function E(e,t,n,l){Object.assign(e,{element:t,expression:n}),console.warn(`∴ ${e.message}\n\n${l}=${n?`"${n}"\n\n`:""}`,t),r((()=>{throw e}),0)}O.globals=c;var N=new WeakMap;function O(e,t){if(!e.children)return;if(N.has(e))return Object.assign(N.get(e),t);const r=p(t||{}),n=[],l=(e,t=e.parentNode)=>{for(let l in d){let s=":"+l;if(e.hasAttribute?.(s)){let i=e.getAttribute(s);if(e.removeAttribute(s),n.push(d[l](e,i,r,l)),N.has(e))return;if(e.parentNode!==t)return!1}}if(e.attributes)for(let t=0;t<e.attributes.length;){let l=e.attributes[t],s=l.name[0];if(":"===s||"@"===s){e.removeAttribute(l.name);let t="@"===s?`${l.value.includes("await")?"async":""} event=>{${l.value}}`:l.value,i=l.name.slice(1).split(s);for(let l of i){"@"===s&&(l="on"+l);let i=g[l]||b;n.push(i(e,t,r,l))}}else t++}for(let t,r=0;t=e.children[r];r++)!1===l(t,e)&&r--};l(e);for(let e of n)if(e){let t;y((()=>{"function"==typeof t&&t(),t=e(r)}))}return N.set(e,r),r}var $=O;document.currentScript&&O(document.documentElement);export{$ as default};
1
+ function t(){throw new Error("Cycle detected")}var e=Symbol.for("preact-signals");function r(){if(l>1)l--;else{for(var t,e=!1;void 0!==a;){var r=a;for(a=void 0,u++;void 0!==r;){var i=r.o;if(r.o=void 0,r.f&=-3,!(8&r.f)&&p(r))try{r.c()}catch(r){e||(t=r,e=!0)}r=i}}if(u=0,l--,e)throw t}}function i(t){if(l>0)return t();l++;try{return t()}finally{r()}}var n=void 0,o=0;function s(t){if(o>0)return t();var e=n;n=void 0,o++;try{return t()}finally{o--,n=e}}var a=void 0,l=0,u=0,f=0;function c(t){if(void 0!==n){var e=t.n;if(void 0===e||e.t!==n)return e={i:0,S:t,p:n.s,n:void 0,t:n,e:void 0,x:void 0,r:e},void 0!==n.s&&(n.s.n=e),n.s=e,t.n=e,32&n.f&&t.S(e),e;if(-1===e.i)return e.i=0,void 0!==e.n&&(e.n.p=e.p,void 0!==e.p&&(e.p.n=e.n),e.p=n.s,e.n=void 0,n.s.n=e,n.s=e),e}}function h(t){this.v=t,this.i=0,this.n=void 0,this.t=void 0}function v(t){return new h(t)}function p(t){for(var e=t.s;void 0!==e;e=e.n)if(e.S.i!==e.i||!e.S.h()||e.S.i!==e.i)return!0;return!1}function d(t){for(var e=t.s;void 0!==e;e=e.n){var r=e.S.n;if(void 0!==r&&(e.r=r),e.S.n=e,e.i=-1,void 0===e.n){t.s=e;break}}}function y(t){for(var e=t.s,r=void 0;void 0!==e;){var i=e.p;-1===e.i?(e.S.U(e),void 0!==i&&(i.n=e.n),void 0!==e.n&&(e.n.p=i)):r=e,e.S.n=e.r,void 0!==e.r&&(e.r=void 0),e=i}t.s=r}function m(t){h.call(this,void 0),this.x=t,this.s=void 0,this.g=f-1,this.f=4}function g(t){var e=t.u;if(t.u=void 0,"function"==typeof e){l++;var i=n;n=void 0;try{e()}catch(e){throw t.f&=-2,t.f|=8,b(t),e}finally{n=i,r()}}}function b(t){for(var e=t.s;void 0!==e;e=e.n)e.S.U(e);t.x=void 0,t.s=void 0,g(t)}function A(t){if(n!==this)throw new Error("Out-of-order effect");y(this),n=t,this.f&=-2,8&this.f&&b(this),r()}function w(t){this.x=t,this.u=void 0,this.s=void 0,this.o=void 0,this.f=32}function S(t){var e=new w(t);try{e.c()}catch(t){throw e.d(),t}return e.d.bind(e)}h.prototype.brand=e,h.prototype.h=function(){return!0},h.prototype.S=function(t){this.t!==t&&void 0===t.e&&(t.x=this.t,void 0!==this.t&&(this.t.e=t),this.t=t)},h.prototype.U=function(t){if(void 0!==this.t){var e=t.e,r=t.x;void 0!==e&&(e.x=r,t.e=void 0),void 0!==r&&(r.e=e,t.x=void 0),t===this.t&&(this.t=r)}},h.prototype.subscribe=function(t){var e=this;return S((function(){var r=e.value,i=32&this.f;this.f&=-33;try{t(r)}finally{this.f|=i}}))},h.prototype.valueOf=function(){return this.value},h.prototype.toString=function(){return this.value+""},h.prototype.toJSON=function(){return this.value},h.prototype.peek=function(){return this.v},Object.defineProperty(h.prototype,"value",{get:function(){var t=c(this);return void 0!==t&&(t.i=this.i),this.v},set:function(e){if(n instanceof m&&function(){throw new Error("Computed cannot have side-effects")}(),e!==this.v){u>100&&t(),this.v=e,this.i++,f++,l++;try{for(var i=this.t;void 0!==i;i=i.x)i.t.N()}finally{r()}}}}),(m.prototype=new h).h=function(){if(this.f&=-3,1&this.f)return!1;if(32==(36&this.f))return!0;if(this.f&=-5,this.g===f)return!0;if(this.g=f,this.f|=1,this.i>0&&!p(this))return this.f&=-2,!0;var t=n;try{d(this),n=this;var e=this.x();(16&this.f||this.v!==e||0===this.i)&&(this.v=e,this.f&=-17,this.i++)}catch(t){this.v=t,this.f|=16,this.i++}return n=t,y(this),this.f&=-2,!0},m.prototype.S=function(t){if(void 0===this.t){this.f|=36;for(var e=this.s;void 0!==e;e=e.n)e.S.S(e)}h.prototype.S.call(this,t)},m.prototype.U=function(t){if(void 0!==this.t&&(h.prototype.U.call(this,t),void 0===this.t)){this.f&=-33;for(var e=this.s;void 0!==e;e=e.n)e.S.U(e)}},m.prototype.N=function(){if(!(2&this.f)){this.f|=6;for(var t=this.t;void 0!==t;t=t.x)t.t.N()}},m.prototype.peek=function(){if(this.h()||t(),16&this.f)throw this.v;return this.v},Object.defineProperty(m.prototype,"value",{get:function(){1&this.f&&t();var e=c(this);if(this.h(),void 0!==e&&(e.i=this.i),16&this.f)throw this.v;return this.v}}),w.prototype.c=function(){var t=this.S();try{if(8&this.f)return;if(void 0===this.x)return;var e=this.x();"function"==typeof e&&(this.u=e)}finally{t()}},w.prototype.S=function(){1&this.f&&t(),this.f|=1,this.f&=-9,g(this),d(this),l++;var e=n;return n=this,A.bind(this,e)},w.prototype.N=function(){2&this.f||(this.f|=2,this.o=a,a=this)},w.prototype.d=function(){this.f|=8,1&this.f||b(this)};var k,x=Symbol.dispose||=Symbol("dispose"),O=Symbol("signals"),N={Array:Array,Object:Object,Number:Number,String:String,Boolean:Boolean,Date:Date,console:console,window:window,document:document,history:history,navigator:navigator,location:location,screen:screen,localStorage:localStorage,sessionStorage:sessionStorage,alert:alert,prompt:prompt,confirm:confirm,fetch:fetch,performance:performance,setTimeout:setTimeout,setInterval:setInterval,requestAnimationFrame:requestAnimationFrame};function E(t,e){if(t?.constructor!==Object&&!Array.isArray(t))return t;if(t[O]&&!e)return t;const r=t[O],n=Array.isArray(t)&&v((r||t).length),o=e?Object.create((e=E(e))[O]):Array.isArray(t)?[]:{},a=o.constructor.prototype,l=new Proxy(t,{has:()=>!0,get(t,r){if(n){if("length"===r)return a[k]?n.peek():n.value;k=r}if(a[r])return a[r];if(r===O)return o;const i=o[r]||u(r);return i?i.value:e?e[r]:N.hasOwnProperty(r)?N[r]:void 0},set(t,e,r){if(n&&"length"===e)return i((()=>{for(let t=r,e=o.length;t<e;t++)delete l[t];for(let t=o.length;t<r;t++)l[t]=null;n.value=o.length=t.length=r})),!0;const a=o[e]||u(e)||v(),f=a.peek();return r===f||(a._set?a._set(r):Array.isArray(r)&&Array.isArray(f)?s((()=>i((()=>{let i=0,n=r.length;for(;i<n;i++)f[i]=t[e][i]=r[i];f.length=n,a.value=null,a.value=f})))):(Array.isArray(f)&&(f.length=0),a.value=E(t[e]=r))),n&&e>=n.peek()&&(n.value=o.length=t.length=Number(e)+1),!0},deleteProperty:(t,e)=>(e in o&&(o[e]._del?.(),delete o[e],delete t[e]),!0)});for(let e in t)t[e],o[e]=r?.[e]??null;function u(e){if(t.hasOwnProperty(e)){const i=Object.getOwnPropertyDescriptor(t,e);return i?.get?((o[e]=(r=i.get.bind(l),new m(r)))._set=i.set?.bind(l),o[e]):o[e]=i.value?.peek?i.value:v(E(i.value))}var r}return l}var j=Promise.prototype.then.bind(Promise.resolve()),_={},P={};_.if=(t,e,r)=>{let i=document.createTextNode(""),n=[I(t,e,":if")],o=[t],s=t;for(;(s=t.nextElementSibling)&&s.hasAttribute(":else");)s.removeAttribute(":else"),(e=s.getAttribute(":if"))?(s.removeAttribute(":if"),s.remove(),o.push(s),n.push(I(t,e,":else :if"))):(s.remove(),o.push(s),n.push((()=>1)));t.replaceWith(s=i);const a=S((()=>{let t=n.findIndex((t=>t(r)));o[t]!=s&&((s[$]||s).replaceWith(s=o[t]||i),M(s,r))}));return()=>{for(const t of o)t[x]?.();a()}};var $=Symbol(":each");_.each=(t,e,r)=>{let n=function(t){let e=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,r=t.match(/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/);if(!r)return;let i=r[2].trim(),n=r[1].replace(/^\s*\(|\)\s*$/g,"").trim(),o=n.match(e);return o?[n.replace(e,"").trim(),o[1].trim(),i]:[n,"",i]}(e);if(!n)return L(new Error,t,e,":each");const[o,a,l]=n,u=t[$]=document.createTextNode("");t.replaceWith(u);const f=I(t,l,":each");let c,h,v=0;return S((()=>{let n=f(r);return n?"number"==typeof n?n=Array.from({length:n},((t,e)=>e)):Array.isArray(n)||("object"==typeof n?(h=Object.keys(n),n=Object.values(n)):L(Error("Bad items value"),t,e,":each")):n=[],s((()=>i((()=>{if(c?.[O][0]?.peek){let t=n.length,e=0;for(;e<t;e++)c[e]=n[e];c.length=t}else{for(let t=0,e=c?.[O];t<v;t++)e[t]?._del();c=n,c[O]||={}}})))),S((()=>{let e=n.length;v!==e&&s((()=>i((()=>{const i=c[O];for(let n=v;n<e;n++){c[n];const e=t.cloneNode(!0),s=E({[o]:i[n]??c[n],[a]:h?.[n]??n},r);u.before(e),M(e,s);const{_del:l}=i[n]||={};i[n]._del=()=>{delete i[n],l?.(),e[x](),e.remove(),delete c[n]}}v=e}))))}))})),()=>i((()=>{for(let t of c[O])t?._del();c.length=0}))},_.with=(t,e,r)=>(M(t,E(I(t,e,":with")(r),r)),t[x]),_.ref=(t,e,r)=>{r[e]=t},P.render=(t,e,r)=>{let i=I(t,e,":render")(r);i||L(new Error("Template not found"),t,e,":render");let n=i.content.cloneNode(!0);return t.replaceChildren(n),M(t,r),t[x]},P.id=(t,e,r)=>{let i=I(t,e,":id");return S((()=>{return e=i(r),t.id=e||0===e?e:"";var e}))},P.class=(t,e,r)=>{let i=I(t,e,":class"),n=t.getAttribute("class");return S((()=>{let e=i(r),o=[n];e&&("string"==typeof e?o.push(e):Array.isArray(e)?o.push(...e):o.push(...Object.entries(e).map((([t,e])=>e?t:"")))),(o=o.filter(Boolean).join(" "))?t.setAttribute("class",o):t.removeAttribute("class")}))},P.style=(t,e,r)=>{let i=I(t,e,":style"),n=t.getAttribute("style")||"";return n.endsWith(";")||(n+="; "),S((()=>{let e=i(r);"string"==typeof e?t.setAttribute("style",n+e):s((()=>{t.setAttribute("style",n);for(let r in e)"symbol"!=typeof e[r]&&t.style.setProperty(r,e[r])}))}))},P.text=(t,e,r)=>{let i=I(t,e,":text");return S((()=>{let e=i(r);t.textContent=null==e?"":e}))},P[""]=(t,e,r)=>{let i=I(t,e,":");if(i)return S((()=>{let e=i(r);for(let r in e)K(t,r.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(t=>"-"+t.toLowerCase())),e[r])}))},P.value=(t,e,r)=>{let i,n,o=I(t,e,":value"),s="text"===t.type||""===t.type?e=>t.setAttribute("value",t.value=null==e?"":e):"TEXTAREA"===t.tagName||"text"===t.type||""===t.type?e=>(i=t.selectionStart,n=t.selectionEnd,t.setAttribute("value",t.value=null==e?"":e),i&&t.setSelectionRange(i,n)):"checkbox"===t.type?e=>(t.value=e?"on":"",K(t,"checked",e)):"select-one"===t.type?e=>{for(let e in t.options)e.removeAttribute("selected");t.value=e,t.selectedOptions[0]?.setAttribute("selected","")}:e=>t.value=e;return S((()=>{s(o(r))}))};var T=(t,e,r,i)=>{let n=i.startsWith("on")&&i.slice(2),o=I(t,e,":"+i);if(o){if(n){let e,i=S((()=>{e&&(e(),e=null);let i=o(r);i&&(e=C(t,n,i))}));return()=>(e?.(),i())}return S((()=>{K(t,i,o(r))}))}},C=(t,e,r)=>{if(!r)return;const i={evt:"",target:t,test:()=>!0};i.evt=(e.startsWith("on")?e.slice(2):e).replace(/\.(\w+)?-?([-\w]+)?/g,((t,e,r="")=>(i.test=W[e]?.(i,...r.split("-"))||i.test,"")));const{evt:n,target:o,test:s,defer:a,stop:l,prevent:u,...f}=i;a&&(r=a(r));const c=t=>s(t)&&(l&&t.stopPropagation(),u&&t.preventDefault(),r.call(o,t));return o.addEventListener(n,c,f),()=>o.removeEventListener(n,c,f)},W={prevent(t){t.prevent=!0},stop(t){t.stop=!0},once(t){t.once=!0},passive(t){t.passive=!0},capture(t){t.capture=!0},window(t){t.target=window},document(t){t.target=document},throttle(t,e){t.defer=t=>U(t,e?Number(e)||0:108)},debounce(t,e){t.defer=t=>B(t,e?Number(e)||0:108)},outside:t=>e=>{let r=t.target;return!(r.contains(e.target)||!1===e.target.isConnected||r.offsetWidth<1&&r.offsetHeight<1)},self:t=>e=>e.target===t.target,ctrl:(t,...e)=>t=>D.ctrl(t)&&e.every((e=>D[e]?D[e](t):t.key===e)),shift:(t,...e)=>t=>D.shift(t)&&e.every((e=>D[e]?D[e](t):t.key===e)),alt:(t,...e)=>t=>D.alt(t)&&e.every((e=>D[e]?D[e](t):t.key===e)),meta:(t,...e)=>t=>D.meta(t)&&e.every((e=>D[e]?D[e](t):t.key===e)),arrow:t=>D.arrow,enter:t=>D.enter,escape:t=>D.escape,tab:t=>D.tab,space:t=>D.space,backspace:t=>D.backspace,delete:t=>D.delete,digit:t=>D.digit,letter:t=>D.letter,character:t=>D.character},D={ctrl:t=>t.ctrlKey||"Control"===t.key||"Ctrl"===t.key,shift:t=>t.shiftKey||"Shift"===t.key,alt:t=>t.altKey||"Alt"===t.key,meta:t=>t.metaKey||"Meta"===t.key||"Command"===t.key,arrow:t=>t.key.startsWith("Arrow"),enter:t=>"Enter"===t.key,escape:t=>t.key.startsWith("Esc"),tab:t=>"Tab"===t.key,space:t=>" "===t.key||"Space"===t.key||" "===t.key,backspace:t=>"Backspace"===t.key,delete:t=>"Delete"===t.key,digit:t=>/^\d$/.test(t.key),letter:t=>/^[a-zA-Z]$/.test(t.key),character:t=>/^\S$/.test(t.key)},U=(t,e)=>{let r,i,n=o=>{r=!0,setTimeout((()=>{if(r=!1,i)return i=!1,n(o),t(o)}),e)};return e=>r?i=!0:(n(e),t(e))},B=(t,e)=>{let r;return i=>{clearTimeout(r),r=setTimeout((()=>{r=null,t(i)}),e)}},K=(t,e,r)=>{null==r||!1===r?t.removeAttribute(e):t.setAttribute(e,!0===r?"":"number"==typeof r||"string"==typeof r?r:"")},F={};function I(t,e,r){let i=F[e];if(!i)try{i=F[e]=new Function("__scope",`with (__scope) { let __; return ${e.trim()} };`)}catch(i){return L(i,t,e,r)}return n=>{let o;try{o=i.call(t,n)}catch(i){return L(i,t,e,r)}return o}}function L(t,e,r,i){Object.assign(t,{element:e,expression:r}),console.warn(`∴ ${t.message}\n\n${i}=${r?`"${r}"\n\n`:""}`,e),j((()=>{throw t}),0)}M.globals=N;var q=new WeakMap;function M(t,e){if(!t.children)return;if(q.has(t))return i((()=>Object.assign(q.get(t),e)));const r=E(e||{}),n=[],o=(t,e=t.parentNode)=>{for(let i in _){let o=":"+i;if(t.hasAttribute?.(o)){let s=t.getAttribute(o);if(t.removeAttribute(o),n.push(_[i](t,s,r,i)),q.has(t))return;if(t.parentNode!==e)return!1}}if(t.attributes)for(let e=0;e<t.attributes.length;){let i=t.attributes[e],o=i.name[0];if(":"===o||"@"===o){t.removeAttribute(i.name);let e="@"===o?`${i.value.includes("await")?"async":""} event=>{${i.value}}`:i.value,s=i.name.slice(1).split(o);for(let i of s){"@"===o&&(i="on"+i);let s=P[i]||T;n.push(s(t,e,r,i))}}else e++}for(let e,r=0;e=t.children[r];r++)!1===o(e,t)&&r--};return o(t),q.has(t)||(q.set(t,r),n.length&&Object.defineProperty(t,x,{value:()=>{for(;n.length;)n.shift()?.();q.delete(t)}})),r}var R=M;document.currentScript&&M(document.documentElement);export{R as default};
package/src/core.js CHANGED
@@ -1,23 +1,20 @@
1
- import { state as createState, fx, sandbox } from './state.js';
2
- import defaultDirective, { primary, secondary, on } from './directives.js';
1
+ import createState, { batch, sandbox, _dispose } from './state.signals-proxy.js';
2
+ import defaultDirective, { primary, secondary } from './directives.js';
3
3
 
4
+
5
+ // default root sandbox
4
6
  sprae.globals = sandbox
5
7
 
6
8
  // sprae element: apply directives
7
9
  const memo = new WeakMap
8
10
  export default function sprae(container, values) {
9
- if (!container.children) return
10
- if (memo.has(container)) return Object.assign(memo.get(container), values)
11
-
12
- // subscribe to reactives
13
- // for (let prop in values) {
14
- // if (observable(values[prop])) {
15
- // let value = values[prop]; values[prop] = null
16
- // sube(value, value => state[prop] = value)
17
- // }
18
- // }
11
+ if (!container.children) return // ignore what?
12
+
13
+ if (memo.has(container)) return batch(() => Object.assign(memo.get(container), values))
14
+
15
+ // take over existing state instead of creating clone
19
16
  const state = createState(values || {});
20
- const updates = []
17
+ const disposes = []
21
18
 
22
19
  // init directives on element
23
20
  const init = (el, parent = el.parentNode) => {
@@ -28,9 +25,9 @@ export default function sprae(container, values) {
28
25
  let expr = el.getAttribute(attrName)
29
26
  el.removeAttribute(attrName)
30
27
 
31
- updates.push(primary[name](el, expr, state, name))
28
+ disposes.push(primary[name](el, expr, state, name))
32
29
 
33
- // stop if element was spraed by directive or skipped (detached)
30
+ // stop if element was spraed by directive or skipped (detached) like in case of :if or :each
34
31
  if (memo.has(el)) return
35
32
  if (el.parentNode !== parent) return false
36
33
  }
@@ -51,7 +48,7 @@ export default function sprae(container, values) {
51
48
  // @click forwards to :onclick=event=>{...inline}
52
49
  if (prefix === '@') name = `on` + name
53
50
  let dir = secondary[name] || defaultDirective;
54
- updates.push(dir(el, expr, state, name));
51
+ disposes.push(dir(el, expr, state, name));
55
52
  // NOTE: secondary directives don't stop flow nor extend state, so no need to check
56
53
  }
57
54
  }
@@ -67,17 +64,19 @@ export default function sprae(container, values) {
67
64
 
68
65
  init(container);
69
66
 
70
- // call updates: subscribes directives to state;
71
- // state is created after inits because directives can extend init values (expose refs etc)
72
- for (let update of updates) if (update) {
73
- let teardown
74
- fx(() => {
75
- if (typeof teardown === 'function') teardown()
76
- teardown = update(state)
77
- });
78
- }
67
+ // if element was spraed by :with or :each instruction - skip
68
+ if (memo.has(container)) return state //memo.get(container)
79
69
 
70
+ // save
80
71
  memo.set(container, state);
81
72
 
73
+ // expose dispose
74
+ if (disposes.length) Object.defineProperty(container, _dispose, {
75
+ value: () => {
76
+ while (disposes.length) disposes.shift()?.();
77
+ memo.delete(container);
78
+ }
79
+ });
80
+
82
81
  return state;
83
82
  }
package/src/directives.js CHANGED
@@ -1,8 +1,6 @@
1
1
  // directives & parsing
2
2
  import sprae from './core.js'
3
- import swap from './domdiff.js'
4
- // import swap from 'swapdom'
5
- import { state as createState } from './state.js'
3
+ import createState, { effect, computed, untracked, batch, _dispose, _signals } from './state.signals-proxy.js'
6
4
  import { queueMicrotask, WeakishMap } from './util.js'
7
5
 
8
6
  // reserved directives - order matters!
@@ -12,11 +10,12 @@ export const primary = {}, secondary = {}
12
10
  // :if is interchangeable with :each depending on order, :if :each or :each :if have different meanings
13
11
  // as for :if :with - :if must init first, since it is lazy, to avoid initializing component ahead of time by :with
14
12
  // we consider :with={x} :if={x} case insignificant
15
- primary['if'] = (el, expr) => {
13
+ primary['if'] = (el, expr, state) => {
16
14
  let holder = document.createTextNode(''),
17
15
  clauses = [parseExpr(el, expr, ':if')],
18
16
  els = [el], cur = el
19
17
 
18
+ // consume all following siblings with :else, :if into a single group
20
19
  while (cur = el.nextElementSibling) {
21
20
  if (cur.hasAttribute(':else')) {
22
21
  cur.removeAttribute(':else');
@@ -33,7 +32,8 @@ primary['if'] = (el, expr) => {
33
32
 
34
33
  el.replaceWith(cur = holder)
35
34
 
36
- return (state) => {
35
+ const dispose = effect(() => {
36
+ // find matched clause (re-evaluates any time any clause updates)
37
37
  let i = clauses.findIndex(f => f(state))
38
38
  if (els[i] != cur) {
39
39
  ; (cur[_each] || cur).replaceWith(cur = els[i] || holder);
@@ -41,74 +41,89 @@ primary['if'] = (el, expr) => {
41
41
  // but :if must come first to avoid preliminary caching
42
42
  sprae(cur, state);
43
43
  }
44
+ })
45
+
46
+ return () => {
47
+ for (const el of els) el[_dispose]?.() // dispose all internal spraes
48
+ dispose() // dispose subscription
44
49
  }
45
50
  }
46
51
 
47
52
  const _each = Symbol(':each')
48
53
 
49
54
  // :each must init before :ref, :id or any others, since it defines scope
50
- primary['each'] = (tpl, expr) => {
55
+ primary['each'] = (tpl, expr, state) => {
51
56
  let each = parseForExpression(expr);
52
57
  if (!each) return exprError(new Error, tpl, expr, ':each');
53
58
 
54
- // FIXME: make sure no memory leak here
59
+ const [itemVar, idxVar, itemsExpr] = each;
60
+
55
61
  // we need :if to be able to replace holder instead of tpl for :if :each case
56
62
  const holder = tpl[_each] = document.createTextNode('')
57
63
  tpl.replaceWith(holder)
58
64
 
59
- const evaluate = parseExpr(tpl, each[2], ':each');
60
-
61
- const keyExpr = tpl.getAttribute(':key');
62
- const itemKey = keyExpr ? parseExpr(null, keyExpr, ':each') : null;
63
- tpl.removeAttribute(':key')
64
-
65
- const refExpr = tpl.getAttribute(':ref');
66
-
67
- const scopes = new WeakishMap() // stores scope per data item
68
- const itemEls = new WeakishMap() // element per data item
69
- let curEls = []
70
-
71
- return (state) => {
72
- // get items
73
- let list = evaluate(state)
74
-
75
- if (!list) list = []
76
- else if (typeof list === 'number') list = Array.from({ length: list }, (_, i) => [i + 1, i])
77
- else if (Array.isArray(list)) list = list.map((item, i) => [i + 1, item])
78
- else if (typeof list === 'object') list = Object.entries(list)
79
- else exprError(Error('Bad list value'), tpl, expr, ':each', list)
65
+ const evaluate = parseExpr(tpl, itemsExpr, ':each');
80
66
 
81
- // collect elements/scopes for items
82
- let newEls = [], elScopes = []
67
+ // we re-create items any time new items are produced
68
+ let items, prevl = 0, keys
69
+ effect(() => {
70
+ let newItems = evaluate(state)
83
71
 
84
- for (let [idx, item] of list) {
85
- let el, scope, key = itemKey?.({ [each[0]]: item, [each[1]]: idx })
86
-
87
- // we consider if data items are primitive, then nodes needn't be cached
88
- // since likely they're very simple to create
89
- if (key == null) el = tpl.cloneNode(true);
90
- else (el = itemEls.get(key)) || itemEls.set(key, el = tpl.cloneNode(true));
91
-
92
- newEls.push(el)
93
-
94
- if (key == null || !(scope = scopes.get(key))) {
95
- scope = createState({ [each[0]]: item, [refExpr || '']: null, [each[1]]: idx }, state)
96
- if (key != null) scopes.set(key, scope)
97
- }
98
- // need to explicitly set item to update existing children's values
99
- else scope[each[0]] = item
100
-
101
- elScopes.push(scope)
72
+ // convert items to array
73
+ if (!newItems) newItems = []
74
+ else if (typeof newItems === 'number') {
75
+ newItems = Array.from({ length: newItems }, (_, i) => i)
76
+ }
77
+ else if (Array.isArray(newItems));
78
+ else if (typeof newItems === 'object') {
79
+ keys = Object.keys(newItems);
80
+ newItems = Object.values(newItems);
81
+ }
82
+ else {
83
+ exprError(Error('Bad items value'), tpl, expr, ':each', newItems)
102
84
  }
103
85
 
104
- swap(holder.parentNode, curEls, newEls, holder)
105
- curEls = newEls
86
+ untracked(() => batch(() => {
87
+ // init items
88
+ if (!items?.[_signals][0]?.peek) {
89
+ // manual dispose for plain arrays (not states) - _signals here is just fake holder for destructors
90
+ for (let i = 0, signals = items?.[_signals]; i < prevl; i++) signals[i]?._del()
91
+ // NOTE: new items are initialized in length effect below
92
+ items = newItems, items[_signals] ||= {}
93
+ }
94
+ // patch existing items and insert new items - init happens in length effect
95
+ else {
96
+ let newl = newItems.length, i = 0
97
+ for (; i < newl; i++) items[i] = newItems[i]
98
+ items.length = newl // dispose tail (done internally in state)
99
+ }
100
+ }))
101
+
102
+ // length change effect
103
+ return effect(() => {
104
+ let newl = newItems.length // indicate that we track it
105
+ if (prevl !== newl) untracked(() => batch(() => {
106
+ // init new items
107
+ const signals = items[_signals]
108
+ for (let i = prevl; i < newl; i++) {
109
+ items[i]; // touch item to create signal
110
+ const el = tpl.cloneNode(true), scope = createState({
111
+ [itemVar]: signals[i] ?? items[i], [idxVar]: keys?.[i] ?? i
112
+ }, state)
113
+ holder.before(el)
114
+ sprae(el, scope)
115
+ const { _del } = (signals[i] ||= {});
116
+ signals[i]._del = () => { delete signals[i]; _del?.(); el[_dispose](), el.remove(), delete items[i] }
117
+ }
118
+ prevl = newl
119
+ }))
120
+ })
121
+ })
106
122
 
107
- // init new elements
108
- for (let i = 0; i < newEls.length; i++) {
109
- sprae(newEls[i], elScopes[i])
110
- }
111
- }
123
+ return () => batch(() => {
124
+ for (let _i of items[_signals]) _i?._del()
125
+ items.length = 0
126
+ })
112
127
  }
113
128
 
114
129
  // `:each` can redefine scope as `:each="a in {myScope}"`,
@@ -117,7 +132,8 @@ primary['with'] = (el, expr, rootState) => {
117
132
  let evaluate = parseExpr(el, expr, ':with')
118
133
  const localState = evaluate(rootState)
119
134
  let state = createState(localState, rootState)
120
- sprae(el, state);
135
+ sprae(el, state)
136
+ return el[_dispose];
121
137
  }
122
138
 
123
139
  // ref must be last within primaries, since that must be skipped by :each, but before secondaries
@@ -129,6 +145,7 @@ primary['ref'] = (el, expr, state) => {
129
145
 
130
146
 
131
147
  // This was taken AlpineJS, former VueJS 2.* core. Thanks Alpine & Vue!
148
+ // takes instruction like `item in list`, returns ['item', '', 'list']
132
149
  function parseForExpression(expression) {
133
150
  let forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
134
151
  let stripParensRE = /^\s*\(|\)\s*$/g
@@ -159,18 +176,19 @@ secondary['render'] = (el, expr, state) => {
159
176
  let content = tpl.content.cloneNode(true);
160
177
  el.replaceChildren(content)
161
178
  sprae(el, state)
179
+ return el[_dispose]
162
180
  }
163
181
 
164
- secondary['id'] = (el, expr) => {
182
+ secondary['id'] = (el, expr, state) => {
165
183
  let evaluate = parseExpr(el, expr, ':id')
166
184
  const update = v => el.id = v || v === 0 ? v : ''
167
- return (state) => update(evaluate(state))
185
+ return effect(() => update(evaluate(state)))
168
186
  }
169
187
 
170
- secondary['class'] = (el, expr) => {
188
+ secondary['class'] = (el, expr, state) => {
171
189
  let evaluate = parseExpr(el, expr, ':class')
172
190
  let initClassName = el.getAttribute('class')
173
- return (state) => {
191
+ return effect(() => {
174
192
  let v = evaluate(state)
175
193
  let className = [initClassName]
176
194
  if (v) {
@@ -180,42 +198,44 @@ secondary['class'] = (el, expr) => {
180
198
  }
181
199
  if (className = className.filter(Boolean).join(' ')) el.setAttribute('class', className);
182
200
  else el.removeAttribute('class')
183
- }
201
+ })
184
202
  }
185
203
 
186
- secondary['style'] = (el, expr) => {
204
+ secondary['style'] = (el, expr, state) => {
187
205
  let evaluate = parseExpr(el, expr, ':style')
188
206
  let initStyle = el.getAttribute('style') || ''
189
207
  if (!initStyle.endsWith(';')) initStyle += '; '
190
- return (state) => {
208
+ return effect(() => {
191
209
  let v = evaluate(state)
192
210
  if (typeof v === 'string') el.setAttribute('style', initStyle + v)
193
211
  else {
194
- el.setAttribute('style', initStyle)
195
- for (let k in v) el.style.setProperty(k, v[k])
212
+ untracked(() => {
213
+ el.setAttribute('style', initStyle)
214
+ for (let k in v) if (typeof v[k] !== 'symbol') el.style.setProperty(k, v[k])
215
+ })
196
216
  }
197
- }
217
+ })
198
218
  }
199
219
 
200
- secondary['text'] = (el, expr) => {
220
+ secondary['text'] = (el, expr, state) => {
201
221
  let evaluate = parseExpr(el, expr, ':text')
202
- return (state) => {
222
+ return effect(() => {
203
223
  let value = evaluate(state)
204
224
  el.textContent = value == null ? '' : value;
205
- }
225
+ })
206
226
  }
207
227
 
208
228
  // set props in-bulk or run effect
209
- secondary[''] = (el, expr) => {
229
+ secondary[''] = (el, expr, state) => {
210
230
  let evaluate = parseExpr(el, expr, ':')
211
- if (evaluate) return (state) => {
231
+ if (evaluate) return effect(() => {
212
232
  let value = evaluate(state)
213
233
  for (let key in value) attr(el, dashcase(key), value[key]);
214
- }
234
+ })
215
235
  }
216
236
 
217
237
  // connect expr to element value
218
- secondary['value'] = (el, expr) => {
238
+ secondary['value'] = (el, expr, state) => {
219
239
  let evaluate = parseExpr(el, expr, ':value')
220
240
 
221
241
  let from, to
@@ -236,7 +256,7 @@ secondary['value'] = (el, expr) => {
236
256
  value => el.value = value
237
257
  )
238
258
 
239
- return (state) => update(evaluate(state))
259
+ return effect(() => { update(evaluate(state)) })
240
260
  }
241
261
 
242
262
  // any unknown directive
@@ -246,17 +266,21 @@ export default (el, expr, state, name) => {
246
266
 
247
267
  if (!evaluate) return
248
268
 
249
- if (evt) return (state => {
250
- // we need anonymous callback to enable modifiers like prevent
251
- let value = evaluate(state) || (() => { })
252
- return on(el, evt, value)
253
- })
269
+ if (evt) {
270
+ let off, dispose = effect(() => {
271
+ if (off) off(), off = null
272
+ // we need anonymous callback to enable modifiers like prevent
273
+ let value = evaluate(state)
274
+ if (value) off = on(el, evt, value)
275
+ })
276
+ return () => (off?.(), dispose())
277
+ }
254
278
 
255
- return state => attr(el, name, evaluate(state))
279
+ return effect(() => { attr(el, name, evaluate(state)) })
256
280
  }
257
281
 
258
282
  // bind event to a target
259
- export const on = (el, e, fn) => {
283
+ const on = (el, e, fn) => {
260
284
  if (!fn) return
261
285
 
262
286
  const ctx = { evt: '', target: el, test: () => true };
@@ -278,7 +302,9 @@ export const on = (el, e, fn) => {
278
302
  )
279
303
 
280
304
  target.addEventListener(evt, cb, opts)
281
- return () => target.removeEventListener(evt, cb, opts)
305
+
306
+ // return off
307
+ return () => (target.removeEventListener(evt, cb, opts))
282
308
  }
283
309
 
284
310
  // event modifiers
@@ -384,7 +410,7 @@ function parseExpr(el, expression, dir) {
384
410
 
385
411
  if (!evaluate) {
386
412
  try {
387
- evaluate = evaluatorMemo[expression] = new Function(`__scope`, `with (__scope) { return ${expression.trim()} };`)
413
+ evaluate = evaluatorMemo[expression] = new Function(`__scope`, `with (__scope) { let __; return ${expression.trim()} };`)
388
414
  } catch (e) {
389
415
  return exprError(e, el, expression, dir)
390
416
  }
package/src/domdiff.js CHANGED
@@ -1,23 +1,26 @@
1
1
  // https://github.com/luwes/js-diff-benchmark/blob/master/libs/list-difference.js
2
2
  // this implementation is more persistent in terms of preserving in-between nodes
3
+ // also it handles _dispose
3
4
 
4
5
  // a is old list, b is the new
5
- export default function(parent, a, b, before) {
6
+ export default function (parent, a, b, before) {
6
7
  const aIdx = new Map();
7
8
  const bIdx = new Map();
8
9
  let i;
9
10
  let j;
10
11
 
11
- // Create a mapping from keys to their position in the old list
12
- for (i = 0; i < a.length; i++) {
13
- aIdx.set(a[i], i);
14
- }
15
-
16
12
  // Create a mapping from keys to their position in the new list
17
13
  for (i = 0; i < b.length; i++) {
18
14
  bIdx.set(b[i], i);
19
15
  }
20
16
 
17
+ // Create a mapping from keys to their position in the old list
18
+ for (i = 0; i < a.length; i++) {
19
+ aIdx.set(a[i], i);
20
+ // dispose a[i] if is going to disappear
21
+ if (!bIdx.has(a[i])) a[i][Symbol.dispose]?.()
22
+ }
23
+
21
24
  for (i = j = 0; i !== a.length || j !== b.length;) {
22
25
  var aElm = a[i], bElm = b[j];
23
26
  if (aElm === null) {
@@ -65,4 +68,4 @@ export default function(parent, a, b, before) {
65
68
  }
66
69
  }
67
70
  return b;
68
- };
71
+ };
@@ -1,7 +1,10 @@
1
+ // proxy-based store implementation: more flexible, less optimal and with memory leak now
2
+ // FIXME: make simpler prototype chain, likely manual
3
+ // FIXME: detect circular updates
1
4
  import { queueMicrotask } from "./util.js"
2
5
 
3
6
  // currentFx stack of listeners
4
- let currentFx, batch = new Set, pendingUpdate
7
+ let currentFx, batch = [], pendingUpdate
5
8
 
6
9
  const targetFxs = new WeakMap
7
10
  const targetProxy = new WeakMap
@@ -14,19 +17,30 @@ export const sandbox = {
14
17
  console, window, document, history, location
15
18
  }
16
19
 
20
+ const callArg = (fn) => fn(), nop = () => { }
21
+ export { callArg as batch };
22
+
23
+ let lastProp
24
+
17
25
  const handler = {
18
- has(){
26
+ has() {
19
27
  // sandbox everything
20
28
  return true
21
29
  },
22
30
 
23
31
  get(target, prop) {
24
- if (typeof prop === 'symbol') return target[prop]
32
+ if (typeof prop === 'symbol') return lastProp = null, target[prop]
25
33
  if (!(prop in target)) return target[_parent]?.[prop]
34
+
35
+ // ignore .length effects called within .push
36
+ if (lastProp && prop === 'length') return lastProp = null, target[prop];
37
+
26
38
  // .constructor, .slice etc
27
- if (prop in Object.prototype || (Array.isArray(target) && prop in Array.prototype && prop !== 'length')) return target[prop];
39
+ if (Object.prototype[prop]) return target[prop]
40
+ if (Array.isArray(target) && Array.prototype[prop] && prop !== 'length') return lastProp = prop, target[prop];
28
41
 
29
42
  let value = target[prop]
43
+
30
44
  if (currentFx) {
31
45
  // get actual target from prototype chain
32
46
  // while (!target.hasOwnProperty(prop)) target = Object.getPrototypeOf(target)
@@ -61,7 +75,11 @@ const handler = {
61
75
  // whenever target prop is set, call all dependent fxs
62
76
  let propFxs = targetFxs.get(target)?.[prop]
63
77
 
64
- if (propFxs) for (let fx of propFxs) batch.add(fx)
78
+ if (propFxs) for (let fx of propFxs) {
79
+ // put fx latest
80
+ if (fx.planned != null) batch[fx.planned] = null
81
+ fx.planned = batch.push(fx) - 1
82
+ }
65
83
  planUpdate()
66
84
 
67
85
  // FIXME: unsubscribe / delete effects by setting null/undefined
@@ -77,7 +95,7 @@ const handler = {
77
95
  }
78
96
  }
79
97
 
80
- export const state = (obj, parent) => {
98
+ export default function state(obj, parent) {
81
99
  if (targetProxy.has(obj)) return targetProxy.get(obj)
82
100
  if (proxyTarget.has(obj)) return obj // is proxy already
83
101
 
@@ -85,7 +103,7 @@ export const state = (obj, parent) => {
85
103
  targetProxy.set(obj, proxy)
86
104
  proxyTarget.set(proxy, obj)
87
105
 
88
- // bind all getters here to proxy
106
+ // bind all getters & functions here to proxy
89
107
  // FIXME: alternatively we can store getters somewhere
90
108
  let descriptors = Object.getOwnPropertyDescriptors(obj)
91
109
  for (let name in descriptors) {
@@ -93,6 +111,9 @@ export const state = (obj, parent) => {
93
111
  if (desc.get) {
94
112
  if (desc.get) desc.get = desc.get.bind(proxy), Object.defineProperty(obj, name, desc)
95
113
  }
114
+ // else if (typeof desc.value === 'function') {
115
+ // desc.value = desc.value.bind(proxy), Object.defineProperty(obj, name, desc)
116
+ // }
96
117
  }
97
118
 
98
119
  // inherit from parent state
@@ -101,7 +122,7 @@ export const state = (obj, parent) => {
101
122
  return proxy
102
123
  }
103
124
 
104
- export const fx = (fn) => {
125
+ const fx = (fn) => {
105
126
  const call = () => {
106
127
  let prev = currentFx
107
128
  currentFx = call
@@ -115,13 +136,15 @@ export const fx = (fn) => {
115
136
  return call
116
137
  }
117
138
 
118
- export const planUpdate = () => {
119
- if (!pendingUpdate) {
120
- pendingUpdate = true
121
- queueMicrotask(() => {
122
- for (let fx of batch) fx.call()
123
- batch.clear()
124
- pendingUpdate = false
125
- })
126
- }
139
+ const planUpdate = () => {
140
+ // if (!pendingUpdate) {
141
+ // pendingUpdate = true
142
+ // queueMicrotask(() => {
143
+ let fx
144
+ while (batch.length) fx = batch.shift(), fx && (fx(), fx.planned = null)
145
+ // pendingUpdate = false
146
+ // })
147
+ // }
127
148
  }
149
+
150
+ export { fx as effect };