what-core 0.5.3 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # what-core
2
+
3
+ The reactive engine behind [What Framework](https://whatfw.com). Provides signals, fine-grained reactivity, components, hooks, and DOM rendering -- all without a virtual DOM diffing step.
4
+
5
+ Most users should install [`what-framework`](https://www.npmjs.com/package/what-framework) instead. `what-core` is the internal engine consumed by other What packages.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install what-core
11
+ ```
12
+
13
+ ## Reactive Primitives
14
+
15
+ ```js
16
+ import { signal, computed, effect, batch, untrack } from 'what-core';
17
+
18
+ const count = signal(0);
19
+
20
+ // Read
21
+ count(); // 0
22
+
23
+ // Write
24
+ count.set(5);
25
+ count.set(c => c + 1);
26
+
27
+ // Derived value
28
+ const doubled = computed(() => count() * 2);
29
+
30
+ // Side effects
31
+ effect(() => {
32
+ console.log('Count:', count());
33
+ });
34
+
35
+ // Batch updates (effects run once at the end)
36
+ batch(() => {
37
+ a.set(1);
38
+ b.set(2);
39
+ });
40
+
41
+ // Read without subscribing
42
+ untrack(() => someSignal());
43
+ count.peek();
44
+ ```
45
+
46
+ ## Hooks
47
+
48
+ React-compatible hooks backed by signals internally.
49
+
50
+ ```js
51
+ import {
52
+ useState, useEffect, useMemo, useCallback,
53
+ useRef, useReducer, useContext, createContext,
54
+ onMount, onCleanup,
55
+ } from 'what-core';
56
+
57
+ const [count, setCount] = useState(0);
58
+
59
+ useEffect(() => {
60
+ const id = setInterval(tick, 1000);
61
+ return () => clearInterval(id);
62
+ }, []);
63
+ ```
64
+
65
+ ## Components
66
+
67
+ ```js
68
+ import { h, mount, Fragment, memo, lazy, Suspense, ErrorBoundary, Show, For } from 'what-core';
69
+
70
+ function Counter() {
71
+ const count = signal(0);
72
+ return h('button', { onclick: () => count.set(c => c + 1) }, () => count());
73
+ }
74
+
75
+ mount(h(Counter), '#app');
76
+ ```
77
+
78
+ ## Additional Modules
79
+
80
+ | Export path | Contents |
81
+ |---|---|
82
+ | `what-core` | Signals, hooks, components, store, forms, data fetching, animation, a11y, skeleton loaders |
83
+ | `what-core/render` | Fine-grained rendering primitives (`template`, `insert`, `spread`, `delegateEvents`) |
84
+ | `what-core/jsx-runtime` | JSX automatic runtime |
85
+ | `what-core/testing` | Test utilities |
86
+
87
+ ## API Overview
88
+
89
+ **Reactivity** -- `signal`, `computed`, `effect`, `batch`, `untrack`, `flushSync`, `createRoot`
90
+
91
+ **Rendering** -- `h`, `Fragment`, `html`, `mount`, `template`, `insert`, `spread`, `delegateEvents`
92
+
93
+ **Hooks** -- `useState`, `useSignal`, `useComputed`, `useEffect`, `useMemo`, `useCallback`, `useRef`, `useContext`, `useReducer`, `createContext`, `onMount`, `onCleanup`, `createResource`
94
+
95
+ **Components** -- `memo`, `lazy`, `Suspense`, `ErrorBoundary`, `Show`, `For`, `Switch`, `Match`, `Island`, `Portal`
96
+
97
+ **Store** -- `createStore`, `derived`, `atom`
98
+
99
+ **Data Fetching** -- `useSWR`, `useQuery`, `useInfiniteQuery`, `invalidateQueries`, `prefetchQuery`
100
+
101
+ **Forms** -- `useForm`, `useField`, `rules`, `zodResolver`, `Input`, `Select`, `Checkbox`, `ErrorMessage`
102
+
103
+ **Animation** -- `spring`, `tween`, `easings`, `useGesture`, `useTransition`
104
+
105
+ **Accessibility** -- `useFocusTrap`, `FocusTrap`, `announce`, `SkipLink`, `useRovingTabIndex`, `VisuallyHidden`, `useId`
106
+
107
+ **Scheduler** -- `scheduleRead`, `scheduleWrite`, `measure`, `mutate`, `onResize`, `onIntersect`
108
+
109
+ **Head** -- `Head`, `clearHead`
110
+
111
+ ## Links
112
+
113
+ - [Documentation](https://whatfw.com)
114
+ - [GitHub](https://github.com/zvndev/what-fw)
115
+ - [Benchmarks](https://benchmarks.whatfw.com)
116
+
117
+ ## License
118
+
119
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "what-core",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "description": "What Framework - The closest framework to vanilla JS",
5
5
  "type": "module",
6
6
  "main": "dist/what.js",
@@ -53,5 +53,5 @@
53
53
  "bugs": {
54
54
  "url": "https://github.com/zvndev/what-fw/issues"
55
55
  },
56
- "homepage": "https://whatframework.dev"
56
+ "homepage": "https://whatfw.com"
57
57
  }
package/src/data.js CHANGED
@@ -636,3 +636,20 @@ export function clearCache() {
636
636
  lastFetchTimestamps.clear();
637
637
  inFlightRequests.clear();
638
638
  }
639
+
640
+ /**
641
+ * Get a snapshot of all cache entries for devtools.
642
+ * @internal
643
+ */
644
+ export function __getCacheSnapshot() {
645
+ const entries = [];
646
+ for (const [key, sig] of cacheSignals) {
647
+ entries.push({
648
+ key,
649
+ data: sig.peek(),
650
+ error: errorSignals.has(key) ? errorSignals.get(key).peek() : null,
651
+ isValidating: validatingSignals.has(key) ? validatingSignals.get(key).peek() : false,
652
+ });
653
+ }
654
+ return entries;
655
+ }
package/src/dom.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // Components use <what-c> wrapper elements (display:contents) for clean reconciliation.
4
4
  // No virtual DOM tree kept in memory — we diff against the live DOM.
5
5
 
6
- import { effect, batch, untrack, signal } from './reactive.js';
6
+ import { effect, batch, untrack, signal, __DEV__, __devtools } from './reactive.js';
7
7
  import { reportError, _injectGetCurrentComponent, shallowEqual } from './components.js';
8
8
  import { _setComponentRef } from './helpers.js';
9
9
 
@@ -97,6 +97,7 @@ function disposeComponent(ctx) {
97
97
  try { dispose(); } catch (e) { /* effect already disposed */ }
98
98
  }
99
99
 
100
+ if (__DEV__ && __devtools?.onComponentUnmount) __devtools.onComponentUnmount(ctx);
100
101
  mountedComponents.delete(ctx);
101
102
  }
102
103
 
@@ -304,6 +305,7 @@ function createComponent(vnode, parent, isSvg) {
304
305
 
305
306
  // Track for disposal
306
307
  mountedComponents.add(ctx);
308
+ if (__DEV__ && __devtools?.onComponentMount) __devtools.onComponentMount(ctx);
307
309
 
308
310
  // Props signal for reactive updates from parent
309
311
  // Match React's children semantics: 0→undefined, 1→single child, N→array
package/src/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // The closest framework to vanilla JS.
3
3
 
4
4
  // Reactive primitives
5
- export { signal, computed, effect, memo as signalMemo, batch, untrack, flushSync, createRoot } from './reactive.js';
5
+ export { signal, computed, effect, memo as signalMemo, batch, untrack, flushSync, createRoot, __setDevToolsHooks } from './reactive.js';
6
6
 
7
7
  // Fine-grained rendering primitives
8
8
  export { template, insert, mapArray, spread, setProp, delegateEvents, on, classList } from './render.js';
@@ -132,6 +132,7 @@ export {
132
132
  setQueryData,
133
133
  getQueryData,
134
134
  clearCache,
135
+ __getCacheSnapshot,
135
136
  } from './data.js';
136
137
 
137
138
  // Form utilities
package/src/reactive.js CHANGED
@@ -2,7 +2,9 @@
2
2
  // Signals + Effects: fine-grained reactivity without virtual DOM overhead
3
3
 
4
4
  // Dev-mode flag — build tools can dead-code-eliminate when false
5
- export const __DEV__ = typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production' || true;
5
+ export const __DEV__ = typeof process !== 'undefined'
6
+ ? process.env?.NODE_ENV !== 'production'
7
+ : true;
6
8
 
7
9
  // DevTools hooks — set by what-devtools when installed.
8
10
  // These are no-ops in production (dead-code eliminated with __DEV__).
@@ -22,7 +24,7 @@ let pendingEffects = [];
22
24
  // A reactive value. Reading inside an effect auto-tracks the dependency.
23
25
  // Writing triggers only the effects that depend on this signal.
24
26
 
25
- export function signal(initial) {
27
+ export function signal(initial, debugName) {
26
28
  let value = initial;
27
29
  const subs = new Set();
28
30
 
@@ -59,6 +61,10 @@ export function signal(initial) {
59
61
  };
60
62
 
61
63
  sig._signal = true;
64
+ if (__DEV__) {
65
+ sig._subs = subs;
66
+ if (debugName) sig._debugName = debugName;
67
+ }
62
68
 
63
69
  // Notify devtools of signal creation
64
70
  if (__DEV__ && __devtools) __devtools.onSignalCreate(sig);
@@ -175,9 +181,13 @@ function _runEffect(e) {
175
181
  try {
176
182
  const result = e.fn();
177
183
  if (typeof result === 'function') e._cleanup = result;
184
+ } catch (err) {
185
+ if (__devtools?.onError) __devtools.onError(err, { type: 'effect', effect: e });
186
+ if (__DEV__) console.warn('[what] Error in stable effect:', err);
178
187
  } finally {
179
188
  currentEffect = prev;
180
189
  }
190
+ if (__DEV__ && __devtools?.onEffectRun) __devtools.onEffectRun(e);
181
191
  return;
182
192
  }
183
193
 
@@ -185,6 +195,7 @@ function _runEffect(e) {
185
195
  // Run effect cleanup from previous run
186
196
  if (e._cleanup) {
187
197
  try { e._cleanup(); } catch (err) {
198
+ if (__devtools?.onError) __devtools.onError(err, { type: 'effect-cleanup', effect: e });
188
199
  if (__DEV__) console.warn('[what] Error in effect cleanup:', err);
189
200
  }
190
201
  e._cleanup = null;
@@ -197,9 +208,13 @@ function _runEffect(e) {
197
208
  if (typeof result === 'function') {
198
209
  e._cleanup = result;
199
210
  }
211
+ } catch (err) {
212
+ if (__devtools?.onError) __devtools.onError(err, { type: 'effect', effect: e });
213
+ throw err;
200
214
  } finally {
201
215
  currentEffect = prev;
202
216
  }
217
+ if (__DEV__ && __devtools?.onEffectRun) __devtools.onEffectRun(e);
203
218
  }
204
219
 
205
220
  function _disposeEffect(e) {
@@ -238,6 +253,7 @@ function notify(subs) {
238
253
  e._cleanup = result;
239
254
  }
240
255
  } catch (err) {
256
+ if (__devtools?.onError) __devtools.onError(err, { type: 'effect', effect: e });
241
257
  if (__DEV__) console.warn('[what] Error in stable effect:', err);
242
258
  } finally {
243
259
  currentEffect = prev;