vite-plugin-vanjs 0.1.15 → 0.1.17

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/client/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import van from "vanjs-core";
1
2
  import { unwrap } from "../router/unwrap.mjs";
2
3
  import { getTagKey } from "../meta/helpers.mjs";
3
4
 
@@ -225,20 +226,16 @@ function createHydrationContext() {
225
226
  export const hydrate = (target, content) => {
226
227
  if (content instanceof Promise) {
227
228
  content.then((res) => {
228
- if (!target.hasAttribute("data-h")) {
229
- const { diffAndHydrate } = createHydrationContext();
230
- diffAndHydrate(target, res);
231
- target.setAttribute("data-h", "");
232
- } else {
233
- const wrapper = unwrap(res);
234
- target.replaceChildren(
235
- ...(Array.from(wrapper.children)),
236
- );
237
- }
229
+ hydrate(target, res);
230
+ });
231
+ return target;
232
+ }
233
+ if (typeof content === "function") {
234
+ van.derive(() => {
235
+ hydrate(target, content());
238
236
  });
239
237
  return target;
240
238
  }
241
-
242
239
  const wrapper = unwrap(content);
243
240
  const currentChildren = Array.from(target.children);
244
241
  const newChildren = Array.from(wrapper.children);
@@ -257,7 +254,9 @@ export const hydrate = (target, content) => {
257
254
  getTagKey(child) === key
258
255
  );
259
256
  if (existing) {
260
- existing.replaceWith(newChild);
257
+ if (existing.outerHTML !== newChild.outerHTML) {
258
+ existing.replaceWith(newChild);
259
+ }
261
260
  } else {
262
261
  target.appendChild(newChild);
263
262
  }
@@ -268,9 +267,16 @@ export const hydrate = (target, content) => {
268
267
  diffAndHydrate(target, content);
269
268
  target.setAttribute("data-h", "");
270
269
  } else {
271
- target.replaceChildren(...newChildren);
270
+ // target.replaceChildren(...newChildren);
271
+ const parsed = [];
272
+ for (const child of newChildren) {
273
+ parsed.push(
274
+ child instanceof Element
275
+ ? child
276
+ : /* istanbul ignore next */ String(child),
277
+ );
278
+ }
279
+ target.replaceChildren(...parsed);
272
280
  }
273
281
  }
274
-
275
- return target;
276
282
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-vanjs",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "author": "thednp",
5
5
  "license": "MIT",
6
6
  "description": "A mini meta-framework for VanJS powered by Vite",
@@ -15,6 +15,7 @@
15
15
  "setup",
16
16
  "router",
17
17
  "meta",
18
+ "data",
18
19
  "client",
19
20
  "server",
20
21
  "jsx",
@@ -93,7 +94,7 @@
93
94
  "@vitest/ui": "^4.1.5",
94
95
  "happy-dom": "^20.9.0",
95
96
  "typescript": "6.0.3",
96
- "vite": "^8.0.9",
97
+ "vite": "^8.0.10",
97
98
  "vitest": "^4.1.5"
98
99
  },
99
100
  "engines": {
@@ -206,27 +206,29 @@ export const generateRouteProloaders = (route) => {
206
206
  const layoutName = "Module";
207
207
 
208
208
  return `{
209
- preload: async (params) => {
210
- ${
209
+ preload: async (params) => {
210
+ ${
211
211
  route.layouts.map((layout) =>
212
212
  `if (${layout.id + layoutName}?.route?.preload) await ${
213
213
  layout.id + layoutName
214
214
  }?.route?.preload(params);`
215
- ).join("\n ")
215
+ ).join("\n ")
216
216
  }
217
- if (${moduleName}?.route?.preload) await ${moduleName}?.route?.preload(params);
218
- },
219
- load: async (params) => {
220
- ${
217
+ if (${moduleName}?.route?.preload) await ${moduleName}?.route?.preload(params);
218
+ },
219
+ load: async (params) => {
220
+ let _data;
221
+ ${
221
222
  route.layouts.map((layout) =>
222
223
  `if (${layout.id + layoutName}?.route?.load) await ${
223
224
  layout.id + layoutName
224
225
  }?.route?.load(params);`
225
- ).join("\n ")
226
+ ).join("\n ")
226
227
  }
227
- if (${moduleName}?.route?.load) await ${moduleName}?.route?.load(params);
228
- }
229
- }`;
228
+ if (${moduleName}?.route?.load) _data = await ${moduleName}?.route?.load(params);
229
+ return _data;
230
+ }
231
+ }`;
230
232
  };
231
233
 
232
234
  /** @type {(route: RouteFile) => string} */
package/plugin/index.mjs CHANGED
@@ -87,6 +87,7 @@ export default function VitePluginVanJS(options = {}) {
87
87
  ],
88
88
  },
89
89
  ssr: {
90
+ // external: ["virtual:@vanjs/routes"],
90
91
  noExternal: ["vanjs-*", "*-vanjs", "@vanjs/*"],
91
92
  },
92
93
  resolve: {
@@ -235,8 +236,9 @@ routes.length = 0;
235
236
  // Register routes
236
237
  ${currentRoutes.map(generateRoute).join("\n")}
237
238
  ${
238
- (ops && ops.ssr && currentRoutes.length) &&
239
- `console.log(\`🍦 @vanjs/router registered ${currentRoutes.length} routes.\`)`
239
+ (ops?.ssr && currentRoutes.length)
240
+ ? `console.log(\`🍦 @vanjs/router registered ${currentRoutes.length} routes.\`)`
241
+ : /* istanbul ignore next */ ""
240
242
  }
