bruh 2.0.0-beta.2 → 2.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/browser.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { isReactive, reactiveDo } from './reactive.mjs';
1
+ import { isReactive, flat, reactiveDo } from './reactive.mjs';
2
2
 
3
3
  const terminalBruhChildToNode = (child) => {
4
4
  if (child instanceof Node)
@@ -9,21 +9,31 @@ const terminalBruhChildToNode = (child) => {
9
9
  return document.createTextNode(child + "");
10
10
  };
11
11
  const isBruhIterable = (x) => x != null && typeof x === "object" && !(x instanceof Node) && Symbol.iterator in x;
12
+ const ownedReactivesSymbol = /* @__PURE__ */ Symbol.for("bruh owned reactives");
12
13
  const reactiveTerminalBruhChildToNode = (child) => {
13
- let node = terminalBruhChildToNode(child.value);
14
+ const node = terminalBruhChildToNode(child.value);
15
+ let ownedReactives = node[ownedReactivesSymbol] ??= /* @__PURE__ */ new Set();
16
+ ownedReactives.add(child);
17
+ let nodeWeakRef = new WeakRef(node);
14
18
  const stopReacting = child.addReaction(() => {
15
- if (!node.parentNode) {
19
+ const node2 = nodeWeakRef.deref();
20
+ if (!node2?.parentNode) {
16
21
  stopReacting();
17
22
  return;
18
23
  }
19
- const oldNode = node;
24
+ const oldNode = node2;
20
25
  const child_ = child;
26
+ ownedReactives.delete(child);
21
27
  if (isBruhIterable(child.value)) {
22
28
  stopReacting();
23
- oldNode.replaceWith(...reactiveIterableBruhChildToNodes(child_));
29
+ const nodes = reactiveIterableBruhChildToNodes(child_);
30
+ oldNode.replaceWith(...nodes);
24
31
  } else {
25
- node = terminalBruhChildToNode(child_.value);
26
- oldNode.replaceWith(node);
32
+ const node3 = terminalBruhChildToNode(child.value);
33
+ ownedReactives = node3[ownedReactivesSymbol] ??= /* @__PURE__ */ new Set();
34
+ ownedReactives.add(child);
35
+ nodeWeakRef = new WeakRef(node3);
36
+ oldNode.replaceWith(node3);
27
37
  }
28
38
  });
29
39
  return node;
@@ -31,24 +41,30 @@ const reactiveTerminalBruhChildToNode = (child) => {
31
41
  function* reactiveIterableBruhChildToNodes(child) {
32
42
  const first = document.createComment("[");
33
43
  const last = document.createComment("]");
44
+ let ownedReactives = first[ownedReactivesSymbol] ??= /* @__PURE__ */ new Set();
45
+ ownedReactives.add(child);
46
+ let firstWeakRef = new WeakRef(first);
47
+ let lastWeakRef = new WeakRef(last);
34
48
  const stopReacting = child.addReaction(() => {
35
- if (!first.parentNode) {
49
+ const first2 = firstWeakRef.deref();
50
+ const last2 = lastWeakRef.deref();
51
+ if (!first2?.parentNode || !last2?.parentNode) {
36
52
  stopReacting();
37
53
  return;
38
54
  }
55
+ const child_ = child;
39
56
  const range = document.createRange();
40
- range.setStartAfter(first);
57
+ range.setStartAfter(first2);
41
58
  if (isBruhIterable(child.value)) {
42
- const child_ = child;
43
- range.setEndBefore(last);
59
+ range.setEndBefore(last2);
44
60
  range.deleteContents();
45
- first.after(...bruhChildrenToNodes(child_.value));
61
+ first2.after(...bruhChildrenToNodes(child.value));
46
62
  } else {
47
- const child_ = child;
63
+ ownedReactives.delete(child);
48
64
  stopReacting();
49
- range.setEndAfter(last);
65
+ range.setEndAfter(last2);
50
66
  range.deleteContents();
51
- first.replaceWith(reactiveTerminalBruhChildToNode(child_));
67
+ first2.replaceWith(reactiveTerminalBruhChildToNode(child_));
52
68
  }
53
69
  });
54
70
  yield first;
@@ -64,10 +80,11 @@ function* bruhChildrenToNodes(children) {
64
80
  else
65
81
  yield terminalBruhChildToNode(child);
66
82
  } else {
67
- if (isBruhIterable(child.value))
68
- yield* reactiveIterableBruhChildToNodes(child);
83
+ const flattened = flat(child);
84
+ if (isBruhIterable(flattened.value))
85
+ yield* reactiveIterableBruhChildToNodes(flattened);
69
86
  else
70
- yield reactiveTerminalBruhChildToNode(child);
87
+ yield reactiveTerminalBruhChildToNode(flattened);
71
88
  }
72
89
  }
73
90
  }
@@ -76,32 +93,58 @@ const isElementWithStyle = (element) => (
76
93
  element.style instanceof CSSStyleDeclaration
77
94
  );
78
95
  const applyStyles = (element, styles) => {
96
+ const ownedReactives = element[ownedReactivesSymbol] ??= /* @__PURE__ */ new Set();
97
+ let elementWeakRef = new WeakRef(element);
79
98
  for (const property in styles) {
80
99
  const property_ = property;
81
- reactiveDo(styles[property_], (value) => {
100
+ const maybeReactive = styles[property_];
101
+ if (isReactive(maybeReactive))
102
+ ownedReactives.add(maybeReactive);
103
+ reactiveDo(maybeReactive, (value) => {
104
+ const element2 = elementWeakRef.deref();
105
+ if (!element2)
106
+ return;
82
107
  if (value != null && typeof value !== "boolean")
83
- element.style.setProperty(property, value + "");
108
+ element2.style.setProperty(property, value + "");
84
109
  else
85
- element.style.removeProperty(property);
110
+ element2.style.removeProperty(property);
86
111
  });
87
112
  }
88
113
  };
89
114
  const applyClasses = (element, classes) => {
90
- for (const name in classes)
91
- reactiveDo(classes[name], (value) => {
92
- element.classList.toggle(name, value === true);
115
+ const ownedReactives = element[ownedReactivesSymbol] ??= /* @__PURE__ */ new Set();
116
+ let elementWeakRef = new WeakRef(element);
117
+ for (const name in classes) {
118
+ const maybeReactive = classes[name];
119
+ if (isReactive(maybeReactive))
120
+ ownedReactives.add(maybeReactive);
121
+ reactiveDo(maybeReactive, (value) => {
122
+ const element2 = elementWeakRef.deref();
123
+ if (!element2)
124
+ return;
125
+ element2.classList.toggle(name, value === true);
93
126
  });
127
+ }
94
128
  };
95
129
  const applyAttributes = (element, attributes) => {
96
- for (const name in attributes)
130
+ const ownedReactives = element[ownedReactivesSymbol] ??= /* @__PURE__ */ new Set();
131
+ let elementWeakRef = new WeakRef(element);
132
+ for (const name in attributes) {
133
+ const maybeReactive = attributes[name];
134
+ if (isReactive(maybeReactive))
135
+ ownedReactives.add(maybeReactive);
97
136
  reactiveDo(attributes[name], (value) => {
137
+ const element2 = elementWeakRef.deref();
138
+ if (!element2)
139
+ return;
98
140
  if (typeof value === "boolean")
99
- element.toggleAttribute(name, value);
141
+ element2.toggleAttribute(name, value);
100
142
  else if (value != null)
101
- element.setAttribute(name, value + "");
143
+ element2.setAttribute(name, value + "");
102
144
  else
103
- element.removeAttribute(name);
145
+ element2.removeAttribute(name);
104
146
  });
147
+ }
105
148
  };
106
149
  const t = (textContent) => {
107
150
  if (!isReactive(textContent))
@@ -171,5 +214,5 @@ class BruhText extends HTMLElement {
171
214
  }
172
215
  customElements.define("bruh-text", BruhText);
173
216
 
174
- export { BruhText, Fragment, applyAttributes, applyClasses, applyStyles, bruhChildrenToNodes, jsx, t };
217
+ export { BruhText, Fragment, applyAttributes, applyClasses, applyStyles, bruhChildrenToNodes, jsx, ownedReactivesSymbol, t };
175
218
  //# sourceMappingURL=browser.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser.mjs","sources":["../src/dom/index.browser.mts"],"sourcesContent":["import { isReactive, reactiveDo } from \"../reactive/index.mts\"\nimport type { Reactive, MaybeReactive } from \"../reactive/index.mts\"\nimport type {\n PropertyWiseOr,\n LikelyAsString,\n LikelyAsAbsent,\n LikelyAsBoolean\n} from \"./types.mts\"\nimport type { PropertiesHyphen as Styles } from \"csstype\"\nimport type {\n ElementType,\n\n HTMLTag,\n SVGTag,\n MathMLTag,\n\n Namespace,\n HTMLNamespace,\n SVGNamespace,\n MathMLNamespace,\n\n ElementToEventMap,\n ElementToAttributes\n} from \"html-info\"\n\nexport type TerminalBruhChild =\n | Node\n | LikelyAsString\n | LikelyAsAbsent\n\nexport type TerminalBruhChildOutputNode<Child extends TerminalBruhChild>\n = Child extends Node\n ? Child\n : Child extends LikelyAsString\n ? Text\n : Child extends LikelyAsAbsent\n ? Comment\n : Node\n\n// disallow a reactive from directly holding a reactive\nexport type FlatBruhChild =\n | MaybeReactive<TerminalBruhChild>\n | Reactive<TerminalBruhChild | Iterable<BruhChild>>\n\nexport type BruhChild =\n | FlatBruhChild\n | Iterable<BruhChild>\n\nexport type StylesToApply = {\n [Property in keyof Styles]: MaybeReactive<Styles[Property] | LikelyAsAbsent>\n}\n\nexport interface ClassesToApply {\n [className: string]: MaybeReactive<LikelyAsBoolean>\n}\n\nexport type AttributesToApply<\n Name extends string,\n NS extends Namespace = HTMLNamespace\n> = {\n [Attribute in keyof ElementToAttributes<Name, NS>]?:\n MaybeReactive<ElementToAttributes<Name, NS>[Attribute] | LikelyAsBoolean>\n}\n\ntype EventMapToListenerProps<EventMap> = {\n [E in ((keyof EventMap) & string) as `on${E}`]:\n (event: EventMap[E]) => void\n}\n\ntype AttributeMapToProps<AttributeMap> = {\n [Attribute in keyof AttributeMap]:\n MaybeReactive<AttributeMap[Attribute] | LikelyAsBoolean>\n | ( Attribute extends \"style\"\n ? StylesToApply\n : Attribute extends \"class\"\n ? ClassesToApply\n : never\n )\n}\n\nexport interface BruhOptions {\n namespace?: Namespace\n}\n\ninterface BaseBruhProps {\n bruh?: BruhOptions,\n children?: BruhChild\n}\n\nexport type BruhProps<\n Name extends string,\n NS extends string = HTMLNamespace\n>\n = BaseBruhProps\n & (\n NS extends HTMLNamespace\n ? { bruh?: { namespace?: HTMLNamespace } }\n : { bruh: { namespace: NS } }\n )\n & Partial<\n EventMapToListenerProps<ElementToEventMap<Name, NS>> &\n AttributeMapToProps<ElementToAttributes<Name, NS>>\n >\n\nexport type HTMLTagToBruhProps = { [Name in HTMLTag]: BruhProps<Name, HTMLNamespace> }\nexport type SVGTagToBruhProps = { [Name in SVGTag]: BruhProps<Name, SVGNamespace> }\nexport type MathMLTagToBruhProps = { [Name in MathMLTag]: BruhProps<Name, MathMLNamespace> }\n\nexport type TagToBruhProps =\n PropertyWiseOr<\n HTMLTagToBruhProps,\n PropertyWiseOr<\n SVGTagToBruhProps,\n MathMLTagToBruhProps\n >\n >\n\nexport namespace JSX {\n /** @see https://www.typescriptlang.org/docs/handbook/jsx.html#children-type-checking */\n export interface ElementChildrenAttribute { children: {} }\n\n /** @see https://www.typescriptlang.org/docs/handbook/jsx.html#attribute-type-checking */\n export interface IntrinsicElements extends TagToBruhProps {}\n\n export type Element = any\n\n export type ElementType = HTMLTag | SVGTag | MathMLTag | ((props: any) => Element)\n}\n\n// Coerces input into a DOM node, if it isn't already one\nconst terminalBruhChildToNode: {\n <Child extends TerminalBruhChild>(child: Child): TerminalBruhChildOutputNode<Child>\n} = (child: TerminalBruhChild): any => {\n // Existing DOM nodes are untouched\n if (child instanceof Node)\n return child\n // Nullish and booleans are ignored\n else if (child == null || typeof child === \"boolean\")\n return document.createComment(child + \"\")\n // Anything else is treated as text\n else\n return document.createTextNode(child + \"\")\n}\n\nconst isBruhIterable = (x: unknown): x is Iterable<BruhChild> =>\n x != null &&\n typeof x === \"object\" &&\n !(x instanceof Node) &&\n Symbol.iterator in x\n\n// Auto-swapping single reactive node\nconst reactiveTerminalBruhChildToNode: {\n <Child extends TerminalBruhChild>(child: Reactive<Child>): TerminalBruhChildOutputNode<Child>\n} = (child: Reactive<TerminalBruhChild>): any => {\n let node = terminalBruhChildToNode(child.value)\n\n const stopReacting = child.addReaction(() => {\n // Stop swapping if no longer possible\n if (!node.parentNode) {\n stopReacting()\n return\n }\n\n const oldNode = node as typeof node & ChildNode\n const child_ = child as Reactive<BruhChild>\n\n // If an iterable now, stop swapping, then switch to reactive iterable swapping\n if (isBruhIterable(child.value)) {\n stopReacting()\n oldNode.replaceWith(...reactiveIterableBruhChildToNodes(child_ as Reactive<Iterable<BruhChild>>))\n }\n // Normal swap\n else {\n node = terminalBruhChildToNode(child_.value as TerminalBruhChild)\n oldNode.replaceWith(node)\n }\n })\n\n return node\n}\n\n// Auto-swapping reactive iterable of nodes\nfunction * reactiveIterableBruhChildToNodes(child: Reactive<Iterable<BruhChild>>): IterableIterator<Node> {\n // Markers owned by the swapper here itself, so that\n // the values in the iterable can be swapped separately\n const first = document.createComment(\"[\")\n const last = document.createComment(\"]\")\n\n const stopReacting = child.addReaction(() => {\n // Stop swapping if there is no parent to swap within\n if (!first.parentNode) {\n stopReacting()\n return\n }\n\n // Make a range starting after the first marker\n const range = document.createRange()\n range.setStartAfter(first)\n\n // Normal swap, replacing content between the first and last markers\n if (isBruhIterable(child.value)) {\n const child_ = child as Reactive<Iterable<BruhChild>>\n\n range.setEndBefore(last)\n range.deleteContents()\n first.after(...bruhChildrenToNodes(child_.value))\n }\n // Switch to single swapping node by replacing everything\n else {\n const child_ = child as unknown as Reactive<TerminalBruhChild>\n\n stopReacting()\n range.setEndAfter(last)\n range.deleteContents()\n first.replaceWith(reactiveTerminalBruhChildToNode(child_))\n }\n })\n\n yield first\n yield* bruhChildrenToNodes(child.value)\n yield last\n}\n\n// Processes bruh children into an iterable of DOM nodes\n// Reactive values are automatically replaced, so the output must be placed into a parent node\n// before any top level (after flattening iterables) reactions run\nexport function * bruhChildrenToNodes(children: Iterable<BruhChild>): IterableIterator<Node> {\n const partiallyFlattened =\n Array.isArray(children)\n ? children.flat<BruhChild, number>(Infinity)\n : children\n\n for (const child of partiallyFlattened) {\n if (!isReactive(child)) {\n if (isBruhIterable(child))\n yield* bruhChildrenToNodes(child)\n else\n yield terminalBruhChildToNode(child)\n }\n else {\n if (isBruhIterable(child.value))\n yield* reactiveIterableBruhChildToNodes(child as Reactive<Iterable<BruhChild>>)\n else\n yield reactiveTerminalBruhChildToNode(child as Reactive<TerminalBruhChild>)\n }\n }\n}\n\n//#endregion\n\n//#region Reactive-aware element helper functions e.g. applyAttributes()\n\ntype ElementWithStyle = HTMLElement | SVGElement | MathMLElement\n\n// https://w3c.github.io/csswg-drafts/cssom/#the-elementcssinlinestyle-mixin\nconst isElementWithStyle = <T extends Element>(element: T): element is (T & ElementWithStyle) =>\n // @ts-ignore\n element.style instanceof CSSStyleDeclaration\n\n/**\n * Style attribute rules from an object with\n * potentially reactive and/or set as absent values\n */\nexport const applyStyles = <E extends ElementWithStyle>(\n element: E,\n styles: StylesToApply\n) => {\n for (const property in styles) {\n const property_ = property as keyof StylesToApply\n reactiveDo(styles[property_], value => {\n if (value != null && typeof value !== \"boolean\")\n element.style.setProperty (property, value + \"\")\n else\n element.style.removeProperty(property)\n })\n }\n}\n\n/**\n * Class list from an object mapping from\n * class names to potentially reactive booleans\n */\nexport const applyClasses = (\n element: Element,\n classes: ClassesToApply\n) => {\n for (const name in classes)\n reactiveDo(classes[name], value => {\n // without coercing to a boolean, `undefined` would toggle instead of forcing removal\n element.classList.toggle(name, value === true)\n })\n}\n\n/**\n * Attributes from an object with\n * potentially reactive and/or set as absent values\n */\nexport const applyAttributes = <\n Name extends string,\n NS extends Namespace = HTMLNamespace\n>(\n element: ElementType<Name, NS>,\n attributes: AttributesToApply<Name, NS>\n) => {\n for (const name in attributes)\n reactiveDo<ElementToAttributes<Name, NS>[typeof name] | LikelyAsBoolean>(attributes[name], value => {\n if (typeof value === \"boolean\")\n element.toggleAttribute(name, value)\n else if (value != null)\n element.setAttribute (name, value + \"\")\n else\n element.removeAttribute(name)\n })\n}\n\n//#endregion\n\n//#region t()\n\n// Text nodes\nexport const t = (textContent: MaybeReactive<LikelyAsString>) => {\n // Non-reactive values are just text nodes\n if (!isReactive(textContent))\n return document.createTextNode(textContent + \"\")\n\n // Reactive values auto-update the node's text content\n const node = document.createTextNode(textContent.value + \"\")\n textContent.addReaction(() => {\n node.textContent = textContent.value + \"\"\n })\n return node\n}\n\n//#endregion\n\n//#region JSX integration\n\ndeclare global {\n interface String {\n startsWith<Prefix extends string>(\n prefix: Prefix,\n position?: 0\n ): this is `${Prefix}${string}`\n }\n}\n\nexport const jsx: {\n /**\n * Create an HTML element\n */\n <\n Name extends HTMLTag\n >(\n name: Name,\n props: HTMLTagToBruhProps[Name],\n key?: string\n ):\n HTMLElementTagNameMap[Name]\n\n /**\n * Create an SVG element\n */\n <\n Name extends SVGTag\n >(\n name: Name,\n props: SVGTagToBruhProps[Name],\n key?: string\n ):\n SVGElementTagNameMap[Name]\n\n /**\n * Create a MathML element\n */\n <\n Name extends MathMLTag\n >(\n name: Name,\n props: MathMLTagToBruhProps[Name],\n key?: string\n ):\n MathMLElementTagNameMap[Name]\n\n /**\n * Create an element\n */\n <\n Name extends string,\n NS extends Namespace = HTMLNamespace\n >(\n name: Name,\n props: BruhProps<Name, NS>,\n key?: string\n ):\n ElementType<Name, NS>\n\n /**\n * Call a function as a JSX component\n */\n <\n Props extends Record<any, unknown>,\n Result\n >(\n component: (props: Props) => Result,\n props: Props,\n key?: string\n ): Result\n} =\n<\n Name extends string,\n NS extends Namespace = HTMLNamespace\n>\n(\n nameOrComponent: Name | Function,\n props_: Record<any, unknown>,\n key?: string\n) => {\n if (key !== undefined)\n props_.key = key\n\n // It must be a component, as bruh components are just functions\n // Due to JSX, this would mean a function with only one parameter - props\n // This object includes all of the normal props and a \"children\" key\n if (typeof nameOrComponent !== \"string\") {\n const component = nameOrComponent\n const props = props_ as Record<any, unknown>\n\n return component(props)\n }\n\n const name = nameOrComponent\n const props = props_ as BruhProps<Name, NS>\n\n // Extract explicit options from the bruh prop\n let options: BruhOptions = {}\n if (typeof props.bruh === \"object\" && !isReactive(props.bruh)) {\n options = props.bruh\n delete props.bruh\n }\n const { namespace } = options\n\n // Make an element with optional namespace\n const element =\n namespace\n ? document.createElementNS(namespace, name) as ElementType<Name, NS>\n : document.createElement ( name) as ElementType<Name, NS>\n\n if (\"children\" in props) {\n element.append(\n ...bruhChildrenToNodes(\n isBruhIterable(props.children)\n ? props.children\n : [props.children]\n )\n )\n\n delete props.children\n }\n\n // Apply overloaded props, if possible\n\n // Inline style object\n if (\n \"style\" in props &&\n props.style != null &&\n typeof props.style === \"object\" &&\n !isReactive(props.style) &&\n isElementWithStyle(element)\n ) {\n applyStyles(element, props.style)\n delete props.style\n }\n\n // Classes object\n if (\n \"class\" in props &&\n props.class != null &&\n typeof props.class === \"object\" &&\n !isReactive(props.class)\n ) {\n applyClasses(element, props.class)\n delete props.class\n }\n\n for (const name_ in props) {\n const name = name_ as string & (keyof typeof props)\n // Event listener functions\n if (typeof props[name] === \"function\" && name.startsWith(\"on\")) {\n element.addEventListener(name.slice(2), props[name] as any)\n delete props[name]\n }\n }\n\n // The rest of the props are attributes\n applyAttributes(element, props as AttributesToApply<Name, NS>)\n\n return element\n}\n\nexport const Fragment =\n <Children extends BruhChild>\n (props: { children: Children }) => props.children\n\n//#endregion\n\n\nexport class BruhText extends HTMLElement {\n static hydrated: { readonly [tag: string]: ReadonlySet<Text> } = {}\n\n constructor() {\n super()\n\n const textNode = document.createTextNode(this.textContent!)\n\n const tag = this.getAttribute(\"tag\")\n if (tag) {\n const set = (BruhText.hydrated as { [tag: string]: Set<Text> })[tag] ??= new Set()\n set.add(textNode)\n }\n\n this.replaceWith(textNode)\n }\n}\n\ncustomElements.define(\"bruh-text\", BruhText)\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"bruh-text\": BruhText\n }\n}\ndeclare module \"html-info\" {\n interface HTMLTagToAttributes {\n \"bruh-text\": {\n \"tag\"?: string\n }\n }\n}\n"],"names":["props","name"],"mappings":"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"}
1
+ {"version":3,"file":"browser.mjs","sources":["../src/dom/index.browser.mts"],"sourcesContent":["import { isReactive, reactiveDo, flat } from \"../reactive/index.mts\"\nimport type { Reactive, MaybeReactive, NestedReactive } from \"../reactive/index.mts\"\nimport type {\n PropertyWiseOr,\n LikelyAsString,\n LikelyAsAbsent,\n LikelyAsBoolean\n} from \"./types.mts\"\nimport type { PropertiesHyphen as Styles } from \"csstype\"\nimport type {\n ElementType,\n\n HTMLTag,\n SVGTag,\n MathMLTag,\n\n Namespace,\n HTMLNamespace,\n SVGNamespace,\n MathMLNamespace,\n\n ElementToEventMap,\n ElementToAttributes\n} from \"html-info\"\n\nexport type TerminalBruhChild =\n | Node\n | LikelyAsString\n | LikelyAsAbsent\n\nexport type TerminalBruhChildOutputNode<Child extends TerminalBruhChild>\n = Child extends Node\n ? Child\n : Child extends LikelyAsString\n ? Text\n : Child extends LikelyAsAbsent\n ? Comment\n : Node\n\nexport type BruhChild =\n | MaybeReactive<TerminalBruhChild>\n | Iterable<BruhChild>\n | NestedReactive<TerminalBruhChild | Iterable<BruhChild>>\n\nexport type StylesToApply = {\n [Property in keyof Styles]: MaybeReactive<Styles[Property] | LikelyAsAbsent>\n}\n\nexport interface ClassesToApply {\n [className: string]: MaybeReactive<LikelyAsBoolean>\n}\n\nexport type AttributesToApply<\n Name extends string,\n NS extends Namespace = HTMLNamespace\n> = {\n [Attribute in keyof ElementToAttributes<Name, NS>]?:\n MaybeReactive<ElementToAttributes<Name, NS>[Attribute] | LikelyAsBoolean>\n}\n\ntype EventMapToListenerProps<EventMap> = {\n [E in ((keyof EventMap) & string) as `on${E}`]:\n (event: EventMap[E]) => void\n}\n\ntype AttributeMapToProps<AttributeMap> = {\n [Attribute in keyof AttributeMap]:\n MaybeReactive<AttributeMap[Attribute] | LikelyAsBoolean>\n | ( Attribute extends \"style\"\n ? StylesToApply\n : Attribute extends \"class\"\n ? ClassesToApply\n : never\n )\n}\n\nexport interface BruhOptions {\n namespace?: Namespace\n}\n\ninterface BaseBruhProps {\n bruh?: BruhOptions,\n children?: BruhChild\n}\n\nexport type BruhProps<\n Name extends string,\n NS extends string = HTMLNamespace\n>\n = BaseBruhProps\n & (\n NS extends HTMLNamespace\n ? { bruh?: { namespace?: HTMLNamespace } }\n : { bruh: { namespace: NS } }\n )\n & Partial<\n EventMapToListenerProps<ElementToEventMap<Name, NS>> &\n AttributeMapToProps<ElementToAttributes<Name, NS>>\n >\n\nexport type HTMLTagToBruhProps = { [Name in HTMLTag]: BruhProps<Name, HTMLNamespace> }\nexport type SVGTagToBruhProps = { [Name in SVGTag]: BruhProps<Name, SVGNamespace> }\nexport type MathMLTagToBruhProps = { [Name in MathMLTag]: BruhProps<Name, MathMLNamespace> }\n\nexport type TagToBruhProps =\n PropertyWiseOr<\n HTMLTagToBruhProps,\n PropertyWiseOr<\n SVGTagToBruhProps,\n MathMLTagToBruhProps\n >\n >\n\nexport namespace JSX {\n /** @see https://www.typescriptlang.org/docs/handbook/jsx.html#children-type-checking */\n export interface ElementChildrenAttribute { children: {} }\n\n /** @see https://www.typescriptlang.org/docs/handbook/jsx.html#attribute-type-checking */\n export interface IntrinsicElements extends TagToBruhProps {}\n\n export type Element = any\n\n export type ElementType = HTMLTag | SVGTag | MathMLTag | ((props: any) => Element)\n}\n\n// Coerces input into a DOM node, if it isn't already one\nconst terminalBruhChildToNode: {\n <Child extends TerminalBruhChild>(child: Child): TerminalBruhChildOutputNode<Child>\n} = (child: TerminalBruhChild): any => {\n // Existing DOM nodes are untouched\n if (child instanceof Node)\n return child\n // Nullish and booleans are ignored\n else if (child == null || typeof child === \"boolean\")\n return document.createComment(child + \"\")\n // Anything else is treated as text\n else\n return document.createTextNode(child + \"\")\n}\n\nconst isBruhIterable = (x: unknown): x is Iterable<BruhChild> =>\n x != null &&\n typeof x === \"object\" &&\n !(x instanceof Node) &&\n Symbol.iterator in x\n\nexport const ownedReactivesSymbol = Symbol.for(\"bruh owned reactives\")\n\ndeclare global {\n interface Node {\n [ownedReactivesSymbol]?: Set<Reactive<unknown>>\n }\n}\n\n// Auto-swapping single reactive node\nconst reactiveTerminalBruhChildToNode: {\n <Child extends TerminalBruhChild>(child: Reactive<Child>): TerminalBruhChildOutputNode<Child>\n} = (child: Reactive<TerminalBruhChild>): any => {\n const node = terminalBruhChildToNode(child.value)\n\n let ownedReactives = node[ownedReactivesSymbol] ??= new Set()\n ownedReactives.add(child)\n\n let nodeWeakRef = new WeakRef(node)\n\n const stopReacting = child.addReaction(() => {\n const node = nodeWeakRef.deref()\n\n // Stop swapping if no longer possible\n if (!node?.parentNode) {\n stopReacting()\n return\n }\n\n const oldNode = node as typeof node & ChildNode\n const child_ = child as Reactive<BruhChild>\n\n ownedReactives.delete(child)\n\n // If an iterable now, stop swapping, then switch to reactive iterable swapping\n if (isBruhIterable(child.value)) {\n stopReacting()\n const nodes = reactiveIterableBruhChildToNodes(child_ as Reactive<Iterable<BruhChild>>)\n oldNode.replaceWith(...nodes)\n }\n // Normal swap\n else {\n const node = terminalBruhChildToNode(child.value)\n ownedReactives = node[ownedReactivesSymbol] ??= new Set()\n ownedReactives.add(child)\n nodeWeakRef = new WeakRef(node)\n oldNode.replaceWith(node)\n }\n })\n\n return node\n}\n\n// Auto-swapping reactive iterable of nodes\nfunction * reactiveIterableBruhChildToNodes(child: Reactive<Iterable<BruhChild>>): IterableIterator<Node> {\n // Markers owned by the swapper here itself, so that\n // the values in the iterable can be swapped separately\n const first = document.createComment(\"[\")\n const last = document.createComment(\"]\")\n\n let ownedReactives = first[ownedReactivesSymbol] ??= new Set()\n ownedReactives.add(child)\n\n let firstWeakRef = new WeakRef(first)\n let lastWeakRef = new WeakRef(last)\n\n const stopReacting = child.addReaction(() => {\n const first = firstWeakRef.deref()\n const last = lastWeakRef.deref()\n\n // Stop swapping if there is no parent to swap within\n if (!first?.parentNode || !last?.parentNode) {\n stopReacting()\n return\n }\n\n const child_ = child as Reactive<BruhChild>\n\n // Make a range starting after the first marker\n const range = document.createRange()\n range.setStartAfter(first)\n\n // Normal swap, replacing content between the first and last markers\n if (isBruhIterable(child.value)) {\n range.setEndBefore(last)\n range.deleteContents()\n first.after(...bruhChildrenToNodes(child.value))\n }\n // Switch to single swapping node by replacing everything\n else {\n ownedReactives.delete(child)\n\n stopReacting()\n range.setEndAfter(last)\n range.deleteContents()\n first.replaceWith(reactiveTerminalBruhChildToNode(child_ as Reactive<TerminalBruhChild>))\n }\n })\n\n yield first\n yield* bruhChildrenToNodes(child.value)\n yield last\n}\n\n// Processes bruh children into an iterable of DOM nodes\n// Reactive values are automatically replaced, so the output must be placed into a parent node\n// before any top level (after flattening iterables) reactions run\nexport function * bruhChildrenToNodes(children: Iterable<BruhChild>): IterableIterator<Node> {\n const partiallyFlattened =\n Array.isArray(children)\n ? children.flat<BruhChild, number>(Infinity)\n : children\n\n for (const child of partiallyFlattened) {\n if (!isReactive(child)) {\n if (isBruhIterable(child))\n yield* bruhChildrenToNodes(child)\n else\n yield terminalBruhChildToNode(child)\n }\n else {\n const flattened = flat(child)\n\n if (isBruhIterable(flattened.value))\n yield* reactiveIterableBruhChildToNodes(flattened as Reactive<Iterable<BruhChild>>)\n else\n yield reactiveTerminalBruhChildToNode(flattened as Reactive<TerminalBruhChild>)\n }\n }\n}\n\n//#endregion\n\n//#region Reactive-aware element helper functions e.g. applyAttributes()\n\ntype ElementWithStyle = HTMLElement | SVGElement | MathMLElement\n\n// https://w3c.github.io/csswg-drafts/cssom/#the-elementcssinlinestyle-mixin\nconst isElementWithStyle = <T extends Element>(element: T): element is (T & ElementWithStyle) =>\n // @ts-ignore\n element.style instanceof CSSStyleDeclaration\n\n/**\n * Style attribute rules from an object with\n * potentially reactive and/or set as absent values\n */\nexport const applyStyles = <E extends ElementWithStyle>(\n element: E,\n styles: StylesToApply\n) => {\n const ownedReactives = element[ownedReactivesSymbol] ??= new Set()\n let elementWeakRef = new WeakRef(element)\n\n for (const property in styles) {\n const property_ = property as keyof StylesToApply\n const maybeReactive = styles[property_]\n if (isReactive(maybeReactive))\n ownedReactives.add(maybeReactive)\n\n reactiveDo(maybeReactive, value => {\n const element = elementWeakRef.deref()\n if (!element)\n return\n\n if (value != null && typeof value !== \"boolean\")\n element.style.setProperty (property, value + \"\")\n else\n element.style.removeProperty(property)\n })\n }\n}\n\n/**\n * Class list from an object mapping from\n * class names to potentially reactive booleans\n */\nexport const applyClasses = (\n element: Element,\n classes: ClassesToApply\n) => {\n const ownedReactives = element[ownedReactivesSymbol] ??= new Set()\n let elementWeakRef = new WeakRef(element)\n\n for (const name in classes) {\n const maybeReactive = classes[name]\n if (isReactive(maybeReactive))\n ownedReactives.add(maybeReactive)\n\n reactiveDo(maybeReactive, value => {\n const element = elementWeakRef.deref()\n if (!element)\n return\n\n // without coercing to a boolean, `undefined` would toggle instead of forcing removal\n element.classList.toggle(name, value === true)\n })\n }\n}\n\n/**\n * Attributes from an object with\n * potentially reactive and/or set as absent values\n */\nexport const applyAttributes = <\n Name extends string,\n NS extends Namespace = HTMLNamespace\n>(\n element: ElementType<Name, NS>,\n attributes: AttributesToApply<Name, NS>\n) => {\n const ownedReactives = element[ownedReactivesSymbol] ??= new Set()\n let elementWeakRef = new WeakRef(element)\n\n for (const name in attributes) {\n const maybeReactive = attributes[name]\n if (isReactive(maybeReactive))\n ownedReactives.add(maybeReactive)\n\n reactiveDo<ElementToAttributes<Name, NS>[typeof name] | LikelyAsBoolean>(attributes[name], value => {\n const element = elementWeakRef.deref()\n if (!element)\n return\n\n if (typeof value === \"boolean\")\n element.toggleAttribute(name, value)\n else if (value != null)\n element.setAttribute (name, value + \"\")\n else\n element.removeAttribute(name)\n })\n }\n}\n\n//#endregion\n\n//#region t()\n\n// Text nodes\nexport const t = (textContent: MaybeReactive<LikelyAsString>) => {\n // Non-reactive values are just text nodes\n if (!isReactive(textContent))\n return document.createTextNode(textContent + \"\")\n\n // Reactive values auto-update the node's text content\n const node = document.createTextNode(textContent.value + \"\")\n textContent.addReaction(() => {\n node.textContent = textContent.value + \"\"\n })\n return node\n}\n\n//#endregion\n\n//#region JSX integration\n\ndeclare global {\n interface String {\n startsWith<Prefix extends string>(\n prefix: Prefix,\n position?: 0\n ): this is `${Prefix}${string}`\n }\n}\n\nexport const jsx: {\n /**\n * Create an HTML element\n */\n <\n Name extends HTMLTag\n >(\n name: Name,\n props: HTMLTagToBruhProps[Name],\n key?: string\n ):\n HTMLElementTagNameMap[Name]\n\n /**\n * Create an SVG element\n */\n <\n Name extends SVGTag\n >(\n name: Name,\n props: SVGTagToBruhProps[Name],\n key?: string\n ):\n SVGElementTagNameMap[Name]\n\n /**\n * Create a MathML element\n */\n <\n Name extends MathMLTag\n >(\n name: Name,\n props: MathMLTagToBruhProps[Name],\n key?: string\n ):\n MathMLElementTagNameMap[Name]\n\n /**\n * Create an element\n */\n <\n Name extends string,\n NS extends Namespace = HTMLNamespace\n >(\n name: Name,\n props: BruhProps<Name, NS>,\n key?: string\n ):\n ElementType<Name, NS>\n\n /**\n * Call a function as a JSX component\n */\n <\n Props extends Record<any, unknown>,\n Result\n >(\n component: (props: Props) => Result,\n props: Props,\n key?: string\n ): Result\n} =\n<\n Name extends string,\n NS extends Namespace = HTMLNamespace\n>\n(\n nameOrComponent: Name | Function,\n props_: Record<any, unknown>,\n key?: string\n) => {\n if (key !== undefined)\n props_.key = key\n\n // It must be a component, as bruh components are just functions\n // Due to JSX, this would mean a function with only one parameter - props\n // This object includes all of the normal props and a \"children\" key\n if (typeof nameOrComponent !== \"string\") {\n const component = nameOrComponent\n const props = props_ as Record<any, unknown>\n\n return component(props)\n }\n\n const name = nameOrComponent\n const props = props_ as BruhProps<Name, NS>\n\n // Extract explicit options from the bruh prop\n let options: BruhOptions = {}\n if (typeof props.bruh === \"object\" && !isReactive(props.bruh)) {\n options = props.bruh\n delete props.bruh\n }\n const { namespace } = options\n\n // Make an element with optional namespace\n const element =\n namespace\n ? document.createElementNS(namespace, name) as ElementType<Name, NS>\n : document.createElement ( name) as ElementType<Name, NS>\n\n if (\"children\" in props) {\n element.append(\n ...bruhChildrenToNodes(\n isBruhIterable(props.children)\n ? props.children\n : [props.children]\n )\n )\n\n delete props.children\n }\n\n // Apply overloaded props, if possible\n\n // Inline style object\n if (\n \"style\" in props &&\n props.style != null &&\n typeof props.style === \"object\" &&\n !isReactive(props.style) &&\n isElementWithStyle(element)\n ) {\n applyStyles(element, props.style)\n delete props.style\n }\n\n // Classes object\n if (\n \"class\" in props &&\n props.class != null &&\n typeof props.class === \"object\" &&\n !isReactive(props.class)\n ) {\n applyClasses(element, props.class)\n delete props.class\n }\n\n for (const name_ in props) {\n const name = name_ as string & (keyof typeof props)\n // Event listener functions\n if (typeof props[name] === \"function\" && name.startsWith(\"on\")) {\n element.addEventListener(name.slice(2), props[name] as any)\n delete props[name]\n }\n }\n\n // The rest of the props are attributes\n applyAttributes(element, props as AttributesToApply<Name, NS>)\n\n return element\n}\n\nexport const Fragment =\n <Children extends BruhChild>\n (props: { children: Children }) => props.children\n\n//#endregion\n\n\nexport class BruhText extends HTMLElement {\n static hydrated: { readonly [tag: string]: ReadonlySet<Text> } = {}\n\n constructor() {\n super()\n\n const textNode = document.createTextNode(this.textContent!)\n\n const tag = this.getAttribute(\"tag\")\n if (tag) {\n const set = (BruhText.hydrated as { [tag: string]: Set<Text> })[tag] ??= new Set()\n set.add(textNode)\n }\n\n this.replaceWith(textNode)\n }\n}\n\ncustomElements.define(\"bruh-text\", BruhText)\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"bruh-text\": BruhText\n }\n}\ndeclare module \"html-info\" {\n interface HTMLTagToAttributes {\n \"bruh-text\": {\n \"tag\"?: string\n }\n }\n}\n"],"names":["node","first","last","element","props","name"],"mappings":"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"}
package/dist/reactive.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ import { attempt } from './utils.mjs';
2
+
1
3
  const isReactiveSymbol = /* @__PURE__ */ Symbol.for("bruh reactive");
2
4
  const isReactive = (x) => (
3
5
  // @ts-ignore
@@ -18,10 +20,7 @@ class SimpleReactive {
18
20
  return;
19
21
  this.#value = newValue;
20
22
  for (const reaction of this.#reactions)
21
- try {
22
- reaction();
23
- } catch {
24
- }
23
+ attempt(reaction);
25
24
  }
26
25
  /**
27
26
  * @param reaction called every time that the value changes
@@ -41,6 +40,7 @@ class FunctionalReactive {
41
40
  // For derived nodes, this is the derivation function
42
41
  // @ts-ignore
43
42
  #f;
43
+ isEqual;
44
44
  // Source nodes are 0 deep in the derivation graph
45
45
  // This is for topological sort
46
46
  #depth = 0;
@@ -53,21 +53,21 @@ class FunctionalReactive {
53
53
  static #derivativesQueue = [];
54
54
  // A queue of reactions to run after the graph is fully updated
55
55
  static #reactionsQueue = [];
56
- constructor(x, f) {
57
- if (!f) {
56
+ constructor(xOrDependencies, optionsOrF, options) {
57
+ if (typeof optionsOrF !== "function") {
58
58
  const this_2 = this;
59
- const value = x;
59
+ const value = xOrDependencies;
60
+ const options2 = optionsOrF;
60
61
  this_2.#value = value;
62
+ this_2.isEqual = options2?.isEqual;
61
63
  return;
62
64
  }
63
65
  const this_ = this;
64
- const dependencies = x;
65
- try {
66
- this_.#value = f();
67
- } catch (e) {
68
- this_.#value = e;
69
- }
66
+ const dependencies = xOrDependencies;
67
+ const f = optionsOrF;
68
+ this_.#value = attempt(f);
70
69
  this_.#f = f;
70
+ this_.isEqual = options?.isEqual;
71
71
  this_.#depth = Math.max(0, ...dependencies.map((dependency) => dependency.#depth)) + 1;
72
72
  dependencies.forEach((dependency) => dependency.#derivatives.add(this_.#weakRef));
73
73
  }
@@ -87,7 +87,8 @@ class FunctionalReactive {
87
87
  if (this.#depth !== 0)
88
88
  return;
89
89
  const this_ = this;
90
- if (newValue === this.#value) {
90
+ const isEqual = this.isEqual ? this.isEqual(newValue, this.#value) : newValue === this.#value;
91
+ if (isEqual) {
91
92
  FunctionalReactive.#settersQueue.delete(this_);
92
93
  return;
93
94
  }
@@ -105,7 +106,8 @@ class FunctionalReactive {
105
106
  }
106
107
  // Apply an update for a node and queue its derivatives if it actually changed
107
108
  #applyUpdate(newValue) {
108
- if (newValue === this.#value)
109
+ const isEqual = this.isEqual ? this.isEqual(newValue, this.#value) : newValue === this.#value;
110
+ if (isEqual)
109
111
  return;
110
112
  this.#value = newValue;
111
113
  FunctionalReactive.#reactionsQueue.push(...this.#reactions);
@@ -130,23 +132,20 @@ class FunctionalReactive {
130
132
  FunctionalReactive.#settersQueue.clear();
131
133
  for (const depthSet of FunctionalReactive.#derivativesQueue) if (depthSet)
132
134
  for (const derivative of depthSet)
133
- try {
134
- derivative.#applyUpdate(derivative.#f());
135
- } catch (e) {
136
- derivative.#applyUpdate(e);
137
- }
135
+ derivative.#applyUpdate(
136
+ attempt(
137
+ () => derivative.#f()
138
+ )
139
+ );
138
140
  FunctionalReactive.#derivativesQueue.length = 0;
139
141
  for (const reaction of FunctionalReactive.#reactionsQueue)
140
- try {
141
- reaction();
142
- } catch {
143
- }
142
+ attempt(reaction);
144
143
  FunctionalReactive.#reactionsQueue.length = 0;
145
144
  }
146
145
  }
147
- const r = (x, f) => (
146
+ const r = (xOrDependencies, optionsOrF, options) => (
148
147
  // @ts-ignore
149
- new FunctionalReactive(x, f)
148
+ new FunctionalReactive(xOrDependencies, optionsOrF, options)
150
149
  );
151
150
  const reactiveDo = (x, f) => {
152
151
  if (isReactive(x)) {
@@ -155,6 +154,39 @@ const reactiveDo = (x, f) => {
155
154
  }
156
155
  f(x);
157
156
  };
157
+ const flat = (source) => {
158
+ const chain = [];
159
+ const reactive = new FunctionalReactive(void 0);
160
+ const reaction = () => {
161
+ let lastInCommon = source;
162
+ let i = 0;
163
+ while (lastInCommon.value === chain[i + 1]?.reactive) {
164
+ lastInCommon = lastInCommon.value;
165
+ i++;
166
+ }
167
+ if (i + 1 < chain.length) {
168
+ for (let j = i + 1; j < chain.length; j++)
169
+ chain[j].stopReacting();
170
+ chain.length = i;
171
+ }
172
+ updateChain(lastInCommon.value);
173
+ };
174
+ const updateChain = (newStart) => {
175
+ while (isReactive(newStart)) {
176
+ chain.push({
177
+ reactive: newStart,
178
+ stopReacting: newStart.addReaction(reaction)
179
+ });
180
+ newStart = newStart.value;
181
+ }
182
+ const innerMost = chain[chain.length - 1].reactive;
183
+ reactive.isEqual = innerMost.isEqual;
184
+ reactive.value = innerMost.value;
185
+ FunctionalReactive.applyUpdates();
186
+ };
187
+ updateChain(source);
188
+ return reactive;
189
+ };
158
190
 
159
- export { FunctionalReactive, SimpleReactive, isReactive, isReactiveSymbol, r, reactiveDo };
191
+ export { FunctionalReactive, SimpleReactive, flat, isReactive, isReactiveSymbol, r, reactiveDo };
160
192
  //# sourceMappingURL=reactive.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"reactive.mjs","sources":["../src/reactive/index.mts"],"sourcesContent":["export const isReactiveSymbol = Symbol.for(\"bruh reactive\")\n\nexport const isReactive = (x: unknown): x is Reactive<unknown> =>\n // @ts-ignore\n x?.[isReactiveSymbol] === true\n\ntype Reaction = () => void\ntype StopReacting = () => void\n\nexport interface Reactive<T> {\n [isReactiveSymbol]: true\n\n value: T\n\n addReaction(reaction: Reaction): StopReacting\n}\n\nexport type Unreactive<T> = Exclude<T, { [isReactiveSymbol]: true }>\n\nexport type MaybeReactive<T> = Reactive<T> | Unreactive<T>\n\n/**\n * A super simple and performant reactive value implementation\n */\nexport class SimpleReactive<T> implements Reactive<T> {\n [isReactiveSymbol] = true as const\n\n #value: T\n #reactions = new Set<Reaction>()\n\n constructor(value: T) {\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 try { reaction() } catch {}\n }\n\n /**\n * @param reaction called every time that the value changes\n * @returns a function that stops the reactions\n */\n addReaction(reaction: Reaction) {\n this.#reactions.add(reaction)\n\n return () =>\n this.#reactions.delete(reaction)\n }\n}\n\nexport type SourceNode<T> = FunctionalReactive<T, \"source\">\nexport type DerivativeNode<T> = FunctionalReactive<T, \"derivative\">\n\n/**\n * A reactive implementation for building functional reactive graphs.\n * Ensures state consistency, minimal node updates, and transparent update batching.\n */\nexport class FunctionalReactive<T, U extends \"source\" | \"derivative\" = any> implements Reactive<T> {\n [isReactiveSymbol] = true as const\n\n #weakRef = new WeakRef(this)\n\n // @ts-ignore\n #value: T\n #reactions = new Set<Reaction>()\n\n // For derived nodes, this is the derivation function\n // @ts-ignore\n #f:\n U extends \"derivative\"\n ? () => T\n : never\n\n // Source nodes are 0 deep in the derivation graph\n // This is for topological sort\n #depth:\n U extends \"source\"\n ? 0\n : number // natural, > 0\n = 0\n\n // All nodes have a set of derivatives that update when the node changes\n #derivatives = new Set<WeakRef<DerivativeNode<unknown>>>()\n\n // Keep track of all the pending changes from the value setter\n static #settersQueue = new Map<SourceNode<unknown>, unknown>()\n\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: Array<Set<DerivativeNode<unknown>> | undefined> = []\n\n // A queue of reactions to run after the graph is fully updated\n static #reactionsQueue: Array<Reaction> = []\n\n constructor(value: T)\n constructor(\n dependencies: ReadonlyArray<FunctionalReactive<unknown>>,\n f: () => T\n )\n constructor(\n x: T | ReadonlyArray<FunctionalReactive<unknown>>,\n f?: undefined | (() => T)\n ) {\n // No derivation function means this is a source node\n if (!f) {\n const this_ = this as SourceNode<T>\n const value = x as T\n\n this_.#value = value\n return\n }\n\n // Derived node\n const this_ = this as DerivativeNode<T>\n const dependencies = x as ReadonlyArray<FunctionalReactive<unknown>>\n\n try {\n this_.#value = f()\n }\n catch (e: any) {\n this_.#value = e\n }\n this_.#f = f\n\n this_.#depth = Math.max(0, ...dependencies.map(dependency => dependency.#depth)) + 1\n\n dependencies.forEach(dependency => dependency.#derivatives.add(this_.#weakRef))\n }\n\n get value(): T {\n // If there are any pending updates\n if (FunctionalReactive.#settersQueue.size) {\n // If this is a source node that was updated, just return that\n // new value without actually updating any derived nodes yet\n if (this.#depth === 0) {\n const this_ = this as SourceNode<T>\n if (FunctionalReactive.#settersQueue.has(this_))\n return FunctionalReactive.#settersQueue.get(this_) as T\n }\n // Heuristic quick invalidation for derived nodes\n // Apply updates now, it's ok that there's already a microtask queued for this\n else {\n FunctionalReactive.applyUpdates()\n }\n }\n\n return this.#value\n }\n\n set value(newValue) {\n // Only allow source nodes to be directly updated\n if (this.#depth !== 0)\n return\n\n const this_ = this as SourceNode<T>\n\n if (newValue === this.#value) {\n FunctionalReactive.#settersQueue.delete(this_)\n return\n }\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 /**\n * @param reaction called every time that the value changes\n * @returns a function that stops the reactions\n */\n addReaction(reaction: 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: T) {\n if (newValue === this.#value)\n return\n\n this.#value = newValue\n FunctionalReactive.#reactionsQueue.push(...this.#reactions)\n\n this.#derivatives.forEach(weakRef => {\n const derivative = weakRef.deref()\n if (!derivative) {\n this.#derivatives.delete(weakRef)\n return\n }\n\n const depthSet = FunctionalReactive.#derivativesQueue[derivative.#depth] ??= new Set()\n depthSet.add(derivative)\n })\n }\n\n /**\n * Apply pending updates from actually changed source nodes\n */\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 try {\n derivative.#applyUpdate(derivative.#f())\n }\n catch (e) {\n derivative.#applyUpdate(e)\n }\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 try { reaction() } catch {}\n FunctionalReactive.#reactionsQueue.length = 0\n }\n}\n\ntype R = {\n /**\n * An initially undefined source node\n */\n <T>(): SourceNode<T | undefined>\n\n /**\n * A source node\n */\n <T>(value: T): SourceNode<T>\n\n /**\n * A derived node\n */\n <T>(\n dependencies: ReadonlyArray<FunctionalReactive<unknown>>,\n f: () => T\n ): DerivativeNode<T>\n}\n/**\n * A convenient wrapper for FunctionalReactive\n */\nexport const r: R = <T extends unknown>(\n x?: T | ReadonlyArray<FunctionalReactive<unknown>>,\n f?: undefined | (() => T)\n) =>\n // @ts-ignore\n new FunctionalReactive(x, f)\n\ntype ReactiveDo = {\n /**\n * Calls the given function with the reactive's value whenever it changes\n * @returns a function that stops the reactions\n */\n <T>(\n reactive: Reactive<T>,\n f: (value: T) => unknown\n ): StopReacting\n\n /**\n * Calls the given function with the value once\n */\n <T>(\n value: Unreactive<T>,\n f: (value: Unreactive<T>) => unknown\n ): undefined\n\n /**\n * If the value is not reactive, it calls the given function with that value once.\n * If it is a reactive value, it calls the given function with the reactive's value whenever it changes.\n * @returns a function that stops the reactions, if the value is reactive\n */\n <T>(\n x: MaybeReactive<T>,\n f: (value: T) => unknown\n ): StopReacting | undefined\n}\n/**\n * Do something with a value, updating if it is reactive\n */\nexport const reactiveDo: ReactiveDo = <T extends unknown>(\n x: MaybeReactive<T>,\n f: (value: T) => unknown\n): any => {\n if (isReactive(x)) {\n f(x.value)\n return x.addReaction(() => f(x.value))\n }\n\n f(x)\n}\n"],"names":["this_"],"mappings":"AAAa,MAAA,gBAAA,mBAA0B,MAAA,CAAA,GAAA,CAAI,eAAe;AAEnD,MAAM,aAAa,CAAC,CAAA;AAAA;AAAA,EAEzB,CAAA,GAAI,gBAAgB,CAAM,KAAA;AAAA;AAoBrB,MAAM,cAAyC,CAAA;AAAA,EACpD,CAAC,gBAAgB,IAAI,IAAA;AAAA,EAErB,MAAA;AAAA,EACA,UAAA,uBAAiB,GAAc,EAAA;AAAA,EAE/B,YAAY,KAAU,EAAA;AACpB,IAAA,IAAA,CAAK,MAAS,GAAA,KAAA;AAAA;AAChB,EAEA,IAAI,KAAQ,GAAA;AACV,IAAA,OAAO,IAAK,CAAA,MAAA;AAAA;AACd,EAEA,IAAI,MAAM,QAAU,EAAA;AAClB,IAAA,IAAI,aAAa,IAAK,CAAA,MAAA;AACpB,MAAA;AAEF,IAAA,IAAA,CAAK,MAAS,GAAA,QAAA;AACd,IAAA,KAAA,MAAW,YAAY,IAAK,CAAA,UAAA;AAC1B,MAAI,IAAA;AAAE,QAAS,QAAA,EAAA;AAAA,OAAU,CAAA,MAAA;AAAA;AAAC;AAC9B;AAAA;AAAA;AAAA;AAAA,EAMA,YAAY,QAAoB,EAAA;AAC9B,IAAK,IAAA,CAAA,UAAA,CAAW,IAAI,QAAQ,CAAA;AAE5B,IAAA,OAAO,MACL,IAAA,CAAK,UAAW,CAAA,MAAA,CAAO,QAAQ,CAAA;AAAA;AAErC;AASO,MAAM,kBAAsF,CAAA;AAAA,EACjG,CAAC,gBAAgB,IAAI,IAAA;AAAA,EAErB,QAAA,GAAW,IAAI,OAAA,CAAQ,IAAI,CAAA;AAAA;AAAA,EAG3B,MAAA;AAAA,EACA,UAAA,uBAAiB,GAAc,EAAA;AAAA;AAAA;AAAA,EAI/B,EAAA;AAAA;AAAA;AAAA,EAOA,MAII,GAAA,CAAA;AAAA;AAAA,EAGJ,YAAA,uBAAmB,GAAsC,EAAA;AAAA;AAAA,EAGzD,OAAO,aAAgB,mBAAA,IAAI,GAAkC,EAAA;AAAA;AAAA;AAAA,EAI7D,OAAO,oBAAqE,EAAC;AAAA;AAAA,EAG7E,OAAO,kBAAmC,EAAC;AAAA,EAO3C,WAAA,CACE,GACA,CACA,EAAA;AAEA,IAAA,IAAI,CAAC,CAAG,EAAA;AACN,MAAA,MAAMA,MAAQ,GAAA,IAAA;AACd,MAAA,MAAM,KAAQ,GAAA,CAAA;AAEd,MAAAA,OAAM,MAAS,GAAA,KAAA;AACf,MAAA;AAAA;AAIF,IAAA,MAAM,KAAQ,GAAA,IAAA;AACd,IAAA,MAAM,YAAe,GAAA,CAAA;AAErB,IAAI,IAAA;AACF,MAAA,KAAA,CAAM,SAAS,CAAE,EAAA;AAAA,aAEZ,CAAQ,EAAA;AACb,MAAA,KAAA,CAAM,MAAS,GAAA,CAAA;AAAA;AAEjB,IAAA,KAAA,CAAM,EAAK,GAAA,CAAA;AAEX,IAAM,KAAA,CAAA,MAAA,GAAS,IAAK,CAAA,GAAA,CAAI,CAAG,EAAA,GAAG,YAAa,CAAA,GAAA,CAAI,CAAc,UAAA,KAAA,UAAA,CAAW,MAAM,CAAC,CAAI,GAAA,CAAA;AAEnF,IAAA,YAAA,CAAa,QAAQ,CAAc,UAAA,KAAA,UAAA,CAAW,aAAa,GAAI,CAAA,KAAA,CAAM,QAAQ,CAAC,CAAA;AAAA;AAChF,EAEA,IAAI,KAAW,GAAA;AAEb,IAAI,IAAA,kBAAA,CAAmB,cAAc,IAAM,EAAA;AAGzC,MAAI,IAAA,IAAA,CAAK,WAAW,CAAG,EAAA;AACrB,QAAA,MAAM,KAAQ,GAAA,IAAA;AACd,QAAI,IAAA,kBAAA,CAAmB,aAAc,CAAA,GAAA,CAAI,KAAK,CAAA;AAC5C,UAAO,OAAA,kBAAA,CAAmB,aAAc,CAAA,GAAA,CAAI,KAAK,CAAA;AAAA,OAIhD,MAAA;AACH,QAAA,kBAAA,CAAmB,YAAa,EAAA;AAAA;AAClC;AAGF,IAAA,OAAO,IAAK,CAAA,MAAA;AAAA;AACd,EAEA,IAAI,MAAM,QAAU,EAAA;AAElB,IAAA,IAAI,KAAK,MAAW,KAAA,CAAA;AAClB,MAAA;AAEF,IAAA,MAAM,KAAQ,GAAA,IAAA;AAEd,IAAI,IAAA,QAAA,KAAa,KAAK,MAAQ,EAAA;AAC5B,MAAmB,kBAAA,CAAA,aAAA,CAAc,OAAO,KAAK,CAAA;AAC7C,MAAA;AAAA;AAIF,IAAI,IAAA,CAAC,mBAAmB,aAAc,CAAA,IAAA;AACpC,MAAA,cAAA,CAAe,mBAAmB,YAAY,CAAA;AAEhD,IAAmB,kBAAA,CAAA,aAAA,CAAc,GAAI,CAAA,KAAA,EAAO,QAAQ,CAAA;AAAA;AACtD;AAAA;AAAA;AAAA;AAAA,EAMA,YAAY,QAAoB,EAAA;AAC9B,IAAK,IAAA,CAAA,UAAA,CAAW,IAAI,QAAQ,CAAA;AAE5B,IAAA,OAAO,MACL,IAAA,CAAK,UAAW,CAAA,MAAA,CAAO,QAAQ,CAAA;AAAA;AACnC;AAAA,EAGA,aAAa,QAAa,EAAA;AACxB,IAAA,IAAI,aAAa,IAAK,CAAA,MAAA;AACpB,MAAA;AAEF,IAAA,IAAA,CAAK,MAAS,GAAA,QAAA;AACd,IAAA,kBAAA,CAAmB,eAAgB,CAAA,IAAA,CAAK,GAAG,IAAA,CAAK,UAAU,CAAA;AAE1D,IAAK,IAAA,CAAA,YAAA,CAAa,QAAQ,CAAW,OAAA,KAAA;AACnC,MAAM,MAAA,UAAA,GAAa,QAAQ,KAAM,EAAA;AACjC,MAAA,IAAI,CAAC,UAAY,EAAA;AACf,QAAK,IAAA,CAAA,YAAA,CAAa,OAAO,OAAO,CAAA;AAChC,QAAA;AAAA;AAGF,MAAA,MAAM,WAAW,kBAAmB,CAAA,iBAAA,CAAkB,WAAW,MAAM,CAAA,yBAAU,GAAI,EAAA;AACrF,MAAA,QAAA,CAAS,IAAI,UAAU,CAAA;AAAA,KACxB,CAAA;AAAA;AACH;AAAA;AAAA;AAAA,EAKA,OAAO,YAAe,GAAA;AACpB,IAAI,IAAA,CAAC,mBAAmB,aAAc,CAAA,IAAA;AACpC,MAAA;AAGF,IAAA,KAAA,MAAW,CAAC,UAAY,EAAA,QAAQ,CAAK,IAAA,kBAAA,CAAmB,cAAc,OAAQ,EAAA;AAC5E,MAAA,UAAA,CAAW,aAAa,QAAQ,CAAA;AAClC,IAAA,kBAAA,CAAmB,cAAc,KAAM,EAAA;AAIvC,IAAW,KAAA,MAAA,QAAA,IAAY,kBAAmB,CAAA,iBAAA,EAAuB,IAAA,QAAA;AAC/D,MAAA,KAAA,MAAW,UAAc,IAAA,QAAA;AACvB,QAAI,IAAA;AACF,UAAW,UAAA,CAAA,YAAA,CAAa,UAAW,CAAA,EAAA,EAAI,CAAA;AAAA,iBAElC,CAAG,EAAA;AACR,UAAA,UAAA,CAAW,aAAa,CAAC,CAAA;AAAA;AAE/B,IAAA,kBAAA,CAAmB,kBAAkB,MAAS,GAAA,CAAA;AAG9C,IAAA,KAAA,MAAW,YAAY,kBAAmB,CAAA,eAAA;AACxC,MAAI,IAAA;AAAE,QAAS,QAAA,EAAA;AAAA,OAAU,CAAA,MAAA;AAAA;AAC3B,IAAA,kBAAA,CAAmB,gBAAgB,MAAS,GAAA,CAAA;AAAA;AAEhD;AAwBa,MAAA,CAAA,GAAO,CAClB,CACA,EAAA,CAAA;AAAA;AAAA,EAGA,IAAI,kBAAmB,CAAA,CAAA,EAAG,CAAC;AAAA;AAiChB,MAAA,UAAA,GAAyB,CACpC,CAAA,EACA,CACQ,KAAA;AACR,EAAI,IAAA,UAAA,CAAW,CAAC,CAAG,EAAA;AACjB,IAAA,CAAA,CAAE,EAAE,KAAK,CAAA;AACT,IAAA,OAAO,EAAE,WAAY,CAAA,MAAM,CAAE,CAAA,CAAA,CAAE,KAAK,CAAC,CAAA;AAAA;AAGvC,EAAA,CAAA,CAAE,CAAC,CAAA;AACL;;;;"}
1
+ {"version":3,"file":"reactive.mjs","sources":["../src/reactive/index.mts"],"sourcesContent":["import { attempt } from \"../utils/index.mts\"\n\nexport const isReactiveSymbol = Symbol.for(\"bruh reactive\")\n\nexport const isReactive = (x: unknown): x is Reactive<unknown> =>\n // @ts-ignore\n x?.[isReactiveSymbol] === true\n\ntype Reaction = () => void\ntype StopReacting = () => void\n\nexport interface Reactive<T> {\n [isReactiveSymbol]: true\n\n value: T\n\n addReaction(reaction: Reaction): StopReacting\n}\n\nexport type Unreactive<T = unknown> = Exclude<T, { [isReactiveSymbol]: true }>\n\nexport type MaybeReactive<T> = Reactive<T> | Unreactive<T>\n\nexport type NestedReactive<T extends Unreactive> = Reactive<T | NestedReactive<T>>\n\n/**\n * A super simple and performant reactive value implementation\n */\nexport class SimpleReactive<T> implements Reactive<T> {\n [isReactiveSymbol] = true as const\n\n #value: T\n #reactions = new Set<Reaction>()\n\n constructor(value: T) {\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 attempt(reaction)\n }\n\n /**\n * @param reaction called every time that the value changes\n * @returns a function that stops the reactions\n */\n addReaction(reaction: Reaction) {\n this.#reactions.add(reaction)\n\n return () =>\n this.#reactions.delete(reaction)\n }\n}\n\nexport type IsEqual<T> = {\n bivarianceHack(a: T, b: T): boolean\n}[\"bivarianceHack\"]\n\nexport type ReactiveOptions<T> = {\n isEqual?: IsEqual<T>\n}\n\nexport type SourceNode<T> = FunctionalReactive<T, \"source\">\nexport type DerivativeNode<T> = FunctionalReactive<T, \"derivative\">\n\n/**\n * A reactive implementation for building functional reactive graphs.\n * Ensures state consistency, minimal node updates, and transparent update batching.\n */\nexport class FunctionalReactive<T, U extends \"source\" | \"derivative\" = any> implements Reactive<T> {\n [isReactiveSymbol] = true as const\n\n #weakRef = new WeakRef(this)\n\n // @ts-ignore\n #value: T\n #reactions = new Set<Reaction>()\n\n // For derived nodes, this is the derivation function\n // @ts-ignore\n #f:\n U extends \"derivative\"\n ? () => T\n : never\n\n isEqual?: IsEqual<T>\n\n // Source nodes are 0 deep in the derivation graph\n // This is for topological sort\n #depth:\n U extends \"source\"\n ? 0\n : number // natural, > 0\n = 0\n\n // All nodes have a set of derivatives that update when the node changes\n #derivatives = new Set<WeakRef<DerivativeNode<unknown>>>()\n\n // Keep track of all the pending changes from the value setter\n static #settersQueue = new Map<SourceNode<unknown>, unknown>()\n\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: Array<Set<DerivativeNode<unknown>> | undefined> = []\n\n // A queue of reactions to run after the graph is fully updated\n static #reactionsQueue: Array<Reaction> = []\n\n constructor(value: T, options?: ReactiveOptions<T>)\n constructor(\n dependencies: ReadonlyArray<FunctionalReactive<unknown>>,\n f: () => T,\n options?: ReactiveOptions<T>\n )\n constructor(\n xOrDependencies: T | ReadonlyArray<FunctionalReactive<unknown>>,\n optionsOrF?: ReactiveOptions<T> | (() => T),\n options?: ReactiveOptions<T>\n ) {\n // No derivation function means this is a source node\n if (typeof optionsOrF !== \"function\") {\n const this_ = this as SourceNode<T>\n const value = xOrDependencies as T\n const options = optionsOrF as ReactiveOptions<T> | undefined\n\n this_.#value = value\n this_.isEqual = options?.isEqual\n return\n }\n\n // Derived node\n const this_ = this as DerivativeNode<T>\n const dependencies = xOrDependencies as ReadonlyArray<FunctionalReactive<unknown>>\n const f = optionsOrF as () => T\n\n this_.#value = attempt(f)\n this_.#f = f\n this_.isEqual = options?.isEqual\n\n this_.#depth = Math.max(0, ...dependencies.map(dependency => dependency.#depth)) + 1\n\n dependencies.forEach(dependency => dependency.#derivatives.add(this_.#weakRef))\n }\n\n get value(): T {\n // If there are any pending updates\n if (FunctionalReactive.#settersQueue.size) {\n // If this is a source node that was updated, just return that\n // new value without actually updating any derived nodes yet\n if (this.#depth === 0) {\n const this_ = this as SourceNode<T>\n if (FunctionalReactive.#settersQueue.has(this_))\n return FunctionalReactive.#settersQueue.get(this_) as T\n }\n // Heuristic quick invalidation for derived nodes\n // Apply updates now, it's ok that there's already a microtask queued for this\n else {\n FunctionalReactive.applyUpdates()\n }\n }\n\n return this.#value\n }\n\n set value(newValue) {\n // Only allow source nodes to be directly updated\n if (this.#depth !== 0)\n return\n\n const this_ = this as SourceNode<T>\n\n const isEqual = this.isEqual\n ? this.isEqual(newValue, this.#value)\n : newValue === this.#value\n\n if (isEqual) {\n FunctionalReactive.#settersQueue.delete(this_)\n return\n }\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 /**\n * @param reaction called every time that the value changes\n * @returns a function that stops the reactions\n */\n addReaction(reaction: 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: T) {\n const isEqual = this.isEqual\n ? this.isEqual(newValue, this.#value)\n : newValue === this.#value\n\n if (isEqual)\n return\n\n this.#value = newValue\n FunctionalReactive.#reactionsQueue.push(...this.#reactions)\n\n this.#derivatives.forEach(weakRef => {\n const derivative = weakRef.deref()\n if (!derivative) {\n this.#derivatives.delete(weakRef)\n return\n }\n\n const depthSet = FunctionalReactive.#derivativesQueue[derivative.#depth] ??= new Set()\n depthSet.add(derivative)\n })\n }\n\n /**\n * Apply pending updates from actually changed source nodes\n */\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(\n attempt(() =>\n derivative.#f()\n )\n )\n\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 attempt(reaction)\n FunctionalReactive.#reactionsQueue.length = 0\n }\n}\n\ntype R = {\n /**\n * An initially undefined source node\n */\n <T>(): SourceNode<T | undefined>\n\n /**\n * A source node\n */\n <T>(value: T, options?: ReactiveOptions<T>): SourceNode<T>\n\n /**\n * A derived node\n */\n <T>(\n dependencies: ReadonlyArray<FunctionalReactive<unknown>>,\n f: () => T,\n options?: ReactiveOptions<T>\n ): DerivativeNode<T>\n}\n/**\n * A convenient wrapper for FunctionalReactive\n */\nexport const r: R = <T extends unknown>(\n xOrDependencies?: T | ReadonlyArray<FunctionalReactive<unknown>>,\n optionsOrF?: ReactiveOptions<T> | (() => T),\n options?: ReactiveOptions<T>\n) =>\n // @ts-ignore\n new FunctionalReactive(xOrDependencies, optionsOrF, options)\n\ntype ReactiveDo = {\n /**\n * Calls the given function with the reactive's value whenever it changes\n * @returns a function that stops the reactions\n */\n <T>(\n reactive: Reactive<T>,\n f: (value: T) => unknown\n ): StopReacting\n\n /**\n * Calls the given function with the value once\n */\n <T>(\n value: Unreactive<T>,\n f: (value: Unreactive<T>) => unknown\n ): undefined\n\n /**\n * If the value is not reactive, it calls the given function with that value once.\n * If it is a reactive value, it calls the given function with the reactive's value whenever it changes.\n * @returns a function that stops the reactions, if the value is reactive\n */\n <T>(\n x: MaybeReactive<T>,\n f: (value: T) => unknown\n ): StopReacting | undefined\n}\n/**\n * Do something with a value, updating if it is reactive\n */\nexport const reactiveDo: ReactiveDo = <T extends unknown>(\n x: MaybeReactive<T>,\n f: (value: T) => unknown\n): any => {\n if (isReactive(x)) {\n f(x.value)\n return x.addReaction(() => f(x.value))\n }\n\n f(x)\n}\n\nexport const flat = <T extends Unreactive>(source: NestedReactive<T>): FunctionalReactive<T> => {\n const chain: Array<{\n reactive: NestedReactive<T>,\n stopReacting: StopReacting\n }> = []\n\n const reactive = new FunctionalReactive<T>(undefined as T)\n\n const reaction = () => {\n let lastInCommon: NestedReactive<T> = source\n let i = 0\n while (lastInCommon.value === chain[i + 1]?.reactive) {\n lastInCommon = lastInCommon.value as NestedReactive<T>\n i++\n }\n\n if (i + 1 < chain.length) {\n for (let j = i + 1; j < chain.length; j++)\n chain[j].stopReacting()\n\n chain.length = i\n }\n\n updateChain(lastInCommon.value)\n }\n\n const updateChain = (newStart: T | NestedReactive<T>) => {\n while (isReactive(newStart)) {\n chain.push({\n reactive: newStart,\n stopReacting: newStart.addReaction(reaction)\n })\n newStart = newStart.value\n }\n\n const innerMost = chain[chain.length - 1].reactive as Reactive<T>\n reactive.isEqual = (innerMost as FunctionalReactive<T>).isEqual\n reactive.value = innerMost.value\n FunctionalReactive.applyUpdates()\n }\n\n updateChain(source)\n\n return reactive\n}\n"],"names":["this_","options"],"mappings":"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"}
@@ -1,11 +1,10 @@
1
- import { Reactive, MaybeReactive } from '../reactive/index.mts';
1
+ import { Reactive, MaybeReactive, NestedReactive } from '../reactive/index.mts';
2
2
  import { PropertyWiseOr, LikelyAsString, LikelyAsAbsent, LikelyAsBoolean } from './types.mts';
3
3
  import { PropertiesHyphen as Styles } from 'csstype';
4
4
  import { ElementType, HTMLTag, SVGTag, MathMLTag, Namespace, HTMLNamespace, SVGNamespace, MathMLNamespace, ElementToEventMap, ElementToAttributes } from 'html-info';
5
5
  export type TerminalBruhChild = Node | LikelyAsString | LikelyAsAbsent;
6
6
  export type TerminalBruhChildOutputNode<Child extends TerminalBruhChild> = Child extends Node ? Child : Child extends LikelyAsString ? Text : Child extends LikelyAsAbsent ? Comment : Node;
7
- export type FlatBruhChild = MaybeReactive<TerminalBruhChild> | Reactive<TerminalBruhChild | Iterable<BruhChild>>;
8
- export type BruhChild = FlatBruhChild | Iterable<BruhChild>;
7
+ export type BruhChild = MaybeReactive<TerminalBruhChild> | Iterable<BruhChild> | NestedReactive<TerminalBruhChild | Iterable<BruhChild>>;
9
8
  export type StylesToApply = {
10
9
  [Property in keyof Styles]: MaybeReactive<Styles[Property] | LikelyAsAbsent>;
11
10
  };
@@ -58,6 +57,12 @@ export declare namespace JSX {
58
57
  type Element = any;
59
58
  type ElementType = HTMLTag | SVGTag | MathMLTag | ((props: any) => Element);
60
59
  }
60
+ export declare const ownedReactivesSymbol: unique symbol;
61
+ declare global {
62
+ interface Node {
63
+ [ownedReactivesSymbol]?: Set<Reactive<unknown>>;
64
+ }
65
+ }
61
66
  export declare function bruhChildrenToNodes(children: Iterable<BruhChild>): IterableIterator<Node>;
62
67
  type ElementWithStyle = HTMLElement | SVGElement | MathMLElement;
63
68
  /**
@@ -7,10 +7,11 @@ export interface Reactive<T> {
7
7
  value: T;
8
8
  addReaction(reaction: Reaction): StopReacting;
9
9
  }
10
- export type Unreactive<T> = Exclude<T, {
10
+ export type Unreactive<T = unknown> = Exclude<T, {
11
11
  [isReactiveSymbol]: true;
12
12
  }>;
13
13
  export type MaybeReactive<T> = Reactive<T> | Unreactive<T>;
14
+ export type NestedReactive<T extends Unreactive> = Reactive<T | NestedReactive<T>>;
14
15
  /**
15
16
  * A super simple and performant reactive value implementation
16
17
  */
@@ -26,6 +27,12 @@ export declare class SimpleReactive<T> implements Reactive<T> {
26
27
  */
27
28
  addReaction(reaction: Reaction): () => boolean;
28
29
  }
30
+ export type IsEqual<T> = {
31
+ bivarianceHack(a: T, b: T): boolean;
32
+ }["bivarianceHack"];
33
+ export type ReactiveOptions<T> = {
34
+ isEqual?: IsEqual<T>;
35
+ };
29
36
  export type SourceNode<T> = FunctionalReactive<T, "source">;
30
37
  export type DerivativeNode<T> = FunctionalReactive<T, "derivative">;
31
38
  /**
@@ -35,8 +42,9 @@ export type DerivativeNode<T> = FunctionalReactive<T, "derivative">;
35
42
  export declare class FunctionalReactive<T, U extends "source" | "derivative" = any> implements Reactive<T> {
36
43
  #private;
37
44
  [isReactiveSymbol]: true;
38
- constructor(value: T);
39
- constructor(dependencies: ReadonlyArray<FunctionalReactive<unknown>>, f: () => T);
45
+ isEqual?: IsEqual<T>;
46
+ constructor(value: T, options?: ReactiveOptions<T>);
47
+ constructor(dependencies: ReadonlyArray<FunctionalReactive<unknown>>, f: () => T, options?: ReactiveOptions<T>);
40
48
  get value(): T;
41
49
  set value(newValue: T);
42
50
  /**
@@ -57,11 +65,11 @@ type R = {
57
65
  /**
58
66
  * A source node
59
67
  */
60
- <T>(value: T): SourceNode<T>;
68
+ <T>(value: T, options?: ReactiveOptions<T>): SourceNode<T>;
61
69
  /**
62
70
  * A derived node
63
71
  */
64
- <T>(dependencies: ReadonlyArray<FunctionalReactive<unknown>>, f: () => T): DerivativeNode<T>;
72
+ <T>(dependencies: ReadonlyArray<FunctionalReactive<unknown>>, f: () => T, options?: ReactiveOptions<T>): DerivativeNode<T>;
65
73
  };
66
74
  /**
67
75
  * A convenient wrapper for FunctionalReactive
@@ -88,4 +96,5 @@ type ReactiveDo = {
88
96
  * Do something with a value, updating if it is reactive
89
97
  */
90
98
  export declare const reactiveDo: ReactiveDo;
99
+ export declare const flat: <T extends Unreactive>(source: NestedReactive<T>) => FunctionalReactive<T>;
91
100
  export {};
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "library",
11
11
  "modern"
12
12
  ],
13
- "version": "2.0.0-beta.2",
13
+ "version": "2.0.0-beta.4",
14
14
  "license": "MIT",
15
15
  "author": {
16
16
  "name": "Daniel Ethridge",