@ilha/router 0.11.3 → 0.11.5

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/dist/head.d.ts CHANGED
@@ -1,31 +1,34 @@
1
1
  /**
2
- * Render-scoped head collection and serialization.
2
+ * Render-scoped head collection and document sync.
3
3
  *
4
4
  * Loaders and render-time `head()` calls push `HeadInput` entries into a
5
- * store scoped to the current render (`withHeadStore`); `serializeHead`
6
- * turns the collected entries into document-shell fragments.
5
+ * store scoped to the current render (`withHeadStore` / `withHeadStoreSync`).
6
+ * SSR serializes them with `serializeHead`; on the client the router applies
7
+ * the collected entries to `document` after each mount/navigation.
7
8
  */
9
+ /** Attribute map contributed to html/body or head tags. */
10
+ export type HeadAttrMap = Readonly<Record<string, string | undefined>>;
11
+ type HeadScriptTag = HeadAttrMap & {
12
+ children?: string;
13
+ };
8
14
  /**
9
15
  * Serializable description of `<head>` (and html/body attributes) contributed
10
- * by a loader or a render-time `head()` call. Deliberately a plain POJO — Tier
11
- * 1 head management is SSR-only, so there is no reactive wrapper. Dedup keys
12
- * mirror unhead so a later move to a runtime head manager stays a drop-in.
16
+ * by a loader or a render-time `head()` call. Dedup keys mirror unhead so a
17
+ * later move to a runtime head manager stays a drop-in.
13
18
  */
14
19
  export interface HeadInput {
15
20
  title?: string;
16
21
  /** Wrap the resolved title. The last template in merge order wins. */
17
22
  titleTemplate?: string | ((title?: string) => string);
18
- meta?: Array<Record<string, string>>;
19
- link?: Array<Record<string, string>>;
23
+ meta?: HeadAttrMap[];
24
+ link?: HeadAttrMap[];
20
25
  /**
21
26
  * Inline script bodies are emitted raw in SSR (`serializeHead`). Must be trusted
22
27
  * app code and must not contain a literal `</script>` sequence.
23
28
  */
24
- script?: Array<Record<string, string> & {
25
- children?: string;
26
- }>;
27
- htmlAttrs?: Record<string, string>;
28
- bodyAttrs?: Record<string, string>;
29
+ script?: HeadScriptTag[];
30
+ htmlAttrs?: HeadAttrMap;
31
+ bodyAttrs?: HeadAttrMap;
29
32
  }
30
33
  /** Serialized head fragments ready to inject into a document shell. */
