what-core 0.11.7 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-NCPX66TV.min.js +1 -0
- package/dist/chunk-RXISSKLI.min.js +11 -0
- package/dist/index.min.js +18 -15
- package/dist/render.min.js +1 -1
- package/dist/testing.min.js +1 -1
- package/index.d.ts +182 -11
- package/jsx-dev-runtime.d.ts +2 -2
- package/jsx-runtime.d.ts +2 -2
- package/package.json +1 -1
- package/render.d.ts +15 -1
- package/src/a11y.js +34 -2
- package/src/agent-context.js +1 -1
- package/src/components.js +175 -82
- package/src/data.js +47 -4
- package/src/dom.js +269 -24
- package/src/errors.js +29 -0
- package/src/head.js +35 -5
- package/src/hooks.js +3 -0
- package/src/index.js +3 -0
- package/src/reactive.js +5 -5
- package/src/render.js +99 -35
- package/src/server-context.js +12 -0
- package/testing.d.ts +36 -1
- package/dist/chunk-5QCEMXNL.min.js +0 -1
- package/dist/chunk-H67HFVDV.min.js +0 -1
package/index.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export interface Computed<T> {
|
|
|
24
24
|
_signal: true;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export function signal<T>(initial: T): Signal<T>;
|
|
27
|
+
export function signal<T>(initial: T, debugName?: string): Signal<T>;
|
|
28
28
|
export function computed<T>(fn: () => T): Computed<T>;
|
|
29
29
|
export function effect(fn: () => void | (() => void), opts?: { stable?: boolean }): () => void;
|
|
30
30
|
export function signalMemo<T>(fn: () => T): Computed<T>;
|
|
@@ -33,12 +33,36 @@ export function untrack<T>(fn: () => T): T;
|
|
|
33
33
|
export function flushSync(): void;
|
|
34
34
|
export function createRoot<T>(fn: (dispose: () => void) => T): T;
|
|
35
35
|
|
|
36
|
+
/** Opaque ownership scope handle. Pair with runWithOwner() for async work. */
|
|
37
|
+
export interface Owner {
|
|
38
|
+
disposals: Array<() => void>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getOwner(): Owner | null;
|
|
42
|
+
export function runWithOwner<T>(owner: Owner | null, fn: () => T): T;
|
|
43
|
+
/** Register a cleanup with the current owner/root (root-level onCleanup). */
|
|
44
|
+
export function onRootCleanup(fn: () => void): void;
|
|
45
|
+
|
|
36
46
|
// --- Virtual DOM ---
|
|
37
47
|
|
|
38
48
|
export type PrimitiveChild = string | number | boolean | null | undefined;
|
|
39
49
|
export type VNodeChild = PrimitiveChild | VNode | (() => VNodeChild) | VNodeChild[];
|
|
40
50
|
|
|
41
|
-
|
|
51
|
+
/** A component may legitimately render nothing, so `null` is part of the contract. */
|
|
52
|
+
export type Component<P = {}> = ((props: P & { children?: VNodeChild }) => VNode | null) & {
|
|
53
|
+
/**
|
|
54
|
+
* Opt out of realizing compiled children before the component runs.
|
|
55
|
+
*
|
|
56
|
+
* A component that establishes a scope its children depend on (a context
|
|
57
|
+
* provider, an error or suspense boundary) receives `props.children` as a
|
|
58
|
+
* zero-argument factory instead of built nodes, and the runtime realizes it
|
|
59
|
+
* once that scope exists. `ErrorBoundary`, `Suspense` and `Context.Provider`
|
|
60
|
+
* set this themselves, and the compiler keeps a component that only forwards
|
|
61
|
+
* its children lazy, so this is only needed by a component that both
|
|
62
|
+
* inspects its children and forwards them into a boundary or a provider.
|
|
63
|
+
*/
|
|
64
|
+
_deferChildren?: boolean;
|
|
65
|
+
};
|
|
42
66
|
|
|
43
67
|
export interface VNode<P = Record<string, any>> {
|
|
44
68
|
tag: string | Component<P>;
|
|
@@ -54,15 +78,20 @@ export function h<P extends Record<string, any>>(
|
|
|
54
78
|
...children: VNodeChild[]
|
|
55
79
|
): VNode<P>;
|
|
56
80
|
|
|
57
|
-
export function Fragment(props: { children?: VNodeChild }):
|
|
81
|
+
export function Fragment(props: { children?: VNodeChild }): VNode;
|
|
58
82
|
export function html(strings: TemplateStringsArray, ...values: any[]): VNode | VNode[];
|
|
59
83
|
|
|
60
84
|
// --- DOM ---
|
|
61
85
|
|
|
62
86
|
export function mount(vnode: VNodeChild, container: string | Element): () => void;
|
|
63
87
|
|
|
88
|
+
/** Attach reactive bindings to server-rendered DOM instead of creating it. */
|
|
89
|
+
export function hydrate(vnode: VNodeChild, container: Element): Node | null;
|
|
90
|
+
export function isHydrating(): boolean;
|
|
91
|
+
|
|
64
92
|
// Fine-grained rendering primitives
|
|
65
93
|
export function template(html: string): () => Element;
|
|
94
|
+
export function svgTemplate(html: string): () => Element;
|
|
66
95
|
export function insert(parent: Node, child: any, marker?: Node | null): any;
|
|
67
96
|
export function mapArray<T>(
|
|
68
97
|
source: () => T[],
|
|
@@ -132,18 +161,18 @@ export function Show(props: {
|
|
|
132
161
|
when: boolean | (() => boolean);
|
|
133
162
|
fallback?: VNodeChild;
|
|
134
163
|
children?: VNodeChild;
|
|
135
|
-
}):
|
|
164
|
+
}): VNode;
|
|
136
165
|
|
|
137
166
|
export function For<T>(props: {
|
|
138
167
|
each: T[] | (() => T[]);
|
|
139
168
|
fallback?: VNodeChild;
|
|
140
169
|
children: ((item: T, index: number) => VNodeChild) | VNodeChild;
|
|
141
|
-
}):
|
|
170
|
+
}): VNode;
|
|
142
171
|
|
|
143
172
|
export function Switch(props: {
|
|
144
173
|
fallback?: VNodeChild;
|
|
145
174
|
children?: VNodeChild;
|
|
146
|
-
}):
|
|
175
|
+
}): VNode;
|
|
147
176
|
|
|
148
177
|
export function Match(props: {
|
|
149
178
|
when: boolean | (() => boolean);
|
|
@@ -198,6 +227,37 @@ export function Head(props: {
|
|
|
198
227
|
}): null;
|
|
199
228
|
export function clearHead(): void;
|
|
200
229
|
|
|
230
|
+
/** Per-render head accumulator used by the SSR renderer. */
|
|
231
|
+
export interface HeadSink {
|
|
232
|
+
title: string | null;
|
|
233
|
+
metas: Map<string, Record<string, string>>;
|
|
234
|
+
links: Map<string, Record<string, string>>;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function beginHeadCollection(): HeadSink;
|
|
238
|
+
export function endHeadCollection(sink: HeadSink | null): string;
|
|
239
|
+
|
|
240
|
+
// --- Loader Data ---
|
|
241
|
+
|
|
242
|
+
export function useLoaderData<T = any>(): T | undefined;
|
|
243
|
+
export function getLoaderData<T = any>(): T | undefined;
|
|
244
|
+
export function getResource<T = any>(key: string): T | undefined;
|
|
245
|
+
|
|
246
|
+
// --- Server Context ---
|
|
247
|
+
|
|
248
|
+
export interface ServerContext {
|
|
249
|
+
loaderData?: any;
|
|
250
|
+
head?: HeadSink;
|
|
251
|
+
resources?: Record<string, any>;
|
|
252
|
+
[key: string]: any;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** The active render context, or null on the client / outside a render. */
|
|
256
|
+
export function getServerContext(): ServerContext | null;
|
|
257
|
+
/** Set the active context and return the previous one so callers can restore it. */
|
|
258
|
+
export function setServerContext(ctx: ServerContext | null): ServerContext | null;
|
|
259
|
+
export function runWithServerContext<T>(ctx: ServerContext, fn: () => T): T;
|
|
260
|
+
|
|
201
261
|
// --- Scheduler ---
|
|
202
262
|
|
|
203
263
|
export function scheduleRead(fn: () => void): () => void;
|
|
@@ -334,17 +394,17 @@ export function onKeys(keys: string[], handler: (e: KeyboardEvent) => void): (e:
|
|
|
334
394
|
|
|
335
395
|
// --- Skeleton ---
|
|
336
396
|
|
|
337
|
-
export function Skeleton(props?: Record<string, any>):
|
|
397
|
+
export function Skeleton(props?: Record<string, any>): VNode;
|
|
338
398
|
export function SkeletonText(props?: Record<string, any>): VNode;
|
|
339
|
-
export function SkeletonAvatar(props?: Record<string, any>):
|
|
399
|
+
export function SkeletonAvatar(props?: Record<string, any>): VNode;
|
|
340
400
|
export function SkeletonCard(props?: Record<string, any>): VNode;
|
|
341
401
|
export function SkeletonTable(props?: Record<string, any>): VNode;
|
|
342
|
-
export function IslandSkeleton(props?: Record<string, any>):
|
|
402
|
+
export function IslandSkeleton(props?: Record<string, any>): VNode;
|
|
343
403
|
export function useSkeleton<T>(asyncFn: () => Promise<T> | T, deps?: unknown[]): {
|
|
344
404
|
isLoading: () => boolean;
|
|
345
405
|
data: () => T | null;
|
|
346
406
|
error: () => any;
|
|
347
|
-
Skeleton: (props?: Record<string, any>) =>
|
|
407
|
+
Skeleton: (props?: Record<string, any>) => VNode;
|
|
348
408
|
};
|
|
349
409
|
export function Placeholder(props?: Record<string, any>): VNode;
|
|
350
410
|
export function LoadingDots(props?: Record<string, any>): VNode;
|
|
@@ -504,4 +564,115 @@ export function ErrorMessage(props: {
|
|
|
504
564
|
formState?: FormState;
|
|
505
565
|
errors?: Record<string, FieldError> | (() => Record<string, FieldError>);
|
|
506
566
|
render?: (args: { message?: string; type?: string }) => VNodeChild;
|
|
507
|
-
}):
|
|
567
|
+
}): VNode;
|
|
568
|
+
|
|
569
|
+
// --- Structured Errors ---
|
|
570
|
+
|
|
571
|
+
export interface ErrorCodeDefinition {
|
|
572
|
+
code: string;
|
|
573
|
+
severity: 'error' | 'warning';
|
|
574
|
+
template: string;
|
|
575
|
+
suggestion: string;
|
|
576
|
+
codeExample?: string;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
export const ERROR_CODES: Record<string, ErrorCodeDefinition>;
|
|
580
|
+
|
|
581
|
+
export interface WhatErrorJSON {
|
|
582
|
+
code: string;
|
|
583
|
+
message: string;
|
|
584
|
+
suggestion?: string;
|
|
585
|
+
file?: string;
|
|
586
|
+
line?: number;
|
|
587
|
+
component?: string;
|
|
588
|
+
signal?: string;
|
|
589
|
+
effect?: string;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
export class WhatError extends Error {
|
|
593
|
+
constructor(init: {
|
|
594
|
+
code: string;
|
|
595
|
+
message: string;
|
|
596
|
+
suggestion?: string;
|
|
597
|
+
file?: string;
|
|
598
|
+
line?: number;
|
|
599
|
+
component?: string;
|
|
600
|
+
signal?: string;
|
|
601
|
+
effect?: string;
|
|
602
|
+
});
|
|
603
|
+
code: string;
|
|
604
|
+
suggestion?: string;
|
|
605
|
+
file?: string;
|
|
606
|
+
line?: number;
|
|
607
|
+
component?: string;
|
|
608
|
+
signal?: string;
|
|
609
|
+
effect?: string;
|
|
610
|
+
toJSON(): WhatErrorJSON;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
export function createWhatError(
|
|
614
|
+
errorCode: string | ErrorCodeDefinition,
|
|
615
|
+
context?: Record<string, any>,
|
|
616
|
+
): WhatError;
|
|
617
|
+
export function classifyError(err: unknown, context?: Record<string, any>): WhatError;
|
|
618
|
+
export function collectError(error: WhatError): void;
|
|
619
|
+
export function getCollectedErrors(since?: number): Array<WhatErrorJSON & { timestamp: number }>;
|
|
620
|
+
export function clearCollectedErrors(): void;
|
|
621
|
+
|
|
622
|
+
// --- Guardrails ---
|
|
623
|
+
|
|
624
|
+
export interface GuardrailConfig {
|
|
625
|
+
signalReadDetection: boolean;
|
|
626
|
+
componentNaming: boolean;
|
|
627
|
+
importValidation: boolean;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
export function configureGuardrails(overrides: Partial<GuardrailConfig>): void;
|
|
631
|
+
export function getGuardrailConfig(): GuardrailConfig;
|
|
632
|
+
export function installSignalReadGuardrail<T>(signalFn: T, debugName?: string): T;
|
|
633
|
+
|
|
634
|
+
// --- Agent Context ---
|
|
635
|
+
|
|
636
|
+
export interface HealthReport {
|
|
637
|
+
effectCycleRisk: boolean;
|
|
638
|
+
orphanEffects: number;
|
|
639
|
+
signalLeaks: number;
|
|
640
|
+
memoryPressure: 'low' | 'medium' | 'high';
|
|
641
|
+
recentErrorCount: number;
|
|
642
|
+
totalSignals: number;
|
|
643
|
+
totalComponents: number;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
export function getHealth(): HealthReport;
|
|
647
|
+
/** Expose globalThis.__WHAT_AGENT__ for agent tooling (dev mode only). */
|
|
648
|
+
export function installAgentContext(): void;
|
|
649
|
+
|
|
650
|
+
/** Warn when a component function is not PascalCase. Dev-mode only; returns null in production. */
|
|
651
|
+
export function checkComponentName(name: string): GuardrailWarning | null;
|
|
652
|
+
|
|
653
|
+
export interface GuardrailWarning {
|
|
654
|
+
code: string;
|
|
655
|
+
name: string;
|
|
656
|
+
suggestion: string;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
export interface InvalidImport {
|
|
660
|
+
name: string;
|
|
661
|
+
message: string;
|
|
662
|
+
suggestion: string;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/** Report import names that are not valid exports of what-framework. Dev-mode only. */
|
|
666
|
+
export function validateImports(importNames: readonly string[]): InvalidImport[];
|
|
667
|
+
|
|
668
|
+
// --- Agent registries ---
|
|
669
|
+
// The devtools bridge and MCP server read these to enumerate the live graph.
|
|
670
|
+
// Registration is a no-op in production builds; the getters always return a copy.
|
|
671
|
+
|
|
672
|
+
export function registerComponent(component: unknown): void;
|
|
673
|
+
export function unregisterComponent(component: unknown): void;
|
|
674
|
+
export function getMountedComponents(): unknown[];
|
|
675
|
+
|
|
676
|
+
export function registerSignal(sig: unknown): void;
|
|
677
|
+
export function unregisterSignal(sig: unknown): void;
|
|
678
|
+
export function getActiveSignals(): unknown[];
|
package/jsx-dev-runtime.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// What Framework — JSX dev-runtime type definitions.
|
|
2
2
|
// Re-exports the runtime types (including the JSX namespace) and adds jsxDEV,
|
|
3
3
|
// which the "react-jsxdev" transform emits in development builds.
|
|
4
|
-
import type { VNode } from './index';
|
|
4
|
+
import type { VNode } from './index.js';
|
|
5
5
|
|
|
6
|
-
export * from './jsx-runtime';
|
|
6
|
+
export * from './jsx-runtime.js';
|
|
7
7
|
|
|
8
8
|
export function jsxDEV(
|
|
9
9
|
type: any,
|
package/jsx-runtime.d.ts
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
// documentation, while an index signature keeps arbitrary/custom attributes and
|
|
12
12
|
// web-component tags valid (never false-flagging code the runtime accepts).
|
|
13
13
|
|
|
14
|
-
import type { VNode, VNodeChild } from './index';
|
|
14
|
+
import type { VNode, VNodeChild } from './index.js';
|
|
15
15
|
|
|
16
|
-
export { Fragment } from './index';
|
|
16
|
+
export { Fragment } from './index.js';
|
|
17
17
|
|
|
18
18
|
/** A JSX attribute value in What may be static or a reactive `() => value` thunk. */
|
|
19
19
|
export type Reactive<T> = T | (() => T);
|
package/package.json
CHANGED
package/render.d.ts
CHANGED
|
@@ -9,7 +9,8 @@ export {
|
|
|
9
9
|
classList,
|
|
10
10
|
effect,
|
|
11
11
|
untrack,
|
|
12
|
-
} from './index';
|
|
12
|
+
} from './index.js';
|
|
13
|
+
import type { VNodeChild } from './index.js';
|
|
13
14
|
|
|
14
15
|
// Compiler-internal template alias — identical to template() but never
|
|
15
16
|
// dev-warns. Compiled output imports this (SPRINT v0.11 C5).
|
|
@@ -28,3 +29,16 @@ export function setChecked(el: Element, value: any): void;
|
|
|
28
29
|
// exported from the package index). Emitted by the compiler for branch
|
|
29
30
|
// memoization of conditional JSX (SPRINT v0.11 C1).
|
|
30
31
|
export function memo<T>(fn: () => T): (() => T) & { peek(): T };
|
|
32
|
+
|
|
33
|
+
// --- Hydration ---
|
|
34
|
+
// hydrate() adopts server-rendered DOM instead of recreating it: it walks the
|
|
35
|
+
// existing nodes, attaches event handlers and reactive bindings in place, and
|
|
36
|
+
// restarts the useId sequence so client ids reproduce the server's.
|
|
37
|
+
export function hydrate(vnode: VNodeChild, container: Element): Node | Node[] | null;
|
|
38
|
+
|
|
39
|
+
/** True while a hydration pass is walking existing DOM. */
|
|
40
|
+
export function isHydrating(): boolean;
|
|
41
|
+
|
|
42
|
+
// SVG counterpart to template(): elements are created in the SVG namespace, which
|
|
43
|
+
// a plain innerHTML template cannot do.
|
|
44
|
+
export function svgTemplate(html: string): () => Element;
|
package/src/a11y.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { signal, effect } from './reactive.js';
|
|
5
5
|
import { h } from './h.js';
|
|
6
6
|
import { getCurrentComponent } from './dom.js';
|
|
7
|
+
import { getServerContext } from './server-context.js';
|
|
7
8
|
|
|
8
9
|
// --- Focus Management ---
|
|
9
10
|
|
|
@@ -401,17 +402,48 @@ export function LiveRegion({ children, priority = 'polite', atomic = true }) {
|
|
|
401
402
|
// --- ID Generator ---
|
|
402
403
|
// Generate unique IDs for ARIA attributes
|
|
403
404
|
|
|
405
|
+
// The counter is render-scoped on the server and module-global on the client.
|
|
406
|
+
//
|
|
407
|
+
// A bare module-global counter is wrong on the server in two independent ways.
|
|
408
|
+
// Ids drift between the SSR pass and hydration, which breaks exactly the
|
|
409
|
+
// relationships useId exists to create (`for`/`id`, `aria-labelledby`,
|
|
410
|
+
// `aria-describedby`), and concurrent requests interleave into each other's
|
|
411
|
+
// sequence, so two visitors can be served HTML whose ids were allocated in the
|
|
412
|
+
// order the event loop happened to run. Every framework in the cohort ships this
|
|
413
|
+
// primitive as SSR-stable because that is the whole point of shipping it.
|
|
414
|
+
//
|
|
415
|
+
// getServerContext() is the render-scoped store the SSR keystone already
|
|
416
|
+
// maintains (AsyncLocalStorage-backed in Node), so this is wiring rather than
|
|
417
|
+
// new machinery, and it adds no public API.
|
|
404
418
|
let idCounter = 0;
|
|
405
419
|
|
|
420
|
+
function nextIdSuffix() {
|
|
421
|
+
const ctx = getServerContext();
|
|
422
|
+
if (ctx) {
|
|
423
|
+
ctx.idCounter = (ctx.idCounter || 0) + 1;
|
|
424
|
+
return ctx.idCounter;
|
|
425
|
+
}
|
|
426
|
+
return ++idCounter;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Reset the client-side counter. Called at the start of hydration so the client
|
|
431
|
+
* reproduces the server's id sequence instead of continuing past it.
|
|
432
|
+
* @internal
|
|
433
|
+
*/
|
|
434
|
+
export function __resetIdCounter() {
|
|
435
|
+
idCounter = 0;
|
|
436
|
+
}
|
|
437
|
+
|
|
406
438
|
export function useId(prefix = 'what') {
|
|
407
|
-
const id = `${prefix}-${
|
|
439
|
+
const id = `${prefix}-${nextIdSuffix()}`;
|
|
408
440
|
return () => id;
|
|
409
441
|
}
|
|
410
442
|
|
|
411
443
|
export function useIds(count, prefix = 'what') {
|
|
412
444
|
const ids = [];
|
|
413
445
|
for (let i = 0; i < count; i++) {
|
|
414
|
-
ids.push(`${prefix}-${
|
|
446
|
+
ids.push(`${prefix}-${nextIdSuffix()}`);
|
|
415
447
|
}
|
|
416
448
|
return ids;
|
|
417
449
|
}
|
package/src/agent-context.js
CHANGED
|
@@ -8,7 +8,7 @@ import { getCollectedErrors } from './errors.js';
|
|
|
8
8
|
// --- Version ---
|
|
9
9
|
// Keep in sync with packages/core/package.json (checked by
|
|
10
10
|
// core/test/guardrails.test.js so it can't silently go stale again).
|
|
11
|
-
const VERSION = '0.
|
|
11
|
+
const VERSION = '0.12.0';
|
|
12
12
|
|
|
13
13
|
// --- Component Registry ---
|
|
14
14
|
// Tracks mounted components for agent inspection.
|