bruh 1.6.4 → 1.8.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.
@@ -0,0 +1,2 @@
1
+ var e,t,r,a,n,o,s,i,d,c,l,h,u,p,f=Object.defineProperty,b=Object.defineProperties,m=Object.getOwnPropertyDescriptors,v=Object.getOwnPropertySymbols,y=Object.prototype.hasOwnProperty,M=Object.prototype.propertyIsEnumerable,g=(e,t,r)=>t in e?f(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,w=(e,t)=>{for(var r in t||(t={}))y.call(t,r)&&g(e,r,t[r]);if(v)for(var r of v(t))M.call(t,r)&&g(e,r,t[r]);return e},k=(e,t,r)=>(g(e,"symbol"!=typeof t?t+"":t,r),r),S=(e,t,r)=>{if(!t.has(e))throw TypeError("Cannot "+r)},O=(e,t,r)=>(S(e,t,"read from private field"),r?r.call(e):t.get(e)),x=(e,t,r)=>{if(t.has(e))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(e):t.set(e,r)},T=(e,t,r,a)=>(S(e,t,"write to private field"),a?a.call(e,r):t.set(e,r),r),W=(e,t,r)=>(S(e,t,"access private method"),r);class _{constructor(){this.startMarker=document.createTextNode(""),this.endMarker=document.createTextNode("")}static from(e,t){const r=new this;return e.before(r.startMarker),t.after(r.endMarker),r}before(...e){this.startMarker.before(...e)}prepend(...e){this.startMarker.after(...e)}append(...e){this.endMarker.before(...e)}after(...e){this.endMarker.after(...e)}remove(){const e=document.createRange();e.setStartBefore(this.startMarker),e.setEndAfter(this.endMarker),e.deleteContents()}replaceChildren(...e){const t=document.createRange();t.setStartAfter(this.startMarker),t.setEndBefore(this.endMarker),t.deleteContents(),this.startMarker.after(...e)}replaceWith(...e){this.endMarker.after(...e),this.remove()}get childNodes(){const e=[];for(let t=this.startMarker.nextSibling;t!=this.endMarker&&t;t=t.nextSibling)e.push(t);return e}get children(){return this.childNodes.filter((e=>e instanceof HTMLElement))}}var j=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",LiveFragment:_});const A=Symbol.for("bruh reactive");e=A,t=new WeakMap,r=new WeakMap;const N=class{constructor(e,t){x(this,u),k(this,a,!0),x(this,n,void 0),x(this,o,new Set),x(this,s,void 0),x(this,i,0),x(this,d,new Set),t?(T(this,n,t()),T(this,s,t),T(this,i,Math.max(...e.map((e=>O(e,i))))+1),e.forEach((e=>O(e,d).add(this)))):T(this,n,e)}get value(){return O(N,c).size&&N.applyUpdates(),O(this,n)}set value(e){0===O(this,i)&&(O(N,c).size||queueMicrotask(N.applyUpdates),O(N,c).set(this,e))}addReaction(e){return O(this,o).add(e),()=>O(this,o).delete(e)}static applyUpdates(){var e,t,r;if(O(N,c).size){for(const[t,r]of O(N,c).entries())W(e=t,u,p).call(e,r);O(N,c).clear();for(const e of O(N,l))if(e)for(const a of e)W(r=a,u,p).call(r,O(t=a,s).call(t));O(N,l).length=0;for(const e of O(N,h))e();O(N,h).length=0}}};let E=N;a=A,n=new WeakMap,o=new WeakMap,s=new WeakMap,i=new WeakMap,d=new WeakMap,c=new WeakMap,l=new WeakMap,h=new WeakMap,u=new WeakSet,p=function(e){if(e===O(this,n))return;T(this,n,e),O(N,h).push(...O(this,o));const t=O(N,l);for(const r of O(this,d)){const e=O(r,i);t[e]||(t[e]=new Set),t[e].add(r)}},x(E,c,new Map),x(E,l,[]),x(E,h,[]);const C=(e,t)=>{if(null==e?void 0:e[A])return t(e.value),e.addReaction((()=>t(e.value)));t(e)};var R=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",SimpleReactive:class{constructor(a){k(this,e,!0),x(this,t,void 0),x(this,r,new Set),T(this,t,a)}get value(){return O(this,t)}set value(e){if(e!==O(this,t)){T(this,t,e);for(const e of O(this,r))e()}}addReaction(e){return O(this,r).add(e),()=>O(this,r).delete(e)}},FunctionalReactive:E,r:(e,t)=>new E(e,t),reactiveDo:C});const P=(e,t)=>r=>{Array.isArray(r)?r.length?e(r[0]):t():e(r)};var z=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",pipe:(e,...t)=>t.reduce(((e,t)=>t(e)),e),dispatch:(e,t,r)=>e.dispatchEvent(new CustomEvent(t,w({bubbles:!0,cancelable:!0,composed:!0},r))),createDestructable:(e,t)=>{const r=(a=w({},e),n={[Symbol.iterator]:()=>t[Symbol.iterator]()},b(a,m(n)));var a,n;return Object.defineProperty(r,Symbol.iterator,{enumerable:!1}),r},maybeDo:P,functionAsObject:e=>new Proxy({},{get:(t,r)=>e(r)})});const D=Symbol.for("bruh reactive"),B=Symbol.for("bruh meta node"),F=Symbol.for("bruh meta text node"),U=Symbol.for("bruh meta element"),L=e=>e[B]?e.node:e instanceof Node?e:document.createTextNode(e),q=e=>e.flat(1/0).flatMap((e=>{if(!e[D])return[L(e)];if(Array.isArray(e.value)){const t=new _;return e.addReaction((()=>{t.replaceChildren(...q(e.value))})),[t.startMarker,...q(e.value),t.endMarker]}let t=L(e.value);return e.addReaction((()=>{const r=t;t=L(e.value),r.replaceWith(t)})),[t]}));class H{constructor(e){this[B]=this[F]=!0,e[D]?(this.node=document.createTextNode(e.value),e.addReaction((()=>{this.node.textContent=e.value}))):this.node=document.createTextNode(e)}addProperties(e={}){return Object.assign(this.node,e),this}}class I{constructor(e,t){this[B]=this[U]=!0,this.node=t?document.createElementNS(t,e):document.createElement(e)}static from(e){const t=new this("div");return t.node=e,t}addProperties(e={}){return Object.assign(this.node,e),this}addAttributes(e={}){for(const t in e)C(e[t],P((e=>this.node.setAttribute(t,e)),(()=>this.node.removeAttribute(t))));return this}addDataAttributes(e={}){for(const t in e)C(e[t],P((e=>this.node.dataset[t]=e),(()=>delete this.node.dataset[t])));return this}before(...e){this.node.before(...q(e))}prepend(...e){this.node.prepend(...q(e))}append(...e){this.node.append(...q(e))}after(...e){this.node.after(...q(e))}replaceChildren(...e){this.node.replaceChildren(...q(e))}replaceWith(...e){this.node.replaceWith(...q(e))}}var J=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",childrenToNodes:q,MetaTextNode:H,MetaElement:I,hydrateTextNodes:()=>{const e={},t=document.getElementsByTagName("bruh-textnode");for(const r of t){const t=document.createTextNode(r.textContent);r.dataset.tag&&(e[r.dataset.tag]=t),r.replaceWith(t)}return e},t:e=>new H(e),e:(e,t)=>(...r)=>{const a=new I(e,t);if((null==(n=r[0])?void 0:n[B])||(null==n?void 0:n[D])||n instanceof Node||Array.isArray(n)||"function"!=typeof n)a.append(r);else{const[e,...t]=r;a.addAttributes(e),a.append(t)}var n;return a},h:(e,t,...r)=>{if("string"==typeof e){const a=new I(e);return a.addAttributes(t||{}),a.append(r),a}return e(Object.assign({},t,{children:r}))},JSXFragment:({children:e})=>e});export{J as dom,j as liveFragment,R as reactive,z as util};
2
+ //# sourceMappingURL=bruh.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bruh.es.js","sources":["../src/dom/live-fragment.mjs","../src/reactive/index.mjs","../src/util/index.mjs","../src/dom/index.browser.mjs"],"sourcesContent":["// Lightweight and performant DOM fragment that keeps its place,\n// useful for grouping siblings without making a parent element.\n// Not a true analog of the DocumentFragment, because the implementation\n// would be infeasible with that scope, adding a performance penalty as well.\n// Works as long as the start & end placeholders are siblings in that order\n// and they do not overlap other LiveFragment's:\n// Works: (start A)(start B)(end B)(end A)\n// Fails: (start A)(start B)(end A)(end B)\n// Also, make sure not to call .normalize() on the parent element,\n// because that would ruin the placeholders.\nexport class LiveFragment {\n constructor() {\n this.startMarker = document.createTextNode(\"\")\n this.endMarker = document.createTextNode(\"\")\n }\n\n static from(firstNode, lastNode) {\n const liveFragment = new this()\n firstNode.before(liveFragment.startMarker)\n lastNode.after(liveFragment.endMarker)\n return liveFragment\n }\n\n before(...xs) {\n this.startMarker.before(...xs)\n }\n\n prepend(...xs) {\n this.startMarker.after(...xs)\n }\n\n append(...xs) {\n this.endMarker.before(...xs)\n }\n\n after(...xs) {\n this.endMarker.after(...xs)\n }\n\n remove() {\n const range = document.createRange()\n range.setStartBefore(this.startMarker)\n range.setEndAfter(this.endMarker)\n range.deleteContents()\n }\n\n replaceChildren(...xs) {\n const range = document.createRange()\n range.setStartAfter(this.startMarker)\n range.setEndBefore(this.endMarker)\n range.deleteContents()\n this.startMarker.after(...xs)\n }\n\n replaceWith(...xs) {\n this.endMarker.after(...xs)\n this.remove()\n }\n\n get childNodes() {\n const childNodes = []\n\n for (\n let currentNode = this.startMarker.nextSibling;\n currentNode != this.endMarker && currentNode;\n currentNode = currentNode.nextSibling\n )\n childNodes.push(currentNode)\n\n return childNodes\n }\n\n get children() {\n return this.childNodes\n .filter(node => node instanceof HTMLElement)\n }\n}\n","const isReactive = Symbol.for(\"bruh reactive\")\n\n// A super simple and performant reactive value implementation\nexport class SimpleReactive {\n [isReactive] = true\n\n #value\n #reactions = new Set()\n\n constructor(value) {\n this.#value = value\n }\n\n get value() {\n return this.#value\n }\n\n set value(newValue) {\n if (newValue === this.#value)\n return\n\n this.#value = newValue\n for (const reaction of this.#reactions)\n reaction()\n }\n\n addReaction(reaction) {\n this.#reactions.add(reaction)\n\n return () =>\n this.#reactions.delete(reaction)\n }\n}\n\n// A reactive implementation for building functional reactive graphs\n// Ensures state consistency, minimal node updates, and transparent update batching\nexport class FunctionalReactive {\n [isReactive] = true\n\n #value\n #reactions = new Set()\n\n // For derived nodes, f is the derivation function\n #f\n // Source nodes are 0 deep in the derivation graph\n // This is for topological sort\n #depth = 0\n // All nodes have a set of derivatives that update when the node changes\n #derivatives = new Set()\n\n // Keep track of all the pending changes from the value setter\n static #settersQueue = new Map()\n // A queue of derivatives to potentially update, sorted into sets by depth\n // This starts with depth 1 and can potentially have holes\n static #derivativesQueue = []\n // A queue of reactions to run after the graph is fully updated\n static #reactionsQueue = []\n\n constructor(x, f) {\n if (!f) {\n this.#value = x\n return\n }\n\n this.#value = f()\n this.#f = f\n this.#depth = Math.max(...x.map(d => d.#depth)) + 1\n\n x.forEach(d => d.#derivatives.add(this))\n }\n\n get value() {\n // If there are any pending updates, go ahead and apply them first\n // It's ok that there's already a microtask queued for this\n if (FunctionalReactive.#settersQueue.size)\n FunctionalReactive.applyUpdates()\n\n return this.#value\n }\n\n set value(newValue) {\n // Only allow souce nodes to be directly updated\n if (this.#depth !== 0)\n return\n\n // Unless asked for earlier, these updates are just queued up until the microtasks run\n if (!FunctionalReactive.#settersQueue.size)\n queueMicrotask(FunctionalReactive.applyUpdates)\n\n FunctionalReactive.#settersQueue.set(this, newValue)\n }\n\n addReaction(reaction) {\n this.#reactions.add(reaction)\n\n return () =>\n this.#reactions.delete(reaction)\n }\n\n // Apply an update for a node and queue its derivatives if it actually changed\n #applyUpdate(newValue) {\n if (newValue === this.#value)\n return\n\n this.#value = newValue\n FunctionalReactive.#reactionsQueue.push(...this.#reactions)\n\n const queue = FunctionalReactive.#derivativesQueue\n for (const derivative of this.#derivatives) {\n const depth = derivative.#depth\n if (!queue[depth])\n queue[depth] = new Set()\n\n queue[depth].add(derivative)\n }\n }\n\n // Apply pending updates from actually changed source nodes\n static applyUpdates() {\n if (!FunctionalReactive.#settersQueue.size)\n return\n\n // Bootstrap by applying the updates from the pending setters\n for (const [sourceNode, newValue] of FunctionalReactive.#settersQueue.entries())\n sourceNode.#applyUpdate(newValue)\n FunctionalReactive.#settersQueue.clear()\n\n // Iterate down the depths, ignoring holes\n // Note that both the queue (Array) and each depth Set iterators update as items are added\n for (const depthSet of FunctionalReactive.#derivativesQueue) if (depthSet)\n for (const derivative of depthSet)\n derivative.#applyUpdate(derivative.#f())\n FunctionalReactive.#derivativesQueue.length = 0\n\n // Call all reactions now that the graph has a fully consistent state\n for (const reaction of FunctionalReactive.#reactionsQueue)\n reaction()\n FunctionalReactive.#reactionsQueue.length = 0\n }\n}\n\n// A little convenience function\nexport const r = (x, f) => new FunctionalReactive(x, f)\n\n// Do something with a value, updating if it is reactive\nexport const reactiveDo = (x, f) => {\n if (x?.[isReactive]) {\n f(x.value)\n return x.addReaction(() => f(x.value))\n }\n\n f(x)\n}\n","// Create a pipeline with an initial value and a series of functions\nexport const pipe = (x, ...fs) =>\n fs.reduce((y, f) => f(y), x)\n\n// Dispatch a custom event to (capturing) and from (bubbling) a target (usually a DOM node)\n// Returns false if the event was cancelled (preventDefault()) and true otherwise\nexport const dispatch = (target, type, options) =>\n target.dispatchEvent(\n // Default to behave like most DOM events\n new CustomEvent(type, {\n bubbles: true,\n cancelable: true,\n composed: true,\n ...options\n })\n )\n\n// Inspired by https://antfu.me/posts/destructuring-with-object-or-array#take-away\n// Creates an object that is both destructable with {...} and [...]\n// Useful for writing library functions à la react-use & vueuse\nexport const createDestructable = (object, iterable) => {\n const destructable = {\n ...object,\n [Symbol.iterator]: () => iterable[Symbol.iterator]()\n }\n\n Object.defineProperty(destructable, Symbol.iterator, {\n enumerable: false\n })\n\n return destructable\n}\n\n// A function that acts like Maybe.from(x).ifExists(existsThen).ifEmpty(emptyThen)\n// Except we just use an array in place of a true Maybe type\n// This is useful for setting and removing reactive attributes\nexport const maybeDo = (existsThen, emptyThen) => x => {\n if (Array.isArray(x)) {\n if (x.length)\n existsThen(x[0])\n else\n emptyThen()\n }\n else\n existsThen(x)\n}\n\n// Creates an object (as a Proxy) that acts as a function\n// So functionAsObject(f).property is equivalent to f(\"property\")\n// This is can be useful when combined with destructuring syntax, e.g.:\n// const { html, head, title, body, main, h1, p } = functionAsObject(e)\nexport const functionAsObject = f =>\n new Proxy({}, {\n get: (_, property) => f(property)\n })\n","import { LiveFragment } from \"./live-fragment.mjs\"\nimport { reactiveDo } from \"../reactive/index.mjs\"\nimport { maybeDo } from \"../util/index.mjs\"\n\nconst isReactive = Symbol.for(\"bruh reactive\")\nconst isMetaNode = Symbol.for(\"bruh meta node\")\nconst isMetaTextNode = Symbol.for(\"bruh meta text node\")\nconst isMetaElement = Symbol.for(\"bruh meta element\")\n\n// A basic check for if a value is allowed as a meta node's child\n// It's responsible for quickly checking the type, not deep validation\nconst isMetaNodeChild = x =>\n // meta nodes, reactives, and DOM nodes\n x?.[isMetaNode] ||\n x?.[isReactive] ||\n x instanceof Node ||\n // Any array, just assume it contains valid children\n Array.isArray(x) ||\n // Everything else, as long as it isn't a function, can be a child when stringified\n typeof x !== \"function\"\n\nconst toNode = x => {\n if (x[isMetaNode])\n return x.node\n\n if (x instanceof Node)\n return x\n\n return document.createTextNode(x)\n}\n\nexport const childrenToNodes = children =>\n children\n .flat(Infinity)\n .flatMap(child => {\n if (!child[isReactive])\n return [toNode(child)]\n\n if (Array.isArray(child.value)) {\n const liveFragment = new LiveFragment()\n child.addReaction(() => {\n liveFragment.replaceChildren(...childrenToNodes(child.value))\n })\n return [liveFragment.startMarker, ...childrenToNodes(child.value), liveFragment.endMarker]\n }\n\n let node = toNode(child.value)\n child.addReaction(() => {\n const oldNode = node\n node = toNode(child.value)\n oldNode.replaceWith(node)\n })\n return [node]\n })\n\n\n\n// Meta Nodes\n\nexport class MetaTextNode {\n constructor(textContent) {\n this[isMetaNode] =\n this[isMetaTextNode] = true\n\n if (textContent[isReactive]) {\n this.node = document.createTextNode(textContent.value)\n textContent.addReaction(() => {\n this.node.textContent = textContent.value\n })\n }\n else {\n this.node = document.createTextNode(textContent)\n }\n }\n\n addProperties(properties = {}) {\n Object.assign(this.node, properties)\n\n return this\n }\n}\n\nexport class MetaElement {\n constructor(name, namespace) {\n this[isMetaNode] =\n this[isMetaElement] = true\n\n this.node =\n namespace\n ? document.createElementNS(namespace, name)\n : document.createElement ( name)\n }\n\n static from(element) {\n const result = new this(\"div\")\n result.node = element\n return result\n }\n\n addProperties(properties = {}) {\n Object.assign(this.node, properties)\n\n return this\n }\n\n addAttributes(attributes = {}) {\n for (const name in attributes)\n reactiveDo(attributes[name],\n maybeDo(\n value => this.node.setAttribute (name, value),\n () => this.node.removeAttribute(name)\n )\n )\n\n return this\n }\n\n addDataAttributes(dataAttributes = {}) {\n for (const name in dataAttributes)\n reactiveDo(dataAttributes[name],\n maybeDo(\n value => this.node.dataset[name] = value,\n () => delete this.node.dataset[name]\n )\n )\n\n return this\n }\n\n before(...xs) {\n this.node.before(...childrenToNodes(xs))\n }\n\n prepend(...xs) {\n this.node.prepend(...childrenToNodes(xs))\n }\n\n append(...xs) {\n this.node.append(...childrenToNodes(xs))\n }\n\n after(...xs) {\n this.node.after(...childrenToNodes(xs))\n }\n\n replaceChildren(...xs) {\n this.node.replaceChildren(...childrenToNodes(xs))\n }\n\n replaceWith(...xs) {\n this.node.replaceWith(...childrenToNodes(xs))\n }\n}\n\n\n\n// Convenience functions\n\nexport const hydrateTextNodes = () => {\n const tagged = {}\n const bruhTextNodes = document.getElementsByTagName(\"bruh-textnode\")\n\n for (const bruhTextNode of bruhTextNodes) {\n const textNode = document.createTextNode(bruhTextNode.textContent)\n\n if (bruhTextNode.dataset.tag)\n tagged[bruhTextNode.dataset.tag] = textNode\n\n bruhTextNode.replaceWith(textNode)\n }\n\n return tagged\n}\n\nconst createMetaTextNode = textContent =>\n new MetaTextNode(textContent)\n\nconst createMetaElement = (name, namespace) => (...variadic) => {\n const meta = new MetaElement(name, namespace)\n\n // Implement optional attributes as first argument\n if (!isMetaNodeChild(variadic[0])) {\n const [attributes, ...children] = variadic\n meta.addAttributes(attributes)\n meta.append(children)\n }\n else {\n meta.append(variadic)\n }\n\n return meta\n}\n\n// JSX integration\nconst createMetaElementJSX = (nameOrComponent, attributesOrProps, ...children) => {\n // If we are making a html element\n // This is likely when the jsx tag name begins with a lowercase character\n if (typeof nameOrComponent == \"string\") {\n const meta = new MetaElement(nameOrComponent)\n\n // These are attributes then, but they might be null/undefined\n meta.addAttributes(attributesOrProps || {})\n meta.append(children)\n\n return meta\n }\n\n // It must be a component, then\n // Bruh components are just functions that return meta elements\n // Due to JSX, this would mean a function with only one parameter - a \"props\" object\n // This object includes the all of the attributes and a \"children\" key\n return nameOrComponent( Object.assign({}, attributesOrProps, { children }) )\n}\n\n// These will be called with short names\nexport {\n createMetaTextNode as t,\n createMetaElement as e,\n createMetaElementJSX as h\n}\n\n// The JSX fragment is made into a bruh fragment (just an array)\nexport const JSXFragment = ({ children }) => children\n"],"names":["constructor","startMarker","document","createTextNode","endMarker","firstNode","lastNode","liveFragment2","this","before","after","xs","prepend","append","remove","range","createRange","setStartBefore","setEndAfter","deleteContents","replaceChildren","setStartAfter","setEndBefore","replaceWith","childNodes","currentNode","nextSibling","push","filter","node","HTMLElement","isReactive","Symbol","for","_a","_value","_reactions","x","f","Set","_value2","_f","_depth","Math","max","map","__privateGet","forEach","_derivatives","add","_settersQueue","size","applyUpdates","newValue","_FunctionalReactive","set","addReaction","reaction","_reactions2","delete","sourceNode","entries","_applyUpdate","call","clear","depthSet","_derivativesQueue","derivative","length","_reactionsQueue","_b","applyUpdate_fn","queue","depth","__privateAdd","FunctionalReactive","Map","reactiveDo","value","maybeDo","existsThen","emptyThen","Array","isArray","fs","reduce","y","target","type","options","dispatchEvent","CustomEvent","__spreadValues","bubbles","cancelable","composed","object","iterable","destructable","iterator","defineProperty","enumerable","Proxy","get","_","property","isMetaNode","isMetaTextNode","isMetaElement","toNode","Node","childrenToNodes","children","flat","Infinity","flatMap","child","LiveFragment","oldNode","textContent","addProperties","properties","assign","name","namespace","createElementNS","createElement","element","result","addAttributes","attributes","setAttribute","removeAttribute","addDataAttributes","dataAttributes","dataset","tagged","bruhTextNodes","getElementsByTagName","bruhTextNode","textNode","tag","MetaTextNode","variadic","meta","MetaElement","nameOrComponent","attributesOrProps","Object"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ var __defProp=Object.defineProperty,__defProps=Object.defineProperties,__getOwnPropDescs=Object.getOwnPropertyDescriptors,__getOwnPropSymbols=Object.getOwnPropertySymbols,__hasOwnProp=Object.prototype.hasOwnProperty,__propIsEnum=Object.prototype.propertyIsEnumerable,__defNormalProp=(e,t,r)=>t in e?__defProp(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,__spreadValues=(e,t)=>{for(var r in t||(t={}))__hasOwnProp.call(t,r)&&__defNormalProp(e,r,t[r]);if(__getOwnPropSymbols)for(var r of __getOwnPropSymbols(t))__propIsEnum.call(t,r)&&__defNormalProp(e,r,t[r]);return e},__spreadProps=(e,t)=>__defProps(e,__getOwnPropDescs(t)),__publicField=(e,t,r)=>(__defNormalProp(e,"symbol"!=typeof t?t+"":t,r),r),__accessCheck=(e,t,r)=>{if(!t.has(e))throw TypeError("Cannot "+r)},__privateGet=(e,t,r)=>(__accessCheck(e,t,"read from private field"),r?r.call(e):t.get(e)),__privateAdd=(e,t,r)=>{if(t.has(e))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(e):t.set(e,r)},__privateSet=(e,t,r,a)=>(__accessCheck(e,t,"write to private field"),a?a.call(e,r):t.set(e,r),r),__privateMethod=(e,t,r)=>(__accessCheck(e,t,"access private method"),r);!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).bruh={})}(this,(function(e){var t,r,a,o,i,n,s,d,_,p,c,l,h,u;class f{constructor(){this.startMarker=document.createTextNode(""),this.endMarker=document.createTextNode("")}static from(e,t){const r=new this;return e.before(r.startMarker),t.after(r.endMarker),r}before(...e){this.startMarker.before(...e)}prepend(...e){this.startMarker.after(...e)}append(...e){this.endMarker.before(...e)}after(...e){this.endMarker.after(...e)}remove(){const e=document.createRange();e.setStartBefore(this.startMarker),e.setEndAfter(this.endMarker),e.deleteContents()}replaceChildren(...e){const t=document.createRange();t.setStartAfter(this.startMarker),t.setEndBefore(this.endMarker),t.deleteContents(),this.startMarker.after(...e)}replaceWith(...e){this.endMarker.after(...e),this.remove()}get childNodes(){const e=[];for(let t=this.startMarker.nextSibling;t!=this.endMarker&&t;t=t.nextSibling)e.push(t);return e}get children(){return this.childNodes.filter((e=>e instanceof HTMLElement))}}var v=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",LiveFragment:f});const b=Symbol.for("bruh reactive");t=b,r=new WeakMap,a=new WeakMap;const m=class{constructor(e,t){__privateAdd(this,h),__publicField(this,o,!0),__privateAdd(this,i,void 0),__privateAdd(this,n,new Set),__privateAdd(this,s,void 0),__privateAdd(this,d,0),__privateAdd(this,_,new Set),t?(__privateSet(this,i,t()),__privateSet(this,s,t),__privateSet(this,d,Math.max(...e.map((e=>__privateGet(e,d))))+1),e.forEach((e=>__privateGet(e,_).add(this)))):__privateSet(this,i,e)}get value(){return __privateGet(m,p).size&&m.applyUpdates(),__privateGet(this,i)}set value(e){0===__privateGet(this,d)&&(__privateGet(m,p).size||queueMicrotask(m.applyUpdates),__privateGet(m,p).set(this,e))}addReaction(e){return __privateGet(this,n).add(e),()=>__privateGet(this,n).delete(e)}static applyUpdates(){var e,t,r;if(__privateGet(m,p).size){for(const[t,r]of __privateGet(m,p).entries())__privateMethod(e=t,h,u).call(e,r);__privateGet(m,p).clear();for(const e of __privateGet(m,c))if(e)for(const a of e)__privateMethod(r=a,h,u).call(r,__privateGet(t=a,s).call(t));__privateGet(m,c).length=0;for(const e of __privateGet(m,l))e();__privateGet(m,l).length=0}}};let y=m;o=b,i=new WeakMap,n=new WeakMap,s=new WeakMap,d=new WeakMap,_=new WeakMap,p=new WeakMap,c=new WeakMap,l=new WeakMap,h=new WeakSet,u=function(e){if(e===__privateGet(this,i))return;__privateSet(this,i,e),__privateGet(m,l).push(...__privateGet(this,n));const t=__privateGet(m,c);for(const r of __privateGet(this,_)){const e=__privateGet(r,d);t[e]||(t[e]=new Set),t[e].add(r)}},__privateAdd(y,p,new Map),__privateAdd(y,c,[]),__privateAdd(y,l,[]);const g=(e,t)=>{if(null==e?void 0:e[b])return t(e.value),e.addReaction((()=>t(e.value)));t(e)};var M=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",SimpleReactive:class{constructor(e){__publicField(this,t,!0),__privateAdd(this,r,void 0),__privateAdd(this,a,new Set),__privateSet(this,r,e)}get value(){return __privateGet(this,r)}set value(e){if(e!==__privateGet(this,r)){__privateSet(this,r,e);for(const e of __privateGet(this,a))e()}}addReaction(e){return __privateGet(this,a).add(e),()=>__privateGet(this,a).delete(e)}},FunctionalReactive:y,r:(e,t)=>new y(e,t),reactiveDo:g});const S=(e,t)=>r=>{Array.isArray(r)?r.length?e(r[0]):t():e(r)};var w=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",pipe:(e,...t)=>t.reduce(((e,t)=>t(e)),e),dispatch:(e,t,r)=>e.dispatchEvent(new CustomEvent(t,__spreadValues({bubbles:!0,cancelable:!0,composed:!0},r))),createDestructable:(e,t)=>{const r=__spreadProps(__spreadValues({},e),{[Symbol.iterator]:()=>t[Symbol.iterator]()});return Object.defineProperty(r,Symbol.iterator,{enumerable:!1}),r},maybeDo:S,functionAsObject:e=>new Proxy({},{get:(t,r)=>e(r)})});const k=Symbol.for("bruh reactive"),G=Symbol.for("bruh meta node"),A=Symbol.for("bruh meta text node"),P=Symbol.for("bruh meta element"),O=e=>e[G]?e.node:e instanceof Node?e:document.createTextNode(e),T=e=>e.flat(1/0).flatMap((e=>{if(!e[k])return[O(e)];if(Array.isArray(e.value)){const t=new f;return e.addReaction((()=>{t.replaceChildren(...T(e.value))})),[t.startMarker,...T(e.value),t.endMarker]}let t=O(e.value);return e.addReaction((()=>{const r=t;t=O(e.value),r.replaceWith(t)})),[t]}));class x{constructor(e){this[G]=this[A]=!0,e[k]?(this.node=document.createTextNode(e.value),e.addReaction((()=>{this.node.textContent=e.value}))):this.node=document.createTextNode(e)}addProperties(e={}){return Object.assign(this.node,e),this}}class N{constructor(e,t){this[G]=this[P]=!0,this.node=t?document.createElementNS(t,e):document.createElement(e)}static from(e){const t=new this("div");return t.node=e,t}addProperties(e={}){return Object.assign(this.node,e),this}addAttributes(e={}){for(const t in e)g(e[t],S((e=>this.node.setAttribute(t,e)),(()=>this.node.removeAttribute(t))));return this}addDataAttributes(e={}){for(const t in e)g(e[t],S((e=>this.node.dataset[t]=e),(()=>delete this.node.dataset[t])));return this}before(...e){this.node.before(...T(e))}prepend(...e){this.node.prepend(...T(e))}append(...e){this.node.append(...T(e))}after(...e){this.node.after(...T(e))}replaceChildren(...e){this.node.replaceChildren(...T(e))}replaceWith(...e){this.node.replaceWith(...T(e))}}var j=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",childrenToNodes:T,MetaTextNode:x,MetaElement:N,hydrateTextNodes:()=>{const e={},t=document.getElementsByTagName("bruh-textnode");for(const r of t){const t=document.createTextNode(r.textContent);r.dataset.tag&&(e[r.dataset.tag]=t),r.replaceWith(t)}return e},t:e=>new x(e),e:(e,t)=>(...r)=>{const a=new N(e,t);if((null==(o=r[0])?void 0:o[G])||(null==o?void 0:o[k])||o instanceof Node||Array.isArray(o)||"function"!=typeof o)a.append(r);else{const[e,...t]=r;a.addAttributes(e),a.append(t)}var o;return a},h:(e,t,...r)=>{if("string"==typeof e){const a=new N(e);return a.addAttributes(t||{}),a.append(r),a}return e(Object.assign({},t,{children:r}))},JSXFragment:({children:e})=>e});e.dom=j,e.liveFragment=v,e.reactive=M,e.util=w,Object.defineProperty(e,"__esModule",{value:!0}),e[Symbol.toStringTag]="Module"}));
2
+ //# sourceMappingURL=bruh.umd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bruh.umd.js","sources":["../src/dom/live-fragment.mjs","../src/reactive/index.mjs","../src/util/index.mjs","../src/dom/index.browser.mjs"],"sourcesContent":["// Lightweight and performant DOM fragment that keeps its place,\n// useful for grouping siblings without making a parent element.\n// Not a true analog of the DocumentFragment, because the implementation\n// would be infeasible with that scope, adding a performance penalty as well.\n// Works as long as the start & end placeholders are siblings in that order\n// and they do not overlap other LiveFragment's:\n// Works: (start A)(start B)(end B)(end A)\n// Fails: (start A)(start B)(end A)(end B)\n// Also, make sure not to call .normalize() on the parent element,\n// because that would ruin the placeholders.\nexport class LiveFragment {\n constructor() {\n this.startMarker = document.createTextNode(\"\")\n this.endMarker = document.createTextNode(\"\")\n }\n\n static from(firstNode, lastNode) {\n const liveFragment = new this()\n firstNode.before(liveFragment.startMarker)\n lastNode.after(liveFragment.endMarker)\n return liveFragment\n }\n\n before(...xs) {\n this.startMarker.before(...xs)\n }\n\n prepend(...xs) {\n this.startMarker.after(...xs)\n }\n\n append(...xs) {\n this.endMarker.before(...xs)\n }\n\n after(...xs) {\n this.endMarker.after(...xs)\n }\n\n remove() {\n const range = document.createRange()\n range.setStartBefore(this.startMarker)\n range.setEndAfter(this.endMarker)\n range.deleteContents()\n }\n\n replaceChildren(...xs) {\n const range = document.createRange()\n range.setStartAfter(this.startMarker)\n range.setEndBefore(this.endMarker)\n range.deleteContents()\n this.startMarker.after(...xs)\n }\n\n replaceWith(...xs) {\n this.endMarker.after(...xs)\n this.remove()\n }\n\n get childNodes() {\n const childNodes = []\n\n for (\n let currentNode = this.startMarker.nextSibling;\n currentNode != this.endMarker && currentNode;\n currentNode = currentNode.nextSibling\n )\n childNodes.push(currentNode)\n\n return childNodes\n }\n\n get children() {\n return this.childNodes\n .filter(node => node instanceof HTMLElement)\n }\n}\n","const isReactive = Symbol.for(\"bruh reactive\")\n\n// A super simple and performant reactive value implementation\nexport class SimpleReactive {\n [isReactive] = true\n\n #value\n #reactions = new Set()\n\n constructor(value) {\n this.#value = value\n }\n\n get value() {\n return this.#value\n }\n\n set value(newValue) {\n if (newValue === this.#value)\n return\n\n this.#value = newValue\n for (const reaction of this.#reactions)\n reaction()\n }\n\n addReaction(reaction) {\n this.#reactions.add(reaction)\n\n return () =>\n this.#reactions.delete(reaction)\n }\n}\n\n// A reactive implementation for building functional reactive graphs\n// Ensures state consistency, minimal node updates, and transparent update batching\nexport class FunctionalReactive {\n [isReactive] = true\n\n #value\n #reactions = new Set()\n\n // For derived nodes, f is the derivation function\n #f\n // Source nodes are 0 deep in the derivation graph\n // This is for topological sort\n #depth = 0\n // All nodes have a set of derivatives that update when the node changes\n #derivatives = new Set()\n\n // Keep track of all the pending changes from the value setter\n static #settersQueue = new Map()\n // A queue of derivatives to potentially update, sorted into sets by depth\n // This starts with depth 1 and can potentially have holes\n static #derivativesQueue = []\n // A queue of reactions to run after the graph is fully updated\n static #reactionsQueue = []\n\n constructor(x, f) {\n if (!f) {\n this.#value = x\n return\n }\n\n this.#value = f()\n this.#f = f\n this.#depth = Math.max(...x.map(d => d.#depth)) + 1\n\n x.forEach(d => d.#derivatives.add(this))\n }\n\n get value() {\n // If there are any pending updates, go ahead and apply them first\n // It's ok that there's already a microtask queued for this\n if (FunctionalReactive.#settersQueue.size)\n FunctionalReactive.applyUpdates()\n\n return this.#value\n }\n\n set value(newValue) {\n // Only allow souce nodes to be directly updated\n if (this.#depth !== 0)\n return\n\n // Unless asked for earlier, these updates are just queued up until the microtasks run\n if (!FunctionalReactive.#settersQueue.size)\n queueMicrotask(FunctionalReactive.applyUpdates)\n\n FunctionalReactive.#settersQueue.set(this, newValue)\n }\n\n addReaction(reaction) {\n this.#reactions.add(reaction)\n\n return () =>\n this.#reactions.delete(reaction)\n }\n\n // Apply an update for a node and queue its derivatives if it actually changed\n #applyUpdate(newValue) {\n if (newValue === this.#value)\n return\n\n this.#value = newValue\n FunctionalReactive.#reactionsQueue.push(...this.#reactions)\n\n const queue = FunctionalReactive.#derivativesQueue\n for (const derivative of this.#derivatives) {\n const depth = derivative.#depth\n if (!queue[depth])\n queue[depth] = new Set()\n\n queue[depth].add(derivative)\n }\n }\n\n // Apply pending updates from actually changed source nodes\n static applyUpdates() {\n if (!FunctionalReactive.#settersQueue.size)\n return\n\n // Bootstrap by applying the updates from the pending setters\n for (const [sourceNode, newValue] of FunctionalReactive.#settersQueue.entries())\n sourceNode.#applyUpdate(newValue)\n FunctionalReactive.#settersQueue.clear()\n\n // Iterate down the depths, ignoring holes\n // Note that both the queue (Array) and each depth Set iterators update as items are added\n for (const depthSet of FunctionalReactive.#derivativesQueue) if (depthSet)\n for (const derivative of depthSet)\n derivative.#applyUpdate(derivative.#f())\n FunctionalReactive.#derivativesQueue.length = 0\n\n // Call all reactions now that the graph has a fully consistent state\n for (const reaction of FunctionalReactive.#reactionsQueue)\n reaction()\n FunctionalReactive.#reactionsQueue.length = 0\n }\n}\n\n// A little convenience function\nexport const r = (x, f) => new FunctionalReactive(x, f)\n\n// Do something with a value, updating if it is reactive\nexport const reactiveDo = (x, f) => {\n if (x?.[isReactive]) {\n f(x.value)\n return x.addReaction(() => f(x.value))\n }\n\n f(x)\n}\n","// Create a pipeline with an initial value and a series of functions\nexport const pipe = (x, ...fs) =>\n fs.reduce((y, f) => f(y), x)\n\n// Dispatch a custom event to (capturing) and from (bubbling) a target (usually a DOM node)\n// Returns false if the event was cancelled (preventDefault()) and true otherwise\nexport const dispatch = (target, type, options) =>\n target.dispatchEvent(\n // Default to behave like most DOM events\n new CustomEvent(type, {\n bubbles: true,\n cancelable: true,\n composed: true,\n ...options\n })\n )\n\n// Inspired by https://antfu.me/posts/destructuring-with-object-or-array#take-away\n// Creates an object that is both destructable with {...} and [...]\n// Useful for writing library functions à la react-use & vueuse\nexport const createDestructable = (object, iterable) => {\n const destructable = {\n ...object,\n [Symbol.iterator]: () => iterable[Symbol.iterator]()\n }\n\n Object.defineProperty(destructable, Symbol.iterator, {\n enumerable: false\n })\n\n return destructable\n}\n\n// A function that acts like Maybe.from(x).ifExists(existsThen).ifEmpty(emptyThen)\n// Except we just use an array in place of a true Maybe type\n// This is useful for setting and removing reactive attributes\nexport const maybeDo = (existsThen, emptyThen) => x => {\n if (Array.isArray(x)) {\n if (x.length)\n existsThen(x[0])\n else\n emptyThen()\n }\n else\n existsThen(x)\n}\n\n// Creates an object (as a Proxy) that acts as a function\n// So functionAsObject(f).property is equivalent to f(\"property\")\n// This is can be useful when combined with destructuring syntax, e.g.:\n// const { html, head, title, body, main, h1, p } = functionAsObject(e)\nexport const functionAsObject = f =>\n new Proxy({}, {\n get: (_, property) => f(property)\n })\n","import { LiveFragment } from \"./live-fragment.mjs\"\nimport { reactiveDo } from \"../reactive/index.mjs\"\nimport { maybeDo } from \"../util/index.mjs\"\n\nconst isReactive = Symbol.for(\"bruh reactive\")\nconst isMetaNode = Symbol.for(\"bruh meta node\")\nconst isMetaTextNode = Symbol.for(\"bruh meta text node\")\nconst isMetaElement = Symbol.for(\"bruh meta element\")\n\n// A basic check for if a value is allowed as a meta node's child\n// It's responsible for quickly checking the type, not deep validation\nconst isMetaNodeChild = x =>\n // meta nodes, reactives, and DOM nodes\n x?.[isMetaNode] ||\n x?.[isReactive] ||\n x instanceof Node ||\n // Any array, just assume it contains valid children\n Array.isArray(x) ||\n // Everything else, as long as it isn't a function, can be a child when stringified\n typeof x !== \"function\"\n\nconst toNode = x => {\n if (x[isMetaNode])\n return x.node\n\n if (x instanceof Node)\n return x\n\n return document.createTextNode(x)\n}\n\nexport const childrenToNodes = children =>\n children\n .flat(Infinity)\n .flatMap(child => {\n if (!child[isReactive])\n return [toNode(child)]\n\n if (Array.isArray(child.value)) {\n const liveFragment = new LiveFragment()\n child.addReaction(() => {\n liveFragment.replaceChildren(...childrenToNodes(child.value))\n })\n return [liveFragment.startMarker, ...childrenToNodes(child.value), liveFragment.endMarker]\n }\n\n let node = toNode(child.value)\n child.addReaction(() => {\n const oldNode = node\n node = toNode(child.value)\n oldNode.replaceWith(node)\n })\n return [node]\n })\n\n\n\n// Meta Nodes\n\nexport class MetaTextNode {\n constructor(textContent) {\n this[isMetaNode] =\n this[isMetaTextNode] = true\n\n if (textContent[isReactive]) {\n this.node = document.createTextNode(textContent.value)\n textContent.addReaction(() => {\n this.node.textContent = textContent.value\n })\n }\n else {\n this.node = document.createTextNode(textContent)\n }\n }\n\n addProperties(properties = {}) {\n Object.assign(this.node, properties)\n\n return this\n }\n}\n\nexport class MetaElement {\n constructor(name, namespace) {\n this[isMetaNode] =\n this[isMetaElement] = true\n\n this.node =\n namespace\n ? document.createElementNS(namespace, name)\n : document.createElement ( name)\n }\n\n static from(element) {\n const result = new this(\"div\")\n result.node = element\n return result\n }\n\n addProperties(properties = {}) {\n Object.assign(this.node, properties)\n\n return this\n }\n\n addAttributes(attributes = {}) {\n for (const name in attributes)\n reactiveDo(attributes[name],\n maybeDo(\n value => this.node.setAttribute (name, value),\n () => this.node.removeAttribute(name)\n )\n )\n\n return this\n }\n\n addDataAttributes(dataAttributes = {}) {\n for (const name in dataAttributes)\n reactiveDo(dataAttributes[name],\n maybeDo(\n value => this.node.dataset[name] = value,\n () => delete this.node.dataset[name]\n )\n )\n\n return this\n }\n\n before(...xs) {\n this.node.before(...childrenToNodes(xs))\n }\n\n prepend(...xs) {\n this.node.prepend(...childrenToNodes(xs))\n }\n\n append(...xs) {\n this.node.append(...childrenToNodes(xs))\n }\n\n after(...xs) {\n this.node.after(...childrenToNodes(xs))\n }\n\n replaceChildren(...xs) {\n this.node.replaceChildren(...childrenToNodes(xs))\n }\n\n replaceWith(...xs) {\n this.node.replaceWith(...childrenToNodes(xs))\n }\n}\n\n\n\n// Convenience functions\n\nexport const hydrateTextNodes = () => {\n const tagged = {}\n const bruhTextNodes = document.getElementsByTagName(\"bruh-textnode\")\n\n for (const bruhTextNode of bruhTextNodes) {\n const textNode = document.createTextNode(bruhTextNode.textContent)\n\n if (bruhTextNode.dataset.tag)\n tagged[bruhTextNode.dataset.tag] = textNode\n\n bruhTextNode.replaceWith(textNode)\n }\n\n return tagged\n}\n\nconst createMetaTextNode = textContent =>\n new MetaTextNode(textContent)\n\nconst createMetaElement = (name, namespace) => (...variadic) => {\n const meta = new MetaElement(name, namespace)\n\n // Implement optional attributes as first argument\n if (!isMetaNodeChild(variadic[0])) {\n const [attributes, ...children] = variadic\n meta.addAttributes(attributes)\n meta.append(children)\n }\n else {\n meta.append(variadic)\n }\n\n return meta\n}\n\n// JSX integration\nconst createMetaElementJSX = (nameOrComponent, attributesOrProps, ...children) => {\n // If we are making a html element\n // This is likely when the jsx tag name begins with a lowercase character\n if (typeof nameOrComponent == \"string\") {\n const meta = new MetaElement(nameOrComponent)\n\n // These are attributes then, but they might be null/undefined\n meta.addAttributes(attributesOrProps || {})\n meta.append(children)\n\n return meta\n }\n\n // It must be a component, then\n // Bruh components are just functions that return meta elements\n // Due to JSX, this would mean a function with only one parameter - a \"props\" object\n // This object includes the all of the attributes and a \"children\" key\n return nameOrComponent( Object.assign({}, attributesOrProps, { children }) )\n}\n\n// These will be called with short names\nexport {\n createMetaTextNode as t,\n createMetaElement as e,\n createMetaElementJSX as h\n}\n\n// The JSX fragment is made into a bruh fragment (just an array)\nexport const JSXFragment = ({ children }) => children\n"],"names":["constructor","startMarker","document","createTextNode","endMarker","firstNode","lastNode","liveFragment2","this","before","after","xs","prepend","append","remove","range","createRange","setStartBefore","setEndAfter","deleteContents","replaceChildren","setStartAfter","setEndBefore","replaceWith","childNodes","currentNode","nextSibling","push","filter","node","HTMLElement","isReactive","Symbol","for","x","f","Set","_value2","_f","_depth","Math","max","map","__privateGet","forEach","_derivatives","add","_settersQueue","size","applyUpdates","newValue","_FunctionalReactive","set","addReaction","reaction","_reactions2","delete","sourceNode","entries","_applyUpdate","call","clear","depthSet","_derivativesQueue","derivative","length","_reactionsQueue","queue","depth","FunctionalReactive","Map","reactiveDo","value","_value","_reactions","maybeDo","existsThen","emptyThen","Array","isArray","fs","reduce","y","target","type","options","dispatchEvent","CustomEvent","__spreadValues","bubbles","cancelable","composed","object","iterable","destructable","__spreadProps","iterator","defineProperty","enumerable","Proxy","get","_","property","isMetaNode","isMetaTextNode","isMetaElement","toNode","Node","childrenToNodes","children","flat","Infinity","flatMap","child","LiveFragment","oldNode","textContent","addProperties","properties","assign","name","namespace","createElementNS","createElement","element","result","addAttributes","attributes","setAttribute","removeAttribute","addDataAttributes","dataAttributes","dataset","tagged","bruhTextNodes","getElementsByTagName","bruhTextNode","textNode","tag","MetaTextNode","variadic","meta","MetaElement","nameOrComponent","attributesOrProps","Object"],"mappings":"66CAWEA,mBACOC,YAAcC,SAASC,eAAe,SACtCC,UAAcF,SAASC,eAAe,gBAGjCE,EAAWC,SACfC,EAAe,IAAIC,cACfC,OAAOF,EAAaN,eACrBS,MAAMH,EAAaH,WACrBG,EAGTE,UAAUE,QACHV,YAAYQ,UAAUE,GAG7BC,WAAWD,QACJV,YAAYS,SAASC,GAG5BE,UAAUF,QACHP,UAAUK,UAAUE,GAG3BD,SAASC,QACFP,UAAUM,SAASC,GAG1BG,eACQC,EAAQb,SAASc,gBACjBC,eAAeT,KAAKP,eACpBiB,YAAYV,KAAKJ,aACjBe,iBAGRC,mBAAmBT,SACXI,EAAQb,SAASc,gBACjBK,cAAcb,KAAKP,eACnBqB,aAAad,KAAKJ,aAClBe,sBACDlB,YAAYS,SAASC,GAG5BY,eAAeZ,QACRP,UAAUM,SAASC,QACnBG,gCAICU,EAAa,WAGbC,EAAcjB,KAAKP,YAAYyB,YACnCD,GAAejB,KAAKJ,WAAaqB,EACjCA,EAAcA,EAAYC,cAEfC,KAAKF,UAEXD,wBAIAhB,KAAKgB,WACTI,WAAeC,aAAgBC,wGC1EhCC,EAAaC,OAAOC,IAAI,iBAI3BF,8CAsDD/B,YAAYkC,EAAGC,8CArBA,mDAGF,IAAIC,qDAMR,uBAEM,IAAIA,KAWZD,qBAKAE,EAASF,uBACTG,EAAKH,qBACLI,EAASC,KAAKC,OAAOP,EAAEQ,QAASC,eAAEJ,MAAW,KAEhDK,YAAaD,eAAEE,GAAaC,IAAItC,2BAR3B6B,EAASH,sBAcZS,eAAmBI,GAAcC,QAChBC,eAEdN,kBAAKN,aAGJa,GAEY,IAAhBP,kBAAKJ,KAIJI,eAAmBI,GAAcC,qBACrBG,EAAmBF,6BAEjBF,GAAcK,IAAI5C,KAAM0C,IAG7CG,YAAYC,4BACLC,GAAWT,IAAIQ,GAEb,IACLX,kBAAKY,GAAWC,OAAOF,sCAuBpBX,eAAmBI,GAAcC,gBAI1BS,EAAYP,KAAaP,eAAmBI,GAAcW,8BACzDC,KAAXC,OAAwBV,kBACPH,GAAcc,kBAItBC,KAAYnB,eAAmBoB,MAAuBD,YACpDE,KAAcF,sBACZH,KAAXC,OAAwBjB,iBAAWL,GAAXsB,wBACTG,GAAkBE,OAAS,YAGnCX,KAAYX,eAAmBuB,sBAEvBA,GAAgBD,OAAS,aApG7ClC,oIA+DW,SAACmB,MACPA,IAAaP,kBAAKN,4BAGjBA,EAASa,kBACKgB,GAAgBvC,QAAQgB,kBAAKY,UAE1CY,EAAQxB,eAAmBoB,aACtBC,KAAcrB,kBAAKE,GAAc,OACpCuB,EAAQzB,eAAWJ,GACpB4B,EAAMC,OACHA,GAAS,IAAIhC,OAEfgC,GAAOtB,IAAIkB,kBA7EhBK,EAeEtB,EAAgB,IAAIuB,kBAftBD,EAkBEN,EAAoB,iBAlBtBM,EAoBEH,EAAkB,UAyFdK,EAAa,CAACrC,EAAGC,cACxBD,WAAIH,YACJG,EAAEsC,OACGtC,EAAEmB,aAAY,IAAMlB,EAAED,EAAEsC,WAG/BtC,2FA9IFlC,YAAYwE,yBALG,mDAGF,IAAIpC,uBAGVqC,EAASD,sBAIP7B,kBAAK8B,aAGJvB,MACJA,IAAaP,kBAAK8B,sBAGjBA,EAASvB,aACHI,KAAYX,kBAAK+B,QAI9BrB,YAAYC,4BACLoB,GAAW5B,IAAIQ,GAEb,IACLX,kBAAK+B,GAAWlB,OAAOF,4BAgHZ,CAACpB,EAAGC,IAAM,IAAIkC,EAAmBnC,EAAGC,wBC1GxCwC,EAAU,CAACC,EAAYC,QAC9BC,MAAMC,QAAQ7C,GACZA,EAAE+B,SACO/B,EAAE,UAKJA,2EA3CK,CAACA,KAAM8C,IACzBA,EAAGC,QAAO,CAACC,EAAG/C,IAAMA,EAAE+C,IAAIhD,YAIJ,CAACiD,EAAQC,EAAMC,IACrCF,EAAOG,cAEL,IAAIC,YAAYH,EAAMI,gBACpBC,SAAS,EACTC,YAAY,EACZC,UAAU,GACPN,wBAOyB,CAACO,EAAQC,WACnCC,EAAeC,gCAChBH,GADgB,EAElB5D,OAAOgE,UAAW,IAAMH,EAAS7D,OAAOgE,4BAGpCC,eAAeH,EAAc9D,OAAOgE,SAAU,CACnDE,YAAY,IAGPJ,iCAsBP,IAAIK,MAAM,GAAI,CACZC,IAAK,CAACC,EAAGC,IAAanE,EAAEmE,aCjDtBvE,EAAiBC,OAAOC,IAAI,iBAC5BsE,EAAiBvE,OAAOC,IAAI,kBAC5BuE,EAAiBxE,OAAOC,IAAI,uBAC5BwE,EAAiBzE,OAAOC,IAAI,qBAc5ByE,KACAxE,EAAEqE,GACGrE,EAAEL,KAEPK,aAAayE,KACRzE,EAEFhC,SAASC,eAAe+B,GAGpB0E,KACXC,EACGC,KAAKC,EAAAA,GACLC,iBACMC,EAAMlF,SACF,CAAC2E,EAAOO,OAEbnC,MAAMC,QAAQkC,EAAMzC,OAAQ,OACxBjE,EAAe,IAAI2G,WACnB7D,aAAY,OACHjC,mBAAmBwF,EAAgBK,EAAMzC,WAEjD,CAACjE,EAAaN,eAAgB2G,EAAgBK,EAAMzC,OAAQjE,EAAaH,eAG9EyB,EAAO6E,EAAOO,EAAMzC,gBAClBnB,aAAY,WACV8D,EAAUtF,IACT6E,EAAOO,EAAMzC,SACZjD,YAAYM,MAEf,CAACA,cAQZ7B,YAAYoH,QACLb,GACL/F,KAAKgG,IAAkB,EAEnBY,EAAYrF,SACTF,KAAO3B,SAASC,eAAeiH,EAAY5C,SACpCnB,aAAY,UACjBxB,KAAKuF,YAAcA,EAAY5C,eAIjC3C,KAAO3B,SAASC,eAAeiH,GAIxCC,cAAcC,EAAa,kBAClBC,OAAO/G,KAAKqB,KAAMyF,GAElB9G,cAKTR,YAAYwH,EAAMC,QACXlB,GACL/F,KAAKiG,IAAiB,OAEjB5E,KACH4F,EACIvH,SAASwH,gBAAgBD,EAAWD,GACpCtH,SAASyH,cAA2BH,eAGhCI,SACJC,EAAS,IAAIrH,KAAK,gBACjBqB,KAAO+F,EACPC,EAGTR,cAAcC,EAAa,kBAClBC,OAAO/G,KAAKqB,KAAMyF,GAElB9G,KAGTsH,cAAcC,EAAa,cACdP,KAAQO,IACNA,EAAWP,GACpB7C,MACWnE,KAAKqB,KAAKmG,aAAgBR,EAAMhD,KACzC,IAAShE,KAAKqB,KAAKoG,gBAAgBT,aAIlChH,KAGT0H,kBAAkBC,EAAiB,cACtBX,KAAQW,IACNA,EAAeX,GACxB7C,MACkBnE,KAAKqB,KAAKuG,QAAQZ,GAAQhD,IAC1C,WAAgBhE,KAAKqB,KAAKuG,QAAQZ,aAIjChH,KAGTC,UAAUE,QACHkB,KAAKpB,UAAUmG,EAAgBjG,IAGtCC,WAAWD,QACJkB,KAAKjB,WAAWgG,EAAgBjG,IAGvCE,UAAUF,QACHkB,KAAKhB,UAAU+F,EAAgBjG,IAGtCD,SAASC,QACFkB,KAAKnB,SAASkG,EAAgBjG,IAGrCS,mBAAmBT,QACZkB,KAAKT,mBAAmBwF,EAAgBjG,IAG/CY,eAAeZ,QACRkB,KAAKN,eAAeqF,EAAgBjG,uIAQb,WACxB0H,EAAS,GACTC,EAAgBpI,SAASqI,qBAAqB,2BAEzCC,KAAgBF,EAAe,OAClCG,EAAWvI,SAASC,eAAeqI,EAAapB,aAElDoB,EAAaJ,QAAQM,QAChBF,EAAaJ,QAAQM,KAAOD,KAExBlH,YAAYkH,UAGpBJ,QAIP,IAAIM,EAAavB,KAEO,CAACI,EAAMC,IAAc,IAAImB,WAC3CC,EAAO,IAAIC,EAAYtB,EAAMC,gBAGdmB,EAAS,aAxK1BrC,uBACAxE,KACJG,aAAayE,MAEb7B,MAAMC,QAAQ7C,IAED,mBAANA,IAwKArB,OAAO+H,OANqB,OAC1Bb,KAAelB,GAAY+B,IAC7Bd,cAAcC,KACdlH,OAAOgG,GA7KQ,aAmLfgC,KAIoB,CAACE,EAAiBC,KAAsBnC,QAGrC,iBAAnBkC,EAA6B,OAChCF,EAAO,IAAIC,EAAYC,YAGxBjB,cAAckB,GAAqB,MACnCnI,OAAOgG,GAELgC,SAOFE,EAAiBE,OAAO1B,OAAO,GAAIyB,EAAmB,CAAEnC,SAAAA,kBAWtC,EAAGA,SAAAA,KAAeA"}
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "library",
11
11
  "modern"
