@kasperrt/smolter 0.4.2 → 0.4.4

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.
Files changed (151) hide show
  1. package/.generated/client/hash-history.ts +27 -0
  2. package/.generated/client/hydration.ts +98 -0
  3. package/.generated/client/root-data.ts +52 -0
  4. package/.generated/core/environment.ts +1 -0
  5. package/.generated/core/navigation.ts +1 -0
  6. package/.generated/core/prepare-routes.ts +50 -0
  7. package/.generated/core/presentation.ts +1 -1
  8. package/.generated/core/responses.ts +12 -0
  9. package/.generated/core/utils.ts +5 -2
  10. package/.generated/preact/await.tsx +1 -0
  11. package/.generated/preact/client.tsx +8 -27
  12. package/.generated/preact/deferred-hydration.ts +1 -98
  13. package/.generated/preact/document.tsx +10 -0
  14. package/.generated/preact/index.ts +11 -2
  15. package/.generated/preact/lazy-route.ts +7 -0
  16. package/.generated/preact/navigation.tsx +19 -5
  17. package/.generated/preact/root-data.ts +1 -45
  18. package/.generated/preact/route-hooks.tsx +12 -2
  19. package/.generated/preact/route-types.ts +14 -0
  20. package/.generated/preact/router.tsx +5 -48
  21. package/.generated/react/client.tsx +130 -0
  22. package/.generated/react/document.tsx +235 -0
  23. package/.generated/react/fetcher.ts +29 -7
  24. package/.generated/react/hooks.ts +20 -18
  25. package/.generated/react/index.ts +28 -2
  26. package/.generated/react/lazy-route.ts +116 -0
  27. package/.generated/react/link.ts +1 -38
  28. package/.generated/react/navigation-state.ts +13 -0
  29. package/.generated/react/navigation.ts +145 -0
  30. package/.generated/react/outlet.ts +48 -0
  31. package/.generated/react/root-context.ts +22 -0
  32. package/.generated/react/route-hooks.ts +159 -0
  33. package/.generated/react/route-types.ts +91 -0
  34. package/.generated/react/router.ts +50 -0
  35. package/.generated/react/runtime.ts +1 -0
  36. package/.generated/server/application.tsx +18 -250
  37. package/.generated/server/assets.ts +7 -0
  38. package/.generated/server/dispatch.ts +87 -0
  39. package/.generated/server/handler.ts +3 -0
  40. package/.generated/server/http.ts +2 -0
  41. package/.generated/server/index.ts +2 -0
  42. package/.generated/server/node.ts +5 -2
  43. package/.generated/server/preact.ts +5 -2
  44. package/.generated/server/react.ts +34 -0
  45. package/.generated/server/render-application.ts +203 -0
  46. package/.generated/vite/application/lazy-files.ts +2 -1
  47. package/.generated/vite/application/vite.ts +12 -4
  48. package/.generated/vite/react.ts +11 -0
  49. package/CHANGELOG.md +20 -0
  50. package/README.md +48 -48
  51. package/dist/client/hash-history.d.ts +2 -0
  52. package/dist/client/hash-history.js +26 -0
  53. package/dist/client/hydration.d.ts +10 -0
  54. package/dist/{preact/deferred-hydration.js → client/hydration.js} +1 -1
  55. package/dist/client/root-data.d.ts +14 -0
  56. package/dist/{preact → client}/root-data.js +1 -1
  57. package/dist/core/environment.d.ts +1 -0
  58. package/dist/core/navigation.d.ts +1 -0
  59. package/dist/core/prepare-routes.d.ts +7 -0
  60. package/dist/core/prepare-routes.js +28 -0
  61. package/dist/core/presentation.js +1 -1
  62. package/dist/core/responses.d.ts +12 -0
  63. package/dist/core/responses.js +5 -0
  64. package/dist/core/utils.d.ts +5 -2
  65. package/dist/core/utils.js +1 -2
  66. package/dist/preact/await.d.ts +1 -0
  67. package/dist/preact/await.js +1 -0
  68. package/dist/preact/client.d.ts +7 -1
  69. package/dist/preact/client.js +11 -27
  70. package/dist/preact/deferred-hydration.d.ts +1 -10
  71. package/dist/preact/document.d.ts +10 -0
  72. package/dist/preact/document.js +11 -2
  73. package/dist/preact/index.d.ts +6 -2
  74. package/dist/preact/index.js +1 -1
  75. package/dist/preact/lazy-route.d.ts +2 -0
  76. package/dist/preact/lazy-route.js +6 -0
  77. package/dist/preact/navigation-state.js +1 -1
  78. package/dist/preact/navigation.d.ts +6 -0
  79. package/dist/preact/navigation.js +9 -5
  80. package/dist/preact/root-data.d.ts +1 -8
  81. package/dist/preact/route-hooks.d.ts +10 -0
  82. package/dist/preact/route-hooks.js +13 -3
  83. package/dist/preact/route-types.d.ts +14 -0
  84. package/dist/preact/router.d.ts +5 -2
  85. package/dist/preact/router.js +4 -26
  86. package/dist/react/client.d.ts +20 -0
  87. package/dist/react/client.js +86 -0
  88. package/dist/react/document.d.ts +25 -0
  89. package/dist/react/document.js +169 -0
  90. package/dist/react/fetcher.d.ts +8 -4
  91. package/dist/react/fetcher.js +12 -1
  92. package/dist/react/form.js +1 -1
  93. package/dist/react/hooks.d.ts +7 -6
  94. package/dist/react/index.d.ts +13 -2
  95. package/dist/react/index.js +7 -2
  96. package/dist/react/lazy-route.d.ts +22 -0
  97. package/dist/react/lazy-route.js +57 -0
  98. package/dist/react/link.d.ts +1 -12
  99. package/dist/react/navigation-state.d.ts +2 -0
  100. package/dist/react/navigation-state.js +9 -0
  101. package/dist/react/navigation.d.ts +33 -0
  102. package/dist/react/navigation.js +64 -0
  103. package/dist/react/outlet.d.ts +16 -0
  104. package/dist/react/outlet.js +26 -0
  105. package/dist/react/root-context.d.ts +14 -0
  106. package/dist/react/root-context.js +10 -0
  107. package/dist/react/route-hooks.d.ts +34 -0
  108. package/dist/react/route-hooks.js +89 -0
  109. package/dist/react/route-types.d.ts +69 -0
  110. package/dist/react/router.d.ts +10 -0
  111. package/dist/react/router.js +22 -0
  112. package/dist/react/runtime.d.ts +1 -1
  113. package/dist/react/runtime.js +1 -1
  114. package/dist/server/application.d.ts +4 -5
  115. package/dist/server/application.js +9 -154
  116. package/dist/server/assets.d.ts +2 -0
  117. package/dist/server/assets.js +6 -0
  118. package/dist/server/dispatch.d.ts +5 -0
  119. package/dist/server/dispatch.js +50 -0
  120. package/dist/server/handler.d.ts +3 -0
  121. package/dist/server/http.d.ts +2 -0
  122. package/dist/server/http.js +2 -0
  123. package/dist/server/index.d.ts +1 -0
  124. package/dist/server/index.js +2 -1
  125. package/dist/server/node.d.ts +5 -1
  126. package/dist/server/preact.d.ts +5 -1
  127. package/dist/server/preact.js +2 -1
  128. package/dist/server/react.d.ts +10 -0
  129. package/dist/server/react.js +22 -0
  130. package/dist/server/render-application.d.ts +31 -0
  131. package/dist/server/render-application.js +118 -0
  132. package/dist/vite/application/lazy-files.d.ts +1 -1
  133. package/dist/vite/application/lazy-files.js +2 -2
  134. package/dist/vite/application/vite.d.ts +2 -1
  135. package/dist/vite/application/vite.js +12 -5
  136. package/dist/vite/react.d.ts +4 -0
  137. package/dist/vite/react.js +9 -1
  138. package/jsr.json +10 -6
  139. package/package.json +21 -12
  140. package/.generated/core/resource-types.ts +0 -44
  141. package/.generated/core/resources.ts +0 -444
  142. package/.generated/react/data.ts +0 -25
  143. package/.generated/react/resources/components.tsx +0 -404
  144. package/dist/core/resource-types.d.ts +0 -37
  145. package/dist/core/resources.d.ts +0 -19
  146. package/dist/core/resources.js +0 -345
  147. package/dist/react/data.d.ts +0 -5
  148. package/dist/react/data.js +0 -3
  149. package/dist/react/link.js +0 -27
  150. package/dist/react/resources/components.d.ts +0 -64
  151. package/dist/react/resources/components.js +0 -239
