@zap-js/client 0.0.2 → 0.0.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 +310 -24
- package/bin/zap +0 -0
- package/bin/zap-codegen +0 -0
- package/dist/cli/commands/build.d.ts +11 -0
- package/dist/cli/commands/build.js +282 -0
- package/dist/cli/commands/codegen.d.ts +8 -0
- package/dist/cli/commands/codegen.js +95 -0
- package/dist/cli/commands/dev.d.ts +20 -0
- package/dist/cli/commands/dev.js +78 -0
- package/dist/cli/commands/new.d.ts +9 -0
- package/dist/cli/commands/new.js +307 -0
- package/dist/cli/commands/routes-old.d.ts +9 -0
- package/dist/cli/commands/routes-old.js +106 -0
- package/dist/cli/commands/routes.d.ts +11 -0
- package/dist/cli/commands/routes.js +280 -0
- package/dist/cli/commands/serve.d.ts +17 -0
- package/dist/cli/commands/serve.js +386 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +76 -0
- package/dist/cli/utils/index.d.ts +2 -0
- package/dist/cli/utils/index.js +2 -0
- package/dist/cli/utils/logger.d.ts +84 -0
- package/dist/cli/utils/logger.js +181 -0
- package/dist/cli/utils/port-finder.d.ts +8 -0
- package/dist/cli/utils/port-finder.js +48 -0
- package/dist/dev-server/codegen-runner.d.ts +41 -0
- package/dist/dev-server/codegen-runner.js +172 -0
- package/dist/dev-server/hot-reload.d.ts +72 -0
- package/dist/dev-server/hot-reload.js +280 -0
- package/dist/dev-server/index.d.ts +8 -0
- package/dist/dev-server/index.js +8 -0
- package/dist/dev-server/route-scanner.d.ts +71 -0
- package/dist/dev-server/route-scanner.js +114 -0
- package/dist/dev-server/rust-builder.d.ts +66 -0
- package/dist/dev-server/rust-builder.js +286 -0
- package/dist/dev-server/server.d.ts +147 -0
- package/dist/dev-server/server.js +658 -0
- package/dist/dev-server/vite-proxy.d.ts +56 -0
- package/dist/dev-server/vite-proxy.js +212 -0
- package/dist/dev-server/watcher.d.ts +48 -0
- package/dist/dev-server/watcher.js +127 -0
- package/dist/router/codegen-enhanced.d.ts +5 -0
- package/dist/router/codegen-enhanced.js +275 -0
- package/dist/router/codegen.d.ts +17 -0
- package/dist/router/codegen.js +654 -0
- package/dist/router/index.d.ts +16 -0
- package/dist/router/index.js +19 -0
- package/dist/router/scanner.d.ts +86 -0
- package/dist/router/scanner.js +689 -0
- package/dist/router/ssg.d.ts +115 -0
- package/dist/router/ssg.js +202 -0
- package/dist/router/types.d.ts +124 -0
- package/dist/router/types.js +9 -0
- package/dist/router/watch.d.ts +38 -0
- package/dist/router/watch.js +135 -0
- package/dist/runtime/csrf.d.ts +146 -0
- package/dist/runtime/csrf.js +166 -0
- package/dist/runtime/error-boundary.d.ts +129 -0
- package/dist/runtime/error-boundary.js +287 -0
- package/dist/runtime/hooks.d.ts +83 -0
- package/dist/runtime/hooks.js +96 -0
- package/dist/runtime/index.d.ts +229 -0
- package/dist/runtime/index.js +449 -0
- package/dist/runtime/ipc-client.d.ts +144 -0
- package/dist/runtime/ipc-client.js +621 -0
- package/dist/runtime/logger.d.ts +71 -0
- package/dist/runtime/logger.js +164 -0
- package/dist/runtime/middleware.d.ts +66 -0
- package/dist/runtime/middleware.js +114 -0
- package/dist/runtime/process-manager.d.ts +51 -0
- package/dist/runtime/process-manager.js +207 -0
- package/dist/runtime/router-simple.d.ts +98 -0
- package/dist/runtime/router-simple.js +330 -0
- package/dist/runtime/router.d.ts +103 -0
- package/dist/runtime/router.js +435 -0
- package/dist/runtime/rpc-client.d.ts +35 -0
- package/dist/runtime/rpc-client.js +140 -0
- package/dist/runtime/streaming-utils.d.ts +86 -0
- package/dist/runtime/streaming-utils.js +150 -0
- package/dist/runtime/types.d.ts +465 -0
- package/dist/runtime/types.js +60 -0
- package/dist/runtime/websockets-utils.d.ts +50 -0
- package/dist/runtime/websockets-utils.js +92 -0
- package/package.json +30 -20
- package/index.js +0 -29
- package/internal/cli/package.json +0 -46
- package/internal/cli/tsconfig.tsbuildinfo +0 -1
- package/internal/dev-server/node_modules/ora/index.d.ts +0 -332
- package/internal/dev-server/node_modules/ora/index.js +0 -416
- package/internal/dev-server/node_modules/ora/license +0 -9
- package/internal/dev-server/node_modules/ora/node_modules/string-width/index.d.ts +0 -36
- package/internal/dev-server/node_modules/ora/node_modules/string-width/index.js +0 -65
- package/internal/dev-server/node_modules/ora/node_modules/string-width/license +0 -9
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/LICENSE-MIT.txt +0 -20
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/README.md +0 -107
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/index.d.ts +0 -3
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/index.js +0 -4
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/index.mjs +0 -4
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/package.json +0 -46
- package/internal/dev-server/node_modules/ora/node_modules/string-width/package.json +0 -60
- package/internal/dev-server/node_modules/ora/node_modules/string-width/readme.md +0 -62
- package/internal/dev-server/node_modules/ora/package.json +0 -66
- package/internal/dev-server/node_modules/ora/readme.md +0 -325
- package/internal/dev-server/package.json +0 -41
- package/internal/router/package.json +0 -28
- package/internal/runtime/package.json +0 -41
- package/internal/runtime/src/error-boundary.tsx +0 -476
- package/internal/runtime/src/router-simple.tsx +0 -640
- package/internal/runtime/src/router.tsx +0 -771
- package/internal/runtime/tsconfig.tsbuildinfo +0 -1
- package/src/errors.js +0 -33
- package/src/logger.js +0 -10
- package/src/middleware.js +0 -32
- package/src/router.js +0 -41
- package/src/types.js +0 -39
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ZapJS Production Router with Nested Layouts
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Nested layout support
|
|
7
|
+
* - Route-level code splitting
|
|
8
|
+
* - Error boundaries per route
|
|
9
|
+
* - Suspense boundaries
|
|
10
|
+
* - Type-safe navigation
|
|
11
|
+
*/
|
|
12
|
+
import React, { createContext, useContext, useState, useEffect, useCallback, useMemo, useTransition, Suspense, memo, } from 'react';
|
|
13
|
+
const RouterContext = createContext(null);
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Route Matching
|
|
16
|
+
// ============================================================================
|
|
17
|
+
function matchRoute(pathname, routes) {
|
|
18
|
+
const normalizedPath = pathname === '' ? '/' : pathname;
|
|
19
|
+
for (const route of routes) {
|
|
20
|
+
const match = normalizedPath.match(route.pattern);
|
|
21
|
+
if (match) {
|
|
22
|
+
const params = {};
|
|
23
|
+
route.paramNames.forEach((name, index) => {
|
|
24
|
+
const value = match[index + 1];
|
|
25
|
+
if (value !== undefined && value !== '') {
|
|
26
|
+
params[name] = decodeURIComponent(value);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return { route, params, pathname: normalizedPath };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
function parseUrl(url) {
|
|
35
|
+
try {
|
|
36
|
+
const parsed = new URL(url, window.location.origin);
|
|
37
|
+
return {
|
|
38
|
+
pathname: parsed.pathname,
|
|
39
|
+
search: parsed.search,
|
|
40
|
+
hash: parsed.hash,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
const hashIndex = url.indexOf('#');
|
|
45
|
+
const searchIndex = url.indexOf('?');
|
|
46
|
+
let pathname = url;
|
|
47
|
+
let search = '';
|
|
48
|
+
let hash = '';
|
|
49
|
+
if (hashIndex !== -1) {
|
|
50
|
+
hash = url.slice(hashIndex);
|
|
51
|
+
pathname = url.slice(0, hashIndex);
|
|
52
|
+
}
|
|
53
|
+
if (searchIndex !== -1 && (hashIndex === -1 || searchIndex < hashIndex)) {
|
|
54
|
+
search = pathname.slice(searchIndex, hashIndex !== -1 ? hashIndex - searchIndex : undefined);
|
|
55
|
+
pathname = pathname.slice(0, searchIndex);
|
|
56
|
+
}
|
|
57
|
+
return { pathname: pathname || '/', search, hash };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const LayoutWrapper = memo(({ layouts, layoutPath, children }) => {
|
|
61
|
+
if (!layoutPath) {
|
|
62
|
+
return _jsx(_Fragment, { children: children });
|
|
63
|
+
}
|
|
64
|
+
// Find the layout and build the chain
|
|
65
|
+
const layoutChain = [];
|
|
66
|
+
let currentPath = layoutPath;
|
|
67
|
+
while (currentPath) {
|
|
68
|
+
const layout = layouts.find(l => l.path === currentPath);
|
|
69
|
+
if (layout) {
|
|
70
|
+
layoutChain.unshift(layout);
|
|
71
|
+
currentPath = layout.parentLayout;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Nest layouts
|
|
78
|
+
return layoutChain.reduce((content, layout) => {
|
|
79
|
+
const LayoutComponent = layout.component;
|
|
80
|
+
return (_jsx(Suspense, { fallback: _jsx("div", { children: "Loading layout..." }), children: _jsx(LayoutComponent, { children: content }) }));
|
|
81
|
+
}, children);
|
|
82
|
+
});
|
|
83
|
+
LayoutWrapper.displayName = 'LayoutWrapper';
|
|
84
|
+
export function RouterProvider({ routes, layouts = [], children, notFound: NotFound, fallback = null, }) {
|
|
85
|
+
const [isPending, startTransition] = useTransition();
|
|
86
|
+
const [state, setState] = useState(() => {
|
|
87
|
+
const { pathname, search, hash } = parseUrl(window.location.href);
|
|
88
|
+
return {
|
|
89
|
+
pathname,
|
|
90
|
+
search,
|
|
91
|
+
hash,
|
|
92
|
+
match: matchRoute(pathname, routes),
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
const navigate = useCallback((path, options = {}) => {
|
|
96
|
+
const { replace = false, scroll = true } = options;
|
|
97
|
+
const { pathname, search, hash } = parseUrl(path);
|
|
98
|
+
const url = pathname + search + hash;
|
|
99
|
+
if (replace) {
|
|
100
|
+
window.history.replaceState(options.state ?? null, '', url);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
window.history.pushState(options.state ?? null, '', url);
|
|
104
|
+
}
|
|
105
|
+
startTransition(() => {
|
|
106
|
+
setState({
|
|
107
|
+
pathname,
|
|
108
|
+
search,
|
|
109
|
+
hash,
|
|
110
|
+
match: matchRoute(pathname, routes),
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
if (scroll) {
|
|
114
|
+
if (hash) {
|
|
115
|
+
const element = document.querySelector(hash);
|
|
116
|
+
element?.scrollIntoView();
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
window.scrollTo(0, 0);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}, [routes]);
|
|
123
|
+
const router = useMemo(() => ({
|
|
124
|
+
push: (path, options) => navigate(path, options),
|
|
125
|
+
replace: (path, options) => navigate(path, { ...options, replace: true }),
|
|
126
|
+
back: () => window.history.back(),
|
|
127
|
+
forward: () => window.history.forward(),
|
|
128
|
+
refresh: () => {
|
|
129
|
+
startTransition(() => {
|
|
130
|
+
setState((prev) => ({ ...prev, match: matchRoute(prev.pathname, routes) }));
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
prefetch: (path) => {
|
|
134
|
+
const { pathname } = parseUrl(path);
|
|
135
|
+
const match = matchRoute(pathname, routes);
|
|
136
|
+
if (match?.route.component) {
|
|
137
|
+
const component = match.route.component;
|
|
138
|
+
if (component._payload && component._init) {
|
|
139
|
+
try {
|
|
140
|
+
component._init(component._payload);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Component will load when rendered
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
}), [navigate, routes]);
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
const handlePopState = () => {
|
|
151
|
+
const { pathname, search, hash } = parseUrl(window.location.href);
|
|
152
|
+
startTransition(() => {
|
|
153
|
+
setState({
|
|
154
|
+
pathname,
|
|
155
|
+
search,
|
|
156
|
+
hash,
|
|
157
|
+
match: matchRoute(pathname, routes),
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
window.addEventListener('popstate', handlePopState);
|
|
162
|
+
return () => window.removeEventListener('popstate', handlePopState);
|
|
163
|
+
}, [routes]);
|
|
164
|
+
// Update document meta on route change
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
if (state.match?.route.meta) {
|
|
167
|
+
state.match.route.meta().then((meta) => {
|
|
168
|
+
if (meta.title) {
|
|
169
|
+
document.title = meta.title;
|
|
170
|
+
}
|
|
171
|
+
if (meta.description) {
|
|
172
|
+
const metaDesc = document.querySelector('meta[name="description"]');
|
|
173
|
+
if (metaDesc) {
|
|
174
|
+
metaDesc.setAttribute('content', meta.description);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}).catch(() => {
|
|
178
|
+
// Ignore meta errors
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}, [state.match]);
|
|
182
|
+
const contextValue = useMemo(() => ({
|
|
183
|
+
state,
|
|
184
|
+
router,
|
|
185
|
+
routes,
|
|
186
|
+
layouts,
|
|
187
|
+
isPending,
|
|
188
|
+
}), [state, router, routes, layouts, isPending]);
|
|
189
|
+
return (_jsx(RouterContext.Provider, { value: contextValue, children: _jsx(Suspense, { fallback: fallback, children: children }) }));
|
|
190
|
+
}
|
|
191
|
+
// ============================================================================
|
|
192
|
+
// Hooks
|
|
193
|
+
// ============================================================================
|
|
194
|
+
export function useRouter() {
|
|
195
|
+
const context = useContext(RouterContext);
|
|
196
|
+
if (!context) {
|
|
197
|
+
throw new Error('useRouter must be used within a RouterProvider');
|
|
198
|
+
}
|
|
199
|
+
return context.router;
|
|
200
|
+
}
|
|
201
|
+
export function useParams() {
|
|
202
|
+
const context = useContext(RouterContext);
|
|
203
|
+
if (!context) {
|
|
204
|
+
throw new Error('useParams must be used within a RouterProvider');
|
|
205
|
+
}
|
|
206
|
+
return (context.state.match?.params ?? {});
|
|
207
|
+
}
|
|
208
|
+
export function usePathname() {
|
|
209
|
+
const context = useContext(RouterContext);
|
|
210
|
+
if (!context) {
|
|
211
|
+
throw new Error('usePathname must be used within a RouterProvider');
|
|
212
|
+
}
|
|
213
|
+
return context.state.pathname;
|
|
214
|
+
}
|
|
215
|
+
export function useSearchParams() {
|
|
216
|
+
const context = useContext(RouterContext);
|
|
217
|
+
if (!context) {
|
|
218
|
+
throw new Error('useSearchParams must be used within a RouterProvider');
|
|
219
|
+
}
|
|
220
|
+
const searchParams = useMemo(() => new URLSearchParams(context.state.search), [context.state.search]);
|
|
221
|
+
const setSearchParams = useCallback((params) => {
|
|
222
|
+
const newParams = new URLSearchParams(params);
|
|
223
|
+
const newSearch = newParams.toString();
|
|
224
|
+
const path = context.state.pathname + (newSearch ? `?${newSearch}` : '') + context.state.hash;
|
|
225
|
+
context.router.push(path, { scroll: false });
|
|
226
|
+
}, [context.router, context.state.pathname, context.state.hash]);
|
|
227
|
+
return [searchParams, setSearchParams];
|
|
228
|
+
}
|
|
229
|
+
export function useRouteMatch() {
|
|
230
|
+
const context = useContext(RouterContext);
|
|
231
|
+
if (!context) {
|
|
232
|
+
throw new Error('useRouteMatch must be used within a RouterProvider');
|
|
233
|
+
}
|
|
234
|
+
return context.state.match;
|
|
235
|
+
}
|
|
236
|
+
export function useIsPending() {
|
|
237
|
+
const context = useContext(RouterContext);
|
|
238
|
+
if (!context) {
|
|
239
|
+
throw new Error('useIsPending must be used within a RouterProvider');
|
|
240
|
+
}
|
|
241
|
+
return context.isPending;
|
|
242
|
+
}
|
|
243
|
+
export function Link({ to, replace = false, prefetch = true, scroll = true, children, onClick, onMouseEnter, ...props }) {
|
|
244
|
+
const context = useContext(RouterContext);
|
|
245
|
+
const handleClick = useCallback((e) => {
|
|
246
|
+
onClick?.(e);
|
|
247
|
+
if (e.defaultPrevented ||
|
|
248
|
+
e.button !== 0 ||
|
|
249
|
+
e.metaKey ||
|
|
250
|
+
e.ctrlKey ||
|
|
251
|
+
e.shiftKey ||
|
|
252
|
+
e.altKey) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const href = to;
|
|
256
|
+
if (href.startsWith('http://') || href.startsWith('https://') || href.startsWith('//')) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
e.preventDefault();
|
|
260
|
+
context?.router[replace ? 'replace' : 'push'](to, { scroll });
|
|
261
|
+
}, [context?.router, to, replace, scroll, onClick]);
|
|
262
|
+
const handleMouseEnter = useCallback((e) => {
|
|
263
|
+
onMouseEnter?.(e);
|
|
264
|
+
if (prefetch && context) {
|
|
265
|
+
context.router.prefetch(to);
|
|
266
|
+
}
|
|
267
|
+
}, [context, to, prefetch, onMouseEnter]);
|
|
268
|
+
return (_jsx("a", { href: to, onClick: handleClick, onMouseEnter: handleMouseEnter, ...props, children: children }));
|
|
269
|
+
}
|
|
270
|
+
export function Outlet({ notFound: NotFound, fallback = null }) {
|
|
271
|
+
const context = useContext(RouterContext);
|
|
272
|
+
if (!context) {
|
|
273
|
+
throw new Error('Outlet must be used within a RouterProvider');
|
|
274
|
+
}
|
|
275
|
+
const { match } = context.state;
|
|
276
|
+
if (!match) {
|
|
277
|
+
return NotFound ? _jsx(NotFound, {}) : null;
|
|
278
|
+
}
|
|
279
|
+
const { route, params } = match;
|
|
280
|
+
const Component = route.component;
|
|
281
|
+
const ErrorComponent = route.errorComponent;
|
|
282
|
+
const PendingComponent = route.pendingComponent;
|
|
283
|
+
const routeElement = (_jsx(Suspense, { fallback: PendingComponent ? _jsx(PendingComponent, {}) : fallback, children: _jsx(Component, { params: params }) }));
|
|
284
|
+
const wrappedElement = (_jsx(LayoutWrapper, { layouts: context.layouts, layoutPath: route.layoutPath, children: ErrorComponent ? (_jsx(RouteErrorBoundary, { fallback: _jsx(ErrorComponent, {}), children: routeElement })) : routeElement }));
|
|
285
|
+
return wrappedElement;
|
|
286
|
+
}
|
|
287
|
+
class RouteErrorBoundary extends React.Component {
|
|
288
|
+
constructor(props) {
|
|
289
|
+
super(props);
|
|
290
|
+
this.state = { hasError: false, error: null };
|
|
291
|
+
}
|
|
292
|
+
static getDerivedStateFromError(error) {
|
|
293
|
+
return { hasError: true, error };
|
|
294
|
+
}
|
|
295
|
+
componentDidCatch(error, errorInfo) {
|
|
296
|
+
console.error('Route error:', error, errorInfo);
|
|
297
|
+
}
|
|
298
|
+
render() {
|
|
299
|
+
if (this.state.hasError) {
|
|
300
|
+
return this.props.fallback;
|
|
301
|
+
}
|
|
302
|
+
return this.props.children;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
export function NavLink({ to, activeClassName, activeStyle, exact = false, pending = false, pendingClassName, pendingStyle, className, style, ...props }) {
|
|
306
|
+
const pathname = usePathname();
|
|
307
|
+
const isPending = useIsPending();
|
|
308
|
+
const isActive = exact
|
|
309
|
+
? pathname === to
|
|
310
|
+
: pathname.startsWith(to) && (to === '/' ? pathname === '/' : true);
|
|
311
|
+
const isPendingRoute = pending && isPending;
|
|
312
|
+
const combinedClassName = [
|
|
313
|
+
className,
|
|
314
|
+
isActive && activeClassName,
|
|
315
|
+
isPendingRoute && pendingClassName,
|
|
316
|
+
].filter(Boolean).join(' ').trim() || undefined;
|
|
317
|
+
const combinedStyle = {
|
|
318
|
+
...style,
|
|
319
|
+
...(isActive ? activeStyle : {}),
|
|
320
|
+
...(isPendingRoute ? pendingStyle : {}),
|
|
321
|
+
};
|
|
322
|
+
return (_jsx(Link, { to: to, className: combinedClassName, style: combinedStyle, ...props }));
|
|
323
|
+
}
|
|
324
|
+
export function Redirect({ to, replace = true }) {
|
|
325
|
+
const router = useRouter();
|
|
326
|
+
useEffect(() => {
|
|
327
|
+
router[replace ? 'replace' : 'push'](to);
|
|
328
|
+
}, [router, to, replace]);
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZapJS Production Router
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Nested layouts
|
|
6
|
+
* - Route-level middleware
|
|
7
|
+
* - Code splitting
|
|
8
|
+
* - Error boundaries
|
|
9
|
+
* - Type-safe navigation
|
|
10
|
+
*/
|
|
11
|
+
import React, { type ReactNode, type ComponentType } from 'react';
|
|
12
|
+
import { type RouteMiddleware } from './middleware.js';
|
|
13
|
+
export interface LayoutDefinition {
|
|
14
|
+
path: string;
|
|
15
|
+
component: React.LazyExoticComponent<ComponentType<any>>;
|
|
16
|
+
parentLayout?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface RouteDefinition {
|
|
19
|
+
path: string;
|
|
20
|
+
pattern: RegExp;
|
|
21
|
+
paramNames: string[];
|
|
22
|
+
component: React.LazyExoticComponent<ComponentType<any>>;
|
|
23
|
+
layoutPath?: string;
|
|
24
|
+
errorComponent?: React.LazyExoticComponent<ComponentType<any>>;
|
|
25
|
+
pendingComponent?: React.LazyExoticComponent<ComponentType<any>>;
|
|
26
|
+
meta?: () => Promise<RouteMeta>;
|
|
27
|
+
middleware?: () => Promise<RouteMiddleware[]>;
|
|
28
|
+
}
|
|
29
|
+
export interface RouteMeta {
|
|
30
|
+
title?: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
keywords?: string[];
|
|
33
|
+
[key: string]: any;
|
|
34
|
+
}
|
|
35
|
+
export interface RouteMatch {
|
|
36
|
+
route: RouteDefinition;
|
|
37
|
+
params: Record<string, string>;
|
|
38
|
+
pathname: string;
|
|
39
|
+
}
|
|
40
|
+
export interface RouterState {
|
|
41
|
+
pathname: string;
|
|
42
|
+
search: string;
|
|
43
|
+
hash: string;
|
|
44
|
+
match: RouteMatch | null;
|
|
45
|
+
middlewareData?: Record<string, any>;
|
|
46
|
+
}
|
|
47
|
+
export interface NavigateOptions {
|
|
48
|
+
replace?: boolean;
|
|
49
|
+
scroll?: boolean;
|
|
50
|
+
state?: unknown;
|
|
51
|
+
}
|
|
52
|
+
export interface Router {
|
|
53
|
+
push(path: string, options?: NavigateOptions): void;
|
|
54
|
+
replace(path: string, options?: NavigateOptions): void;
|
|
55
|
+
back(): void;
|
|
56
|
+
forward(): void;
|
|
57
|
+
refresh(): void;
|
|
58
|
+
prefetch(path: string): void;
|
|
59
|
+
}
|
|
60
|
+
interface RouterProviderProps {
|
|
61
|
+
routes: RouteDefinition[];
|
|
62
|
+
layouts?: LayoutDefinition[];
|
|
63
|
+
children: ReactNode;
|
|
64
|
+
notFound?: ComponentType;
|
|
65
|
+
fallback?: ReactNode;
|
|
66
|
+
onRouteError?: (error: Error) => void;
|
|
67
|
+
}
|
|
68
|
+
export declare function RouterProvider({ routes, layouts, children, notFound: NotFound, fallback, onRouteError, }: RouterProviderProps): JSX.Element;
|
|
69
|
+
export declare function useRouter(): Router;
|
|
70
|
+
export declare function useParams<T extends Record<string, string> = Record<string, string>>(): T;
|
|
71
|
+
export declare function usePathname(): string;
|
|
72
|
+
export declare function useSearchParams(): [URLSearchParams, (params: Record<string, string>) => void];
|
|
73
|
+
export declare function useMiddlewareData<T = Record<string, any>>(): T | undefined;
|
|
74
|
+
export declare function useRouteMatch(): RouteMatch | null;
|
|
75
|
+
export declare function useIsPending(): boolean;
|
|
76
|
+
export interface LinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {
|
|
77
|
+
to: string;
|
|
78
|
+
replace?: boolean;
|
|
79
|
+
prefetch?: boolean;
|
|
80
|
+
scroll?: boolean;
|
|
81
|
+
children: ReactNode;
|
|
82
|
+
}
|
|
83
|
+
export declare function Link({ to, replace, prefetch, scroll, children, onClick, onMouseEnter, ...props }: LinkProps): JSX.Element;
|
|
84
|
+
interface OutletProps {
|
|
85
|
+
notFound?: ComponentType;
|
|
86
|
+
fallback?: ReactNode;
|
|
87
|
+
}
|
|
88
|
+
export declare function Outlet({ notFound: NotFound, fallback }: OutletProps): JSX.Element | null;
|
|
89
|
+
interface NavLinkProps extends LinkProps {
|
|
90
|
+
activeClassName?: string;
|
|
91
|
+
activeStyle?: React.CSSProperties;
|
|
92
|
+
exact?: boolean;
|
|
93
|
+
pending?: boolean;
|
|
94
|
+
pendingClassName?: string;
|
|
95
|
+
pendingStyle?: React.CSSProperties;
|
|
96
|
+
}
|
|
97
|
+
export declare function NavLink({ to, activeClassName, activeStyle, exact, pending, pendingClassName, pendingStyle, className, style, ...props }: NavLinkProps): JSX.Element;
|
|
98
|
+
interface RedirectProps {
|
|
99
|
+
to: string;
|
|
100
|
+
replace?: boolean;
|
|
101
|
+
}
|
|
102
|
+
export declare function Redirect({ to, replace }: RedirectProps): null;
|
|
103
|
+
export {};
|