what-framework 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/package.json +5 -5
- package/router.d.ts +146 -14
- package/server.d.ts +11 -113
- package/testing.d.ts +3 -103
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "what-framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "The web framework built for AI agents — signals, components, islands, SSR",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -84,9 +84,9 @@
|
|
|
84
84
|
},
|
|
85
85
|
"homepage": "https://whatfw.com",
|
|
86
86
|
"dependencies": {
|
|
87
|
-
"what-core": "^0.
|
|
88
|
-
"what-router": "^0.
|
|
89
|
-
"what-server": "^0.
|
|
90
|
-
"what-compiler": "^0.
|
|
87
|
+
"what-core": "^0.12.0",
|
|
88
|
+
"what-router": "^0.12.0",
|
|
89
|
+
"what-server": "^0.12.0",
|
|
90
|
+
"what-compiler": "^0.12.0"
|
|
91
91
|
}
|
|
92
92
|
}
|
package/router.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// What Framework Router - TypeScript Definitions
|
|
2
2
|
|
|
3
|
-
import { VNode, VNodeChild, Component, Signal, Computed } from './index';
|
|
3
|
+
import { VNode, VNodeChild, Component, Signal, Computed } from './index.js';
|
|
4
4
|
|
|
5
5
|
// --- Route State ---
|
|
6
6
|
|
|
@@ -37,9 +37,6 @@ export interface NavigateOptions {
|
|
|
37
37
|
/** Navigate to a new URL */
|
|
38
38
|
export function navigate(to: string, options?: NavigateOptions): Promise<void>;
|
|
39
39
|
|
|
40
|
-
/** Redirect (throws to navigate) */
|
|
41
|
-
export function redirect(to: string, options?: NavigateOptions): never;
|
|
42
|
-
|
|
43
40
|
// --- Route Configuration ---
|
|
44
41
|
|
|
45
42
|
export interface RouteConfig {
|
|
@@ -52,7 +49,7 @@ export interface RouteConfig {
|
|
|
52
49
|
/** Loading component */
|
|
53
50
|
loading?: Component<{}>;
|
|
54
51
|
/** Error component */
|
|
55
|
-
error?: Component<{ error: Error }>;
|
|
52
|
+
error?: Component<{ error: Error; reset: () => void }>;
|
|
56
53
|
/** Route middleware */
|
|
57
54
|
middleware?: RouteMiddleware[];
|
|
58
55
|
}
|
|
@@ -81,6 +78,25 @@ export interface RouterProps {
|
|
|
81
78
|
|
|
82
79
|
export function Router(props: RouterProps): VNode;
|
|
83
80
|
|
|
81
|
+
// --- File-Based Router ---
|
|
82
|
+
|
|
83
|
+
export interface FileRouteConfig {
|
|
84
|
+
path: string;
|
|
85
|
+
component: Component<RouteComponentProps>;
|
|
86
|
+
layout?: Component<LayoutProps>;
|
|
87
|
+
mode?: 'static' | 'server' | 'client' | 'hybrid';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface FileRouterProps {
|
|
91
|
+
routes: FileRouteConfig[];
|
|
92
|
+
layout?: Component<{ children?: VNodeChild }>;
|
|
93
|
+
fallback?: Component<{}>;
|
|
94
|
+
error?: Component<{ error: Error; reset: () => void }>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Router driven by what-compiler's generated route manifest (virtual:what-routes). */
|
|
98
|
+
export function FileRouter(props: FileRouterProps): VNode;
|
|
99
|
+
|
|
84
100
|
// --- Link Component ---
|
|
85
101
|
|
|
86
102
|
export interface LinkProps {
|
|
@@ -104,25 +120,141 @@ export function NavLink(props: LinkProps): VNode;
|
|
|
104
120
|
/** Define routes from object config */
|
|
105
121
|
export function defineRoutes(config: Record<string, Component | Partial<RouteConfig>>): RouteConfig[];
|
|
106
122
|
|
|
123
|
+
/** Create nested routes with shared options */
|
|
124
|
+
export function nestedRoutes(
|
|
125
|
+
basePath: string,
|
|
126
|
+
children: RouteConfig[],
|
|
127
|
+
options?: { layout?: Component; loading?: Component; error?: Component }
|
|
128
|
+
): RouteConfig[];
|
|
129
|
+
|
|
130
|
+
/** Group routes without affecting URLs */
|
|
131
|
+
export function routeGroup(
|
|
132
|
+
name: string,
|
|
133
|
+
routes: RouteConfig[],
|
|
134
|
+
options?: { layout?: Component; middleware?: RouteMiddleware[] }
|
|
135
|
+
): RouteConfig[];
|
|
136
|
+
|
|
137
|
+
// --- Redirect ---
|
|
138
|
+
|
|
139
|
+
export function Redirect(props: { to: string }): null;
|
|
140
|
+
|
|
141
|
+
// --- Guards ---
|
|
142
|
+
|
|
143
|
+
/** Create a route guard */
|
|
144
|
+
export function guard(
|
|
145
|
+
check: (props: RouteComponentProps) => boolean,
|
|
146
|
+
fallback: string | Component
|
|
147
|
+
): <P>(component: Component<P>) => Component<P>;
|
|
148
|
+
|
|
149
|
+
/** Create an async route guard */
|
|
150
|
+
export function asyncGuard(
|
|
151
|
+
check: (props: RouteComponentProps) => Promise<boolean>,
|
|
152
|
+
options?: { fallback?: string | Component; loading?: Component }
|
|
153
|
+
): <P>(component: Component<P>) => Component<P>;
|
|
154
|
+
|
|
107
155
|
// --- Prefetch ---
|
|
108
156
|
|
|
109
|
-
export function
|
|
157
|
+
export function prefetch(href: string): void;
|
|
110
158
|
|
|
111
|
-
// ---
|
|
159
|
+
// --- Scroll Restoration ---
|
|
112
160
|
|
|
113
|
-
export function
|
|
114
|
-
|
|
161
|
+
export function enableScrollRestoration(): void;
|
|
162
|
+
|
|
163
|
+
// --- View Transitions ---
|
|
164
|
+
|
|
165
|
+
export function viewTransitionName(name: string): { style: { viewTransitionName: string } };
|
|
166
|
+
export function setViewTransition(type: string): void;
|
|
115
167
|
|
|
116
|
-
// --- useRoute
|
|
168
|
+
// --- useRoute Hook ---
|
|
117
169
|
|
|
118
|
-
export
|
|
170
|
+
export interface UseRouteResult {
|
|
119
171
|
path: Computed<string>;
|
|
120
172
|
params: Computed<Record<string, string>>;
|
|
121
173
|
query: Computed<Record<string, string>>;
|
|
122
174
|
hash: Computed<string>;
|
|
123
175
|
isNavigating: Computed<boolean>;
|
|
124
|
-
|
|
176
|
+
navigate: typeof navigate;
|
|
177
|
+
prefetch: typeof prefetch;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function useRoute(): UseRouteResult;
|
|
181
|
+
|
|
182
|
+
// --- Route Accessors ---
|
|
183
|
+
|
|
184
|
+
/** Current route params. Subscribes when read inside a tracking scope. */
|
|
185
|
+
export function useParams<T = Record<string, string>>(): T;
|
|
125
186
|
|
|
126
|
-
|
|
127
|
-
|
|
187
|
+
/**
|
|
188
|
+
* Query string of the last successfully matched route, parsed. Subscribes when
|
|
189
|
+
* read inside a tracking scope. Only the Router's match branch writes it, so on
|
|
190
|
+
* an unmatched (404) route this is the previous route's query, not the current
|
|
191
|
+
* URL's. Same value and same caveat as `route.query`.
|
|
192
|
+
*/
|
|
193
|
+
export function useSearch<T = Record<string, string>>(): T;
|
|
194
|
+
|
|
195
|
+
/** The navigate function, for symmetry with useParams/useSearch. */
|
|
128
196
|
export function useNavigate(): typeof navigate;
|
|
197
|
+
|
|
198
|
+
/** Prefetch a route's assets. */
|
|
199
|
+
export function prefetchRoute(href: string): void;
|
|
200
|
+
|
|
201
|
+
// --- Redirect Signal ---
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Abort the current render and navigate.
|
|
205
|
+
*
|
|
206
|
+
* Throws a navigation signal. Two places catch it: route middleware, caught by
|
|
207
|
+
* the Router's matching pass, and a component body, caught by the runtime where
|
|
208
|
+
* it instantiates components. Anywhere else (an event handler, a promise
|
|
209
|
+
* callback, a timer, or a reactive thunk such as `{() => cond() && redirect(to)}`)
|
|
210
|
+
* nothing catches it and the signal surfaces as an uncaught error carrying
|
|
211
|
+
* `ERR_REDIRECT_NOT_CAUGHT`; call `navigate(to)` there instead. In a thunk the
|
|
212
|
+
* first render reports the error, but on a later re-run the navigation simply
|
|
213
|
+
* does not happen and the stale DOM stays, so prefer `navigate(to)` there.
|
|
214
|
+
* A `try/catch` around the call also swallows it, so rethrow anything whose
|
|
215
|
+
* `name` is `RouterRedirect`. On the server the signal escapes `renderToString`
|
|
216
|
+
* to its caller: read `.to` and emit a 302.
|
|
217
|
+
*/
|
|
218
|
+
export function redirect(to: string, options?: NavigateOptions): never;
|
|
219
|
+
|
|
220
|
+
// --- Navigation Hooks ---
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Run before every route navigation; return false to cancel. Returns an
|
|
224
|
+
* unsubscribe. Not consulted for same-page hash navigation (`navigate('#x')`
|
|
225
|
+
* scrolls, it does not change the route). Cancelling a back/forward navigation
|
|
226
|
+
* restores the address bar by pushing the previous URL as a new history entry:
|
|
227
|
+
* the entry the browser moved to is not recovered and its `history.state` is
|
|
228
|
+
* not carried over.
|
|
229
|
+
*/
|
|
230
|
+
export function beforeNavigate(fn: (to: string, from: string) => boolean | Promise<boolean>): () => void;
|
|
231
|
+
|
|
232
|
+
/** Run after every committed navigation. Returns an unsubscribe. */
|
|
233
|
+
export function afterNavigate(fn: (to: string, from: string) => void): () => void;
|
|
234
|
+
|
|
235
|
+
// --- Outlet ---
|
|
236
|
+
|
|
237
|
+
export function Outlet(props: { children?: VNodeChild }): VNode;
|
|
238
|
+
|
|
239
|
+
// --- Path Matching ---
|
|
240
|
+
|
|
241
|
+
export interface CompiledPath {
|
|
242
|
+
regex: RegExp;
|
|
243
|
+
paramNames: string[];
|
|
244
|
+
catchAll: string | null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Compile a path pattern (`/users/:id`, `/posts/*`, `/[slug]`) to a matcher. */
|
|
248
|
+
export function compilePath(path: string): CompiledPath;
|
|
249
|
+
|
|
250
|
+
/** Match a pathname against routes, most specific first. */
|
|
251
|
+
export function matchRoute<T extends { path?: string }>(
|
|
252
|
+
path: string,
|
|
253
|
+
routes: T[],
|
|
254
|
+
): { route: T; params: Record<string, string> } | null;
|
|
255
|
+
|
|
256
|
+
/** Parse a query string into a null-prototype object. */
|
|
257
|
+
export function parseQuery(search: string): Record<string, string>;
|
|
258
|
+
|
|
259
|
+
/** Reject javascript:, data:, vbscript: and protocol-relative URLs. */
|
|
260
|
+
export function isSafeUrl(url: string): boolean;
|
package/server.d.ts
CHANGED
|
@@ -1,113 +1,11 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
/** Render a full page with document wrapper */
|
|
14
|
-
export function renderPage(vnode: VNode, options?: {
|
|
15
|
-
title?: string;
|
|
16
|
-
meta?: Record<string, string>;
|
|
17
|
-
scripts?: string[];
|
|
18
|
-
styles?: string[];
|
|
19
|
-
mode?: 'static' | 'server' | 'client' | 'hybrid';
|
|
20
|
-
}): string;
|
|
21
|
-
|
|
22
|
-
// --- Page Configuration ---
|
|
23
|
-
|
|
24
|
-
export interface PageConfig {
|
|
25
|
-
/** Rendering mode */
|
|
26
|
-
mode?: 'static' | 'server' | 'client' | 'hybrid';
|
|
27
|
-
/** Page title */
|
|
28
|
-
title?: string;
|
|
29
|
-
/** Meta tags */
|
|
30
|
-
meta?: Record<string, string>;
|
|
31
|
-
/** Page component */
|
|
32
|
-
component: (data?: any) => VNode;
|
|
33
|
-
/** Islands to hydrate */
|
|
34
|
-
islands?: string[];
|
|
35
|
-
/** Scripts to load */
|
|
36
|
-
scripts?: string[];
|
|
37
|
-
/** Stylesheets to load */
|
|
38
|
-
styles?: string[];
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function definePage(config: Partial<PageConfig>): PageConfig;
|
|
42
|
-
|
|
43
|
-
// --- Islands ---
|
|
44
|
-
|
|
45
|
-
export type IslandMode = 'static' | 'idle' | 'visible' | 'load' | 'media' | 'action';
|
|
46
|
-
|
|
47
|
-
export const IslandModes: {
|
|
48
|
-
STATIC: 'static';
|
|
49
|
-
IDLE: 'idle';
|
|
50
|
-
VISIBLE: 'visible';
|
|
51
|
-
LOAD: 'load';
|
|
52
|
-
MEDIA: 'media';
|
|
53
|
-
ACTION: 'action';
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export interface IslandOptions {
|
|
57
|
-
/** Hydration mode */
|
|
58
|
-
mode?: IslandMode;
|
|
59
|
-
/** Media query for 'media' mode */
|
|
60
|
-
media?: string;
|
|
61
|
-
/** Priority (higher = hydrate first) */
|
|
62
|
-
priority?: number;
|
|
63
|
-
/** Shared stores this island uses */
|
|
64
|
-
stores?: string[];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** Register an island component */
|
|
68
|
-
export function island(
|
|
69
|
-
name: string,
|
|
70
|
-
loader: () => Promise<any>,
|
|
71
|
-
options?: IslandOptions
|
|
72
|
-
): void;
|
|
73
|
-
|
|
74
|
-
/** Island component wrapper for SSR */
|
|
75
|
-
export function Island(props: {
|
|
76
|
-
name: string;
|
|
77
|
-
props?: Record<string, any>;
|
|
78
|
-
mode?: IslandMode;
|
|
79
|
-
priority?: number;
|
|
80
|
-
stores?: string[];
|
|
81
|
-
children?: VNodeChild;
|
|
82
|
-
}): VNode;
|
|
83
|
-
|
|
84
|
-
/** Hydrate all islands on the page */
|
|
85
|
-
export function hydrateIslands(): void;
|
|
86
|
-
|
|
87
|
-
// --- Server Actions ---
|
|
88
|
-
|
|
89
|
-
export interface ActionOptions {
|
|
90
|
-
id?: string;
|
|
91
|
-
onError?: (error: Error) => void;
|
|
92
|
-
onSuccess?: (result: any) => void;
|
|
93
|
-
revalidate?: string[];
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** Create a server action */
|
|
97
|
-
export function createAction<T extends any[], R>(
|
|
98
|
-
fn: (...args: T) => Promise<R>,
|
|
99
|
-
options?: ActionOptions
|
|
100
|
-
): (...args: T) => Promise<R>;
|
|
101
|
-
|
|
102
|
-
/** Hook for using server actions */
|
|
103
|
-
export interface UseActionResult<T extends any[], R> {
|
|
104
|
-
trigger: (...args: T) => Promise<R>;
|
|
105
|
-
isPending: () => boolean;
|
|
106
|
-
error: () => Error | null;
|
|
107
|
-
data: () => R | null;
|
|
108
|
-
reset: () => void;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function useAction<T extends any[], R>(
|
|
112
|
-
actionFn: (...args: T) => Promise<R>
|
|
113
|
-
): UseActionResult<T, R>;
|
|
1
|
+
// what-framework/server re-exports the full server API. Its runtime
|
|
2
|
+
// (src/server.js) is a pure barrel, so its declarations are one too: this file
|
|
3
|
+
// used to hand-mirror what-server's surface and had drifted 45 exports behind it,
|
|
4
|
+
// which is invisible to every TypeScript user and caught by nothing.
|
|
5
|
+
export * from 'what-server';
|
|
6
|
+
export * from 'what-server/islands';
|
|
7
|
+
|
|
8
|
+
// The island shapes are exported by both entries (the root re-exports them for
|
|
9
|
+
// convenience). An explicit re-export outranks the two star exports and resolves
|
|
10
|
+
// the ambiguity, which is what TS2308 asks for.
|
|
11
|
+
export type { IslandOptions, IslandStore, IslandStatus } from 'what-server/islands';
|
package/testing.d.ts
CHANGED
|
@@ -1,103 +1,3 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
// Setup and Cleanup
|
|
6
|
-
export function setupDOM(): HTMLElement | null;
|
|
7
|
-
export function cleanup(): void;
|
|
8
|
-
|
|
9
|
-
// Render
|
|
10
|
-
export interface RenderResult {
|
|
11
|
-
container: HTMLElement;
|
|
12
|
-
unmount: () => void;
|
|
13
|
-
getByText: (text: string | RegExp) => HTMLElement | null;
|
|
14
|
-
getByTestId: (id: string) => HTMLElement | null;
|
|
15
|
-
getByRole: (role: string) => HTMLElement | null;
|
|
16
|
-
getAllByText: (text: string | RegExp) => HTMLElement[];
|
|
17
|
-
queryByText: (text: string | RegExp) => HTMLElement | null;
|
|
18
|
-
queryByTestId: (id: string) => HTMLElement | null;
|
|
19
|
-
debug: () => void;
|
|
20
|
-
findByText: (text: string | RegExp, timeout?: number) => Promise<HTMLElement>;
|
|
21
|
-
findByTestId: (id: string, timeout?: number) => Promise<HTMLElement>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface RenderOptions {
|
|
25
|
-
container?: HTMLElement;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function render(vnode: VNode, options?: RenderOptions): RenderResult;
|
|
29
|
-
|
|
30
|
-
// Fire Events
|
|
31
|
-
export interface FireEvent {
|
|
32
|
-
click(element: HTMLElement): MouseEvent;
|
|
33
|
-
change(element: HTMLInputElement, value: string): Event;
|
|
34
|
-
input(element: HTMLInputElement, value: string): Event;
|
|
35
|
-
submit(element: HTMLFormElement): Event;
|
|
36
|
-
focus(element: HTMLElement): FocusEvent;
|
|
37
|
-
blur(element: HTMLElement): FocusEvent;
|
|
38
|
-
keyDown(element: HTMLElement, key: string, options?: KeyboardEventInit): KeyboardEvent;
|
|
39
|
-
keyUp(element: HTMLElement, key: string, options?: KeyboardEventInit): KeyboardEvent;
|
|
40
|
-
mouseEnter(element: HTMLElement): MouseEvent;
|
|
41
|
-
mouseLeave(element: HTMLElement): MouseEvent;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export const fireEvent: FireEvent;
|
|
45
|
-
|
|
46
|
-
// Wait Utilities
|
|
47
|
-
export interface WaitOptions {
|
|
48
|
-
timeout?: number;
|
|
49
|
-
interval?: number;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function waitFor<T>(callback: () => T, options?: WaitOptions): Promise<T>;
|
|
53
|
-
export function waitForElementToBeRemoved(callback: () => HTMLElement | null, options?: WaitOptions): Promise<void>;
|
|
54
|
-
|
|
55
|
-
// Act
|
|
56
|
-
export function act<T>(callback: () => T | Promise<T>): Promise<T>;
|
|
57
|
-
|
|
58
|
-
// Signal Testing Helpers
|
|
59
|
-
export interface TestSignal<T> {
|
|
60
|
-
signal: Signal<T>;
|
|
61
|
-
value: T;
|
|
62
|
-
history: T[];
|
|
63
|
-
reset(): void;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function createTestSignal<T>(initial: T): TestSignal<T>;
|
|
67
|
-
|
|
68
|
-
// Mocking
|
|
69
|
-
export interface MockComponent {
|
|
70
|
-
(props: Record<string, any>): VNode;
|
|
71
|
-
displayName: string;
|
|
72
|
-
calls: Array<{ props: Record<string, any>; timestamp: number }>;
|
|
73
|
-
lastCall(): { props: Record<string, any>; timestamp: number } | undefined;
|
|
74
|
-
reset(): void;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function mockComponent(name?: string): MockComponent;
|
|
78
|
-
|
|
79
|
-
// Assertions
|
|
80
|
-
export interface Expect {
|
|
81
|
-
toBeInTheDocument(element: HTMLElement | null): void;
|
|
82
|
-
toHaveTextContent(element: HTMLElement | null, text: string | RegExp): void;
|
|
83
|
-
toHaveAttribute(element: HTMLElement | null, attr: string, value?: string): void;
|
|
84
|
-
toHaveClass(element: HTMLElement | null, className: string): void;
|
|
85
|
-
toBeVisible(element: HTMLElement | null): void;
|
|
86
|
-
toBeDisabled(element: HTMLElement | null): void;
|
|
87
|
-
toHaveValue(element: HTMLInputElement | null, value: string): void;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export const expect: Expect;
|
|
91
|
-
|
|
92
|
-
// Screen
|
|
93
|
-
export interface Screen {
|
|
94
|
-
getByText(text: string | RegExp): HTMLElement | null;
|
|
95
|
-
getByTestId(id: string): HTMLElement | null;
|
|
96
|
-
getByRole(role: string): HTMLElement | null;
|
|
97
|
-
getAllByText(text: string | RegExp): HTMLElement[];
|
|
98
|
-
queryByText(text: string | RegExp): HTMLElement | null;
|
|
99
|
-
queryByTestId(id: string): HTMLElement | null;
|
|
100
|
-
debug(): void;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export const screen: Screen;
|
|
1
|
+
// what-framework/testing re-exports what-core/testing. Runtime is a pure barrel
|
|
2
|
+
// (src/testing.js), so the declarations mirror it rather than duplicating them.
|
|
3
|
+
export * from 'what-core/testing';
|