what-core 0.6.1 → 0.6.3
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 +331 -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 +263 -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/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 +94 -24
- 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;
|
|
@@ -2366,6 +2589,7 @@ function setProp2(el, key, value) {
|
|
|
2366
2589
|
else if (value && typeof value === "object") value.current = el;
|
|
2367
2590
|
return;
|
|
2368
2591
|
}
|
|
2592
|
+
if (key === "key") return;
|
|
2369
2593
|
if (URL_ATTRS.has(key) || URL_ATTRS.has(key.toLowerCase())) {
|
|
2370
2594
|
if (!isSafeUrl(value)) {
|
|
2371
2595
|
if (typeof console !== "undefined") {
|
|
@@ -2446,8 +2670,14 @@ function isHydrating() {
|
|
|
2446
2670
|
function hydrate(vnode, container) {
|
|
2447
2671
|
_isHydrating = true;
|
|
2448
2672
|
_hydrationCursor = { parent: container, index: 0 };
|
|
2673
|
+
_hydrationMismatchCount = 0;
|
|
2449
2674
|
try {
|
|
2450
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
|
+
}
|
|
2451
2681
|
return result;
|
|
2452
2682
|
} finally {
|
|
2453
2683
|
_isHydrating = false;
|
|
@@ -2470,10 +2700,37 @@ function claimNode(parent) {
|
|
|
2470
2700
|
}
|
|
2471
2701
|
return null;
|
|
2472
2702
|
}
|
|
2473
|
-
|
|
2474
|
-
|
|
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
|
+
}
|
|
2475
2732
|
}
|
|
2476
|
-
function hydrateNode(vnode, parent) {
|
|
2733
|
+
function hydrateNode(vnode, parent, _componentName) {
|
|
2477
2734
|
if (vnode == null || typeof vnode === "boolean") {
|
|
2478
2735
|
return null;
|
|
2479
2736
|
}
|
|
@@ -2481,19 +2738,21 @@ function hydrateNode(vnode, parent) {
|
|
|
2481
2738
|
const existing = claimNode(parent);
|
|
2482
2739
|
const text = String(vnode);
|
|
2483
2740
|
if (existing && existing.nodeType === 3) {
|
|
2484
|
-
if (
|
|
2485
|
-
|
|
2486
|
-
`
|
|
2741
|
+
if (__DEV__ && existing.textContent !== text) {
|
|
2742
|
+
reportHydrationMismatch(
|
|
2743
|
+
`text "${text}"`,
|
|
2744
|
+
`text "${existing.textContent}"`,
|
|
2745
|
+
_componentName
|
|
2487
2746
|
);
|
|
2488
2747
|
existing.textContent = text;
|
|
2489
2748
|
}
|
|
2490
2749
|
return existing;
|
|
2491
2750
|
}
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2751
|
+
reportHydrationMismatch(
|
|
2752
|
+
`text node "${text}"`,
|
|
2753
|
+
existing ? existing.nodeName : "nothing",
|
|
2754
|
+
_componentName
|
|
2755
|
+
);
|
|
2497
2756
|
const textNode2 = document.createTextNode(text);
|
|
2498
2757
|
if (existing) {
|
|
2499
2758
|
parent.replaceChild(textNode2, existing);
|
|
@@ -2504,7 +2763,7 @@ function hydrateNode(vnode, parent) {
|
|
|
2504
2763
|
}
|
|
2505
2764
|
if (typeof vnode === "function") {
|
|
2506
2765
|
const initialValue = vnode();
|
|
2507
|
-
let current = hydrateNode(initialValue, parent);
|
|
2766
|
+
let current = hydrateNode(initialValue, parent, _componentName);
|
|
2508
2767
|
effect(() => {
|
|
2509
2768
|
const value = vnode();
|
|
2510
2769
|
if (!_isHydrating) {
|
|
@@ -2516,7 +2775,7 @@ function hydrateNode(vnode, parent) {
|
|
|
2516
2775
|
if (Array.isArray(vnode)) {
|
|
2517
2776
|
const nodes = [];
|
|
2518
2777
|
for (const child of vnode) {
|
|
2519
|
-
const node = hydrateNode(child, parent);
|
|
2778
|
+
const node = hydrateNode(child, parent, _componentName);
|
|
2520
2779
|
if (node) nodes.push(node);
|
|
2521
2780
|
}
|
|
2522
2781
|
return nodes.length === 1 ? nodes[0] : nodes;
|
|
@@ -2525,6 +2784,7 @@ function hydrateNode(vnode, parent) {
|
|
|
2525
2784
|
if (typeof vnode.tag === "function") {
|
|
2526
2785
|
const componentStack2 = getComponentStack();
|
|
2527
2786
|
const Component = vnode.tag;
|
|
2787
|
+
const compName = Component.displayName || Component.name || "Anonymous";
|
|
2528
2788
|
const props = vnode.props || {};
|
|
2529
2789
|
const children = vnode.children || [];
|
|
2530
2790
|
const ctx = {
|
|
@@ -2545,7 +2805,9 @@ function hydrateNode(vnode, parent) {
|
|
|
2545
2805
|
result = Component({ ...props, children: propsChildren });
|
|
2546
2806
|
} catch (error) {
|
|
2547
2807
|
componentStack2.pop();
|
|
2548
|
-
|
|
2808
|
+
if (__DEV__) {
|
|
2809
|
+
console.error("[what] Error in component during hydration:", compName, error);
|
|
2810
|
+
}
|
|
2549
2811
|
return null;
|
|
2550
2812
|
}
|
|
2551
2813
|
componentStack2.pop();
|
|
@@ -2562,28 +2824,36 @@ function hydrateNode(vnode, parent) {
|
|
|
2562
2824
|
}
|
|
2563
2825
|
});
|
|
2564
2826
|
}
|
|
2565
|
-
return hydrateNode(result, parent);
|
|
2827
|
+
return hydrateNode(result, parent, compName);
|
|
2566
2828
|
}
|
|
2567
2829
|
const existing = claimNode(parent);
|
|
2568
2830
|
const expectedTag = vnode.tag.toUpperCase();
|
|
2569
2831
|
if (existing && existing.nodeType === 1 && existing.nodeName === expectedTag) {
|
|
2570
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
|
+
}
|
|
2571
2841
|
const savedCursor = _hydrationCursor;
|
|
2572
2842
|
_hydrationCursor = { parent: existing, index: 0 };
|
|
2573
2843
|
const rawInner = vnode.props?.dangerouslySetInnerHTML?.__html;
|
|
2574
2844
|
if (rawInner == null) {
|
|
2575
2845
|
for (const child of vnode.children) {
|
|
2576
|
-
hydrateNode(child, existing);
|
|
2846
|
+
hydrateNode(child, existing, _componentName);
|
|
2577
2847
|
}
|
|
2578
2848
|
}
|
|
2579
2849
|
_hydrationCursor = savedCursor;
|
|
2580
2850
|
return existing;
|
|
2581
2851
|
}
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2852
|
+
reportHydrationMismatch(
|
|
2853
|
+
`<${vnode.tag}>`,
|
|
2854
|
+
existing ? existing.nodeName : "nothing",
|
|
2855
|
+
_componentName
|
|
2856
|
+
);
|
|
2587
2857
|
const newEl = document.createElement(vnode.tag);
|
|
2588
2858
|
for (const key in vnode.props || {}) {
|
|
2589
2859
|
if (key === "children" || key === "key") continue;
|
|
@@ -2640,6 +2910,18 @@ function hydrateElementProps(el, props) {
|
|
|
2640
2910
|
continue;
|
|
2641
2911
|
}
|
|
2642
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
|
+
}
|
|
2643
2925
|
}
|
|
2644
2926
|
}
|
|
2645
2927
|
|
|
@@ -2657,16 +2939,6 @@ function getHook(ctx) {
|
|
|
2657
2939
|
const index = ctx.hookIndex++;
|
|
2658
2940
|
return { index, exists: index < ctx.hooks.length };
|
|
2659
2941
|
}
|
|
2660
|
-
function useState(initial) {
|
|
2661
|
-
const ctx = getCtx("useState");
|
|
2662
|
-
const { index, exists } = getHook(ctx);
|
|
2663
|
-
if (!exists) {
|
|
2664
|
-
const s2 = signal(typeof initial === "function" ? initial() : initial);
|
|
2665
|
-
ctx.hooks[index] = s2;
|
|
2666
|
-
}
|
|
2667
|
-
const s = ctx.hooks[index];
|
|
2668
|
-
return [s, s.set];
|
|
2669
|
-
}
|
|
2670
2942
|
function useSignal(initial) {
|
|
2671
2943
|
const ctx = getCtx("useSignal");
|
|
2672
2944
|
const { index, exists } = getHook(ctx);
|
|
@@ -2683,97 +2955,6 @@ function useComputed(fn) {
|
|
|
2683
2955
|
}
|
|
2684
2956
|
return ctx.hooks[index];
|
|
2685
2957
|
}
|
|
2686
|
-
function useEffect(fn, deps) {
|
|
2687
|
-
const ctx = getCtx("useEffect");
|
|
2688
|
-
const { index, exists } = getHook(ctx);
|
|
2689
|
-
if (!exists) {
|
|
2690
|
-
ctx.hooks[index] = { cleanup: null, dispose: null };
|
|
2691
|
-
}
|
|
2692
|
-
if (__DEV__ && Array.isArray(deps) && deps.length > 0) {
|
|
2693
|
-
for (let i = 0; i < deps.length; i++) {
|
|
2694
|
-
const dep = deps[i];
|
|
2695
|
-
if (dep != null && typeof dep !== "function") {
|
|
2696
|
-
console.warn(
|
|
2697
|
-
`[what] useEffect dep at index ${i} is not a function. Did you mean to pass a signal? Use count instead of count()`
|
|
2698
|
-
);
|
|
2699
|
-
}
|
|
2700
|
-
}
|
|
2701
|
-
}
|
|
2702
|
-
const hook = ctx.hooks[index];
|
|
2703
|
-
if (hook.dispose) return;
|
|
2704
|
-
if (deps === void 0) {
|
|
2705
|
-
queueMicrotask(() => {
|
|
2706
|
-
if (ctx.disposed) return;
|
|
2707
|
-
hook.dispose = effect(() => {
|
|
2708
|
-
if (hook.cleanup) {
|
|
2709
|
-
try {
|
|
2710
|
-
hook.cleanup();
|
|
2711
|
-
} catch (e) {
|
|
2712
|
-
}
|
|
2713
|
-
hook.cleanup = null;
|
|
2714
|
-
}
|
|
2715
|
-
const result = fn();
|
|
2716
|
-
if (typeof result === "function") hook.cleanup = result;
|
|
2717
|
-
});
|
|
2718
|
-
ctx.effects = ctx.effects || [];
|
|
2719
|
-
ctx.effects.push(hook.dispose);
|
|
2720
|
-
});
|
|
2721
|
-
} else if (deps.length === 0) {
|
|
2722
|
-
queueMicrotask(() => {
|
|
2723
|
-
if (ctx.disposed) return;
|
|
2724
|
-
const result = fn();
|
|
2725
|
-
if (typeof result === "function") hook.cleanup = result;
|
|
2726
|
-
});
|
|
2727
|
-
hook.dispose = true;
|
|
2728
|
-
} else {
|
|
2729
|
-
queueMicrotask(() => {
|
|
2730
|
-
if (ctx.disposed) return;
|
|
2731
|
-
hook.dispose = effect(() => {
|
|
2732
|
-
for (let i = 0; i < deps.length; i++) {
|
|
2733
|
-
const dep = deps[i];
|
|
2734
|
-
if (typeof dep === "function" && dep._signal) {
|
|
2735
|
-
dep();
|
|
2736
|
-
}
|
|
2737
|
-
}
|
|
2738
|
-
if (hook.cleanup) {
|
|
2739
|
-
try {
|
|
2740
|
-
hook.cleanup();
|
|
2741
|
-
} catch (e) {
|
|
2742
|
-
}
|
|
2743
|
-
hook.cleanup = null;
|
|
2744
|
-
}
|
|
2745
|
-
const result = untrack(() => fn());
|
|
2746
|
-
if (typeof result === "function") hook.cleanup = result;
|
|
2747
|
-
});
|
|
2748
|
-
ctx.effects = ctx.effects || [];
|
|
2749
|
-
ctx.effects.push(hook.dispose);
|
|
2750
|
-
});
|
|
2751
|
-
}
|
|
2752
|
-
}
|
|
2753
|
-
function useMemo(fn, deps) {
|
|
2754
|
-
const ctx = getCtx("useMemo");
|
|
2755
|
-
const { index, exists } = getHook(ctx);
|
|
2756
|
-
if (!exists) {
|
|
2757
|
-
ctx.hooks[index] = { computed: computed(fn) };
|
|
2758
|
-
}
|
|
2759
|
-
return ctx.hooks[index].computed;
|
|
2760
|
-
}
|
|
2761
|
-
function useCallback(fn, deps) {
|
|
2762
|
-
const ctx = getCtx("useCallback");
|
|
2763
|
-
const { index, exists } = getHook(ctx);
|
|
2764
|
-
if (!exists) {
|
|
2765
|
-
ctx.hooks[index] = { callback: fn };
|
|
2766
|
-
}
|
|
2767
|
-
return ctx.hooks[index].callback;
|
|
2768
|
-
}
|
|
2769
|
-
function useRef(initial) {
|
|
2770
|
-
const ctx = getCtx("useRef");
|
|
2771
|
-
const { index, exists } = getHook(ctx);
|
|
2772
|
-
if (!exists) {
|
|
2773
|
-
ctx.hooks[index] = { current: initial };
|
|
2774
|
-
}
|
|
2775
|
-
return ctx.hooks[index];
|
|
2776
|
-
}
|
|
2777
2958
|
function useContext(context) {
|
|
2778
2959
|
let ctx = getCurrentComponent();
|
|
2779
2960
|
if (__DEV__ && !ctx) {
|
|
@@ -4842,18 +5023,6 @@ function clearCache() {
|
|
|
4842
5023
|
lastFetchTimestamps.clear();
|
|
4843
5024
|
inFlightRequests.clear();
|
|
4844
5025
|
}
|
|
4845
|
-
function __getCacheSnapshot() {
|
|
4846
|
-
const entries = [];
|
|
4847
|
-
for (const [key, sig] of cacheSignals) {
|
|
4848
|
-
entries.push({
|
|
4849
|
-
key,
|
|
4850
|
-
data: sig.peek(),
|
|
4851
|
-
error: errorSignals.has(key) ? errorSignals.get(key).peek() : null,
|
|
4852
|
-
isValidating: validatingSignals.has(key) ? validatingSignals.get(key).peek() : false
|
|
4853
|
-
});
|
|
4854
|
-
}
|
|
4855
|
-
return entries;
|
|
4856
|
-
}
|
|
4857
5026
|
|
|
4858
5027
|
// packages/core/src/form.js
|
|
4859
5028
|
function useForm(options = {}) {
|
|
@@ -5311,213 +5480,6 @@ function ErrorMessage({ name, formState, errors, render }) {
|
|
|
5311
5480
|
return h("span", { class: "what-error", role: "alert" }, error.message);
|
|
5312
5481
|
}
|
|
5313
5482
|
|
|
5314
|
-
// packages/core/src/errors.js
|
|
5315
|
-
var ERROR_CODES = {
|
|
5316
|
-
INFINITE_EFFECT: {
|
|
5317
|
-
code: "ERR_INFINITE_EFFECT",
|
|
5318
|
-
severity: "error",
|
|
5319
|
-
template: 'Effect "{{effectName}}" exceeded 25 flush iterations \u2014 likely an infinite loop.',
|
|
5320
|
-
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.",
|
|
5321
|
-
codeExample: `// Bad \u2014 reads and writes count, creating a cycle:
|
|
5322
|
-
effect(() => { count(count() + 1); });
|
|
5323
|
-
|
|
5324
|
-
// Good \u2014 use untrack() so the read doesn't subscribe:
|
|
5325
|
-
effect(() => { count(untrack(count) + 1); });
|
|
5326
|
-
|
|
5327
|
-
// Better \u2014 split into separate logic:
|
|
5328
|
-
const doubled = computed(() => count() * 2);`
|
|
5329
|
-
},
|
|
5330
|
-
MISSING_SIGNAL_READ: {
|
|
5331
|
-
code: "ERR_MISSING_SIGNAL_READ",
|
|
5332
|
-
severity: "warning",
|
|
5333
|
-
template: 'Signal "{{signalName}}" used without calling () \u2014 renders as "[Function]" instead of its value.',
|
|
5334
|
-
suggestion: "Signals are functions. Call them to read: count() not count. In JSX: {count()} not {count}.",
|
|
5335
|
-
codeExample: `// Bad \u2014 signal reference, not value:
|
|
5336
|
-
<span>{count}</span> // renders "[Function]"
|
|
5337
|
-
|
|
5338
|
-
// Good \u2014 call the signal:
|
|
5339
|
-
<span>{count()}</span> // renders the actual value`
|
|
5340
|
-
},
|
|
5341
|
-
HYDRATION_MISMATCH: {
|
|
5342
|
-
code: "ERR_HYDRATION_MISMATCH",
|
|
5343
|
-
severity: "error",
|
|
5344
|
-
template: 'Hydration mismatch in component "{{component}}": server rendered "{{serverHTML}}" but client expects "{{clientHTML}}".',
|
|
5345
|
-
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.",
|
|
5346
|
-
codeExample: `// Bad \u2014 different on server vs client:
|
|
5347
|
-
function App() {
|
|
5348
|
-
return <p>{window.innerWidth}</p>;
|
|
5349
|
-
}
|
|
5350
|
-
|
|
5351
|
-
// Good \u2014 use onMount for client-only values:
|
|
5352
|
-
function App() {
|
|
5353
|
-
const width = signal(0);
|
|
5354
|
-
onMount(() => width(window.innerWidth));
|
|
5355
|
-
return <p>{width()}</p>;
|
|
5356
|
-
}`
|
|
5357
|
-
},
|
|
5358
|
-
ORPHAN_EFFECT: {
|
|
5359
|
-
code: "ERR_ORPHAN_EFFECT",
|
|
5360
|
-
severity: "warning",
|
|
5361
|
-
template: 'Effect "{{effectName}}" was created outside a reactive root \u2014 it will never be cleaned up.',
|
|
5362
|
-
suggestion: "Wrap effect creation in createRoot() or create effects inside component functions where they are automatically tracked.",
|
|
5363
|
-
codeExample: `// Bad \u2014 orphaned, leaks memory:
|
|
5364
|
-
effect(() => console.log(count()));
|
|
5365
|
-
|
|
5366
|
-
// Good \u2014 inside a root with cleanup:
|
|
5367
|
-
createRoot(dispose => {
|
|
5368
|
-
effect(() => console.log(count()));
|
|
5369
|
-
// later: dispose() cleans up
|
|
5370
|
-
});`
|
|
5371
|
-
},
|
|
5372
|
-
SIGNAL_WRITE_IN_RENDER: {
|
|
5373
|
-
code: "ERR_SIGNAL_WRITE_IN_RENDER",
|
|
5374
|
-
severity: "error",
|
|
5375
|
-
template: 'Signal "{{signalName}}" written during render of component "{{component}}". This triggers re-execution.',
|
|
5376
|
-
suggestion: "Move signal writes into event handlers, effects, or onMount(). The component body should only read signals, not write them.",
|
|
5377
|
-
codeExample: `// Bad \u2014 write during render:
|
|
5378
|
-
function Counter() {
|
|
5379
|
-
count(count() + 1); // triggers infinite loop
|
|
5380
|
-
return <span>{count()}</span>;
|
|
5381
|
-
}
|
|
5382
|
-
|
|
5383
|
-
// Good \u2014 write in event handler:
|
|
5384
|
-
function Counter() {
|
|
5385
|
-
return <button onclick={() => count(c => c + 1)}>{count()}</button>;
|
|
5386
|
-
}`
|
|
5387
|
-
},
|
|
5388
|
-
MISSING_CLEANUP: {
|
|
5389
|
-
code: "ERR_MISSING_CLEANUP",
|
|
5390
|
-
severity: "warning",
|
|
5391
|
-
template: 'Effect sets up "{{resource}}" but does not return a cleanup function.',
|
|
5392
|
-
suggestion: "Effects that add event listeners, set timers, or open connections should return a cleanup function to prevent memory leaks.",
|
|
5393
|
-
codeExample: `// Bad \u2014 no cleanup:
|
|
5394
|
-
effect(() => {
|
|
5395
|
-
window.addEventListener('resize', handler);
|
|
5396
|
-
});
|
|
5397
|
-
|
|
5398
|
-
// Good \u2014 return cleanup:
|
|
5399
|
-
effect(() => {
|
|
5400
|
-
window.addEventListener('resize', handler);
|
|
5401
|
-
return () => window.removeEventListener('resize', handler);
|
|
5402
|
-
});`
|
|
5403
|
-
},
|
|
5404
|
-
UNSAFE_INNERHTML: {
|
|
5405
|
-
code: "ERR_UNSAFE_INNERHTML",
|
|
5406
|
-
severity: "warning",
|
|
5407
|
-
template: "innerHTML set on element without using the __html safety marker.",
|
|
5408
|
-
suggestion: "Use the html tagged template literal or pass { __html: content } to mark innerHTML as intentional and reviewed.",
|
|
5409
|
-
codeExample: `// Bad \u2014 raw innerHTML (XSS risk):
|
|
5410
|
-
<div innerHTML={userInput} />
|
|
5411
|
-
|
|
5412
|
-
// Good \u2014 explicit opt-in:
|
|
5413
|
-
<div innerHTML={{ __html: sanitizedContent }} />
|
|
5414
|
-
|
|
5415
|
-
// Better \u2014 use the html template literal:
|
|
5416
|
-
html\`<div>\${sanitizedContent}</div>\``
|
|
5417
|
-
},
|
|
5418
|
-
MISSING_KEY: {
|
|
5419
|
-
code: "ERR_MISSING_KEY",
|
|
5420
|
-
severity: "warning",
|
|
5421
|
-
template: 'List rendered without key prop in component "{{component}}". Items may re-order incorrectly.',
|
|
5422
|
-
suggestion: "Add a unique key prop to each item in a list. Use a stable identifier (like an ID), not the array index.",
|
|
5423
|
-
codeExample: `// Bad \u2014 no key:
|
|
5424
|
-
<For each={items()}>{item => <li>{item.name}</li>}</For>
|
|
5425
|
-
|
|
5426
|
-
// Good \u2014 stable key:
|
|
5427
|
-
<For each={items()}>{item => <li key={item.id}>{item.name}</li>}</For>`
|
|
5428
|
-
}
|
|
5429
|
-
};
|
|
5430
|
-
var WhatError = class extends Error {
|
|
5431
|
-
constructor({ code, message, suggestion, file, line, component, signal: signal2, effect: effect2 }) {
|
|
5432
|
-
super(message);
|
|
5433
|
-
this.name = "WhatError";
|
|
5434
|
-
this.code = code;
|
|
5435
|
-
this.suggestion = suggestion;
|
|
5436
|
-
this.file = file;
|
|
5437
|
-
this.line = line;
|
|
5438
|
-
this.component = component;
|
|
5439
|
-
this.signal = signal2;
|
|
5440
|
-
this.effect = effect2;
|
|
5441
|
-
}
|
|
5442
|
-
toJSON() {
|
|
5443
|
-
return {
|
|
5444
|
-
code: this.code,
|
|
5445
|
-
message: this.message,
|
|
5446
|
-
suggestion: this.suggestion,
|
|
5447
|
-
file: this.file,
|
|
5448
|
-
line: this.line,
|
|
5449
|
-
component: this.component,
|
|
5450
|
-
signal: this.signal,
|
|
5451
|
-
effect: this.effect
|
|
5452
|
-
};
|
|
5453
|
-
}
|
|
5454
|
-
};
|
|
5455
|
-
function createWhatError(errorCode, context = {}) {
|
|
5456
|
-
const def = typeof errorCode === "string" ? ERROR_CODES[errorCode] : errorCode;
|
|
5457
|
-
if (!def) {
|
|
5458
|
-
return new WhatError({
|
|
5459
|
-
code: "ERR_UNKNOWN",
|
|
5460
|
-
message: `Unknown error: ${errorCode}`,
|
|
5461
|
-
suggestion: "Check the error code and try again."
|
|
5462
|
-
});
|
|
5463
|
-
}
|
|
5464
|
-
let message = def.template;
|
|
5465
|
-
for (const [key, val] of Object.entries(context)) {
|
|
5466
|
-
message = message.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), String(val));
|
|
5467
|
-
}
|
|
5468
|
-
message = message.replace(/\{\{[^}]+\}\}/g, "(unknown)");
|
|
5469
|
-
return new WhatError({
|
|
5470
|
-
code: def.code,
|
|
5471
|
-
message,
|
|
5472
|
-
suggestion: def.suggestion,
|
|
5473
|
-
file: context.file,
|
|
5474
|
-
line: context.line,
|
|
5475
|
-
component: context.component,
|
|
5476
|
-
signal: context.signal || context.signalName,
|
|
5477
|
-
effect: context.effect || context.effectName
|
|
5478
|
-
});
|
|
5479
|
-
}
|
|
5480
|
-
var collectedErrors = [];
|
|
5481
|
-
var MAX_COLLECTED = 200;
|
|
5482
|
-
function collectError(whatError) {
|
|
5483
|
-
if (!__DEV__) return;
|
|
5484
|
-
collectedErrors.push({
|
|
5485
|
-
...whatError.toJSON(),
|
|
5486
|
-
timestamp: Date.now()
|
|
5487
|
-
});
|
|
5488
|
-
if (collectedErrors.length > MAX_COLLECTED) {
|
|
5489
|
-
collectedErrors = collectedErrors.slice(-MAX_COLLECTED);
|
|
5490
|
-
}
|
|
5491
|
-
}
|
|
5492
|
-
function getCollectedErrors(since) {
|
|
5493
|
-
if (since) return collectedErrors.filter((e) => e.timestamp > since);
|
|
5494
|
-
return collectedErrors.slice();
|
|
5495
|
-
}
|
|
5496
|
-
function clearCollectedErrors() {
|
|
5497
|
-
collectedErrors = [];
|
|
5498
|
-
}
|
|
5499
|
-
function classifyError(err, context = {}) {
|
|
5500
|
-
const msg = err?.message || String(err);
|
|
5501
|
-
if (msg.includes("infinite effect loop") || msg.includes("25 iterations")) {
|
|
5502
|
-
return createWhatError("INFINITE_EFFECT", context);
|
|
5503
|
-
}
|
|
5504
|
-
if (msg.includes("hydration") || msg.includes("Hydration")) {
|
|
5505
|
-
return createWhatError("HYDRATION_MISMATCH", context);
|
|
5506
|
-
}
|
|
5507
|
-
if (msg.includes("Signal.set() called inside a computed")) {
|
|
5508
|
-
return createWhatError("SIGNAL_WRITE_IN_RENDER", {
|
|
5509
|
-
...context,
|
|
5510
|
-
signalName: msg.match(/signal: (\w+)/)?.[1] || context.signalName
|
|
5511
|
-
});
|
|
5512
|
-
}
|
|
5513
|
-
return new WhatError({
|
|
5514
|
-
code: "ERR_RUNTIME",
|
|
5515
|
-
message: msg,
|
|
5516
|
-
suggestion: "Check the stack trace and component context for more details.",
|
|
5517
|
-
...context
|
|
5518
|
-
});
|
|
5519
|
-
}
|
|
5520
|
-
|
|
5521
5483
|
// packages/core/src/guardrails.js
|
|
5522
5484
|
var guardrails = {
|
|
5523
5485
|
signalReadDetection: true,
|
|
@@ -5578,10 +5540,8 @@ var VALID_EXPORTS = /* @__PURE__ */ new Set([
|
|
|
5578
5540
|
"getOwner",
|
|
5579
5541
|
"runWithOwner",
|
|
5580
5542
|
"onRootCleanup",
|
|
5581
|
-
"__setDevToolsHooks",
|
|
5582
5543
|
// Rendering
|
|
5583
5544
|
"template",
|
|
5584
|
-
"_template",
|
|
5585
5545
|
"svgTemplate",
|
|
5586
5546
|
"insert",
|
|
5587
5547
|
"mapArray",
|
|
@@ -5592,7 +5552,6 @@ var VALID_EXPORTS = /* @__PURE__ */ new Set([
|
|
|
5592
5552
|
"classList",
|
|
5593
5553
|
"hydrate",
|
|
5594
5554
|
"isHydrating",
|
|
5595
|
-
"_$createComponent",
|
|
5596
5555
|
// JSX
|
|
5597
5556
|
"h",
|
|
5598
5557
|
"Fragment",
|
|
@@ -5705,7 +5664,6 @@ var VALID_EXPORTS = /* @__PURE__ */ new Set([
|
|
|
5705
5664
|
"setQueryData",
|
|
5706
5665
|
"getQueryData",
|
|
5707
5666
|
"clearCache",
|
|
5708
|
-
"__getCacheSnapshot",
|
|
5709
5667
|
// Form
|
|
5710
5668
|
"useForm",
|
|
5711
5669
|
"useField",
|
|
@@ -5767,7 +5725,7 @@ function levenshtein(a, b) {
|
|
|
5767
5725
|
}
|
|
5768
5726
|
|
|
5769
5727
|
// packages/core/src/agent-context.js
|
|
5770
|
-
var VERSION = "0.6.
|
|
5728
|
+
var VERSION = "0.6.3";
|
|
5771
5729
|
var mountedComponents2 = [];
|
|
5772
5730
|
function registerComponent(component) {
|
|
5773
5731
|
if (!__DEV__) return;
|
|
@@ -5880,11 +5838,6 @@ export {
|
|
|
5880
5838
|
Textarea,
|
|
5881
5839
|
VisuallyHidden,
|
|
5882
5840
|
WhatError,
|
|
5883
|
-
_$createComponent,
|
|
5884
|
-
_$templateImpl as _$template,
|
|
5885
|
-
__getCacheSnapshot,
|
|
5886
|
-
__setDevToolsHooks,
|
|
5887
|
-
template as _template,
|
|
5888
5841
|
announce,
|
|
5889
5842
|
announceAssertive,
|
|
5890
5843
|
atom,
|
|
@@ -5918,6 +5871,7 @@ export {
|
|
|
5918
5871
|
getCollectedErrors,
|
|
5919
5872
|
getGuardrailConfig,
|
|
5920
5873
|
getHealth,
|
|
5874
|
+
getHydrationMismatchCount,
|
|
5921
5875
|
getMountedComponents,
|
|
5922
5876
|
getOwner,
|
|
5923
5877
|
getQueryData,
|
|
@@ -5974,12 +5928,10 @@ export {
|
|
|
5974
5928
|
useAriaChecked,
|
|
5975
5929
|
useAriaExpanded,
|
|
5976
5930
|
useAriaSelected,
|
|
5977
|
-
useCallback,
|
|
5978
5931
|
useClickOutside,
|
|
5979
5932
|
useComputed,
|
|
5980
5933
|
useContext,
|
|
5981
5934
|
useDescribedBy,
|
|
5982
|
-
useEffect,
|
|
5983
5935
|
useFetch,
|
|
5984
5936
|
useField,
|
|
5985
5937
|
useFocus,
|
|
@@ -5993,16 +5945,13 @@ export {
|
|
|
5993
5945
|
useLabelledBy,
|
|
5994
5946
|
useLocalStorage,
|
|
5995
5947
|
useMediaQuery,
|
|
5996
|
-
useMemo,
|
|
5997
5948
|
useQuery,
|
|
5998
5949
|
useReducer,
|
|
5999
|
-
useRef,
|
|
6000
5950
|
useRovingTabIndex,
|
|
6001
5951
|
useSWR,
|
|
6002
5952
|
useScheduledEffect,
|
|
6003
5953
|
useSignal,
|
|
6004
5954
|
useSkeleton,
|
|
6005
|
-
useState,
|
|
6006
5955
|
useTransition,
|
|
6007
5956
|
validateImports,
|
|
6008
5957
|
yupResolver,
|