@netrojs/fnetro 0.2.21 → 0.3.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/README.md +185 -878
- package/client.ts +213 -242
- package/core.ts +74 -175
- package/dist/client.d.ts +69 -60
- package/dist/client.js +170 -177
- package/dist/core.d.ts +57 -40
- package/dist/core.js +50 -28
- package/dist/server.d.ts +69 -66
- package/dist/server.js +178 -199
- package/dist/types.d.ts +99 -0
- package/package.json +21 -20
- package/server.ts +263 -350
- package/types.ts +125 -0
package/core.ts
CHANGED
|
@@ -1,135 +1,41 @@
|
|
|
1
1
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
2
2
|
// FNetro · core.ts
|
|
3
|
-
//
|
|
4
|
-
// Reactivity: consumers use solid-js primitives directly
|
|
3
|
+
// Route builders · path matching · route resolution · async-loader detection
|
|
5
4
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
6
5
|
|
|
7
|
-
import type {
|
|
8
|
-
import type {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
ogImageWidth?: string
|
|
36
|
-
ogImageHeight?: string
|
|
37
|
-
ogUrl?: string
|
|
38
|
-
ogType?: string
|
|
39
|
-
ogSiteName?: string
|
|
40
|
-
ogLocale?: string
|
|
41
|
-
// Twitter / X
|
|
42
|
-
twitterCard?: 'summary' | 'summary_large_image' | 'app' | 'player'
|
|
43
|
-
twitterSite?: string
|
|
44
|
-
twitterCreator?: string
|
|
45
|
-
twitterTitle?: string
|
|
46
|
-
twitterDescription?: string
|
|
47
|
-
twitterImage?: string
|
|
48
|
-
twitterImageAlt?: string
|
|
49
|
-
// Structured data (JSON-LD)
|
|
50
|
-
jsonLd?: Record<string, unknown> | Record<string, unknown>[]
|
|
51
|
-
// Arbitrary extra <meta> tags
|
|
52
|
-
extra?: Array<{ name?: string; property?: string; httpEquiv?: string; content: string }>
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// ══════════════════════════════════════════════════════════════════════════════
|
|
56
|
-
// § 3 Component prop shapes
|
|
57
|
-
// ══════════════════════════════════════════════════════════════════════════════
|
|
58
|
-
|
|
59
|
-
export type PageProps<TData extends object = {}> = TData & {
|
|
60
|
-
url: string
|
|
61
|
-
params: Record<string, string>
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export interface LayoutProps {
|
|
65
|
-
children: JSX.Element
|
|
66
|
-
url: string
|
|
67
|
-
params: Record<string, string>
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// ══════════════════════════════════════════════════════════════════════════════
|
|
71
|
-
// § 4 Route definitions
|
|
72
|
-
// ══════════════════════════════════════════════════════════════════════════════
|
|
73
|
-
|
|
74
|
-
export interface PageDef<TData extends object = {}> {
|
|
75
|
-
readonly __type: 'page'
|
|
76
|
-
path: string
|
|
77
|
-
middleware?: HonoMiddleware[]
|
|
78
|
-
loader?: (c: LoaderCtx) => TData | Promise<TData>
|
|
79
|
-
seo?: SEOMeta | ((data: TData, params: Record<string, string>) => SEOMeta)
|
|
80
|
-
layout?: LayoutDef | false
|
|
81
|
-
Page: Component<PageProps<TData>>
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export interface GroupDef {
|
|
85
|
-
readonly __type: 'group'
|
|
86
|
-
prefix: string
|
|
87
|
-
layout?: LayoutDef | false
|
|
88
|
-
middleware?: HonoMiddleware[]
|
|
89
|
-
routes: Route[]
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export interface LayoutDef {
|
|
93
|
-
readonly __type: 'layout'
|
|
94
|
-
Component: Component<LayoutProps>
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export interface ApiRouteDef {
|
|
98
|
-
readonly __type: 'api'
|
|
99
|
-
path: string
|
|
100
|
-
register: (app: Hono, globalMiddleware: HonoMiddleware[]) => void
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export type Route = PageDef<any> | GroupDef | ApiRouteDef
|
|
104
|
-
|
|
105
|
-
// ══════════════════════════════════════════════════════════════════════════════
|
|
106
|
-
// § 5 App config
|
|
107
|
-
// ══════════════════════════════════════════════════════════════════════════════
|
|
108
|
-
|
|
109
|
-
export interface AppConfig {
|
|
110
|
-
layout?: LayoutDef
|
|
111
|
-
seo?: SEOMeta
|
|
112
|
-
middleware?: HonoMiddleware[]
|
|
113
|
-
routes: Route[]
|
|
114
|
-
notFound?: Component
|
|
115
|
-
htmlAttrs?: Record<string, string>
|
|
116
|
-
head?: string
|
|
6
|
+
import type { Component } from 'vue'
|
|
7
|
+
import type {
|
|
8
|
+
PageDef, GroupDef, LayoutDef, ApiRouteDef, Route,
|
|
9
|
+
ResolvedRoute, CompiledPath, HonoMiddleware, AsyncLoader,
|
|
10
|
+
} from './types'
|
|
11
|
+
|
|
12
|
+
// ── Async-loader detection ────────────────────────────────────────────────────
|
|
13
|
+
//
|
|
14
|
+
// A Vue component (SFC compiled by vite-plugin-vue) always carries one or more
|
|
15
|
+
// of these brand properties. A plain () => import('./Page.vue') factory has
|
|
16
|
+
// none of them, so checking for their absence is sufficient for real-world use.
|
|
17
|
+
|
|
18
|
+
const VUE_BRANDS = ['__name', '__file', '__vccOpts', 'setup', 'render', 'data', 'components'] as const
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns true when `c` is an async factory function (i.e. `() => import(...)`)
|
|
22
|
+
* rather than a resolved Vue component object.
|
|
23
|
+
*
|
|
24
|
+
* Used by both server.ts (to resolve the import before SSR) and client.ts
|
|
25
|
+
* (to wrap with defineAsyncComponent for lazy hydration).
|
|
26
|
+
*/
|
|
27
|
+
export function isAsyncLoader(c: unknown): c is AsyncLoader {
|
|
28
|
+
if (typeof c !== 'function') return false
|
|
29
|
+
const f = c as unknown as Record<string, unknown>
|
|
30
|
+
for (const brand of VUE_BRANDS) {
|
|
31
|
+
if (brand in f) return false
|
|
32
|
+
}
|
|
33
|
+
return true
|
|
117
34
|
}
|
|
118
35
|
|
|
119
|
-
//
|
|
120
|
-
// § 6 Client middleware
|
|
121
|
-
// ══════════════════════════════════════════════════════════════════════════════
|
|
122
|
-
|
|
123
|
-
export type ClientMiddleware = (
|
|
124
|
-
url: string,
|
|
125
|
-
next: () => Promise<void>,
|
|
126
|
-
) => Promise<void>
|
|
36
|
+
// ── Builder functions ─────────────────────────────────────────────────────────
|
|
127
37
|
|
|
128
|
-
|
|
129
|
-
// § 7 Builder functions
|
|
130
|
-
// ══════════════════════════════════════════════════════════════════════════════
|
|
131
|
-
|
|
132
|
-
export function definePage<TData extends object = {}>(
|
|
38
|
+
export function definePage<TData extends object = Record<string, never>>(
|
|
133
39
|
def: Omit<PageDef<TData>, '__type'>,
|
|
134
40
|
): PageDef<TData> {
|
|
135
41
|
return { __type: 'page', ...def }
|
|
@@ -139,8 +45,9 @@ export function defineGroup(def: Omit<GroupDef, '__type'>): GroupDef {
|
|
|
139
45
|
return { __type: 'group', ...def }
|
|
140
46
|
}
|
|
141
47
|
|
|
142
|
-
|
|
143
|
-
|
|
48
|
+
/** Wrap a Vue layout component (must render <slot />) as a FNetro layout. */
|
|
49
|
+
export function defineLayout(component: Component): LayoutDef {
|
|
50
|
+
return { __type: 'layout', component }
|
|
144
51
|
}
|
|
145
52
|
|
|
146
53
|
export function defineApiRoute(
|
|
@@ -150,17 +57,42 @@ export function defineApiRoute(
|
|
|
150
57
|
return { __type: 'api', path, register }
|
|
151
58
|
}
|
|
152
59
|
|
|
153
|
-
//
|
|
154
|
-
|
|
155
|
-
|
|
60
|
+
// ── Path matching (FNetro [param] syntax → RegExp) ────────────────────────────
|
|
61
|
+
|
|
62
|
+
export function compilePath(path: string): CompiledPath {
|
|
63
|
+
const keys: string[] = []
|
|
64
|
+
const src = path
|
|
65
|
+
.replace(/\[\.\.\.([^\]]+)\]/g, (_, k: string) => { keys.push(k); return '(.*)' })
|
|
66
|
+
.replace(/\[([^\]]+)\]/g, (_, k: string) => { keys.push(k); return '([^/]+)' })
|
|
67
|
+
.replace(/\*/g, '(.*)')
|
|
68
|
+
return { re: new RegExp(`^${src}$`), keys }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function matchPath(
|
|
72
|
+
cp: CompiledPath,
|
|
73
|
+
pathname: string,
|
|
74
|
+
): Record<string, string> | null {
|
|
75
|
+
const m = pathname.match(cp.re)
|
|
76
|
+
if (!m) return null
|
|
77
|
+
const params: Record<string, string> = {}
|
|
78
|
+
cp.keys.forEach((k, i) => { params[k] = decodeURIComponent(m[i + 1] ?? '') })
|
|
79
|
+
return params
|
|
80
|
+
}
|
|
156
81
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Convert FNetro `[param]` syntax to Vue Router `:param` syntax.
|
|
84
|
+
*
|
|
85
|
+
* `/posts/[slug]` → `/posts/:slug`
|
|
86
|
+
* `/files/[...path]` → `/files/:path(.*)*`
|
|
87
|
+
*/
|
|
88
|
+
export function toVueRouterPath(fnetroPath: string): string {
|
|
89
|
+
return fnetroPath
|
|
90
|
+
.replace(/\[\.\.\.([^\]]+)\]/g, ':$1(.*)*')
|
|
91
|
+
.replace(/\[([^\]]+)\]/g, ':$1')
|
|
162
92
|
}
|
|
163
93
|
|
|
94
|
+
// ── Route resolution ──────────────────────────────────────────────────────────
|
|
95
|
+
|
|
164
96
|
export function resolveRoutes(
|
|
165
97
|
routes: Route[],
|
|
166
98
|
options: {
|
|
@@ -179,7 +111,11 @@ export function resolveRoutes(
|
|
|
179
111
|
const prefix = (options.prefix ?? '') + route.prefix
|
|
180
112
|
const mw = [...(options.middleware ?? []), ...(route.middleware ?? [])]
|
|
181
113
|
const layout = route.layout !== undefined ? route.layout : options.layout
|
|
182
|
-
const sub = resolveRoutes(route.routes, {
|
|
114
|
+
const sub = resolveRoutes(route.routes, {
|
|
115
|
+
prefix,
|
|
116
|
+
middleware: mw,
|
|
117
|
+
...(layout !== undefined && { layout }),
|
|
118
|
+
})
|
|
183
119
|
pages.push(...sub.pages)
|
|
184
120
|
apis.push(...sub.apis)
|
|
185
121
|
} else {
|
|
@@ -195,42 +131,5 @@ export function resolveRoutes(
|
|
|
195
131
|
return { pages, apis }
|
|
196
132
|
}
|
|
197
133
|
|
|
198
|
-
//
|
|
199
|
-
|
|
200
|
-
// ══════════════════════════════════════════════════════════════════════════════
|
|
201
|
-
|
|
202
|
-
export interface CompiledPath {
|
|
203
|
-
re: RegExp
|
|
204
|
-
keys: string[]
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export function compilePath(path: string): CompiledPath {
|
|
208
|
-
const keys: string[] = []
|
|
209
|
-
const src = path
|
|
210
|
-
.replace(/\[\.\.\.([^\]]+)\]/g, (_, k: string) => { keys.push(k); return '(.*)' })
|
|
211
|
-
.replace(/\[([^\]]+)\]/g, (_, k: string) => { keys.push(k); return '([^/]+)' })
|
|
212
|
-
.replace(/\*/g, '(.*)')
|
|
213
|
-
return { re: new RegExp(`^${src}$`), keys }
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export function matchPath(
|
|
217
|
-
compiled: CompiledPath,
|
|
218
|
-
pathname: string,
|
|
219
|
-
): Record<string, string> | null {
|
|
220
|
-
const m = pathname.match(compiled.re)
|
|
221
|
-
if (!m) return null
|
|
222
|
-
const params: Record<string, string> = {}
|
|
223
|
-
compiled.keys.forEach((k, i) => {
|
|
224
|
-
params[k] = decodeURIComponent(m[i + 1] ?? '')
|
|
225
|
-
})
|
|
226
|
-
return params
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// ══════════════════════════════════════════════════════════════════════════════
|
|
230
|
-
// § 10 Shared constants
|
|
231
|
-
// ══════════════════════════════════════════════════════════════════════════════
|
|
232
|
-
|
|
233
|
-
export const SPA_HEADER = 'x-fnetro-spa'
|
|
234
|
-
export const STATE_KEY = '__FNETRO_STATE__'
|
|
235
|
-
export const PARAMS_KEY = '__FNETRO_PARAMS__'
|
|
236
|
-
export const SEO_KEY = '__FNETRO_SEO__'
|
|
134
|
+
// Re-export all types so `import from '@netrojs/fnetro'` (root export) works
|
|
135
|
+
export * from './types'
|
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { Component } from 'vue';
|
|
1
2
|
import { Hono, MiddlewareHandler, Context } from 'hono';
|
|
2
|
-
|
|
3
|
-
export { A, useLocation, useNavigate, useParams, useSearchParams } from '@solidjs/router';
|
|
3
|
+
export { RouterLink, RouterView, useRoute, useRouter } from 'vue-router';
|
|
4
4
|
|
|
5
5
|
type HonoMiddleware = MiddlewareHandler;
|
|
6
6
|
type LoaderCtx = Context;
|
|
@@ -16,44 +16,33 @@ interface SEOMeta {
|
|
|
16
16
|
ogDescription?: string;
|
|
17
17
|
ogImage?: string;
|
|
18
18
|
ogImageAlt?: string;
|
|
19
|
-
ogImageWidth?: string;
|
|
20
|
-
ogImageHeight?: string;
|
|
21
19
|
ogUrl?: string;
|
|
22
20
|
ogType?: string;
|
|
23
21
|
ogSiteName?: string;
|
|
24
|
-
ogLocale?: string;
|
|
25
22
|
twitterCard?: 'summary' | 'summary_large_image' | 'app' | 'player';
|
|
26
23
|
twitterSite?: string;
|
|
27
|
-
twitterCreator?: string;
|
|
28
24
|
twitterTitle?: string;
|
|
29
25
|
twitterDescription?: string;
|
|
30
26
|
twitterImage?: string;
|
|
31
|
-
|
|
27
|
+
/** Structured data injected as <script type="application/ld+json">. */
|
|
32
28
|
jsonLd?: Record<string, unknown> | Record<string, unknown>[];
|
|
33
|
-
extra?: Array<{
|
|
34
|
-
name?: string;
|
|
35
|
-
property?: string;
|
|
36
|
-
httpEquiv?: string;
|
|
37
|
-
content: string;
|
|
38
|
-
}>;
|
|
39
29
|
}
|
|
40
|
-
type
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
interface LayoutProps {
|
|
45
|
-
children: JSX.Element;
|
|
46
|
-
url: string;
|
|
47
|
-
params: Record<string, string>;
|
|
48
|
-
}
|
|
49
|
-
interface PageDef<TData extends object = {}> {
|
|
30
|
+
type AsyncLoader = () => Promise<{
|
|
31
|
+
default: Component;
|
|
32
|
+
} | Component>;
|
|
33
|
+
interface PageDef<TData extends object = Record<string, never>> {
|
|
50
34
|
readonly __type: 'page';
|
|
51
35
|
path: string;
|
|
52
36
|
middleware?: HonoMiddleware[];
|
|
53
37
|
loader?: (c: LoaderCtx) => TData | Promise<TData>;
|
|
54
38
|
seo?: SEOMeta | ((data: TData, params: Record<string, string>) => SEOMeta);
|
|
39
|
+
/** Override or disable the app-level layout for this route. */
|
|
55
40
|
layout?: LayoutDef | false;
|
|
56
|
-
|
|
41
|
+
/**
|
|
42
|
+
* The Vue component to render for this route.
|
|
43
|
+
* Use () => import('./Page.vue') for automatic code splitting.
|
|
44
|
+
*/
|
|
45
|
+
component: Component | AsyncLoader;
|
|
57
46
|
}
|
|
58
47
|
interface GroupDef {
|
|
59
48
|
readonly __type: 'group';
|
|
@@ -64,7 +53,8 @@ interface GroupDef {
|
|
|
64
53
|
}
|
|
65
54
|
interface LayoutDef {
|
|
66
55
|
readonly __type: 'layout';
|
|
67
|
-
|
|
56
|
+
/** Vue layout component — must contain <slot /> for page content. */
|
|
57
|
+
component: Component;
|
|
68
58
|
}
|
|
69
59
|
interface ApiRouteDef {
|
|
70
60
|
readonly __type: 'api';
|
|
@@ -79,38 +69,67 @@ interface AppConfig {
|
|
|
79
69
|
routes: Route[];
|
|
80
70
|
notFound?: Component;
|
|
81
71
|
htmlAttrs?: Record<string, string>;
|
|
72
|
+
/** Extra HTML injected into <head> (e.g. font preloads). */
|
|
82
73
|
head?: string;
|
|
83
74
|
}
|
|
84
|
-
type ClientMiddleware = (url: string, next: () => Promise<void>) => Promise<void>;
|
|
85
|
-
declare function definePage<TData extends object = {}>(def: Omit<PageDef<TData>, '__type'>): PageDef<TData>;
|
|
86
|
-
declare function defineGroup(def: Omit<GroupDef, '__type'>): GroupDef;
|
|
87
|
-
declare function defineLayout(Component: Component<LayoutProps>): LayoutDef;
|
|
88
|
-
declare function defineApiRoute(path: string, register: ApiRouteDef['register']): ApiRouteDef;
|
|
89
75
|
interface ResolvedRoute {
|
|
90
76
|
fullPath: string;
|
|
91
77
|
page: PageDef<any>;
|
|
92
78
|
layout: LayoutDef | false | undefined;
|
|
93
79
|
middleware: HonoMiddleware[];
|
|
94
80
|
}
|
|
95
|
-
declare function resolveRoutes(routes: Route[], options?: {
|
|
96
|
-
prefix?: string;
|
|
97
|
-
middleware?: HonoMiddleware[];
|
|
98
|
-
layout?: LayoutDef | false;
|
|
99
|
-
}): {
|
|
100
|
-
pages: ResolvedRoute[];
|
|
101
|
-
apis: ApiRouteDef[];
|
|
102
|
-
};
|
|
103
81
|
interface CompiledPath {
|
|
104
82
|
re: RegExp;
|
|
105
83
|
keys: string[];
|
|
106
84
|
}
|
|
107
|
-
|
|
108
|
-
|
|
85
|
+
type ClientMiddleware = (url: string, next: () => Promise<void>) => Promise<void>;
|
|
86
|
+
/** Custom request header that identifies an SPA navigation (JSON payload). */
|
|
109
87
|
declare const SPA_HEADER = "x-fnetro-spa";
|
|
88
|
+
/** window key for SSR-injected per-page loader data. */
|
|
110
89
|
declare const STATE_KEY = "__FNETRO_STATE__";
|
|
90
|
+
/** window key for SSR-injected URL params. */
|
|
111
91
|
declare const PARAMS_KEY = "__FNETRO_PARAMS__";
|
|
92
|
+
/** window key for SSR-injected SEO meta. */
|
|
112
93
|
declare const SEO_KEY = "__FNETRO_SEO__";
|
|
94
|
+
/**
|
|
95
|
+
* Vue provide/inject key for the reactive page-data object.
|
|
96
|
+
* Symbol.for() ensures the same reference across module instances (SSR safe).
|
|
97
|
+
*/
|
|
98
|
+
declare const DATA_KEY: unique symbol;
|
|
113
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Returns true when `c` is an async factory function (i.e. `() => import(...)`)
|
|
102
|
+
* rather than a resolved Vue component object.
|
|
103
|
+
*
|
|
104
|
+
* Used by both server.ts (to resolve the import before SSR) and client.ts
|
|
105
|
+
* (to wrap with defineAsyncComponent for lazy hydration).
|
|
106
|
+
*/
|
|
107
|
+
declare function isAsyncLoader(c: unknown): c is AsyncLoader;
|
|
108
|
+
declare function definePage<TData extends object = Record<string, never>>(def: Omit<PageDef<TData>, '__type'>): PageDef<TData>;
|
|
109
|
+
declare function defineGroup(def: Omit<GroupDef, '__type'>): GroupDef;
|
|
110
|
+
/** Wrap a Vue layout component (must render <slot />) as a FNetro layout. */
|
|
111
|
+
declare function defineLayout(component: Component): LayoutDef;
|
|
112
|
+
declare function defineApiRoute(path: string, register: ApiRouteDef['register']): ApiRouteDef;
|
|
113
|
+
declare function compilePath(path: string): CompiledPath;
|
|
114
|
+
declare function matchPath(cp: CompiledPath, pathname: string): Record<string, string> | null;
|
|
115
|
+
/**
|
|
116
|
+
* Convert FNetro `[param]` syntax to Vue Router `:param` syntax.
|
|
117
|
+
*
|
|
118
|
+
* `/posts/[slug]` → `/posts/:slug`
|
|
119
|
+
* `/files/[...path]` → `/files/:path(.*)*`
|
|
120
|
+
*/
|
|
121
|
+
declare function toVueRouterPath(fnetroPath: string): string;
|
|
122
|
+
declare function resolveRoutes(routes: Route[], options?: {
|
|
123
|
+
prefix?: string;
|
|
124
|
+
middleware?: HonoMiddleware[];
|
|
125
|
+
layout?: LayoutDef | false;
|
|
126
|
+
}): {
|
|
127
|
+
pages: ResolvedRoute[];
|
|
128
|
+
apis: ApiRouteDef[];
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
declare function syncSEO(seo: SEOMeta): void;
|
|
132
|
+
declare function prefetch(url: string): void;
|
|
114
133
|
/**
|
|
115
134
|
* Register a client-side navigation middleware.
|
|
116
135
|
* Must be called **before** `boot()`.
|
|
@@ -119,35 +138,25 @@ declare const SEO_KEY = "__FNETRO_SEO__";
|
|
|
119
138
|
* useClientMiddleware(async (url, next) => {
|
|
120
139
|
* if (!isLoggedIn() && url.startsWith('/dashboard')) {
|
|
121
140
|
* await navigate('/login')
|
|
122
|
-
* return
|
|
141
|
+
* return
|
|
123
142
|
* }
|
|
124
143
|
* await next()
|
|
125
144
|
* })
|
|
126
145
|
*/
|
|
127
146
|
declare function useClientMiddleware(mw: ClientMiddleware): void;
|
|
128
|
-
declare function syncSEO(seo: SEOMeta): void;
|
|
129
|
-
interface NavPayload {
|
|
130
|
-
state: Record<string, unknown>;
|
|
131
|
-
params: Record<string, string>;
|
|
132
|
-
seo: SEOMeta;
|
|
133
|
-
url: string;
|
|
134
|
-
}
|
|
135
|
-
declare function fetchPayload(href: string): Promise<NavPayload>;
|
|
136
|
-
/** Warm the prefetch cache for a URL on hover/focus/etc. */
|
|
137
|
-
declare function prefetch(url: string): void;
|
|
138
|
-
interface NavigateOptions {
|
|
139
|
-
replace?: boolean;
|
|
140
|
-
scroll?: boolean;
|
|
141
|
-
}
|
|
142
147
|
/**
|
|
143
|
-
*
|
|
144
|
-
*
|
|
148
|
+
* Access the current page's loader data inside any Vue component.
|
|
149
|
+
* The returned object is reactive — it updates automatically on navigation.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* const data = usePageData<{ title: string; posts: Post[] }>()
|
|
153
|
+
* // data.title is typed and reactive
|
|
145
154
|
*/
|
|
146
|
-
declare function
|
|
155
|
+
declare function usePageData<T extends Record<string, unknown> = Record<string, unknown>>(): T;
|
|
147
156
|
interface BootOptions extends AppConfig {
|
|
148
|
-
/**
|
|
157
|
+
/** Warm fetch cache on link hover. @default true */
|
|
149
158
|
prefetchOnHover?: boolean;
|
|
150
159
|
}
|
|
151
160
|
declare function boot(options: BootOptions): Promise<void>;
|
|
152
161
|
|
|
153
|
-
export { type ApiRouteDef, type AppConfig, type BootOptions, type ClientMiddleware, type CompiledPath, type GroupDef, type HonoMiddleware, type LayoutDef, type
|
|
162
|
+
export { type ApiRouteDef, type AppConfig, type AsyncLoader, type BootOptions, type ClientMiddleware, type CompiledPath, DATA_KEY, type GroupDef, type HonoMiddleware, type LayoutDef, type LoaderCtx, PARAMS_KEY, type PageDef, type ResolvedRoute, type Route, type SEOMeta, SEO_KEY, SPA_HEADER, STATE_KEY, boot, compilePath, defineApiRoute, defineGroup, defineLayout, definePage, isAsyncLoader, matchPath, prefetch, resolveRoutes, syncSEO, toVueRouterPath, useClientMiddleware, usePageData };
|