@ilha/router 0.2.6 → 0.3.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.
package/README.md CHANGED
@@ -75,6 +75,65 @@ pageRouter.hydrate(registry);
75
75
 
76
76
  ---
77
77
 
78
+ ## Hash mode
79
+
80
+ By default, the router uses the HTML5 History API and treats `location.pathname` as the route. This requires either a server that serves the SPA shell at every URL, or a static host with a SPA fallback. When neither is available — the document is loaded over `file://`, embedded in a desktop wrapper like Electron or Electrobun, opened directly from disk, or served from a host that can't be configured for SPA fallbacks — switch to **hash mode**, which stores the route in `location.hash`:
81
+
82
+ ```ts
83
+ import { setHistoryMode, router } from "@ilha/router";
84
+
85
+ setHistoryMode("hash"); // ← call once, before mounting
86
+
87
+ router().route("/", homePage).route("/about", aboutPage).route("/user/:id", userPage).mount("#app");
88
+ ```
89
+
90
+ `setHistoryMode("hash")` must be called **before** `.mount()`, `.hydrate()`, or `prime()`. Once set, every navigation API in this package — `navigate()`, `RouterLink`, `enableLinkInterception()`, popstate handling — operates against `location.hash` instead of `location.pathname`.
91
+
92
+ URLs in hash mode look like:
93
+
94
+ ```
95
+ file:///path/to/index.html#/
96
+ file:///path/to/index.html#/about
97
+ file:///path/to/index.html#/user/42?tab=overview
98
+ file:///path/to/index.html#/docs/intro#section
99
+ ```
100
+
101
+ The portion after the `#` is parsed as if it were a real URL — the path comes first, followed by an optional query string and an optional in-page anchor. `routeHash()` returns the in-hash anchor (`#section`), so in-page anchor links keep working alongside hash routing.
102
+
103
+ ### Links
104
+
105
+ Both forms work — pick whichever is easier in your code:
106
+
107
+ ```html
108
+ <a href="/about">About</a>
109
+ <!-- plain path — preferred for shared code -->
110
+ <a href="#/about">About</a>
111
+ <!-- explicit hash form — also intercepted -->
112
+ ```
113
+
114
+ `<RouterLink>` automatically renders the hash form (`<a href="#/about">`) in hash mode, so right-click → copy link gives a working URL.
115
+
116
+ In-page anchor links (`<a href="#section">`) are not intercepted — they behave as normal browser anchors. Only links beginning with `#/` (a slash after the hash) are treated as in-app navigations.
117
+
118
+ ### What's not supported in hash mode
119
+
120
+ **SSR + hydration.** The hash is never sent to the server, so it cannot pre-render the active route. Calling `mount({ hydrate: true })` or `.hydrate(registry)` while in hash mode logs a warning. Use plain SPA mode for hash-mode apps:
121
+
122
+ ```ts
123
+ setHistoryMode("hash");
124
+ pageRouter.mount("#app"); // ← no { hydrate: true }
125
+ ```
126
+
127
+ You can still register loaders, but they run on the client (via the loader endpoint or by calling `runLoader()` yourself) — there is no server-rendered initial state.
128
+
129
+ **Per-router mode.** History mode is process-global, not per-builder. This is intentional: `navigate()`, `RouterLink`, and `prefetch()` are module-level and would otherwise need explicit instance threading. If your app needs both modes simultaneously, that's not a use case this router supports.
130
+
131
+ ### Switching modes
132
+
133
+ `setHistoryMode()` can be called more than once, but listeners registered before a switch keep using their original adapter until the router is unmounted and remounted. In practice, set the mode once at app entry and leave it alone.
134
+
135
+ ---
136
+
78
137
  ## Core API
79
138
 
80
139
  ### `router()`
@@ -136,6 +195,8 @@ unmount();
136
195
 
137
196
  When `hydrate: true`, `.mount()` does **not** wipe existing SSR HTML. It instead mounts a hidden navigation handler that re-renders routes with hydration on subsequent navigations.