@@ -1,12 +1 @@
1
- /**
2
- * React components and typed data hooks. Import from @kasperrt/smolter/react.
3
- * @module
4
- */
5
- import { type AnchorHTMLAttributes, type ReactNode } from "./runtime.ts";
6
- /** Anchor attributes with internal navigation and optional history replacement. */
7
- export interface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
8
- href: string;
9
- replace?: boolean;
10
- }
11
- /** Render an anchor that handles internal navigation and preserves native modified clicks. */
12
- export declare function Link(options: LinkProps): ReactNode;
1
+ export { Link, type LinkProps } from "./navigation.ts";
@@ -0,0 +1,2 @@
1
+ import type { Router } from "../core/index.ts";
2
+ export declare function useNavigationSnapshot(router: Router): import("../core/navigation.ts").NavigationSnapshot;
@@ -0,0 +1,9 @@
1
+ import { useSyncExternalStore } from "./runtime.js";
2
+ import { navigationStore } from "../core/navigation.js";
3
+ //#region .generated/react/navigation-state.ts
4
+ function useNavigationSnapshot(router) {
5
+ const store = navigationStore(router);
6
+ return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
7
+ }
8
+ //#endregion
9
+ export { useNavigationSnapshot };
@@ -0,0 +1,33 @@
1
+ import { type NavigateOptions } from "../core/index.ts";
2
+ import type { AnchorHTMLAttributes, ReactNode } from "./runtime.ts";
3
+ export type { NavigateOptions } from "../core/index.ts";
4
+ export type { NavigationType } from "../core/navigation.ts";
5
+ /** Navigate to a URL or move through browser history using a numeric offset. */
6
+ export type NavigateFunction = (to: string | number | Partial<Pick<URL, "pathname" | "search" | "hash">>, options?: NavigateOptions) => void;
7
+ /** Anchor properties and router navigation options, including document reload and prefetch controls. */
8
+ export interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"> {
9
+ to?: string;
10
+ href?: string;
11
+ replace?: boolean;
12
+ prefetch?: string;
13
+ state?: unknown;
14
+ preventScrollReset?: boolean;
15
+ viewTransition?: boolean;
16
+ reloadDocument?: boolean;
17
+ }
18
+ /** Active and pending flags supplied to NavLink render callbacks. */
19
+ export interface NavLinkRenderProps {
20
+ isActive: boolean;
21
+ isPending: boolean;
22
+ }
23
+ /** Link options with matching rules and state-aware class or children callbacks. */
24
+ export interface NavLinkProps extends Omit<LinkProps, "className" | "children"> {
25
+ end?: boolean;
26
+ className?: string | ((state: NavLinkRenderProps) => string);
27
+ children?: ReactNode | ((state: NavLinkRenderProps) => ReactNode);
28
+ }
29
+ /** Render an anchor with same-origin router navigation and optional intent prefetching. */
30
+ export declare function Link(options: LinkProps): ReactNode;
31
+ /** Render a Link with active-route state and aria-current for the matching destination. */
32
+ export declare function NavLink({ href, to, end, className, children, ...props }: NavLinkProps): ReactNode;
33
+ export { useNavigate, useNavigationType, useSearchParams, useURLLocation, } from "./route-hooks.ts";
@@ -0,0 +1,64 @@
1
+ import { matchRoutes } from "../core/match.js";
2
+ import { navigationClick } from "../core/url.js";
3
+ import { createElement } from "./runtime.js";
4
+ import { useRouter, useSnapshot } from "./context.js";
5
+ import { handoffDocument, resolveApplicationURL } from "../core/environment.js";
6
+ import { useNavigate, useNavigation, useNavigationType, useSearchParams, useURLLocation } from "./route-hooks.js";
7
+ //#region .generated/react/navigation.ts
8
+ /** Render an anchor with same-origin router navigation and optional intent prefetching. */
9
+ function Link(options) {
10
+ const { href: hrefProp, to = hrefProp ?? "/", replace, preventScrollReset, viewTransition, prefetch, state, reloadDocument, ...props } = options;
11
+ const router = useRouter();
12
+ const current = new URL(useSnapshot("url"));
13
+ const destination = resolveApplicationURL(new URL(to, current));
14
+ const internal = destination.origin === current.origin;
15
+ const preload = () => {
16
+ (matchRoutes(router.routes, destination.pathname)?.route)?.preload?.().catch(() => {});
17
+ };
18
+ if (!reloadDocument && prefetch !== "none" && internal) for (const name of [
19
+ "onPointerEnter",
20
+ "onFocus",
21
+ "onTouchStart"
22
+ ]) {
23
+ const handler = props[name];
24
+ props[name] = (event) => {
25
+ handler?.(event);
26
+ preload();
27
+ };
28
+ }
29
+ return createElement("a", {
30
+ ...props,
31
+ href: destination.href,
32
+ onClick(event) {
33
+ props.onClick?.(event);
34
+ if (reloadDocument || !navigationClick(event, event.currentTarget)) return;
35
+ if (!internal) {
36
+ handoffDocument(event.nativeEvent, destination);
37
+ return;
38
+ }
39
+ event.preventDefault();
40
+ router.navigate(destination.href, options);
41
+ }
42
+ });
43
+ }
44
+ /** Render a Link with active-route state and aria-current for the matching destination. */
45
+ function NavLink({ href, to = href ?? "/", end, className, children, ...props }) {
46
+ const location = useURLLocation();
47
+ const destination = resolveApplicationURL(new URL(to, location.href));
48
+ const target = destination.pathname;
49
+ const isActive = destination.origin === location.origin && (location.pathname === target || !end && target !== "/" && location.pathname.startsWith(`${target}/`));
50
+ const pending = useNavigation().location;
51
+ const state = {
52
+ isActive,
53
+ isPending: !!pending && pending.origin === destination.origin && (pending.pathname === target || !end && target !== "/" && pending.pathname.startsWith(`${target}/`))
54
+ };
55
+ return createElement(Link, {
56
+ ...props,
57
+ to,
58
+ className: typeof className === "function" ? className(state) : className,
59
+ ...isActive && { "aria-current": "page" },
60
+ children: typeof children === "function" ? children(state) : children
61
+ });
62
+ }
63
+ //#endregion
64
+ export { Link, NavLink, useNavigate, useNavigationType, useSearchParams, useURLLocation };
@@ -0,0 +1,16 @@
1
+ import { type ReactNode } from "./runtime.ts";
2
+ /** Optional children and context shared with descendants through useOutletContext. */
3
+ export interface OutletProps {
4
+ context?: unknown;
5
+ children?: ReactNode;
6
+ }
7
+ /** Render the active route under a layout's shared context. */
8
+ export declare function Outlet({ context, children }: OutletProps): ReactNode;
9
+ /** Read the nearest Outlet's context. */
10
+ export declare function useOutletContext<T>(): T;
11
+ /** Read the active flat route match; this is not a nested layout match chain. */
12
+ export declare function useMatches(): {
13
+ id: string;
14
+ data: unknown;
15
+ params: import("../core/index.ts").Params;
16
+ }[];
@@ -0,0 +1,26 @@
1
+ import { createContext, createElement, useContext } from "./runtime.js";
2
+ import { useRouter, useSnapshot } from "./context.js";
3
+ import { Routes } from "./routes.js";
4
+ //#region .generated/react/outlet.ts
5
+ var OutletContext = createContext(void 0);
6
+ /** Render the active route under a layout's shared context. */
7
+ function Outlet({ context, children }) {
8
+ const inherited = useContext(OutletContext);
9
+ return createElement(OutletContext.Provider, { value: context ?? inherited }, children ?? createElement(Routes, { fallback: createElement("p", { role: "status" }, "Loading…") }));
10
+ }
11
+ /** Read the nearest Outlet's context. */
12
+ function useOutletContext() {
13
+ return useContext(OutletContext);
14
+ }
15
+ /** Read the active flat route match; this is not a nested layout match chain. */
16
+ function useMatches() {
17
+ const router = useRouter();
18
+ const snapshot = useSnapshot();
19
+ return [{
20
+ id: router.routes.find((item) => item.path === snapshot.path)?.id ?? "",
21
+ data: snapshot.data,
22
+ params: snapshot.params
23
+ }];
24
+ }
25
+ //#endregion
26
+ export { Outlet, useMatches, useOutletContext };
@@ -0,0 +1,14 @@
1
+ import type { Router } from "../core/index.ts";
2
+ import type { AppRoute, RootModule } from "./route-types.ts";
3
+ export interface ApplicationContext {
4
+ router: Router;
5
+ routes: AppRoute[];
6
+ root: unknown;
7
+ rootError?: boolean;
8
+ entry?: string;
9
+ streaming?: boolean;
10
+ initialData?: string;
11
+ rootModule?: RootModule;
12
+ }
13
+ export declare const Application: import("react").Context<ApplicationContext | null>;
14
+ export declare function useApplication(): ApplicationContext;
@@ -0,0 +1,10 @@
1
+ import { createContext, useContext } from "./runtime.js";
2
+ //#region .generated/react/root-context.ts
3
+ var Application = createContext(null);
4
+ function useApplication() {
5
+ const app = useContext(Application);
6
+ if (!app) throw new Error("Root data requires RouterApplication.");
7
+ return app;
8
+ }
9
+ //#endregion
10
+ export { Application, useApplication };
@@ -0,0 +1,34 @@
1
+ import type { NavigateOptions } from "../core/index.ts";
2
+ import type { NavigationType } from "../core/navigation.ts";
3
+ import { type Submission, type SubmissionOptions } from "../core/submission.ts";
4
+ import { useSnapshot } from "./context.ts";
5
+ import type { NavigateFunction } from "./navigation.ts";
6
+ import type { RouteData } from "./route-types.ts";
7
+ /** Read root loader data from the application context. */
8
+ export declare function useRootData<T = unknown>(): RouteData<T>;
9
+ /** Read root data or the active route's data by its manifest identifier. */
10
+ export declare function useRouteLoaderData<T = unknown>(id: string): RouteData<T> | undefined;
11
+ export { useSnapshot };
12
+ /** Read the current route error for an error boundary. */
13
+ export declare function useRouteError(): Error | undefined;
14
+ /** Read the current page navigation or submission state. */
15
+ export declare function useNavigation(): {
16
+ state: "idle" | "loading" | "submitting";
17
+ location: URL | undefined;
18
+ };
19
+ /** Return a function that submits form data through the current router. */
20
+ export declare function useSubmit(): (value: Submission, options?: SubmissionOptions) => Promise<void> | Promise<import("../core/index.ts").Result<unknown>>;
21
+ /** Read the current router location as a URL. */
22
+ export declare function useURLLocation(): URL & {
23
+ state: unknown;
24
+ key: string;
25
+ };
26
+ /** Return the application navigation function, including history offsets and relative destinations. */
27
+ export declare function useNavigate(): NavigateFunction;
28
+ /** Read URL search parameters and update them through router navigation. */
29
+ export declare function useSearchParams(): [
30
+ URLSearchParams,
31
+ (value: URLSearchParams | Record<string, string>, options?: NavigateOptions) => void
32
+ ];
33
+ /** Read the last browser history operation. */
34
+ export declare function useNavigationType(): NavigationType;
@@ -0,0 +1,89 @@
1
+ import { submissionBody, submissionSearch } from "../core/submission.js";
2
+ import { useCallback, useMemo } from "./runtime.js";
3
+ import { useRouter, useSnapshot } from "./context.js";
4
+ import { pendingLocation } from "../core/presentation.js";
5
+ import { useNavigationSnapshot } from "./navigation-state.js";
6
+ import { useApplication } from "./root-context.js";
7
+ //#region .generated/react/route-hooks.ts
8
+ /** Read root loader data from the application context. */
9
+ function useRootData() {
10
+ return useApplication().root;
11
+ }
12
+ /** Read root data or the active route's data by its manifest identifier. */
13
+ function useRouteLoaderData(id) {
14
+ const app = useApplication();
15
+ const snapshot = useSnapshot();
16
+ if (id === "root") return app.root;
17
+ const route = app.routes.find((item) => item.id === id);
18
+ if (!route || route.path !== snapshot.path) return void 0;
19
+ return snapshot.data;
20
+ }
21
+ /** Read the current route error for an error boundary. */
22
+ function useRouteError() {
23
+ return useSnapshot("error");
24
+ }
25
+ /** Read the current page navigation or submission state. */
26
+ function useNavigation() {
27
+ const router = useRouter();
28
+ const snapshot = useSnapshot();
29
+ const location = pendingLocation(router);
30
+ return useMemo(() => ({
31
+ state: snapshot.state,
32
+ location
33
+ }), [snapshot.state, location?.href]);
34
+ }
35
+ /** Return a function that submits form data through the current router. */
36
+ function useSubmit() {
37
+ const router = useRouter();
38
+ return useCallback((value, options = {}) => {
39
+ const body = submissionBody(value, options.encType);
40
+ const method = options.method?.toUpperCase();
41
+ const target = options.action ?? router.getSnapshot().url;
42
+ if (method === "GET") {
43
+ const url = new URL(target, router.getSnapshot().url);
44
+ url.search = submissionSearch(body);
45
+ return router.navigate(url.href, options);
46
+ }
47
+ return router.submit(target, body, { method });
48
+ }, [router]);
49
+ }
50
+ /** Read the current router location as a URL. */
51
+ function useURLLocation() {
52
+ const href = useSnapshot("url");
53
+ const snapshot = useNavigationSnapshot(useRouter());
54
+ return useMemo(() => Object.assign(new URL(href), {
55
+ state: snapshot.state,
56
+ key: snapshot.key
57
+ }), [
58
+ href,
59
+ snapshot.state,
60
+ snapshot.key
61
+ ]);
62
+ }
63
+ /** Return the application navigation function, including history offsets and relative destinations. */
64
+ function useNavigate() {
65
+ const router = useRouter();
66
+ return useCallback((to, options) => {
67
+ if (typeof to === "number") {
68
+ window.history.go(to);
69
+ return;
70
+ }
71
+ const current = new URL(router.getSnapshot().url);
72
+ const target = typeof to === "string" ? to : `${to.pathname ?? current.pathname}${to.search ?? current.search}${to.hash ?? current.hash}`;
73
+ router.navigate(target, options);
74
+ }, [router]);
75
+ }
76
+ /** Read URL search parameters and update them through router navigation. */
77
+ function useSearchParams() {
78
+ const location = useURLLocation();
79
+ const navigate = useNavigate();
80
+ return [useMemo(() => new URLSearchParams(location.search), [location.search]), useCallback((value, options) => {
81
+ navigate(`${location.pathname}?${new URLSearchParams(value)}`, options);
82
+ }, [location.pathname, navigate])];
83
+ }
84
+ /** Read the last browser history operation. */
85
+ function useNavigationType() {
86
+ return useNavigationSnapshot(useRouter()).type;
87
+ }
88
+ //#endregion
89
+ export { useNavigate, useNavigation, useNavigationType, useRootData, useRouteError, useRouteLoaderData, useSearchParams, useSnapshot, useSubmit, useURLLocation };
@@ -0,0 +1,69 @@
1
+ import type { DataArgs, RevalidationArgs, Route, Snapshot } from "../core/index.ts";
2
+ import type { DataResponse } from "../core/responses.ts";
3
+ import type { ComponentType, ReactNode } from "./runtime.ts";
4
+ /** Request, route parameters and cancellation signal supplied to a loader. */
5
+ export interface LoaderFunctionArgs extends DataArgs {
6
+ context?: unknown;
7
+ }
8
+ /** Request, route parameters and cancellation signal supplied to an action. */
9
+ export type ActionFunctionArgs = LoaderFunctionArgs;
10
+ /** Loader arguments with a serverLoader callback for the server transport. */
11
+ export interface ClientLoaderFunctionArgs extends LoaderFunctionArgs {
12
+ serverLoader: <T = unknown>() => Promise<T>;
13
+ }
14
+ /** Action arguments with a serverAction callback for the server transport. */
15
+ export interface ClientActionFunctionArgs extends LoaderFunctionArgs {
16
+ serverAction: <T = unknown>() => Promise<T>;
17
+ }
18
+ /** Navigation or mutation context used to decide whether loader data should refresh. */
19
+ export type ShouldRevalidateFunctionArgs = RevalidationArgs;
20
+ type UnwrapData<T> = T extends DataResponse<infer V> ? V : T;
21
+ /** Infer successful loader data, unwrapping promises and data responses. */
22
+ export type RouteData<T> = T extends (...args: never[]) => infer R ? UnwrapData<Exclude<Awaited<R>, Response | Error>> : T;
23
+ /** A document metadata descriptor, such as a title, meta tag or structured-data record. */
24
+ export type MetaDescriptor = Record<string, unknown>;
25
+ /** Loader data, error, parameters and location supplied to a route metadata function. */
26
+ export interface MetaArgs<T> {
27
+ data: RouteData<T> | undefined;
28
+ loaderData: RouteData<T> | undefined;
29
+ error?: unknown;
30
+ params: Record<string, string>;
31
+ location: URL;
32
+ }
33
+ /** Derive document metadata descriptors from the current route data and location. */
34
+ export type MetaFunction<T = unknown> = (args: MetaArgs<T>) => MetaDescriptor[];
35
+ /** Return link descriptors rendered in the document head. */
36
+ export type LinksFunction = () => Record<string, string>[];
37
+ /** Data route with optional lazy component, preload and document metadata integration. */
38
+ export interface AppRoute extends Route {
39
+ resolveComponent?: () => ComponentType | undefined;
40
+ preload?: () => Promise<void>;
41
+ id?: string;
42
+ routePath?: string;
43
+ meta?: MetaFunction;
44
+ }
45
+ /** Root document components, loader, metadata and revalidation policy for a React application. */
46
+ export interface RootModule {
47
+ /** Route identity used by targeted revalidation. Defaults to root. */
48
+ id?: string;
49
+ /** Optional registered route URL used to revalidate root data after mutations. */
50
+ loaderPath?: string;
51
+ ErrorBoundary?: ComponentType;
52
+ default: ComponentType;
53
+ Layout: ComponentType<{
54
+ children: ReactNode;
55
+ }>;
56
+ loader?: (args: LoaderFunctionArgs) => unknown;
57
+ links?: LinksFunction;
58
+ meta?: MetaFunction;
59
+ shouldRevalidate?: (args: RevalidationArgs) => boolean;
60
+ }
61
+ /** Serialized root data and route snapshot used to hydrate without repeating initial loaders. */
62
+ export interface Hydration {
63
+ rootError?: boolean;
64
+ root: unknown;
65
+ snapshot: Snapshot;
66
+ }
67
+ /** Transform a data transport response using the current request context. */
68
+ export type HandleDataRequestFunction = (response: Response, args: LoaderFunctionArgs) => Response | Promise<Response>;
69
+ export {};
@@ -0,0 +1,10 @@
1
+ import { createStore } from "../core/store.ts";
2
+ import { type ApplicationContext } from "./root-context.ts";
3
+ import { type ReactNode } from "./runtime.ts";
4
+ /** Root data, document components and request-scoped router for an application. */
5
+ export interface ApplicationProps extends ApplicationContext {
6
+ document?: boolean;
7
+ rootStore?: ReturnType<typeof createStore>;
8
+ }
9
+ /** Compose a persistent root layout with route views and observable root loader data. */
10
+ export declare function RouterApplication(props: ApplicationProps): ReactNode;
@@ -0,0 +1,22 @@
1
+ import { createElement, useMemo, useSyncExternalStore } from "./runtime.js";
2
+ import { RouterProvider } from "./context.js";
3
+ import { createStore } from "../core/store.js";
4
+ import { Application } from "./root-context.js";
5
+ import { Outlet } from "./outlet.js";
6
+ //#region .generated/react/router.ts
7
+ /** Compose a persistent root layout with route views and observable root loader data. */
8
+ function RouterApplication(props) {
9
+ const store = useMemo(() => props.rootStore ?? createStore(props.root), [props.rootStore, props.root]);
10
+ const root = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
11
+ const snapshot = useSyncExternalStore(props.router.subscribe, props.router.getSnapshot, props.router.getSnapshot);
12
+ const route = props.routes.find((item) => item.path === snapshot.path);
13
+ const Root = snapshot.error && (props.rootError || !route?.ErrorBoundary) ? props.rootModule?.ErrorBoundary ?? Outlet : props.rootModule?.default ?? Outlet;
14
+ const Layout = props.rootModule?.Layout;
15
+ const children = createElement(Root);
16
+ return createElement(RouterProvider, { router: props.router }, createElement(Application.Provider, { value: {
17
+ ...props,
18
+ root
19
+ } }, props.document && Layout ? createElement(Layout, { children }) : children));
20
+ }
21
+ //#endregion
22
+ export { RouterApplication };
@@ -1,2 +1,2 @@
1
1
  export type { AnchorHTMLAttributes, ComponentType, FormHTMLAttributes, MouseEvent, ReactNode, } from "react";
