@zenithbuild/core 0.6.3 → 1.2.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/cli/commands/dev.ts +107 -48
- package/compiler/discovery/componentDiscovery.ts +75 -11
- package/compiler/output/types.ts +15 -1
- package/compiler/parse/parseTemplate.ts +29 -0
- package/compiler/runtime/dataExposure.ts +27 -12
- package/compiler/runtime/generateDOM.ts +12 -3
- package/compiler/runtime/transformIR.ts +36 -0
- package/compiler/runtime/wrapExpression.ts +32 -13
- package/compiler/runtime/wrapExpressionWithLoop.ts +24 -10
- package/compiler/ssg-build.ts +71 -7
- package/compiler/test/component-stacking.test.ts +365 -0
- package/compiler/transform/componentResolver.ts +42 -4
- package/compiler/transform/fragmentLowering.ts +153 -1
- package/compiler/transform/generateBindings.ts +31 -10
- package/compiler/transform/transformNode.ts +114 -1
- package/core/config/index.ts +5 -3
- package/core/config/types.ts +67 -37
- package/core/plugins/bridge.ts +193 -0
- package/core/plugins/registry.ts +51 -6
- package/dist/cli.js +8 -0
- package/dist/zen-build.js +482 -1802
- package/dist/zen-dev.js +482 -1802
- package/dist/zen-preview.js +482 -1802
- package/dist/zenith.js +482 -1802
- package/package.json +11 -3
- package/runtime/bundle-generator.ts +10 -1
- package/runtime/client-runtime.ts +462 -120
- package/cli/utils/content.ts +0 -112
- package/router/manifest.ts +0 -314
- package/router/navigation/ZenLink.zen +0 -231
- package/router/navigation/index.ts +0 -78
- package/router/navigation/zen-link.ts +0 -584
- package/router/runtime.ts +0 -458
- package/router/types.ts +0 -168
package/router/runtime.ts
DELETED
|
@@ -1,458 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Zenith Runtime Router
|
|
3
|
-
*
|
|
4
|
-
* SPA-style client-side router that handles:
|
|
5
|
-
* - URL resolution and route matching
|
|
6
|
-
* - Browser history management (pushState/popstate)
|
|
7
|
-
* - Reactive route state
|
|
8
|
-
* - Page component mounting/unmounting
|
|
9
|
-
*
|
|
10
|
-
* Extension points for future ZenLink:
|
|
11
|
-
* - navigate() API
|
|
12
|
-
* - beforeEach/afterEach guards
|
|
13
|
-
* - Active link state
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import type {
|
|
17
|
-
RouteState,
|
|
18
|
-
NavigateOptions,
|
|
19
|
-
RouteRecord,
|
|
20
|
-
PageModule
|
|
21
|
-
} from "./types"
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Runtime route record (with load function bound)
|
|
25
|
-
*/
|
|
26
|
-
interface RuntimeRouteRecord {
|
|
27
|
-
path: string
|
|
28
|
-
regex: RegExp
|
|
29
|
-
paramNames: string[]
|
|
30
|
-
score: number
|
|
31
|
-
filePath: string
|
|
32
|
-
/** Page module or loader function */
|
|
33
|
-
module?: PageModule
|
|
34
|
-
load?: () => PageModule
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Global route state - reactive and accessible from page components
|
|
39
|
-
*/
|
|
40
|
-
let currentRoute: RouteState = {
|
|
41
|
-
path: "/",
|
|
42
|
-
params: {},
|
|
43
|
-
query: {}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Route change listeners
|
|
48
|
-
*/
|
|
49
|
-
type RouteListener = (route: RouteState, prevRoute: RouteState) => void
|
|
50
|
-
const routeListeners: Set<RouteListener> = new Set()
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Route manifest (populated at build time)
|
|
54
|
-
*/
|
|
55
|
-
let routeManifest: RuntimeRouteRecord[] = []
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Current page module
|
|
59
|
-
*/
|
|
60
|
-
let currentPageModule: PageModule | null = null
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Router outlet element
|
|
64
|
-
*/
|
|
65
|
-
let routerOutlet: HTMLElement | null = null
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Initialize the router with the route manifest
|
|
69
|
-
*/
|
|
70
|
-
export function initRouter(
|
|
71
|
-
manifest: RuntimeRouteRecord[],
|
|
72
|
-
outlet?: HTMLElement | string
|
|
73
|
-
): void {
|
|
74
|
-
routeManifest = manifest
|
|
75
|
-
|
|
76
|
-
// Set router outlet
|
|
77
|
-
if (outlet) {
|
|
78
|
-
routerOutlet = typeof outlet === "string"
|
|
79
|
-
? document.querySelector(outlet)
|
|
80
|
-
: outlet
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Listen for popstate (back/forward navigation)
|
|
84
|
-
window.addEventListener("popstate", handlePopState)
|
|
85
|
-
|
|
86
|
-
// Resolve initial route
|
|
87
|
-
const initialPath = window.location.pathname
|
|
88
|
-
const initialQuery = parseQueryString(window.location.search)
|
|
89
|
-
|
|
90
|
-
resolveAndRender(initialPath, initialQuery, false)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Parse query string into object
|
|
95
|
-
*/
|
|
96
|
-
function parseQueryString(search: string): Record<string, string> {
|
|
97
|
-
const query: Record<string, string> = {}
|
|
98
|
-
|
|
99
|
-
if (!search || search === "?") {
|
|
100
|
-
return query
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const params = new URLSearchParams(search)
|
|
104
|
-
params.forEach((value, key) => {
|
|
105
|
-
query[key] = value
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
return query
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Handle browser back/forward navigation
|
|
113
|
-
*/
|
|
114
|
-
function handlePopState(_event: PopStateEvent): void {
|
|
115
|
-
const path = window.location.pathname
|
|
116
|
-
const query = parseQueryString(window.location.search)
|
|
117
|
-
|
|
118
|
-
// Don't update history on popstate - browser already changed it
|
|
119
|
-
resolveAndRender(path, query, false, false)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Resolve route from path
|
|
124
|
-
*/
|
|
125
|
-
export function resolveRoute(
|
|
126
|
-
pathname: string
|
|
127
|
-
): { record: RuntimeRouteRecord; params: Record<string, string> } | null {
|
|
128
|
-
// Normalize pathname
|
|
129
|
-
const normalizedPath = pathname === "" ? "/" : pathname
|
|
130
|
-
|
|
131
|
-
for (const route of routeManifest) {
|
|
132
|
-
const match = route.regex.exec(normalizedPath)
|
|
133
|
-
|
|
134
|
-
if (match) {
|
|
135
|
-
// Extract params from capture groups
|
|
136
|
-
const params: Record<string, string> = {}
|
|
137
|
-
|
|
138
|
-
for (let i = 0; i < route.paramNames.length; i++) {
|
|
139
|
-
const paramName = route.paramNames[i]
|
|
140
|
-
const paramValue = match[i + 1] // +1 because match[0] is full match
|
|
141
|
-
|
|
142
|
-
if (paramName && paramValue !== undefined) {
|
|
143
|
-
params[paramName] = decodeURIComponent(paramValue)
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return { record: route, params }
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return null
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Resolve route and render page
|
|
156
|
-
*/
|
|
157
|
-
async function resolveAndRender(
|
|
158
|
-
path: string,
|
|
159
|
-
query: Record<string, string>,
|
|
160
|
-
updateHistory: boolean = true,
|
|
161
|
-
replace: boolean = false
|
|
162
|
-
): Promise<void> {
|
|
163
|
-
const prevRoute = { ...currentRoute }
|
|
164
|
-
|
|
165
|
-
const resolved = resolveRoute(path)
|
|
166
|
-
|
|
167
|
-
if (resolved) {
|
|
168
|
-
// Update route state
|
|
169
|
-
currentRoute = {
|
|
170
|
-
path,
|
|
171
|
-
params: resolved.params,
|
|
172
|
-
query,
|
|
173
|
-
matched: resolved.record as unknown as RouteRecord
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Load and render page
|
|
177
|
-
const pageModule = resolved.record.module ||
|
|
178
|
-
(resolved.record.load ? resolved.record.load() : null)
|
|
179
|
-
|
|
180
|
-
if (pageModule) {
|
|
181
|
-
await renderPage(pageModule)
|
|
182
|
-
}
|
|
183
|
-
} else {
|
|
184
|
-
// No route matched - could render 404
|
|
185
|
-
currentRoute = {
|
|
186
|
-
path,
|
|
187
|
-
params: {},
|
|
188
|
-
query,
|
|
189
|
-
matched: undefined
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
console.warn(`[Zenith Router] No route matched for path: ${path}`)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Update browser history
|
|
196
|
-
if (updateHistory) {
|
|
197
|
-
const url = path + (Object.keys(query).length > 0
|
|
198
|
-
? "?" + new URLSearchParams(query).toString()
|
|
199
|
-
: "")
|
|
200
|
-
|
|
201
|
-
if (replace) {
|
|
202
|
-
window.history.replaceState(null, "", url)
|
|
203
|
-
} else {
|
|
204
|
-
window.history.pushState(null, "", url)
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Notify listeners
|
|
209
|
-
notifyListeners(currentRoute, prevRoute)
|
|
210
|
-
|
|
211
|
-
// Expose route to window for component access
|
|
212
|
-
;(window as any).__zenith_route = currentRoute
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Render a page module to the router outlet
|
|
217
|
-
*/
|
|
218
|
-
async function renderPage(pageModule: PageModule): Promise<void> {
|
|
219
|
-
if (!routerOutlet) {
|
|
220
|
-
console.warn("[Zenith Router] No router outlet configured")
|
|
221
|
-
return
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Clear previous page scripts from window
|
|
225
|
-
cleanupPreviousPage()
|
|
226
|
-
|
|
227
|
-
currentPageModule = pageModule
|
|
228
|
-
|
|
229
|
-
// Render HTML to outlet
|
|
230
|
-
routerOutlet.innerHTML = pageModule.html
|
|
231
|
-
|
|
232
|
-
// Inject styles
|
|
233
|
-
injectStyles(pageModule.styles)
|
|
234
|
-
|
|
235
|
-
// Execute scripts
|
|
236
|
-
executeScripts(pageModule.scripts)
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Clean up previous page (remove event listeners, etc.)
|
|
241
|
-
*/
|
|
242
|
-
function cleanupPreviousPage(): void {
|
|
243
|
-
// Remove previous page styles
|
|
244
|
-
const prevStyles = document.querySelectorAll("style[data-zen-page-style]")
|
|
245
|
-
prevStyles.forEach(style => style.remove())
|
|
246
|
-
|
|
247
|
-
// Note: Script cleanup is handled by the state management system
|
|
248
|
-
// State variables and event handlers will be overwritten by new page
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Inject page styles into document head
|
|
253
|
-
*/
|
|
254
|
-
function injectStyles(styles: string[]): void {
|
|
255
|
-
styles.forEach((styleContent, index) => {
|
|
256
|
-
const styleEl = document.createElement("style")
|
|
257
|
-
styleEl.setAttribute("data-zen-page-style", String(index))
|
|
258
|
-
styleEl.textContent = styleContent
|
|
259
|
-
document.head.appendChild(styleEl)
|
|
260
|
-
})
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Execute page scripts
|
|
265
|
-
*/
|
|
266
|
-
function executeScripts(scripts: string[]): void {
|
|
267
|
-
scripts.forEach(scriptContent => {
|
|
268
|
-
try {
|
|
269
|
-
// Create a function and execute it
|
|
270
|
-
const scriptFn = new Function(scriptContent)
|
|
271
|
-
scriptFn()
|
|
272
|
-
} catch (error) {
|
|
273
|
-
console.error("[Zenith Router] Error executing page script:", error)
|
|
274
|
-
}
|
|
275
|
-
})
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Notify route change listeners
|
|
280
|
-
*/
|
|
281
|
-
function notifyListeners(route: RouteState, prevRoute: RouteState): void {
|
|
282
|
-
routeListeners.forEach(listener => {
|
|
283
|
-
try {
|
|
284
|
-
listener(route, prevRoute)
|
|
285
|
-
} catch (error) {
|
|
286
|
-
console.error("[Zenith Router] Error in route listener:", error)
|
|
287
|
-
}
|
|
288
|
-
})
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Navigate to a new URL (SPA navigation)
|
|
293
|
-
*
|
|
294
|
-
* This is the main API for programmatic navigation.
|
|
295
|
-
* ZenLink will use this internally.
|
|
296
|
-
*
|
|
297
|
-
* @param to - The target URL path
|
|
298
|
-
* @param options - Navigation options
|
|
299
|
-
*/
|
|
300
|
-
export async function navigate(
|
|
301
|
-
to: string,
|
|
302
|
-
options: NavigateOptions = {}
|
|
303
|
-
): Promise<void> {
|
|
304
|
-
// Parse the URL
|
|
305
|
-
let path: string
|
|
306
|
-
let query: Record<string, string> = {}
|
|
307
|
-
|
|
308
|
-
if (to.includes("?")) {
|
|
309
|
-
const [pathname, search] = to.split("?")
|
|
310
|
-
path = pathname || "/"
|
|
311
|
-
query = parseQueryString("?" + (search || ""))
|
|
312
|
-
} else {
|
|
313
|
-
path = to
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Normalize path
|
|
317
|
-
if (!path.startsWith("/")) {
|
|
318
|
-
// Relative path - resolve against current path
|
|
319
|
-
const currentDir = currentRoute.path.split("/").slice(0, -1).join("/")
|
|
320
|
-
path = currentDir + "/" + path
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Normalize path for comparison (ensure trailing slash consistency)
|
|
324
|
-
const normalizedPath = path === "" ? "/" : path
|
|
325
|
-
const currentPath = currentRoute.path === "" ? "/" : currentRoute.path
|
|
326
|
-
|
|
327
|
-
// Check if we're already on this path
|
|
328
|
-
const isSamePath = normalizedPath === currentPath
|
|
329
|
-
|
|
330
|
-
// If same path and same query, don't navigate (idempotent)
|
|
331
|
-
if (isSamePath && JSON.stringify(query) === JSON.stringify(currentRoute.query)) {
|
|
332
|
-
return
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// Resolve and render with replace option if specified
|
|
336
|
-
await resolveAndRender(path, query, true, options.replace || false)
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Get current route state
|
|
341
|
-
*/
|
|
342
|
-
export function getRoute(): RouteState {
|
|
343
|
-
return { ...currentRoute }
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Subscribe to route changes
|
|
348
|
-
*/
|
|
349
|
-
export function onRouteChange(listener: RouteListener): () => void {
|
|
350
|
-
routeListeners.add(listener)
|
|
351
|
-
|
|
352
|
-
// Return unsubscribe function
|
|
353
|
-
return () => {
|
|
354
|
-
routeListeners.delete(listener)
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* FUTURE EXTENSION POINTS
|
|
360
|
-
*
|
|
361
|
-
* These are placeholders for features ZenLink and other extensions will use.
|
|
362
|
-
* They are not implemented yet but define the API surface.
|
|
363
|
-
*/
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Navigation guards (future extension)
|
|
367
|
-
*/
|
|
368
|
-
type NavigationGuard = (
|
|
369
|
-
to: RouteState,
|
|
370
|
-
from: RouteState
|
|
371
|
-
) => boolean | string | Promise<boolean | string>
|
|
372
|
-
|
|
373
|
-
const beforeGuards: NavigationGuard[] = []
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Register a navigation guard (future extension)
|
|
377
|
-
*/
|
|
378
|
-
export function beforeEach(guard: NavigationGuard): () => void {
|
|
379
|
-
beforeGuards.push(guard)
|
|
380
|
-
return () => {
|
|
381
|
-
const index = beforeGuards.indexOf(guard)
|
|
382
|
-
if (index > -1) beforeGuards.splice(index, 1)
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* After navigation hooks (future extension)
|
|
388
|
-
*/
|
|
389
|
-
type AfterHook = (to: RouteState, from: RouteState) => void | Promise<void>
|
|
390
|
-
|
|
391
|
-
const afterHooks: AfterHook[] = []
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Register an after-navigation hook (future extension)
|
|
395
|
-
*/
|
|
396
|
-
export function afterEach(hook: AfterHook): () => void {
|
|
397
|
-
afterHooks.push(hook)
|
|
398
|
-
return () => {
|
|
399
|
-
const index = afterHooks.indexOf(hook)
|
|
400
|
-
if (index > -1) afterHooks.splice(index, 1)
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Check if a path is active (for ZenLink active state)
|
|
406
|
-
*/
|
|
407
|
-
export function isActive(path: string, exact: boolean = false): boolean {
|
|
408
|
-
if (exact) {
|
|
409
|
-
return currentRoute.path === path
|
|
410
|
-
}
|
|
411
|
-
return currentRoute.path.startsWith(path)
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* Prefetch a route for faster navigation
|
|
416
|
-
*
|
|
417
|
-
* This preloads the page module into the route manifest cache,
|
|
418
|
-
* so when the user navigates to it, there's no loading delay.
|
|
419
|
-
*/
|
|
420
|
-
const prefetchedRoutes = new Set<string>()
|
|
421
|
-
|
|
422
|
-
export async function prefetch(path: string): Promise<void> {
|
|
423
|
-
// Normalize path
|
|
424
|
-
const normalizedPath = path === "" ? "/" : path
|
|
425
|
-
|
|
426
|
-
// Don't prefetch if already done
|
|
427
|
-
if (prefetchedRoutes.has(normalizedPath)) {
|
|
428
|
-
return
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// Find matching route
|
|
432
|
-
const resolved = resolveRoute(normalizedPath)
|
|
433
|
-
|
|
434
|
-
if (!resolved) {
|
|
435
|
-
console.warn(`[Zenith Router] Cannot prefetch: no route matches ${path}`)
|
|
436
|
-
return
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
// Mark as prefetched
|
|
440
|
-
prefetchedRoutes.add(normalizedPath)
|
|
441
|
-
|
|
442
|
-
// If route has a load function, call it to preload the module
|
|
443
|
-
if (resolved.record.load && !resolved.record.module) {
|
|
444
|
-
try {
|
|
445
|
-
resolved.record.module = resolved.record.load()
|
|
446
|
-
} catch (error) {
|
|
447
|
-
console.warn(`[Zenith Router] Error prefetching ${path}:`, error)
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
/**
|
|
453
|
-
* Check if a route has been prefetched
|
|
454
|
-
*/
|
|
455
|
-
export function isPrefetched(path: string): boolean {
|
|
456
|
-
return prefetchedRoutes.has(path === "" ? "/" : path)
|
|
457
|
-
}
|
|
458
|
-
|
package/router/types.ts
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Zenith Router Types
|
|
3
|
-
*
|
|
4
|
-
* File-based routing system types for build-time manifest generation
|
|
5
|
-
* and runtime route resolution.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* A compiled route record used for runtime matching
|
|
10
|
-
*/
|
|
11
|
-
export interface RouteRecord {
|
|
12
|
-
/** The route pattern (e.g., /blog/:id, /posts/*slug) */
|
|
13
|
-
path: string
|
|
14
|
-
/** Compiled regex for matching URLs */
|
|
15
|
-
regex: RegExp
|
|
16
|
-
/** Parameter names extracted from the route pattern */
|
|
17
|
-
paramNames: string[]
|
|
18
|
-
/** Dynamic import function for the page module */
|
|
19
|
-
load: () => Promise<PageModule>
|
|
20
|
-
/** Route priority score for deterministic matching */
|
|
21
|
-
score: number
|
|
22
|
-
/** Original file path (for debugging) */
|
|
23
|
-
filePath: string
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* A compiled page module containing the page's compiled code
|
|
28
|
-
*/
|
|
29
|
-
export interface PageModule {
|
|
30
|
-
/** The compiled HTML template */
|
|
31
|
-
html: string
|
|
32
|
-
/** Array of compiled script contents */
|
|
33
|
-
scripts: string[]
|
|
34
|
-
/** Array of compiled style contents */
|
|
35
|
-
styles: string[]
|
|
36
|
-
/** Page metadata (title, etc.) */
|
|
37
|
-
meta?: PageMeta
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Page metadata for head management
|
|
42
|
-
*/
|
|
43
|
-
export interface PageMeta {
|
|
44
|
-
title?: string
|
|
45
|
-
description?: string
|
|
46
|
-
[key: string]: string | undefined
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* The reactive route state exposed to components
|
|
51
|
-
*/
|
|
52
|
-
export interface RouteState {
|
|
53
|
-
/** Current pathname (e.g., /blog/123) */
|
|
54
|
-
path: string
|
|
55
|
-
/** Extracted route parameters (e.g., { id: '123' }) */
|
|
56
|
-
params: Record<string, string>
|
|
57
|
-
/** Parsed query string parameters */
|
|
58
|
-
query: Record<string, string>
|
|
59
|
-
/** The matched route record (if any) */
|
|
60
|
-
matched?: RouteRecord
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Navigation options for programmatic navigation
|
|
65
|
-
*/
|
|
66
|
-
export interface NavigateOptions {
|
|
67
|
-
/** Replace current history entry instead of pushing */
|
|
68
|
-
replace?: boolean
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Route segment types for scoring
|
|
73
|
-
*/
|
|
74
|
-
export enum SegmentType {
|
|
75
|
-
/** Static segment (e.g., "blog") - highest priority */
|
|
76
|
-
STATIC = 'static',
|
|
77
|
-
/** Dynamic parameter (e.g., "[id]") - medium priority */
|
|
78
|
-
DYNAMIC = 'dynamic',
|
|
79
|
-
/** Required catch-all (e.g., "[...slug]") - low priority */
|
|
80
|
-
CATCH_ALL = 'catch_all',
|
|
81
|
-
/** Optional catch-all (e.g., "[[...slug]]") - lowest priority */
|
|
82
|
-
OPTIONAL_CATCH_ALL = 'optional_catch_all'
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Parsed segment information
|
|
87
|
-
*/
|
|
88
|
-
export interface ParsedSegment {
|
|
89
|
-
/** The segment type */
|
|
90
|
-
type: SegmentType
|
|
91
|
-
/** The parameter name (for dynamic/catch-all segments) */
|
|
92
|
-
paramName?: string
|
|
93
|
-
/** The raw segment string */
|
|
94
|
-
raw: string
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Build-time route definition before regex compilation
|
|
99
|
-
*/
|
|
100
|
-
export interface RouteDefinition {
|
|
101
|
-
/** The route pattern */
|
|
102
|
-
path: string
|
|
103
|
-
/** Parsed segments */
|
|
104
|
-
segments: ParsedSegment[]
|
|
105
|
-
/** Parameter names */
|
|
106
|
-
paramNames: string[]
|
|
107
|
-
/** Route score */
|
|
108
|
-
score: number
|
|
109
|
-
/** Source file path */
|
|
110
|
-
filePath: string
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Route manifest generated at build time
|
|
115
|
-
*/
|
|
116
|
-
export interface RouteManifest {
|
|
117
|
-
/** Array of route records, sorted by score (highest first) */
|
|
118
|
-
routes: RouteRecord[]
|
|
119
|
-
/** Timestamp of manifest generation */
|
|
120
|
-
generatedAt: number
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Router instance interface (for future ZenLink extension)
|
|
125
|
-
*/
|
|
126
|
-
export interface Router {
|
|
127
|
-
/** Current reactive route state */
|
|
128
|
-
readonly route: RouteState
|
|
129
|
-
|
|
130
|
-
/** Navigate to a new URL */
|
|
131
|
-
navigate(to: string, options?: NavigateOptions): Promise<void>
|
|
132
|
-
|
|
133
|
-
/** Resolve a route without navigating */
|
|
134
|
-
resolve(path: string): { record: RouteRecord; params: Record<string, string> } | null
|
|
135
|
-
|
|
136
|
-
/** Add a navigation guard (future extension point) */
|
|
137
|
-
beforeEach?(guard: NavigationGuard): () => void
|
|
138
|
-
|
|
139
|
-
/** Add an after-navigation hook (future extension point) */
|
|
140
|
-
afterEach?(hook: NavigationHook): () => void
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Navigation guard for route protection (future extension)
|
|
145
|
-
*/
|
|
146
|
-
export type NavigationGuard = (
|
|
147
|
-
to: RouteState,
|
|
148
|
-
from: RouteState
|
|
149
|
-
) => boolean | string | Promise<boolean | string>
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Navigation hook for post-navigation actions (future extension)
|
|
153
|
-
*/
|
|
154
|
-
export type NavigationHook = (
|
|
155
|
-
to: RouteState,
|
|
156
|
-
from: RouteState
|
|
157
|
-
) => void | Promise<void>
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Router view mount options
|
|
161
|
-
*/
|
|
162
|
-
export interface RouterViewOptions {
|
|
163
|
-
/** Container element or selector */
|
|
164
|
-
container: HTMLElement | string
|
|
165
|
-
/** Whether to preserve layout between routes */
|
|
166
|
-
preserveLayout?: boolean
|
|
167
|
-
}
|
|
168
|
-
|