@ilha/router 0.10.4 → 0.11.1

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.
@@ -0,0 +1,5 @@
1
+ //#region src/als-key.ts
2
+ const REQUEST_ALS_KEY = Symbol.for("ilha.requestAls");
3
+
4
+ //#endregion
5
+ export { REQUEST_ALS_KEY as t };
@@ -0,0 +1 @@
1
+ export declare const REQUEST_ALS_KEY: unique symbol;
package/dist/codegen.d.ts CHANGED
@@ -22,8 +22,6 @@ export interface GeneratedPaths {
22
22
  serverFile: string;
23
23
  /** Client module: ?client imports, browser-optimised. `ilha:pages/client` */
24
24
  clientFile: string;
25
- /** Server-only loaders side-effect module. `ilha:loaders` */
26
- loadersFile: string;
27
25
  }
28
26
  export declare function resolveGeneratedPaths(outDir: string): GeneratedPaths;
29
27
  export declare function generate(pagesDir: string, outDir: string, options?: GenerateOptions): Promise<void>;
@@ -27,7 +27,10 @@ let _headAlsInit = null;
27
27
  /** ESM dynamic import — Nitro/Vite SSR workers have no `require`. */
28
28
  async function getHeadAlsAsync() {
29
29
  if (_headAls) return _headAls;
30
- if (!_headAlsInit) _headAlsInit = import("node:async_hooks").then(({ AsyncLocalStorage }) => {
30
+ if (!_headAlsInit) _headAlsInit = import(
31
+ /* @vite-ignore */
32
+ "node:async_hooks"
33
+ ).then(({ AsyncLocalStorage }) => {
31
34
  _headAls = new AsyncLocalStorage();
32
35
  return _headAls;
33
36
  });
@@ -161,10 +164,6 @@ const HEAD_ESC = {
161
164
  function escapeHeadAttr(value) {
162
165
  return String(value).replace(/[&<>"']/g, (c) => HEAD_ESC[c]);
163
166
  }
164
- /** Escape text content for inline HTML (loader error messages etc.). */
165
- function escapeHtml(value) {
166
- return String(value).replace(/[&<>]/g, (c) => HEAD_ESC[c]);
167
- }
168
167
  function serializeAttrs(attrs) {
169
168
  const parts = [];
170
169
  for (const [k, v] of Object.entries(attrs)) {
@@ -283,55 +282,4 @@ function isSafeRefreshTarget(content) {
283
282
  }
284
283
 
285
284
  //#endregion
286
- //#region src/snapshot.ts
287
- /**
288
- * Defensive parser for `data-ilha-state` / `data-ilha-props` snapshot
289
- * attributes on the router side. Mirrors the core ilha guards: size cap,
290
- * plain-object check, depth cap, and prototype-key stripping. Returns
291
- * undefined (degrade gracefully) on any failure.
292
- */
293
- const MAX_SNAPSHOT_CHARS = 262144;
294
- const MAX_SNAPSHOT_DEPTH = 32;
295
- const UNSAFE_SNAPSHOT_KEYS = /* @__PURE__ */ new Set([
296
- "__proto__",
297
- "constructor",
298
- "prototype"
299
- ]);
300
- function stripUnsafeKeys(value) {
301
- if (value === null || typeof value !== "object") return;
302
- if (Array.isArray(value)) {
303
- for (const item of value) stripUnsafeKeys(item);
304
- return;
305
- }
306
- for (const key of Object.getOwnPropertyNames(value)) if (UNSAFE_SNAPSHOT_KEYS.has(key)) delete value[key];
307
- else stripUnsafeKeys(value[key]);
308
- }
309
- function exceedsDepth(value, depth) {
310
- if (depth > MAX_SNAPSHOT_DEPTH) return true;
311
- if (value === null || typeof value !== "object") return false;
312
- if (Array.isArray(value)) {
313
- for (const item of value) if (exceedsDepth(item, depth + 1)) return true;
314
- return false;
315
- }
316
- for (const key in value) {
317
- if (!Object.hasOwn(value, key)) continue;
318
- if (exceedsDepth(value[key], depth + 1)) return true;
319
- }
320
- return false;
321
- }
322
- function parseSnapshotAttr(raw) {
323
- if (raw.length > MAX_SNAPSHOT_CHARS) return void 0;
324
- let parsed;
325
- try {
326
- parsed = JSON.parse(raw);
327
- } catch {
328
- return;
329
- }
330
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return void 0;
331
- if (exceedsDepth(parsed, 1)) return void 0;
332
- stripUnsafeKeys(parsed);
333
- return parsed;
334
- }
335
-
336
- //#endregion
337
- export { escapeHtml as a, withHeadStore as c, escapeHeadAttr as i, applyHeadEntriesToDocument as n, head as o, cssEscapeAttr as r, serializeHead as s, parseSnapshotAttr as t };
285
+ export { withHeadStore as i, head as n, serializeHead as r, applyHeadEntriesToDocument as t };
package/dist/index.d.ts CHANGED
@@ -1,15 +1,16 @@
1
- import type { Island, HydratableOptions } from "ilha";
2
- import { head, type HeadInput, type SerializedHead } from "./head";
1
+ import type { Component, View } from "ilha";
2
+ import { type SerializedHead } from "./head";
3
3
  import { type HttpResponseOptions } from "./http";
4
4
  export { setHistoryMode, getHistoryMode } from "./hash";
5
5
  export type { HistoryMode } from "./hash";
6
+ export { head, serializeHead } from "./head";
7
+ export type { HeadInput, SerializedHead } from "./head";
8
+ export { httpResponse, EMPTY_HEAD } from "./http";
9
+ export type { HttpResponseOptions } from "./http";
10
+ export type Page = Component;
6
11
  export interface RouteRecord {
7
12
  pattern: string;
8
- island: Island<any>;
9
- /** Merged loader chain (layouts outer→inner, then page) — `undefined` if no loaders. */
10
- loader?: Loader<any>;
11
- /** True when the route has a server-side loader, even if the client only has a marker. */
12
- hasLoader?: boolean;
13
+ page: Page;
13
14
  }
14
15
  export interface RouteSnapshot {
15
16
  path: string;
@@ -22,154 +23,46 @@ export interface AppError {
22
23
  status?: number;
23
24
  stack?: string;
24
25
  }
25
- export type LayoutHandler = (children: Island<any>) => Island<any>;
26
- export type ErrorHandler = (error: AppError, route: RouteSnapshot) => Island<any>;
27
- export interface LoaderContext {
28
- params: Record<string, string>;
29
- request: Request;
30
- url: URL;
31
- signal: AbortSignal;
32
- /** Contribute `<head>` data for this route. Safe to call multiple times. */
33
- head: (input: HeadInput) => void;
34
- }
35
- export type Loader<T> = (ctx: LoaderContext) => Promise<T> | T;
36
- /**
37
- * Identity function for declaring a loader. Exists purely as a type anchor and
38
- * a marker for the Vite plugin to detect by export name.
39
- *
40
- * Loaders must read `ctx.params`/`ctx.url` rather than `useRoute()` — the
41
- * route store still holds the previous route while a navigation's loader is
42
- * in flight, so `useRoute().params()` inside a loader reads stale params.
43
- */
44
- export declare function loader<T>(fn: Loader<T>): Loader<T>;
45
- export declare namespace loader {
46
- var client: <T>(fn: Loader<T>) => Loader<T>;
26
+ export interface IslandContext {
27
+ request?: Request;
47
28
  }
48
- /** Infer the page props produced by a loader. */
49
- export type InferLoader<L> = L extends Loader<infer T> ? {
50
- load: {
51
- loading: boolean;
52
- value: Awaited<T>;
53
- error: Error | undefined;
54
- };
55
- } : never;
56
- type InferLoaderValue<L> = L extends Loader<infer T> ? Awaited<T> : never;
57
- /**
58
- * Merge multiple loader return types into a single object type.
59
- * Later loaders override earlier ones on key collision — matching runtime merge.
60
- *
61
- * @example
62
- * type PageInput = MergeLoaders<[typeof rootLayoutLoad, typeof sectionLayoutLoad, typeof pageLoad]>;
63
- */
64
- export type MergeLoaders<Ls extends readonly Loader<any>[]> = Ls extends readonly [
65
- infer First extends Loader<any>,
66
- ...infer Rest extends readonly Loader<any>[]
67
- ] ? Rest extends readonly [] ? InferLoaderValue<First> : Omit<InferLoaderValue<First>, keyof MergeLoaders<Rest>> & MergeLoaders<Rest> : {};
29
+ export type LayoutHandler = (props: {
30
+ children?: View;
31
+ } & Record<string, unknown>) => unknown;
32
+ export type ErrorHandler = (error: AppError, route: RouteSnapshot) => Page | View;
68
33
  export declare class Redirect {
69
34
  readonly __ilhaRedirect: true;
70
35
  readonly to: string;
71
36
  readonly status: number;
72
37
  constructor(to: string, status?: number);
73
38
  }
74
- export declare class LoaderError {
75
- readonly __ilhaLoaderError: true;
39
+ export declare class RouteError {
40
+ readonly __ilhaRouteError: true;
76
41
  readonly status: number;
77
42
  readonly message: string;
78
43
  constructor(status: number, message: string);
79
44
  }
80
45
  export declare function redirect(to: string, status?: number): never;
81
46
  export declare function error(status: number, message: string): never;
82
- /**
83
- * Compose a list of loaders into a single loader. Later loaders win on key
84
- * collision (page loader overrides layout loader for the same key). All loaders
85
- * run concurrently within a chain since they share the same abort signal and
86
- * request — re-fetching is cheap with a request-scoped cache (future work).
87
- *
88
- * For v1 we run them in parallel via `Promise.all`. If a loader throws a
89
- * `Redirect` or `LoaderError`, the composed loader re-throws it unchanged.
90
- */
91
- export declare function composeLoaders<Ls extends readonly Loader<any>[]>(loaders: Ls): Loader<MergeLoaders<Ls>>;
92
- export declare function wrapLayout(layout: LayoutHandler, page: Island<any>): Island<any>;
93
- export declare function wrapError(handler: ErrorHandler, page: Island<any>): Island<any>;
47
+ export declare function wrapLayout(layout: LayoutHandler, page: Page): Page;
48
+ export declare function wrapError(handler: ErrorHandler, page: Page): Page;
94
49
  export declare function defineLayout(layout: LayoutHandler): LayoutHandler;
95
50
  export interface NavigateOptions {
96
51
  replace?: boolean;
97
- /**
98
- * When `false`, keep the current scroll position instead of scrolling to the
99
- * top (or to the URL hash target) after navigation. Default: `true`.
100
- */
101
52
  scroll?: boolean;
102
53
  }
103
54
  export type RouterMode = "spa" | "static";
104
55
  export interface RouterOptions {
105
- /**
106
- * Client navigation mode.
107
- * - `spa` — full route graph, SSR/hydration, client-side navigation.
108
- * - `static` — no route graph bundled; hydrate islands on the current
109
- * pre-rendered page only.
110
- * Default: `spa`.
111
- */
112
56
  mode?: RouterMode;
113
- /**
114
- * When `true` (default), internal `<a>` clicks are intercepted and handled
115
- * by the client router. Set to `false` for MPA-style behavior where links
116
- * perform full document navigations.
117
- * Only meaningful in `spa` mode; ignored in `static` mode.
118
- * Default: `true`.
119
- */
120
57
  interceptLinks?: boolean;
121
- /**
122
- * Island rendered when no route matches the current URL — both on the
123
- * server (with a 404 status) and in the client `RouterView`.
124
- */
125
- notFound?: Island<any>;
126
- /**
127
- * Allow loader `redirect()` targets pointing at other origins. When `false`
128
- * (default), absolute cross-origin redirect targets are rejected with a 500
129
- * — redirect targets frequently carry user input (`?next=` params), and
130
- * rejecting external targets by default prevents open redirects.
131
- */
132
58
  allowExternalRedirects?: boolean;
133
- /**
134
- * Abort a route loader after this many milliseconds during SSR / loader
135
- * endpoint execution. `0`/`undefined` disables the timeout. The loader's
136
- * `ctx.signal` also aborts when the incoming `Request`'s signal aborts.
137
- */
138
- loaderTimeout?: number;
139
- /**
140
- * Wrap client-side view swaps in `document.startViewTransition()` when the
141
- * browser supports it (falls back to an instant swap otherwise).
142
- * Default: `false`.
143
- */
144
59
  viewTransitions?: boolean;
145
- }
146
- export interface HydratableRenderOptions extends Partial<Omit<HydratableOptions, "name">> {
147
- /**
148
- * Base `<head>` data merged before loader and render-time contributions, so
149
- * route-level head overrides it. Used by host entries rendering routes server-side
150
- * to supply app-wide title/meta/scripts.
151
- */
152
- baseHead?: HeadInput;
153
- }
154
- export interface HydrateOptions {
155
- root?: Element;
156
- target?: string | Element;
157
- /**
158
- * When `true` (default), internal `<a>` clicks are intercepted for
159
- * client-side navigation. Set to `false` for MPA-style full-page navigations.
160
- */
161
- interceptLinks?: boolean;
60
+ notFound?: Page;
162
61
  }
163
62
  export interface MountOptions {
164
63
  hydrate?: boolean;
165
- registry?: Record<string, Island<any>>;
166
- /**
167
- * When `true` (default), internal `<a>` clicks are intercepted for
168
- * client-side navigation. Set to `false` for MPA-style full-page navigations.
169
- */
170
64
  interceptLinks?: boolean;
171
65
  }
172
- /** Response envelope returned by `renderResponse` — lets the host app handle redirects. */
173
66
  export type RenderResponse = {
174
67
  kind: "html";
175
68
  html: string;
@@ -186,147 +79,49 @@ export type RenderResponse = {
186
79
  html: string;
187
80
  head?: SerializedHead;
188
81
  };
82
+ export interface RespondOptions extends HttpResponseOptions {
83
+ timeout?: number;
84
+ snapshot?: boolean;
85
+ markers?: boolean;
86
+ shell?: (head: SerializedHead, html: string) => string;
87
+ }
189
88
  export interface RouterBuilder {
190
- /**
191
- * Register a route. The optional `loader` is the merged loader chain
192
- * (layout loaders outer→inner followed by the page loader) produced by
193
- * the FS-routing codegen.
194
- */
195
- route(pattern: string, island: Island<any>, loader?: Loader<any>): RouterBuilder;
196
- /**
197
- * Attach (or replace) a loader on an already-registered route pattern.
198
- * Used by the `ilha:loaders` virtual module to wire server-only loaders
199
- * onto the client-safe `pageRouter` at SSR time. No-op if the pattern
200
- * was never registered via `.route()`.
201
- */
202
- attachLoader(pattern: string, loader: Loader<any>): RouterBuilder;
203
- /**
204
- * Attach a loader that runs **in the browser** on client navigations,
205
- * instead of fetching from the loader endpoint. Used by the FS-routing
206
- * codegen for `loader.client` exports; also available for manual routers.
207
- * When a route has both, the client loader wins on client navigations and
208
- * the server loader runs during SSR. No-op if the pattern was never
209
- * registered via `.route()`.
210
- */
211
- clientLoader(pattern: string, loader: Loader<any>): RouterBuilder;
212
- /**
213
- * Attach the route's nearest `+error` boundary so **loader** errors render
214
- * through it (render errors are already handled by `wrapError` inside the
215
- * island). Used by the FS-routing codegen; also available for manual
216
- * routers. No-op if the pattern was never registered via `.route()`.
217
- */
89
+ route(pattern: string, page: Page): RouterBuilder;
218
90
  errorBoundary(pattern: string, handler: ErrorHandler): RouterBuilder;
219
- /**
220
- * Mark an already-registered route as having a server-side loader without
221
- * importing that loader into the client bundle. Used by FS-routing codegen
222
- * so SPA navigation knows to call the loader endpoint.
223
- */
224
- markLoader(pattern: string): RouterBuilder;
225
- /**
226
- * Return a snapshot of every registered route in match order. Useful for
227
- * prerenderers that need to discover the filesystem routes exposed by
228
- * `pageRouter` without reaching into router internals.
229
- */
230
91
  routes(): RouteRecord[];
231
92
  prime(): void;
232
93
  mount(target: string | Element, options?: MountOptions): () => void;
233
- render(url: string | URL): string;
234
- renderHydratable(urlOrRequest: string | URL | Request, registry: Record<string, Island<any>>, options?: HydratableRenderOptions, request?: Request): Promise<string>;
235
- /**
236
- * Like `renderHydratable` but surfaces loader redirects and errors as
237
- * structured responses instead of baking them into HTML. Prefer this from
238
- * host server code so you can emit proper 302 / 4xx responses.
239
- */
240
- renderResponse(urlOrRequest: string | URL | Request, registry: Record<string, Island<any>>, options?: HydratableRenderOptions, request?: Request): Promise<RenderResponse>;
241
- /**
242
- * Run the loader chain for a given URL without rendering. Backs the
243
- * `/__ilha/loader` endpoint that the host server handler
244
- * serves as JSON for client-side navigation. Returns the raw loader result, a
245
- * redirect sentinel, or an error sentinel.
246
- */
247
- runLoader(urlOrRequest: string | URL | Request, request?: Request): Promise<{
248
- kind: "data";
249
- data: Record<string, unknown>;
250
- head?: SerializedHead;
251
- } | {
252
- kind: "redirect";
253
- to: string;
254
- status: number;
255
- } | {
256
- kind: "error";
257
- status: number;
258
- message: string;
259
- } | {
260
- kind: "not-found";
261
- }>;
262
- /**
263
- * Render a route to a ready-to-send HTTP `Response`, handling redirects,
264
- * loader errors, and security headers. `request` (or a URL string) selects
265
- * the route; the optional `shell` injects `head` tags into a document.
266
- */
267
- respond(urlOrRequest: string | URL | Request, registry: Record<string, Island<any>>, options?: RespondOptions): Promise<Response>;
268
- /**
269
- * Hydrate the application - combines prime(), mount(), and router.mount() into one call.
270
- * @param registry - The island registry from ilha:registry
271
- * @param options - Optional root element (defaults to document.body) and router target (defaults to root)
272
- * @returns Cleanup function
273
- */
274
- hydrate(registry: Record<string, Island<any>>, options?: HydrateOptions): () => void;
275
- /**
276
- * Hydrate islands on the current pre-rendered page without mounting a route
277
- * view or enabling client navigation. Intended for `static` mode: each page
278
- * is a self-contained HTML file; only interactive islands need activation.
279
- */
280
- hydrateStatic(registry: Record<string, Island<any>>, options?: {
94
+ render(url: string | URL | Request, options?: {
95
+ timeout?: number;
96
+ snapshot?: boolean;
97
+ markers?: boolean;
98
+ }): Promise<string>;
99
+ renderResponse(url: string | URL | Request, options?: {
100
+ timeout?: number;
101
+ snapshot?: boolean;
102
+ markers?: boolean;
103
+ }): Promise<RenderResponse>;
104
+ respond(url: string | URL | Request, options?: RespondOptions): Promise<Response>;
105
+ hydrate(options?: {
281
106
  root?: Element;
107
+ interceptLinks?: boolean;
282
108
  }): () => void;
283
109
  }
284
- /** Path of the loader endpoint served by the Vite plugin / production adapter. */
285
- export declare const LOADER_ENDPOINT = "/__ilha/loader";
286
- /**
287
- * Prefetch loader data for a given path. Safe to call repeatedly — a single
288
- * inflight request is reused until it either resolves (and is consumed by
289
- * navigation) or is superseded by another prefetch.
290
- */
291
- export declare function prefetch(pathWithSearch: string): void;
292
- /** @internal Internal reactive route-state accessor; use the aggregate `useRoute()` for the public surface. */
293
- export declare function routePath(value?: string): string;
294
- /** @internal Internal reactive route-state accessor; use the aggregate `useRoute()` for the public surface. */
295
- export declare function routeParams(value?: Record<string, string>): Record<string, string>;
296
- /** @internal Internal reactive route-state accessor; use the aggregate `useRoute()` for the public surface. */
297
- export declare function routeSearch(value?: string): string;
298
- /** @internal Internal reactive route-state accessor; use the aggregate `useRoute()` for the public surface. */
299
- export declare function routeHash(value?: string): string;
300
- /** Reactive: `true` while a client navigation (loader fetch + view swap) is in flight. */
301
- export declare function navigating(): boolean;
302
- /**
303
- * Re-run the current route's loader and re-render the view with fresh data —
304
- * e.g. after a mutation. Resolves when the view has updated. No-op on the
305
- * server or when no router is mounted.
306
- */
307
- export declare function invalidate(): Promise<void>;
308
- /**
309
- * Context seeded for server-owned island renders. Extensible: future entries
310
- * (route params, app middleware values) land here without moving call sites.
311
- */
312
- export interface IslandContext {
313
- /**
314
- * The `Request` seeding this render, when one exists — page SSR passes the
315
- * incoming request; frame renders pass a synthesized POST with forwarded
316
- * identity headers (`cookie`, `authorization`, …). Absent in unscoped
317
- * renders (plain `toString()` in tests).
318
- */
319
- request?: Request;
110
+ export interface Navigation {
111
+ from: string;
112
+ to: string;
113
+ type: "push" | "replace" | "pop";
114
+ cancel?: () => void;
320
115
  }
321
- /**
322
- * The context seeding the current server-island render. Always returns an
323
- * object — check `.request` rather than the context itself. Never throws, so
324
- * render functions stay safe under plain `toString()` calls and in tests.
325
- *
326
- * Only meaningful inside `.server.tsx` render functions (page SSR or frame
327
- * rendering); client code never has a reason to call it.
328
- */
329
- export declare function useContext(): IslandContext;
116
+ type BeforeNavigateHook = (nav: Navigation & {
117
+ cancel: () => void;
118
+ }) => void;
119
+ type AfterNavigateHook = (nav: Navigation) => void;
120
+ export declare function routePath(): string;
121
+ export declare function routeParams(): Record<string, string>;
122
+ export declare function routeSearch(): string;
123
+ export declare function routeHash(): string;
124
+ export declare function navigating(): boolean;
330
125
  export declare function useRoute(): {
331
126
  path: typeof routePath;
332
127
  params: typeof routeParams;
@@ -334,73 +129,20 @@ export declare function useRoute(): {
334
129
  hash: typeof routeHash;
335
130
  navigating: typeof navigating;
336
131
  };
337
- /**
338
- * Prime route context signals from the current `location` so that islands
339
- * hydrated by `ilha.mount()` see the correct route values on their first
340
- * render — preventing a mismatch morph that would destroy hydrated bindings.
341
- */
132
+ export declare function useContext(): {
133
+ request: Request | undefined;
134
+ };
342
135
  export declare function prime(): void;
343
- export interface Navigation {
344
- /** Logical URL (path + search + hash) being navigated away from. */
345
- from: string;
346
- /** Logical URL being navigated to. */
347
- to: string;
348
- /** `"push"`/`"replace"` for programmatic navigations, `"pop"` for history traversal. */
349
- type: "push" | "replace" | "pop";
350
- }
351
- export type BeforeNavigateHook = (nav: Navigation & {
352
- cancel(): void;
353
- }) => void;
354
- export type AfterNavigateHook = (nav: Navigation) => void;
355
- /**
356
- * Run before a programmatic navigation commits. Call `nav.cancel()` to keep
357
- * the current URL (e.g. unsaved-changes guards). Not invoked for browser
358
- * back/forward — the URL has already changed by the time `popstate` fires.
359
- * Returns an unsubscribe function.
360
- */
361
136
  export declare function beforeNavigate(fn: BeforeNavigateHook): () => void;
362
- /** Run after a navigation (push, replace, or pop) has committed. Returns an unsubscribe function. */
363
137
  export declare function afterNavigate(fn: AfterNavigateHook): () => void;
364
138
  export declare function navigate(to: string, opts?: NavigateOptions): void;
365
139
  export interface LinkInterceptionOptions {
366
- /**
367
- * Prefetch loader data on `mouseenter` for eligible links. Links opt in via
368
- * the `data-prefetch` attribute (set `data-prefetch="false"` to opt out a
369
- * specific link even when the framework is configured to prefetch by default).
370
- * Default: `true` — prefetches on hover for any link with `data-prefetch`.
371
- */
372
- prefetch?: boolean;
373
140
  }
374
- export declare function enableLinkInterception(root?: Element | Document, options?: LinkInterceptionOptions): () => void;
375
- export declare const RouterView: Island<unknown>;
376
- export declare const RouterLink: Island<{
377
- href?: string;
378
- label?: string;
379
- }>;
141
+ export declare function enableLinkInterception(root?: Element | Document, _options?: LinkInterceptionOptions): () => void;
380
142
  export interface IsActiveOptions {
381
- /**
382
- * When `false`, `isActive("/docs")` also matches nested paths like
383
- * `/docs/getting-started` (prefix match on the current path).
384
- * Default: `true` (the matched route's pattern must equal `pattern`).
385
- */
386
- exact?: boolean;
143
+ end?: boolean;
387
144
  }
388
145
  export declare function isActive(pattern: string, options?: IsActiveOptions): boolean;
389
- export interface RespondOptions extends HydratableRenderOptions, HttpResponseOptions {
390
- /**
391
- * Wrap the rendered body with a document shell. Receives the serialized
392
- * head (title/meta/link/script tags + html/body attributes) and the inner
393
- * HTML; return the full document.
394
- */
395
- shell?: (head: SerializedHead, html: string) => string;
396
- }
397
- /**
398
- * Validate a loader redirect target. Relative paths always pass; same-origin
399
- * absolute URLs collapse to a path; cross-origin targets (including
400
- * protocol-relative `//host` URLs) are rejected unless `allowExternal` is set.
401
- * Non-http(s) schemes, backslash/control-char smuggling, and unparsable
402
- * targets are always rejected.
403
- */
404
146
  export declare function resolveRedirectTarget(to: string, base: URL, allowExternal: boolean): {
405
147
  ok: true;
406
148
  to: string;
@@ -408,27 +150,3 @@ export declare function resolveRedirectTarget(to: string, base: URL, allowExtern
408
150
  ok: false;
409
151
  };
410
152
  export declare function router(options?: RouterOptions): RouterBuilder;
411
- declare const _default: {
412
- router: typeof router;
413
- navigate: typeof navigate;
414
- useRoute: typeof useRoute;
415
- isActive: typeof isActive;
416
- enableLinkInterception: typeof enableLinkInterception;
417
- prime: typeof prime;
418
- prefetch: typeof prefetch;
419
- beforeNavigate: typeof beforeNavigate;
420
- afterNavigate: typeof afterNavigate;
421
- RouterView: Island<unknown>;
422
- RouterLink: Island<{
423
- href?: string;
424
- label?: string;
425
- }>;
426
- loader: typeof loader;
427
- redirect: typeof redirect;
428
- error: typeof error;
429
- composeLoaders: typeof composeLoaders;
430
- head: typeof head;
431
- };
432
- export default _default;
433
- export { head, serializeHead, type HeadInput, type SerializedHead } from "./head";
434
- export { httpResponse, type HttpResponseOptions } from "./http";