thunderous 2.3.7 → 2.3.9

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/index.cjs CHANGED
@@ -58,25 +58,34 @@ var createSignal = (initVal, options) => {
58
58
  subscribers.add(subscriber);
59
59
  }
60
60
  if (options?.debugMode === true || getterOptions?.debugMode === true) {
61
- queueMicrotask(() => {
62
- let label = "anonymous signal";
63
- if (options?.label !== void 0) {
64
- label = `(${options.label})`;
65
- if (getterOptions?.label !== void 0) {
66
- label += ` ${getterOptions.label}`;
67
- }
68
- } else if (getterOptions?.label !== void 0) {
69
- label = getterOptions.label;
61
+ let label = "anonymous signal";
62
+ if (options?.label !== void 0) {
63
+ label = `(${options.label})`;
64
+ if (getterOptions?.label !== void 0) {
65
+ label += ` ${getterOptions.label}`;
70
66
  }
71
- console.log("Signal retrieved:", { value, subscribers, label });
72
- });
67
+ } else if (getterOptions?.label !== void 0) {
68
+ label = getterOptions.label;
69
+ }
70
+ console.log("Signal retrieved:", { value, subscribers, label });
73
71
  }
74
72
  return value;
75
73
  };
76
74
  getter.getter = true;
75
+ let stackLength = 0;
77
76
  const setter = (newValue, setterOptions) => {
77
+ stackLength++;
78
+ queueMicrotask(() => stackLength--);
79
+ if (stackLength > 1e3) {
80
+ console.error(new Error("Signal setter stack overflow detected. Possible infinite loop. Bailing out."));
81
+ stackLength = 0;
82
+ return;
83
+ }
78
84
  const isObject = typeof newValue === "object" && newValue !== null;
79
85
  if (!isObject && value === newValue) return;
86
+ if (isObject && typeof value === "object" && value !== null) {
87
+ if (JSON.stringify(value) === JSON.stringify(newValue)) return;
88
+ }
80
89
  const oldValue = value;
81
90
  value = newValue;
82
91
  for (const fn of subscribers) {
@@ -85,24 +94,24 @@ var createSignal = (initVal, options) => {
85
94
  } catch (error) {
86
95
  console.error("Error in subscriber:", { error, oldValue, newValue, fn });
87
96
  }
88
- if (options?.debugMode === true || setterOptions?.debugMode === true) {
89
- let label = "anonymous signal";
90
- if (options?.label !== void 0) {
91
- label = `(${options.label})`;
92
- if (setterOptions?.label !== void 0) {
93
- label += ` ${setterOptions.label}`;
94
- }
95
- } else if (setterOptions?.label !== void 0) {
96
- label = setterOptions.label;
97
+ }
98
+ if (options?.debugMode === true || setterOptions?.debugMode === true) {
99
+ let label = "anonymous signal";
100
+ if (options?.label !== void 0) {
101
+ label = `(${options.label})`;
102
+ if (setterOptions?.label !== void 0) {
103
+ label += ` ${setterOptions.label}`;
97
104
  }
98
- console.log("Signal set:", { oldValue, newValue, subscribers, label });
105
+ } else if (setterOptions?.label !== void 0) {
106
+ label = setterOptions.label;
99
107
  }
108
+ console.log("Signal set:", { oldValue, newValue, subscribers, label });
100
109
  }
101
110
  };
102
111
  return [getter, setter];
103
112
  };
104
- var derived = (fn) => {
105
- const [getter, setter] = createSignal();
113
+ var derived = (fn, options) => {
114
+ const [getter, setter] = createSignal(void 0, options);
106
115
  createEffect(() => {
107
116
  try {
108
117
  setter(fn());
@@ -282,13 +291,7 @@ var renderState = {
282
291
  signalMap: /* @__PURE__ */ new Map(),
283
292
  callbackMap: /* @__PURE__ */ new Map(),
284
293
  fragmentMap: /* @__PURE__ */ new Map(),
285
- registry: customElements
286
- };
287
- var logValueError = (value) => {
288
- console.error(
289
- "An invalid value was passed to a template function. Non-primitive values are not supported.\n\nValue:\n",
290
- value
291
- );
294
+ registry: typeof customElements !== "undefined" ? customElements : {}
292
295
  };
293
296
  var logPropertyWarning = (propName, element) => {
294
297
  console.warn(
@@ -363,10 +366,6 @@ var processValue = (value) => {
363
366
  renderState.callbackMap.set(uniqueKey, value);
364
367
  return isServer ? String(value()) : `{{callback:${uniqueKey}}}`;
365
368
  }
366
- if (typeof value === "object" && value !== null) {
367
- logValueError(value);
368
- return "";
369
- }
370
369
  return String(value);
371
370
  };
372
371
  var evaluateBindings = (element, fragment) => {
@@ -440,6 +439,7 @@ var evaluateBindings = (element, fragment) => {
440
439
  const attrName = attr.name;
441
440
  if (SIGNAL_BINDING_REGEX.test(attr.value)) {
442
441
  const textList = attr.value.split(SIGNAL_BINDING_REGEX);
442
+ let prevText = attr.value;
443
443
  createEffect(() => {
444
444
  let newText = "";
445
445
  let hasNull = false;
@@ -456,17 +456,18 @@ var evaluateBindings = (element, fragment) => {
456
456
  }
457
457
  }
458
458
  if (hasNull && newText === "null" || attrName.startsWith("prop:")) {
459
- child.removeAttribute(attrName);
459
+ if (child.hasAttribute(attrName)) child.removeAttribute(attrName);
460
460
  } else {
461
- child.setAttribute(attrName, newText);
461
+ if (newText !== prevText) child.setAttribute(attrName, newText);
462
462
  }
463
463
  if (attrName.startsWith("prop:")) {
464
- child.removeAttribute(attrName);
464
+ if (child.hasAttribute(attrName)) child.removeAttribute(attrName);
465
465
  const propName = attrName.replace("prop:", "");
466
466
  const newValue = hasNull && newText === "null" ? null : newText;
467
467
  if (!(propName in child)) logPropertyWarning(propName, child);
468
468
  child[propName] = signal !== void 0 ? signal() : newValue;
469
469
  }
470
+ prevText = newText;
470
471
  });
471
472
  } else if (LEGACY_CALLBACK_BINDING_REGEX.test(attr.value)) {
472
473
  const getRootNode = child.getRootNode.bind(child);
@@ -542,7 +543,7 @@ var css = (strings, ...values) => {
542
543
  value = isServer ? value() : `{{signal:${uniqueKey}}}`;
543
544
  }
544
545
  if (typeof value === "object" && value !== null) {
545
- logValueError(value);
546
+ console.error("Objects are not valid in CSS values. Received:", value);
546
547
  value = "";
547
548
  }
548
549
  cssText += string + String(value);
@@ -669,14 +670,44 @@ var customElement = (render, options) => {
669
670
  });
670
671
  #getPropSignal = ((prop, { allowUndefined = false } = {}) => {
671
672
  if (!(prop in this.#propSignals)) this.#propSignals[prop] = createSignal();
672
- const [_getter, _setter] = this.#propSignals[prop];
673
- let setFromProp = false;
674
- const setter = (newValue) => {
675
- if (!setFromProp) this[prop] = newValue;
676
- _setter(newValue);
673
+ const [_getter, __setter] = this.#propSignals[prop];
674
+ let stackLength = 0;
675
+ const _setter = (newValue, options2) => {
676
+ stackLength++;
677
+ queueMicrotask(() => stackLength--);
678
+ if (stackLength > 999) {
679
+ console.error(
680
+ new Error(
681
+ `Property signal setter stack overflow detected. Possible infinite loop. Bailing out.
682
+
683
+ Property: ${prop}
684
+
685
+ New value: ${JSON.stringify(newValue, null, 2)}
686
+
687
+ Element: <${this.tagName.toLowerCase()}>
688
+ `
689
+ )
690
+ );
691
+ stackLength = 0;
692
+ return;
693
+ }
694
+ __setter(newValue, options2);
695
+ };
696
+ const descriptor = Object.getOwnPropertyDescriptor(this, prop);
697
+ if (descriptor === void 0) {
698
+ Object.defineProperty(this, prop, {
699
+ get: _getter,
700
+ set: _setter,
701
+ configurable: false,
702
+ enumerable: true
703
+ });
704
+ }
705
+ const setter = (newValue, options2) => {
706
+ this[prop] = newValue;
707
+ _setter(newValue, options2);
677
708
  };
678
- const getter = () => {
679
- const value = _getter();
709
+ const getter = (options2) => {
710
+ const value = _getter(options2);
680
711
  if (value === void 0 && !allowUndefined) {
681
712
  const error = new Error(
682
713
  `Error accessing property: "${prop}"
@@ -689,17 +720,6 @@ You must set an initial value before calling a property signal's getter.
689
720
  return value;
690
721
  };
691
722
  getter.getter = true;
692
- const descriptor = Object.getOwnPropertyDescriptor(this, prop);
693
- if (descriptor === void 0) {
694
- Object.defineProperty(this, prop, {
695
- get: getter,
696
- set: (newValue) => {
697
- setFromProp = true;
698
- _setter(newValue);
699
- setFromProp = false;
700
- }
701
- });
702
- }
703
723
  const publicSignal = [getter, setter];
704
724
  publicSignal.init = (value) => {
705
725
  _setter(value);
@@ -707,7 +727,7 @@ You must set an initial value before calling a property signal's getter.
707
727
  };
708
728
  return publicSignal;
709
729
  }).bind(this);
710
- #render() {
730
+ #render = (() => {
711
731
  const root = this.#shadowRoot ?? this;
712
732
  renderState.currentShadowRoot = this.#shadowRoot;
713
733
  renderState.registry = shadowRootOptions.customElements ?? customElements;
@@ -797,7 +817,7 @@ You must set an initial value before calling a property signal's getter.
797
817
  root.replaceChildren(fragment);
798
818
  renderState.currentShadowRoot = null;
799
819
  renderState.registry = customElements;
800
- }
820
+ }).bind(this);
801
821
  static get formAssociated() {
802
822
  return formAssociated;
803
823
  }
package/dist/index.d.cts CHANGED
@@ -163,7 +163,7 @@ declare const createSignal: <T = undefined>(initVal?: T, options?: SignalOptions
163
163
  * const doubleCount = derived(() => getCount() * 2);
164
164
  * ```
165
165
  */
166
- declare const derived: <T>(fn: () => T) => SignalGetter<T>;
166
+ declare const derived: <T>(fn: () => T, options?: SignalOptions) => SignalGetter<T>;
167
167
  /**
168
168
  * Create an effect that runs when signals change.
169
169
  * @example
package/dist/index.d.ts CHANGED
@@ -163,7 +163,7 @@ declare const createSignal: <T = undefined>(initVal?: T, options?: SignalOptions
163
163
  * const doubleCount = derived(() => getCount() * 2);
164
164
  * ```
165
165
  */
166
- declare const derived: <T>(fn: () => T) => SignalGetter<T>;
166
+ declare const derived: <T>(fn: () => T, options?: SignalOptions) => SignalGetter<T>;
167
167
  /**
168
168
  * Create an effect that runs when signals change.
169
169
  * @example
package/dist/index.js CHANGED
@@ -23,25 +23,34 @@ var createSignal = (initVal, options) => {
23
23
  subscribers.add(subscriber);
24
24
  }
25
25
  if (options?.debugMode === true || getterOptions?.debugMode === true) {
26
- queueMicrotask(() => {
27
- let label = "anonymous signal";
28
- if (options?.label !== void 0) {
29
- label = `(${options.label})`;
30
- if (getterOptions?.label !== void 0) {
31
- label += ` ${getterOptions.label}`;
32
- }
33
- } else if (getterOptions?.label !== void 0) {
34
- label = getterOptions.label;
26
+ let label = "anonymous signal";
27
+ if (options?.label !== void 0) {
28
+ label = `(${options.label})`;
29
+ if (getterOptions?.label !== void 0) {
30
+ label += ` ${getterOptions.label}`;
35
31
  }
36
- console.log("Signal retrieved:", { value, subscribers, label });
37
- });
32
+ } else if (getterOptions?.label !== void 0) {
33
+ label = getterOptions.label;
34
+ }
35
+ console.log("Signal retrieved:", { value, subscribers, label });
38
36
  }
39
37
  return value;
40
38
  };
41
39
  getter.getter = true;
40
+ let stackLength = 0;
42
41
  const setter = (newValue, setterOptions) => {
42
+ stackLength++;
43
+ queueMicrotask(() => stackLength--);
44
+ if (stackLength > 1e3) {
45
+ console.error(new Error("Signal setter stack overflow detected. Possible infinite loop. Bailing out."));
46
+ stackLength = 0;
47
+ return;
48
+ }
43
49
  const isObject = typeof newValue === "object" && newValue !== null;
44
50
  if (!isObject && value === newValue) return;
51
+ if (isObject && typeof value === "object" && value !== null) {
52
+ if (JSON.stringify(value) === JSON.stringify(newValue)) return;
53
+ }
45
54
  const oldValue = value;
46
55
  value = newValue;
47
56
  for (const fn of subscribers) {
@@ -50,24 +59,24 @@ var createSignal = (initVal, options) => {
50
59
  } catch (error) {
51
60
  console.error("Error in subscriber:", { error, oldValue, newValue, fn });
52
61
  }
53
- if (options?.debugMode === true || setterOptions?.debugMode === true) {
54
- let label = "anonymous signal";
55
- if (options?.label !== void 0) {
56
- label = `(${options.label})`;
57
- if (setterOptions?.label !== void 0) {
58
- label += ` ${setterOptions.label}`;
59
- }
60
- } else if (setterOptions?.label !== void 0) {
61
- label = setterOptions.label;
62
+ }
63
+ if (options?.debugMode === true || setterOptions?.debugMode === true) {
64
+ let label = "anonymous signal";
65
+ if (options?.label !== void 0) {
66
+ label = `(${options.label})`;
67
+ if (setterOptions?.label !== void 0) {
68
+ label += ` ${setterOptions.label}`;
62
69
  }
63
- console.log("Signal set:", { oldValue, newValue, subscribers, label });
70
+ } else if (setterOptions?.label !== void 0) {
71
+ label = setterOptions.label;
64
72
  }
73
+ console.log("Signal set:", { oldValue, newValue, subscribers, label });
65
74
  }
66
75
  };
67
76
  return [getter, setter];
68
77
  };
69
- var derived = (fn) => {
70
- const [getter, setter] = createSignal();
78
+ var derived = (fn, options) => {
79
+ const [getter, setter] = createSignal(void 0, options);
71
80
  createEffect(() => {
72
81
  try {
73
82
  setter(fn());
@@ -247,13 +256,7 @@ var renderState = {
247
256
  signalMap: /* @__PURE__ */ new Map(),
248
257
  callbackMap: /* @__PURE__ */ new Map(),
249
258
  fragmentMap: /* @__PURE__ */ new Map(),
250
- registry: customElements
251
- };
252
- var logValueError = (value) => {
253
- console.error(
254
- "An invalid value was passed to a template function. Non-primitive values are not supported.\n\nValue:\n",
255
- value
256
- );
259
+ registry: typeof customElements !== "undefined" ? customElements : {}
257
260
  };
258
261
  var logPropertyWarning = (propName, element) => {
259
262
  console.warn(
@@ -328,10 +331,6 @@ var processValue = (value) => {
328
331
  renderState.callbackMap.set(uniqueKey, value);
329
332
  return isServer ? String(value()) : `{{callback:${uniqueKey}}}`;
330
333
  }
331
- if (typeof value === "object" && value !== null) {
332
- logValueError(value);
333
- return "";
334
- }
335
334
  return String(value);
336
335
  };
337
336
  var evaluateBindings = (element, fragment) => {
@@ -405,6 +404,7 @@ var evaluateBindings = (element, fragment) => {
405
404
  const attrName = attr.name;
406
405
  if (SIGNAL_BINDING_REGEX.test(attr.value)) {
407
406
  const textList = attr.value.split(SIGNAL_BINDING_REGEX);
407
+ let prevText = attr.value;
408
408
  createEffect(() => {
409
409
  let newText = "";
410
410
  let hasNull = false;
@@ -421,17 +421,18 @@ var evaluateBindings = (element, fragment) => {
421
421
  }
422
422
  }
423
423
  if (hasNull && newText === "null" || attrName.startsWith("prop:")) {
424
- child.removeAttribute(attrName);
424
+ if (child.hasAttribute(attrName)) child.removeAttribute(attrName);
425
425
  } else {
426
- child.setAttribute(attrName, newText);
426
+ if (newText !== prevText) child.setAttribute(attrName, newText);
427
427
  }
428
428
  if (attrName.startsWith("prop:")) {
429
- child.removeAttribute(attrName);
429
+ if (child.hasAttribute(attrName)) child.removeAttribute(attrName);
430
430
  const propName = attrName.replace("prop:", "");
431
431
  const newValue = hasNull && newText === "null" ? null : newText;
432
432
  if (!(propName in child)) logPropertyWarning(propName, child);
433
433
  child[propName] = signal !== void 0 ? signal() : newValue;
434
434
  }
435
+ prevText = newText;
435
436
  });
436
437
  } else if (LEGACY_CALLBACK_BINDING_REGEX.test(attr.value)) {
437
438
  const getRootNode = child.getRootNode.bind(child);
@@ -507,7 +508,7 @@ var css = (strings, ...values) => {
507
508
  value = isServer ? value() : `{{signal:${uniqueKey}}}`;
508
509
  }
509
510
  if (typeof value === "object" && value !== null) {
510
- logValueError(value);
511
+ console.error("Objects are not valid in CSS values. Received:", value);
511
512
  value = "";
512
513
  }
513
514
  cssText += string + String(value);
@@ -634,14 +635,44 @@ var customElement = (render, options) => {
634
635
  });
635
636
  #getPropSignal = ((prop, { allowUndefined = false } = {}) => {
636
637
  if (!(prop in this.#propSignals)) this.#propSignals[prop] = createSignal();
637
- const [_getter, _setter] = this.#propSignals[prop];
638
- let setFromProp = false;
639
- const setter = (newValue) => {
640
- if (!setFromProp) this[prop] = newValue;
641
- _setter(newValue);
638
+ const [_getter, __setter] = this.#propSignals[prop];
639
+ let stackLength = 0;
640
+ const _setter = (newValue, options2) => {
641
+ stackLength++;
642
+ queueMicrotask(() => stackLength--);
643
+ if (stackLength > 999) {
644
+ console.error(
645
+ new Error(
646
+ `Property signal setter stack overflow detected. Possible infinite loop. Bailing out.
647
+
648
+ Property: ${prop}
649
+
650
+ New value: ${JSON.stringify(newValue, null, 2)}
651
+
652
+ Element: <${this.tagName.toLowerCase()}>
653
+ `
654
+ )
655
+ );
656
+ stackLength = 0;
657
+ return;
658
+ }
659
+ __setter(newValue, options2);
660
+ };
661
+ const descriptor = Object.getOwnPropertyDescriptor(this, prop);
662
+ if (descriptor === void 0) {
663
+ Object.defineProperty(this, prop, {
664
+ get: _getter,
665
+ set: _setter,
666
+ configurable: false,
667
+ enumerable: true
668
+ });
669
+ }
670
+ const setter = (newValue, options2) => {
671
+ this[prop] = newValue;
672
+ _setter(newValue, options2);
642
673
  };
643
- const getter = () => {
644
- const value = _getter();
674
+ const getter = (options2) => {
675
+ const value = _getter(options2);
645
676
  if (value === void 0 && !allowUndefined) {
646
677
  const error = new Error(
647
678
  `Error accessing property: "${prop}"
@@ -654,17 +685,6 @@ You must set an initial value before calling a property signal's getter.
654
685
  return value;
655
686
  };
656
687
  getter.getter = true;
657
- const descriptor = Object.getOwnPropertyDescriptor(this, prop);
658
- if (descriptor === void 0) {
659
- Object.defineProperty(this, prop, {
660
- get: getter,
661
- set: (newValue) => {
662
- setFromProp = true;
663
- _setter(newValue);
664
- setFromProp = false;
665
- }
666
- });
667
- }
668
688
  const publicSignal = [getter, setter];
669
689
  publicSignal.init = (value) => {
670
690
  _setter(value);
@@ -672,7 +692,7 @@ You must set an initial value before calling a property signal's getter.
672
692
  };
673
693
  return publicSignal;
674
694
  }).bind(this);
675
- #render() {
695
+ #render = (() => {
676
696
  const root = this.#shadowRoot ?? this;
677
697
  renderState.currentShadowRoot = this.#shadowRoot;
678
698
  renderState.registry = shadowRootOptions.customElements ?? customElements;
@@ -762,7 +782,7 @@ You must set an initial value before calling a property signal's getter.
762
782
  root.replaceChildren(fragment);
763
783
  renderState.currentShadowRoot = null;
764
784
  renderState.registry = customElements;
765
- }
785
+ }).bind(this);
766
786
  static get formAssociated() {
767
787
  return formAssociated;
768
788
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thunderous",
3
- "version": "2.3.7",
3
+ "version": "2.3.9",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",