what-core 0.6.2 → 0.6.5
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/README.md +2 -0
- package/compiler.d.ts +30 -0
- package/devtools.d.ts +2 -0
- package/dist/compiler.js +1787 -0
- package/dist/compiler.js.map +7 -0
- package/dist/compiler.min.js +2 -0
- package/dist/compiler.min.js.map +7 -0
- package/dist/devtools.js +10 -0
- package/dist/devtools.js.map +7 -0
- package/dist/devtools.min.js +2 -0
- package/dist/devtools.min.js.map +7 -0
- package/dist/index.js +330 -382
- package/dist/index.js.map +4 -4
- package/dist/index.min.js +62 -62
- package/dist/index.min.js.map +4 -4
- package/dist/render.js +262 -21
- package/dist/render.js.map +4 -4
- package/dist/render.min.js +58 -1
- package/dist/render.min.js.map +4 -4
- package/dist/testing.js +3 -0
- package/dist/testing.js.map +2 -2
- package/dist/testing.min.js +1 -1
- package/dist/testing.min.js.map +2 -2
- package/index.d.ts +176 -1
- package/jsx-runtime.d.ts +622 -0
- package/package.json +20 -2
- package/render.d.ts +1 -1
- package/src/agent-context.js +1 -1
- package/src/compiler.js +18 -0
- package/src/components.js +73 -27
- package/src/devtools.js +4 -0
- package/src/dom.js +7 -0
- package/src/guardrails.js +3 -4
- package/src/hooks.js +0 -11
- package/src/index.js +5 -9
- package/src/render.js +91 -24
- package/testing.d.ts +1 -1
- package/dist/a11y.js +0 -440
- package/dist/animation.js +0 -548
- package/dist/components.js +0 -229
- package/dist/data.js +0 -638
- package/dist/dom.js +0 -439
- package/dist/form.js +0 -509
- package/dist/h.js +0 -152
- package/dist/head.js +0 -51
- package/dist/helpers.js +0 -140
- package/dist/hooks.js +0 -210
- package/dist/reactive.js +0 -432
- package/dist/scheduler.js +0 -246
- package/dist/skeleton.js +0 -363
- package/dist/store.js +0 -83
- package/dist/what.js +0 -117
package/dist/index.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
// packages/core/src/reactive.js
|
|
2
2
|
var __DEV__ = typeof process !== "undefined" ? true : true;
|
|
3
3
|
var __devtools = null;
|
|
4
|
-
function __setDevToolsHooks(hooks) {
|
|
5
|
-
if (__DEV__) __devtools = hooks;
|
|
6
|
-
}
|
|
7
4
|
var currentEffect = null;
|
|
8
5
|
var currentRoot = null;
|
|
9
6
|
var currentOwner = null;
|
|
@@ -814,36 +811,60 @@ function reportError(error, startCtx) {
|
|
|
814
811
|
return false;
|
|
815
812
|
}
|
|
816
813
|
function Show({ when, fallback = null, children }) {
|
|
817
|
-
|
|
818
|
-
|
|
814
|
+
if (typeof when === "function") {
|
|
815
|
+
return () => when() ? children : fallback;
|
|
816
|
+
}
|
|
817
|
+
return when ? children : fallback;
|
|
819
818
|
}
|
|
820
|
-
function For({ each: each2, fallback = null, children }) {
|
|
821
|
-
const list = typeof each2 === "function" ? each2() : each2;
|
|
822
|
-
if (!list || list.length === 0) return fallback;
|
|
819
|
+
function For({ each: each2, fallback = null, children, key: keyFn }) {
|
|
823
820
|
const renderFn = Array.isArray(children) ? children[0] : children;
|
|
824
821
|
if (typeof renderFn !== "function") {
|
|
825
|
-
|
|
822
|
+
if (__DEV__) {
|
|
823
|
+
console.warn("[what] For: children must be a render function, e.g. <For each={items}>{(item) => ...}</For>");
|
|
824
|
+
}
|
|
826
825
|
return fallback;
|
|
827
826
|
}
|
|
828
|
-
|
|
827
|
+
const source = typeof each2 === "function" ? each2 : () => each2;
|
|
828
|
+
const mapFn = (item, index) => {
|
|
829
829
|
const vnode = renderFn(item, index);
|
|
830
830
|
if (vnode && typeof vnode === "object" && vnode.key == null) {
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
831
|
+
const rawItem = typeof item === "function" && item._signal ? item() : item;
|
|
832
|
+
if (rawItem != null && typeof rawItem === "object") {
|
|
833
|
+
if (rawItem.id != null) vnode.key = rawItem.id;
|
|
834
|
+
else if (rawItem.key != null) vnode.key = rawItem.key;
|
|
835
|
+
} else if (typeof rawItem === "string" || typeof rawItem === "number") {
|
|
836
|
+
vnode.key = rawItem;
|
|
836
837
|
}
|
|
837
838
|
}
|
|
838
839
|
return vnode;
|
|
839
|
-
}
|
|
840
|
+
};
|
|
841
|
+
return () => {
|
|
842
|
+
const list = source();
|
|
843
|
+
if (!list || list.length === 0) return fallback;
|
|
844
|
+
return list.map((item, i) => mapFn(item, i));
|
|
845
|
+
};
|
|
840
846
|
}
|
|
841
847
|
function Switch({ fallback = null, children }) {
|
|
842
848
|
const kids = Array.isArray(children) ? children : [children];
|
|
849
|
+
const hasReactiveCondition = kids.some(
|
|
850
|
+
(child) => child && child.tag === Match && typeof child.props.when === "function"
|
|
851
|
+
);
|
|
852
|
+
if (hasReactiveCondition) {
|
|
853
|
+
return () => {
|
|
854
|
+
for (const child of kids) {
|
|
855
|
+
if (child && child.tag === Match) {
|
|
856
|
+
const condition = typeof child.props.when === "function" ? child.props.when() : child.props.when;
|
|
857
|
+
if (condition) {
|
|
858
|
+
return child.children;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
return fallback;
|
|
863
|
+
};
|
|
864
|
+
}
|
|
843
865
|
for (const child of kids) {
|
|
844
866
|
if (child && child.tag === Match) {
|
|
845
|
-
|
|
846
|
-
if (condition) {
|
|
867
|
+
if (child.props.when) {
|
|
847
868
|
return child.children;
|
|
848
869
|
}
|
|
849
870
|
}
|
|
@@ -854,7 +875,6 @@ function Match({ when, children }) {
|
|
|
854
875
|
return { tag: Match, props: { when }, children, _vnode: true };
|
|
855
876
|
}
|
|
856
877
|
function Island({ component: Component, mode, mediaQuery, ...props }) {
|
|
857
|
-
const placeholder = h("div", { "data-island": Component.name || "Island", "data-hydrate": mode });
|
|
858
878
|
const wrapper = signal(null);
|
|
859
879
|
const hydrated = signal(false);
|
|
860
880
|
function doHydrate() {
|
|
@@ -925,7 +945,7 @@ function Island({ component: Component, mode, mediaQuery, ...props }) {
|
|
|
925
945
|
return h(
|
|
926
946
|
"div",
|
|
927
947
|
{ "data-island": Component.name || "Island", "data-hydrate": mode, ref: refCallback },
|
|
928
|
-
hydrated() ? wrapper() : null
|
|
948
|
+
() => hydrated() ? wrapper() : null
|
|
929
949
|
);
|
|
930
950
|
}
|
|
931
951
|
|
|
@@ -1270,6 +1290,9 @@ function createDOM(vnode, parent, isSvg) {
|
|
|
1270
1290
|
if (isVNode(vnode) && typeof vnode.tag === "function") {
|
|
1271
1291
|
return createComponent(vnode, parent, isSvg);
|
|
1272
1292
|
}
|
|
1293
|
+
if (isVNode(vnode) && (vnode.tag === "__errorBoundary" || vnode.tag === "__suspense" || vnode.tag === "__portal")) {
|
|
1294
|
+
return createComponent(vnode, parent, isSvg);
|
|
1295
|
+
}
|
|
1273
1296
|
if (isVNode(vnode)) {
|
|
1274
1297
|
return createElementFromVNode(vnode, parent, isSvg);
|
|
1275
1298
|
}
|
|
@@ -1660,14 +1683,214 @@ function setProp(el, key, value, isSvg) {
|
|
|
1660
1683
|
}
|
|
1661
1684
|
}
|
|
1662
1685
|
|
|
1663
|
-
// packages/core/src/
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1686
|
+
// packages/core/src/errors.js
|
|
1687
|
+
var ERROR_CODES = {
|
|
1688
|
+
INFINITE_EFFECT: {
|
|
1689
|
+
code: "ERR_INFINITE_EFFECT",
|
|
1690
|
+
severity: "error",
|
|
1691
|
+
template: 'Effect "{{effectName}}" exceeded 25 flush iterations \u2014 likely an infinite loop.',
|
|
1692
|
+
suggestion: "An effect is writing to a signal it also reads, creating a cycle. Use untrack() to read the signal without subscribing, or restructure so the write and read are in separate effects.",
|
|
1693
|
+
codeExample: `// Bad \u2014 reads and writes count, creating a cycle:
|
|
1694
|
+
effect(() => { count(count() + 1); });
|
|
1695
|
+
|
|
1696
|
+
// Good \u2014 use untrack() so the read doesn't subscribe:
|
|
1697
|
+
effect(() => { count(untrack(count) + 1); });
|
|
1698
|
+
|
|
1699
|
+
// Better \u2014 split into separate logic:
|
|
1700
|
+
const doubled = computed(() => count() * 2);`
|
|
1701
|
+
},
|
|
1702
|
+
MISSING_SIGNAL_READ: {
|
|
1703
|
+
code: "ERR_MISSING_SIGNAL_READ",
|
|
1704
|
+
severity: "warning",
|
|
1705
|
+
template: 'Signal "{{signalName}}" used without calling () \u2014 renders as "[Function]" instead of its value.',
|
|
1706
|
+
suggestion: "Signals are functions. Call them to read: count() not count. In JSX: {count()} not {count}.",
|
|
1707
|
+
codeExample: `// Bad \u2014 signal reference, not value:
|
|
1708
|
+
<span>{count}</span> // renders "[Function]"
|
|
1709
|
+
|
|
1710
|
+
// Good \u2014 call the signal:
|
|
1711
|
+
<span>{count()}</span> // renders the actual value`
|
|
1712
|
+
},
|
|
1713
|
+
HYDRATION_MISMATCH: {
|
|
1714
|
+
code: "ERR_HYDRATION_MISMATCH",
|
|
1715
|
+
severity: "error",
|
|
1716
|
+
template: 'Hydration mismatch in component "{{component}}": server rendered "{{serverHTML}}" but client expects "{{clientHTML}}".',
|
|
1717
|
+
suggestion: "Ensure server and client render identical initial HTML. Avoid reading browser-only APIs (window, localStorage) during the initial render. Use onMount() for client-only logic.",
|
|
1718
|
+
codeExample: `// Bad \u2014 different on server vs client:
|
|
1719
|
+
function App() {
|
|
1720
|
+
return <p>{window.innerWidth}</p>;
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
// Good \u2014 use onMount for client-only values:
|
|
1724
|
+
function App() {
|
|
1725
|
+
const width = signal(0);
|
|
1726
|
+
onMount(() => width(window.innerWidth));
|
|
1727
|
+
return <p>{width()}</p>;
|
|
1728
|
+
}`
|
|
1729
|
+
},
|
|
1730
|
+
ORPHAN_EFFECT: {
|
|
1731
|
+
code: "ERR_ORPHAN_EFFECT",
|
|
1732
|
+
severity: "warning",
|
|
1733
|
+
template: 'Effect "{{effectName}}" was created outside a reactive root \u2014 it will never be cleaned up.',
|
|
1734
|
+
suggestion: "Wrap effect creation in createRoot() or create effects inside component functions where they are automatically tracked.",
|
|
1735
|
+
codeExample: `// Bad \u2014 orphaned, leaks memory:
|
|
1736
|
+
effect(() => console.log(count()));
|
|
1737
|
+
|
|
1738
|
+
// Good \u2014 inside a root with cleanup:
|
|
1739
|
+
createRoot(dispose => {
|
|
1740
|
+
effect(() => console.log(count()));
|
|
1741
|
+
// later: dispose() cleans up
|
|
1742
|
+
});`
|
|
1743
|
+
},
|
|
1744
|
+
SIGNAL_WRITE_IN_RENDER: {
|
|
1745
|
+
code: "ERR_SIGNAL_WRITE_IN_RENDER",
|
|
1746
|
+
severity: "error",
|
|
1747
|
+
template: 'Signal "{{signalName}}" written during render of component "{{component}}". This triggers re-execution.',
|
|
1748
|
+
suggestion: "Move signal writes into event handlers, effects, or onMount(). The component body should only read signals, not write them.",
|
|
1749
|
+
codeExample: `// Bad \u2014 write during render:
|
|
1750
|
+
function Counter() {
|
|
1751
|
+
count(count() + 1); // triggers infinite loop
|
|
1752
|
+
return <span>{count()}</span>;
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
// Good \u2014 write in event handler:
|
|
1756
|
+
function Counter() {
|
|
1757
|
+
return <button onclick={() => count(c => c + 1)}>{count()}</button>;
|
|
1758
|
+
}`
|
|
1759
|
+
},
|
|
1760
|
+
MISSING_CLEANUP: {
|
|
1761
|
+
code: "ERR_MISSING_CLEANUP",
|
|
1762
|
+
severity: "warning",
|
|
1763
|
+
template: 'Effect sets up "{{resource}}" but does not return a cleanup function.',
|
|
1764
|
+
suggestion: "Effects that add event listeners, set timers, or open connections should return a cleanup function to prevent memory leaks.",
|
|
1765
|
+
codeExample: `// Bad \u2014 no cleanup:
|
|
1766
|
+
effect(() => {
|
|
1767
|
+
window.addEventListener('resize', handler);
|
|
1768
|
+
});
|
|
1769
|
+
|
|
1770
|
+
// Good \u2014 return cleanup:
|
|
1771
|
+
effect(() => {
|
|
1772
|
+
window.addEventListener('resize', handler);
|
|
1773
|
+
return () => window.removeEventListener('resize', handler);
|
|
1774
|
+
});`
|
|
1775
|
+
},
|
|
1776
|
+
UNSAFE_INNERHTML: {
|
|
1777
|
+
code: "ERR_UNSAFE_INNERHTML",
|
|
1778
|
+
severity: "warning",
|
|
1779
|
+
template: "innerHTML set on element without using the __html safety marker.",
|
|
1780
|
+
suggestion: "Use the html tagged template literal or pass { __html: content } to mark innerHTML as intentional and reviewed.",
|
|
1781
|
+
codeExample: `// Bad \u2014 raw innerHTML (XSS risk):
|
|
1782
|
+
<div innerHTML={userInput} />
|
|
1783
|
+
|
|
1784
|
+
// Good \u2014 explicit opt-in:
|
|
1785
|
+
<div innerHTML={{ __html: sanitizedContent }} />
|
|
1786
|
+
|
|
1787
|
+
// Better \u2014 use the html template literal:
|
|
1788
|
+
html\`<div>\${sanitizedContent}</div>\``
|
|
1789
|
+
},
|
|
1790
|
+
MISSING_KEY: {
|
|
1791
|
+
code: "ERR_MISSING_KEY",
|
|
1792
|
+
severity: "warning",
|
|
1793
|
+
template: 'List rendered without key prop in component "{{component}}". Items may re-order incorrectly.',
|
|
1794
|
+
suggestion: "Add a unique key prop to each item in a list. Use a stable identifier (like an ID), not the array index.",
|
|
1795
|
+
codeExample: `// Bad \u2014 no key:
|
|
1796
|
+
<For each={items()}>{item => <li>{item.name}</li>}</For>
|
|
1797
|
+
|
|
1798
|
+
// Good \u2014 stable key:
|
|
1799
|
+
<For each={items()}>{item => <li key={item.id}>{item.name}</li>}</For>`
|
|
1800
|
+
}
|
|
1801
|
+
};
|
|
1802
|
+
var WhatError = class extends Error {
|
|
1803
|
+
constructor({ code, message, suggestion, file, line, component, signal: signal2, effect: effect2 }) {
|
|
1804
|
+
super(message);
|
|
1805
|
+
this.name = "WhatError";
|
|
1806
|
+
this.code = code;
|
|
1807
|
+
this.suggestion = suggestion;
|
|
1808
|
+
this.file = file;
|
|
1809
|
+
this.line = line;
|
|
1810
|
+
this.component = component;
|
|
1811
|
+
this.signal = signal2;
|
|
1812
|
+
this.effect = effect2;
|
|
1813
|
+
}
|
|
1814
|
+
toJSON() {
|
|
1815
|
+
return {
|
|
1816
|
+
code: this.code,
|
|
1817
|
+
message: this.message,
|
|
1818
|
+
suggestion: this.suggestion,
|
|
1819
|
+
file: this.file,
|
|
1820
|
+
line: this.line,
|
|
1821
|
+
component: this.component,
|
|
1822
|
+
signal: this.signal,
|
|
1823
|
+
effect: this.effect
|
|
1824
|
+
};
|
|
1825
|
+
}
|
|
1826
|
+
};
|
|
1827
|
+
function createWhatError(errorCode, context = {}) {
|
|
1828
|
+
const def = typeof errorCode === "string" ? ERROR_CODES[errorCode] : errorCode;
|
|
1829
|
+
if (!def) {
|
|
1830
|
+
return new WhatError({
|
|
1831
|
+
code: "ERR_UNKNOWN",
|
|
1832
|
+
message: `Unknown error: ${errorCode}`,
|
|
1833
|
+
suggestion: "Check the error code and try again."
|
|
1834
|
+
});
|
|
1668
1835
|
}
|
|
1669
|
-
|
|
1836
|
+
let message = def.template;
|
|
1837
|
+
for (const [key, val] of Object.entries(context)) {
|
|
1838
|
+
message = message.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), String(val));
|
|
1839
|
+
}
|
|
1840
|
+
message = message.replace(/\{\{[^}]+\}\}/g, "(unknown)");
|
|
1841
|
+
return new WhatError({
|
|
1842
|
+
code: def.code,
|
|
1843
|
+
message,
|
|
1844
|
+
suggestion: def.suggestion,
|
|
1845
|
+
file: context.file,
|
|
1846
|
+
line: context.line,
|
|
1847
|
+
component: context.component,
|
|
1848
|
+
signal: context.signal || context.signalName,
|
|
1849
|
+
effect: context.effect || context.effectName
|
|
1850
|
+
});
|
|
1670
1851
|
}
|
|
1852
|
+
var collectedErrors = [];
|
|
1853
|
+
var MAX_COLLECTED = 200;
|
|
1854
|
+
function collectError(whatError) {
|
|
1855
|
+
if (!__DEV__) return;
|
|
1856
|
+
collectedErrors.push({
|
|
1857
|
+
...whatError.toJSON(),
|
|
1858
|
+
timestamp: Date.now()
|
|
1859
|
+
});
|
|
1860
|
+
if (collectedErrors.length > MAX_COLLECTED) {
|
|
1861
|
+
collectedErrors = collectedErrors.slice(-MAX_COLLECTED);
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
function getCollectedErrors(since) {
|
|
1865
|
+
if (since) return collectedErrors.filter((e) => e.timestamp > since);
|
|
1866
|
+
return collectedErrors.slice();
|
|
1867
|
+
}
|
|
1868
|
+
function clearCollectedErrors() {
|
|
1869
|
+
collectedErrors = [];
|
|
1870
|
+
}
|
|
1871
|
+
function classifyError(err, context = {}) {
|
|
1872
|
+
const msg = err?.message || String(err);
|
|
1873
|
+
if (msg.includes("infinite effect loop") || msg.includes("25 iterations")) {
|
|
1874
|
+
return createWhatError("INFINITE_EFFECT", context);
|
|
1875
|
+
}
|
|
1876
|
+
if (msg.includes("hydration") || msg.includes("Hydration")) {
|
|
1877
|
+
return createWhatError("HYDRATION_MISMATCH", context);
|
|
1878
|
+
}
|
|
1879
|
+
if (msg.includes("Signal.set() called inside a computed")) {
|
|
1880
|
+
return createWhatError("SIGNAL_WRITE_IN_RENDER", {
|
|
1881
|
+
...context,
|
|
1882
|
+
signalName: msg.match(/signal: (\w+)/)?.[1] || context.signalName
|
|
1883
|
+
});
|
|
1884
|
+
}
|
|
1885
|
+
return new WhatError({
|
|
1886
|
+
code: "ERR_RUNTIME",
|
|
1887
|
+
message: msg,
|
|
1888
|
+
suggestion: "Check the stack trace and component context for more details.",
|
|
1889
|
+
...context
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
// packages/core/src/render.js
|
|
1671
1894
|
var URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "action", "formaction", "formAction"]);
|
|
1672
1895
|
function isSafeUrl(url) {
|
|
1673
1896
|
if (typeof url !== "string") return true;
|
|
@@ -2447,8 +2670,14 @@ function isHydrating() {
|
|
|
2447
2670
|
function hydrate(vnode, container) {
|
|
2448
2671
|
_isHydrating = true;
|
|
2449
2672
|
_hydrationCursor = { parent: container, index: 0 };
|
|
2673
|
+
_hydrationMismatchCount = 0;
|
|
2450
2674
|
try {
|
|
2451
2675
|
const result = hydrateNode(vnode, container);
|
|
2676
|
+
if (__DEV__ && _hydrationMismatchCount > 0) {
|
|
2677
|
+
console.warn(
|
|
2678
|
+
`[what] Hydration completed with ${_hydrationMismatchCount} mismatch${_hydrationMismatchCount === 1 ? "" : "es"}. See previous warnings for details. This usually means server and client render different initial HTML.`
|
|
2679
|
+
);
|
|
2680
|
+
}
|
|
2452
2681
|
return result;
|
|
2453
2682
|
} finally {
|
|
2454
2683
|
_isHydrating = false;
|
|
@@ -2471,10 +2700,37 @@ function claimNode(parent) {
|
|
|
2471
2700
|
}
|
|
2472
2701
|
return null;
|
|
2473
2702
|
}
|
|
2474
|
-
|
|
2475
|
-
|
|
2703
|
+
var _hydrationMismatchCount = 0;
|
|
2704
|
+
function getHydrationMismatchCount() {
|
|
2705
|
+
return _hydrationMismatchCount;
|
|
2706
|
+
}
|
|
2707
|
+
function reportHydrationMismatch(expected, actual, componentName) {
|
|
2708
|
+
_hydrationMismatchCount++;
|
|
2709
|
+
if (__DEV__) {
|
|
2710
|
+
const context = {
|
|
2711
|
+
component: componentName || "unknown",
|
|
2712
|
+
serverHTML: actual,
|
|
2713
|
+
clientHTML: expected
|
|
2714
|
+
};
|
|
2715
|
+
const whatError = createWhatError("HYDRATION_MISMATCH", context);
|
|
2716
|
+
collectError(whatError);
|
|
2717
|
+
if (__devtools?.onHydrationMismatch) {
|
|
2718
|
+
__devtools.onHydrationMismatch({
|
|
2719
|
+
component: componentName || "unknown",
|
|
2720
|
+
expected,
|
|
2721
|
+
actual,
|
|
2722
|
+
mismatchCount: _hydrationMismatchCount
|
|
2723
|
+
});
|
|
2724
|
+
}
|
|
2725
|
+
if (__devtools?.onError) {
|
|
2726
|
+
__devtools.onError(whatError, { type: "hydration", component: componentName });
|
|
2727
|
+
}
|
|
2728
|
+
console.warn(
|
|
2729
|
+
`[what] Hydration mismatch: expected ${expected}, got ${actual}` + (componentName ? ` (in ${componentName})` : "") + ". Falling back to client render."
|
|
2730
|
+
);
|
|
2731
|
+
}
|
|
2476
2732
|
}
|
|
2477
|
-
function hydrateNode(vnode, parent) {
|
|
2733
|
+
function hydrateNode(vnode, parent, _componentName) {
|
|
2478
2734
|
if (vnode == null || typeof vnode === "boolean") {
|
|
2479
2735
|
return null;
|
|
2480
2736
|
}
|
|
@@ -2482,19 +2738,21 @@ function hydrateNode(vnode, parent) {
|
|
|
2482
2738
|
const existing = claimNode(parent);
|
|
2483
2739
|
const text = String(vnode);
|
|
2484
2740
|
if (existing && existing.nodeType === 3) {
|
|
2485
|
-
if (
|
|
2486
|
-
|
|
2487
|
-
`
|
|
2741
|
+
if (__DEV__ && existing.textContent !== text) {
|
|
2742
|
+
reportHydrationMismatch(
|
|
2743
|
+
`text "${text}"`,
|
|
2744
|
+
`text "${existing.textContent}"`,
|
|
2745
|
+
_componentName
|
|
2488
2746
|
);
|
|
2489
2747
|
existing.textContent = text;
|
|
2490
2748
|
}
|
|
2491
2749
|
return existing;
|
|
2492
2750
|
}
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2751
|
+
reportHydrationMismatch(
|
|
2752
|
+
`text node "${text}"`,
|
|
2753
|
+
existing ? existing.nodeName : "nothing",
|
|
2754
|
+
_componentName
|
|
2755
|
+
);
|
|
2498
2756
|
const textNode2 = document.createTextNode(text);
|
|
2499
2757
|
if (existing) {
|
|
2500
2758
|
parent.replaceChild(textNode2, existing);
|
|
@@ -2505,7 +2763,7 @@ function hydrateNode(vnode, parent) {
|
|
|
2505
2763
|
}
|
|
2506
2764
|
if (typeof vnode === "function") {
|
|
2507
2765
|
const initialValue = vnode();
|
|
2508
|
-
let current = hydrateNode(initialValue, parent);
|
|
2766
|
+
let current = hydrateNode(initialValue, parent, _componentName);
|
|
2509
2767
|
effect(() => {
|
|
2510
2768
|
const value = vnode();
|
|
2511
2769
|
if (!_isHydrating) {
|
|
@@ -2517,7 +2775,7 @@ function hydrateNode(vnode, parent) {
|
|
|
2517
2775
|
if (Array.isArray(vnode)) {
|
|
2518
2776
|
const nodes = [];
|
|
2519
2777
|
for (const child of vnode) {
|
|
2520
|
-
const node = hydrateNode(child, parent);
|
|
2778
|
+
const node = hydrateNode(child, parent, _componentName);
|
|
2521
2779
|
if (node) nodes.push(node);
|
|
2522
2780
|
}
|
|
2523
2781
|
return nodes.length === 1 ? nodes[0] : nodes;
|
|
@@ -2526,6 +2784,7 @@ function hydrateNode(vnode, parent) {
|
|
|
2526
2784
|
if (typeof vnode.tag === "function") {
|
|
2527
2785
|
const componentStack2 = getComponentStack();
|
|
2528
2786
|
const Component = vnode.tag;
|
|
2787
|
+
const compName = Component.displayName || Component.name || "Anonymous";
|
|
2529
2788
|
const props = vnode.props || {};
|
|
2530
2789
|
const children = vnode.children || [];
|
|
2531
2790
|
const ctx = {
|
|
@@ -2546,7 +2805,9 @@ function hydrateNode(vnode, parent) {
|
|
|
2546
2805
|
result = Component({ ...props, children: propsChildren });
|
|
2547
2806
|
} catch (error) {
|
|
2548
2807
|
componentStack2.pop();
|
|
2549
|
-
|
|
2808
|
+
if (__DEV__) {
|
|
2809
|
+
console.error("[what] Error in component during hydration:", compName, error);
|
|
2810
|
+
}
|
|
2550
2811
|
return null;
|
|
2551
2812
|
}
|
|
2552
2813
|
componentStack2.pop();
|
|
@@ -2563,28 +2824,36 @@ function hydrateNode(vnode, parent) {
|
|
|
2563
2824
|
}
|
|
2564
2825
|
});
|
|
2565
2826
|
}
|
|
2566
|
-
return hydrateNode(result, parent);
|
|
2827
|
+
return hydrateNode(result, parent, compName);
|
|
2567
2828
|
}
|
|
2568
2829
|
const existing = claimNode(parent);
|
|
2569
2830
|
const expectedTag = vnode.tag.toUpperCase();
|
|
2570
2831
|
if (existing && existing.nodeType === 1 && existing.nodeName === expectedTag) {
|
|
2571
2832
|
hydrateElementProps(existing, vnode.props || {});
|
|
2833
|
+
if (vnode.props?.ref) {
|
|
2834
|
+
const ref = vnode.props.ref;
|
|
2835
|
+
if (typeof ref === "function") {
|
|
2836
|
+
ref(existing);
|
|
2837
|
+
} else if (typeof ref === "object" && ref !== null) {
|
|
2838
|
+
ref.current = existing;
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2572
2841
|
const savedCursor = _hydrationCursor;
|
|
2573
2842
|
_hydrationCursor = { parent: existing, index: 0 };
|
|
2574
2843
|
const rawInner = vnode.props?.dangerouslySetInnerHTML?.__html;
|
|
2575
2844
|
if (rawInner == null) {
|
|
2576
2845
|
for (const child of vnode.children) {
|
|
2577
|
-
hydrateNode(child, existing);
|
|
2846
|
+
hydrateNode(child, existing, _componentName);
|
|
2578
2847
|
}
|
|
2579
2848
|
}
|
|
2580
2849
|
_hydrationCursor = savedCursor;
|
|
2581
2850
|
return existing;
|
|
2582
2851
|
}
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2852
|
+
reportHydrationMismatch(
|
|
2853
|
+
`<${vnode.tag}>`,
|
|
2854
|
+
existing ? existing.nodeName : "nothing",
|
|
2855
|
+
_componentName
|
|
2856
|
+
);
|
|
2588
2857
|
const newEl = document.createElement(vnode.tag);
|
|
2589
2858
|
for (const key in vnode.props || {}) {
|
|
2590
2859
|
if (key === "children" || key === "key") continue;
|
|
@@ -2641,6 +2910,18 @@ function hydrateElementProps(el, props) {
|
|
|
2641
2910
|
continue;
|
|
2642
2911
|
}
|
|
2643
2912
|
if (key === "data-hk") continue;
|
|
2913
|
+
if (__DEV__ && typeof value === "string") {
|
|
2914
|
+
const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
|
|
2915
|
+
const serverValue = el.getAttribute(attrName);
|
|
2916
|
+
if (serverValue !== null && serverValue !== value) {
|
|
2917
|
+
reportHydrationMismatch(
|
|
2918
|
+
`${attrName}="${value}"`,
|
|
2919
|
+
`${attrName}="${serverValue}"`,
|
|
2920
|
+
el.tagName.toLowerCase()
|
|
2921
|
+
);
|
|
2922
|
+
setProp2(el, key, value);
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2644
2925
|
}
|
|
2645
2926
|
}
|
|
2646
2927
|
|
|
@@ -2658,16 +2939,6 @@ function getHook(ctx) {
|
|
|
2658
2939
|
const index = ctx.hookIndex++;
|
|
2659
2940
|
return { index, exists: index < ctx.hooks.length };
|
|
2660
2941
|
}
|
|
2661
|
-
function useState(initial) {
|
|
2662
|
-
const ctx = getCtx("useState");
|
|
2663
|
-
const { index, exists } = getHook(ctx);
|
|
2664
|
-
if (!exists) {
|
|
2665
|
-
const s2 = signal(typeof initial === "function" ? initial() : initial);
|
|
2666
|
-
ctx.hooks[index] = s2;
|
|
2667
|
-
}
|
|
2668
|
-
const s = ctx.hooks[index];
|
|
2669
|
-
return [s, s.set];
|
|
2670
|
-
}
|
|
2671
2942
|
function useSignal(initial) {
|
|
2672
2943
|
const ctx = getCtx("useSignal");
|
|
2673
2944
|
const { index, exists } = getHook(ctx);
|
|
@@ -2684,97 +2955,6 @@ function useComputed(fn) {
|
|
|
2684
2955
|
}
|
|
2685
2956
|
return ctx.hooks[index];
|
|
2686
2957
|
}
|
|
2687
|
-
function useEffect(fn, deps) {
|
|
2688
|
-
const ctx = getCtx("useEffect");
|
|
2689
|
-
const { index, exists } = getHook(ctx);
|
|
2690
|
-
if (!exists) {
|
|
2691
|
-
ctx.hooks[index] = { cleanup: null, dispose: null };
|
|
2692
|
-
}
|
|
2693
|
-
if (__DEV__ && Array.isArray(deps) && deps.length > 0) {
|
|
2694
|
-
for (let i = 0; i < deps.length; i++) {
|
|
2695
|
-
const dep = deps[i];
|
|
2696
|
-
if (dep != null && typeof dep !== "function") {
|
|
2697
|
-
console.warn(
|
|
2698
|
-
`[what] useEffect dep at index ${i} is not a function. Did you mean to pass a signal? Use count instead of count()`
|
|
2699
|
-
);
|
|
2700
|
-
}
|
|
2701
|
-
}
|
|
2702
|
-
}
|
|
2703
|
-
const hook = ctx.hooks[index];
|
|
2704
|
-
if (hook.dispose) return;
|
|
2705
|
-
if (deps === void 0) {
|
|
2706
|
-
queueMicrotask(() => {
|
|
2707
|
-
if (ctx.disposed) return;
|
|
2708
|
-
hook.dispose = effect(() => {
|
|
2709
|
-
if (hook.cleanup) {
|
|
2710
|
-
try {
|
|
2711
|
-
hook.cleanup();
|
|
2712
|
-
} catch (e) {
|
|
2713
|
-
}
|
|
2714
|
-
hook.cleanup = null;
|
|
2715
|
-
}
|
|
2716
|
-
const result = fn();
|
|
2717
|
-
if (typeof result === "function") hook.cleanup = result;
|
|
2718
|
-
});
|
|
2719
|
-
ctx.effects = ctx.effects || [];
|
|
2720
|
-
ctx.effects.push(hook.dispose);
|
|
2721
|
-
});
|
|
2722
|
-
} else if (deps.length === 0) {
|
|
2723
|
-
queueMicrotask(() => {
|
|
2724
|
-
if (ctx.disposed) return;
|
|
2725
|
-
const result = fn();
|
|
2726
|
-
if (typeof result === "function") hook.cleanup = result;
|
|
2727
|
-
});
|
|
2728
|
-
hook.dispose = true;
|
|
2729
|
-
} else {
|
|
2730
|
-
queueMicrotask(() => {
|
|
2731
|
-
if (ctx.disposed) return;
|
|
2732
|
-
hook.dispose = effect(() => {
|
|
2733
|
-
for (let i = 0; i < deps.length; i++) {
|
|
2734
|
-
const dep = deps[i];
|
|
2735
|
-
if (typeof dep === "function" && dep._signal) {
|
|
2736
|
-
dep();
|
|
2737
|
-
}
|
|
2738
|
-
}
|
|
2739
|
-
if (hook.cleanup) {
|
|
2740
|
-
try {
|
|
2741
|
-
hook.cleanup();
|
|
2742
|
-
} catch (e) {
|
|
2743
|
-
}
|
|
2744
|
-
hook.cleanup = null;
|
|
2745
|
-
}
|
|
2746
|
-
const result = untrack(() => fn());
|
|
2747
|
-
if (typeof result === "function") hook.cleanup = result;
|
|
2748
|
-
});
|
|
2749
|
-
ctx.effects = ctx.effects || [];
|
|
2750
|
-
ctx.effects.push(hook.dispose);
|
|
2751
|
-
});
|
|
2752
|
-
}
|
|
2753
|
-
}
|
|
2754
|
-
function useMemo(fn, deps) {
|
|
2755
|
-
const ctx = getCtx("useMemo");
|
|
2756
|
-
const { index, exists } = getHook(ctx);
|
|
2757
|
-
if (!exists) {
|
|
2758
|
-
ctx.hooks[index] = { computed: computed(fn) };
|
|
2759
|
-
}
|
|
2760
|
-
return ctx.hooks[index].computed;
|
|
2761
|
-
}
|
|
2762
|
-
function useCallback(fn, deps) {
|
|
2763
|
-
const ctx = getCtx("useCallback");
|
|
2764
|
-
const { index, exists } = getHook(ctx);
|
|
2765
|
-
if (!exists) {
|
|
2766
|
-
ctx.hooks[index] = { callback: fn };
|
|
2767
|
-
}
|
|
2768
|
-
return ctx.hooks[index].callback;
|
|
2769
|
-
}
|
|
2770
|
-
function useRef(initial) {
|
|
2771
|
-
const ctx = getCtx("useRef");
|
|
2772
|
-
const { index, exists } = getHook(ctx);
|
|
2773
|
-
if (!exists) {
|
|
2774
|
-
ctx.hooks[index] = { current: initial };
|
|
2775
|
-
}
|
|
2776
|
-
return ctx.hooks[index];
|
|
2777
|
-
}
|
|
2778
2958
|
function useContext(context) {
|
|
2779
2959
|
let ctx = getCurrentComponent();
|
|
2780
2960
|
if (__DEV__ && !ctx) {
|
|
@@ -4843,18 +5023,6 @@ function clearCache() {
|
|
|
4843
5023
|
lastFetchTimestamps.clear();
|
|
4844
5024
|
inFlightRequests.clear();
|
|
4845
5025
|
}
|
|
4846
|
-
function __getCacheSnapshot() {
|
|
4847
|
-
const entries = [];
|
|
4848
|
-
for (const [key, sig] of cacheSignals) {
|
|
4849
|
-
entries.push({
|
|
4850
|
-
key,
|
|
4851
|
-
data: sig.peek(),
|
|
4852
|
-
error: errorSignals.has(key) ? errorSignals.get(key).peek() : null,
|
|
4853
|
-
isValidating: validatingSignals.has(key) ? validatingSignals.get(key).peek() : false
|
|
4854
|
-
});
|
|
4855
|
-
}
|
|
4856
|
-
return entries;
|
|
4857
|
-
}
|
|
4858
5026
|
|
|
4859
5027
|
// packages/core/src/form.js
|
|
4860
5028
|
function useForm(options = {}) {
|
|
@@ -5312,213 +5480,6 @@ function ErrorMessage({ name, formState, errors, render }) {
|
|
|
5312
5480
|
return h("span", { class: "what-error", role: "alert" }, error.message);
|
|
5313
5481
|
}
|
|
5314
5482
|
|
|
5315
|
-
// packages/core/src/errors.js
|
|
5316
|
-
var ERROR_CODES = {
|
|
5317
|
-
INFINITE_EFFECT: {
|
|
5318
|
-
code: "ERR_INFINITE_EFFECT",
|
|
5319
|
-
severity: "error",
|
|
5320
|
-
template: 'Effect "{{effectName}}" exceeded 25 flush iterations \u2014 likely an infinite loop.',
|
|
5321
|
-
suggestion: "An effect is writing to a signal it also reads, creating a cycle. Use untrack() to read the signal without subscribing, or restructure so the write and read are in separate effects.",
|
|
5322
|
-
codeExample: `// Bad \u2014 reads and writes count, creating a cycle:
|
|
5323
|
-
effect(() => { count(count() + 1); });
|
|
5324
|
-
|
|
5325
|
-
// Good \u2014 use untrack() so the read doesn't subscribe:
|
|
5326
|
-
effect(() => { count(untrack(count) + 1); });
|
|
5327
|
-
|
|
5328
|
-
// Better \u2014 split into separate logic:
|
|
5329
|
-
const doubled = computed(() => count() * 2);`
|
|
5330
|
-
},
|
|
5331
|
-
MISSING_SIGNAL_READ: {
|
|
5332
|
-
code: "ERR_MISSING_SIGNAL_READ",
|
|
5333
|
-
severity: "warning",
|
|
5334
|
-
template: 'Signal "{{signalName}}" used without calling () \u2014 renders as "[Function]" instead of its value.',
|
|
5335
|
-
suggestion: "Signals are functions. Call them to read: count() not count. In JSX: {count()} not {count}.",
|
|
5336
|
-
codeExample: `// Bad \u2014 signal reference, not value:
|
|
5337
|
-
<span>{count}</span> // renders "[Function]"
|
|
5338
|
-
|
|
5339
|
-
// Good \u2014 call the signal:
|
|
5340
|
-
<span>{count()}</span> // renders the actual value`
|
|
5341
|
-
},
|
|
5342
|
-
HYDRATION_MISMATCH: {
|
|
5343
|
-
code: "ERR_HYDRATION_MISMATCH",
|
|
5344
|
-
severity: "error",
|
|
5345
|
-
template: 'Hydration mismatch in component "{{component}}": server rendered "{{serverHTML}}" but client expects "{{clientHTML}}".',
|
|
5346
|
-
suggestion: "Ensure server and client render identical initial HTML. Avoid reading browser-only APIs (window, localStorage) during the initial render. Use onMount() for client-only logic.",
|
|
5347
|
-
codeExample: `// Bad \u2014 different on server vs client:
|
|
5348
|
-
function App() {
|
|
5349
|
-
return <p>{window.innerWidth}</p>;
|
|
5350
|
-
}
|
|
5351
|
-
|
|
5352
|
-
// Good \u2014 use onMount for client-only values:
|
|
5353
|
-
function App() {
|
|
5354
|
-
const width = signal(0);
|
|
5355
|
-
onMount(() => width(window.innerWidth));
|
|
5356
|
-
return <p>{width()}</p>;
|
|
5357
|
-
}`
|
|
5358
|
-
},
|
|
5359
|
-
ORPHAN_EFFECT: {
|
|
5360
|
-
code: "ERR_ORPHAN_EFFECT",
|
|
5361
|
-
severity: "warning",
|
|
5362
|
-
template: 'Effect "{{effectName}}" was created outside a reactive root \u2014 it will never be cleaned up.',
|
|
5363
|
-
suggestion: "Wrap effect creation in createRoot() or create effects inside component functions where they are automatically tracked.",
|
|
5364
|
-
codeExample: `// Bad \u2014 orphaned, leaks memory:
|
|
5365
|
-
effect(() => console.log(count()));
|
|
5366
|
-
|
|
5367
|
-
// Good \u2014 inside a root with cleanup:
|
|
5368
|
-
createRoot(dispose => {
|
|
5369
|
-
effect(() => console.log(count()));
|
|
5370
|
-
// later: dispose() cleans up
|
|
5371
|
-
});`
|
|
5372
|
-
},
|
|
5373
|
-
SIGNAL_WRITE_IN_RENDER: {
|
|
5374
|
-
code: "ERR_SIGNAL_WRITE_IN_RENDER",
|
|
5375
|
-
severity: "error",
|
|
5376
|
-
template: 'Signal "{{signalName}}" written during render of component "{{component}}". This triggers re-execution.',
|
|
5377
|
-
suggestion: "Move signal writes into event handlers, effects, or onMount(). The component body should only read signals, not write them.",
|
|
5378
|
-
codeExample: `// Bad \u2014 write during render:
|
|
5379
|
-
function Counter() {
|
|
5380
|
-
count(count() + 1); // triggers infinite loop
|
|
5381
|
-
return <span>{count()}</span>;
|
|
5382
|
-
}
|
|
5383
|
-
|
|
5384
|
-
// Good \u2014 write in event handler:
|
|
5385
|
-
function Counter() {
|
|
5386
|
-
return <button onclick={() => count(c => c + 1)}>{count()}</button>;
|
|
5387
|
-
}`
|
|
5388
|
-
},
|
|
5389
|
-
MISSING_CLEANUP: {
|
|
5390
|
-
code: "ERR_MISSING_CLEANUP",
|
|
5391
|
-
severity: "warning",
|
|
5392
|
-
template: 'Effect sets up "{{resource}}" but does not return a cleanup function.',
|
|
5393
|
-
suggestion: "Effects that add event listeners, set timers, or open connections should return a cleanup function to prevent memory leaks.",
|
|
5394
|
-
codeExample: `// Bad \u2014 no cleanup:
|
|
5395
|
-
effect(() => {
|
|
5396
|
-
window.addEventListener('resize', handler);
|
|
5397
|
-
});
|
|
5398
|
-
|
|
5399
|
-
// Good \u2014 return cleanup:
|
|
5400
|
-
effect(() => {
|
|
5401
|
-
window.addEventListener('resize', handler);
|
|
5402
|
-
return () => window.removeEventListener('resize', handler);
|
|
5403
|
-
});`
|
|
5404
|
-
},
|
|
5405
|
-
UNSAFE_INNERHTML: {
|
|
5406
|
-
code: "ERR_UNSAFE_INNERHTML",
|
|
5407
|
-
severity: "warning",
|
|
5408
|
-
template: "innerHTML set on element without using the __html safety marker.",
|
|
5409
|
-
suggestion: "Use the html tagged template literal or pass { __html: content } to mark innerHTML as intentional and reviewed.",
|
|
5410
|
-
codeExample: `// Bad \u2014 raw innerHTML (XSS risk):
|
|
5411
|
-
<div innerHTML={userInput} />
|
|
5412
|
-
|
|
5413
|
-
// Good \u2014 explicit opt-in:
|
|
5414
|
-
<div innerHTML={{ __html: sanitizedContent }} />
|
|
5415
|
-
|
|
5416
|
-
// Better \u2014 use the html template literal:
|
|
5417
|
-
html\`<div>\${sanitizedContent}</div>\``
|
|
5418
|
-
},
|
|
5419
|
-
MISSING_KEY: {
|
|
5420
|
-
code: "ERR_MISSING_KEY",
|
|
5421
|
-
severity: "warning",
|
|
5422
|
-
template: 'List rendered without key prop in component "{{component}}". Items may re-order incorrectly.',
|
|
5423
|
-
suggestion: "Add a unique key prop to each item in a list. Use a stable identifier (like an ID), not the array index.",
|
|
5424
|
-
codeExample: `// Bad \u2014 no key:
|
|
5425
|
-
<For each={items()}>{item => <li>{item.name}</li>}</For>
|
|
5426
|
-
|
|
5427
|
-
// Good \u2014 stable key:
|
|
5428
|
-
<For each={items()}>{item => <li key={item.id}>{item.name}</li>}</For>`
|
|
5429
|
-
}
|
|
5430
|
-
};
|
|
5431
|
-
var WhatError = class extends Error {
|
|
5432
|
-
constructor({ code, message, suggestion, file, line, component, signal: signal2, effect: effect2 }) {
|
|
5433
|
-
super(message);
|
|
5434
|
-
this.name = "WhatError";
|
|
5435
|
-
this.code = code;
|
|
5436
|
-
this.suggestion = suggestion;
|
|
5437
|
-
this.file = file;
|
|
5438
|
-
this.line = line;
|
|
5439
|
-
this.component = component;
|
|
5440
|
-
this.signal = signal2;
|
|
5441
|
-
this.effect = effect2;
|
|
5442
|
-
}
|
|
5443
|
-
toJSON() {
|
|
5444
|
-
return {
|
|
5445
|
-
code: this.code,
|
|
5446
|
-
message: this.message,
|
|
5447
|
-
suggestion: this.suggestion,
|
|
5448
|
-
file: this.file,
|
|
5449
|
-
line: this.line,
|
|
5450
|
-
component: this.component,
|
|
5451
|
-
signal: this.signal,
|
|
5452
|
-
effect: this.effect
|
|
5453
|
-
};
|
|
5454
|
-
}
|
|
5455
|
-
};
|
|
5456
|
-
function createWhatError(errorCode, context = {}) {
|
|
5457
|
-
const def = typeof errorCode === "string" ? ERROR_CODES[errorCode] : errorCode;
|
|
5458
|
-
if (!def) {
|
|
5459
|
-
return new WhatError({
|
|
5460
|
-
code: "ERR_UNKNOWN",
|
|
5461
|
-
message: `Unknown error: ${errorCode}`,
|
|
5462
|
-
suggestion: "Check the error code and try again."
|
|
5463
|
-
});
|
|
5464
|
-
}
|
|
5465
|
-
let message = def.template;
|
|
5466
|
-
for (const [key, val] of Object.entries(context)) {
|
|
5467
|
-
message = message.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), String(val));
|
|
5468
|
-
}
|
|
5469
|
-
message = message.replace(/\{\{[^}]+\}\}/g, "(unknown)");
|
|
5470
|
-
return new WhatError({
|
|
5471
|
-
code: def.code,
|
|
5472
|
-
message,
|
|
5473
|
-
suggestion: def.suggestion,
|
|
5474
|
-
file: context.file,
|
|
5475
|
-
line: context.line,
|
|
5476
|
-
component: context.component,
|
|
5477
|
-
signal: context.signal || context.signalName,
|
|
5478
|
-
effect: context.effect || context.effectName
|
|
5479
|
-
});
|
|
5480
|
-
}
|
|
5481
|
-
var collectedErrors = [];
|
|
5482
|
-
var MAX_COLLECTED = 200;
|
|
5483
|
-
function collectError(whatError) {
|
|
5484
|
-
if (!__DEV__) return;
|
|
5485
|
-
collectedErrors.push({
|
|
5486
|
-
...whatError.toJSON(),
|
|
5487
|
-
timestamp: Date.now()
|
|
5488
|
-
});
|
|
5489
|
-
if (collectedErrors.length > MAX_COLLECTED) {
|
|
5490
|
-
collectedErrors = collectedErrors.slice(-MAX_COLLECTED);
|
|
5491
|
-
}
|
|
5492
|
-
}
|
|
5493
|
-
function getCollectedErrors(since) {
|
|
5494
|
-
if (since) return collectedErrors.filter((e) => e.timestamp > since);
|
|
5495
|
-
return collectedErrors.slice();
|
|
5496
|
-
}
|
|
5497
|
-
function clearCollectedErrors() {
|
|
5498
|
-
collectedErrors = [];
|
|
5499
|
-
}
|
|
5500
|
-
function classifyError(err, context = {}) {
|
|
5501
|
-
const msg = err?.message || String(err);
|
|
5502
|
-
if (msg.includes("infinite effect loop") || msg.includes("25 iterations")) {
|
|
5503
|
-
return createWhatError("INFINITE_EFFECT", context);
|
|
5504
|
-
}
|
|
5505
|
-
if (msg.includes("hydration") || msg.includes("Hydration")) {
|
|
5506
|
-
return createWhatError("HYDRATION_MISMATCH", context);
|
|
5507
|
-
}
|
|
5508
|
-
if (msg.includes("Signal.set() called inside a computed")) {
|
|
5509
|
-
return createWhatError("SIGNAL_WRITE_IN_RENDER", {
|
|
5510
|
-
...context,
|
|
5511
|
-
signalName: msg.match(/signal: (\w+)/)?.[1] || context.signalName
|
|
5512
|
-
});
|
|
5513
|
-
}
|
|
5514
|
-
return new WhatError({
|
|
5515
|
-
code: "ERR_RUNTIME",
|
|
5516
|
-
message: msg,
|
|
5517
|
-
suggestion: "Check the stack trace and component context for more details.",
|
|
5518
|
-
...context
|
|
5519
|
-
});
|
|
5520
|
-
}
|
|
5521
|
-
|
|
5522
5483
|
// packages/core/src/guardrails.js
|
|
5523
5484
|
var guardrails = {
|
|
5524
5485
|
signalReadDetection: true,
|
|
@@ -5579,10 +5540,8 @@ var VALID_EXPORTS = /* @__PURE__ */ new Set([
|
|
|
5579
5540
|
"getOwner",
|
|
5580
5541
|
"runWithOwner",
|
|
5581
5542
|
"onRootCleanup",
|
|
5582
|
-
"__setDevToolsHooks",
|
|
5583
5543
|
// Rendering
|
|
5584
5544
|
"template",
|
|
5585
|
-
"_template",
|
|
5586
5545
|
"svgTemplate",
|
|
5587
5546
|
"insert",
|
|
5588
5547
|
"mapArray",
|
|
@@ -5593,7 +5552,6 @@ var VALID_EXPORTS = /* @__PURE__ */ new Set([
|
|
|
5593
5552
|
"classList",
|
|
5594
5553
|
"hydrate",
|
|
5595
5554
|
"isHydrating",
|
|
5596
|
-
"_$createComponent",
|
|
5597
5555
|
// JSX
|
|
5598
5556
|
"h",
|
|
5599
5557
|
"Fragment",
|
|
@@ -5706,7 +5664,6 @@ var VALID_EXPORTS = /* @__PURE__ */ new Set([
|
|
|
5706
5664
|
"setQueryData",
|
|
5707
5665
|
"getQueryData",
|
|
5708
5666
|
"clearCache",
|
|
5709
|
-
"__getCacheSnapshot",
|
|
5710
5667
|
// Form
|
|
5711
5668
|
"useForm",
|
|
5712
5669
|
"useField",
|
|
@@ -5768,7 +5725,7 @@ function levenshtein(a, b) {
|
|
|
5768
5725
|
}
|
|
5769
5726
|
|
|
5770
5727
|
// packages/core/src/agent-context.js
|
|
5771
|
-
var VERSION = "0.6.
|
|
5728
|
+
var VERSION = "0.6.3";
|
|
5772
5729
|
var mountedComponents2 = [];
|
|
5773
5730
|
function registerComponent(component) {
|
|
5774
5731
|
if (!__DEV__) return;
|
|
@@ -5881,11 +5838,6 @@ export {
|
|
|
5881
5838
|
Textarea,
|
|
5882
5839
|
VisuallyHidden,
|
|
5883
5840
|
WhatError,
|
|
5884
|
-
_$createComponent,
|
|
5885
|
-
_$templateImpl as _$template,
|
|
5886
|
-
__getCacheSnapshot,
|
|
5887
|
-
__setDevToolsHooks,
|
|
5888
|
-
template as _template,
|
|
5889
5841
|
announce,
|
|
5890
5842
|
announceAssertive,
|
|
5891
5843
|
atom,
|
|
@@ -5919,6 +5871,7 @@ export {
|
|
|
5919
5871
|
getCollectedErrors,
|
|
5920
5872
|
getGuardrailConfig,
|
|
5921
5873
|
getHealth,
|
|
5874
|
+
getHydrationMismatchCount,
|
|
5922
5875
|
getMountedComponents,
|
|
5923
5876
|
getOwner,
|
|
5924
5877
|
getQueryData,
|
|
@@ -5975,12 +5928,10 @@ export {
|
|
|
5975
5928
|
useAriaChecked,
|
|
5976
5929
|
useAriaExpanded,
|
|
5977
5930
|
useAriaSelected,
|
|
5978
|
-
useCallback,
|
|
5979
5931
|
useClickOutside,
|
|
5980
5932
|
useComputed,
|
|
5981
5933
|
useContext,
|
|
5982
5934
|
useDescribedBy,
|
|
5983
|
-
useEffect,
|
|
5984
5935
|
useFetch,
|
|
5985
5936
|
useField,
|
|
5986
5937
|
useFocus,
|
|
@@ -5994,16 +5945,13 @@ export {
|
|
|
5994
5945
|
useLabelledBy,
|
|
5995
5946
|
useLocalStorage,
|
|
5996
5947
|
useMediaQuery,
|
|
5997
|
-
useMemo,
|
|
5998
5948
|
useQuery,
|
|
5999
5949
|
useReducer,
|
|
6000
|
-
useRef,
|
|
6001
5950
|
useRovingTabIndex,
|
|
6002
5951
|
useSWR,
|
|
6003
5952
|
useScheduledEffect,
|
|
6004
5953
|
useSignal,
|
|
6005
5954
|
useSkeleton,
|
|
6006
|
-
useState,
|
|
6007
5955
|
useTransition,
|
|
6008
5956
|
validateImports,
|
|
6009
5957
|
yupResolver,
|