2
- export { createContext, createElement, useContext, useEffect, useMemo, useSyncExternalStore, } from "react";
2
+ export { createContext, createElement, useCallback, useContext, useEffect, useMemo, useSyncExternalStore, } from "react";
@@ -1 +1 @@
1
- export { createContext, createElement, useContext, useEffect, useMemo, useSyncExternalStore } from "react";
1
+ export { createContext, createElement, useCallback, useContext, useEffect, useMemo, useSyncExternalStore } from "react";
@@ -1,8 +1,7 @@
1
1
  /** @jsxImportSource preact */
2
2
  import type { AppRoute, RootModule } from "../preact/route-types.ts";
3
- export interface RenderApplicationOptions {
4
- nonce?: string;
5
- earlyHydration?: boolean;
6
- }
3
+ import { type RenderApplicationOptions } from "./render-application.ts";
4
+ export { dispatchRoute } from "./dispatch.ts";
5
+ export type { RenderApplicationOptions } from "./render-application.ts";
6
+ /** Render a Preact application response with root data and optional early deferred hydration. */
7
7
  export declare function renderApplication(request: Request, routes: AppRoute[], rootModule: RootModule, entry: string, options?: RenderApplicationOptions): Promise<Response>;
8
- export declare function dispatchRoute(request: Request, routes: AppRoute[], document?: boolean): Promise<Response>;
@@ -1,119 +1,17 @@
1
- import { safeWrapAsync } from "../core/errors.js";
2
- import { matchRoutes } from "../core/match.js";
3
- import { createDataRouter } from "../core/router.js";
4
- import { normalizeResult, resolveData } from "../core/responses.js";
5
- import { RouterApplication, prepareRoutes } from "../preact/router.js";
6
- import { privateResponse } from "./private-response.js";
7
- import { loadRouter } from "./index.js";
8
- import { deferredHydration } from "./deferred-hydration.js";
9
- import { progressiveDocument } from "./progressive-document.js";
10
- import { resolveHydration, streamDocument } from "./stream-document.js";
1
+ import { RouterApplication } from "../preact/router.js";
2
+ import { dispatchRoute } from "./dispatch.js";
3
+ import { renderDocument } from "./render-application.js";
11
4
  import { Suspense } from "preact/compat";
