sprae 3.0.1 → 3.1.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sprae",
3
3
  "description": "DOM microhydration.",
4
- "version": "3.0.1",
4
+ "version": "3.1.0",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
7
7
  "type": "module",
package/readme.md CHANGED
@@ -254,7 +254,7 @@ Expose element to data scope with the `id`:
254
254
 
255
255
  ## Sandbox
256
256
 
257
- Expressions are sandboxed, ie. have no access to global or window.
257
+ Expressions are sandboxed, ie. have no access to global or window (since sprae can be run in server environment).
258
258
 
259
259
  Default sandbox provides: _Array_, _Object_, _Number_, _String_, _Boolean_, _Date_, _console_.
260
260
 
package/sprae.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/state.js
2
2
  var currentFx;
3
- var currentBatch;
3
+ var batch = /* @__PURE__ */ new Set();
4
+ var pendingUpdate;
4
5
  var targetFxs = /* @__PURE__ */ new WeakMap();
5
6
  var targetProxy = /* @__PURE__ */ new WeakMap();
6
7
  var proxyTarget = /* @__PURE__ */ new WeakMap();
@@ -55,7 +56,8 @@ var handler = {
55
56
  let propFxs = targetFxs.get(target)?.[prop];
56
57
  if (propFxs)
57
58
  for (let fx2 of propFxs)
58
- currentBatch ? currentBatch.add(fx2) : fx2.call();
59
+ batch.add(fx2);
60
+ planUpdate();
59
61
  return true;
60
62
  },