241
243
  `;
242
244
 
package/plugin/types.d.ts CHANGED
@@ -14,6 +14,6 @@ export type VanJSPluginOptions = {
14
14
 
15
15
  export declare const VitePluginVanJS: (
16
16
  config?: VanJSPluginOptions,
17
- ) => Plugin<VanJSPluginOptions>;
17
+ ) => Plugin;
18
18
 
19
19
  export default VitePluginVanJS;
package/router/a.mjs CHANGED
@@ -1,7 +1,12 @@
1
1
  // router/a.mjs
2
2
  import van from "vanjs-core";
3
3
  import { matchRoute } from "./matchRoute.mjs";
4
- import { executeLifecycle, isCurrentPage, navigate } from "./helpers.mjs";
4
+ import {
5
+ getValue,
6
+ isCurrentLocation,
7
+ isCurrentPage,
8
+ navigate,
9
+ } from "./helpers.mjs";
5
10
 
6
11
  /** @typedef {typeof import("./types").A} A */
7
12
 
@@ -9,46 +14,53 @@ import { executeLifecycle, isCurrentPage, navigate } from "./helpers.mjs";
9
14
  * @type {A}
10
15
  */
11
16
  export const A = (
12
- /* istanbul ignore next */ { href, children, ...rest } = {},
17
+ /* istanbul ignore next */
18
+ { href, children, ...rest } = {},
13
19
  ...otherChildren
14
20
  ) => {
15
21
  /* istanbul ignore next - try again later */
16
22
  const props = Object.fromEntries(
17
23
  Object.entries(rest || {}).filter(([_, val]) => val !== undefined),
18
24
  );
25
+
26
+ const preloaded = van.state(false);
27
+
19
28
  const newProps = {
20
29
  href,
21
30
  ...props,
22
31
  onclick: async (e) => {
32
+ const HREF = getValue(href);
23
33
  e.preventDefault();
24
34
  /* istanbul ignore next */
25
- if (isCurrentPage(href)) return;
35
+ if (isCurrentPage(HREF)) return;
26
36
 
27
37
  // istanbul ignore else
28
- if (props.onclick) {
38
+ if (typeof props.onclick === "function") {
29
39
  await props.onclick(e);
30
40
  }
31
41
 
32
- const route = matchRoute(href);
33
- const module = await route.component();
34
- await executeLifecycle(module, route.params);
35
-
36
- navigate(href);
42
+ navigate(HREF);
37
43
  },
38
- onmouseenter: () => {
39
- const route = matchRoute(href);
44
+ onmouseenter: async () => {
45
+ const HREF = getValue(href);
46
+ const route = matchRoute(HREF);
40
47
 
48
+ // istanbul ignore else
49
+ if (typeof props.onmouseenter === "function") {
50
+ await props.onmouseenter(e);
51
+ }
41
52
  /* istanbul ignore else */
42
- if (route?.component) {
53
+ if (route?.component && !preloaded.val) {
43
54
  route.component();
55
+ preloaded.val = true;
44
56
  }
45
57
  },
46
58
  };
47
59
 
48
- van.derive(() => {
49
- if (isCurrentPage(href)) {
50
- newProps["aria-current"] = "page";
51
- }
60
+ newProps["aria-current"] = van.derive(() => {
61
+ const isPage = isCurrentPage(href);
62
+ const isLocation = isCurrentLocation(href);
63
+ return isPage ? "page" : isLocation ? "location" : null;
52
64
  });
53
65
 
54
66
  return van.tags.a(newProps, children || otherChildren);
@@ -0,0 +1,168 @@
1
+ /** @typedef {{ data: unknown; error: Error | null; status: "idle" | "pending" | "success" | "error"; timestamp: number }} CacheEntry */
2
+ /** @typedef {CacheEntry["status"]} DataStatus */
3
+
4
+ /**
5
+ * @param {string} pathname
6
+ * @returns {string}
7
+ */
8
+ export const normalizePath = (pathname) => {
9
+ if (!pathname || pathname === "/") return "/";
10
+ return pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
11
+ };
12
+
13
+ /** @type {Map<string, Map<string, CacheEntry>>} */
14
+ const dataCacheMap = new Map();
15
+
16
+ const DEFAULT_MAX_ROUTES = 20;
17
+ let maxRoutes = DEFAULT_MAX_ROUTES;
18
+
19
+ /**
20
+ * @param {number} n
21
+ */
22
+ export const setMaxRoutes = (n) => {
23
+ maxRoutes = n;
24
+ };
25
+
26
+ const evictIfNeeded = () => {
27
+ if (maxRoutes <= 0) return;
28
+ while (dataCacheMap.size > maxRoutes) {
29
+ const firstKey = dataCacheMap.keys().next().value;
30
+ if (firstKey !== undefined) {
31
+ dataCacheMap.delete(firstKey);
32
+ }
33
+ }
34
+ };
35
+
36
+ /**
37
+ * @param {string} pathname
38
+ * @param {string} key
39
+ * @returns {CacheEntry | undefined}
40
+ */
41
+ export const get = (pathname, key) => {
42
+ const path = normalizePath(pathname);
43
+ const routeMap = dataCacheMap.get(path);
44
+ if (!routeMap) return undefined;
45
+ return routeMap.get(key);
46
+ };
47
+
48
+ /**
49
+ * @param {string} pathname
50
+ * @param {string} key
51
+ * @param {Partial<CacheEntry>} entry
52
+ */
53
+ export const set = (pathname, key, entry) => {
54
+ const path = normalizePath(pathname);
55
+ let routeMap = dataCacheMap.get(path);
56
+ if (!routeMap) {
57
+ routeMap = new Map();
58
+ dataCacheMap.set(path, routeMap);
59
+ evictIfNeeded();
60
+ }
61
+ routeMap.set(key, {
62
+ ...entry,
63
+ timestamp: entry?.timestamp || Date.now(),
64
+ });
65
+ };
66
+
67
+ /**
68
+ * @param {string} pathname
69
+ * @param {string} [key]
70
+ * @returns {boolean}
71
+ */
72
+ export const has = (pathname, key) => {
73
+ const path = normalizePath(pathname);
74
+ const routeMap = dataCacheMap.get(path);
75
+ if (!routeMap) return false;
76
+ if (key === undefined) return routeMap.size > 0;
77
+ return routeMap.has(key);
78
+ };
79
+
80
+ /**
81
+ * @param {string} pathname
82
+ * @returns {Record<string, CacheEntry> | undefined}
83
+ */
84
+ export const getRoute = (pathname) => {
85
+ const path = normalizePath(pathname);
86
+ const routeMap = dataCacheMap.get(path);
87
+ if (!routeMap) return undefined;
88
+ return Object.fromEntries(routeMap.entries());
89
+ };
90
+
91
+ /**
92
+ * @param {string} pathname
93
+ * @param {string} [key]
94
+ * @returns {boolean}
95
+ */
96
+ export const del = (pathname, key) => {
97
+ const path = normalizePath(pathname);
98
+ if (key === undefined) {
99
+ const deleted = dataCacheMap.delete(path);
100
+ return deleted;
101
+ }
102
+ const routeMap = dataCacheMap.get(path);
103
+ if (!routeMap) return false;
104
+ const deleted = routeMap.delete(key);
105
+ if (routeMap.size === 0) {
106
+ dataCacheMap.delete(path);
107
+ }
108
+ return deleted;
109
+ };
110
+
111
+ export const clear = () => {
112
+ dataCacheMap.clear();
113
+ };
114
+
115
+ /**
116
+ * @param {string} pathname
117
+ */
118
+ export const touch = (pathname) => {
119
+ const path = normalizePath(pathname);
120
+ const routeMap = dataCacheMap.get(path);
121
+ if (!routeMap) return;
122
+ dataCacheMap.delete(path);
123
+ dataCacheMap.set(path, routeMap);
124
+ };
125
+
126
+ /**
127
+ * @returns {Record<string, Record<string, { data: unknown; status: string; timestamp: number; error: { message: string } | null }>>}
128
+ */
129
+ export const toJSON = () => {
130
+ /** @type {Record<string, Record<string, { data: unknown; status: string; timestamp: number; error: { message: string } | null }>>} */
131
+ const result = {};
132
+ for (const [path, routeMap] of dataCacheMap.entries()) {
133
+ result[path] = {};
134
+ for (const [key, entry] of routeMap.entries()) {
135
+ result[path][key] = {
136
+ data: entry.data,
137
+ timestamp: entry.timestamp,
138
+ error: entry.error ? { message: entry.error.message } : null,
139
+ };
140
+ }
141
+ }
142
+ return result;
143
+ };
144
+
145
+ /**
146
+ * @param {Record<string, Record<string, { data: unknown; status?: string; timestamp?: number; error?: { message: string } | null }>>} json
147
+ */
148
+ export const hydrateFromJSON = (json) => {
149
+ if (!json || typeof json !== "object") return;
150
+ for (const [path, routeEntries] of Object.entries(json)) {
151
+ const normalized = normalizePath(path);
152
+ let routeMap = dataCacheMap.get(normalized);
153
+ if (!routeMap) {
154
+ routeMap = new Map();
155
+ dataCacheMap.set(normalized, routeMap);
156
+ }
157
+ for (const [key, entry] of Object.entries(routeEntries)) {
158
+ routeMap.set(key, {
159
+ data: entry.data,
160
+ timestamp: entry.timestamp || Date.now(),
161
+ error: entry.error ? new Error(entry.error.message) : null,
162
+ });
163
+ }
164
+ }
165
+ };
166
+
167
+ /** @returns {number} */
168
+ export const size = () => dataCacheMap.size;
@@ -12,9 +12,8 @@ declare module "@vanjs/router" {
12
12
  PropValueOrDerived,
13
13
  State,
14
14
  } from "vanjs-core";
15
+ import type { LayoutFile } from "../plugin/types.d.ts";
15
16
 
16
- // type VanElement = VElement | Exclude<Primitive, boolean | undefined>;
17
- // type DOMElement = globalThis.Element;
18
17
  type PrimitiveChild = Primitive | State<Primitive | undefined | null>;
19
18
 
20
19
  type DOMElement<T extends keyof HTMLElementTagNameMap = "div"> =
@@ -27,13 +26,6 @@ declare module "@vanjs/router" {
27
26
  | DOMElement
28
27
  | VElement
29
28
  | PrimitiveChild;
30
- // type VanElement =
31
- // | SVGElement
32
- // | HTMLElement
33
- // | DOMElement
34
- // | Node
35
- // | VElement
36
- // | PrimitiveChild;
37
29
  export type VanNode =
38
30
  | VanElement
39
31
  | VanElement[]
@@ -41,9 +33,7 @@ declare module "@vanjs/router" {
41
33
  | null
42
34
  | undefined;
43
35
 
44
- type ComponentProps1<T> =
45
- & Props
46
- & PropsWithKnownKeys<T>;
36
+ type ComponentProps1<T> = Props & PropsWithKnownKeys<T>;
47
37
 
48
38
  type ComponentProps<K extends keyof HTMLElementTagNameMap> =
49
39
  & Props
@@ -69,7 +59,7 @@ declare module "@vanjs/router" {
69
59
  * import { Router } from '@vanjs/router';
70
60
  *
71
61
  * export const App = () => {
72
- * return Router(); // or <Router /> for JSX
62
+ * return Router(); // or <Router /> for JSX
73
63
  * }
74
64
  */
75
65
  export const Router:
@@ -89,12 +79,11 @@ declare module "@vanjs/router" {
89
79
  * import van from 'vanjs-core';
90
80
  *
91
81
  * export const Navigation = () => {
92
- * const { nav } = van.tags
93
- * return nav(
94
- * A({ href="/" }, "Home"), // or <A href="/">Home</A> with JSX
95
- * A({ href="/about" }, "About"), // or <A href="/about">About</A> with JSX
96
- * // ...other children
97
- * );
82
+ * const { nav } = van.tags
83
+ * return nav(
84
+ * A({ href="/" }, "Home"),
85
+ * A({ href="/about" }, "About"),
86
+ * );
98
87
  * }
99
88
  */
100
89
  export const A:
@@ -114,23 +103,41 @@ declare module "@vanjs/router" {
114
103
  options?: { replace?: boolean },
115
104
  ) => void;
116
105
 
117
- /**
118
- * A client only helper function that reloads the current page.
119
- */
106
+ /** A client only helper function that reloads the current page. */
120
107
  export const reload: () => void;
121
108
 
122
109
  /**
123
110
  * A helper function that redirects the user to the specified href.
124
111
  * When called in the server, it will return a function that will redirect the user
125
112
  * to the specified href when called.
126
- * @param {string | undefined} href the URL to redirect to
113
+ * @param href the URL to redirect to
127
114
  */
128
115
  export const redirect: (href?: string) => void | (() => void);
129
116
 
117
+ /**
118
+ * Returns the string value of a State or primitive
119
+ */
120
+ export const getValue: (v: unknown) => string;
121
+
122
+ /**
123
+ * Check if selected page is the current page
124
+ */
125
+ export const isCurrentPage: (pageName: string) => boolean;
126
+
127
+ /**
128
+ * Check if selected page is related to the current page
129
+ */
130
+ export const isCurrentLocation: (pageName: string) => boolean;
131
+
132
+ /**
133
+ * Check if component is a lazy component
134
+ */
135
+ export const isLazyComponent: (component: unknown) => boolean;
136
+
130
137
  // routes.mjs
131
138
  export type RouteEntry = {
132
139
  path: string;
133
- component: Promise<ComponentModule>;
140
+ component: () => Promise<ComponentModule>;
134
141
  params?: Record<string, string>;
135
142
  preload?: (
136
143
  params?: Record<string, string>,
@@ -140,17 +147,16 @@ declare module "@vanjs/router" {
140
147
  ) => boolean | void | Promise<boolean | void>;
141
148
  };
142
149
 
150
+ export type ImportFn = () => LazyComponent;
151
+
143
152
  export type RouteProps = {
144
153
  path: string;
154
+ params?: Record<string, string>;
145
155
  component:
146
- | (() =>
147
- | HTMLElementTagNameMap[unknown]
148
- | HTMLElementTagNameMap[unknown][])
156
+ | (() => DOMElement)
149
157
  | VanComponent
150
158
  | ComponentFn
151
- | (() => Promise<DynamicModule>);
152
- // | ComponentModule["component"];
153
- // component: ComponentFn | (() => Promise<ComponentModule>);
159
+ | (() => Promise<ComponentModule>);
154
160
  preload?: (
155
161
  params?: Record<string, string>,
156
162
  ) => boolean | void | Promise<boolean | void>;
@@ -158,6 +164,13 @@ declare module "@vanjs/router" {
158
164
  params?: Record<string, string>,
159
165
  ) => boolean | void | Promise<boolean | void>;
160
166
  };
167
+
168
+ export type RouteConfig = {
169
+ routePath: string;
170
+ path: string;
171
+ layouts?: LayoutFile[];
172
+ };
173
+
161
174
  export const routes: RouteEntry[];
162
175
 
163
176
  /**
@@ -180,15 +193,18 @@ declare module "@vanjs/router" {
180
193
  pathname: string;
181
194
  searchParams: URLSearchParams;
182
195
  params: Record<string, string>;
196
+ status: "idle" | "pending" | "success" | "error";
183
197
  };
198
+
184
199
  /**
185
200
  * A reactive object that holds the current router state.
186
201
  * This state is maintained by both server and client.
187
202
  */
188
203
  export const routerState: {
189
- pathname: van.state<RouterState.pathname>;
190
- searchParams: van.state<RouterState.searchParams>;
191
- params?: van.state<RouterState.params>;
204
+ pathname: RouterState["pathname"];
205
+ searchParams: RouterState["searchParams"];
206
+ params?: RouterState["params"];
207
+ status: RouterState["status"];
192
208
  };
193
209
 
194
210
  /**
@@ -203,12 +219,24 @@ declare module "@vanjs/router" {
203
219
  params?: Record<string, string>,
204
220
  ) => void;
205
221
 
222
+ /**
223
+ * Creates a reactive micro store from an initial object
224
+ */
225
+ export const microStore: <T extends Record<string, unknown>>(init: T) => T;
226
+
227
+ /**
228
+ * Fixes the URL of a route.
229
+ * @param url
230
+ */
231
+ export const fixRouteUrl: (url: string) => string;
232
+
233
+ // unwrap.mjs
206
234
  /**
207
235
  * Merge the children of an Element or an array of elements with an optional array of children
208
- * into the childen prperty of a simple object.
209
- * @param source
210
- * @param children
236
+ * into the children property of a simple object.
211
237
  */
238
+ export type UnwrapResult = { children: VanNode[] };
239
+
212
240
  export const unwrap: (
213
241
  source:
214
242
  | VanNode
@@ -223,8 +251,8 @@ declare module "@vanjs/router" {
223
251
  | ReturnType<IsoTagFunc>[]
224
252
  | VanNode
225
253
  | VanNode[]),
226
- ...children: ChildDom[]
227
- ) => { children: ChildDom[] };
254
+ ...children: VanNode[]
255
+ ) => UnwrapResult;
228
256
 
229
257
  export type JSXComponentFn = () => JSX.Element;
230
258
  export type FragmentFn = () => ChildDom | ChildDom[];
@@ -235,15 +263,11 @@ declare module "@vanjs/router" {
235
263
  route?: Pick<RouteEntry, "load" | "preload">;
236
264
  };
237
265
 
238
- export type DynamicModule = {
239
- Page: ComponentFn;
266
+ export type LazyComponent = Promise<{
240
267
  default?: ComponentFn;
268
+ Page?: ComponentFn;
241
269
  route?: Pick<RouteEntry, "load" | "preload">;
242
- };
243
-
244
- export type LazyComponent = Promise<DynamicModule>;
245
-
246
- export type ImportFn = () => LazyComponent;
270
+ }>;
247
271
 
248
272
  /**
249
273
  * Registers a lazy component.
@@ -252,21 +276,57 @@ declare module "@vanjs/router" {
252
276
  export const lazy: (importFn: ImportFn) => () => Promise<ComponentModule>;
253
277
 
254
278
  /**
255
- * Fixes the URL of a route.
256
- * @param url
279
+ * Cache a route.
280
+ * @param key the cache route key
281
+ * @param value the cache route
257
282
  */
258
- export const fixRouteUrl: (url: string) => string;
283
+ export const cacheRoute: (key: ImportFn, value: ComponentModule) => void;
284
+
285
+ /**
286
+ * Return a route cache.
287
+ * @param key the cache route key
288
+ */
289
+ export const getCachedRoute: (key: ImportFn) => ComponentModule | undefined;
259
290
 
260
291
  /**
261
292
  * Execute lifecycle methods preload and / or load
262
293
  */
263
294
  export const executeLifecycle: (
264
- { route }: ComponentModule,
265
- params?: Record<string, string> | undefined,
266
- ) => Promise<boolean | void>;
295
+ route: RouteEntry | {
296
+ preload?: (params?: Record<string, string>) => unknown;
297
+ load?: (params?: Record<string, string>) => unknown;
298
+ } | null,
299
+ params: Record<string, string> | undefined,
300
+ ) => Promise<boolean>;
267
301
 
268
302
  /**
269
303
  * Find a registered route that matches the given path
270
304
  */
271
305
  export const matchRoute: (path: string) => RouteEntry | null;
306
+
307
+ /**
308
+ * Convenience hook to get the current route's cached data.
309
+ */
310
+ export const useRouteData: <T>() => T | undefined;
311
+
312
+ // dataCache.mjs
313
+ export type CacheEntry<T = unknown> = {
314
+ data: T;
315
+ error: Error | null;
316
+ timestamp: number;
317
+ };
318
+
319
+ export const dataCache: {
320
+ get<T>(pathname: string, key: string): CacheEntry<T> | undefined;
321
+ set<T>(pathname: string, key: string, entry: Partial<CacheEntry<T>>): void;
322
+ has(pathname: string, key?: string): boolean;
323
+ getRoute(pathname: string): Record<string, CacheEntry> | undefined;
324
+ del(pathname: string, key?: string): boolean;
325
+ clear(): void;
326
+ touch(pathname: string): void;
327
+ toJSON(): Record<string, Record<string, CacheEntry>>;
328
+ hydrateFromJSON(json: Record<string, Record<string, CacheEntry>>): void;
329
+ size(): number;
330
+ setMaxRoutes(n: number): void;
331
+ };
272
332
  }