138
197
 
198
+ > Combining `hydrate: true` with hash mode logs a warning — hash routes are never visible to the server, so SSR can't pre-render them. Use plain SPA mode (no `hydrate`) for hash-mode apps.
199
+
139
200
  No-op with a console warning when called outside a browser environment.
140
201
 
141
202
  ---
@@ -253,6 +314,8 @@ pageRouter.hydrate(registry, {
253
314
 
254
315
  Returns an `unmount` function that tears down all listeners and hydrated islands.
255
316
 
317
+ > `.hydrate()` is for SSR + history-mode apps. In hash mode, use plain `.mount("#app")` instead — the server has no visibility into hash routes, so there's nothing to hydrate against.
318
+
256
319
  ---
257
320
 
258
321
  #### `.attachLoader(pattern, loader)` — runtime
@@ -265,6 +328,21 @@ router().route("/user/:id", userPage).attachLoader("/user/:id", serverLoader);
265
328
 
266
329
  ---
267
330
 
331
+ ### `setHistoryMode(mode)` · `getHistoryMode()`
332
+
333
+ Selects the history strategy used by the router. Defaults to `"history"` (HTML5 History API, reads/writes `location.pathname`). Set to `"hash"` to store the route in `location.hash` instead — see the [Hash mode](#hash-mode) section above for when to use it.
334
+
335
+ ```ts
336
+ import { setHistoryMode, getHistoryMode } from "@ilha/router";
337
+
338
+ setHistoryMode("hash");
339
+ getHistoryMode(); // → "hash"
340
+ ```
341
+
342
+ The mode is process-global. Call `setHistoryMode()` once at app entry, before any `.mount()`, `.hydrate()`, or `prime()` call.
343
+
344
+ ---
345
+
268
346
  ### `navigate(to, options?)`
269
347
 
270
348
  Programmatically navigate to a path. Updates the URL, history stack, and all reactive signals. Duplicate navigations (same URL) are no-ops.
@@ -276,6 +354,8 @@ navigate("/about");
276
354
  navigate("/about", { replace: true }); // replaces instead of pushing
277
355
  ```
278
356
 
357
+ In hash mode, `navigate("/about")` writes `#/about` into `location.hash`. The argument is always the logical path — no need to prefix it with `#`.
358
+
279
359
  No-op on the server.
280
360
 
281
361
  ---
@@ -583,6 +663,8 @@ interface HydrateOptions {
583
663
  target?: string | Element;
584
664
  }
585
665
 
666
+ type HistoryMode = "history" | "hash";
667
+
586
668
  // Helper — returns fn as-is with LayoutHandler type enforced
587
669
  function defineLayout(fn: LayoutHandler): LayoutHandler;
588
670
 
@@ -597,6 +679,10 @@ function error(status: number, message: string): never;
597
679
 
598
680
  // Merges loaders — later loaders win on key collision
599
681
  function composeLoaders<Ls extends readonly Loader<any>[]>(loaders: Ls): Loader<MergeLoaders<Ls>>;
682
+
683
+ // Selects the history strategy. Default: "history". Call before .mount() / .hydrate().
684
+ function setHistoryMode(mode: HistoryMode): void;
685
+ function getHistoryMode(): HistoryMode;
600
686
  ```
601
687
 
602
688
  ---
@@ -0,0 +1,254 @@
1
+ import { HydratableOptions, Island } from "ilha";
2
+
3
+ //#region src/hash.d.ts
4
+ type HistoryMode = "history" | "hash";
5
+ /**
6
+ * Set the router's history mode. Call this once at app entry, before
7
+ * mounting any router. Defaults to "history" (HTML5 History API).
8
+ *
9
+ * Use "hash" when the document is loaded over file:// (Electron, Tauri, etc.)
10
+ * or any time there's no server able to serve a SPA fallback at arbitrary
11
+ * pathnames.
12
+ *
13
+ * Switching modes mid-session is supported but not common — listeners
14
+ * registered before the switch will keep using their original adapter
15
+ * until they're re-attached (typically by unmounting and remounting
16
+ * the router).
17
+ */
18
+ declare function setHistoryMode(mode: HistoryMode): void;
19
+ declare function getHistoryMode(): HistoryMode;
20
+ //#endregion
21
+ //#region src/index.d.ts
22
+ interface RouteRecord {
23
+ pattern: string;
24
+ island: Island<any, any>;
25
+ /** Merged loader chain (layouts outer→inner, then page) — `undefined` if no loaders. */
26
+ loader?: Loader<any>;
27
+ }
28
+ interface RouteSnapshot {
29
+ path: string;
30
+ params: Record<string, string>;
31
+ search: string;
32
+ hash: string;
33
+ }
34
+ interface AppError {
35
+ message: string;
36
+ status?: number;
37
+ stack?: string;
38
+ }
39
+ type LayoutHandler = (children: Island<any, any>) => Island<any, any>;
40
+ type ErrorHandler = (error: AppError, route: RouteSnapshot) => Island<any, any>;
41
+ interface LoaderContext {
42
+ params: Record<string, string>;
43
+ request: Request;
44
+ url: URL;
45
+ signal: AbortSignal;
46
+ }
47
+ type Loader<T> = (ctx: LoaderContext) => Promise<T> | T;
48
+ /**
49
+ * Identity function for declaring a loader. Exists purely as a type anchor and
50
+ * a marker for the Vite plugin to detect by export name.
51
+ */
52
+ declare function loader<T>(fn: Loader<T>): Loader<T>;
53
+ /** Extract the return type of a loader. */
54
+ type InferLoader<L> = L extends Loader<infer T> ? Awaited<T> : never;
55
+ /**
56
+ * Merge multiple loader return types into a single object type.
57
+ * Later loaders override earlier ones on key collision — matching runtime merge.
58
+ *
59
+ * @example
60
+ * type PageInput = MergeLoaders<[typeof rootLayoutLoad, typeof sectionLayoutLoad, typeof pageLoad]>;
61
+ */
62
+ type MergeLoaders<Ls extends readonly Loader<any>[]> = Ls extends readonly [infer First extends Loader<any>, ...infer Rest extends readonly Loader<any>[]] ? Rest extends readonly [] ? InferLoader<First> : Omit<InferLoader<First>, keyof MergeLoaders<Rest>> & MergeLoaders<Rest> : {};
63
+ declare class Redirect {
64
+ readonly __ilhaRedirect: true;
65
+ readonly to: string;
66
+ readonly status: number;
67
+ constructor(to: string, status?: number);
68
+ }
69
+ declare class LoaderError {
70
+ readonly __ilhaLoaderError: true;
71
+ readonly status: number;
72
+ readonly message: string;
73
+ constructor(status: number, message: string);
74
+ }
75
+ declare function redirect(to: string, status?: number): never;
76
+ declare function error(status: number, message: string): never;
77
+ /**
78
+ * Compose a list of loaders into a single loader. Later loaders win on key
79
+ * collision (page loader overrides layout loader for the same key). All loaders
80
+ * run concurrently within a chain since they share the same abort signal and
81
+ * request — re-fetching is cheap with a request-scoped cache (future work).
82
+ *
83
+ * For v1 we run them in parallel via `Promise.all`. If a loader throws a
84
+ * `Redirect` or `LoaderError`, the composed loader re-throws it unchanged.
85
+ */
86
+ declare function composeLoaders<Ls extends readonly Loader<any>[]>(loaders: Ls): Loader<MergeLoaders<Ls>>;
87
+ declare function wrapLayout(layout: LayoutHandler, page: Island<any, any>): Island<any, any>;
88
+ declare function wrapError(handler: ErrorHandler, page: Island<any, any>): Island<any, any>;
89
+ declare function defineLayout(layout: LayoutHandler): LayoutHandler;
90
+ interface NavigateOptions {
91
+ replace?: boolean;
92
+ }
93
+ interface HydratableRenderOptions extends Partial<Omit<HydratableOptions, "name">> {}
94
+ interface HydrateOptions {
95
+ root?: Element;
96
+ target?: string | Element;
97
+ }
98
+ interface MountOptions {
99
+ hydrate?: boolean;
100
+ registry?: Record<string, Island<any, any>>;
101
+ }
102
+ /** Response envelope returned by `renderResponse` — lets the host app handle redirects. */
103
+ type RenderResponse = {
104
+ kind: "html";
105
+ html: string;
106
+ status?: number;
107
+ } | {
108
+ kind: "redirect";
109
+ to: string;
110
+ status: number;
111
+ } | {
112
+ kind: "error";
113
+ status: number;
114
+ message: string;
115
+ html: string;
116
+ };
117
+ interface RouterBuilder {
118
+ /**
119
+ * Register a route. The optional `loader` is the merged loader chain
120
+ * (layout loaders outer→inner followed by the page loader) produced by
121
+ * the FS-routing codegen.
122
+ */
123
+ route(pattern: string, island: Island<any, any>, loader?: Loader<any>): RouterBuilder;
124
+ /**
125
+ * Attach (or replace) a loader on an already-registered route pattern.
126
+ * Used by the `ilha:loaders` virtual module to wire server-only loaders
127
+ * onto the client-safe `pageRouter` at SSR time. No-op if the pattern
128
+ * was never registered via `.route()`.
129
+ */
130
+ attachLoader(pattern: string, loader: Loader<any>): RouterBuilder;
131
+ prime(): void;
132
+ mount(target: string | Element, options?: MountOptions): () => void;
133
+ render(url: string | URL): string;
134
+ renderHydratable(url: string | URL, registry: Record<string, Island<any, any>>, options?: HydratableRenderOptions, request?: Request): Promise<string>;
135
+ /**
136
+ * Like `renderHydratable` but surfaces loader redirects and errors as
137
+ * structured responses instead of baking them into HTML. Prefer this from
138
+ * host server code so you can emit proper 302 / 4xx responses.
139
+ */
140
+ renderResponse(url: string | URL, registry: Record<string, Island<any, any>>, options?: HydratableRenderOptions, request?: Request): Promise<RenderResponse>;
141
+ /**
142
+ * Run the loader chain for a given URL without rendering. Used by the
143
+ * `/__ilha/loader` endpoint the Vite plugin exposes for client-side
144
+ * navigation. Returns the raw loader result, a redirect sentinel, or an
145
+ * error sentinel.
146
+ */
147
+ runLoader(url: string | URL, request?: Request): Promise<{
148
+ kind: "data";
149
+ data: Record<string, unknown>;
150
+ } | {
151
+ kind: "redirect";
152
+ to: string;
153
+ status: number;
154
+ } | {
155
+ kind: "error";
156
+ status: number;
157
+ message: string;
158
+ } | {
159
+ kind: "not-found";
160
+ }>;
161
+ /**
162
+ * Hydrate the application - combines prime(), mount(), and router.mount() into one call.
163
+ * @param registry - The island registry from ilha:registry
164
+ * @param options - Optional root element (defaults to document.body) and router target (defaults to root)
165
+ * @returns Cleanup function
166
+ */
167
+ hydrate(registry: Record<string, Island<any, any>>, options?: HydrateOptions): () => void;
168
+ }
169
+ /** Path of the loader endpoint served by the Vite plugin / production adapter. */
170
+ declare const LOADER_ENDPOINT = "/__ilha/loader";
171
+ /**
172
+ * Prefetch loader data for a given path. Safe to call repeatedly — a single
173
+ * inflight request is reused until it either resolves (and is consumed by
174
+ * navigation) or is superseded by another prefetch.
175
+ */
176
+ declare function prefetch(pathWithSearch: string): void;
177
+ declare const routePath: {
178
+ (): string;
179
+ (value: string): void;
180
+ };
181
+ declare const routeParams: {
182
+ (): Record<string, string>;
183
+ (value: Record<string, string>): void;
184
+ };
185
+ declare const routeSearch: {
186
+ (): string;
187
+ (value: string): void;
188
+ };
189
+ declare const routeHash: {
190
+ (): string;
191
+ (value: string): void;
192
+ };
193
+ declare function useRoute(): {
194
+ path: {
195
+ (): string;
196
+ (value: string): void;
197
+ };
198
+ params: {
199
+ (): Record<string, string>;
200
+ (value: Record<string, string>): void;
201
+ };
202
+ search: {
203
+ (): string;
204
+ (value: string): void;
205
+ };
206
+ hash: {
207
+ (): string;
208
+ (value: string): void;
209
+ };
210
+ };
211
+ /**
212
+ * Prime route context signals from the current `location` so that islands
213
+ * hydrated by `ilha.mount()` see the correct route values on their first
214
+ * render — preventing a mismatch morph that would destroy hydrated bindings.
215
+ */
216
+ declare function prime(): void;
217
+ declare function navigate(to: string, opts?: NavigateOptions): void;
218
+ interface LinkInterceptionOptions {
219
+ /**
220
+ * Prefetch loader data on `mouseenter` for eligible links. Links opt in via
221
+ * the `data-prefetch` attribute (set `data-prefetch="false"` to opt out a
222
+ * specific link even when the framework is configured to prefetch by default).
223
+ * Default: `true` — prefetches on hover for any link with `data-prefetch`.
224
+ */
225
+ prefetch?: boolean;
226
+ }
227
+ declare function enableLinkInterception(root?: Element | Document, options?: LinkInterceptionOptions): () => void;
228
+ declare const RouterView: Island<Record<string, unknown>, Record<string, never>>;
229
+ declare const RouterLink: Island<Record<string, unknown>, {
230
+ [x: string]: never;
231
+ href: string;
232
+ } & Record<"label", string>>;
233
+ declare function isActive(pattern: string): boolean;
234
+ declare function router(): RouterBuilder;
235
+ declare const _default: {
236
+ router: typeof router;
237
+ navigate: typeof navigate;
238
+ useRoute: typeof useRoute;
239
+ isActive: typeof isActive;
240
+ enableLinkInterception: typeof enableLinkInterception;
241
+ prime: typeof prime;
242
+ prefetch: typeof prefetch;
243
+ RouterView: Island<Record<string, unknown>, Record<string, never>>;
244
+ RouterLink: Island<Record<string, unknown>, {
245
+ [x: string]: never;
246
+ href: string;
247
+ } & Record<"label", string>>;
248
+ loader: typeof loader;
249
+ redirect: typeof redirect;
250
+ error: typeof error;
251
+ composeLoaders: typeof composeLoaders;
252
+ };
253
+ //#endregion
254
+ export { prefetch as A, wrapLayout as B, composeLoaders as C, isActive as D, error as E, routePath as F, getHistoryMode as H, routeSearch as I, router as L, redirect as M, routeHash as N, loader as O, routeParams as P, useRoute as R, _default as S, enableLinkInterception as T, setHistoryMode as U, HistoryMode as V, RouteRecord as _, InferLoader as a, RouterLink as b, LinkInterceptionOptions as c, LoaderError as d, MergeLoaders as f, RenderResponse as g, Redirect as h, HydrateOptions as i, prime as j, navigate as k, Loader as l, NavigateOptions as m, ErrorHandler as n, LOADER_ENDPOINT as o, MountOptions as p, HydratableRenderOptions as r, LayoutHandler as s, AppError as t, LoaderContext as u, RouteSnapshot as v, defineLayout as w, RouterView as x, RouterBuilder as y, wrapError as z };
package/dist/index.d.ts CHANGED
@@ -1,236 +1,2 @@
1
- import { HydratableOptions, Island } from "ilha";
2
-
3
- //#region src/index.d.ts
4
- interface RouteRecord {
5
- pattern: string;
6
- island: Island<any, any>;
7
- /** Merged loader chain (layouts outer→inner, then page) — `undefined` if no loaders. */
8
- loader?: Loader<any>;
9
- }
10
- interface RouteSnapshot {
11
- path: string;
12
- params: Record<string, string>;
13
- search: string;
14
- hash: string;
15
- }
16
- interface AppError {
17
- message: string;
18
- status?: number;
19
- stack?: string;
20
- }
21
- type LayoutHandler = (children: Island<any, any>) => Island<any, any>;
22
- type ErrorHandler = (error: AppError, route: RouteSnapshot) => Island<any, any>;
23
- interface LoaderContext {
24
- params: Record<string, string>;
25
- request: Request;
26
- url: URL;
27
- signal: AbortSignal;
28
- }
29
- type Loader<T> = (ctx: LoaderContext) => Promise<T> | T;
30
- /**
31
- * Identity function for declaring a loader. Exists purely as a type anchor and
32
- * a marker for the Vite plugin to detect by export name.
33
- */
34
- declare function loader<T>(fn: Loader<T>): Loader<T>;
35
- /** Extract the return type of a loader. */
36
- type InferLoader<L> = L extends Loader<infer T> ? Awaited<T> : never;
37
- /**
38
- * Merge multiple loader return types into a single object type.
39
- * Later loaders override earlier ones on key collision — matching runtime merge.
40
- *
41
- * @example
42
- * type PageInput = MergeLoaders<[typeof rootLayoutLoad, typeof sectionLayoutLoad, typeof pageLoad]>;
43
- */
44
- type MergeLoaders<Ls extends readonly Loader<any>[]> = Ls extends readonly [infer First extends Loader<any>, ...infer Rest extends readonly Loader<any>[]] ? Rest extends readonly [] ? InferLoader<First> : Omit<InferLoader<First>, keyof MergeLoaders<Rest>> & MergeLoaders<Rest> : {};
45
- declare class Redirect {
46
- readonly __ilhaRedirect: true;
47
- readonly to: string;
48
- readonly status: number;
49
- constructor(to: string, status?: number);
50
- }
51
- declare class LoaderError {
52
- readonly __ilhaLoaderError: true;
53
- readonly status: number;
54
- readonly message: string;
55
- constructor(status: number, message: string);
56
- }
57
- declare function redirect(to: string, status?: number): never;
58
- declare function error(status: number, message: string): never;
59
- /**
60
- * Compose a list of loaders into a single loader. Later loaders win on key
61
- * collision (page loader overrides layout loader for the same key). All loaders
62
- * run concurrently within a chain since they share the same abort signal and
63
- * request — re-fetching is cheap with a request-scoped cache (future work).
64
- *
65
- * For v1 we run them in parallel via `Promise.all`. If a loader throws a
66
- * `Redirect` or `LoaderError`, the composed loader re-throws it unchanged.
67
- */
68
- declare function composeLoaders<Ls extends readonly Loader<any>[]>(loaders: Ls): Loader<MergeLoaders<Ls>>;
69
- declare function wrapLayout(layout: LayoutHandler, page: Island<any, any>): Island<any, any>;
70
- declare function wrapError(handler: ErrorHandler, page: Island<any, any>): Island<any, any>;
71
- declare function defineLayout(layout: LayoutHandler): LayoutHandler;
72
- interface NavigateOptions {
73
- replace?: boolean;
74
- }
75
- interface HydratableRenderOptions extends Partial<Omit<HydratableOptions, "name">> {}
76
- interface HydrateOptions {
77
- root?: Element;
78
- target?: string | Element;
79
- }
80
- interface MountOptions {
81
- hydrate?: boolean;
82
- registry?: Record<string, Island<any, any>>;
83
- }
84
- /** Response envelope returned by `renderResponse` — lets the host app handle redirects. */
85
- type RenderResponse = {
86
- kind: "html";
87
- html: string;
88
- status?: number;
89
- } | {
90
- kind: "redirect";
91
- to: string;
92
- status: number;
93
- } | {
94
- kind: "error";
95
- status: number;
96
- message: string;
97
- html: string;
98
- };
99
- interface RouterBuilder {
100
- /**
101
- * Register a route. The optional `loader` is the merged loader chain
102
- * (layout loaders outer→inner followed by the page loader) produced by
103
- * the FS-routing codegen.
104
- */
105
- route(pattern: string, island: Island<any, any>, loader?: Loader<any>): RouterBuilder;
106
- /**
107
- * Attach (or replace) a loader on an already-registered route pattern.
108
- * Used by the `ilha:loaders` virtual module to wire server-only loaders
109
- * onto the client-safe `pageRouter` at SSR time. No-op if the pattern
110
- * was never registered via `.route()`.
111
- */
112
- attachLoader(pattern: string, loader: Loader<any>): RouterBuilder;
113
- prime(): void;
114
- mount(target: string | Element, options?: MountOptions): () => void;
115
- render(url: string | URL): string;
116
- renderHydratable(url: string | URL, registry: Record<string, Island<any, any>>, options?: HydratableRenderOptions, request?: Request): Promise<string>;
117
- /**
118
- * Like `renderHydratable` but surfaces loader redirects and errors as
119
- * structured responses instead of baking them into HTML. Prefer this from
120
- * host server code so you can emit proper 302 / 4xx responses.
121
- */
122
- renderResponse(url: string | URL, registry: Record<string, Island<any, any>>, options?: HydratableRenderOptions, request?: Request): Promise<RenderResponse>;
123
- /**
124
- * Run the loader chain for a given URL without rendering. Used by the
125
- * `/__ilha/loader` endpoint the Vite plugin exposes for client-side
126
- * navigation. Returns the raw loader result, a redirect sentinel, or an
127
- * error sentinel.
128
- */
129
- runLoader(url: string | URL, request?: Request): Promise<{
130
- kind: "data";
131
- data: Record<string, unknown>;
132
- } | {
133
- kind: "redirect";
134
- to: string;
135
- status: number;
136
- } | {
137
- kind: "error";
138
- status: number;
139
- message: string;
140
- } | {
141
- kind: "not-found";
142
- }>;
143
- /**
144
- * Hydrate the application - combines prime(), mount(), and router.mount() into one call.
145
- * @param registry - The island registry from ilha:registry
146
- * @param options - Optional root element (defaults to document.body) and router target (defaults to root)
147
- * @returns Cleanup function
148
- */
149
- hydrate(registry: Record<string, Island<any, any>>, options?: HydrateOptions): () => void;
150
- }
151
- /** Path of the loader endpoint served by the Vite plugin / production adapter. */
152
- declare const LOADER_ENDPOINT = "/__ilha/loader";
153
- /**
154
- * Prefetch loader data for a given path. Safe to call repeatedly — a single
155
- * inflight request is reused until it either resolves (and is consumed by
156
- * navigation) or is superseded by another prefetch.
157
- */
158
- declare function prefetch(pathWithSearch: string): void;
159
- declare const routePath: {
160
- (): string;
161
- (value: string): void;
162
- };
163
- declare const routeParams: {
164
- (): Record<string, string>;
165
- (value: Record<string, string>): void;
166
- };
167
- declare const routeSearch: {
168
- (): string;
169
- (value: string): void;
170
- };
171
- declare const routeHash: {
172
- (): string;
173
- (value: string): void;
174
- };
175
- declare function useRoute(): {
176
- path: {
177
- (): string;
178
- (value: string): void;
179
- };
180
- params: {
181
- (): Record<string, string>;
182
- (value: Record<string, string>): void;
183
- };
184
- search: {
185
- (): string;
186
- (value: string): void;
187
- };
188
- hash: {
189
- (): string;
190
- (value: string): void;
191
- };
192
- };
193
- /**
194
- * Prime route context signals from the current `location` so that islands
195
- * hydrated by `ilha.mount()` see the correct route values on their first
196
- * render — preventing a mismatch morph that would destroy hydrated bindings.
197
- */
198
- declare function prime(): void;
199
- declare function navigate(to: string, opts?: NavigateOptions): void;
200
- interface LinkInterceptionOptions {
201
- /**
202
- * Prefetch loader data on `mouseenter` for eligible links. Links opt in via
203
- * the `data-prefetch` attribute (set `data-prefetch="false"` to opt out a
204
- * specific link even when the framework is configured to prefetch by default).
205
- * Default: `true` — prefetches on hover for any link with `data-prefetch`.
206
- */
207
- prefetch?: boolean;
208
- }
209
- declare function enableLinkInterception(root?: Element | Document, options?: LinkInterceptionOptions): () => void;
210
- declare const RouterView: Island<Record<string, unknown>, Record<string, never>>;
211
- declare const RouterLink: Island<Record<string, unknown>, {
212
- [x: string]: never;
213
- href: string;
214
- } & Record<"label", string>>;
215
- declare function isActive(pattern: string): boolean;
216
- declare function router(): RouterBuilder;
217
- declare const _default: {
218
- router: typeof router;
219
- navigate: typeof navigate;
220
- useRoute: typeof useRoute;
221
- isActive: typeof isActive;
222
- enableLinkInterception: typeof enableLinkInterception;
223
- prime: typeof prime;
224
- prefetch: typeof prefetch;
225
- RouterView: Island<Record<string, unknown>, Record<string, never>>;
226
- RouterLink: Island<Record<string, unknown>, {
227
- [x: string]: never;
228
- href: string;
229
- } & Record<"label", string>>;
230
- loader: typeof loader;
231
- redirect: typeof redirect;
232
- error: typeof error;
233
- composeLoaders: typeof composeLoaders;
234
- };
235
- //#endregion
236
- export { AppError, ErrorHandler, HydratableRenderOptions, HydrateOptions, InferLoader, LOADER_ENDPOINT, LayoutHandler, LinkInterceptionOptions, Loader, LoaderContext, LoaderError, MergeLoaders, MountOptions, NavigateOptions, Redirect, RenderResponse, RouteRecord, RouteSnapshot, RouterBuilder, RouterLink, RouterView, composeLoaders, _default as default, defineLayout, enableLinkInterception, error, isActive, loader, navigate, prefetch, prime, redirect, routeHash, routeParams, routePath, routeSearch, router, useRoute, wrapError, wrapLayout };
1
+ import { A as prefetch, B as wrapLayout, C as composeLoaders, D as isActive, E as error, F as routePath, H as getHistoryMode, I as routeSearch, L as router, M as redirect, N as routeHash, O as loader, P as routeParams, R as useRoute, S as _default, T as enableLinkInterception, U as setHistoryMode, V as HistoryMode, _ as RouteRecord, a as InferLoader, b as RouterLink, c as LinkInterceptionOptions, d as LoaderError, f as MergeLoaders, g as RenderResponse, h as Redirect, i as HydrateOptions, j as prime, k as navigate, l as Loader, m as NavigateOptions, n as ErrorHandler, o as LOADER_ENDPOINT, p as MountOptions, r as HydratableRenderOptions, s as LayoutHandler, t as AppError, u as LoaderContext, v as RouteSnapshot, w as defineLayout, x as RouterView, y as RouterBuilder, z as wrapError } from "./index-BsCpWQzC.js";
2
+ export { AppError, ErrorHandler, HistoryMode, HydratableRenderOptions, HydrateOptions, InferLoader, LOADER_ENDPOINT, LayoutHandler, LinkInterceptionOptions, Loader, LoaderContext, LoaderError, MergeLoaders, MountOptions, NavigateOptions, Redirect, RenderResponse, RouteRecord, RouteSnapshot, RouterBuilder, RouterLink, RouterView, composeLoaders, _default as default, defineLayout, enableLinkInterception, error, getHistoryMode, isActive, loader, navigate, prefetch, prime, redirect, routeHash, routeParams, routePath, routeSearch, router, setHistoryMode, useRoute, wrapError, wrapLayout };