12
5
  import { jsx } from "preact/jsx-runtime";
13
6
  import { renderToReadableStream } from "preact-render-to-string/stream";
14
7
  //#region .generated/server/application.tsx
15
- /** @jsxImportSource preact */
16
- async function renderApplication(request, routes, rootModule, entry, options = {}) {
17
- routes = routes.map((route) => ({
18
- ...route,
19
- path: route.routePath ?? route.path
20
- }));
21
- const match = matchRoutes(routes, new URL(request.url).pathname);
22
- if (match && !match.route.default) return dispatchRoute(request, routes, true);
23
- let actionResponse;
24
- let actionData;
25
- if (request.method !== "GET" && request.method !== "HEAD") {
26
- actionResponse = await dispatchRoute(request, routes, true);
27
- if (actionResponse.headers.has("Location") || !actionResponse.headers.get("Content-Type")?.includes("application/json")) return actionResponse;
28
- actionData = await normalizeResult(await actionResponse.json());
29
- }
30
- const rootResult = safeWrapAsync(() => Promise.resolve().then(() => rootModule.loader?.({
31
- request,
32
- params: {},
33
- signal: request.signal
34
- })).then((value) => normalizeResult(value, false), (error) => error instanceof Response ? normalizeResult(error, false) : error instanceof Error ? error : new Error(String(error), { cause: error })));
35
- let redirectResponse;
36
- const prepared = prepareRoutes(routes.map((route) => ({
37
- ...route,
38
- ErrorBoundary: route.ErrorBoundary ?? rootModule.ErrorBoundary
39
- })), true).map((route) => {
40
- if (!route.loader) return route;
41
- const loader = route.loader;
42
- return {
43
- ...route,
44
- loader: async (args) => {
45
- const value = await loader(args);
46
- if (value instanceof Response && value.headers.has("Location")) redirectResponse = value;
47
- return value;
48
- }
49
- };
50
- });
51
- const [rootState, loadedRouter] = await Promise.all([rootResult, loadRouter(prepared, request)]);
52
- let router = loadedRouter;
53
- const rootError = rootState.error ?? (rootState.data instanceof Error ? rootState.data : void 0);
54
- const root = rootError ? void 0 : rootState.data;
55
- if (root instanceof Response && root.headers.has("Location")) {
56
- router.dispose();
57
- return root;
58
- }
59
- if (redirectResponse) {
60
- router.dispose();
61
- return redirectResponse;
62
- }
63
- if (actionResponse || rootError) {
64
- const hydrationData = {
65
- ...router.getSnapshot(),
66
- actionData: actionData instanceof Error ? void 0 : actionData,
67
- ...actionData instanceof Error && { error: actionData },
68
- ...rootError && { error: rootError }
69
- };
70
- router.dispose();
71
- router = createDataRouter(router.routes, { hydrationData });
72
- }
73
- const snapshot = router.getSnapshot();
74
- const hydrationData = {
75
- root,
76
- snapshot: {
77
- ...snapshot,
78
- error: snapshot.error && {
79
- ...snapshot.error,
80
- name: snapshot.error.name,
81
- message: snapshot.error.message
82
- }
83
- },
84
- ...rootError && { rootError: true }
85
- };
86
- const deferred = options.earlyHydration ? deferredHydration(hydrationData, options.nonce) : void 0;
87
- const stream = renderToReadableStream(/* @__PURE__ */ jsx(RouterApplication, {
88
- router,
89
- routes,
90
- root,
8
+ /** Render a Preact application response with root data and optional early deferred hydration. */
9
+ function renderApplication(request, routes, rootModule, entry, options = {}) {
10
+ return renderDocument(request, routes, rootModule, entry, options, (props) => renderToReadableStream(/* @__PURE__ */ jsx(RouterApplication, {
11
+ ...props,
91
12
  rootModule,
92
- entry,
93
- rootError: Boolean(rootError),
94
- streaming: true,
95
- deferredBoundary: options.earlyHydration ? void 0 : deferredBoundary,
96
- initialData: deferred?.initial,
97
- document: true
98
- }), options);
99
- stream.allReady.catch(() => {});
100
- const headers = new Headers(actionResponse?.headers);
101
- headers.delete("Content-Length");
102
- headers.delete("Content-Encoding");
103
- headers.set("Content-Type", "text/html; charset=utf-8");
104
- headers.set("Cache-Control", "no-store");
105
- return new Response(deferred ? progressiveDocument(stream, deferred.stream, {
106
- signal: request.signal,
107
- dispose: () => router.dispose()
108
- }) : streamDocument(stream, {
109
- hydration: resolveHydration(hydrationData),
110
- signal: request.signal,
111
- nonce: options.nonce,
112
- dispose: () => router.dispose()
113
- }), {
114
- status: snapshot.error ? Number("status" in snapshot.error ? snapshot.error.status : 500) : actionResponse?.status ?? 200,
115
- headers
116
- });
13
+ deferredBoundary: options.earlyHydration ? void 0 : deferredBoundary
14
+ }), options));
117
15
  }
118
16
  function deferredBoundary(children, fallback) {
119
17
  return /* @__PURE__ */ jsx(Suspense, {
@@ -121,48 +19,5 @@ function deferredBoundary(children, fallback) {
121
19
  children
122
20
  });
123
21
  }
124
- async function dispatch(request, routes, document = false) {
125
- const match = matchRoutes(routes.map((route) => ({
126
- ...route,
127
- path: route.routePath ?? route.path
128
- })), new URL(request.url).pathname);
129
- if (!match) return Response.json({ error: "No route" }, { status: 404 });
130
- const handler = request.method === "GET" || request.method === "HEAD" ? match.route.loader : match.route.action;
131
- if (!handler) return Response.json({ error: "No handler" }, { status: 405 });
132
- const value = await Promise.resolve().then(() => handler({
133
- request,
134
- params: match.params,
135
- signal: request.signal
136
- })).then((result) => result, (error) => error);
137
- if (value instanceof Response) {
138
- const location = value.headers.get("Location");
139
- if (document) return value;
140
- if (!location) {
141
- if (value.ok) return value;
142
- return Response.json({ __routeError: {
143
- message: await value.text(),
144
- status: value.status,
145
- statusText: value.statusText
146
- } });
147
- }
148
- const headers = new Headers(value.headers);
149
- headers.delete("Location");
150
- headers.set("X-Smolter-Redirect", location);
151
- return new Response(null, { headers });
152
- }
153
- if (value instanceof Error) return Response.json({ __routeError: {
154
- ...value,
155
- message: value.message
156
- } });
157
- if (value && typeof value === "object" && "__routeData" in value) {
158
- const result = value;
159
- return Response.json(await resolveData(result.value), document ? result.init : { headers: result.init.headers });
160
- }
161
- return Response.json(await normalizeResult(value) ?? null);
162
- }
163
- async function dispatchRoute(request, routes, document = false) {
164
- const response = await dispatch(request, routes, document);
165
- return document ? response : privateResponse(response);
166
- }
167
22
  //#endregion
168
23
  export { dispatchRoute, renderApplication };
@@ -1,6 +1,8 @@
1
+ /** Emitted JavaScript and stylesheet URLs associated with a route. */
1
2
  export interface RouteAssets {
2
3
  path: string;
3
4
  files: string[];
4
5
  styles?: string[];
5
6
  }
7
+ /** Collect matched-route JavaScript and stylesheet assets from the production route manifest. */
6
8
  export declare function routePreloads(routes: RouteAssets[], pathname: string, base: string): string;
@@ -1,5 +1,11 @@
1
1
  import { matchRoutes } from "../core/match.js";
2
2
  //#region .generated/server/assets.ts
3
+ /**
4
+ * Production route asset discovery for SSR preload hints.
5
+ *
6
+ * @module
7
+ */
8
+ /** Collect matched-route JavaScript and stylesheet assets from the production route manifest. */
3
9
  function routePreloads(routes, pathname, base) {
4
10
  const route = matchRoutes(routes, pathname)?.route;
5
11
  const prefix = base.replace(/\/$/, "");
@@ -0,0 +1,5 @@
1
+ import { type Route } from "../core/index.ts";
2
+ /** Dispatch a normalized application URL to its route loader or action and return an HTTP response. */
3
+ export declare function dispatchRoute(request: Request, routes: readonly (Route & {
4
+ routePath?: string;
5
+ })[], document?: boolean): Promise<Response>;