12
12
  ],
13
- "version": "1.6.4",
13
+ "version": "1.8.1",
14
14
  "license": "MIT",
15
15
  "author": {
16
16
  "name": "Daniel Ethridge",
@@ -25,20 +25,15 @@
25
25
  },
26
26
  "type": "module",
27
27
  "exports": {
28
- "./dom": {
29
- "node": "./src/dom/node/index.mjs",
30
- "browser": "./src/dom/browser/index.mjs"
31
- },
32
- "./dom/html": {
33
- "node": "./src/dom/node/html.mjs",
34
- "browser": "./src/dom/browser/html.mjs"
28
+ "./": {
29
+ "browser": "./src/index.mjs"
35
30
  },
36
- "./dom/svg": {
37
- "node": "./src/dom/node/svg.mjs",
38
- "browser": "./src/dom/browser/svg.mjs"
31
+ "./dom": {
32
+ "node": "./src/dom/index.node.mjs",
33
+ "browser": "./src/dom/index.browser.mjs"
39
34
  },
40
35
  "./dom/live-fragment": {
41
- "browser": "./src/dom/browser/live-fragment.mjs"
36
+ "browser": "./src/dom/live-fragment.mjs"
42
37
  },
43
38
  "./reactive": "./src/reactive/index.mjs",
44
39
  "./util": "./src/util/index.mjs",
@@ -50,11 +45,17 @@
50
45
  "bin": {
51
46
  "bruh": "./src/cli/index.mjs"
52
47
  },
53
- "scripts": {},
48
+ "scripts": {
49
+ "build": "vite build",
50
+ "prepare": "npm run build"
51
+ },
54
52
  "optionalDependencies": {
55
53
  "cac": "^6.7.3",
56
54
  "sharp": "^0.28.3"
57
55
  },
56
+ "devDependencies": {
57
+ "vite": "^2.5.10"
58
+ },
58
59
  "browserslist": [
59
60
  "ios_saf >= 12.4",
60
61
  "and_chr >= 88",
@@ -1,6 +1,6 @@
1
1
  import { LiveFragment } from "./live-fragment.mjs"
2
- import { reactiveDo } from "../../reactive/index.mjs"
3
- import { maybeDo } from "../../util/index.mjs"
2
+ import { reactiveDo } from "../reactive/index.mjs"
3
+ import { maybeDo } from "../util/index.mjs"
4
4
 
5
5
  const isReactive = Symbol.for("bruh reactive")
6
6
  const isMetaNode = Symbol.for("bruh meta node")
@@ -10,15 +10,14 @@ const isMetaElement = Symbol.for("bruh meta element")
10
10
  // A basic check for if a value is allowed as a meta node's child
11
11
  // It's responsible for quickly checking the type, not deep validation
12
12
  const isMetaNodeChild = x =>
13
- (typeof x === "object" && x !== null)
14
- // Only specific objects are allowed to be children
15
- ? x[isMetaNode] ||
16
- x[isReactive] ||
17
- x instanceof Node ||
18
- // We don't bother checking every array item, just assume it contains valid children
19
- Array.isArray(x)
20
- // Everything else, as long as it isn't a function, can be a child when stringified
21
- : typeof x !== "function"
13
+ // meta nodes, reactives, and DOM nodes
14
+ x?.[isMetaNode] ||
15
+ x?.[isReactive] ||
16
+ x instanceof Node ||
17
+ // Any array, just assume it contains valid children
18
+ Array.isArray(x) ||
19
+ // Everything else, as long as it isn't a function, can be a child when stringified
20
+ typeof x !== "function"
22
21
 
23
22
  const toNode = x => {
24
23
  if (x[isMetaNode])
@@ -39,14 +38,14 @@ export const childrenToNodes = children =>
39
38
 
40
39
  if (Array.isArray(child.value)) {
41
40
  const liveFragment = new LiveFragment()
42
- child.react(() => {
41
+ child.addReaction(() => {
43
42
  liveFragment.replaceChildren(...childrenToNodes(child.value))
44
43
  })
45
44
  return [liveFragment.startMarker, ...childrenToNodes(child.value), liveFragment.endMarker]
46
45
  }
47
46
 
48
47
  let node = toNode(child.value)
49
- child.react(() => {
48
+ child.addReaction(() => {
50
49
  const oldNode = node
51
50
  node = toNode(child.value)
52
51
  oldNode.replaceWith(node)
@@ -65,7 +64,7 @@ export class MetaTextNode {
65
64
 
66
65
  if (textContent[isReactive]) {
67
66
  this.node = document.createTextNode(textContent.value)
68
- textContent.react(() => {
67
+ textContent.addReaction(() => {
69
68
  this.node.textContent = textContent.value
70
69
  })
71
70
  }
@@ -45,15 +45,13 @@ const isMetaRawString = Symbol.for("bruh meta raw string")
45
45
  // A basic check for if a value is allowed as a meta node's child
46
46
  // It's responsible for quickly checking the type, not deep validation
47
47
  const isMetaNodeChild = x =>
48
- (typeof x === "object" && x !== null)
49
- // Only specific objects are allowed to be children
50
- ? x[isMetaNode] ||
51
- x[isMetaRawString] ||
52
- // We don't bother checking every array item, just assume it contains valid children
53
- Array.isArray(x)
54
- // Everything else, as long as it isn't a function, can be a child when stringified
55
- : typeof x !== "function"
56
-
48
+ // meta nodes, reactives, and DOM nodes
49
+ x?.[isMetaNode] ||
50
+ x?.[isMetaRawString] ||
51
+ // Any array, just assume it contains valid children
52
+ Array.isArray(x) ||
53
+ // Everything else, as long as it isn't a function, can be a child when stringified
54
+ typeof x !== "function"
57
55
 
58
56
 
59
57
  // Meta Nodes
package/src/index.mjs ADDED
@@ -0,0 +1,4 @@
1
+ export * as dom from "./dom/index.browser.mjs"
2
+ export * as liveFragment from "./dom/live-fragment.mjs"
3
+ export * as reactive from "./reactive/index.mjs"
4
+ export * as util from "./util/index.mjs"
@@ -0,0 +1,31 @@
1
+ export declare const isReactive: unique symbol
2
+
3
+ export declare type Reactive<T> = {
4
+ [isReactive]: true
5
+ value: T
6
+ addReaction: (reaction: Function) => Function
7
+ }
8
+
9
+ export declare const reactiveDo: {
10
+ <T>(reactive: Reactive<T>, f: (value: T) => unknown): Function
11
+ <T>(value: T, f: (value: T) => unknown): void
12
+ }
13
+
14
+ export declare class SimpleReactive<T> implements Reactive<T> {
15
+ constructor(value: T)
16
+
17
+ [isReactive]: true
18
+ get value(): T
19
+ set value(newValue: T)
20
+ addReaction(reaction: Function): () => boolean
21
+ }
22
+
23
+ export declare class FunctionalReactive<T> implements Reactive<T> {
24
+ constructor(value: T)
25
+ constructor(dependencies: Iterable<FunctionalReactive<unknown>>, f: () => T)
26
+
27
+ [isReactive]: true
28
+ get value(): T
29
+ set value(newValue: T)
30
+ addReaction(reaction: Function): () => boolean
31
+ }
@@ -1,59 +1,153 @@
1
1
  const isReactive = Symbol.for("bruh reactive")
2
2
 
3
- export class Reactive {
4
- constructor(value) {
5
- this[isReactive] = true
3
+ // A super simple and performant reactive value implementation
4
+ export class SimpleReactive {
5
+ [isReactive] = true
6
+
7
+ #value
8
+ #reactions = new Set()
6
9
 
7
- this._value = value
8
- this._reactors = new Set()
10
+ constructor(value) {
11
+ this.#value = value
9
12
  }
10
13
 
11
14
  get value() {
12
- return this._value
15
+ return this.#value
13
16
  }
14
17
 
15
18
  set value(newValue) {
16
- if (newValue === this._value)
19
+ if (newValue === this.#value)
20
+ return
21
+
22
+ this.#value = newValue
23
+ for (const reaction of this.#reactions)
24
+ reaction()
25
+ }
26
+
27
+ addReaction(reaction) {
28
+ this.#reactions.add(reaction)
29
+
30
+ return () =>
31
+ this.#reactions.delete(reaction)
32
+ }
33
+ }
34
+
35
+ // A reactive implementation for building functional reactive graphs
36
+ // Ensures state consistency, minimal node updates, and transparent update batching
37
+ export class FunctionalReactive {
38
+ [isReactive] = true
39
+
40
+ #value
41
+ #reactions = new Set()
42
+
43
+ // For derived nodes, f is the derivation function
44
+ #f
45
+ // Source nodes are 0 deep in the derivation graph
46
+ // This is for topological sort
47
+ #depth = 0
48
+ // All nodes have a set of derivatives that update when the node changes
49
+ #derivatives = new Set()
50
+
51
+ // Keep track of all the pending changes from the value setter
52
+ static #settersQueue = new Map()
53
+ // A queue of derivatives to potentially update, sorted into sets by depth
54
+ // This starts with depth 1 and can potentially have holes
55
+ static #derivativesQueue = []
56
+ // A queue of reactions to run after the graph is fully updated
57
+ static #reactionsQueue = []
58
+
59
+ constructor(x, f) {
60
+ if (!f) {
61
+ this.#value = x
17
62
  return
63
+ }
64
+
65
+ this.#value = f()
66
+ this.#f = f
67
+ this.#depth = Math.max(...x.map(d => d.#depth)) + 1
68
+
69
+ x.forEach(d => d.#derivatives.add(this))
70
+ }
18
71
 
19
- this._value = newValue
20
- this.wasUpdated()
72
+ get value() {
73
+ // If there are any pending updates, go ahead and apply them first
74
+ // It's ok that there's already a microtask queued for this
75
+ if (FunctionalReactive.#settersQueue.size)
76
+ FunctionalReactive.applyUpdates()
21
77
 
22
- return newValue
78
+ return this.#value
23
79
  }
24
80
 
25
- wasUpdated() {
26
- for (const reactor of this._reactors)
27
- reactor()
81
+ set value(newValue) {
82
+ // Only allow souce nodes to be directly updated
83
+ if (this.#depth !== 0)
84
+ return
85
+
86
+ // Unless asked for earlier, these updates are just queued up until the microtasks run
87
+ if (!FunctionalReactive.#settersQueue.size)
88
+ queueMicrotask(FunctionalReactive.applyUpdates)
89
+
90
+ FunctionalReactive.#settersQueue.set(this, newValue)
28
91
  }
29
92
 
30
- react(reactor) {
31
- this._reactors.add(reactor)
93
+ addReaction(reaction) {
94
+ this.#reactions.add(reaction)
32
95
 
33
96
  return () =>
34
- this._reactors.delete(reactor)
97
+ this.#reactions.delete(reaction)
35
98
  }
36
- }
37
99
 
38
- export const r = (xOrDependencies, f) => {
39
- // If no function f is specified, make a node for x
40
- if (!f)
41
- return new Reactive(xOrDependencies)
100
+ // Apply an update for a node and queue its derivatives if it actually changed
101
+ #applyUpdate(newValue) {
102
+ if (newValue === this.#value)
103
+ return
104
+
105
+ this.#value = newValue
106
+ FunctionalReactive.#reactionsQueue.push(...this.#reactions)
42
107
 
43
- // If there is a function, make a node that depends on other nodes
44
- const derived = new Reactive(f())
108
+ const queue = FunctionalReactive.#derivativesQueue
109
+ for (const derivative of this.#derivatives) {
110
+ const depth = derivative.#depth
111
+ if (!queue[depth])
112
+ queue[depth] = new Set()
45
113
 
46
- const update = () =>
47
- derived.value = f()
48
- for (const dependency of xOrDependencies)
49
- dependency.react(update)
114
+ queue[depth].add(derivative)
115
+ }
116
+ }
117
+
118
+ // Apply pending updates from actually changed source nodes
119
+ static applyUpdates() {
120
+ if (!FunctionalReactive.#settersQueue.size)
121
+ return
122
+
123
+ // Bootstrap by applying the updates from the pending setters
124
+ for (const [sourceNode, newValue] of FunctionalReactive.#settersQueue.entries())
125
+ sourceNode.#applyUpdate(newValue)
126
+ FunctionalReactive.#settersQueue.clear()
127
+
128
+ // Iterate down the depths, ignoring holes
129
+ // Note that both the queue (Array) and each depth Set iterators update as items are added
130
+ for (const depthSet of FunctionalReactive.#derivativesQueue) if (depthSet)
131
+ for (const derivative of depthSet)
132
+ derivative.#applyUpdate(derivative.#f())
133
+ FunctionalReactive.#derivativesQueue.length = 0
50
134
 
51
- return derived
135
+ // Call all reactions now that the graph has a fully consistent state
136
+ for (const reaction of FunctionalReactive.#reactionsQueue)
137
+ reaction()
138
+ FunctionalReactive.#reactionsQueue.length = 0
139
+ }
52
140
  }
53
141
 
142
+ // A little convenience function
143
+ export const r = (x, f) => new FunctionalReactive(x, f)
144
+
145
+ // Do something with a value, updating if it is reactive
54
146
  export const reactiveDo = (x, f) => {
55
- if (x[isReactive])
56
- x.react(() => f(x.value))
57
- else
58
- f(x)
147
+ if (x?.[isReactive]) {
148
+ f(x.value)
149
+ return x.addReaction(() => f(x.value))
150
+ }
151
+
152
+ f(x)
59
153
  }
@@ -44,3 +44,12 @@ export const maybeDo = (existsThen, emptyThen) => x => {
44
44
  else
45
45
  existsThen(x)
46
46
  }
47
+
48
+ // Creates an object (as a Proxy) that acts as a function
49
+ // So functionAsObject(f).property is equivalent to f("property")
50
+ // This is can be useful when combined with destructuring syntax, e.g.:
51
+ // const { html, head, title, body, main, h1, p } = functionAsObject(e)
52
+ export const functionAsObject = f =>
53
+ new Proxy({}, {
54
+ get: (_, property) => f(property)
55
+ })
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from "vite"
2
+
3
+ export default defineConfig({
4
+ build: {
5
+ lib: {
6
+ name: "bruh",
7
+ entry: new URL("./src/index.mjs", import.meta.url).pathname,
8
+ fileName: format => `bruh.${format}.js`
9
+ },
10
+ sourcemap: true
11
+ }
12
+ })