61
63
  deleteProperty(target, prop) {
@@ -85,14 +87,16 @@ var fx = (fn) => {
85
87
  call();
86
88
  return call;
87
89
  };
88
- var batch = (fn) => {
89
- let prevBatch = currentBatch;
90
- currentBatch = /* @__PURE__ */ new Set();
91
- fn();
92
- for (let fx2 of currentBatch)
93
- fx2.call();
94
- currentBatch.clear();
95
- currentBatch = prevBatch;
90
+ var planUpdate = () => {
91
+ if (!pendingUpdate) {
92
+ pendingUpdate = true;
93
+ queueMicrotask(() => {
94
+ for (let fx2 of batch)
95
+ fx2.call();
96
+ batch.clear();
97
+ pendingUpdate = false;
98
+ });
99
+ }
96
100
  };
97
101
 
98
102
  // src/domdiff.js
@@ -569,11 +573,8 @@ var memo = /* @__PURE__ */ new WeakMap();
569
573
  function sprae(container, values) {
570
574
  if (!container.children)
571
575
  return;
572
- if (memo.has(container)) {
573
- let state3 = memo.get(container);
574
- batch(() => Object.assign(state3, values));
575
- return state3;
576
- }
576
+ if (memo.has(container))
577
+ return Object.assign(memo.get(container), values);
577
578
  const state2 = state(values || {});
578
579
  const updates = [];
579
580
  const init = (el, parent = el.parentNode) => {
package/sprae.min.js CHANGED
@@ -1 +1 @@
1
- var e,t,r=new WeakMap,n=new WeakMap,l=new WeakMap,s=Symbol("parent"),a={Array:Array,Object:Object,Number:Number,String:String,Boolean:Boolean,Date:Date,console:console},i={has:()=>!0,get(t,l){if("symbol"==typeof l)return t[l];if(!(l in t))return t[s]?.[l];if(Array.isArray(t)&&l in Array.prototype)return t[l];let a=t[l];if(e){let n=r.get(t);n||r.set(t,n={}),n[l]?n[l].includes(e)||n[l].push(e):n[l]=[e]}if(a&&a.constructor===Object||Array.isArray(a)){let e=n.get(a);return e||n.set(a,e=new Proxy(a,i)),e}return a},set(e,n,l){if(!(n in e)&&e[s]&&n in e[s])return e[s][n]=l;if(Array.isArray(e)&&n in Array.prototype)return e[n]=l;const a=e[n];if(Object.is(a,l))return!0;e[n]=l;let i=r.get(e)?.[n];if(i)for(let e of i)t?t.add(e):e.call();return!0},deleteProperty:(e,t)=>(e[t]=void 0,delete e[t],!0)},o=(e,t)=>{if(n.has(e))return n.get(e);if(l.has(e))return e;let r=new Proxy(e,i);return n.set(e,r),l.set(r,e),e[s]=t?o(t):a,r},u=t=>{const r=()=>{let n=e;e=r,t(),e=n};return r(),r},c=new WeakMap,f=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=>c.get(e)||(e=>{const t=new WeakRef(e);return c.set(e,t),t})(e))(t),this.#e.register(t,e,r),super.set(e,r)}},p={},y={};p.if=(e,t)=>{let r=document.createTextNode(""),n=[W(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(W(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[h]||s).replaceWith(s=l[t]||r),j(s,e))}},p.with=(e,t,r)=>{const n=W(e,t,"with")(r);j(e,o(n,r))};var h=Symbol(":each");p.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 x(new Error,e,t);const n=e[h]=document.createTextNode("");e.replaceWith(n);const l=W(e,r[2],":each"),s=e.getAttribute(":key"),a=s?W(null,s):null;e.removeAttribute(":key");const i=e.getAttribute(":ref"),u=new f,c=new f;let p=[];return s=>{let f=l(s);f?"number"==typeof f?f=Array.from({length:f},((e,t)=>[t,t+1])):Array.isArray(f)?f=f.map(((e,t)=>[t+1,e])):"object"==typeof f?f=Object.entries(f):x(Error("Bad list value"),e,t,":each"):f=[];let y=[],h=[];for(let[t,n]of f){let l,f,p=a?.({[r[0]]:n,[r[1]]:t});null==p?l=e.cloneNode(!0):(l=c.get(p))||c.set(p,l=e.cloneNode(!0)),y.push(l),null!=p&&(f=u.get(p))?f[r[0]]=n:(f=o({[r[0]]:n,[i||""]:null,[r[1]]:t},s),null!=p&&u.set(p,f)),h.push(f)}!function(e,t,r,n){const l=new Map,s=new Map;let a,i;for(a=0;a<t.length;a++)l.set(t[a],a);for(a=0;a<r.length;a++)s.set(r[a],a);for(a=i=0;a!==t.length||i!==r.length;){var o=t[a],u=r[i];if(null===o)a++;else if(r.length<=i)e.removeChild(t[a]),a++;else if(t.length<=a)e.insertBefore(u,t[a]||n),i++;else if(o===u)a++,i++;else{var c=s.get(o),f=l.get(u);void 0===c?(e.removeChild(t[a]),a++):void 0===f?(e.insertBefore(u,t[a]||n),i++):(e.insertBefore(t[f],t[a]||n),t[f]=null,f>a+1&&a++,i++)}}}(n.parentNode,p,y,n),p=y;for(let e=0;e<y.length;e++)j(y[e],h[e])}},y.ref=(e,t,r)=>{r[t]=e},y.id=(e,t)=>{let r=W(e,t,":id");return t=>{return n=r(t),e.id=n||0===n?n:"";var n}},y.class=(e,t)=>{let r=W(e,t,":class"),n=e.className;return t=>{let l=r(t),s="string"==typeof l?l:(Array.isArray(l)?l:Object.entries(l).map((([e,t])=>t?e:""))).filter(Boolean).join(" ");e.className=[n,s].filter(Boolean).join(" ")}},y.style=(e,t)=>{let r=W(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])}}},y.text=(e,t)=>{let r=W(e,t,":text");return t=>{let n=r(t);e.textContent=null==n?"":n}},y.data=(e,t)=>{let r=W(e,t,":data");return t=>{let n=r(t);for(let t in n)e.dataset[t]=n[t]}},y.aria=(e,t)=>{let r=W(e,t,":aria");return t=>(t=>{for(let r in t)k(e,"aria-"+N(r),null==t[r]?null:t[r]+"")})(r(t))},y[""]=(e,t)=>{let r=W(e,t,":");if(r)return t=>{let n=r(t);for(let t in n)k(e,N(t),n[t])}},y.value=(e,t)=>{let r,n,l=W(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":"",k(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))},y.on=(e,t)=>{let r=W(e,t,":on");return t=>{let n=r(t),l=[];for(let t in n)l.push(d(e,t,n[t]));return()=>{for(let e of l)e()}}};var g=(e,t,r,n)=>{let l=n.startsWith("on")&&n.slice(2),s=W(e,t,":"+n);if(s)return l?t=>{let r=s(t)||(()=>{});return d(e,l,r)}:t=>k(e,n,s(t))},d=(e,t,r)=>{if(!r)return;let n=t.split("..").map((t=>{let r={evt:"",target:e,test:()=>!0};return r.evt=(t.startsWith("on")?t.slice(2):t).replace(/\.(\w+)?-?([-\w]+)?/g,((e,t,n="")=>(r.test=m[t]?.(r,...n.split("-"))||r.test,""))),r}));if(1==n.length)return a(r,n[0]);const l=(t,r=0)=>{let s;return s=a((a=>{r&&s();let i=t.call(e,a);"function"!=typeof i&&(i=()=>{}),r+1<n.length&&l(i,r?r+1:1)}),n[r])};let s=l(r);return()=>s();function a(e,{evt:t,target:r,test:n,defer:l,stop:s,prevent:a,...i}){l&&(e=l(e));let o=t=>n(t)&&(s&&t.stopPropagation(),a&&t.preventDefault(),e.call(r,t));return r.addEventListener(t,o,i),()=>r.removeEventListener(t,o,i)}},m={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},toggle(e){e.defer=(t,r)=>n=>r?(r.call?.(e.target,n),r=null):r=t()},throttle(e,t){e.defer=e=>v(e,t?Number(t)||0:108)},debounce(e,t){e.defer=e=>A(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=>b.ctrl(e)&&t.every((t=>b[t]?b[t](e):e.key===t)),shift:(e,...t)=>e=>b.shift(e)&&t.every((t=>b[t]?b[t](e):e.key===t)),alt:(e,...t)=>e=>b.alt(e)&&t.every((t=>b[t]?b[t](e):e.key===t)),meta:(e,...t)=>e=>b.meta(e)&&t.every((t=>b[t]?b[t](e):e.key===t)),arrow:e=>b.arrow,enter:e=>b.enter,escape:e=>b.escape,tab:e=>b.tab,space:e=>b.space,backspace:e=>b.backspace,delete:e=>b.delete,digit:e=>b.digit,letter:e=>b.letter,character:e=>b.character},b={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)},v=(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))},A=(e,t)=>{let r;return n=>{clearTimeout(r),r=setTimeout((()=>{r=null,e(n)}),t)}},k=(e,t,r)=>{null==r||!1===r?e.removeAttribute(t):e.setAttribute(t,!0===r?"":"number"==typeof r||"string"==typeof r?r:"")},w={};function W(e,t,r){let n=w[t];if(!n){let l=/^[\n\s]*if.*\(.*\)/.test(t)||/\b(let|const)\s/.test(t)&&!r.startsWith(":on")?`(() => {${t}})()`:t;try{n=w[t]=new Function("__scope",`with (__scope) { return ${l.trim()} };`)}catch(n){return x(n,e,t,r)}}return l=>{let s;try{s=n.call(e,l)}catch(n){return x(n,e,t,r)}return s}}function x(e,t,r,n){Object.assign(e,{element:t,expression:r}),console.warn(`∴ ${e.message}\n\n${n}=${r?`"${r}"\n\n`:""}`,t),queueMicrotask((()=>{throw e}),0)}function N(e){return e.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(e=>"-"+e.toLowerCase()))}j.globals=a;var S=new WeakMap;function j(e,r){if(!e.children)return;if(S.has(e)){let n=S.get(e);return(e=>{let l=t;t=new Set,Object.assign(n,r);for(let e of t)e.call();t.clear(),t=l})(),n}const n=o(r||{}),l=[],s=(e,t=e.parentNode)=>{for(let r in p){let s=":"+r;if(e.hasAttribute?.(s)){let a=e.getAttribute(s);if(e.removeAttribute(s),l.push(p[r](e,a,n,r)),S.has(e)||e.parentNode!==t)return!1}}if(e.attributes)for(let r=0;r<e.attributes.length;){let s=e.attributes[r];if(":"!==s.name[0]){r++;continue}e.removeAttribute(s.name);let a=s.value,i=s.name.slice(1).split(":");for(let r of i){let s=y[r]||g;if(l.push(s(e,a,n,r)),S.has(e)||e.parentNode!==t)return!1}}for(let t,r=0;t=e.children[r];r++)!1===s(t,e)&&r--};s(e);for(let e of l)if(e){let t;u((()=>{"function"==typeof t&&t(),t=e(n)}))}return S.set(e,n),n}var E=j;export{E as default};
1
+ var e,t,r=new Set,n=new WeakMap,l=new WeakMap,s=new WeakMap,a=Symbol("parent"),i={Array:Array,Object:Object,Number:Number,String:String,Boolean:Boolean,Date:Date,console:console},o={has:()=>!0,get(t,r){if("symbol"==typeof r)return t[r];if(!(r in t))return t[a]?.[r];if(Array.isArray(t)&&r in Array.prototype)return t[r];let s=t[r];if(e){let l=n.get(t);l||n.set(t,l={}),l[r]?l[r].includes(e)||l[r].push(e):l[r]=[e]}if(s&&s.constructor===Object||Array.isArray(s)){let e=l.get(s);return e||l.set(s,e=new Proxy(s,o)),e}return s},set(e,t,l){if(!(t in e)&&e[a]&&t in e[a])return e[a][t]=l;if(Array.isArray(e)&&t in Array.prototype)return e[t]=l;const s=e[t];if(Object.is(s,l))return!0;e[t]=l;let i=n.get(e)?.[t];if(i)for(let e of i)r.add(e);return f(),!0},deleteProperty:(e,t)=>(e[t]=void 0,delete e[t],!0)},u=(e,t)=>{if(l.has(e))return l.get(e);if(s.has(e))return e;let r=new Proxy(e,o);return l.set(e,r),s.set(r,e),e[a]=t?u(t):i,r},c=t=>{const r=()=>{let n=e;e=r,t(),e=n};return r(),r},f=()=>{t||(t=!0,queueMicrotask((()=>{for(let e of r)e.call();r.clear(),t=!1})))},p=new WeakMap,y=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=>p.get(e)||(e=>{const t=new WeakRef(e);return p.set(e,t),t})(e))(t),this.#e.register(t,e,r),super.set(e,r)}},h={},g={};h.if=(e,t)=>{let r=document.createTextNode(""),n=[N(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(N(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[d]||s).replaceWith(s=l[t]||r),M(s,e))}},h.with=(e,t,r)=>{const n=N(e,t,"with")(r);M(e,u(n,r))};var d=Symbol(":each");h.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 S(new Error,e,t);const n=e[d]=document.createTextNode("");e.replaceWith(n);const l=N(e,r[2],":each"),s=e.getAttribute(":key"),a=s?N(null,s):null;e.removeAttribute(":key");const i=e.getAttribute(":ref"),o=new y,c=new y;let f=[];return s=>{let p=l(s);p?"number"==typeof p?p=Array.from({length:p},((e,t)=>[t,t+1])):Array.isArray(p)?p=p.map(((e,t)=>[t+1,e])):"object"==typeof p?p=Object.entries(p):S(Error("Bad list value"),e,t,":each"):p=[];let y=[],h=[];for(let[t,n]of p){let l,f,p=a?.({[r[0]]:n,[r[1]]:t});null==p?l=e.cloneNode(!0):(l=c.get(p))||c.set(p,l=e.cloneNode(!0)),y.push(l),null!=p&&(f=o.get(p))?f[r[0]]=n:(f=u({[r[0]]:n,[i||""]:null,[r[1]]:t},s),null!=p&&o.set(p,f)),h.push(f)}!function(e,t,r,n){const l=new Map,s=new Map;let a,i;for(a=0;a<t.length;a++)l.set(t[a],a);for(a=0;a<r.length;a++)s.set(r[a],a);for(a=i=0;a!==t.length||i!==r.length;){var o=t[a],u=r[i];if(null===o)a++;else if(r.length<=i)e.removeChild(t[a]),a++;else if(t.length<=a)e.insertBefore(u,t[a]||n),i++;else if(o===u)a++,i++;else{var c=s.get(o),f=l.get(u);void 0===c?(e.removeChild(t[a]),a++):void 0===f?(e.insertBefore(u,t[a]||n),i++):(e.insertBefore(t[f],t[a]||n),t[f]=null,f>a+1&&a++,i++)}}}(n.parentNode,f,y,n),f=y;for(let e=0;e<y.length;e++)M(y[e],h[e])}},g.ref=(e,t,r)=>{r[t]=e},g.id=(e,t)=>{let r=N(e,t,":id");return t=>{return n=r(t),e.id=n||0===n?n:"";var n}},g.class=(e,t)=>{let r=N(e,t,":class"),n=e.className;return t=>{let l=r(t),s="string"==typeof l?l:(Array.isArray(l)?l:Object.entries(l).map((([e,t])=>t?e:""))).filter(Boolean).join(" ");e.className=[n,s].filter(Boolean).join(" ")}},g.style=(e,t)=>{let r=N(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=N(e,t,":text");return t=>{let n=r(t);e.textContent=null==n?"":n}},g.data=(e,t)=>{let r=N(e,t,":data");return t=>{let n=r(t);for(let t in n)e.dataset[t]=n[t]}},g.aria=(e,t)=>{let r=N(e,t,":aria");return t=>(t=>{for(let r in t)W(e,"aria-"+j(r),null==t[r]?null:t[r]+"")})(r(t))},g[""]=(e,t)=>{let r=N(e,t,":");if(r)return t=>{let n=r(t);for(let t in n)W(e,j(t),n[t])}},g.value=(e,t)=>{let r,n,l=N(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))},g.on=(e,t)=>{let r=N(e,t,":on");return t=>{let n=r(t),l=[];for(let t in n)l.push(b(e,t,n[t]));return()=>{for(let e of l)e()}}};var m=(e,t,r,n)=>{let l=n.startsWith("on")&&n.slice(2),s=N(e,t,":"+n);if(s)return l?t=>{let r=s(t)||(()=>{});return b(e,l,r)}:t=>W(e,n,s(t))},b=(e,t,r)=>{if(!r)return;let n=t.split("..").map((t=>{let r={evt:"",target:e,test:()=>!0};return r.evt=(t.startsWith("on")?t.slice(2):t).replace(/\.(\w+)?-?([-\w]+)?/g,((e,t,n="")=>(r.test=v[t]?.(r,...n.split("-"))||r.test,""))),r}));if(1==n.length)return a(r,n[0]);const l=(t,r=0)=>{let s;return s=a((a=>{r&&s();let i=t.call(e,a);"function"!=typeof i&&(i=()=>{}),r+1<n.length&&l(i,r?r+1:1)}),n[r])};let s=l(r);return()=>s();function a(e,{evt:t,target:r,test:n,defer:l,stop:s,prevent:a,...i}){l&&(e=l(e));let o=t=>n(t)&&(s&&t.stopPropagation(),a&&t.preventDefault(),e.call(r,t));return r.addEventListener(t,o,i),()=>r.removeEventListener(t,o,i)}},v={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},toggle(e){e.defer=(t,r)=>n=>r?(r.call?.(e.target,n),r=null):r=t()},throttle(e,t){e.defer=e=>k(e,t?Number(t)||0:108)},debounce(e,t){e.defer=e=>w(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=>A.ctrl(e)&&t.every((t=>A[t]?A[t](e):e.key===t)),shift:(e,...t)=>e=>A.shift(e)&&t.every((t=>A[t]?A[t](e):e.key===t)),alt:(e,...t)=>e=>A.alt(e)&&t.every((t=>A[t]?A[t](e):e.key===t)),meta:(e,...t)=>e=>A.meta(e)&&t.every((t=>A[t]?A[t](e):e.key===t)),arrow:e=>A.arrow,enter:e=>A.enter,escape:e=>A.escape,tab:e=>A.tab,space:e=>A.space,backspace:e=>A.backspace,delete:e=>A.delete,digit:e=>A.digit,letter:e=>A.letter,character:e=>A.character},A={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))},w=(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:"")},x={};function N(e,t,r){let n=x[t];if(!n){let l=/^[\n\s]*if.*\(.*\)/.test(t)||/\b(let|const)\s/.test(t)&&!r.startsWith(":on")?`(() => {${t}})()`:t;try{n=x[t]=new Function("__scope",`with (__scope) { return ${l.trim()} };`)}catch(n){return S(n,e,t,r)}}return l=>{let s;try{s=n.call(e,l)}catch(n){return S(n,e,t,r)}return s}}function S(e,t,r,n){Object.assign(e,{element:t,expression:r}),console.warn(`∴ ${e.message}\n\n${n}=${r?`"${r}"\n\n`:""}`,t),queueMicrotask((()=>{throw e}),0)}function j(e){return e.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(e=>"-"+e.toLowerCase()))}M.globals=i;var E=new WeakMap;function M(e,t){if(!e.children)return;if(E.has(e))return Object.assign(E.get(e),t);const r=u(t||{}),n=[],l=(e,t=e.parentNode)=>{for(let l in h){let s=":"+l;if(e.hasAttribute?.(s)){let a=e.getAttribute(s);if(e.removeAttribute(s),n.push(h[l](e,a,r,l)),E.has(e)||e.parentNode!==t)return!1}}if(e.attributes)for(let l=0;l<e.attributes.length;){let s=e.attributes[l];if(":"!==s.name[0]){l++;continue}e.removeAttribute(s.name);let a=s.value,i=s.name.slice(1).split(":");for(let l of i){let s=g[l]||m;if(n.push(s(e,a,r,l)),E.has(e)||e.parentNode!==t)return!1}}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;c((()=>{"function"==typeof t&&t(),t=e(r)}))}return E.set(e,r),r}var $=M;export{$ as default};
package/src/core.js CHANGED
@@ -1,4 +1,4 @@
1
- import { state as createState, fx, batch, sandbox } from './state.js';
1
+ import { state as createState, fx, sandbox } from './state.js';
2
2
  import defaultDirective, { primary, secondary } from './directives.js';
3
3
 
4
4
  sprae.globals=sandbox
@@ -7,11 +7,7 @@ sprae.globals=sandbox
7
7
  const memo = new WeakMap
8
8
  export default function sprae(container, values) {
9
9
  if (!container.children) return
10
- if (memo.has(container)) {
11
- let state = memo.get(container)
12
- batch(() => Object.assign(state, values))
13
- return state
14
- }
10
+ if (memo.has(container)) return Object.assign( memo.get(container), values)
15
11
 
16
12
  // subscribe to reactives
17
13
  // for (let prop in values) {
package/src/state.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // currentFx stack of listeners
2
- let currentFx, currentBatch
2
+ let currentFx, batch = new Set, pendingUpdate
3
3
 
4
4
  const targetFxs = new WeakMap
5
5
  const targetProxy = new WeakMap
@@ -60,7 +60,8 @@ const handler = {
60
60
  // whenever target prop is set, call all dependent fxs
61
61
  let propFxs = targetFxs.get(target)?.[prop]
62
62
 
63
- if (propFxs) for (let fx of propFxs) currentBatch ? currentBatch.add(fx) : fx.call()
63
+ if (propFxs) for (let fx of propFxs) batch.add(fx)
64
+ planUpdate()
64
65
 
65
66
  // FIXME: unsubscribe / delete effects by setting null/undefined
66
67
  // if (value == null) targetFxs.delete(prev)
@@ -103,11 +104,13 @@ export const fx = (fn) => {
103
104
  return call
104
105
  }
105
106
 
106
- export const batch = fn => {
107
- let prevBatch = currentBatch
108
- currentBatch = new Set
109
- fn()
110
- for (let fx of currentBatch) fx.call()
111
- currentBatch.clear()
112
- currentBatch = prevBatch
113
- }
107
+ export const planUpdate = () => {
108
+ if (!pendingUpdate) {
109
+ pendingUpdate = true
110
+ queueMicrotask(() => {
111
+ for (let fx of batch) fx.call()
112
+ batch.clear()
113
+ pendingUpdate = false
114
+ })
115
+ }
116
+ }
package/test/dom.js CHANGED
@@ -25,6 +25,7 @@ test('hidden: core', async () => {
25
25
  let params = sprae(el, {hidden:true})
26
26
  is(el.outerHTML, `<div hidden=""></div>`)
27
27
  params.hidden = false
28
+ await tick()
28
29
  is(el.outerHTML, `<div></div>`)
29
30
  })
30
31
 
@@ -42,6 +43,7 @@ test('common: reactive', async () => {
42
43
  let params = sprae(el, {name:'text', url:'//google.com'})
43
44
  is(el.outerHTML, `<label for="text">text</label><input id="text" name="text" type="text"><a href="//google.com"></a><img src="//google.com">`)
44
45
  params.name = 'email'
46
+ await tick()
45
47
  is(el.outerHTML, `<label for="email">email</label><input id="email" name="email" type="email"><a href="//google.com"></a><img src="//google.com">`)
46
48
  })
47
49
 
@@ -76,17 +78,22 @@ test('style', async () => {
76
78
  let el = h`<x style="left: 1px" :style="style"></x>`
77
79
  let params = sprae(el, {style: "top: 1px"})
78
80
  is(el.outerHTML, `<x style="left: 1px; top: 1px"></x>`)
81
+
79
82
  params.style = {top: '2px'}
83
+ await tick()
80
84
  is(el.outerHTML, `<x style="left: 1px; top: 2px;"></x>`)
81
85
 
82
86
  params.style = {'--x': 123}
87
+ await tick()
83
88
  is(el.style.getPropertyValue('--x'), '123')
84
89
 
85
90
  params.style = {top:'1px', bottom:'2px'}
91
+ await tick()
86
92
  is(el.outerHTML, `<x style="left: 1px; top: 1px; bottom: 2px;"></x>`)
87
93
 
88
94
  params.style = {top:'2px', bottom: null}
89
95
  // FIXME
96
+ await tick()
90
97
  is(el.outerHTML, `<x style="left: 1px; top: 2px;"></x>`)
91
98
  })
92
99
 
@@ -96,6 +103,7 @@ test('class', async () => {
96
103
  let params = sprae(el, {a:'x', b:'y', c, d:false});
97
104
  is(el.outerHTML, `<x class="base x"></x><y class="y z"></y><z class="b"></z>`);
98
105
  params.d = true;
106
+ await tick()
99
107
  is(el.outerHTML, `<x class="base x"></x><y class="y z"></y><z class="b c"></z>`);
100
108
  // c.value = 'w'
101
109
  // is(el.outerHTML, `<x class="base x"></x><y class="y w"></y><z class="b c"></z>`);
@@ -126,6 +134,7 @@ test('props: calculation', async () => {
126
134
  let state = sprae(el, {x:3});
127
135
  is(el.outerHTML, `<x x="012"></x>`)
128
136
  state.x = 4
137
+ await tick()
129
138
  is(el.outerHTML, `<x x="0123"></x>`)
130
139
  })
131
140
 
@@ -156,6 +165,7 @@ test('value: direct', async () => {
156
165
  is(el.value, '1')
157
166
  is(el.outerHTML, `<input value="1">`)
158
167
  state.a = 2
168
+ await tick()
159
169
  is(el.value, '2')
160
170
  is(el.outerHTML, `<input value="2">`)
161
171
 
@@ -182,6 +192,7 @@ test('text: core', async () => {
182
192
  let params = sprae(el, {text:'abc'})
183
193
  is(el.outerHTML, `<div>abc</div>`)
184
194
  params.text = null
195
+ await tick()
185
196
  is(el.outerHTML, `<div></div>`)
186
197
  })
187
198
 
@@ -196,10 +207,13 @@ test('if: base', async () => {
196
207
 
197
208
  is(el.innerHTML, '<span>a</span>')
198
209
  params.a = 2
210
+ await tick()
199
211
  is(el.innerHTML, '<span>b</span>')
200
212
  params.a = 3
213
+ await tick()
201
214
  is(el.innerHTML, '<span>c</span>')
202
215
  params.a = null
216
+ await tick()
203
217
  is(el.innerHTML, '<span>c</span>')
204
218
  })
205
219
 
@@ -214,15 +228,20 @@ test('if: short with insertions', async () => {
214
228
 
215
229
  is(el.innerHTML, '<span>1:1</span>')
216
230
  params.a = 2
231
+ await tick()
217
232
  is(el.innerHTML, '<span>2:2</span>')
218
233
  params.a = 3
234
+ await tick()
219
235
  is(el.innerHTML, '<span>3</span>')
220
236
  params.a = 4
237
+ await tick()
221
238
  is(el.innerHTML, '<span>4</span>')
222
239
 
223
240
  params.a = 1
241
+ await tick()
224
242
  is(el.innerHTML, '<span>1:1</span>')
225
243
  params.a = 4
244
+ await tick()
226
245
  is(el.innerHTML, '<span>4</span>')
227
246
 
228
247
  params.a = null
@@ -258,11 +277,12 @@ test('if: (#3) subsequent content is not abandoned', async () => {
258
277
  is(x.outerHTML, `<x><z>123</z></x>`)
259
278
  })
260
279
 
261
- test('if: + :with doesnt prevent secondary effects from happening', () => {
280
+ test('if: + :with doesnt prevent secondary effects from happening', async () => {
262
281
  let el = h`<div><x :if="x" :with="{}" :text="x"></x></div>`
263
282
  let state = sprae(el, {x:''})
264
283
  is(el.innerHTML, ``)
265
284
  state.x = '123'
285
+ await tick()
266
286
  is(el.innerHTML, `<x>123</x>`)
267
287
 
268
288
  // NOTE: we ignore this case
@@ -284,10 +304,13 @@ test('each: array', async () => {
284
304
  is(el.innerHTML, '')
285
305
  console.log('set 1,2')
286
306
  params.b = [1,2]
307
+ await tick()
287
308
  is(el.innerHTML, '<span>1</span><span>2</span>')
288
309
  params.b = []
310
+ await tick()
289
311
  is(el.innerHTML, '')
290
312
  params.b = null
313
+ await tick()
291
314
  is(el.innerHTML, '')
292
315
  })
293
316
 
@@ -302,10 +325,13 @@ test('each: object', async () => {
302
325
  is(el.innerHTML, '')
303
326
  console.log('set 1,2')
304
327
  params.b = { x:1, y:2 }
328
+ await tick()
305
329
  is(el.innerHTML, '<span>x,1</span><span>y,2</span>')
306
330
  params.b = []
331
+ await tick()
307
332
  is(el.innerHTML, '')
308
333
  params.b = null
334
+ await tick()
309
335
  is(el.innerHTML, '')
310
336
  })
311
337
 
@@ -318,12 +344,15 @@ test('each: loop within loop', async () => {
318
344
 
319
345
  is(el.innerHTML, '<x><y>1</y><y>2</y></x><x><y>3</y><y>4</y></x>')
320
346
  params.c = [[5,6], [3,4]]
347
+ await tick()
321
348
  is(el.innerHTML, '<x><y>5</y><y>6</y></x><x><y>3</y><y>4</y></x>')
322
349
  // params.c[1] = [7,8]
323
350
  params.c = [params.c[0], [7,8]]
351
+ await tick()
324
352
  is(el.innerHTML, '<x><y>5</y><y>6</y></x><x><y>7</y><y>8</y></x>')
325
353
  // is(el.innerHTML, '<span>1</span><span>2</span>')
326
354
  params.c = []
355
+ await tick()
327
356
  is(el.innerHTML, '')
328
357
  // params.b = null
329
358
  // is(el.innerHTML, '')
@@ -358,10 +387,13 @@ test('each: loop with condition', async () => {
358
387
 
359
388
  is(el.innerHTML, '')
360
389
  params.c = true
390
+ await tick()
361
391
  is(el.innerHTML, '<span>1</span><span>2</span>')
362
392
  params.b = [1]
393
+ await tick()
363
394
  is(el.innerHTML, '<span>1</span>')
364
395
  params.b = null
396
+ await tick()
365
397
  is(el.innerHTML, '')
366
398
  })
367
399
 
@@ -375,13 +407,17 @@ test('each: condition with loop', async () => {
375
407
 
376
408
  is(el.innerHTML, '<span>false</span>')
377
409
  params.c = true
410
+ await tick()
378
411
  is(el.innerHTML, '<span>1</span><span>2</span>')
379
412
  params.b = [1]
413
+ await tick()
380
414
  is(el.innerHTML, '<span>1</span>')
381
415
  params.b = null
416
+ await tick()
382
417
  is(el.innerHTML, '')
383
418
  console.log('c=false')
384
419
  params.c = false
420
+ await tick()
385
421
  is(el.innerHTML, '<span>false</span>')
386
422
  })
387
423
 
@@ -395,8 +431,10 @@ test('each: loop within condition', async () => {
395
431
 
396
432
  is(el.innerHTML, '<x><y>1</y></x>')
397
433
  params.a = 2
434
+ await tick()
398
435
  is(el.innerHTML, '<x><y>-1</y><y>-2</y></x>')
399
436
  params.a = 0
437
+ await tick()
400
438
  is(el.innerHTML, '')
401
439
  })
402
440
 
@@ -413,8 +451,10 @@ test('each: condition within loop', async () => {
413
451
 
414
452
  is(el.innerHTML, '<x><y>1:1</y></x><x><y>2:2</y></x><x><y>3</y></x>')
415
453
  params.b = [2]
454
+ await tick()
416
455
  is(el.innerHTML, '<x><y>2:2</y></x>')
417
456
  params.b = null
457
+ await tick()
418
458
  is(el.innerHTML, '')
419
459
  })
420
460
 
@@ -433,9 +473,11 @@ test('each: unkeyed', async () => {
433
473
  is(el.textContent, '123')
434
474
  // let first = el.firstChild
435
475
  state.xs = [1,3,2]
476
+ await tick()
436
477
  // is(el.firstChild, first)
437
478
  is(el.textContent, '132')
438
479
  state.xs = [3,3,3]
480
+ await tick()
439
481
  is(el.textContent, '333')
440
482
  // is(el.firstChild, first)
441
483
  })
@@ -448,9 +490,11 @@ test('each: keyed', async () => {
448
490
  is(el.textContent, '123')
449
491
  let first = el.firstChild
450
492
  state.xs = [1,3,2]
493
+ await tick()
451
494
  is(el.firstChild, first)
452
495
  is(el.textContent, '132')
453
496
  state.xs = [3,3,3]
497
+ await tick()
454
498
  is(el.textContent, '3')
455
499
  // is(el.firstChild, first)
456
500
  })
@@ -465,18 +509,21 @@ test('each: unmounted elements remove listeners', async () => {
465
509
  // let's hope they get removed without memory leaks :')
466
510
  })
467
511
 
468
- test('each: internal children get updated by state update, also: update by running again', () => {
512
+ test('each: internal children get updated by state update, also: update by running again', async () => {
469
513
  let el = h`<><x :each="item, idx in items" :text="item" :key="idx"></x></>`
470
514
  let state = sprae(el, { items: [1,2,3] })
471
515
  is(el.textContent, '123')
472
516
  state.items = [2, 2, 3]
517
+ await tick()
473
518
  is(el.textContent, '223')
474
519
  state = sprae(el, { items: [0,2,3] })
520
+ await tick()
475
521
  is(el.textContent, '023')
476
522
  // NOTE: this doesn't update items, since they're new array
477
523
  console.log('set items')
478
524
  state.items[0] = 1
479
- state.items = [...state.items]
525
+ // state.items = [...state.items]
526
+ await tick()
480
527
  is(el.textContent, '123')
481
528
  })
482
529
 
@@ -499,7 +546,7 @@ test('each: key-based caching is in-sync with direct elements', () => {
499
546
  is(el.outerHTML, el2.outerHTML)
500
547
  })
501
548
 
502
- test('on: base', () => {
549
+ test('on: base', async () => {
503
550
  let el = h`<div :on="{click(e){log.push('click')}, x}"></div>`
504
551
  let log = []
505
552
  let params = sprae(el, {x(){log.push('x')}, log})
@@ -511,11 +558,13 @@ test('on: base', () => {
511
558
  is(log, ['click','x'])
512
559
 
513
560
  params.x = function(){log.push('xx')}
561
+ await tick()
514
562
  el.dispatchEvent(new window.Event('x'));
515
563
  is(log, ['click','x','xx']);
516
564
 
517
565
  console.log('make null')
518
566
  params.x = null;
567
+ await tick()
519
568
  el.dispatchEvent(new window.Event('x'));
520
569
  is(log, ['click','x','xx']);
521
570
  })
@@ -632,7 +681,7 @@ test('on: parallel chains', e => {
632
681
  is(state.log, ['x','x','y','y','z','z','x']);
633
682
  })
634
683
 
635
- test('on: state changes between chain of events', e => {
684
+ test('on: state changes between chain of events', async e => {
636
685
  let el = h`<x :on="{'x..y':fn}"></x>`
637
686
  let log = []
638
687
  let state = sprae(el, {log, fn: () => (log.push('x1'), ()=>log.push('y1'))})
@@ -641,6 +690,7 @@ test('on: state changes between chain of events', e => {
641
690
  is(log, ['x1'])
642
691
  console.log('update fn')
643
692
  state.fn = () => (log.push('x2'), () => log.push('y2'))
693
+ await tick()
644
694
  is(log, ['x1'])
645
695
  // console.log('xx')
646
696
  // NOTE: state update registers new chain listener before finishing prev chain
@@ -808,12 +858,13 @@ test('on: modifiers chain', async e => {
808
858
  is(state.log,['x', 'x'])
809
859
  })
810
860
 
811
- test('with: inline', () => {
861
+ test('with: inline', async () => {
812
862
  let el = h`<x :with="{foo:'bar'}"><y :text="foo + baz"></y></x>`
813
863
  let state = sprae(el, {baz: 'qux'})
814
864
  // FIXME: this doesn't inherit root scope baz property and instead uses hard-initialized one
815
865
  is(el.innerHTML, `<y>barqux</y>`)
816
866
  state.baz = 'quux'
867
+ await tick()
817
868
  is(el.innerHTML, `<y>barquux</y>`)
818
869
  })
819
870
  test.skip('with: inline reactive', () => {
@@ -825,22 +876,24 @@ test.skip('with: inline reactive', () => {
825
876
  baz.value = 'quux'
826
877
  is(el.innerHTML, `<y>barquux</y>`)
827
878
  })
828
- test('with: data', () => {
879
+ test('with: data', async () => {
829
880
  let el = h`<x :with="x"><y :text="foo"></y></x>`
830
881
  let state = sprae(el, {x: {foo:'bar'}})
831
882
  is(el.innerHTML, `<y>bar</y>`)
832
883
  console.log('update', state.x)
833
884
  state.x.foo = 'baz'
885
+ await tick()
834
886
  // Object.assign(state, {x:{foo:'baz'}})
835
887
  is(el.innerHTML, `<y>baz</y>`)
836
888
  })
837
- test('with: transparency', () => {
889
+ test('with: transparency', async () => {
838
890
  // NOTE: y:text initializes through directive, not through parent
839
891
  // therefore by default :text uses parent's state, not defined by element itself
840
892
  let el = h`<x :with="{foo:'foo'}"><y :with="b" :text="foo+bar"></y></x>`
841
893
  let params = sprae(el, {b:{bar:'bar'}})
842
894
  is(el.innerHTML, `<y>foobar</y>`)
843
895
  params.b.bar = 'baz'
896
+ await tick()
844
897
  is(el.innerHTML, `<y>foobaz</y>`)
845
898
  })
846
899
  test.skip('with: reactive transparency', () => {
@@ -851,22 +904,25 @@ test.skip('with: reactive transparency', () => {
851
904
  bar.value = '3'
852
905
  is(el.innerHTML, `<y>13</y>`)
853
906
  })
854
- test('with: writes to state', () => {
907
+ test('with: writes to state', async () => {
855
908
  let a = h`<x :with="{a:1}"><y :on="{x(){a++}}" :text="a"></y></x>`
856
909
  sprae(a)
857
910
  is(a.innerHTML, `<y>1</y>`)
858
911
  a.firstChild.dispatchEvent(new window.Event('x'))
912
+ await tick()
859
913
  is(a.innerHTML, `<y>2</y>`)
860
914
  a.firstChild.dispatchEvent(new window.Event('x'))
915
+ await tick()
861
916
  is(a.innerHTML, `<y>3</y>`)
862
917
  })
863
918
 
864
- test('ref: base', () => {
919
+ test('ref: base', async () => {
865
920
  let a = h`<a :ref="a" :init="log.push(a), null" :text="b"></a>`
866
921
  let state = sprae(a, {log:[], b:1})
867
922
  is(state.log[0], a)
868
923
  is(a.outerHTML, `<a>1</a>`)
869
924
  state.b = 2
925
+ await tick()
870
926
  is(a.outerHTML, `<a>2</a>`)
871
927
  is(state.a, a, 'Exposes to the state');
872
928
  })
package/test/state.js CHANGED
@@ -1,9 +1,10 @@
1
- import {state, fx, batch} from '../src/state.js'
1
+ import {state, fx} from '../src/state.js'
2
2
  import t, {is, ok} from 'tst'
3
3
  import signalStruct from 'signal-struct'
4
4
  import { effect } from '@preact/signals-core'
5
+ import {tick} from 'wait-please'
5
6
 
6
- t('state: basic', t => {
7
+ t('state: basic', async t => {
7
8
  let s = state({x:0, y:1})
8
9
 
9
10
  let xy; fx(() => xy = s.x + s.y)
@@ -12,12 +13,14 @@ t('state: basic', t => {
12
13
  s.x = 2
13
14
  console.log('set 3')
14
15
  s.y = 3
16
+ await tick()
15
17
  is(xy, 5, 'Eq')
16
18
  s.y = 4
19
+ await tick()
17
20
  is(xy, 6, 'Eq')
18
21
  })
19
22
 
20
- t('state: signal-struct basics', t => {
23
+ t('state: signal-struct basics', async t => {
21
24
  let s = state({
22
25
  x: 0,
23
26
  y: 1,
@@ -39,8 +42,10 @@ t('state: signal-struct basics', t => {
39
42
  is(xy, 1)
40
43
  s.x = 2
41
44
  s.y = 3
45
+ await tick()
42
46
  is(xy, 5)
43
47
  s.y = 4
48
+ await tick()
44
49
  is(xy, 6)
45
50
 
46
51
  // getters are computed
@@ -51,7 +56,7 @@ t('state: signal-struct basics', t => {
51
56
  is(s.xy, 6)
52
57
  })
53
58
 
54
- t('state: deep props', () => {
59
+ t('state: deep props', async () => {
55
60
  let s = state({
56
61
  z: { r: 2, i: 3 }
57
62
  })
@@ -60,17 +65,20 @@ t('state: deep props', () => {
60
65
  let len; fx(() => (len = (s.z.r**2 + s.z.i**2)**0.5))
61
66
  s.z.r = 3
62
67
  s.z.i = 4
68
+ await tick()
63
69
  is(len, 5)
64
70
  s.z.r = 4
65
71
  s.z.i = 3
72
+ await tick()
66
73
  is(len, 5)
67
74
 
68
75
  // updating internal objects/arrays turns them into signals too
69
76
  s.z = { r: 5, i: 12}
77
+ await tick()
70
78
  is(len, 13)
71
79
  })
72
80
 
73
- t('state: array', () => {
81
+ t('state: array', async () => {
74
82
  let s = state({
75
83
  w: [1,2]
76
84
  })
@@ -79,23 +87,27 @@ t('state: array', () => {
79
87
  let mult; fx(() => mult = s.w?.[0] * s.w?.[1] || 0)
80
88
  is(mult, 2)
81
89
  s.w = [3,4]
90
+ await tick()
82
91
  is(mult,12)
83
92
 
84
93
  // nullifying is fine
85
94
  s.w = null
95
+ await tick()
86
96
  is(mult, 0)
87
97
 
88
98
  // delete is fine
89
99
  delete s.w
90
100
 
101
+ await tick()
91
102
  is(mult, 0)
92
103
 
93
104
  console.log('set w')
94
105
  s.w = [1,2]
106
+ await tick()
95
107
  is(mult, 2)
96
108
  })
97
109
 
98
- t('state: bulk-update', () => {
110
+ t('state: bulk-update', async () => {
99
111
  let s = state({
100
112
  x: 0,
101
113
  y: 1,
@@ -114,6 +126,7 @@ t('state: bulk-update', () => {
114
126
  // let [signals, update] = s
115
127
  // update({ x: 1, y: 1, z: { r: 3, i: 4 } })
116
128
  Object.assign(s, { x: 1, y: 1, z: { r: 3, i: 4 } })
129
+ await tick()
117
130
  is(xy, 2)
118
131
  is(len, 5, 'len after update')
119
132
  })
@@ -178,31 +191,37 @@ t('state: inheritance', () => {
178
191
  // })
179
192
  })
180
193
 
181
- t('state: inheritance: updating values in chain', () => {
194
+ t('state: inheritance: updating values in chain', async () => {
182
195
  let s1 = {x:1}
183
196
  let s = state(s1, {y:2})
184
- console.group('fx')
197
+ // console.group('fx')
185
198
  let xy = 0; fx(() => xy = s.x + s.y);
186
- console.groupEnd('fx')
199
+ // console.groupEnd('fx')
200
+ await tick()
187
201
  is(xy, 3)
188
202
  s.x++
203
+ await tick()
189
204
  is(xy, 4)
190
205
  console.log('y++')
191
206
  s.y++
207
+ await tick()
192
208
  is(xy, 5)
193
209
  })
194
210
 
195
- t('state: array items', () => {
211
+ t('state: array items', async () => {
196
212
  // arrays get each item converted to signal struct
197
213
  let s5 = state({list: [{x:1}, {x:2}]})
198
214
  let sum; fx(()=> sum = s5.list.reduce((sum, item)=>item.x + sum, 0))
199
215
  is(sum, 3)
200
216
  s5.list[0].x = 2
217
+ await tick()
201
218
  is(sum, 4)
202
219
  console.log('set array value')
203
220
  s5.list = [{x:3}, {x:3}]
221
+ await tick()
204
222
  is(sum, 6)
205
223
  s5.list = [{x:3}, {x:3}, {x:4}]
224
+ await tick()
206
225
  is(sum, 10)
207
226
  })
208
227
 
@@ -214,14 +233,16 @@ t('state: arrays retain reference', () => {
214
233
  is(list,[1,4,3])
215
234
  })
216
235
 
217
- t('state: direct list', () => {
236
+ t('state: direct list', async () => {
218
237
  // works with arrays as well
219
238
  let list = state([{x:1}, {x:2}])
220
239
  let sum; fx(()=> sum = list.reduce((sum, item)=>item.x + sum, 0))
221
240
  is(sum, 3)
222
241
  list[0].x = 2
242
+ await tick()
223
243
  is(sum, 4)
224
244
  list.splice(0, 2, {x:3}, {x:3})
245
+ await tick()
225
246
  console.log(list)
226
247
  is(sum, 6)
227
248
  })
@@ -240,7 +261,7 @@ t('state: circular?', () => {
240
261
  is (a, [1, 2])
241
262
  })
242
263
 
243
- t('state: batch', () => {
264
+ t.skip('state: batch', () => {
244
265
  let s = state({x:1, y:2})
245
266
  let log = []; fx(() => log.push(s.x + s.y))
246
267
  is(log, [3])