@ilha/router 0.2.5 → 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 +86 -0
- package/dist/index-BsCpWQzC.d.ts +254 -0
- package/dist/index.d.ts +2 -236
- package/dist/index.js +2 -637
- package/dist/src-C2qmhASZ.js +776 -0
- package/dist/vite.d.ts +1 -1
- package/dist/vite.js +13 -4
- package/package.json +1 -1
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 {
|
|
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 };
|