@domphy/app 0.1.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.
@@ -0,0 +1,541 @@
1
+ import { DomphyElement, RecordState, State, Notifier, ElementNode, PartialElement } from '@domphy/core';
2
+
3
+ /**
4
+ * Port of the Next.js Metadata API. Static objects merge from root layout to
5
+ * page; later segments override earlier ones. `title.template` applies to the
6
+ * titles of child segments, exactly like Next.js.
7
+ */
8
+ interface MetadataTitle {
9
+ default?: string;
10
+ template?: string;
11
+ absolute?: string;
12
+ }
13
+ interface OpenGraphImage {
14
+ url: string;
15
+ width?: number;
16
+ height?: number;
17
+ alt?: string;
18
+ }
19
+ interface OpenGraphMetadata {
20
+ title?: string;
21
+ description?: string;
22
+ url?: string;
23
+ siteName?: string;
24
+ type?: string;
25
+ locale?: string;
26
+ images?: (string | OpenGraphImage)[];
27
+ }
28
+ interface TwitterMetadata {
29
+ card?: "summary" | "summary_large_image" | "app" | "player";
30
+ title?: string;
31
+ description?: string;
32
+ site?: string;
33
+ creator?: string;
34
+ images?: string[];
35
+ }
36
+ interface RobotsMetadata {
37
+ index?: boolean;
38
+ follow?: boolean;
39
+ noarchive?: boolean;
40
+ nosnippet?: boolean;
41
+ noimageindex?: boolean;
42
+ }
43
+ interface IconsMetadata {
44
+ icon?: string;
45
+ shortcut?: string;
46
+ apple?: string;
47
+ }
48
+ interface AlternatesMetadata {
49
+ canonical?: string;
50
+ languages?: Record<string, string>;
51
+ }
52
+ interface Metadata {
53
+ title?: string | MetadataTitle;
54
+ description?: string;
55
+ applicationName?: string;
56
+ generator?: string;
57
+ keywords?: string[];
58
+ authors?: {
59
+ name: string;
60
+ url?: string;
61
+ }[];
62
+ referrer?: string;
63
+ themeColor?: string;
64
+ colorScheme?: string;
65
+ viewport?: string;
66
+ robots?: string | RobotsMetadata;
67
+ icons?: string | IconsMetadata;
68
+ openGraph?: OpenGraphMetadata;
69
+ twitter?: TwitterMetadata;
70
+ alternates?: AlternatesMetadata;
71
+ metadataBase?: string;
72
+ /** Arbitrary extra meta tags: name -> content. */
73
+ other?: Record<string, string>;
74
+ }
75
+ interface ResolvedMetadata extends Omit<Metadata, "title"> {
76
+ title?: string;
77
+ }
78
+ interface HeadTag {
79
+ tag: "title" | "meta" | "link";
80
+ attributes: Record<string, string>;
81
+ content?: string;
82
+ }
83
+ /** Resolves the metadata chain of all matched segments into one flat object. */
84
+ declare function resolveMetadata(values: (MetadataValue | undefined)[], context: LoaderContext): Promise<ResolvedMetadata>;
85
+ /** Converts resolved metadata into a flat list of head tag descriptions. */
86
+ declare function metadataToHeadTags(metadata: ResolvedMetadata): HeadTag[];
87
+ /** Serializes head tags for server rendering. */
88
+ declare function renderHeadTags(tags: HeadTag[]): string;
89
+ /** Applies head tags to `document.head`, replacing tags from the previous navigation. */
90
+ declare function applyHeadTags(tags: HeadTag[]): void;
91
+
92
+ /** Route params. Dynamic segments map to strings, catch-all segments to string arrays. */
93
+ type Params = Record<string, string | string[]>;
94
+ /** Shared context given to pages, layouts, loaders, metadata functions and middleware. */
95
+ interface RouteContext<TData = unknown> {
96
+ /** Pathname actually rendered (after middleware rewrites). */
97
+ pathname: string;
98
+ /** Pathname shown in the address bar (before rewrites). */
99
+ url: string;
100
+ params: Params;
101
+ searchParams: URLSearchParams;
102
+ hash: string;
103
+ /** Resolved loader data of the owning segment. */
104
+ data: TData;
105
+ /** Resolved loader data of every matched segment, keyed by segment id. */
106
+ segmentData: Record<string, unknown>;
107
+ /** Request headers, only present during server rendering. */
108
+ headers?: Headers;
109
+ }
110
+ type PageBlock<TData = unknown> = (context: RouteContext<TData>) => DomphyElement;
111
+ type LayoutBlock<TData = unknown> = (children: DomphyElement, context: RouteContext<TData>) => DomphyElement;
112
+ type LoadingBlock = (context: RouteContext) => DomphyElement;
113
+ type ErrorBlock = (error: Error, retry: () => void) => DomphyElement;
114
+ type NotFoundBlock = () => DomphyElement;
115
+ interface LoaderContext {
116
+ pathname: string;
117
+ url: string;
118
+ params: Params;
119
+ searchParams: URLSearchParams;
120
+ headers?: Headers;
121
+ }
122
+ type Loader<TData = unknown> = (context: LoaderContext) => TData | Promise<TData>;
123
+ type MetadataValue = Metadata | ((context: LoaderContext) => Metadata | Promise<Metadata>);
124
+ interface MiddlewareContext {
125
+ url: URL;
126
+ pathname: string;
127
+ searchParams: URLSearchParams;
128
+ headers?: Headers;
129
+ }
130
+ /** A rewrite instruction returned from middleware. Create with `rewrite()`. */
131
+ interface RewriteResult {
132
+ __domphyRewrite: string;
133
+ }
134
+ /**
135
+ * Middleware runs before every navigation and server render.
136
+ * Return `rewrite(path)` to render another route under the same URL,
137
+ * call `redirect(path)` / `notFound()` to interrupt, or return nothing to continue.
138
+ */
139
+ type Middleware = (context: MiddlewareContext) => RewriteResult | undefined | void | Promise<RewriteResult | undefined | void>;
140
+ /**
141
+ * One route segment, the equivalent of one folder in the Next.js `app` directory.
142
+ *
143
+ * `path` uses Next.js conventions:
144
+ * - `"about"` static segment (may contain several parts: `"docs/getting-started"`)
145
+ * - `"[slug]"` dynamic segment
146
+ * - `"[...slug]"` catch-all segment
147
+ * - `"[[...slug]]"` optional catch-all segment
148
+ * - `"(marketing)"` route group, organizes the tree without affecting the URL
149
+ *
150
+ * A segment is routable when it declares `page` or `redirect`.
151
+ */
152
+ interface Route {
153
+ path: string;
154
+ page?: PageBlock<any>;
155
+ layout?: LayoutBlock<any>;
156
+ loading?: LoadingBlock;
157
+ error?: ErrorBlock;
158
+ notFound?: NotFoundBlock;
159
+ metadata?: MetadataValue;
160
+ loader?: Loader<any>;
161
+ /**
162
+ * Loader cache lifetime in seconds, the equivalent of Next.js `revalidate`.
163
+ * `undefined` re-runs the loader on every navigation, `Infinity` caches forever.
164
+ */
165
+ revalidate?: number;
166
+ middleware?: Middleware[];
167
+ /** Static redirect: navigating to this route immediately redirects to the target. */
168
+ redirect?: string;
169
+ /** Use a 308 redirect on the server when `redirect` is set. */
170
+ permanent?: boolean;
171
+ children?: Route[];
172
+ }
173
+ type RouterStatus = "idle" | "loading" | "error" | "notfound";
174
+ interface RouterStateRecord {
175
+ pathname: string;
176
+ search: string;
177
+ hash: string;
178
+ params: Params;
179
+ status: RouterStatus;
180
+ error: Error | null;
181
+ [key: string]: unknown;
182
+ }
183
+ interface NavigateOptions {
184
+ replace?: boolean;
185
+ /** Scroll to the top (or the URL hash) after navigation. Defaults to true. */
186
+ scroll?: boolean;
187
+ }
188
+ type RouterEventName = "routeChangeStart" | "routeChangeComplete" | "routeChangeError";
189
+ /** Identity helper that keeps route literals fully typed. */
190
+ declare function defineRoutes(routes: Route[]): Route[];
191
+
192
+ /**
193
+ * API route handlers on web-standard Request/Response, the equivalent of
194
+ * `route.ts` files. Works in Node 18+, Bun, Deno, and edge runtimes; adapt to
195
+ * `http.createServer` with any Request/Response bridge.
196
+ */
197
+ type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
198
+ interface ApiContext {
199
+ params: Params;
200
+ }
201
+ type ApiMethodHandler = (request: Request, context: ApiContext) => Response | Promise<Response>;
202
+ interface ApiRoute {
203
+ /** Same pattern syntax as page routes: `"/api/users/[id]"`. */
204
+ path: string;
205
+ GET?: ApiMethodHandler;
206
+ POST?: ApiMethodHandler;
207
+ PUT?: ApiMethodHandler;
208
+ PATCH?: ApiMethodHandler;
209
+ DELETE?: ApiMethodHandler;
210
+ HEAD?: ApiMethodHandler;
211
+ OPTIONS?: ApiMethodHandler;
212
+ }
213
+ /** Shorthand for JSON responses, the equivalent of `NextResponse.json()`. */
214
+ declare function json(data: unknown, init?: ResponseInit): Response;
215
+ declare function createApiHandler(routes: ApiRoute[]): (request: Request) => Promise<Response>;
216
+
217
+ /**
218
+ * Minimal history abstraction so the router runs against the real browser
219
+ * history, an in-memory stack (tests, embedded demos) or nothing at all (SSR).
220
+ */
221
+ interface HistoryAdapter {
222
+ url(): URL;
223
+ push(url: string): void;
224
+ replace(url: string): void;
225
+ go(delta: number): void;
226
+ /** Subscribes to external navigation (popstate / memory go). Returns a release function. */
227
+ listen(callback: (url: URL) => void): () => void;
228
+ /** Saves and restores scroll positions per history entry. */
229
+ saveScroll?(position: {
230
+ x: number;
231
+ y: number;
232
+ }): void;
233
+ readScroll?(): {
234
+ x: number;
235
+ y: number;
236
+ } | null;
237
+ }
238
+ declare function createBrowserHistory(): HistoryAdapter;
239
+ declare function createMemoryHistory(initial?: string): HistoryAdapter;
240
+
241
+ /** How long a prefetched entry stays usable, mirrors the Next.js client router cache. */
242
+ declare const PREFETCH_LIFETIME = 30000;
243
+ /**
244
+ * Loader result cache. The `revalidate` option of a route controls the entry
245
+ * lifetime, the equivalent of fetch caching / ISR in Next.js.
246
+ */
247
+ declare class DataCache {
248
+ private entries;
249
+ private inflight;
250
+ /** Seeds entries (from SSR payloads) that satisfy the next load exactly once. */
251
+ seed(record: Record<string, unknown>): void;
252
+ snapshot(keys: string[]): Record<string, unknown>;
253
+ invalidate(prefix?: string): void;
254
+ load(key: string, loader: Loader, context: LoaderContext, revalidate: number | undefined): Promise<unknown>;
255
+ /** Runs the loader ahead of navigation and stores the result as a one-shot entry. */
256
+ prefetch(key: string, loader: Loader, context: LoaderContext, revalidate: number | undefined): Promise<void>;
257
+ }
258
+
259
+ type SegmentKind = "static" | "dynamic" | "catchall" | "optional-catchall" | "group";
260
+ interface PatternSegment {
261
+ kind: SegmentKind;
262
+ /** Literal value for static segments, param name otherwise. */
263
+ value: string;
264
+ }
265
+ /** One routable endpoint: the chain of route nodes from root to the segment that owns the page. */
266
+ interface CompiledRoute {
267
+ /** Stable id, the full pattern path including groups, e.g. "/(shop)/products/[id]". */
268
+ id: string;
269
+ /** URL pattern segments, groups excluded. */
270
+ segments: PatternSegment[];
271
+ /** Route nodes from root to leaf. */
272
+ chain: Route[];
273
+ /** Segment id for each chain node, used as data cache key and diff key. */
274
+ chainIds: string[];
275
+ }
276
+ interface RouteMatch {
277
+ route: CompiledRoute;
278
+ params: Params;
279
+ pathname: string;
280
+ }
281
+ declare function parseSegment(part: string): PatternSegment;
282
+ declare function splitPath(path: string): string[];
283
+ /** Flattens a route tree into a list of routable endpoints. */
284
+ declare function compileRoutes(routes: Route[]): CompiledRoute[];
285
+ /** Static segments win over dynamic, dynamic over catch-all — same priority order as Next.js. */
286
+ declare function compareSpecificity(a: CompiledRoute, b: CompiledRoute): number;
287
+ declare function matchPath(segments: PatternSegment[], pathname: string): Params | null;
288
+ declare function matchRoute(compiled: CompiledRoute[], pathname: string): RouteMatch | null;
289
+ /** Replaces pattern placeholders with params, the inverse of `matchPath`. */
290
+ declare function buildHref(pattern: string, params?: Params): string;
291
+
292
+ interface RouterOptions {
293
+ /** Defaults to the browser history in a DOM environment, otherwise none (SSR). */
294
+ history?: HistoryAdapter | null;
295
+ /** Global middleware, the equivalent of `middleware.ts`. */
296
+ middleware?: Middleware[];
297
+ /** App-level not-found block, the equivalent of the root `not-found.tsx`. */
298
+ notFound?: NotFoundBlock;
299
+ /** App-level error block, the equivalent of `global-error.tsx`. */
300
+ error?: ErrorBlock;
301
+ /** Request headers, forwarded to loaders and middleware during server rendering. */
302
+ headers?: Headers;
303
+ }
304
+ interface TransitionOptions {
305
+ replace?: boolean;
306
+ scroll?: boolean;
307
+ fromHistory?: boolean;
308
+ initial?: boolean;
309
+ }
310
+ /** The most recently created router, used by `navLink` when no router is passed explicitly. */
311
+ declare function getRouter(): AppRouter;
312
+ declare class AppRouter {
313
+ readonly routes: CompiledRoute[];
314
+ readonly state: RecordState<RouterStateRecord>;
315
+ readonly tree: State<DomphyElement>;
316
+ readonly events: Notifier;
317
+ readonly cache: DataCache;
318
+ private history;
319
+ private middleware;
320
+ private notFoundBlock;
321
+ private errorBlock;
322
+ private headers?;
323
+ private navigationToken;
324
+ private releaseHistory;
325
+ private currentMatch;
326
+ /** Metadata resolved for the current route, exposed for server rendering. */
327
+ metadata: ResolvedMetadata;
328
+ /** The last redirect followed during a transition, exposed for server rendering. */
329
+ lastRedirect: {
330
+ to: string;
331
+ permanent: boolean;
332
+ } | null;
333
+ /** Loader data of the latest render keyed by cache key, used for SSR payloads. */
334
+ lastData: Record<string, unknown>;
335
+ constructor(routes: Route[], options?: RouterOptions);
336
+ /** Renders the current URL and starts listening to history navigation. */
337
+ start(): Promise<void>;
338
+ destroy(): void;
339
+ currentUrl(): URL;
340
+ resolve(href: string): URL;
341
+ navigate(href: string, options?: NavigateOptions): Promise<void>;
342
+ push(href: string, options?: Omit<NavigateOptions, "replace">): Promise<void>;
343
+ replace(href: string, options?: Omit<NavigateOptions, "replace">): Promise<void>;
344
+ back(): void;
345
+ forward(): void;
346
+ /** Clears the loader cache and re-renders the current URL, like `router.refresh()`. */
347
+ refresh(): Promise<void>;
348
+ /** Runs the loaders of a route ahead of navigation, like `router.prefetch()`. */
349
+ prefetch(href: string): Promise<void>;
350
+ searchParams(listener?: Parameters<RecordState<RouterStateRecord>["get"]>[1]): URLSearchParams;
351
+ addEventListener(event: RouterEventName, callback: (...args: unknown[]) => void): () => void;
352
+ /** The match rendered by the latest completed transition, null when no route matched. */
353
+ getMatch(): RouteMatch | null;
354
+ private cacheKey;
355
+ private loaderContext;
356
+ /** Core navigation pipeline: middleware -> match -> loaders -> tree -> history/scroll. */
357
+ transition(url: URL, options?: TransitionOptions): Promise<void>;
358
+ private renderMatch;
359
+ private renderNotFound;
360
+ private baseContext;
361
+ private applyMetadata;
362
+ private commit;
363
+ private saveScroll;
364
+ private restoreScroll;
365
+ }
366
+
367
+ interface AppOptions extends Omit<RouterOptions, "history" | "headers"> {
368
+ history?: HistoryAdapter | null;
369
+ }
370
+ interface RenderToStringOptions {
371
+ headers?: Headers;
372
+ }
373
+ interface SSRResult {
374
+ /** Body markup of the app root, ready to place inside the mount element. */
375
+ html: string;
376
+ /** Scoped CSS of the rendered tree, place inside `<style id="domphy-style">`. */
377
+ css: string;
378
+ /** Serialized `<title>`, `<meta>` and `<link>` tags for the document head. */
379
+ head: string;
380
+ /** 200, 404, or the redirect status. */
381
+ status: number;
382
+ /** Set when a loader or middleware redirected. */
383
+ redirect?: string;
384
+ /** Loader data to embed for hydration, see `bootstrapScript`. */
385
+ data: Record<string, unknown>;
386
+ /** Inline script that exposes loader data to `hydrate()` on the client. */
387
+ bootstrapScript: string;
388
+ }
389
+ /**
390
+ * The app shell: routing, rendering and server rendering in one object, the
391
+ * Domphy equivalent of a Next.js application instance.
392
+ */
393
+ declare class DomphyApp {
394
+ readonly routes: Route[];
395
+ readonly options: AppOptions;
396
+ readonly router: AppRouter;
397
+ private node;
398
+ constructor(routes: Route[], options?: AppOptions);
399
+ /** The root element; the whole route tree re-renders through one reactive child. */
400
+ element(): DomphyElement;
401
+ /** Client-side render from scratch. */
402
+ render(target: HTMLElement): Promise<ElementNode>;
403
+ /**
404
+ * Hydrates server-rendered markup. Reads the loader data embedded by
405
+ * `bootstrapScript` so loaders are not re-run and the tree matches the HTML.
406
+ */
407
+ hydrate(target: HTMLElement, style?: HTMLStyleElement): Promise<ElementNode>;
408
+ destroy(): void;
409
+ /** Server rendering: runs middleware and loaders for `url`, returns markup + CSS + head. */
410
+ renderToString(url: string | URL, options?: RenderToStringOptions): Promise<SSRResult>;
411
+ }
412
+ declare function createApp(routes: Route[], options?: AppOptions): DomphyApp;
413
+
414
+ interface ImageLoaderInput {
415
+ src: string;
416
+ width: number;
417
+ quality: number;
418
+ }
419
+ type ImageLoader = (input: ImageLoaderInput) => string;
420
+ interface OptimizedImageProps {
421
+ src: string;
422
+ alt?: string;
423
+ width?: number;
424
+ height?: number;
425
+ /** Stretch over the positioned parent, like the `fill` prop of `next/image`. */
426
+ fill?: boolean;
427
+ sizes?: string;
428
+ quality?: number;
429
+ /** Load eagerly with high fetch priority — use for the LCP image. */
430
+ priority?: boolean;
431
+ placeholder?: "blur" | "empty";
432
+ blurDataURL?: string;
433
+ /** Maps `src` + width to an optimized URL; enables `srcset` generation. */
434
+ loader?: ImageLoader;
435
+ deviceSizes?: number[];
436
+ }
437
+ /**
438
+ * Patch for `img` elements, the equivalent of `next/image`: lazy loading,
439
+ * async decoding, fetch priority, `srcset` through a custom loader, fill
440
+ * layout and blur placeholders. URL optimization itself is the loader's job —
441
+ * point it at any image CDN.
442
+ */
443
+ declare function optimizedImage(props: OptimizedImageProps): PartialElement<"img">;
444
+
445
+ interface NavLinkProps {
446
+ href: string;
447
+ /** Replace the current history entry instead of pushing. */
448
+ replace?: boolean;
449
+ /** Scroll after navigation, defaults to true. */
450
+ scroll?: boolean;
451
+ /**
452
+ * Prefetch strategy, like the `prefetch` prop of `next/link`:
453
+ * `"visible"` when the link enters the viewport, `"hover"` on pointer/focus,
454
+ * `false` never. Defaults to `"hover"`.
455
+ */
456
+ prefetch?: "visible" | "hover" | false;
457
+ /** Mark active only on an exact pathname match, not on descendants. */
458
+ exact?: boolean;
459
+ /** Explicit router; defaults to the app router. */
460
+ router?: AppRouter;
461
+ }
462
+ /**
463
+ * Patch for `a` elements, the equivalent of `next/link`: client-side
464
+ * navigation, prefetching and active state (`aria-current` + `data-active`).
465
+ */
466
+ declare function navLink(props: NavLinkProps): PartialElement<"a">;
467
+
468
+ /**
469
+ * Control-flow signals, the equivalents of `redirect()`, `permanentRedirect()` and
470
+ * `notFound()` from `next/navigation`. They throw and are caught by the router
471
+ * (or by `renderToString` on the server), so they can be called from loaders,
472
+ * metadata functions and middleware.
473
+ */
474
+ declare class RedirectSignal extends Error {
475
+ readonly to: string;
476
+ readonly permanent: boolean;
477
+ constructor(to: string, permanent: boolean);
478
+ }
479
+ declare class NotFoundSignal extends Error {
480
+ constructor();
481
+ }
482
+ declare function redirect(to: string): never;
483
+ declare function permanentRedirect(to: string): never;
484
+ declare function notFound(): never;
485
+ /** Returned from middleware to render another route while keeping the URL. */
486
+ declare function rewrite(to: string): RewriteResult;
487
+ declare function isRewrite(value: unknown): value is RewriteResult;
488
+
489
+ interface ScriptProps {
490
+ src: string;
491
+ /**
492
+ * Loading strategy, like `next/script`:
493
+ * `"afterInteractive"` loads as soon as the element mounts (default),
494
+ * `"lazyOnload"` waits for the window `load` event plus idle time.
495
+ */
496
+ strategy?: "afterInteractive" | "lazyOnload";
497
+ id?: string;
498
+ async?: boolean;
499
+ onLoad?: () => void;
500
+ onError?: () => void;
501
+ }
502
+ /** Resets script dedupe between tests. */
503
+ declare function resetScripts(): void;
504
+ /**
505
+ * Block that loads an external script once, the equivalent of `next/script`.
506
+ * Place it anywhere in the tree; duplicate sources are loaded only once.
507
+ */
508
+ declare function script(props: ScriptProps): DomphyElement<"script">;
509
+
510
+ type SegmentStatus = "success" | "pending" | "error" | "notfound";
511
+ interface SegmentResult {
512
+ status: SegmentStatus;
513
+ data?: unknown;
514
+ error?: Error;
515
+ }
516
+ interface BuildTreeInput {
517
+ match: RouteMatch;
518
+ baseContext: Omit<RouteContext, "data" | "segmentData">;
519
+ results: SegmentResult[];
520
+ retry: () => void;
521
+ defaultError: ErrorBlock;
522
+ defaultNotFound: NotFoundBlock;
523
+ }
524
+ interface BuiltTree {
525
+ element: DomphyElement;
526
+ status: RouterStatus;
527
+ }
528
+ declare function defaultErrorBlock(error: Error): DomphyElement;
529
+ declare function defaultNotFoundBlock(): DomphyElement;
530
+ /**
531
+ * Composes the matched chain into one element: page wrapped by every layout,
532
+ * exactly like nested `layout.tsx` files. When a segment failed, is pending or
533
+ * raised `notFound()`, the subtree below the nearest boundary block (`error`,
534
+ * `loading`, `notFound`) is replaced by that block while ancestor layouts keep
535
+ * rendering — the same boundary model as the Next.js App Router.
536
+ */
537
+ declare function buildTree(input: BuildTreeInput): BuiltTree;
538
+ /** Wraps a bare not-found block with the root layouts of a match-less render. */
539
+ declare function buildNotFoundTree(block: NotFoundBlock): BuiltTree;
540
+
541
+ export { type AlternatesMetadata, type ApiContext, type ApiMethodHandler, type ApiRoute, type AppOptions, AppRouter, type BuildTreeInput, type BuiltTree, type CompiledRoute, DataCache, DomphyApp, type ErrorBlock, type HeadTag, type HistoryAdapter, type HttpMethod, type IconsMetadata, type ImageLoader, type ImageLoaderInput, type LayoutBlock, type Loader, type LoaderContext, type LoadingBlock, type Metadata, type MetadataTitle, type MetadataValue, type Middleware, type MiddlewareContext, type NavLinkProps, type NavigateOptions, type NotFoundBlock, NotFoundSignal, type OpenGraphImage, type OpenGraphMetadata, type OptimizedImageProps, PREFETCH_LIFETIME, type PageBlock, type Params, type PatternSegment, RedirectSignal, type RenderToStringOptions, type ResolvedMetadata, type RewriteResult, type RobotsMetadata, type Route, type RouteContext, type RouteMatch, type RouterEventName, type RouterOptions, type RouterStateRecord, type RouterStatus, type SSRResult, type ScriptProps, type SegmentKind, type SegmentResult, type SegmentStatus, type TwitterMetadata, applyHeadTags, buildHref, buildNotFoundTree, buildTree, compareSpecificity, compileRoutes, createApiHandler, createApp, createBrowserHistory, createMemoryHistory, defaultErrorBlock, defaultNotFoundBlock, defineRoutes, getRouter, isRewrite, json, matchPath, matchRoute, metadataToHeadTags, navLink, notFound, optimizedImage, parseSegment, permanentRedirect, redirect, renderHeadTags, resetScripts, resolveMetadata, rewrite, script, splitPath };
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ var ie=Object.defineProperty,se=Object.defineProperties;var ae=Object.getOwnPropertyDescriptors;var K=Object.getOwnPropertySymbols;var le=Object.prototype.hasOwnProperty,ce=Object.prototype.propertyIsEnumerable;var z=(r,e,t)=>e in r?ie(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t,x=(r,e)=>{for(var t in e||(e={}))le.call(e,t)&&z(r,t,e[t]);if(K)for(var t of K(e))ce.call(e,t)&&z(r,t,e[t]);return r},b=(r,e)=>se(r,ae(e));var R=(r,e,t)=>new Promise((n,o)=>{var i=l=>{try{a(t.next(l))}catch(p){o(p)}},s=l=>{try{a(t.throw(l))}catch(p){o(p)}},a=l=>l.done?n(l.value):Promise.resolve(l.value).then(i,s);a((t=t.apply(r,e)).next())});function W(r){return r.startsWith("(")&&r.endsWith(")")?{kind:"group",value:r.slice(1,-1)}:r.startsWith("[[...")&&r.endsWith("]]")?{kind:"optional-catchall",value:r.slice(5,-2)}:r.startsWith("[...")&&r.endsWith("]")?{kind:"catchall",value:r.slice(4,-1)}:r.startsWith("[")&&r.endsWith("]")?{kind:"dynamic",value:r.slice(1,-1)}:{kind:"static",value:r}}function D(r){return r.split("/").filter(e=>e.length>0)}function I(r){let e=[];function t(n,o,i,s,a){for(let l of n){let p=D(l.path),d=p.map(W),c=[...o,...d.filter(g=>g.kind!=="group")],h=`${a}/${p.join("/")}`.replace(/\/+/g,"/"),m=[...i,l],f=[...s,h===""?"/":h];(l.page||l.redirect)&&e.push({id:h===""?"/":h,segments:c,chain:m,chainIds:f}),l.children&&t(l.children,c,m,f,h)}}return t(r,[],[],[],""),e.sort(ue),e}var q={static:0,dynamic:1,catchall:2,"optional-catchall":3,group:0};function ue(r,e){let t=Math.max(r.segments.length,e.segments.length);for(let n=0;n<t;n++){let o=r.segments[n],i=e.segments[n];if(!o)return-1;if(!i)return 1;let s=q[o.kind]-q[i.kind];if(s!==0)return s}return 0}function de(r,e){let t=D(e).map(i=>decodeURIComponent(i)),n={},o=0;for(let i=0;i<r.length;i++){let s=r[i],a=i===r.length-1;if(s.kind==="static"){if(t[o]!==s.value)return null;o++;continue}if(s.kind==="dynamic"){if(o>=t.length)return null;n[s.value]=t[o],o++;continue}if(s.kind==="catchall")return!a||o>=t.length?null:(n[s.value]=t.slice(o),n);if(s.kind==="optional-catchall")return a?(n[s.value]=t.slice(o),n):null}return o!==t.length?null:n}function T(r,e){for(let t of r){let n=de(t.segments,e);if(n)return{route:t,params:n,pathname:e}}return null}function ke(r,e={}){return`/${D(r).map(W).filter(n=>n.kind!=="group").flatMap(n=>{if(n.kind==="static")return[n.value];let o=e[n.value];if(o===void 0){if(n.kind==="optional-catchall")return[];throw new Error(`Missing param "${n.value}" for pattern "${r}".`)}return(Array.isArray(o)?o:[o]).map(s=>encodeURIComponent(s))}).join("/")}`}var E=class extends Error{constructor(e,t){super(`Redirect to ${e}`),this.name="RedirectSignal",this.to=e,this.permanent=t}},S=class extends Error{constructor(){super("Not found"),this.name="NotFoundSignal"}};function Se(r){throw new E(r,!1)}function Me(r){throw new E(r,!0)}function Te(){throw new S}function Pe(r){return{__domphyRewrite:r}}function N(r){return typeof r=="object"&&r!==null&&typeof r.__domphyRewrite=="string"}var pe=["GET","POST","PUT","PATCH","DELETE","HEAD","OPTIONS"];function C(r,e={}){let t=new Headers(e.headers);return t.has("content-type")||t.set("content-type","application/json; charset=utf-8"),new Response(JSON.stringify(r),b(x({},e),{headers:t}))}function Ce(r){let e=r.map(o=>({apiRoute:o,routeNode:{path:o.path,page:()=>({div:""})}})),t=I(e.map(o=>o.routeNode)),n=new Map(e.map(o=>[o.routeNode,o.apiRoute]));return o=>R(null,null,function*(){let i=new URL(o.url),s=T(t,i.pathname);if(!s)return C({error:"Not Found"},{status:404});let a=n.get(s.route.chain[s.route.chain.length-1]),l=pe.filter(c=>a[c]),p=o.method.toUpperCase(),d=a[p];if(!d&&p==="HEAD"&&a.GET&&(d=a.GET),!d&&p==="OPTIONS")return new Response(null,{status:204,headers:{allow:l.join(", ")}});if(!d)return C({error:"Method Not Allowed"},{status:405,headers:{allow:l.join(", ")}});try{let c=yield d(o,{params:s.params});return p==="HEAD"?new Response(null,{status:c.status,headers:c.headers}):c}catch(c){return c instanceof E?new Response(null,{status:c.permanent?308:307,headers:{location:c.to}}):c instanceof S?C({error:"Not Found"},{status:404}):C({error:"Internal Server Error"},{status:500})}})}import{ElementNode as j}from"@domphy/core";function Y(r,e){return R(this,null,function*(){let t={},n=null;for(let o of r){if(!o)continue;let i=typeof o=="function"?yield o(e):o;for(let a of Object.keys(i)){if(a==="title")continue;let l=i[a];l!==void 0&&(t[a]=l)}let s=i.title;if(s!==void 0){if(typeof s=="string"){t.title=n?n.replace("%s",s):s;continue}s.absolute!==void 0?t.title=s.absolute:s.default!==void 0&&(t.title=n?n.replace("%s",s.default):s.default),s.template!==void 0&&(n=s.template)}}return t})}function P(r,e){return!e||/^[a-z][a-z0-9+.-]*:/i.test(r)?r:new URL(r,e).toString()}function he(r){if(typeof r=="string")return r;let e=[];return e.push(r.index===!1?"noindex":"index"),e.push(r.follow===!1?"nofollow":"follow"),r.noarchive&&e.push("noarchive"),r.nosnippet&&e.push("nosnippet"),r.noimageindex&&e.push("noimageindex"),e.join(", ")}function A(r){var l,p,d,c,h,m,f,g,w,k,v;let e=[],t=r.metadataBase,n=(u,y)=>{y!==void 0&&e.push({tag:"meta",attributes:{name:u,content:y}})},o=(u,y)=>{y!==void 0&&e.push({tag:"meta",attributes:{property:u,content:y}})};r.title!==void 0&&e.push({tag:"title",attributes:{},content:r.title}),n("description",r.description),n("application-name",r.applicationName),n("generator",r.generator),n("keywords",(l=r.keywords)==null?void 0:l.join(", ")),n("referrer",r.referrer),n("theme-color",r.themeColor),n("color-scheme",r.colorScheme),n("viewport",r.viewport),r.robots!==void 0&&n("robots",he(r.robots));for(let u of(p=r.authors)!=null?p:[])n("author",u.name),u.url&&e.push({tag:"link",attributes:{rel:"author",href:u.url}});if(r.icons!==void 0){let u=typeof r.icons=="string"?{icon:r.icons}:r.icons;u.icon&&e.push({tag:"link",attributes:{rel:"icon",href:u.icon}}),u.shortcut&&e.push({tag:"link",attributes:{rel:"shortcut icon",href:u.shortcut}}),u.apple&&e.push({tag:"link",attributes:{rel:"apple-touch-icon",href:u.apple}})}let i=r.openGraph;if(i){o("og:title",(d=i.title)!=null?d:r.title),o("og:description",(c=i.description)!=null?c:r.description),o("og:url",i.url?P(i.url,t):void 0),o("og:site_name",i.siteName),o("og:type",i.type),o("og:locale",i.locale);for(let u of(h=i.images)!=null?h:[]){let y=typeof u=="string"?{url:u}:u;o("og:image",P(y.url,t)),y.width!==void 0&&o("og:image:width",String(y.width)),y.height!==void 0&&o("og:image:height",String(y.height)),y.alt!==void 0&&o("og:image:alt",y.alt)}}let s=r.twitter;if(s){n("twitter:card",(m=s.card)!=null?m:"summary"),n("twitter:title",(f=s.title)!=null?f:r.title),n("twitter:description",(g=s.description)!=null?g:r.description),n("twitter:site",s.site),n("twitter:creator",s.creator);for(let u of(w=s.images)!=null?w:[])n("twitter:image",P(u,t))}let a=r.alternates;if(a){a.canonical&&e.push({tag:"link",attributes:{rel:"canonical",href:P(a.canonical,t)}});for(let u of Object.keys((k=a.languages)!=null?k:{}))e.push({tag:"link",attributes:{rel:"alternate",hreflang:u,href:P(a.languages[u],t)}})}for(let u of Object.keys((v=r.other)!=null?v:{}))n(u,r.other[u]);return e}function me(r){return r.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;")}function fe(r){return r.replace(/&/g,"&amp;").replace(/</g,"&lt;")}function J(r){return r.map(e=>{var n;let t=Object.keys(e.attributes).map(o=>` ${o}="${me(e.attributes[o])}"`).join("");return e.tag==="title"?`<title>${fe((n=e.content)!=null?n:"")}</title>`:`<${e.tag}${t}>`}).join(`
2
+ `)}var V="data-domphy-head";function B(r){var e;if(typeof document!="undefined"){for(let t of Array.from(document.head.querySelectorAll(`[${V}]`)))t.remove();for(let t of r){if(t.tag==="title"){document.title=(e=t.content)!=null?e:"";continue}let n=document.createElement(t.tag);for(let o of Object.keys(t.attributes))n.setAttribute(o,t.attributes[o]);n.setAttribute(V,""),document.head.appendChild(n)}}}import{Notifier as ge,RecordState as ye,toState as we}from"@domphy/core";var Be=3e4,O=class{constructor(){this.entries=new Map;this.inflight=new Map}seed(e){for(let t of Object.keys(e))this.entries.set(t,{data:e[t],timestamp:Date.now(),consumable:!0})}snapshot(e){let t={};for(let n of e){let o=this.entries.get(n);o&&(t[n]=o.data)}return t}invalidate(e){if(e===void 0){this.entries.clear();return}for(let t of this.entries.keys())t.startsWith(e)&&this.entries.delete(t)}load(e,t,n,o){return R(this,null,function*(){let i=this.entries.get(e);if(i){let l=o!==void 0?o*1e3:0,p=Date.now()-i.timestamp<=l,d=i.consumable&&Date.now()-i.timestamp<=3e4;if(p||d)return i.consumable&&!p&&this.entries.delete(e),i.data;this.entries.delete(e)}let s=this.inflight.get(e);if(s)return s;let a=Promise.resolve(t(n)).then(l=>(this.inflight.delete(e),o!==void 0&&o>0&&this.entries.set(e,{data:l,timestamp:Date.now(),consumable:!1}),l),l=>{throw this.inflight.delete(e),l});return this.inflight.set(e,a),a})}prefetch(e,t,n,o){return R(this,null,function*(){if(o!==void 0){yield this.load(e,t,n,o);return}let i=this.entries.get(e);if(i&&Date.now()-i.timestamp<=3e4||this.inflight.has(e))return;let s=yield t(n);this.entries.set(e,{data:s,timestamp:Date.now(),consumable:!0})})}};function Z(){var t,n,o;let r=new Map,e=(n=(t=window.history.state)==null?void 0:t.__domphyIndex)!=null?n:0;return((o=window.history.state)==null?void 0:o.__domphyIndex)===void 0&&window.history.replaceState({__domphyIndex:e},""),"scrollRestoration"in window.history&&(window.history.scrollRestoration="manual"),{url:()=>new URL(window.location.href),push:i=>{e++,window.history.pushState({__domphyIndex:e},"",i)},replace:i=>{window.history.replaceState({__domphyIndex:e},"",i)},go:i=>window.history.go(i),listen:i=>{let s=()=>{var a,l;e=(l=(a=window.history.state)==null?void 0:a.__domphyIndex)!=null?l:0,i(new URL(window.location.href))};return window.addEventListener("popstate",s),()=>window.removeEventListener("popstate",s)},saveScroll:i=>r.set(e,i),readScroll:()=>{var i;return(i=r.get(e))!=null?i:null}}}var X="http://localhost";function Fe(r="/"){let e=[r],t=0,n=new Set;return{url:()=>new URL(e[t],X),push:o=>{e.splice(t+1),e.push(o),t++},replace:o=>{e[t]=o},go:o=>{let i=Math.min(Math.max(t+o,0),e.length-1);if(i===t)return;t=i;let s=new URL(e[t],X);for(let a of n)a(s)},listen:o=>(n.add(o),()=>n.delete(o))}}function Q(r){return{div:[{h2:"Application error"},{p:r.message}]}}function ee(){return{div:[{h2:"404"},{p:"This page could not be found."}]}}function _(r,e){return r.findIndex(t=>t.status===e)}function F(r){var v;let{match:e,baseContext:t,results:n,retry:o,defaultError:i,defaultNotFound:s}=r,{chain:a,chainIds:l}=e.route,p={};for(let u=0;u<a.length;u++)p[l[u]]=(v=n[u])==null?void 0:v.data;let d=a.map((u,y)=>{var M;return b(x({},t),{data:(M=n[y])==null?void 0:M.data,segmentData:p})}),c=_(n,"error"),h=_(n,"notfound"),m=_(n,"pending"),f,g,w;if(c!==-1){let u=U(a,c,M=>!!M.error);f=(u===-1?i:a[u].error)(n[c].error,o),f._key=`${l[Math.max(u,0)]}:error`,g=Math.min(u,c-1),w="error"}else if(h!==-1){let u=U(a,h,M=>!!M.notFound);f=(u===-1?s:a[u].notFound)(),f._key=`${l[Math.max(u,0)]}:notfound`,g=Math.min(u,h-1),w="notfound"}else if(m!==-1){let u=U(a,m,y=>!!y.loading);if(u===-1)return{element:{div:""},status:"loading"};f=a[u].loading(d[u]),f._key=`${l[u]}:loading`,g=Math.min(u,m-1),w="loading"}else f=a[a.length-1].page(d[a.length-1]),f._key=`${e.route.id}:page`,g=a.length-1,w="idle";let k=f;for(let u=g;u>=0;u--){let y=a[u].layout;y&&(k=y(k,d[u]),k._key===void 0&&(k._key=`${l[u]}:layout`))}return{element:k,status:w}}function U(r,e,t){for(let n=e;n>=0;n--)if(t(r[n]))return n;return-1}function te(r){let e=r();return e._key===void 0&&(e._key="app:notfound"),{element:e,status:"notfound"}}var L=null;function re(){if(!L)throw new Error("No router created yet. Call createApp() or new AppRouter() first.");return L}var H=class{constructor(e,t={}){this.events=new ge;this.cache=new O;this.navigationToken=0;this.releaseHistory=null;this.currentMatch=null;this.metadata={};this.lastRedirect=null;this.lastData={};var n,o,i;this.routes=I(e),this.middleware=(n=t.middleware)!=null?n:[],this.notFoundBlock=(o=t.notFound)!=null?o:ee,this.errorBlock=(i=t.error)!=null?i:(s=>Q(s)),this.headers=t.headers,this.history=t.history!==void 0?t.history:typeof window!="undefined"?Z():null,this.state=new ye({pathname:"/",search:"",hash:"",params:{},status:"idle",error:null}),this.tree=we({div:""}),L=this}start(){return R(this,null,function*(){this.history&&!this.releaseHistory&&(this.releaseHistory=this.history.listen(t=>{this.transition(t,{fromHistory:!0})}));let e=this.history?this.history.url():new URL("/","http://localhost");yield this.transition(e,{initial:!0})})}destroy(){var e;(e=this.releaseHistory)==null||e.call(this),this.releaseHistory=null,L===this&&(L=null)}currentUrl(){if(this.history)return this.history.url();let e=this.state.get("search");return new URL(`${this.state.get("pathname")}${e}`,"http://localhost")}resolve(e){return new URL(e,this.currentUrl())}navigate(n){return R(this,arguments,function*(e,t={}){let o=this.resolve(e);if(typeof window!="undefined"&&o.origin!==this.currentUrl().origin){window.location.assign(o.href);return}yield this.transition(o,t)})}push(e,t={}){return this.navigate(e,t)}replace(e,t={}){return this.navigate(e,b(x({},t),{replace:!0}))}back(){var e;(e=this.history)==null||e.go(-1)}forward(){var e;(e=this.history)==null||e.go(1)}refresh(){return R(this,null,function*(){this.cache.invalidate(),yield this.transition(this.currentUrl(),{replace:!0,scroll:!1})})}prefetch(e){return R(this,null,function*(){try{let t=this.resolve(e),n=T(this.routes,t.pathname);if(!n)return;let o=this.loaderContext(t,n);yield Promise.all(n.route.chain.map((i,s)=>{if(!i.loader)return Promise.resolve();let a=this.cacheKey(n.route.chainIds[s],t);return this.cache.prefetch(a,i.loader,o,i.revalidate)}))}catch(t){}})}searchParams(e){return new URLSearchParams(this.state.get("search",e))}addEventListener(e,t){return this.events.addListener(e,t)}getMatch(){return this.currentMatch}cacheKey(e,t){return`${e}|${t.pathname}${t.search}`}loaderContext(e,t){return{pathname:t.pathname,url:e.pathname+e.search,params:t.params,searchParams:e.searchParams,headers:this.headers}}transition(n){return R(this,arguments,function*(e,t={}){var s,a;let o=++this.navigationToken,i=e.pathname+e.search+e.hash;t.fromHistory||(this.lastRedirect=null),this.events.notify("routeChangeStart",i),this.saveScroll();try{let l=e.pathname,p={url:e,pathname:e.pathname,searchParams:e.searchParams,headers:this.headers};for(let c of this.middleware){let h=yield c(p);N(h)&&(l=h.__domphyRewrite)}if(o!==this.navigationToken)return;let d=T(this.routes,l);if(d){let c=d.route.chain[d.route.chain.length-1];if(c.redirect)throw new E(c.redirect,(s=c.permanent)!=null?s:!1);for(let h of d.route.chain)for(let m of(a=h.middleware)!=null?a:[]){let f=yield m(p);if(N(f)){yield this.transition(new URL(f.__domphyRewrite,e),t);return}}if(o!==this.navigationToken)return;yield this.renderMatch(e,d,o,t)}else yield this.renderNotFound(e,o,t)}catch(l){if(o!==this.navigationToken)return;if(l instanceof E){yield this.transition(this.resolve(l.to),b(x({},t),{replace:!0})),this.lastRedirect={to:l.to,permanent:l.permanent};return}if(l instanceof S){yield this.renderNotFound(e,o,t);return}let p=l instanceof Error?l:new Error(String(l));this.tree.set(this.errorBlock(p,()=>{this.refresh()})),this.state.set("status","error"),this.state.set("error",p),this.events.notify("routeChangeError",p,i);return}o===this.navigationToken&&this.events.notify("routeChangeComplete",i)})}renderMatch(e,t,n,o){return R(this,null,function*(){let{chain:i,chainIds:s}=t.route,a=this.loaderContext(e,t),l=i.map(()=>({status:"pending"})),p=null,d=i.map((g,w)=>{if(!g.loader)return l[w]={status:"success",data:void 0},Promise.resolve();let k=this.cacheKey(s[w],e);return this.cache.load(k,g.loader,a,g.revalidate).then(v=>{l[w]={status:"success",data:v}},v=>{v instanceof E?p=p!=null?p:v:v instanceof S?l[w]={status:"notfound"}:l[w]={status:"error",error:v instanceof Error?v:new Error(String(v))}})}),c=!1,h=Promise.all(d).then(()=>{c=!0});if(yield new Promise(g=>setTimeout(g,0)),!c&&i.some(g=>g.loading)){let g=F({match:t,baseContext:this.baseContext(e,t),results:l.map(w=>x({},w)),retry:()=>{this.refresh()},defaultError:this.errorBlock,defaultNotFound:this.notFoundBlock});n===this.navigationToken&&g.status==="loading"&&(this.state.set("status","loading"),this.tree.set(g.element))}if(yield h,n!==this.navigationToken)return;if(p)throw p;let m=F({match:t,baseContext:this.baseContext(e,t),results:l,retry:()=>{this.refresh()},defaultError:this.errorBlock,defaultNotFound:this.notFoundBlock}),f={};i.forEach((g,w)=>{g.loader&&l[w].status==="success"&&(f[this.cacheKey(s[w],e)]=l[w].data)}),this.lastData=f,this.currentMatch=t,yield this.applyMetadata(t,a),n===this.navigationToken&&this.commit(e,t,m.element,m.status,o)})}renderNotFound(e,t,n){return R(this,null,function*(){let o=te(this.notFoundBlock);this.currentMatch=null,this.metadata={},B([]),t===this.navigationToken&&this.commit(e,null,o.element,"notfound",n)})}baseContext(e,t){return{pathname:t.pathname,url:e.pathname+e.search,params:t.params,searchParams:e.searchParams,hash:e.hash,headers:this.headers}}applyMetadata(e,t){return R(this,null,function*(){try{this.metadata=yield Y(e.route.chain.map(n=>n.metadata),t)}catch(n){this.metadata={}}B(A(this.metadata))})}commit(e,t,n,o,i){var s;if(this.history&&!i.fromHistory&&!i.initial){let a=e.pathname+e.search+e.hash;i.replace?this.history.replace(a):this.history.push(a)}this.state.set("pathname",e.pathname),this.state.set("search",e.search),this.state.set("hash",e.hash),this.state.set("params",(s=t==null?void 0:t.params)!=null?s:{}),this.state.set("error",null),this.state.set("status",o),this.tree.set(n),this.restoreScroll(e,i)}saveScroll(){var e,t;typeof window!="undefined"&&((t=(e=this.history)==null?void 0:e.saveScroll)==null||t.call(e,{x:window.scrollX,y:window.scrollY}))}restoreScroll(e,t){var n,o,i,s;if(!(typeof window=="undefined"||t.scroll===!1||t.initial)){if(t.fromHistory){let a=(o=(n=this.history)==null?void 0:n.readScroll)==null?void 0:o.call(n);window.scrollTo((i=a==null?void 0:a.x)!=null?i:0,(s=a==null?void 0:a.y)!=null?s:0);return}if(e.hash){let a=document.getElementById(e.hash.slice(1));if(a){a.scrollIntoView();return}}window.scrollTo(0,0)}}};var ne="__DOMPHY_APP_DATA__",$=class{constructor(e,t={}){this.node=null;this.routes=e,this.options=t,this.router=new H(e,t)}element(){let e=this.router;return{div:t=>[e.tree.get(t)],style:{display:"contents"}}}render(e){return R(this,null,function*(){return yield this.router.start(),this.node=new j(this.element()),this.node.render(e),this.node})}hydrate(e,t){return R(this,null,function*(){let n=globalThis[ne];return n&&typeof n=="object"&&this.router.cache.seed(n),yield this.router.start(),this.node=new j(this.element()),this.node.mount(e,t),this.node})}destroy(){var e;(e=this.node)==null||e.remove(),this.node=null,this.router.destroy()}renderToString(n){return R(this,arguments,function*(e,t={}){let o=typeof e=="string"?new URL(e,"http://localhost"):e,i=new H(this.routes,b(x({},this.options),{history:null,headers:t.headers}));yield i.transition(o,{initial:!0});let s=i.state.get("status"),a=i.lastRedirect,l=new j({div:[i.tree.get()],style:{display:"contents"}}),p=i.lastData,d={html:l.generateHTML(),css:l.generateCSS(),head:J(A(i.metadata)),status:a?a.permanent?308:307:s==="notfound"?404:200,redirect:a==null?void 0:a.to,data:p,bootstrapScript:`<script>window.${ne} = ${Re(p)};</script>`};return i.destroy(),d})}};function Re(r){return JSON.stringify(r).replace(/</g,"\\u003c")}function rt(r,e={}){return new $(r,e)}var ve=[640,750,828,1080,1200,1920,2048,3840];function it(r){let{src:e,alt:t="",width:n,height:o,fill:i=!1,sizes:s,quality:a=75,priority:l=!1,placeholder:p="empty",blurDataURL:d,loader:c,deviceSizes:h=ve}=r,m={src:c?c({src:e,width:n!=null?n:h[h.length-1],quality:a}):e,alt:t,loading:l?"eager":"lazy",decoding:"async",fetchPriority:l?"high":"auto",style:{}};return c&&(m.srcSet=h.map(f=>`${c({src:e,width:f,quality:a})} ${f}w`).join(", "),s&&(m.sizes=s)),n!==void 0&&(m.width=n),o!==void 0&&(m.height=o),i&&(m.style=b(x({},m.style),{position:"absolute",inset:"0",width:"100%",height:"100%",objectFit:"cover"})),p==="blur"&&d&&(m.style=b(x({},m.style),{backgroundImage:`url("${d}")`,backgroundSize:"cover",backgroundPosition:"center"}),m.onLoad=f=>{let g=f.target;g.style.backgroundImage=""}),m}function xe(r){return r.defaultPrevented||r.button!==0||r.metaKey||r.ctrlKey||r.shiftKey||r.altKey}function ct(r){let{href:e,replace:t=!1,scroll:n=!0,prefetch:o="hover",exact:i=!1}=r,s=()=>{var d;return(d=r.router)!=null?d:re()},a=d=>{let c=new URL(e,"http://localhost").pathname;return i||c==="/"?d===c:d===c||d.startsWith(`${c}/`)},l=!1,p=()=>{l||(l=!0,s().prefetch(e))};return{href:e,ariaCurrent:d=>a(s().state.get("pathname",d))?"page":null,dataActive:d=>a(s().state.get("pathname",d))?"":null,onClick:d=>{let c=d,h=c.currentTarget;xe(c)||h.target&&h.target!=="_self"||h.hasAttribute("download")||new URL(h.href,window.location.href).origin===window.location.origin&&(c.preventDefault(),s().navigate(e,{replace:t,scroll:n}))},onMouseEnter:()=>{o==="hover"&&p()},onFocus:()=>{o==="hover"&&p()},_onMount:d=>{if(o!=="visible")return;if(typeof IntersectionObserver=="undefined"){p();return}let c=new IntersectionObserver(h=>{h.some(m=>m.isIntersecting)&&(p(),c.disconnect())});c.observe(d.domElement),d.setMetadata("navLinkObserver",c)},_onRemove:d=>{let c=d.getMetadata("navLinkObserver");c==null||c.disconnect()}}}var G=new Set;function dt(){G.clear()}function oe(r){let e=globalThis.requestIdleCallback;e?e(r):setTimeout(r,0)}function pt(r){let{src:e,strategy:t="afterInteractive",id:n,async:o=!0,onLoad:i,onError:s}=r,a=n!=null?n:e,l=d=>{G.has(a)||(G.add(a),d.src=e)},p={script:"",async:o,onLoad:()=>i==null?void 0:i(),onError:()=>s==null?void 0:s(),_onMount:d=>{let c=d.domElement;if(t==="afterInteractive"){l(c);return}document.readyState==="complete"?oe(()=>l(c)):window.addEventListener("load",()=>oe(()=>l(c)),{once:!0})}};return n!==void 0&&(p.id=n),p}function mt(r){return r}export{H as AppRouter,O as DataCache,$ as DomphyApp,S as NotFoundSignal,Be as PREFETCH_LIFETIME,E as RedirectSignal,B as applyHeadTags,ke as buildHref,te as buildNotFoundTree,F as buildTree,ue as compareSpecificity,I as compileRoutes,Ce as createApiHandler,rt as createApp,Z as createBrowserHistory,Fe as createMemoryHistory,Q as defaultErrorBlock,ee as defaultNotFoundBlock,mt as defineRoutes,re as getRouter,N as isRewrite,C as json,de as matchPath,T as matchRoute,A as metadataToHeadTags,ct as navLink,Te as notFound,it as optimizedImage,W as parseSegment,Me as permanentRedirect,Se as redirect,J as renderHeadTags,dt as resetScripts,Y as resolveMetadata,Pe as rewrite,pt as script,D as splitPath};
3
+ //# sourceMappingURL=index.js.map