31
34
  export interface SerializedHead {
@@ -39,29 +42,40 @@ export interface SerializedHead {
39
42
  export interface HeadStore {
40
43
  entries: HeadInput[];
41
44
  }
42
- /**
43
- * Contribute `<head>` data from inside an island's `.render()` body or a
44
- * layout. During SSR this collects into the active render window; on the
45
- * client, entries are collected when the router re-renders a route inside
46
- * `withHeadStore` and then applied to `document`. Prefer a loader's `ctx.head`
47
- * for data that depends on the request.
48
- */
49
- export declare function head(input: HeadInput): void;
50
- export declare function cssEscapeAttr(value: string): string;
45
+ export declare const cssEscapeAttr: (value: string) => string;
46
+ export declare const escapeHeadAttr: <T>(value: T) => string;
47
+ /** Escape text content for inline HTML (loader error messages etc.). */
48
+ export declare const escapeHtml: <T>(value: T) => string;
51
49
  /**
52
50
  * Apply merged head entries on client navigations. Updates `document.title` and
53
51
  * managed meta/link nodes (`data-ilha-head`). Script tags from HeadInput are
54
52
  * SSR-only and are not re-injected here. Removes managed tags from the previous
55
53
  * route that are not part of this navigation's set.
56
54
  */
57
- export declare function applyHeadEntriesToDocument(entries: HeadInput[]): void;
58
- export declare function withHeadStore<T>(store: HeadStore, fn: () => T | Promise<T>): Promise<T>;
59
- export declare function escapeHeadAttr(value: unknown): string;
60
- /** Escape text content for inline HTML (loader error messages etc.). */
61
- export declare function escapeHtml(value: unknown): string;
55
+ export declare const applyHeadEntriesToDocument: (entries: HeadInput[]) => void;
56
+ export declare const withHeadStore: <T>(store: HeadStore, fn: () => T | Promise<T>) => Promise<T>;
57
+ /**
58
+ * Contribute `<head>` data from inside a page, layout, or island render.
59
+ *
60
+ * During SSR (or a client mount with an open browser head window), entries
61
+ * collect into the active store. On the client with no store, the entry is
62
+ * applied to `document` immediately.
63
+ */
64
+ export declare const head: (input: HeadInput) => void;
65
+ export interface BrowserHeadSession {
66
+ flush: () => void;
67
+ close: () => void;
68
+ }
69
+ /**
70
+ * Open a browser head collection window for a client route mount.
71
+ * Nested page/layout `head()` calls may land after `mount()` returns; keep
72
+ * the window open until `close()`, and flush on a microtask (or via `flush()`).
73
+ */
74
+ export declare const openBrowserHead: () => BrowserHeadSession;
62
75
  /**
63
76
  * Merge head entries in contribution order (loader first as the base, then
64
77
  * render-time outer→inner layouts, then the page) and serialize. Later entries
65
78
  * win on collision; the last `titleTemplate` wraps the resolved title.
66
79
  */
67
- export declare function serializeHead(entries: HeadInput[]): SerializedHead;
80
+ export declare const serializeHead: (entries: HeadInput[]) => SerializedHead;
81
+ export {};
package/dist/http.d.ts CHANGED
@@ -21,7 +21,7 @@ export interface HttpResponseOptions {
21
21
  * a low-level helper — prefer {@link RouterBuilder.respond} for the full
22
22
  * render+head+headers pipeline.
23
23
  */
24
- export declare function httpResponse(body: string | null, options?: HttpResponseOptions): Response;
24
+ export declare const httpResponse: (body: string | null, options?: HttpResponseOptions) => Response;
25
25
  export declare const EMPTY_HEAD: SerializedHead;
26
26
  /**
27
27
  * Options for {@link RouterBuilder.respond}.
package/dist/index.d.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import type { Component, View } from "ilha";
2
- import { type SerializedHead } from "./head";
3
- import { type HttpResponseOptions } from "./http";
2
+ import type { SerializedHead } from "./head";
3
+ import type { HttpResponseOptions } from "./http";
4
4
  export { setHistoryMode, getHistoryMode } from "./hash";
5
5
  export type { HistoryMode } from "./hash";
6
6
  export { head, serializeHead } from "./head";
7
7
  export type { HeadInput, SerializedHead } from "./head";
8
8
  export { httpResponse, EMPTY_HEAD } from "./http";
9
9
  export type { HttpResponseOptions } from "./http";
10
+ export { RouteError } from "./route-error";
10
11
  export type Page = Component;
11
12
  export interface RouteRecord {
12
13
  pattern: string;
@@ -26,9 +27,14 @@ export interface AppError {
26
27
  export interface IslandContext {
27
28
  request?: Request;
28
29
  }
29
- export type LayoutHandler = (props: {
30
+ export type LayoutProps = {
30
31
  children?: View;
31
- } & Record<string, unknown>) => unknown;
32
+ } & SnapshotPropBag;
33
+ export interface SnapshotPropBag {
34
+ readonly [key: string]: SnapshotPropValue | undefined;
35
+ }
36
+ export type SnapshotPropValue = string | number | boolean | null | View | SnapshotPropValue[] | SnapshotPropBag;
37
+ export type LayoutHandler = (props: LayoutProps) => View | Promise<View>;
32
38
  export type ErrorHandler = (error: AppError, route: RouteSnapshot) => Page | View;
33
39
  export declare class Redirect {
34
40
  readonly __ilhaRedirect: true;
@@ -36,17 +42,10 @@ export declare class Redirect {
36
42
  readonly status: number;
37
43
  constructor(to: string, status?: number);
38
44
  }
39
- export declare class RouteError {
40
- readonly __ilhaRouteError: true;
41
- readonly status: number;
42
- readonly message: string;
43
- constructor(status: number, message: string);
44
- }
45
- export declare function redirect(to: string, status?: number): never;
46
- export declare function error(status: number, message: string): never;
47
- export declare function wrapLayout(layout: LayoutHandler, page: Page): Page;
48
- export declare function wrapError(handler: ErrorHandler, page: Page): Page;
49
- export declare function defineLayout(layout: LayoutHandler): LayoutHandler;
45
+ export declare const redirect: (to: string, status?: number) => never;
46
+ declare const throwAppError: (status: number, message: string) => never;
47
+ export { throwAppError as error };
48
+ export declare const defineLayout: (layout: LayoutHandler) => LayoutHandler;
50
49
  export interface NavigateOptions {
51
50
  replace?: boolean;
52
51
  scroll?: boolean;
@@ -85,27 +84,24 @@ export interface RespondOptions extends HttpResponseOptions {
85
84
  markers?: boolean;
86
85
  shell?: (head: SerializedHead, html: string) => string;
87
86
  }
87
+ export interface RenderPageOptions {
88
+ timeout?: number;
89
+ snapshot?: boolean;
90
+ markers?: boolean;
91
+ }
88
92
  export interface RouterBuilder {
89
- route(pattern: string, page: Page): RouterBuilder;
90
- errorBoundary(pattern: string, handler: ErrorHandler): RouterBuilder;
91
- routes(): RouteRecord[];
92
- prime(): void;
93
- mount(target: string | Element, options?: MountOptions): () => void;
94
- render(url: string | URL | Request, options?: {
95
- timeout?: number;
96
- snapshot?: boolean;
97
- markers?: boolean;
98
- }): Promise<string>;
99
- renderResponse(url: string | URL | Request, options?: {
100
- timeout?: number;
101
- snapshot?: boolean;
102
- markers?: boolean;
103
- }): Promise<RenderResponse>;
104
- respond(url: string | URL | Request, options?: RespondOptions): Promise<Response>;
105
- hydrate(options?: {
93
+ route: (pattern: string, page: Page) => RouterBuilder;
94
+ errorBoundary: (pattern: string, handler: ErrorHandler) => RouterBuilder;
95
+ routes: () => RouteRecord[];
96
+ prime: () => void;
97
+ mount: (target: string | Element, options?: MountOptions) => () => void;
98
+ render: (url: string | URL | Request, options?: RenderPageOptions) => Promise<string>;
99
+ renderResponse: (url: string | URL | Request, options?: RenderPageOptions) => Promise<RenderResponse>;
100
+ respond: (url: string | URL | Request, options?: RespondOptions) => Promise<Response>;
101
+ hydrate: (options?: {
106
102
  root?: Element;
107
103
  interceptLinks?: boolean;
108
- }): () => void;
104
+ }) => () => void;
109
105
  }
110
106
  export interface Navigation {
111
107
  from: string;
@@ -117,36 +113,37 @@ type BeforeNavigateHook = (nav: Navigation & {
117
113
  cancel: () => void;
118
114
  }) => void;
119
115
  type AfterNavigateHook = (nav: Navigation) => void;
120
- export declare function routePath(): string;
121
- export declare function routeParams(): Record<string, string>;
122
- export declare function routeSearch(): string;
123
- export declare function routeHash(): string;
124
- export declare function navigating(): boolean;
125
- export declare function useRoute(): {
126
- path: typeof routePath;
127
- params: typeof routeParams;
128
- search: typeof routeSearch;
129
- hash: typeof routeHash;
130
- navigating: typeof navigating;
116
+ export declare const wrapLayout: (layout: LayoutHandler, page: Page) => Page;
117
+ export declare const wrapError: (handler: ErrorHandler, page: Page) => Page;
118
+ export declare const routePath: () => string;
119
+ export declare const routeParams: () => Record<string, string>;
120
+ export declare const routeSearch: () => string;
121
+ export declare const routeHash: () => string;
122
+ export declare const navigating: () => boolean;
123
+ export declare const useRoute: () => {
124
+ hash: () => string;
125
+ navigating: () => boolean;
126
+ params: () => Record<string, string>;
127
+ path: () => string;
128
+ search: () => string;
131
129
  };
132
- export declare function useContext(): {
130
+ export declare const useContext: () => {
133
131
  request: Request | undefined;
134
132
  };
135
- export declare function prime(): void;
136
- export declare function beforeNavigate(fn: BeforeNavigateHook): () => void;
137
- export declare function afterNavigate(fn: AfterNavigateHook): () => void;
138
- export declare function navigate(to: string, opts?: NavigateOptions): void;
139
- export interface LinkInterceptionOptions {
140
- }
141
- export declare function enableLinkInterception(root?: Element | Document, _options?: LinkInterceptionOptions): () => void;
133
+ export declare const prime: () => void;
134
+ export declare const beforeNavigate: (fn: BeforeNavigateHook) => (() => void);
135
+ export declare const afterNavigate: (fn: AfterNavigateHook) => (() => void);
136
+ export declare const navigate: (to: string, opts?: NavigateOptions) => void;
137
+ export type LinkInterceptionOptions = Record<string, never>;
138
+ export declare const enableLinkInterception: (root?: Element | Document, _options?: LinkInterceptionOptions) => (() => void);
142
139
  export interface IsActiveOptions {
143
140
  end?: boolean;
144
141
  }
145
- export declare function isActive(pattern: string, options?: IsActiveOptions): boolean;
146
- export declare function resolveRedirectTarget(to: string, base: URL, allowExternal: boolean): {
142
+ export declare const isActive: (pattern: string, options?: IsActiveOptions) => boolean;
143
+ export declare const resolveRedirectTarget: (to: string, base: URL, allowExternal: boolean) => {
147
144
  ok: true;
148
145
  to: string;
149
146
  } | {
150
147
  ok: false;
151
148
  };
152
- export declare function router(options?: RouterOptions): RouterBuilder;
149
+ export declare const router: (options?: RouterOptions) => RouterBuilder;