@ilha/router 0.7.0 → 0.8.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 +98 -8
- package/dist/index.d.ts +31 -0
- package/dist/index.js +1 -1
- package/dist/plugin-BW2tnuyF.js +8 -0
- package/dist/plugin.d.ts +2 -0
- package/dist/rolldown.js +1 -1
- package/dist/rspack.js +1 -1
- package/dist/src-8LBo_Ieg.js +4 -0
- package/dist/ssr.js +1 -1
- package/dist/vite.js +1 -1
- package/package.json +1 -1
- package/dist/plugin-nkvFqO15.js +0 -8
- package/dist/src-BHVpXJIQ.js +0 -4
package/README.md
CHANGED
|
@@ -135,10 +135,19 @@ You can still register loaders, but they run on the client (via the loader endpo
|
|
|
135
135
|
|
|
136
136
|
## Core API
|
|
137
137
|
|
|
138
|
-
### `router()`
|
|
138
|
+
### `router(options?)`
|
|
139
139
|
|
|
140
140
|
Creates a new router instance and **resets the route registry**. Always call `router()` fresh — never share instances across server requests.
|
|
141
141
|
|
|
142
|
+
| Option | Type | Default | Description |
|
|
143
|
+
| ------------------------ | ------------------- | ------- | ----------------------------------------------------------------------------------------- |
|
|
144
|
+
| `mode` | `"spa" \| "static"` | `"spa"` | `"static"` disables client navigation — hydrate with `hydrateStatic()` |
|
|
145
|
+
| `interceptLinks` | `boolean` | `true` | Intercept internal `<a>` clicks for SPA navigation |
|
|
146
|
+
| `notFound` | `Island` | — | Custom 404 island (SSR status 404; mounted with a full lifecycle in the browser) |
|
|
147
|
+
| `allowExternalRedirects` | `boolean` | `false` | Allow loader `redirect()` to cross-origin URLs; blocked targets become a 500 error |
|
|
148
|
+
| `loaderTimeout` | `number` | — | Abort + fail a loader after this many ms (enforced even if the loader ignores its signal) |
|
|
149
|
+
| `viewTransitions` | `boolean` | `false` | Wrap client view swaps in `document.startViewTransition()` when supported |
|
|
150
|
+
|
|
142
151
|
Returns a `RouterBuilder`.
|
|
143
152
|
|
|
144
153
|
---
|
|
@@ -147,7 +156,9 @@ Returns a `RouterBuilder`.
|
|
|
147
156
|
|
|
148
157
|
Registers a route. Patterns are matched in **declaration order** — first match wins. Uses [rou3](https://github.com/h3js/rou3) for matching, the same engine as Nitro.
|
|
149
158
|
|
|
150
|
-
The optional `loader` is a data-fetching function that runs before the page renders. Its return value is passed as input props to the island.
|
|
159
|
+
The optional `loader` is a data-fetching function that runs before the page renders. Its return value is passed as input props to the island. The loader runs **wherever the router runs**: during SSR it executes on the server; when the route was registered in the browser (a plain SPA, hash mode, `file://`), client navigations execute it locally — no server or `/__ilha/loader` endpoint needed. Routes marked via `.markLoader()` (the SSR-split pages build) still fetch from the endpoint.
|
|
160
|
+
|
|
161
|
+
A locally-executed loader receives a synthetic `Request` (no cookies or server context) — rely on `url`, `params`, and `signal`. Loader `redirect()`s are checked against the same cross-origin policy as on the server (`allowExternalRedirects`).
|
|
151
162
|
|
|
152
163
|
```ts
|
|
153
164
|
import { loader } from "@ilha/router";
|
|
@@ -344,6 +355,35 @@ router().route("/user/:id", userPage).attachLoader("/user/:id", serverLoader);
|
|
|
344
355
|
|
|
345
356
|
---
|
|
346
357
|
|
|
358
|
+
#### `.clientLoader(pattern, loader)` — runtime
|
|
359
|
+
|
|
360
|
+
Attaches a loader that runs **in the browser** on client navigations, instead of fetching from the `/__ilha/loader` endpoint. Used by the FS-routing codegen for `clientLoad` exports; also available for manual routers. When a route has both a server loader and a client loader, the client loader wins on client navigations and the server loader runs during SSR. No-op (with a warning) if the pattern was never registered via `.route()`.
|
|
361
|
+
|
|
362
|
+
```ts
|
|
363
|
+
router()
|
|
364
|
+
.route("/dashboard", dashboardPage)
|
|
365
|
+
.clientLoader(
|
|
366
|
+
"/dashboard",
|
|
367
|
+
loader(async ({ signal }) => ({ stats: await fetchStats({ signal }) })),
|
|
368
|
+
);
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
#### `.errorBoundary(pattern, handler)` — runtime
|
|
374
|
+
|
|
375
|
+
Attaches the route's `+error` boundary so **loader** errors render through it — on the server (`renderResponse` returns the boundary's HTML with the error status) and on client navigations. Render errors are already handled by `wrapError` inside the island; this closes the gap for errors thrown before rendering starts. The FS-routing codegen wires the nearest `+error.ts` automatically; manual routers can call it directly. A throwing boundary falls back to the minimal inline error.
|
|
376
|
+
|
|
377
|
+
```ts
|
|
378
|
+
router()
|
|
379
|
+
.route("/user/:id", userPage, userLoader)
|
|
380
|
+
.errorBoundary("/user/:id", (err, route) =>
|
|
381
|
+
ilha.render(() => `<h1>${err.status ?? 500}</h1><p>${err.message}</p>`),
|
|
382
|
+
);
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
347
387
|
### `setHistoryMode(mode)` · `getHistoryMode()`
|
|
348
388
|
|
|
349
389
|
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.
|
|
@@ -376,6 +416,31 @@ No-op on the server.
|
|
|
376
416
|
|
|
377
417
|
---
|
|
378
418
|
|
|
419
|
+
### `navigating()`
|
|
420
|
+
|
|
421
|
+
Reactive — `true` while a client navigation (loader fetch + view swap) is in flight. Read it inside any island render to drive a progress bar or spinner; it re-renders when the state flips. Also available as `useRoute().navigating`.
|
|
422
|
+
|
|
423
|
+
```ts
|
|
424
|
+
import { navigating } from "@ilha/router";
|
|
425
|
+
|
|
426
|
+
const Spinner = ilha.render(() => (navigating() ? `<div class="bar" />` : ""));
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
### `invalidate()`
|
|
432
|
+
|
|
433
|
+
Re-runs the current route's loader and re-renders the view with fresh data — call it after a mutation. Resolves when the view has updated. No-op on the server or when no router is mounted.
|
|
434
|
+
|
|
435
|
+
```ts
|
|
436
|
+
import { invalidate } from "@ilha/router";
|
|
437
|
+
|
|
438
|
+
await api.deleteItem(id);
|
|
439
|
+
await invalidate(); // current page refetches and re-renders
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
379
444
|
### `prime()`
|
|
380
445
|
|
|
381
446
|
Standalone export of the same signal-priming function available as `.prime()` on the builder. Useful when managing the priming step separately from the router instance.
|
|
@@ -894,6 +959,31 @@ export default defineLayout((children) => /* … */);
|
|
|
894
959
|
|
|
895
960
|
Layout loaders are composed automatically — you do not need to call `composeLoaders()` manually.
|
|
896
961
|
|
|
962
|
+
### Client loaders (`clientLoad`)
|
|
963
|
+
|
|
964
|
+
A page or layout can export a `clientLoad` function that runs **in the browser** on client navigations, instead of fetching from the loader endpoint. Use it for data that is fetchable from the client anyway (public APIs, the app's own REST endpoints) — it saves a server round-trip per navigation, and it works on static hosts with no loader endpoint at all.
|
|
965
|
+
|
|
966
|
+
```ts
|
|
967
|
+
// src/pages/dashboard.ts
|
|
968
|
+
import { loader } from "@ilha/router";
|
|
969
|
+
import ilha from "ilha";
|
|
970
|
+
|
|
971
|
+
export const clientLoad = loader(async ({ signal }) => {
|
|
972
|
+
const stats = await fetch("/api/stats", { signal }).then((r) => r.json());
|
|
973
|
+
return { stats };
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
export default ilha.input<{ stats: Stats }>().render(/* … */);
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
Rules and caveats:
|
|
980
|
+
|
|
981
|
+
- `clientLoad` is bundled into the client — never put secrets, database clients, or server-only imports in it. Keep those in `load`, which stays server-only.
|
|
982
|
+
- A page can export **both**: `load` runs during SSR (first paint), `clientLoad` runs on client navigations instead of the endpoint fetch. Make them return the same shape.
|
|
983
|
+
- Layout `clientLoad`s compose with the page's, layouts first — the page wins on key collision, mirroring server loaders.
|
|
984
|
+
- With SSR + hydration, a `clientLoad`-only page is server-rendered **without** its data; the router runs `clientLoad` on the client right after hydration and re-renders the route with the loaded props.
|
|
985
|
+
- `clientLoad` receives a synthetic `Request` — rely on `url`, `params`, and `signal`, not cookies or headers.
|
|
986
|
+
|
|
897
987
|
### Error boundaries
|
|
898
988
|
|
|
899
989
|
A `+error.ts` catches any error thrown during rendering of pages in its directory and all subdirectories. The nearest boundary wins. If an inner boundary re-throws, the next outer boundary takes over.
|
|
@@ -1007,15 +1097,15 @@ Or use the one-liner: `pageRouter.hydrate(registry)`.
|
|
|
1007
1097
|
|
|
1008
1098
|
On the **server**, loaders run inside `.renderHydratable()` / `.renderResponse()`. Their return value is serialized into `data-ilha-props` on the island element so the client can rehydrate without re-fetching.
|
|
1009
1099
|
|
|
1010
|
-
On the **client**, navigations
|
|
1100
|
+
On the **client**, navigations resolve loader data before mounting the next island. Routes with a loader registered in the browser — a manual `.route(path, island, loader)` or an FS-routing `clientLoad` export — run that loader locally, with no network round-trip. Routes with only a server loader (`markLoader()` / a `load` export) fetch from the `/__ilha/loader` endpoint, served automatically by the Vite plugin (dev) and the Nitro adapter (production).
|
|
1011
1101
|
|
|
1012
1102
|
```
|
|
1013
1103
|
server client (navigation)
|
|
1014
|
-
────────────────────────────
|
|
1015
|
-
renderHydratable
|
|
1016
|
-
→ executeLoader(…) →
|
|
1017
|
-
→ island.hydratable(props) → fetchLoaderData(
|
|
1018
|
-
→ data-ilha-props="{…}"
|
|
1104
|
+
──────────────────────────── ─────────────────────────────────────────
|
|
1105
|
+
renderHydratable local loader (clientLoad / .route loader)?
|
|
1106
|
+
→ executeLoader(…) yes → runLocalLoader(…) in-browser
|
|
1107
|
+
→ island.hydratable(props) no → fetchLoaderData(…) GET /__ilha/loader?path=/user/42
|
|
1108
|
+
→ data-ilha-props="{…}" → mountRouteWithHydration(island, host, …)
|
|
1019
1109
|
```
|
|
1020
1110
|
|
|
1021
1111
|
---
|
package/dist/index.d.ts
CHANGED
|
@@ -151,6 +151,12 @@ export interface RouterOptions {
|
|
|
151
151
|
* `ctx.signal` also aborts when the incoming `Request`'s signal aborts.
|
|
152
152
|
*/
|
|
153
153
|
loaderTimeout?: number;
|
|
154
|
+
/**
|
|
155
|
+
* Wrap client-side view swaps in `document.startViewTransition()` when the
|
|
156
|
+
* browser supports it (falls back to an instant swap otherwise).
|
|
157
|
+
* Default: `false`.
|
|
158
|
+
*/
|
|
159
|
+
viewTransitions?: boolean;
|
|
154
160
|
}
|
|
155
161
|
export interface HydratableRenderOptions extends Partial<Omit<HydratableOptions, "name">> {
|
|
156
162
|
/**
|
|
@@ -209,6 +215,22 @@ export interface RouterBuilder {
|
|
|
209
215
|
* was never registered via `.route()`.
|
|
210
216
|
*/
|
|
211
217
|
attachLoader(pattern: string, loader: Loader<any>): RouterBuilder;
|
|
218
|
+
/**
|
|
219
|
+
* Attach a loader that runs **in the browser** on client navigations,
|
|
220
|
+
* instead of fetching from the loader endpoint. Used by the FS-routing
|
|
221
|
+
* codegen for `clientLoad` exports; also available for manual routers.
|
|
222
|
+
* When a route has both, the client loader wins on client navigations and
|
|
223
|
+
* the server loader runs during SSR. No-op if the pattern was never
|
|
224
|
+
* registered via `.route()`.
|
|
225
|
+
*/
|
|
226
|
+
clientLoader(pattern: string, loader: Loader<any>): RouterBuilder;
|
|
227
|
+
/**
|
|
228
|
+
* Attach the route's nearest `+error` boundary so **loader** errors render
|
|
229
|
+
* through it (render errors are already handled by `wrapError` inside the
|
|
230
|
+
* island). Used by the FS-routing codegen; also available for manual
|
|
231
|
+
* routers. No-op if the pattern was never registered via `.route()`.
|
|
232
|
+
*/
|
|
233
|
+
errorBoundary(pattern: string, handler: ErrorHandler): RouterBuilder;
|
|
212
234
|
/**
|
|
213
235
|
* Mark an already-registered route as having a server-side loader without
|
|
214
236
|
* importing that loader into the client bundle. Used by FS-routing codegen
|
|
@@ -280,11 +302,20 @@ export declare function routePath(value?: string): string;
|
|
|
280
302
|
export declare function routeParams(value?: Record<string, string>): Record<string, string>;
|
|
281
303
|
export declare function routeSearch(value?: string): string;
|
|
282
304
|
export declare function routeHash(value?: string): string;
|
|
305
|
+
/** Reactive: `true` while a client navigation (loader fetch + view swap) is in flight. */
|
|
306
|
+
export declare function navigating(): boolean;
|
|
307
|
+
/**
|
|
308
|
+
* Re-run the current route's loader and re-render the view with fresh data —
|
|
309
|
+
* e.g. after a mutation. Resolves when the view has updated. No-op on the
|
|
310
|
+
* server or when no router is mounted.
|
|
311
|
+
*/
|
|
312
|
+
export declare function invalidate(): Promise<void>;
|
|
283
313
|
export declare function useRoute(): {
|
|
284
314
|
path: typeof routePath;
|
|
285
315
|
params: typeof routeParams;
|
|
286
316
|
search: typeof routeSearch;
|
|
287
317
|
hash: typeof routeHash;
|
|
318
|
+
navigating: typeof navigating;
|
|
288
319
|
};
|
|
289
320
|
/**
|
|
290
321
|
* Prime route context signals from the current `location` so that islands
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{A as e,C as t,D as n,E as r,
|
|
1
|
+
import{A as e,C as t,D as n,E as r,M as i,O as a,S as o,T as s,_ as c,a as l,b as u,c as d,d as f,f as p,g as m,h,i as g,j as _,k as v,l as y,m as b,n as x,o as S,p as C,r as w,s as T,t as E,u as D,v as O,w as k,x as A,y as j}from"./src-8LBo_Ieg.js";export{E as LOADER_ENDPOINT,x as LoaderError,w as Redirect,g as RouterLink,l as RouterView,S as afterNavigate,T as beforeNavigate,d as composeLoaders,n as default,y as defineLayout,D as enableLinkInterception,f as error,_ as getHistoryMode,p as head,C as invalidate,b as isActive,h as loader,m as navigate,c as navigating,O as prefetch,j as prime,u as redirect,A as routeHash,o as routeParams,t as routePath,k as routeSearch,s as router,r as serializeHead,i as setHistoryMode,a as useRoute,v as wrapError,e as wrapLayout};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import{existsSync as e,readFileSync as t,watch as n}from"node:fs";import{basename as r,dirname as i,extname as a,join as o,relative as s,resolve as c,sep as l}from"node:path";import{createUnplugin as u}from"unplugin";import{mkdir as d,readFile as f,readdir as p,writeFile as m}from"node:fs/promises";function h(e){return e.replace(/\\/g,`/`)}const g=/\.(test|spec|d)\.(ts|tsx)$/,_=/^\s*export\s+(?:const|let|var|async\s+function|function)\s+load\b/m,v=/^\s*export\s+(?:const|let|var|async\s+function|function)\s+clientLoad\b/m;async function y(e){try{let t=(await f(e,`utf8`)).replace(/^\s*\/\/.*$/gm,``);return{load:_.test(t),clientLoad:v.test(t)}}catch(t){return t?.code!==`ENOENT`&&console.warn(`[ilha-router] failed to read ${e} while detecting loader exports:`,t),{load:!1,clientLoad:!1}}}function b(e){return e.startsWith(`[...`)&&e.endsWith(`]`)?`**:${e.slice(4,-1)}`:e.startsWith(`[`)&&e.endsWith(`]`)?`:${e.slice(1,-1)}`:e}function x(e){return e.startsWith(`(`)&&e.endsWith(`)`)?``:b(e)}function S(e,t){let n=h(s(e,t)),r=n.slice(0,-a(n).length).split(`/`),i=[...r.slice(0,-1).map(x),b(r.at(-1))];return i.at(-1)===`index`&&i.pop(),`/`+i.filter(Boolean).join(`/`)||`/`}function C(e){return e===`/`?`index`:e.replace(/^\//,``).replace(/\*\*:[^/]*/g,e=>e.length>3?e.slice(3):`wildcard`).replace(/:/g,``).replace(/\*\*/g,`wildcard`).replace(/\//g,`-`).replace(/[^a-zA-Z0-9-]/g,``)||`page`}function w(e){return e===`/`?3:e.includes(`**`)?0:e.includes(`:`)?1:2}function T(e){return[...e].sort((e,t)=>{let n=w(t.pattern)-w(e.pattern);if(n!==0)return n;let r=t.pattern.split(`/`).length-e.pattern.split(`/`).length;return r===0?e.pattern.localeCompare(t.pattern):r})}function E(e,t,n,r){let a=h(s(e,i(t))),c=a===``?[]:a.split(`/`);return[e,...c.map((t,n)=>o(e,...c.slice(0,n+1)))].flatMap(e=>{let t=`${o(e,r)}.tsx`;if(n.has(t))return[t];let i=`${o(e,r)}.ts`;return n.has(i)?[i]:[]})}async function D(e,t=0){if(t>20)return console.warn(`[ilha:pages] Max scan depth (20) reached at ${e} — skipping`),[];let n=[];try{for(let r of await p(e,{withFileTypes:!0})){let i=o(e,r.name);r.isDirectory()?n.push(...await D(i,t+1)):r.isFile()&&/\.(ts|tsx)$/.test(r.name)&&!g.test(r.name)&&n.push(i)}}catch(e){if(e.code===`ENOENT`)return[];throw e}return n}async function O(e){let t=await D(e),n=new Set(t),i=t.filter(e=>!r(e).startsWith(`+`)),a=new Map,o=e=>{let t=a.get(e);return t||(t=y(e),a.set(e,t)),t};return Promise.all(i.map(async t=>{let r=S(e,t),i=E(e,t,n,`+layout`),a=E(e,t,n,`+error`),[s,...c]=await Promise.all([y(t),...i.map(o)]),l=i.filter((e,t)=>c[t].load),u=i.filter((e,t)=>c[t].clientLoad);return{file:t,pattern:r,name:C(r),layouts:i,errors:a,hasLoader:s.load,loaderLayouts:l,hasClientLoader:s.clientLoad,clientLoaderLayouts:u}}))}function k(e,t,n){if(e.length===0){console.warn(`[ilha:pages] No pages found in ${t}`);return}let r=new Map,i=new Map,a=[];for(let t of e){let e=r.get(t.pattern);e?a.push(`Duplicate route pattern "${t.pattern}"\n first: ${e}\n second: ${t.file}\n The first match wins — the second page will never be reached.`):r.set(t.pattern,t.file);let n=i.get(t.name);n?a.push(`Registry name collision: "${t.name}" is used by both\n ${n}\n ${t.file}\n Hydration may not work correctly for one of these routes.`):i.set(t.name,t.file)}if(a.length!==0){if(n)throw Error(`[ilha:pages] Route validation failed:\n\n${a.join(`
|
|
2
|
+
|
|
3
|
+
`)}`);for(let e of a)console.warn(`[ilha:pages] ${e}`)}}function A(e){return{serverFile:o(e,`pages.server.ts`),clientFile:o(e,`pages.client.ts`),loadersFile:o(e,`loaders.ts`)}}async function j(e,t,n={}){let r=n.mode??`spa`,i=n.interceptLinks,a=r===`static`,o=T(await O(e));k(o,e,n.strict===!0),await d(t,{recursive:!0});let{serverFile:s,clientFile:c,loadersFile:l}=A(t),u=await F(s,M(o,s)),f=await F(c,N(o,c,{isStatic:a,interceptLinks:i}));a||await F(l,P(o,l,s)),(u||f)&&await I(t)}function M(e,t){let n=e=>{let n=h(s(i(t),e));return n.startsWith(`.`)?n:`./${n}`},r=[`import { router, wrapLayout, wrapError } from "@ilha/router";`,`import type { Island } from "ilha";`],a=[],o=[],c=[];for(let[t,i]of e.entries()){r.push(`import { default as _page${t} } from ${JSON.stringify(n(i.file))};`);for(let[e,a]of i.layouts.entries())r.push(`import { default as _layout${t}_${e} } from ${JSON.stringify(n(a))};`);for(let[e,a]of i.errors.entries())r.push(`import { default as _error${t}_${e} } from ${JSON.stringify(n(a))};`);let s=`_page${t}`;for(let e=i.errors.length-1;e>=0;e--)s=`wrapError(_error${t}_${e}, ${s})`;for(let e=i.layouts.length-1;e>=0;e--)s=`wrapLayout(_layout${t}_${e}, ${s})`;let l=`_wrapped${t}`;a.push(`const ${l} = ${s};`),o.push(` ${JSON.stringify(i.name)}: ${l}`+(t<e.length-1?`,`:``)),c.push(` .route(${JSON.stringify(i.pattern)}, ${l})`+(i.hasLoader||i.loaderLayouts.length>0?`.markLoader(${JSON.stringify(i.pattern)})`:``)),i.errors.length>0&&c.push(` .errorBoundary(${JSON.stringify(i.pattern)}, _error${t}_${i.errors.length-1})`)}return[`// @generated by @ilha/router — do not edit`,`// Server module. Use for SSR and SSG/prerender.`,`// Import via: import { pageRouter, registry } from "ilha:pages/server";`,``,...r,``,...a,``,`export const registry: Record<string, Island<any, any>> = {`,...o,`};`,``,`export const pageRouter = router()`,...c,` ;`].join(`
|
|
4
|
+
`)}function N(e,t,n){let{isStatic:r,interceptLinks:a}=n,o=e=>{let n=h(s(i(t),e));return n.startsWith(`.`)?n:`./${n}`},c=e=>`${o(e)}?client`,l=e=>`${o(e)}?client-loader`,u=!1,d=r?[`import { router as _router, wrapLayout, wrapError } from "@ilha/router";`,`import type { Island } from "ilha";`]:[`import { router, wrapLayout, wrapError } from "@ilha/router";`,`import type { Island } from "ilha";`],f=[],p=[],m=[];for(let[t,n]of e.entries()){d.push(`import { default as _page${t} } from ${JSON.stringify(c(n.file))};`);for(let[e,r]of n.layouts.entries())d.push(`import { default as _layout${t}_${e} } from ${JSON.stringify(c(r))};`);for(let[e,r]of n.errors.entries())d.push(`import { default as _error${t}_${e} } from ${JSON.stringify(c(r))};`);let i=`_page${t}`;for(let e=n.errors.length-1;e>=0;e--)i=`wrapError(_error${t}_${e}, ${i})`;for(let e=n.layouts.length-1;e>=0;e--)i=`wrapLayout(_layout${t}_${e}, ${i})`;let a=`_wrapped${t}`;if(f.push(`const ${a} = ${i};`),p.push(` ${JSON.stringify(n.name)}: ${a}`+(t<e.length-1?`,`:``)),!r){m.push(` .route(${JSON.stringify(n.pattern)}, ${a})`+(n.hasLoader||n.loaderLayouts.length>0?`.markLoader(${JSON.stringify(n.pattern)})`:``));let e=[];for(let[r,i]of n.clientLoaderLayouts.entries()){let n=`_cl${t}_l${r}`;d.push(`import { clientLoad as ${n} } from ${JSON.stringify(l(i))};`),e.push(n)}if(n.hasClientLoader){let r=`_cl${t}`;d.push(`import { clientLoad as ${r} } from ${JSON.stringify(l(n.file))};`),e.push(r)}if(e.length>0){let t=e.length===1?e[0]:`composeLoaders([${e.join(`, `)}])`;e.length>1&&(u=!0),m.push(` .clientLoader(${JSON.stringify(n.pattern)}, ${t})`)}n.errors.length>0&&m.push(` .errorBoundary(${JSON.stringify(n.pattern)}, _error${t}_${n.errors.length-1})`)}}u&&(d[0]=d[0].replace(`{ router`,`{ composeLoaders, router`));let g=r?`_router({ mode: "static" })`:`router(${a===!1?`{ interceptLinks: false }`:``})`,_=[`// @generated by @ilha/router — do not edit`,`// Client module. Use for browser hydration.`,`// Import via: import { pageRouter, registry } from "ilha:pages/client";`,``,...d,``,...f,``,`export const registry: Record<string, Island<any, any>> = {`,...p,`};`,``];return r?_.push(`export const pageRouter = ${g};`):_.push(`export const pageRouter = ${g}`,...m,` ;`),_.join(`
|
|
5
|
+
`)}function P(e,t,n){let r=e=>{let n=h(s(i(t),e));return n.startsWith(`.`)?n:`./${n}`},a=e.filter(e=>e.hasLoader||e.loaderLayouts.length>0);if(a.length===0)return[`// @generated by @ilha/router — do not edit`,`// This project has no loader exports; this file is intentionally empty.`,``,`export {};`,``].join(`
|
|
6
|
+
`);let o=r(n).replace(/\.tsx?$/,``),c=[`import { pageRouter } from ${JSON.stringify(o)};`],l=!1,u=[];for(let[e,t]of a.entries()){let n=[];for(let[i,a]of t.loaderLayouts.entries()){let t=`_p${e}_l${i}`;c.push(`import { load as ${t} } from ${JSON.stringify(r(a))};`),n.push(t)}if(t.hasLoader){let i=`_p${e}`;c.push(`import { load as ${i} } from ${JSON.stringify(r(t.file))};`),n.push(i)}let i=n.length===1?n[0]:`composeLoaders([${n.join(`, `)}])`;n.length>1&&(l=!0),u.push(`pageRouter.attachLoader(${JSON.stringify(t.pattern)}, ${i});`)}return l&&c.unshift(`import { composeLoaders } from "@ilha/router";`),[`// @generated by @ilha/router — do not edit`,`// Server-only. Import this module from your SSR entry to wire loaders`,`// onto pageRouter. Importing it from the client is a no-op but wastes`,`// bundle size — rely on the default build pipeline to keep it out.`,``,...c,``,...u,``].join(`
|
|
7
|
+
`)}async function F(e,t){try{if(await f(e,`utf8`)===t)return!1}catch{}return await m(e,t,`utf8`),!0}async function I(e){await F(o(e,`pages.d.ts`),[`// @generated by @ilha/router — do not edit`,``,`declare module "ilha:pages/server" {`,` import type { RouterBuilder } from "@ilha/router";`,` import type { Island } from "ilha";`,` export const pageRouter: RouterBuilder;`,` export const registry: Record<string, Island<any, any>>;`,`}`,``,`declare module "ilha:pages/client" {`,` import type { RouterBuilder } from "@ilha/router";`,` import type { Island } from "ilha";`,` export const pageRouter: RouterBuilder;`,` export const registry: Record<string, Island<any, any>>;`,`}`,``,`declare module "ilha:loaders" {`,` // Side-effect-only module. Importing it attaches loaders to pageRouter.`,`}`,``].join(`
|
|
8
|
+
`))}const L=`\0ilha:pages/server`,R=`\0ilha:pages/client`,z=`\0ilha:loaders`,B=[L,R,z];function V(e){try{return JSON.parse(t(e,`utf8`))}catch{return null}}function H(t,n){let r=t;for(;;){let t=o(r,`node_modules`,n,`package.json`);if(e(t))return V(t);let a=i(r);if(a===r)return null;r=a}}function U(e){let t=V(o(e,`package.json`));if(!t)return[];let n={...t.dependencies??{},...t.devDependencies??{}},r=[];for(let t of Object.keys(n)){if(t===`ilha`)continue;let n=H(e,t);if(!n)continue;let i=n.peerDependencies??{},a=n.dependencies??{};(`ilha`in i||`ilha`in a)&&r.push(t)}return r}function W(e,t){let n=c(e,t.dir??`src/pages`),r=c(e,t.outDir??`.ilha`),{serverFile:i,clientFile:a,loadersFile:o}=A(r);return{pagesDir:n,outDir:r,serverFile:i,clientFile:a,loadersFile:o}}function G(e){let t,n,i,a,o,s=r=>{({pagesDir:t,outDir:n,serverFile:i,clientFile:a,loadersFile:o}=W(r,e))},c=async()=>{try{await j(t,n,{mode:e.mode,interceptLinks:e.interceptLinks,strict:e.strict})}catch(t){if(console.error(`[ilha:pages] codegen failed:`,t),e.strict)throw t}},u=e=>e===t||e.startsWith(t+l);return{get pagesDir(){return t},get outDir(){return n},get serverFile(){return i},get clientFile(){return a},get loadersFile(){return o},setPaths:s,regen:c,shouldRegenOnChange:e=>{if(!u(e))return!1;let t=r(e);return t.startsWith(`+`)||/\.(ts|tsx)$/.test(t)},isUnderPagesDir:u}}async function K(e,t,n){n(t)&&await e.regen()}function q(e,t,n){if(t===`ilha:pages/server`)return L;if(t===`ilha:pages/client`)return R;if(t===`ilha:loaders`)return z;for(let r of[`?client-loader`,`?client`]){if(!t.endsWith(r))continue;let i=t.slice(0,-r.length),a=n?c(n.replace(/\?.*$/,``),`..`,i):c(i);return!e.pagesDir||!e.isUnderPagesDir(a)?void 0:a+r}}function J(e,t){if(t===`\0ilha:pages/server`){let t=e.serverFile.replace(/\.tsx?$/,``);return`export { pageRouter, registry } from ${JSON.stringify(t)};`}if(t===`\0ilha:pages/client`){let t=e.clientFile.replace(/\.tsx?$/,``);return`export { pageRouter, registry } from ${JSON.stringify(t)};`}if(t===`\0ilha:loaders`){let t=e.loadersFile.replace(/\.tsx?$/,``);return`import ${JSON.stringify(t)};`}if(t.endsWith(`?client-loader`)){let e=t.slice(0,-14);return`export { clientLoad } from ${JSON.stringify(e)};`}if(t.endsWith(`?client`)){let e=t.slice(0,-7);return`export { default } from ${JSON.stringify(e)};`}}function Y(e,t){return async n=>{e.isUnderPagesDir(n)&&(await e.regen(),await t())}}function X(t,r){let i=null,a=null,s=!1,c=()=>{i=n(t.pagesDir,{recursive:!0},(e,n)=>{n&&r(o(t.pagesDir,n))})};return e(t.pagesDir)?c():(a=setInterval(()=>{s||!e(t.pagesDir)||(clearInterval(a),a=null,c(),r(o(t.pagesDir,`.`)))},1e3),a.unref?.()),()=>{s=!0,a&&clearInterval(a),i?.close()}}const Z=u((e={})=>{let t=G(e);return{name:`ilha:pages`,async buildStart(){t.pagesDir||t.setPaths(process.cwd()),this.addWatchFile?.(t.pagesDir),await t.regen()},async watchChange(e){await K(t,e,e=>t.shouldRegenOnChange(e))},resolveId(e,n){return q(t,e,n)},load(e){return J(t,e)},vite:{config(e){let t=[`ilha`,`@ilha/store`,`@ilha/router`,`alien-signals`,...U(e.root?c(e.root):process.cwd())],n=e.ssr?.noExternal,r=n===!0?!0:[...new Set([...Array.isArray(n)?n:n==null?[]:[n],...t])];return{resolve:{dedupe:[...new Set([...e.resolve?.dedupe??[],...t])]},ssr:{noExternal:r},optimizeDeps:{...e.optimizeDeps,include:[...new Set([...e.optimizeDeps?.include??[],`ilha`,`ilha/jsx-runtime`,`ilha/jsx-dev-runtime`,`@ilha/store`,`alien-signals`])]}}},configResolved(e){t.setPaths(e.root)},configureServer(e){e.watcher.add(t.pagesDir);let n=Y(t,async()=>{for(let t of B){let n=e.moduleGraph.getModuleById(t);n&&e.moduleGraph.invalidateModule(n)}e.hot.send({type:`full-reload`})});e.watcher.on(`add`,n),e.watcher.on(`addDir`,n),e.watcher.on(`unlink`,n),e.watcher.on(`change`,async e=>{t.shouldRegenOnChange(e)&&await n(e)})}},rspack(e){t.setPaths(e.options.context??process.cwd());let n=Y(t,()=>{e.watching&&e.invalidate()}),r;e.hooks.watchRun.tap(`ilha:pages`,()=>{r?.(),r=X(t,n)}),e.hooks.shutdown.tap(`ilha:pages`,()=>r?.())}}});export{Z as t};
|
package/dist/plugin.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export declare const RESOLVED_LOADERS = "\0ilha:loaders";
|
|
|
8
8
|
export declare const RESOLVED_VIRTUAL_IDS: readonly ["\0ilha:pages/server", "\0ilha:pages/client", "\0ilha:loaders"];
|
|
9
9
|
/** Query suffix used on page/layout imports in the client file. */
|
|
10
10
|
export declare const CLIENT_QUERY = "?client";
|
|
11
|
+
/** Query suffix that re-exports a page/layout's `clientLoad` for the browser bundle. */
|
|
12
|
+
export declare const CLIENT_LOADER_QUERY = "?client-loader";
|
|
11
13
|
export interface IlhaPagesOptions {
|
|
12
14
|
/** Directory containing page files. Default: `src/pages` */
|
|
13
15
|
dir?: string;
|
package/dist/rolldown.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{A as e,k as t}from"./src-8LBo_Ieg.js";import{t as n}from"./plugin-BW2tnuyF.js";function r(e={}){return n.rolldown(e)}export{n as ilhaPages,r as pages,t as wrapError,e as wrapLayout};
|
package/dist/rspack.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{A as e,k as t}from"./src-8LBo_Ieg.js";import{t as n}from"./plugin-BW2tnuyF.js";function r(e={}){return n.rspack(e)}export{n as ilhaPages,r as pages,t as wrapError,e as wrapLayout};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import e,{ISLAND_MOUNT_INTERNAL as t,context as n,html as r,mount as i}from"ilha";import{addRoute as a,createRouter as o,findRoute as s}from"rou3";const c=typeof window<`u`&&typeof document<`u`,l={readLocation(){return c?{pathname:location.pathname,search:location.search,hash:location.hash}:{pathname:`/`,search:``,hash:``}},push(e,t){c&&history.pushState(t??null,``,e)},replace(e,t){c&&history.replaceState(t??null,``,e)},onChange(e){return c?(window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)):()=>{}},toLinkHref(e){return e},extractLogicalPath(e){let t=e.getAttribute(`href`);return!t||e.protocol&&!/^(http:|https:)$/.test(e.protocol)||t.startsWith(`#`)||e.hostname&&(e.hostname!==location.hostname||e.protocol!==location.protocol)?null:e.pathname+e.search+e.hash}};function u(e){let t=e.startsWith(`#`)?e.slice(1):e,n=t===``?`/`:t.startsWith(`/`)?t:`/`+t,r=new URL(n,`http://_`);return{pathname:r.pathname,search:r.search,hash:r.hash}}const d={readLocation(){return c?u(location.hash):{pathname:`/`,search:``,hash:``}},push(e,t){c&&history.pushState(t??null,``,e.startsWith(`#`)?e:`#`+e)},replace(e,t){c&&history.replaceState(t??null,``,e.startsWith(`#`)?e:`#`+e)},onChange(e){return c?(window.addEventListener(`popstate`,e),window.addEventListener(`hashchange`,e),()=>{window.removeEventListener(`popstate`,e),window.removeEventListener(`hashchange`,e)}):()=>{}},toLinkHref(e){return e.startsWith(`#`)?e:`#`+e},extractLogicalPath(e){let t=e.getAttribute(`href`);if(!t||e.protocol&&!/^(http:|https:)$/.test(e.protocol))return null;if(t.startsWith(`#`)){let e=t.slice(1);return e===``||!e.startsWith(`/`)?null:e}if(/^https?:\/\//i.test(t))try{let e=new URL(t);if(e.origin!==location.origin||!e.hash||e.hash===`#`)return null;let n=e.hash.slice(1);return n.startsWith(`/`)?n:null}catch{return null}return t}};let f=`history`,p=l;function m(e){f=e,p=e===`hash`?d:l}function h(){return f}function g(){return p}const _=typeof window<`u`&&typeof document<`u`;function v(e){return e}var y=class{__ilhaRedirect=!0;to;status;constructor(e,t=302){this.to=e,this.status=t}},b=class{__ilhaLoaderError=!0;status;message;constructor(e,t){this.status=e,this.message=t}};function x(e,t=302){throw new y(e,t)}function ee(e,t){throw new b(e,t)}function S(e){return e.length===0?async()=>({}):e.length===1?e[0]:async t=>{let n=await Promise.all(e.map(e=>e(t)));return Object.assign({},...n)}}const C=Symbol.for(`ilha.router.wrapLayout.leaf`),te=Symbol.for(`ilha.router.wrapLayout.handler`);function ne(e){let t=e.match(/^<([a-zA-Z][\w-]*)\s[^>]*>([\s\S]*)<\/\1>\s*$/);return t?t[2]:e}function re(e){let t=e.match(/^<([a-zA-Z][\w-]*)\s([^>]*)>/);return t?{tag:t[1],attrs:t[2]}:null}const ie=/<(pre|script|style|textarea)\b/i;function ae(e,t,n){let r=RegExp(`<${n}\\b`,`gi`),i=RegExp(`</${n}>`,`gi`),a=1,o=t;for(;a>0&&o<e.length;){r.lastIndex=o,i.lastIndex=o;let t=r.exec(e),n=i.exec(e);if(!n)return null;if(t&&t.index<n.index)a+=1,o=t.index+t[0].length;else{if(--a,a===0)return n.index+n[0].length;o=n.index+n[0].length}}return o}function oe(e,t){let n=t;for(;n<e.length;){if(e.startsWith(`<!--`,n)){let t=e.indexOf(`-->`,n);if(t===-1)return null;n=t+3;continue}let t=e.slice(n).match(ie);if(t&&t.index!=null&&t.index>=0){let r=n+t.index,i=!1,a=n;for(;a<r;){let t=e.indexOf(`<!--`,a);if(t===-1||t>=r)break;let o=e.indexOf(`-->`,t);if(o===-1)return null;if(r<o+3){n=o+3,i=!0;break}a=o+3}if(i)continue;let o=e.indexOf(`<div`,n),s=e.indexOf(`</div>`,n);if(!(o!==-1&&o<r||s!==-1&&s<r)){let i=t[1].toLowerCase(),a=ae(e,r+t[0].length,i);if(a===null)return null;n=a;continue}}let r=e.indexOf(`<div`,n),i=e.indexOf(`</div>`,n);return i===-1&&r===-1?null:r===-1||i!==-1&&i<r?{kind:`close`,index:i}:{kind:`open`,index:r}}return null}function se(e){let t=[];for(let n of e.matchAll(/<div\s[^>]*data-ilha-slot="k:page"[^>]*>/g)){let r=n.index+n[0].length,i=1,a=r;for(;i>0;){let n=oe(e,a);if(!n)break;if(n.kind===`open`)i+=1,a=n.index+4;else{if(--i,i===0){t.push({openEnd:r,closeStart:n.index});break}a=n.index+6}}}return t}function w(e,t,n){let r=se(e);if(r.length===0)return e;let i=n===`innermost`?r[r.length-1]:r[0];return e.slice(0,i.openEnd)+t+e.slice(i.closeStart)}function ce(e,t){let n=e[te];if(!n)return e.toString(t);let r=e[C]??e;return n(Object.assign(r.key(`page`),{toString:()=>``})).toString(t)}async function le(e,t,n,r){let i=ne(await t.hydratable(n,r));return w(w(ce(e,n),``,`innermost`),i,`innermost`)}function ue(e,n){let r=n[C]??n,i=r===n?null:n,a=e(Object.assign(n.key(`page`),{toString:n.toString.bind(n)}));a[C]=r,a[te]=e;function o(e){let t=[...e.querySelectorAll(`[data-ilha-slot="k:page"]`)].filter(t=>{let n=t.closest(`[data-ilha]`);return n===null||n===e});return t.length===0?e:t[t.length-1]}function s(e,t){let n=e=>{delete e._skipOnMount,t.setAttribute(`data-ilha-state`,JSON.stringify(e))};if(t.hasAttribute(`data-ilha-state`)){let r=e.getAttribute(`data-ilha-state`);if(r)try{n(JSON.parse(r));return}catch{}let i=t.getAttribute(`data-ilha-state`);if(i)try{let e=JSON.parse(i);delete e._skipOnMount,t.setAttribute(`data-ilha-state`,JSON.stringify(e))}catch{}return}let r=e.getAttribute(`data-ilha-state`);if(r)try{n(JSON.parse(r));return}catch{}t.childNodes.length>0&&t.setAttribute(`data-ilha-state`,`{}`)}function c(e){let n=e[t];if(typeof n!=`function`)return;e[t]=(e,t)=>{let r=e.closest(`[data-ilha]`);return r&&r!==e&&s(r,e),n(e,t)};let r=e.mount.bind(e);e.mount=(e,t)=>{let n=e.closest(`[data-ilha]`);return n&&n!==e&&s(n,e),r(e,t)}}c(r);let l=a.mount.bind(a),u=a[t];function d(e){s(e,o(e))}return a.mount=(e,t)=>(d(e),l(e,t)),a[t]=(e,t)=>(d(e),typeof u==`function`?u(e,t):{unmount:l(e,t),updateProps:()=>{}}),a.hydratable=async(e,t)=>{if(!t?.name)throw Error(`wrapLayout: hydratable requires options.name`);let n=e??{},o=await r.hydratable(n,t),s=re(o);if(!s)return o;let c=ne(o);i&&(c=await le(i,r,n,t));let l=w(w(ce(a,n),``,`first`),c,`first`);return`<${s.tag} ${s.attrs}>${l}</${s.tag}>`},a}function de(n,r){let i=e.render(()=>{try{return r.toString()}catch(e){let t={path:A(),params:j(),search:M(),hash:N()};return n({message:e.message,status:e.status,stack:e.stack},t).toString()}});return i.mount=(e,t)=>{try{return r.mount(e,t)}catch(r){let i={path:A(),params:j(),search:M(),hash:N()},a=n({message:r.message,status:r.status,stack:r.stack},i);return e.innerHTML=a.toString(),a.mount(e,t)}},i[t]=(e,i)=>{try{let n=r[t];return typeof n==`function`?n(e,i):{unmount:r.mount(e,i),updateProps:()=>{}}}catch(t){let r={path:A(),params:j(),search:M(),hash:N()},a=n({message:t.message,status:t.status,stack:t.stack},r);return e.innerHTML=a.toString(),{unmount:a.mount(e,i),updateProps:()=>{}}}},i.hydratable=async(e,t)=>{if(!t?.name)throw Error(`wrapError: hydratable requires options.name`);return r.hydratable(e??{},t)},i}function fe(e){return e}function pe(e){let t=new Map;for(let[n,r]of Object.entries(e))t.has(r)||t.set(r,n);return t}const me=`/__ilha/loader`,T=new Map;let he=!1;async function E(e){let t=document.startViewTransition?.bind(document);if(!he||!t)return e();let n,r,i=!1;if(await t(()=>{try{n=e()}catch(e){throw i=!0,r=e,e}}).updateCallbackDone?.catch(()=>{}),i)throw r;return n}async function ge(e,t,n,r){let i=new URL(n,location.origin),a=z(t),o=[],s=await pt(e,i,a,ft(i),r??new AbortController().signal,e=>o.push(e));if(r?.throwIfAborted(),s.kind===`redirect`){let e=ut(s.to,i,Ge);return e.ok?{kind:`redirect`,to:e.to,status:s.status}:(console.warn(`[ilha-router] Blocked unsafe redirect target "${s.to}". Set allowExternalRedirects: true to allow cross-origin redirects.`),{kind:`error`,status:500,message:`Unsafe redirect target`})}return s.kind===`data`?o.length>0?{kind:`data`,data:s.data,headEntries:o}:{kind:`data`,data:s.data}:s}async function _e(e,t){let n=T.get(e);if(n&&(T.delete(e),Date.now()<=n.expires))try{t?.throwIfAborted();let e=await n.promise;return t?.throwIfAborted(),e}catch(e){if(e?.name===`AbortError`)throw e}let r=e.split(`?`)[0]??``,i=s(R,`GET`,r),a=i?.data?.clientLoader??i?.data?.loader;if(a)return ge(a,i?.params,e,t);let o=`${me}?path=${encodeURIComponent(e)}`;try{let e=await fetch(o,{signal:t,headers:{accept:`application/json`}});if(!e.ok){try{let t=await e.json();if(t&&typeof t==`object`&&`kind`in t)return t}catch{}return{kind:`error`,status:e.status,message:e.statusText}}return await e.json()}catch(e){if(e?.name===`AbortError`)throw e;return{kind:`error`,status:0,message:e?.message??`network error`}}}function D(e){if(!_)return;let t=T.get(e);if(t&&Date.now()<=t.expires)return;let n=e.split(`?`)[0]??``;if(!s(R,`GET`,n)?.data?.hasLoader)return;let r=_e(e).catch(e=>({kind:`error`,status:0,message:e?.message??`prefetch failed`}));T.set(e,{promise:r,expires:Date.now()+3e4})}async function ve(e,t,n,r,i,a){if(!e){if(W){let e=W;return E(()=>{t.innerHTML=`<div data-router-view data-router-not-found>${e.toString()}</div>`;let n=t.firstElementChild;return n?e.mount(n):()=>{}})}return await E(()=>{t.innerHTML=`<div data-router-empty></div>`}),()=>{}}let o=s(R,`GET`,n.split(`?`)[0]??``),c=!!o?.data?.hasLoader,l={},u=c?await _e(n,r):{kind:`data`,data:{}};if(u.kind===`redirect`)return Ue(u.to),()=>{};if(u.kind===`error`){let e=o?.data?.errorHandler;if(e)return E(()=>ye(e,t,u.status,u.message));let n=it(u.message);return await E(()=>{t.innerHTML=`<div data-router-view data-router-error="${u.status}">${n}</div>`}),()=>{}}if(u.kind===`not-found`)return await E(()=>{t.innerHTML=`<div data-router-empty></div>`}),()=>{};l=u.data;let d={entries:[...u.headEntries??[]]};if(!i){console.warn(`[ilha-router] No registry provided for client-side navigation. Island will not be interactive.`);let n=await Z(d,()=>e.toString(l));return await E(()=>{X(d.entries),t.innerHTML=`<div data-router-view>${n}</div>`}),()=>{}}let f=a?.get(e)??Object.entries(i).find(([,t])=>t===e)?.[0];if(!f){console.warn(`[ilha-router] Island not found in registry for client-side navigation.`);let n=await Z(d,()=>e.toString(l));return await E(()=>{X(d.entries),t.innerHTML=`<div data-router-view>${n}</div>`}),()=>{}}let p=await Z(d,()=>e.hydratable(l,{name:f,as:`div`,snapshot:!0}));return E(()=>{X(d.entries),t.innerHTML=`<div data-router-view>${p}</div>`;let n=t.querySelector(`[data-ilha="${f}"]`);return n?e.mount(n):()=>{}})}function ye(e,t,n,r){try{let i=e({message:r,status:n},{path:A(),params:j(),search:M(),hash:N()});t.innerHTML=`<div data-router-view data-router-error="${n}">${i.toString()}</div>`;let a=t.firstElementChild;return a?i.mount(a):()=>{}}catch(e){return console.error(`[ilha-router] error boundary threw while rendering a loader error:`,e),t.innerHTML=`<div data-router-view data-router-error="${n}"></div>`,()=>{}}}let O=null,be=null;async function xe(){return O||(be||=import(`node:async_hooks`).then(({AsyncLocalStorage:e})=>(O=new e,O)),be)}_||xe().catch(()=>{});function k(){return _?null:O?.getStore()??null}function Se(){return{path:``,params:{},search:``,hash:``,island:null}}const Ce=n(`router.path`,``),we=n(`router.params`,{}),Te=n(`router.search`,``),Ee=n(`router.hash`,``);function A(e){let t=k();return arguments.length>0?t?t.path=e:(Ce(e),e):t?t.path:Ce()}function j(e){let t=k();return arguments.length>0?t?t.params=e:(we(e),e):t?t.params:we()}function M(e){let t=k();return arguments.length>0?t?t.search=e:(Te(e),e):t?t.search:Te()}function N(e){let t=k();return arguments.length>0?t?t.hash=e:(Ee(e),e):t?t.hash:Ee()}const P=n(`router.navigating`,0);function De(){return P()>0}let F=null;function Oe(){return!_||!F?Promise.resolve():F()}function I(){if(!_)return()=>{};P(P()+1);let e=!1;return()=>{e||(e=!0,P(Math.max(0,P()-1)))}}function ke(){return{path:A,params:j,search:M,hash:N,navigating:De}}const Ae=n(`router.active`,null);function L(e){let t=k();return arguments.length>0?t?t.island=e??null:(Ae(e??null),e??null):t?t.island:Ae()}let R=o();function z(e){let t={};if(e)for(let[n,r]of Object.entries(e))t[n]=decodeURIComponent(r);return t}function je(e,t=R){let n=typeof e==`string`?new URL(e,`http://localhost`):e,r=s(t,`GET`,n.pathname);A(n.pathname),j(z(r?.params)),M(n.search),N(n.hash),L(r?.data?.island??null)}function Me(){let e=g().readLocation(),t=s(R,`GET`,e.pathname);A(e.pathname),j(z(t?.params)),M(e.search),N(e.hash),L(t?.data?.island??null)}function B(){_&&Me()}const Ne=new Set,Pe=new Set;function Fe(e){return Ne.add(e),()=>Ne.delete(e)}function Ie(e){return Pe.add(e),()=>Pe.delete(e)}function Le(e){for(let t of Pe)try{t(e)}catch(e){console.error(`[ilha-router] afterNavigate hook threw:`,e)}}const Re=new Map;let ze=0,V=0;function H(){if(!_)return 0;let e=history.state;return typeof e?.__ilhaNavKey==`number`?e.__ilhaNavKey:0}function Be(){Re.set(H(),{x:window.scrollX,y:window.scrollY})}function Ve(e){requestAnimationFrame(()=>{if(e&&e!==`#`){let t=document.getElementById(e.slice(1))??document.querySelector(`a[name="${Y(e.slice(1))}"]`);if(t){t.scrollIntoView();return}}window.scrollTo(0,0)})}function He(){let e=Re.get(H());e&&requestAnimationFrame(()=>window.scrollTo(e.x,e.y))}function U(e,t={}){if(!_)return;let n=g(),r=n.readLocation(),i=r.pathname+r.search+r.hash;if(e===i)return;let a=t.replace?`replace`:`push`,o=!1;for(let t of Ne)try{t({from:i,to:e,type:a,cancel:()=>o=!0})}catch(e){console.error(`[ilha-router] beforeNavigate hook threw:`,e)}o||(t.replace?n.replace(e,{__ilhaNavKey:H()}):(Be(),ze=Math.max(ze+1,H()+1),n.push(e,{__ilhaNavKey:ze})),V=H(),Me(),t.scroll!==!1&&Ve(n.readLocation().hash),Le({from:i,to:e,type:a}))}function Ue(e){if(/^https?:\/\//i.test(e)){try{let t=new URL(e);if(t.origin===location.origin){U(t.pathname+t.search+t.hash,{replace:!0});return}}catch{return}location.assign(e);return}U(e,{replace:!0})}function We(e=document,t={}){if(!_)return()=>{};let n=t.prefetch!==!1;function r(e,t){let n=e.getAttribute(`target`)===`_blank`,r=!!t&&(t.ctrlKey||t.metaKey||t.shiftKey||t.altKey),i=e.hasAttribute(`data-no-intercept`),a=e.hasAttribute(`download`),o=/\bexternal\b/i.test(e.getAttribute(`rel`)??``);return n||r||i||a||o?null:g().extractLogicalPath(e)}let i=e=>{if(e.defaultPrevented||typeof e.button==`number`&&e.button!==0)return;let t=e.target.closest(`a`);if(!t)return;let n=r(t,e);n!==null&&(e.preventDefault(),U(n))},a=e=>{let t=e.target.closest(`a`);if(!t)return;let n=t.getAttribute(`data-prefetch`);if(n===null||n===`false`)return;let i=r(t);i!==null&&D(i.split(`#`)[0]??i)};return e.addEventListener(`click`,i),n&&e.addEventListener(`mouseover`,a,{passive:!0}),()=>{e.removeEventListener(`click`,i),n&&e.removeEventListener(`mouseover`,a)}}let W=null,Ge=!1;const G=e.render(()=>{let e=L();return e?`<div data-router-view>${e.toString()}</div>`:W?`<div data-router-view data-router-not-found>${W.toString()}</div>`:`<div data-router-empty></div>`}),Ke=e.state(`href`,``).state(`label`,``).on(`[data-link]@click`,({state:e,event:t})=>{t.preventDefault(),U(e.href())}).on(`[data-link]@mouseenter`,({state:e})=>{let t=e.href();if(t){if(/^https?:\/\//i.test(t))try{let e=new URL(t);if(e.origin!==location.origin)return;D(e.pathname+e.search);return}catch{return}D(t)}}).render(({state:e})=>r`<a data-link data-prefetch href="${()=>g().toLinkHref(e.href())}"
|
|
2
|
+
>${e.label}</a
|
|
3
|
+
>`);function qe(e,t={}){if(t.exact===!1){let t=A(),n=e.endsWith(`/`)?e.slice(0,-1):e;return t===n||t===n+`/`||t.startsWith(n+`/`)}let n=s(R,`GET`,A());return n?n.data.pattern===e:!1}const K=`data-ilha-head`,Je=`data-ilha-router-html`,Ye=`data-ilha-router-body`;let q=null,J=null,Xe=null;async function Ze(){return J||(Xe||=import(`node:async_hooks`).then(({AsyncLocalStorage:e})=>(J=new e,J)),Xe)}function Qe(){return _?q:J?.getStore()??null}function $e(e){let t=Qe();if(!t){_||console.warn(`[ilha-router] head() called outside an SSR render window — ignored.`);return}t.entries.push(e)}function Y(e){return typeof CSS<`u`&&typeof CSS.escape==`function`?CSS.escape(e):e.replace(/\\/g,`\\\\`).replace(/"/g,`\\"`)}function et(e){return`charset`in e?`meta[charset][${K}]`:`name`in e?`meta[name="${Y(e.name)}"][${K}]`:`property`in e?`meta[property="${Y(e.property)}"][${K}]`:`http-equiv`in e?`meta[http-equiv="${Y(e[`http-equiv`])}"][${K}]`:null}function tt(e){return e.rel&&e.href?`link[rel="${Y(e.rel)}"][href="${Y(e.href)}"][${K}]`:null}function X(e){if(!_)return;let t,n,r=[],i=[],a={},o={};for(let s of e)s.title!==void 0&&(t=s.title),s.titleTemplate!==void 0&&(n=s.titleTemplate),s.meta&&r.push(...s.meta),s.link&&i.push(...s.link),s.htmlAttrs&&(a={...a,...s.htmlAttrs}),s.bodyAttrs&&(o={...o,...s.bodyAttrs});let s=st(t,n);s!==void 0&&(document.title=s);let c=ot(r,at),l=ot(i,e=>`${e.rel??``}:${e.href??``}`),u=new Set;for(let e of c){let t=et(e);if(!t)continue;let n=document.querySelector(t);n||(n=document.createElement(`meta`),n.setAttribute(K,``),document.head.appendChild(n));for(let[t,r]of Object.entries(e))n.setAttribute(t,r);u.add(n)}for(let e of l){let t=tt(e),n=t?document.querySelector(t):null;n||(n=document.createElement(`link`),n.setAttribute(K,``),document.head.appendChild(n));for(let[t,r]of Object.entries(e))n.setAttribute(t,r);u.add(n)}for(let e of[...document.head.querySelectorAll(`[${K}]`)])u.has(e)||e.remove();let d=document.documentElement,f=(d.getAttribute(Je)??``).split(/\s+/).filter(Boolean);for(let e of f)d.removeAttribute(e);let p=Object.keys(a);for(let[e,t]of Object.entries(a))d.setAttribute(e,t);p.length?d.setAttribute(Je,p.join(` `)):d.removeAttribute(Je);let m=document.body,h=(m.getAttribute(Ye)??``).split(/\s+/).filter(Boolean);for(let e of h)m.removeAttribute(e);let g=Object.keys(o);for(let[e,t]of Object.entries(o))m.setAttribute(e,t);g.length?m.setAttribute(Ye,g.join(` `)):m.removeAttribute(Ye)}async function Z(e,t){if(_){let n=q;q=e;try{return await t()}finally{q=n}}return await(await Ze()).run(e,()=>Promise.resolve(t()))}const nt={"&":`&`,"<":`<`,">":`>`,'"':`"`,"'":`'`};function rt(e){return String(e).replace(/[&<>"']/g,e=>nt[e])}function it(e){return String(e).replace(/[&<>]/g,e=>nt[e])}function Q(e){return Object.entries(e).map(([e,t])=>` ${e}="${rt(t)}"`).join(``)}function at(e){return`charset`in e?`charset`:`name`in e?`name:${e.name}`:`property`in e?`property:${e.property}`:`http-equiv`in e?`http-equiv:${e[`http-equiv`]}`:JSON.stringify(e)}function ot(e,t){let n=new Map;for(let r of e)n.set(t(r),r);return[...n.values()]}function st(e,t){return t===void 0?e:typeof t==`function`?t(e):t.replace(/%s/g,e??``)}function $(e){let t,n,r=[],i=[],a=[],o={},s={};for(let c of e)c.title!==void 0&&(t=c.title),c.titleTemplate!==void 0&&(n=c.titleTemplate),c.meta&&r.push(...c.meta),c.link&&i.push(...c.link),c.script&&a.push(...c.script),c.htmlAttrs&&(o={...o,...c.htmlAttrs}),c.bodyAttrs&&(s={...s,...c.bodyAttrs});let c=st(t,n),l=[];c!==void 0&&l.push(`<title>${rt(c)}</title>`);for(let e of ot(r,at))l.push(`<meta${Q({...e,[K]:``})} />`);for(let e of ot(i,e=>`${e.rel??``}:${e.href??``}`))l.push(`<link${Q({...e,[K]:``})} />`);for(let e of a){let{children:t,...n}=e,r=(t??``).replace(/<\/script/gi,`<\\/script`);l.push(`<script${Q(n)}>${r}<\/script>`)}return{headTags:l.join(`
|
|
4
|
+
`),htmlAttrs:Q(o),bodyAttrs:Q(s)}}function ct(e){return typeof e==`string`?new URL(e,`http://localhost`):e}function lt(){try{return typeof process<`u`&&!!process.env&&process.env.NODE_ENV!==`production`}catch{return!1}}function ut(e,t,n){if(e.startsWith(`/`)&&!e.startsWith(`//`))return{ok:!0,to:e};try{let r=new URL(e,t);return/^https?:$/.test(r.protocol)?r.origin===t.origin?{ok:!0,to:r.pathname+r.search+r.hash}:n?{ok:!0,to:r.href}:{ok:!1}:{ok:!1}}catch{return{ok:!1}}}function dt(e,t){let n=new AbortController,r=()=>n.abort(),i=e?.signal;i&&(i.aborted?r():i.addEventListener(`abort`,r,{once:!0}));let a;return t&&t>0&&(a=setTimeout(r,t)),{signal:n.signal,done:()=>{a!==void 0&&clearTimeout(a),i?.removeEventListener(`abort`,r)}}}function ft(e){try{return new Request(e.toString())}catch{return{url:e.toString(),headers:new Headers}}}async function pt(e,t,n,r,i,a){let o=[],s=a??(e=>o.push(e));try{let a=Promise.resolve(e({params:n,request:r,url:t,signal:i,head:s}));a.catch(()=>{});let c={kind:`data`,data:await Promise.race([a,new Promise((e,t)=>{let n=()=>t(new b(504,`Loader aborted or timed out`));i.aborted?n():i.addEventListener(`abort`,n,{once:!0})})])??{}};return o.length>0&&(c.head=$(o)),c}catch(e){return e instanceof y?{kind:`redirect`,to:e.to,status:e.status}:e instanceof b?{kind:`error`,status:e.status,message:e.message}:(console.error(`[ilha-router] loader failed:`,e),{kind:`error`,status:typeof e?.status==`number`?e.status:500,message:lt()?e?.message??`Loader failed`:`Internal error`})}}function mt(t={}){let n=t.mode??`spa`,r=t.interceptLinks!==!1,c=t.allowExternalRedirects===!0,l=t.loaderTimeout,u=[],d=o(),f=new Map,p=t.notFound??null;R=d,W=p,Ge=c,he=t.viewTransitions===!0;let m=null,v=null,y={route(e,t,n){let r=!!n,i={island:t,pattern:e,loader:n,hasLoader:r};return u.push({pattern:e,island:t,loader:n,hasLoader:r}),a(d,`GET`,e,i),f.set(e,i),y},attachLoader(e,t){let n=f.get(e);if(!n)return console.warn(`[ilha-router] attachLoader("${e}", …): pattern was never registered via .route(). The loader will be ignored.`),y;n.loader=t,n.hasLoader=!0;let r=u.find(t=>t.pattern===e);return r&&(r.loader=t,r.hasLoader=!0),y},clientLoader(e,t){let n=f.get(e);if(!n)return console.warn(`[ilha-router] clientLoader("${e}", …): pattern was never registered via .route(). The loader will be ignored.`),y;n.clientLoader=t,n.hasLoader=!0;let r=u.find(t=>t.pattern===e);return r&&(r.hasLoader=!0),y},errorBoundary(e,t){let n=f.get(e);return n?(n.errorHandler=t,y):(console.warn(`[ilha-router] errorBoundary("${e}", …): pattern was never registered via .route(). The boundary will be ignored.`),y)},markLoader(e){let t=f.get(e);if(!t)return console.warn(`[ilha-router] markLoader("${e}"): pattern was never registered via .route(). The loader marker will be ignored.`),y;t.hasLoader=!0;let n=u.find(t=>t.pattern===e);return n&&(n.hasLoader=!0),y},routes(){return u.map(e=>({...e}))},prime:B,hydrateStatic(e,t={}){if(!_)return()=>{};let n=t.root??document.body;B();let{unmount:r}=i(e,{root:n});return r},mount(t,{hydrate:i=!1,registry:a,interceptLinks:o}={}){if(!_)return console.warn(`[ilha-router] mount() called in a non-browser environment`),()=>{};let c=typeof t==`string`?document.querySelector(t):t;if(!c)return console.warn(`[ilha-router] No element found for selector "${t}"`),()=>{};if(Me(),V=H(),n===`static`)return console.warn(`[ilha-router] router.mount() called in static mode. Use router.hydrateStatic(registry) instead.`),()=>{};let l=!0,u=`scrollRestoration`in history?history.scrollRestoration:null;u!==null&&(history.scrollRestoration=`manual`),m=g().onChange(()=>{if(!l)return;let e=A()+M()+N();Re.set(V,{x:window.scrollX,y:window.scrollY}),V=H(),Me(),He(),Le({from:e,to:A()+M()+N(),type:`pop`})}),v=o??r?We(document):null;let d=null,f=null;if(i){h()===`hash`&&console.warn("[ilha-router] mount({ hydrate: true }) was called in hash mode. SSR + hydration assumes the server can render the active route, but in hash mode the server only ever sees the document URL. Use plain SPA mode (`mount(target)` without `hydrate: true`) for hash-mode apps.");let t=c.querySelector(`[data-router-view]`)??c,n=L(),r=a?pe(a):void 0,i=0,o=e.render(()=>{let e=L();if(e!==n){let o=++i;f?.abort(),f=new AbortController;let s=f.signal;queueMicrotask(async()=>{if(o!==i)return;let c=I();d?.(),d=null;try{let n=g().readLocation();d=await ve(e,t,n.pathname+n.search,s,a,r)}catch(e){if(e?.name===`AbortError`)return;console.error(`[ilha-router] navigation failed:`,e),t.innerHTML=`<div data-router-view data-router-error="500"></div>`;return}finally{c()}n=e})}return``}),p=document.createElement(`div`);p.style.display=`none`,c.appendChild(p);let _=o.mount(p);return(async()=>{let e=L();if(!e)return;let n=g().readLocation(),o=n.pathname+n.search,c=s(R,`GET`,n.pathname);if(c?.data?.clientLoader){let n=++i,s=new AbortController;f=s;try{let c=await ve(e,t,o,s.signal,a,r);n===i?d=c:c()}catch(e){e?.name!==`AbortError`&&console.error(`[ilha-router] initial client loader render failed:`,e)}return}let u=c?.data?.hasLoader?await _e(o):{kind:`data`,data:{}};if(u.kind===`redirect`||u.kind===`error`)return;let p=u.kind===`data`?u.data:{},m={entries:[...u.kind===`data`?u.headEntries??[]:[]]};await Z(m,()=>e.toString(p)),l&&X(m.entries)})(),F=async()=>{let e=L(),o=++i;f?.abort();let s=new AbortController;f=s;let c=I();try{let c=g().readLocation(),l=await ve(e,t,c.pathname+c.search,s.signal,a,r);o===i?(d?.(),d=l,n=e):l()}catch(e){e?.name!==`AbortError`&&console.error(`[ilha-router] invalidate failed:`,e)}finally{c()}},()=>{l=!1,++i,F=null,f?.abort(),_(),p.remove(),d?.(),v?.(),m?.(),v=null,m=null,u!==null&&(history.scrollRestoration=u)}}let p=null,y=null,b=0;d=G.mount(c);async function x(e,t){if(p?.(),p=null,y=e,!e){let e=c?.querySelector(`[data-router-not-found]`);W&&e&&(p=W.mount(e));return}let n=c?.querySelector(`[data-router-view]`);if(!n)return;let r=g().readLocation(),i=s(R,`GET`,r.pathname),a=i?.data?.hasLoader?await _e(r.pathname+r.search,t):{kind:`data`,data:{}};if(t.aborted)return;if(a.kind===`redirect`){Ue(a.to);return}if(a.kind===`error`){let e=i?.data?.errorHandler;if(e){p=await E(()=>ye(e,n,a.status,a.message));return}let t=it(a.message);await E(()=>{n.innerHTML=`<div data-router-error="${a.status}">${t}</div>`});return}let o=a.kind===`data`?a.data:{},l={entries:[...a.kind===`data`?a.headEntries??[]:[]]},u=await Z(l,()=>e.toString(o));p=await E(()=>(X(l.entries),n.innerHTML=u,e.mount(n,o)))}f=new AbortController,x(L(),f.signal).catch(e=>{e?.name!==`AbortError`&&console.error(`[ilha-router] initial mount failed:`,e)});let ee=e.render(()=>{let e=L();if(e!==y){let t=++b;f?.abort(),f=new AbortController;let n=f.signal;queueMicrotask(()=>{if(t!==b)return;let r=I();x(e,n).catch(e=>{e?.name!==`AbortError`&&console.error(`[ilha-router] navigation failed:`,e)}).finally(r)})}return``}),S=document.createElement(`div`);S.style.display=`none`,c.appendChild(S);let C=ee.mount(S);return F=async()=>{++b,f?.abort(),f=new AbortController;let e=I();try{await x(L(),f.signal)}catch(e){e?.name!==`AbortError`&&console.error(`[ilha-router] invalidate failed:`,e)}finally{e()}},()=>{l=!1,++b,F=null,f?.abort(),p?.(),C(),S.remove(),d?.(),v?.(),m?.(),v=null,m=null,u!==null&&(history.scrollRestoration=u)}},render(e){let t=()=>(je(e,d),G.toString());return!_&&O?O.run(Se(),t):t()},async renderHydratable(e,t,n={},r){let i=await this.renderResponse(e,t,n,r);return i.kind===`html`||i.kind===`error`?i.html:`<meta http-equiv="refresh" content="0; url=${rt(i.to)}">`},async renderResponse(e,t,n={},r){if(!_){let i=await xe();if(!i.getStore())return i.run(Se(),()=>b(e,t,n,r))}return b(e,t,n,r)},async runLoader(e,t){let n=ct(e),r=s(d,`GET`,n.pathname);if(!r?.data?.island)return{kind:`not-found`};if(!r.data.loader)return{kind:`data`,data:{}};let i=z(r.params),a=t??ft(n),o=dt(t,l),u={entries:[]};try{let e=await pt(r.data.loader,n,i,a,o.signal,e=>u.entries.push(e));if(e.kind===`redirect`){let t=ut(e.to,n,c);return t.ok?{...e,to:t.to}:(console.warn(`[ilha-router] Blocked unsafe redirect target "${e.to}". Set allowExternalRedirects: true to allow cross-origin redirects.`),{kind:`error`,status:500,message:`Unsafe redirect target`})}return e.kind!==`data`||u.entries.length===0?e:{...e,head:$(u.entries),headEntries:u.entries}}finally{o.done()}},hydrate(e,t={}){if(!_)return console.warn(`[ilha-router] hydrate() called in a non-browser environment`),()=>{};let n=t.root??document.body,r=t.target??n;B();let{unmount:a}=i(e,{root:n}),o=this.mount(r,{hydrate:!0,registry:e,interceptLinks:t.interceptLinks});return()=>{a(),o()}}};async function b(e,t,n={},r){let{baseHead:i,...a}=n,o=ct(e);je(o,d);let u=s(d,`GET`,o.pathname),f=u?.data?.island??null;if(!f){let e={entries:i?[i]:[]};return p?{kind:`html`,html:`<div data-router-view data-router-not-found>${await Z(e,()=>p.toString())}</div>`,status:404,head:$(e.entries)}:{kind:`html`,html:`<div data-router-empty></div>`,status:404,head:i?$([i]):void 0}}let m={entries:i?[i]:[]},h={};if(u?.data?.loader){let e=r??ft(o),t=dt(r,l),n;try{n=await pt(u.data.loader,o,j(),e,t.signal,e=>m.entries.push(e))}finally{t.done()}if(n.kind===`redirect`){let e=ut(n.to,o,c);if(!e.ok)console.warn(`[ilha-router] Blocked unsafe redirect target "${n.to}". Set allowExternalRedirects: true to allow cross-origin redirects.`),n={kind:`error`,status:500,message:`Unsafe redirect target`};else return{kind:`redirect`,to:e.to,status:n.status}}if(n.kind===`error`){let e=u.data.errorHandler;if(e)try{let t=e({message:n.message,status:n.status},{path:A(),params:j(),search:M(),hash:N()}),r=await Z(m,()=>t.toString());return{kind:`error`,status:n.status,message:n.message,html:`<div data-router-view data-router-error="${n.status}">${r}</div>`,head:$(m.entries)}}catch(e){console.error(`[ilha-router] error boundary threw while rendering a loader error:`,e)}let t=it(n.message),r=`<div data-router-view data-router-error="${n.status}">${t}</div>`;return{kind:`error`,status:n.status,message:n.message,html:r,head:$(m.entries)}}h=n.data}let g=pe(t).get(f);return g?{kind:`html`,html:`<div data-router-view>${await Z(m,()=>f.hydratable(h,{name:g,as:`div`,snapshot:!0,...a}))}</div>`,head:$(m.entries)}:(console.warn(`[ilha-router] renderHydratable: active island for "${A()}" is not in the registry. Falling back to plain SSR — the island will not be interactive on the client.`),{kind:`html`,html:`<div data-router-view>${await Z(m,()=>f.toString(h))}</div>`,head:$(m.entries)})}return y}var ht={router:mt,navigate:U,useRoute:ke,isActive:qe,enableLinkInterception:We,prime:B,prefetch:D,beforeNavigate:Fe,afterNavigate:Ie,RouterView:G,RouterLink:Ke,loader:v,redirect:x,error:ee,composeLoaders:S,head:$e};export{ue as A,A as C,ht as D,$ as E,m as M,ke as O,j as S,mt as T,De as _,G as a,x as b,S as c,ee as d,$e as f,U as g,v as h,Ke as i,h as j,de as k,fe as l,qe as m,b as n,Ie as o,Oe as p,y as r,Fe as s,me as t,We as u,D as v,M as w,N as x,B as y};
|
package/dist/ssr.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import"./src-
|
|
1
|
+
import"./src-8LBo_Ieg.js";import{pageRouter as e,registry as t}from"ilha:pages/server";import"ilha:loaders";function n({client:e,server:t}){return e.merge(t)}function r(e){return e}const i={"&":`&`,"<":`<`,">":`>`,'"':`"`,"'":`'`};function a(e){return e.replace(/[&<>"']/g,e=>i[e])}function o(e){let t=e[`data-vite-dev-id`]?` data-vite-dev-id="${a(e[`data-vite-dev-id`])}"`:``;return`<link rel="stylesheet" href="${a(e.href)}"${t} />`}var s=class{assets;lang;appId;clientEntry;head;renderOptions;constructor(e){this.assets=e.assets,this.lang=e.lang??`en`,this.appId=e.appId??`app`,this.clientEntry=e.clientEntry??`/entry-client.js`,this.head=e.head??{},this.renderOptions=e.renderOptions??{}}document(e,t){let n=this.assets.entry??this.clientEntry,r=this.assets.css.map(o).join(`
|
|
2
2
|
`),i=t?.headTags.includes(`<title`)?``:`<title>Ilha</title>
|
|
3
3
|
`,s=t?.headTags?`\n ${t.headTags}`:``,c=t?.htmlAttrs??``;return`<!doctype html>
|
|
4
4
|
<html lang="${c.match(/\blang="([^"]*)"/)?.[1]??this.lang}"${c.replace(/\s*lang="[^"]*"/,``)}>
|
package/dist/vite.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{A as e,k as t}from"./src-8LBo_Ieg.js";import{t as n}from"./plugin-BW2tnuyF.js";function r(e={}){return n.vite(e)}export{n as ilhaPages,r as pages,t as wrapError,e as wrapLayout};
|
package/package.json
CHANGED
package/dist/plugin-nkvFqO15.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import{existsSync as e,readFileSync as t,watch as n}from"node:fs";import{basename as r,dirname as i,extname as a,join as o,relative as s,resolve as c,sep as l}from"node:path";import{createUnplugin as u}from"unplugin";import{mkdir as d,readFile as f,readdir as p,writeFile as m}from"node:fs/promises";function h(e){return e.replace(/\\/g,`/`)}const g=/\.(test|spec|d)\.(ts|tsx)$/,_=/^\s*export\s+(?:const|let|var|async\s+function|function)\s+load\b/m;async function v(e){try{let t=(await f(e,`utf8`)).replace(/^\s*\/\/.*$/gm,``);return _.test(t)}catch{return!1}}function y(e){return e.startsWith(`[...`)&&e.endsWith(`]`)?`**:${e.slice(4,-1)}`:e.startsWith(`[`)&&e.endsWith(`]`)?`:${e.slice(1,-1)}`:e}function b(e){return e.startsWith(`(`)&&e.endsWith(`)`)?``:y(e)}function x(e,t){let n=h(s(e,t)),r=n.slice(0,-a(n).length).split(`/`),i=[...r.slice(0,-1).map(b),y(r.at(-1))];return i.at(-1)===`index`&&i.pop(),`/`+i.filter(Boolean).join(`/`)||`/`}function S(e){return e===`/`?`index`:e.replace(/^\//,``).replace(/\*\*:[^/]*/g,e=>e.length>3?e.slice(3):`wildcard`).replace(/:/g,``).replace(/\*\*/g,`wildcard`).replace(/\//g,`-`).replace(/[^a-zA-Z0-9-]/g,``)||`page`}function C(e){return e===`/`?3:e.includes(`**`)?0:e.includes(`:`)?1:2}function w(e){return[...e].sort((e,t)=>{let n=C(t.pattern)-C(e.pattern);if(n!==0)return n;let r=t.pattern.split(`/`).length-e.pattern.split(`/`).length;return r===0?e.pattern.localeCompare(t.pattern):r})}function T(e,t,n,r){let a=h(s(e,i(t))),c=a===``?[]:a.split(`/`);return[e,...c.map((t,n)=>o(e,...c.slice(0,n+1)))].flatMap(e=>{let t=`${o(e,r)}.tsx`;if(n.has(t))return[t];let i=`${o(e,r)}.ts`;return n.has(i)?[i]:[]})}async function E(e,t=0){if(t>20)return console.warn(`[ilha:pages] Max scan depth (20) reached at ${e} — skipping`),[];let n=[];try{for(let r of await p(e,{withFileTypes:!0})){let i=o(e,r.name);r.isDirectory()?n.push(...await E(i,t+1)):r.isFile()&&/\.(ts|tsx)$/.test(r.name)&&!g.test(r.name)&&n.push(i)}}catch(e){if(e.code===`ENOENT`)return[];throw e}return n}async function D(e){let t=await E(e),n=new Set(t),i=t.filter(e=>!r(e).startsWith(`+`)),a=new Map,o=e=>{let t=a.get(e);return t||(t=v(e),a.set(e,t)),t};return Promise.all(i.map(async t=>{let r=x(e,t),i=T(e,t,n,`+layout`),a=T(e,t,n,`+error`),[s,...c]=await Promise.all([v(t),...i.map(o)]),l=i.filter((e,t)=>c[t]);return{file:t,pattern:r,name:S(r),layouts:i,errors:a,hasLoader:s,loaderLayouts:l}}))}function O(e,t,n){if(e.length===0){console.warn(`[ilha:pages] No pages found in ${t}`);return}let r=new Map,i=new Map,a=[];for(let t of e){let e=r.get(t.pattern);e?a.push(`Duplicate route pattern "${t.pattern}"\n first: ${e}\n second: ${t.file}\n The first match wins — the second page will never be reached.`):r.set(t.pattern,t.file);let n=i.get(t.name);n?a.push(`Registry name collision: "${t.name}" is used by both\n ${n}\n ${t.file}\n Hydration may not work correctly for one of these routes.`):i.set(t.name,t.file)}if(a.length!==0){if(n)throw Error(`[ilha:pages] Route validation failed:\n\n${a.join(`
|
|
2
|
-
|
|
3
|
-
`)}`);for(let e of a)console.warn(`[ilha:pages] ${e}`)}}function k(e){return{serverFile:o(e,`pages.server.ts`),clientFile:o(e,`pages.client.ts`),loadersFile:o(e,`loaders.ts`)}}async function A(e,t,n={}){let r=n.mode??`spa`,i=n.interceptLinks,a=r===`static`,o=w(await D(e));O(o,e,n.strict===!0),await d(t,{recursive:!0});let{serverFile:s,clientFile:c,loadersFile:l}=k(t),u=await P(s,j(o,s)),f=await P(c,M(o,c,{isStatic:a,interceptLinks:i}));a||await P(l,N(o,l,s)),(u||f)&&await F(t)}function j(e,t){let n=e=>{let n=h(s(i(t),e));return n.startsWith(`.`)?n:`./${n}`},r=[`import { router, wrapLayout, wrapError } from "@ilha/router";`,`import type { Island } from "ilha";`],a=[],o=[],c=[];for(let[t,i]of e.entries()){r.push(`import { default as _page${t} } from ${JSON.stringify(n(i.file))};`);for(let[e,a]of i.layouts.entries())r.push(`import { default as _layout${t}_${e} } from ${JSON.stringify(n(a))};`);for(let[e,a]of i.errors.entries())r.push(`import { default as _error${t}_${e} } from ${JSON.stringify(n(a))};`);let s=`_page${t}`;for(let e=i.errors.length-1;e>=0;e--)s=`wrapError(_error${t}_${e}, ${s})`;for(let e=i.layouts.length-1;e>=0;e--)s=`wrapLayout(_layout${t}_${e}, ${s})`;let l=`_wrapped${t}`;a.push(`const ${l} = ${s};`),o.push(` ${JSON.stringify(i.name)}: ${l}`+(t<e.length-1?`,`:``)),c.push(` .route(${JSON.stringify(i.pattern)}, ${l})`+(i.hasLoader||i.loaderLayouts.length>0?`.markLoader(${JSON.stringify(i.pattern)})`:``))}return[`// @generated by @ilha/router — do not edit`,`// Server module. Use for SSR and SSG/prerender.`,`// Import via: import { pageRouter, registry } from "ilha:pages/server";`,``,...r,``,...a,``,`export const registry: Record<string, Island<any, any>> = {`,...o,`};`,``,`export const pageRouter = router()`,...c,` ;`].join(`
|
|
4
|
-
`)}function M(e,t,n){let{isStatic:r,interceptLinks:a}=n,o=e=>{let n=h(s(i(t),e));return n.startsWith(`.`)?n:`./${n}`},c=e=>`${o(e)}?client`,l=r?[`import { router as _router, wrapLayout, wrapError } from "@ilha/router";`,`import type { Island } from "ilha";`]:[`import { router, wrapLayout, wrapError } from "@ilha/router";`,`import type { Island } from "ilha";`],u=[],d=[],f=[];for(let[t,n]of e.entries()){l.push(`import { default as _page${t} } from ${JSON.stringify(c(n.file))};`);for(let[e,r]of n.layouts.entries())l.push(`import { default as _layout${t}_${e} } from ${JSON.stringify(c(r))};`);for(let[e,r]of n.errors.entries())l.push(`import { default as _error${t}_${e} } from ${JSON.stringify(c(r))};`);let i=`_page${t}`;for(let e=n.errors.length-1;e>=0;e--)i=`wrapError(_error${t}_${e}, ${i})`;for(let e=n.layouts.length-1;e>=0;e--)i=`wrapLayout(_layout${t}_${e}, ${i})`;let a=`_wrapped${t}`;u.push(`const ${a} = ${i};`),d.push(` ${JSON.stringify(n.name)}: ${a}`+(t<e.length-1?`,`:``)),r||f.push(` .route(${JSON.stringify(n.pattern)}, ${a})`+(n.hasLoader||n.loaderLayouts.length>0?`.markLoader(${JSON.stringify(n.pattern)})`:``))}let p=r?`_router({ mode: "static" })`:`router(${a===!1?`{ interceptLinks: false }`:``})`,m=[`// @generated by @ilha/router — do not edit`,`// Client module. Use for browser hydration.`,`// Import via: import { pageRouter, registry } from "ilha:pages/client";`,``,...l,``,...u,``,`export const registry: Record<string, Island<any, any>> = {`,...d,`};`,``];return r?m.push(`export const pageRouter = ${p};`):m.push(`export const pageRouter = ${p}`,...f,` ;`),m.join(`
|
|
5
|
-
`)}function N(e,t,n){let r=e=>{let n=h(s(i(t),e));return n.startsWith(`.`)?n:`./${n}`},a=e.filter(e=>e.hasLoader||e.loaderLayouts.length>0);if(a.length===0)return[`// @generated by @ilha/router — do not edit`,`// This project has no loader exports; this file is intentionally empty.`,``,`export {};`,``].join(`
|
|
6
|
-
`);let o=r(n).replace(/\.tsx?$/,``),c=[`import { pageRouter } from ${JSON.stringify(o)};`],l=!1,u=[];for(let[e,t]of a.entries()){let n=[];for(let[i,a]of t.loaderLayouts.entries()){let t=`_p${e}_l${i}`;c.push(`import { load as ${t} } from ${JSON.stringify(r(a))};`),n.push(t)}if(t.hasLoader){let i=`_p${e}`;c.push(`import { load as ${i} } from ${JSON.stringify(r(t.file))};`),n.push(i)}let i=n.length===1?n[0]:`composeLoaders([${n.join(`, `)}])`;n.length>1&&(l=!0),u.push(`pageRouter.attachLoader(${JSON.stringify(t.pattern)}, ${i});`)}return l&&c.unshift(`import { composeLoaders } from "@ilha/router";`),[`// @generated by @ilha/router — do not edit`,`// Server-only. Import this module from your SSR entry to wire loaders`,`// onto pageRouter. Importing it from the client is a no-op but wastes`,`// bundle size — rely on the default build pipeline to keep it out.`,``,...c,``,...u,``].join(`
|
|
7
|
-
`)}async function P(e,t){try{if(await f(e,`utf8`)===t)return!1}catch{}return await m(e,t,`utf8`),!0}async function F(e){await P(o(e,`pages.d.ts`),[`// @generated by @ilha/router — do not edit`,``,`declare module "ilha:pages/server" {`,` import type { RouterBuilder } from "@ilha/router";`,` import type { Island } from "ilha";`,` export const pageRouter: RouterBuilder;`,` export const registry: Record<string, Island<any, any>>;`,`}`,``,`declare module "ilha:pages/client" {`,` import type { RouterBuilder } from "@ilha/router";`,` import type { Island } from "ilha";`,` export const pageRouter: RouterBuilder;`,` export const registry: Record<string, Island<any, any>>;`,`}`,``,`declare module "ilha:loaders" {`,` // Side-effect-only module. Importing it attaches loaders to pageRouter.`,`}`,``].join(`
|
|
8
|
-
`))}const I=`\0ilha:pages/server`,L=`\0ilha:pages/client`,R=`\0ilha:loaders`,z=[I,L,R];function B(e){try{return JSON.parse(t(e,`utf8`))}catch{return null}}function V(t,n){let r=t;for(;;){let t=o(r,`node_modules`,n,`package.json`);if(e(t))return B(t);let a=i(r);if(a===r)return null;r=a}}function H(e){let t=B(o(e,`package.json`));if(!t)return[];let n={...t.dependencies??{},...t.devDependencies??{}},r=[];for(let t of Object.keys(n)){if(t===`ilha`)continue;let n=V(e,t);if(!n)continue;let i=n.peerDependencies??{},a=n.dependencies??{};(`ilha`in i||`ilha`in a)&&r.push(t)}return r}function U(e,t){let n=c(e,t.dir??`src/pages`),r=c(e,t.outDir??`.ilha`),{serverFile:i,clientFile:a,loadersFile:o}=k(r);return{pagesDir:n,outDir:r,serverFile:i,clientFile:a,loadersFile:o}}function W(e){let t,n,i,a,o,s=r=>{({pagesDir:t,outDir:n,serverFile:i,clientFile:a,loadersFile:o}=U(r,e))},c=async()=>{try{await A(t,n,{mode:e.mode,interceptLinks:e.interceptLinks,strict:e.strict})}catch(t){if(console.error(`[ilha:pages] codegen failed:`,t),e.strict)throw t}},u=e=>e===t||e.startsWith(t+l);return{get pagesDir(){return t},get outDir(){return n},get serverFile(){return i},get clientFile(){return a},get loadersFile(){return o},setPaths:s,regen:c,shouldRegenOnChange:e=>{if(!u(e))return!1;let t=r(e);return t.startsWith(`+`)||/\.(ts|tsx)$/.test(t)},isUnderPagesDir:u}}async function G(e,t,n){n(t)&&await e.regen()}function K(e,t,n){if(t===`ilha:pages/server`)return I;if(t===`ilha:pages/client`)return L;if(t===`ilha:loaders`)return R;if(t.endsWith(`?client`)){let r=t.slice(0,-7),i=n?c(n.replace(/\?.*$/,``),`..`,r):c(r);return!e.pagesDir||!e.isUnderPagesDir(i)?void 0:i+`?client`}}function q(e,t){if(t===`\0ilha:pages/server`){let t=e.serverFile.replace(/\.tsx?$/,``);return`export { pageRouter, registry } from ${JSON.stringify(t)};`}if(t===`\0ilha:pages/client`){let t=e.clientFile.replace(/\.tsx?$/,``);return`export { pageRouter, registry } from ${JSON.stringify(t)};`}if(t===`\0ilha:loaders`){let t=e.loadersFile.replace(/\.tsx?$/,``);return`import ${JSON.stringify(t)};`}if(t.endsWith(`?client`)){let e=t.slice(0,-7);return`export { default } from ${JSON.stringify(e)};`}}function J(e,t){return async n=>{e.isUnderPagesDir(n)&&(await e.regen(),await t())}}function Y(t,r){let i=null,a=null,s=!1,c=()=>{i=n(t.pagesDir,{recursive:!0},(e,n)=>{n&&r(o(t.pagesDir,n))})};return e(t.pagesDir)?c():(a=setInterval(()=>{s||!e(t.pagesDir)||(clearInterval(a),a=null,c(),r(o(t.pagesDir,`.`)))},1e3),a.unref?.()),()=>{s=!0,a&&clearInterval(a),i?.close()}}const X=u((e={})=>{let t=W(e);return{name:`ilha:pages`,async buildStart(){t.pagesDir||t.setPaths(process.cwd()),this.addWatchFile?.(t.pagesDir),await t.regen()},async watchChange(e){await G(t,e,e=>t.shouldRegenOnChange(e))},resolveId(e,n){return K(t,e,n)},load(e){return q(t,e)},vite:{config(e){let t=[`ilha`,`@ilha/store`,`@ilha/router`,`alien-signals`,...H(e.root?c(e.root):process.cwd())],n=e.ssr?.noExternal,r=n===!0?!0:[...new Set([...Array.isArray(n)?n:n==null?[]:[n],...t])];return{resolve:{dedupe:[...new Set([...e.resolve?.dedupe??[],...t])]},ssr:{noExternal:r},optimizeDeps:{...e.optimizeDeps,include:[...new Set([...e.optimizeDeps?.include??[],`ilha`,`ilha/jsx-runtime`,`ilha/jsx-dev-runtime`,`@ilha/store`,`alien-signals`])]}}},configResolved(e){t.setPaths(e.root)},configureServer(e){e.watcher.add(t.pagesDir);let n=J(t,async()=>{for(let t of z){let n=e.moduleGraph.getModuleById(t);n&&e.moduleGraph.invalidateModule(n)}e.hot.send({type:`full-reload`})});e.watcher.on(`add`,n),e.watcher.on(`addDir`,n),e.watcher.on(`unlink`,n),e.watcher.on(`change`,async e=>{t.shouldRegenOnChange(e)&&await n(e)})}},rspack(e){t.setPaths(e.options.context??process.cwd());let n=J(t,()=>{e.watching&&e.invalidate()}),r;e.hooks.watchRun.tap(`ilha:pages`,()=>{r?.(),r=Y(t,n)}),e.hooks.shutdown.tap(`ilha:pages`,()=>r?.())}}});export{X as t};
|
package/dist/src-BHVpXJIQ.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import e,{ISLAND_MOUNT_INTERNAL as t,context as n,html as r,mount as i}from"ilha";import{addRoute as a,createRouter as o,findRoute as s}from"rou3";const c=typeof window<`u`&&typeof document<`u`,l={readLocation(){return c?{pathname:location.pathname,search:location.search,hash:location.hash}:{pathname:`/`,search:``,hash:``}},push(e,t){c&&history.pushState(t??null,``,e)},replace(e,t){c&&history.replaceState(t??null,``,e)},onChange(e){return c?(window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)):()=>{}},toLinkHref(e){return e},extractLogicalPath(e){let t=e.getAttribute(`href`);return!t||e.protocol&&!/^(http:|https:)$/.test(e.protocol)||t.startsWith(`#`)||e.hostname&&(e.hostname!==location.hostname||e.protocol!==location.protocol)?null:e.pathname+e.search+e.hash}};function u(e){let t=e.startsWith(`#`)?e.slice(1):e,n=t===``?`/`:t.startsWith(`/`)?t:`/`+t,r=new URL(n,`http://_`);return{pathname:r.pathname,search:r.search,hash:r.hash}}const d={readLocation(){return c?u(location.hash):{pathname:`/`,search:``,hash:``}},push(e,t){c&&history.pushState(t??null,``,e.startsWith(`#`)?e:`#`+e)},replace(e,t){c&&history.replaceState(t??null,``,e.startsWith(`#`)?e:`#`+e)},onChange(e){return c?(window.addEventListener(`popstate`,e),window.addEventListener(`hashchange`,e),()=>{window.removeEventListener(`popstate`,e),window.removeEventListener(`hashchange`,e)}):()=>{}},toLinkHref(e){return e.startsWith(`#`)?e:`#`+e},extractLogicalPath(e){let t=e.getAttribute(`href`);if(!t||e.protocol&&!/^(http:|https:)$/.test(e.protocol))return null;if(t.startsWith(`#`)){let e=t.slice(1);return e===``||!e.startsWith(`/`)?null:e}if(/^https?:\/\//i.test(t))try{let e=new URL(t);if(e.origin!==location.origin||!e.hash||e.hash===`#`)return null;let n=e.hash.slice(1);return n.startsWith(`/`)?n:null}catch{return null}return t}};let f=`history`,p=l;function m(e){f=e,p=e===`hash`?d:l}function h(){return f}function g(){return p}const _=typeof window<`u`&&typeof document<`u`;function v(e){return e}var y=class{__ilhaRedirect=!0;to;status;constructor(e,t=302){this.to=e,this.status=t}},b=class{__ilhaLoaderError=!0;status;message;constructor(e,t){this.status=e,this.message=t}};function x(e,t=302){throw new y(e,t)}function ee(e,t){throw new b(e,t)}function S(e){return e.length===0?async()=>({}):e.length===1?e[0]:async t=>{let n=await Promise.all(e.map(e=>e(t)));return Object.assign({},...n)}}const C=Symbol.for(`ilha.router.wrapLayout.leaf`),te=Symbol.for(`ilha.router.wrapLayout.handler`);function ne(e){let t=e.match(/^<([a-zA-Z][\w-]*)\s[^>]*>([\s\S]*)<\/\1>\s*$/);return t?t[2]:e}function re(e){let t=e.match(/^<([a-zA-Z][\w-]*)\s([^>]*)>/);return t?{tag:t[1],attrs:t[2]}:null}const ie=/<(pre|script|style|textarea)\b/i;function ae(e,t,n){let r=RegExp(`<${n}\\b`,`gi`),i=RegExp(`</${n}>`,`gi`),a=1,o=t;for(;a>0&&o<e.length;){r.lastIndex=o,i.lastIndex=o;let t=r.exec(e),n=i.exec(e);if(!n)return null;if(t&&t.index<n.index)a+=1,o=t.index+t[0].length;else{if(--a,a===0)return n.index+n[0].length;o=n.index+n[0].length}}return o}function oe(e,t){let n=t;for(;n<e.length;){if(e.startsWith(`<!--`,n)){let t=e.indexOf(`-->`,n);if(t===-1)return null;n=t+3;continue}let t=e.slice(n).match(ie);if(t&&t.index!=null&&t.index>=0){let r=n+t.index,i=!1,a=n;for(;a<r;){let t=e.indexOf(`<!--`,a);if(t===-1||t>=r)break;let o=e.indexOf(`-->`,t);if(o===-1)return null;if(r<o+3){n=o+3,i=!0;break}a=o+3}if(i)continue;let o=e.indexOf(`<div`,n),s=e.indexOf(`</div>`,n);if(!(o!==-1&&o<r||s!==-1&&s<r)){let i=t[1].toLowerCase(),a=ae(e,r+t[0].length,i);if(a===null)return null;n=a;continue}}let r=e.indexOf(`<div`,n),i=e.indexOf(`</div>`,n);return i===-1&&r===-1?null:r===-1||i!==-1&&i<r?{kind:`close`,index:i}:{kind:`open`,index:r}}return null}function se(e){let t=[];for(let n of e.matchAll(/<div\s[^>]*data-ilha-slot="k:page"[^>]*>/g)){let r=n.index+n[0].length,i=1,a=r;for(;i>0;){let n=oe(e,a);if(!n)break;if(n.kind===`open`)i+=1,a=n.index+4;else{if(--i,i===0){t.push({openEnd:r,closeStart:n.index});break}a=n.index+6}}}return t}function w(e,t,n){let r=se(e);if(r.length===0)return e;let i=n===`innermost`?r[r.length-1]:r[0];return e.slice(0,i.openEnd)+t+e.slice(i.closeStart)}function ce(e,t){let n=e[te];if(!n)return e.toString(t);let r=e[C]??e;return n(Object.assign(r.key(`page`),{toString:()=>``})).toString(t)}async function le(e,t,n,r){let i=ne(await t.hydratable(n,r));return w(w(ce(e,n),``,`innermost`),i,`innermost`)}function ue(e,n){let r=n[C]??n,i=r===n?null:n,a=e(Object.assign(n.key(`page`),{toString:n.toString.bind(n)}));a[C]=r,a[te]=e;function o(e){let t=[...e.querySelectorAll(`[data-ilha-slot="k:page"]`)].filter(t=>{let n=t.closest(`[data-ilha]`);return n===null||n===e});return t.length===0?e:t[t.length-1]}function s(e,t){let n=e=>{delete e._skipOnMount,t.setAttribute(`data-ilha-state`,JSON.stringify(e))};if(t.hasAttribute(`data-ilha-state`)){let r=e.getAttribute(`data-ilha-state`);if(r)try{n(JSON.parse(r));return}catch{}let i=t.getAttribute(`data-ilha-state`);if(i)try{let e=JSON.parse(i);delete e._skipOnMount,t.setAttribute(`data-ilha-state`,JSON.stringify(e))}catch{}return}let r=e.getAttribute(`data-ilha-state`);if(r)try{n(JSON.parse(r));return}catch{}t.childNodes.length>0&&t.setAttribute(`data-ilha-state`,`{}`)}function c(e){let n=e[t];if(typeof n!=`function`)return;e[t]=(e,t)=>{let r=e.closest(`[data-ilha]`);return r&&r!==e&&s(r,e),n(e,t)};let r=e.mount.bind(e);e.mount=(e,t)=>{let n=e.closest(`[data-ilha]`);return n&&n!==e&&s(n,e),r(e,t)}}c(r);let l=a.mount.bind(a),u=a[t];function d(e){s(e,o(e))}return a.mount=(e,t)=>(d(e),l(e,t)),a[t]=(e,t)=>(d(e),typeof u==`function`?u(e,t):{unmount:l(e,t),updateProps:()=>{}}),a.hydratable=async(e,t)=>{if(!t?.name)throw Error(`wrapLayout: hydratable requires options.name`);let n=e??{},o=await r.hydratable(n,t),s=re(o);if(!s)return o;let c=ne(o);i&&(c=await le(i,r,n,t));let l=w(w(ce(a,n),``,`first`),c,`first`);return`<${s.tag} ${s.attrs}>${l}</${s.tag}>`},a}function de(n,r){let i=e.render(()=>{try{return r.toString()}catch(e){let t={path:A(),params:j(),search:M(),hash:N()};return n({message:e.message,status:e.status,stack:e.stack},t).toString()}});return i.mount=(e,t)=>{try{return r.mount(e,t)}catch(r){let i={path:A(),params:j(),search:M(),hash:N()},a=n({message:r.message,status:r.status,stack:r.stack},i);return e.innerHTML=a.toString(),a.mount(e,t)}},i[t]=(e,i)=>{try{let n=r[t];return typeof n==`function`?n(e,i):{unmount:r.mount(e,i),updateProps:()=>{}}}catch(t){let r={path:A(),params:j(),search:M(),hash:N()},a=n({message:t.message,status:t.status,stack:t.stack},r);return e.innerHTML=a.toString(),{unmount:a.mount(e,i),updateProps:()=>{}}}},i.hydratable=async(e,t)=>{if(!t?.name)throw Error(`wrapError: hydratable requires options.name`);return r.hydratable(e??{},t)},i}function fe(e){return e}function pe(e){let t=new Map;for(let[n,r]of Object.entries(e))t.has(r)||t.set(r,n);return t}const me=`/__ilha/loader`,T=new Map;async function E(e,t){let n=T.get(e);if(n&&(T.delete(e),Date.now()<=n.expires))try{t?.throwIfAborted();let e=await n.promise;return t?.throwIfAborted(),e}catch(e){if(e?.name===`AbortError`)throw e}let r=`${me}?path=${encodeURIComponent(e)}`;try{let e=await fetch(r,{signal:t,headers:{accept:`application/json`}});if(!e.ok){try{let t=await e.json();if(t&&typeof t==`object`&&`kind`in t)return t}catch{}return{kind:`error`,status:e.status,message:e.statusText}}return await e.json()}catch(e){if(e?.name===`AbortError`)throw e;return{kind:`error`,status:0,message:e?.message??`network error`}}}function D(e){if(!_)return;let t=T.get(e);if(t&&Date.now()<=t.expires)return;let n=e.split(`?`)[0]??``;if(!s(F,`GET`,n)?.data?.hasLoader)return;let r=E(e).catch(e=>({kind:`error`,status:0,message:e?.message??`prefetch failed`}));T.set(e,{promise:r,expires:Date.now()+3e4})}async function he(e,t,n,r,i,a){if(!e){if(V){t.innerHTML=`<div data-router-view data-router-not-found>${V.toString()}</div>`;let e=t.firstElementChild;return e?V.mount(e):()=>{}}return t.innerHTML=`<div data-router-empty></div>`,()=>{}}let o=!!s(F,`GET`,n.split(`?`)[0]??``)?.data?.hasLoader,c={},l=o?await E(n,r):{kind:`data`,data:{}};if(l.kind===`redirect`)return Le(l.to),()=>{};if(l.kind===`error`){let e=String(l.message).replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`);return t.innerHTML=`<div data-router-view data-router-error="${l.status}">${e}</div>`,()=>{}}if(l.kind===`not-found`)return t.innerHTML=`<div data-router-empty></div>`,()=>{};c=l.data;let u={entries:[]};if(!i){console.warn(`[ilha-router] No registry provided for client-side navigation. Island will not be interactive.`);let n=await X(u,()=>e.toString(c));return Y(u.entries),t.innerHTML=`<div data-router-view>${n}</div>`,()=>{}}let d=a?.get(e)??Object.entries(i).find(([,t])=>t===e)?.[0];if(!d){console.warn(`[ilha-router] Island not found in registry for client-side navigation.`);let n=await X(u,()=>e.toString(c));return Y(u.entries),t.innerHTML=`<div data-router-view>${n}</div>`,()=>{}}let f=await X(u,()=>e.hydratable(c,{name:d,as:`div`,snapshot:!0}));Y(u.entries),t.innerHTML=`<div data-router-view>${f}</div>`;let p=t.querySelector(`[data-ilha="${d}"]`);return p?e.mount(p):()=>{}}let O=null,ge=null;async function _e(){return O||(ge||=import(`node:async_hooks`).then(({AsyncLocalStorage:e})=>(O=new e,O)),ge)}_||_e().catch(()=>{});function k(){return _?null:O?.getStore()??null}function ve(){return{path:``,params:{},search:``,hash:``,island:null}}const ye=n(`router.path`,``),be=n(`router.params`,{}),xe=n(`router.search`,``),Se=n(`router.hash`,``);function A(e){let t=k();return arguments.length>0?t?t.path=e:(ye(e),e):t?t.path:ye()}function j(e){let t=k();return arguments.length>0?t?t.params=e:(be(e),e):t?t.params:be()}function M(e){let t=k();return arguments.length>0?t?t.search=e:(xe(e),e):t?t.search:xe()}function N(e){let t=k();return arguments.length>0?t?t.hash=e:(Se(e),e):t?t.hash:Se()}function Ce(){return{path:A,params:j,search:M,hash:N}}const we=n(`router.active`,null);function P(e){let t=k();return arguments.length>0?t?t.island=e??null:(we(e??null),e??null):t?t.island:we()}let F=o();function Te(e){let t={};if(e)for(let[n,r]of Object.entries(e))t[n]=decodeURIComponent(r);return t}function Ee(e,t=F){let n=typeof e==`string`?new URL(e,`http://localhost`):e,r=s(t,`GET`,n.pathname);A(n.pathname),j(Te(r?.params)),M(n.search),N(n.hash),P(r?.data?.island??null)}function I(){let e=g().readLocation(),t=s(F,`GET`,e.pathname);A(e.pathname),j(Te(t?.params)),M(e.search),N(e.hash),P(t?.data?.island??null)}function L(){_&&I()}const De=new Set,Oe=new Set;function ke(e){return De.add(e),()=>De.delete(e)}function Ae(e){return Oe.add(e),()=>Oe.delete(e)}function je(e){for(let t of Oe)try{t(e)}catch(e){console.error(`[ilha-router] afterNavigate hook threw:`,e)}}const Me=new Map;let Ne=0,R=0;function z(){if(!_)return 0;let e=history.state;return typeof e?.__ilhaNavKey==`number`?e.__ilhaNavKey:0}function Pe(){Me.set(z(),{x:window.scrollX,y:window.scrollY})}function Fe(e){requestAnimationFrame(()=>{if(e&&e!==`#`){let t=document.getElementById(e.slice(1))??document.querySelector(`a[name="${J(e.slice(1))}"]`);if(t){t.scrollIntoView();return}}window.scrollTo(0,0)})}function Ie(){let e=Me.get(z());e&&requestAnimationFrame(()=>window.scrollTo(e.x,e.y))}function B(e,t={}){if(!_)return;let n=g(),r=n.readLocation(),i=r.pathname+r.search+r.hash;if(e===i)return;let a=t.replace?`replace`:`push`,o=!1;for(let t of De)try{t({from:i,to:e,type:a,cancel:()=>o=!0})}catch(e){console.error(`[ilha-router] beforeNavigate hook threw:`,e)}o||(t.replace?n.replace(e,{__ilhaNavKey:z()}):(Pe(),Ne=Math.max(Ne+1,z()+1),n.push(e,{__ilhaNavKey:Ne})),R=z(),I(),t.scroll!==!1&&Fe(n.readLocation().hash),je({from:i,to:e,type:a}))}function Le(e){if(/^https?:\/\//i.test(e)){try{let t=new URL(e);if(t.origin===location.origin){B(t.pathname+t.search+t.hash,{replace:!0});return}}catch{return}location.assign(e);return}B(e,{replace:!0})}function Re(e=document,t={}){if(!_)return()=>{};let n=t.prefetch!==!1;function r(e,t){let n=e.getAttribute(`target`)===`_blank`,r=!!t&&(t.ctrlKey||t.metaKey||t.shiftKey||t.altKey),i=e.hasAttribute(`data-no-intercept`),a=e.hasAttribute(`download`),o=/\bexternal\b/i.test(e.getAttribute(`rel`)??``);return n||r||i||a||o?null:g().extractLogicalPath(e)}let i=e=>{if(e.defaultPrevented||typeof e.button==`number`&&e.button!==0)return;let t=e.target.closest(`a`);if(!t)return;let n=r(t,e);n!==null&&(e.preventDefault(),B(n))},a=e=>{let t=e.target.closest(`a`);if(!t)return;let n=t.getAttribute(`data-prefetch`);if(n===null||n===`false`)return;let i=r(t);i!==null&&D(i.split(`#`)[0]??i)};return e.addEventListener(`click`,i),n&&e.addEventListener(`mouseover`,a,{passive:!0}),()=>{e.removeEventListener(`click`,i),n&&e.removeEventListener(`mouseover`,a)}}let V=null;const H=e.render(()=>{let e=P();return e?`<div data-router-view>${e.toString()}</div>`:V?`<div data-router-view data-router-not-found>${V.toString()}</div>`:`<div data-router-empty></div>`}),ze=e.state(`href`,``).state(`label`,``).on(`[data-link]@click`,({state:e,event:t})=>{t.preventDefault(),B(e.href())}).on(`[data-link]@mouseenter`,({state:e})=>{let t=e.href();if(t){if(/^https?:\/\//i.test(t))try{let e=new URL(t);if(e.origin!==location.origin)return;D(e.pathname+e.search);return}catch{return}D(t)}}).render(({state:e})=>r`<a data-link data-prefetch href="${()=>g().toLinkHref(e.href())}"
|
|
2
|
-
>${e.label}</a
|
|
3
|
-
>`);function Be(e,t={}){if(t.exact===!1){let t=A(),n=e.endsWith(`/`)?e.slice(0,-1):e;return t===n||t===n+`/`||t.startsWith(n+`/`)}let n=s(F,`GET`,A());return n?n.data.pattern===e:!1}const U=`data-ilha-head`,W=`data-ilha-router-html`,G=`data-ilha-router-body`;let K=null,q=null,Ve=null;async function He(){return q||(Ve||=import(`node:async_hooks`).then(({AsyncLocalStorage:e})=>(q=new e,q)),Ve)}function Ue(){return _?K:q?.getStore()??null}function We(e){let t=Ue();if(!t){_||console.warn(`[ilha-router] head() called outside an SSR render window — ignored.`);return}t.entries.push(e)}function J(e){return typeof CSS<`u`&&typeof CSS.escape==`function`?CSS.escape(e):e.replace(/\\/g,`\\\\`).replace(/"/g,`\\"`)}function Ge(e){return`charset`in e?`meta[charset][${U}]`:`name`in e?`meta[name="${J(e.name)}"][${U}]`:`property`in e?`meta[property="${J(e.property)}"][${U}]`:`http-equiv`in e?`meta[http-equiv="${J(e[`http-equiv`])}"][${U}]`:null}function Ke(e){return e.rel&&e.href?`link[rel="${J(e.rel)}"][href="${J(e.href)}"][${U}]`:null}function Y(e){if(!_)return;let t,n,r=[],i=[],a={},o={};for(let s of e)s.title!==void 0&&(t=s.title),s.titleTemplate!==void 0&&(n=s.titleTemplate),s.meta&&r.push(...s.meta),s.link&&i.push(...s.link),s.htmlAttrs&&(a={...a,...s.htmlAttrs}),s.bodyAttrs&&(o={...o,...s.bodyAttrs});let s=Xe(t,n);s!==void 0&&(document.title=s);let c=Q(r,Ye),l=Q(i,e=>`${e.rel??``}:${e.href??``}`),u=new Set;for(let e of c){let t=Ge(e);if(!t)continue;let n=document.querySelector(t);n||(n=document.createElement(`meta`),n.setAttribute(U,``),document.head.appendChild(n));for(let[t,r]of Object.entries(e))n.setAttribute(t,r);u.add(n)}for(let e of l){let t=Ke(e),n=t?document.querySelector(t):null;n||(n=document.createElement(`link`),n.setAttribute(U,``),document.head.appendChild(n));for(let[t,r]of Object.entries(e))n.setAttribute(t,r);u.add(n)}for(let e of[...document.head.querySelectorAll(`[${U}]`)])u.has(e)||e.remove();let d=document.documentElement,f=(d.getAttribute(W)??``).split(/\s+/).filter(Boolean);for(let e of f)d.removeAttribute(e);let p=Object.keys(a);for(let[e,t]of Object.entries(a))d.setAttribute(e,t);p.length?d.setAttribute(W,p.join(` `)):d.removeAttribute(W);let m=document.body,h=(m.getAttribute(G)??``).split(/\s+/).filter(Boolean);for(let e of h)m.removeAttribute(e);let g=Object.keys(o);for(let[e,t]of Object.entries(o))m.setAttribute(e,t);g.length?m.setAttribute(G,g.join(` `)):m.removeAttribute(G)}async function X(e,t){if(_){let n=K;K=e;try{return await t()}finally{K=n}}return await(await He()).run(e,()=>Promise.resolve(t()))}const qe={"&":`&`,"<":`<`,">":`>`,'"':`"`,"'":`'`};function Je(e){return String(e).replace(/[&<>"']/g,e=>qe[e])}function Z(e){return Object.entries(e).map(([e,t])=>` ${e}="${Je(t)}"`).join(``)}function Ye(e){return`charset`in e?`charset`:`name`in e?`name:${e.name}`:`property`in e?`property:${e.property}`:`http-equiv`in e?`http-equiv:${e[`http-equiv`]}`:JSON.stringify(e)}function Q(e,t){let n=new Map;for(let r of e)n.set(t(r),r);return[...n.values()]}function Xe(e,t){return t===void 0?e:typeof t==`function`?t(e):t.replace(/%s/g,e??``)}function $(e){let t,n,r=[],i=[],a=[],o={},s={};for(let c of e)c.title!==void 0&&(t=c.title),c.titleTemplate!==void 0&&(n=c.titleTemplate),c.meta&&r.push(...c.meta),c.link&&i.push(...c.link),c.script&&a.push(...c.script),c.htmlAttrs&&(o={...o,...c.htmlAttrs}),c.bodyAttrs&&(s={...s,...c.bodyAttrs});let c=Xe(t,n),l=[];c!==void 0&&l.push(`<title>${Je(c)}</title>`);for(let e of Q(r,Ye))l.push(`<meta${Z({...e,[U]:``})} />`);for(let e of Q(i,e=>`${e.rel??``}:${e.href??``}`))l.push(`<link${Z({...e,[U]:``})} />`);for(let e of a){let{children:t,...n}=e,r=(t??``).replace(/<\/script/gi,`<\\/script`);l.push(`<script${Z(n)}>${r}<\/script>`)}return{headTags:l.join(`
|
|
4
|
-
`),htmlAttrs:Z(o),bodyAttrs:Z(s)}}function Ze(e){return typeof e==`string`?new URL(e,`http://localhost`):e}function Qe(){try{return typeof process<`u`&&!!process.env&&process.env.NODE_ENV!==`production`}catch{return!1}}function $e(e,t,n){if(e.startsWith(`/`)&&!e.startsWith(`//`))return{ok:!0,to:e};try{let r=new URL(e,t);return/^https?:$/.test(r.protocol)?r.origin===t.origin?{ok:!0,to:r.pathname+r.search+r.hash}:n?{ok:!0,to:r.href}:{ok:!1}:{ok:!1}}catch{return{ok:!1}}}function et(e,t){let n=new AbortController,r=()=>n.abort(),i=e?.signal;i&&(i.aborted?r():i.addEventListener(`abort`,r,{once:!0}));let a;return t&&t>0&&(a=setTimeout(r,t)),{signal:n.signal,done:()=>{a!==void 0&&clearTimeout(a),i?.removeEventListener(`abort`,r)}}}function tt(e){try{return new Request(e.toString())}catch{return{url:e.toString(),headers:new Headers}}}async function nt(e,t,n,r,i,a){let o=[],s=a??(e=>o.push(e));try{let a=Promise.resolve(e({params:n,request:r,url:t,signal:i,head:s}));a.catch(()=>{});let c={kind:`data`,data:await Promise.race([a,new Promise((e,t)=>{let n=()=>t(new b(504,`Loader aborted or timed out`));i.aborted?n():i.addEventListener(`abort`,n,{once:!0})})])??{}};return o.length>0&&(c.head=$(o)),c}catch(e){return e instanceof y?{kind:`redirect`,to:e.to,status:e.status}:e instanceof b?{kind:`error`,status:e.status,message:e.message}:(console.error(`[ilha-router] loader failed:`,e),{kind:`error`,status:typeof e?.status==`number`?e.status:500,message:Qe()?e?.message??`Loader failed`:`Internal error`})}}function rt(t={}){let n=t.mode??`spa`,r=t.interceptLinks!==!1,c=t.allowExternalRedirects===!0,l=t.loaderTimeout,u=[],d=o(),f=new Map,p=t.notFound??null;F=d,V=p;let m=null,v=null,y={route(e,t,n){let r=!!n,i={island:t,pattern:e,loader:n,hasLoader:r};return u.push({pattern:e,island:t,loader:n,hasLoader:r}),a(d,`GET`,e,i),f.set(e,i),y},attachLoader(e,t){let n=f.get(e);if(!n)return console.warn(`[ilha-router] attachLoader("${e}", …): pattern was never registered via .route(). The loader will be ignored.`),y;n.loader=t,n.hasLoader=!0;let r=u.find(t=>t.pattern===e);return r&&(r.loader=t,r.hasLoader=!0),y},markLoader(e){let t=f.get(e);if(!t)return console.warn(`[ilha-router] markLoader("${e}"): pattern was never registered via .route(). The loader marker will be ignored.`),y;t.hasLoader=!0;let n=u.find(t=>t.pattern===e);return n&&(n.hasLoader=!0),y},routes(){return u.map(e=>({...e}))},prime:L,hydrateStatic(e,t={}){if(!_)return()=>{};let n=t.root??document.body;L();let{unmount:r}=i(e,{root:n});return r},mount(t,{hydrate:i=!1,registry:a,interceptLinks:o}={}){if(!_)return console.warn(`[ilha-router] mount() called in a non-browser environment`),()=>{};let c=typeof t==`string`?document.querySelector(t):t;if(!c)return console.warn(`[ilha-router] No element found for selector "${t}"`),()=>{};if(I(),R=z(),n===`static`)return console.warn(`[ilha-router] router.mount() called in static mode. Use router.hydrateStatic(registry) instead.`),()=>{};let l=!0,u=`scrollRestoration`in history?history.scrollRestoration:null;u!==null&&(history.scrollRestoration=`manual`),m=g().onChange(()=>{if(!l)return;let e=A()+M()+N();Me.set(R,{x:window.scrollX,y:window.scrollY}),R=z(),I(),Ie(),je({from:e,to:A()+M()+N(),type:`pop`})}),v=o??r?Re(document):null;let d=null,f=null;if(i){h()===`hash`&&console.warn("[ilha-router] mount({ hydrate: true }) was called in hash mode. SSR + hydration assumes the server can render the active route, but in hash mode the server only ever sees the document URL. Use plain SPA mode (`mount(target)` without `hydrate: true`) for hash-mode apps.");let t=c.querySelector(`[data-router-view]`)??c,n=P(),r=a?pe(a):void 0,i=0,o=e.render(()=>{let e=P();if(e!==n){let o=++i;f?.abort(),f=new AbortController;let s=f.signal;queueMicrotask(async()=>{if(o===i){d?.(),d=null;try{let n=g().readLocation();d=await he(e,t,n.pathname+n.search,s,a,r)}catch(e){if(e?.name===`AbortError`)return;console.error(`[ilha-router] navigation failed:`,e),t.innerHTML=`<div data-router-view data-router-error="500"></div>`;return}n=e}})}return``}),p=document.createElement(`div`);p.style.display=`none`,c.appendChild(p);let _=o.mount(p);return(async()=>{let e=P();if(!e)return;let t=g().readLocation(),n=t.pathname+t.search,r=s(F,`GET`,t.pathname)?.data?.hasLoader?await E(n):{kind:`data`,data:{}};if(r.kind===`redirect`||r.kind===`error`)return;let i=r.kind===`data`?r.data:{},a={entries:[]};await X(a,()=>e.toString(i)),l&&Y(a.entries)})(),()=>{l=!1,++i,f?.abort(),_(),p.remove(),d?.(),v?.(),m?.(),v=null,m=null,u!==null&&(history.scrollRestoration=u)}}let p=null,y=null,b=0;d=H.mount(c);async function x(e,t){if(p?.(),p=null,y=e,!e){let e=c?.querySelector(`[data-router-not-found]`);V&&e&&(p=V.mount(e));return}let n=c?.querySelector(`[data-router-view]`);if(!n)return;let r=g().readLocation(),i=s(F,`GET`,r.pathname)?.data?.hasLoader?await E(r.pathname+r.search,t):{kind:`data`,data:{}};if(t.aborted)return;if(i.kind===`redirect`){Le(i.to);return}let a=i.kind===`data`?i.data:{},o={entries:[]},l=await X(o,()=>e.toString(a));Y(o.entries),n.innerHTML=l,p=e.mount(n,a)}f=new AbortController,x(P(),f.signal).catch(e=>{e?.name!==`AbortError`&&console.error(`[ilha-router] initial mount failed:`,e)});let ee=e.render(()=>{let e=P();if(e!==y){let t=++b;f?.abort(),f=new AbortController;let n=f.signal;queueMicrotask(()=>{t===b&&x(e,n).catch(e=>{e?.name!==`AbortError`&&console.error(`[ilha-router] navigation failed:`,e)})})}return``}),S=document.createElement(`div`);S.style.display=`none`,c.appendChild(S);let C=ee.mount(S);return()=>{l=!1,++b,f?.abort(),p?.(),C(),S.remove(),d?.(),v?.(),m?.(),v=null,m=null,u!==null&&(history.scrollRestoration=u)}},render(e){let t=()=>(Ee(e,d),H.toString());return!_&&O?O.run(ve(),t):t()},async renderHydratable(e,t,n={},r){let i=await this.renderResponse(e,t,n,r);return i.kind===`html`||i.kind===`error`?i.html:`<meta http-equiv="refresh" content="0; url=${Je(i.to)}">`},async renderResponse(e,t,n={},r){if(!_){let i=await _e();if(!i.getStore())return i.run(ve(),()=>b(e,t,n,r))}return b(e,t,n,r)},async runLoader(e,t){let n=Ze(e),r=s(d,`GET`,n.pathname);if(!r?.data?.island)return{kind:`not-found`};if(!r.data.loader)return{kind:`data`,data:{}};let i=Te(r.params),a=t??tt(n),o=et(t,l),u={entries:[]};try{let e=await nt(r.data.loader,n,i,a,o.signal,e=>u.entries.push(e));if(e.kind===`redirect`){let t=$e(e.to,n,c);return t.ok?{...e,to:t.to}:(console.warn(`[ilha-router] Blocked unsafe redirect target "${e.to}". Set allowExternalRedirects: true to allow cross-origin redirects.`),{kind:`error`,status:500,message:`Unsafe redirect target`})}return e.kind!==`data`||u.entries.length===0?e:{...e,head:$(u.entries)}}finally{o.done()}},hydrate(e,t={}){if(!_)return console.warn(`[ilha-router] hydrate() called in a non-browser environment`),()=>{};let n=t.root??document.body,r=t.target??n;L();let{unmount:a}=i(e,{root:n}),o=this.mount(r,{hydrate:!0,registry:e,interceptLinks:t.interceptLinks});return()=>{a(),o()}}};async function b(e,t,n={},r){let{baseHead:i,...a}=n,o=Ze(e);Ee(o,d);let u=s(d,`GET`,o.pathname),f=u?.data?.island??null;if(!f){let e={entries:i?[i]:[]};return p?{kind:`html`,html:`<div data-router-view data-router-not-found>${await X(e,()=>p.toString())}</div>`,status:404,head:$(e.entries)}:{kind:`html`,html:`<div data-router-empty></div>`,status:404,head:i?$([i]):void 0}}let m={entries:i?[i]:[]},h={};if(u?.data?.loader){let e=r??tt(o),t=et(r,l),n;try{n=await nt(u.data.loader,o,j(),e,t.signal,e=>m.entries.push(e))}finally{t.done()}if(n.kind===`redirect`){let e=$e(n.to,o,c);if(!e.ok)console.warn(`[ilha-router] Blocked unsafe redirect target "${n.to}". Set allowExternalRedirects: true to allow cross-origin redirects.`),n={kind:`error`,status:500,message:`Unsafe redirect target`};else return{kind:`redirect`,to:e.to,status:n.status}}if(n.kind===`error`){let e=String(n.message).replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`),t=`<div data-router-view data-router-error="${n.status}">${e}</div>`;return{kind:`error`,status:n.status,message:n.message,html:t,head:$(m.entries)}}h=n.data}let g=pe(t).get(f);return g?{kind:`html`,html:`<div data-router-view>${await X(m,()=>f.hydratable(h,{name:g,as:`div`,snapshot:!0,...a}))}</div>`,head:$(m.entries)}:(console.warn(`[ilha-router] renderHydratable: active island for "${A()}" is not in the registry. Falling back to plain SSR — the island will not be interactive on the client.`),{kind:`html`,html:`<div data-router-view>${await X(m,()=>f.toString(h))}</div>`,head:$(m.entries)})}return y}var it={router:rt,navigate:B,useRoute:Ce,isActive:Be,enableLinkInterception:Re,prime:L,prefetch:D,beforeNavigate:ke,afterNavigate:Ae,RouterView:H,RouterLink:ze,loader:v,redirect:x,error:ee,composeLoaders:S,head:We};export{m as A,rt as C,de as D,Ce as E,ue as O,M as S,it as T,L as _,H as a,j as b,S as c,ee as d,We as f,D as g,B as h,ze as i,h as k,fe as l,v as m,b as n,Ae as o,Be as p,y as r,ke as s,me as t,Re as u,x as v,$ as w,A as x,N as y};
|