@alepha/react 0.7.5 → 0.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.browser.d.ts +523 -0
- package/dist/index.browser.js +1041 -49
- package/dist/index.browser.js.map +1 -0
- package/dist/index.cjs +1288 -375
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +629 -0
- package/dist/index.d.ts +468 -559
- package/dist/index.js +1245 -354
- package/dist/index.js.map +1 -0
- package/package.json +12 -8
- package/src/descriptors/$page.ts +2 -47
- package/src/hooks/useClient.ts +3 -3
- package/src/index.ts +16 -4
- package/src/providers/BrowserRouterProvider.ts +3 -4
- package/src/providers/PageDescriptorProvider.ts +10 -57
- package/src/providers/ReactBrowserProvider.ts +3 -14
- package/src/providers/ReactServerProvider.ts +66 -58
- package/dist/index.browser.cjs +0 -75
- package/dist/useRouterState-BTmuHxkM.cjs +0 -1183
- package/dist/useRouterState-cCucJfTC.js +0 -1161
- package/src/providers/BrowserHeadProvider.ts +0 -43
- package/src/providers/ServerHeadProvider.ts +0 -91
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["options: PageDescriptorOptions<TConfig, TProps, TPropsParent>","props: PropsWithChildren<ClientOnlyProps>","text: string","opts: {\n\t\tonBegin?: (ev: { state: RouterState }) => void;\n\t\tonEnd?: (ev: { state: RouterState }) => void;\n\t\tonError?: (ev: { state: RouterState; error: Error }) => void;\n\t}","deps: any[]","subs: Function[]","props: ErrorBoundaryProps","error: Error","info: ErrorInfo","props: NestedViewProps","ErrorBoundary","page: HrefLike","envSchema","name: string","options: { params?: Record<string, string>; base?: string }","state: RouterState","context: PageReactContext","NestedView","route: PageRoute","request: PageRequest","layers: Layer[]","context: Record<string, any>","stack: Array<RouterStackItem>","route","config: Record<string, any>","str?: string","element: ReactNode","element","page: PageRoute","props: Record<string, any>","error: Error","ErrorViewer","page: { options: { name?: string } }","params: Record<string, any>","path: string","params: Record<string, string>","index: number","view: ReactNode | undefined","ClientOnly","pages: Array<{ value: { [OPTIONS]: PageDescriptorOptions } }>","target: { [OPTIONS]: PageDescriptorOptions }","entry: PageRouteEntry","it: any","templateLoader: TemplateLoader","root: string","ssrEnabled: boolean","name: string","options: PageDescriptorRenderOptions","context: PageRequest","page: PageRoute","target: PageRoute | undefined","template: string","state: RouterState","context: PageReactContext","hydrationData: ReactHydrationState","response: { html: string }","app: string","script: string","entry: PageRouteEntry","url: URL","options: TransitionOptions","state: RouterState","query: Record<string, string>","context: PageReactContext","props?: Record<string, any>","previous: PreviousLayerData[]","url: string","options: RouterGoOptions","options: { url?: string; previous?: PreviousLayerData[] }","pages: PageRoute[]","state: RouterState","layer: {\n\t\t\tpath: string;\n\t\t}","browser?: ReactBrowserProvider","query: Record<string, string>","props?: Record<string, any>","pathname: HrefLike","layer: { path: string }","options: { params?: Record<string, any> }","path: string","options?: RouterGoOptions","ev: any","record:\n\t\t\t| Record<string, any>\n\t\t\t| ((queryParams: Record<string, any>) => Record<string, any>)","options: {\n\t\t\t/**\n\t\t\t * If true, this will add a new entry to the history stack.\n\t\t\t */\n\t\t\tpush?: boolean;\n\t\t}","props: LinkProps","path: HrefLike","name: string | undefined","ev: any","clazz: Service<T>","_scope?: ClientScope","schema: T","options: UseQueryParamsHookOptions","queryParams: Static<T>","queryParams","alepha: Alepha","schema: TObject","data: any","state","alepha: Alepha"],"sources":["../src/descriptors/$page.ts","../src/components/ClientOnly.tsx","../src/contexts/RouterContext.ts","../src/hooks/useAlepha.ts","../src/components/ErrorViewer.tsx","../src/contexts/RouterLayerContext.ts","../src/hooks/useRouterEvents.ts","../src/components/ErrorBoundary.tsx","../src/components/NestedView.tsx","../src/components/NotFound.tsx","../src/errors/RedirectionError.ts","../src/providers/PageDescriptorProvider.ts","../src/providers/ReactServerProvider.ts","../src/providers/BrowserRouterProvider.ts","../src/providers/ReactBrowserProvider.ts","../src/hooks/RouterHookApi.ts","../src/hooks/useRouter.ts","../src/components/Link.tsx","../src/hooks/useActive.ts","../src/hooks/useInject.ts","../src/hooks/useClient.ts","../src/hooks/useQueryParams.ts","../src/hooks/useRouterState.ts","../src/index.ts"],"sourcesContent":["import {\n\t__descriptor,\n\ttype Async,\n\tKIND,\n\tNotImplementedError,\n\tOPTIONS,\n\ttype Static,\n\ttype TSchema,\n} from \"@alepha/core\";\nimport type { ServerRequest } from \"@alepha/server\";\nimport type { ServerRouteCache } from \"@alepha/server-cache\";\nimport type { FC, ReactNode } from \"react\";\nimport type { ClientOnlyProps } from \"../components/ClientOnly.tsx\";\nimport type { PageReactContext } from \"../providers/PageDescriptorProvider.ts\";\n\nconst KEY = \"PAGE\";\n\nexport interface PageConfigSchema {\n\tquery?: TSchema;\n\tparams?: TSchema;\n}\n\nexport type TPropsDefault = any;\n\nexport type TPropsParentDefault = {};\n\nexport interface PageDescriptorOptions<\n\tTConfig extends PageConfigSchema = PageConfigSchema,\n\tTProps extends object = TPropsDefault,\n\tTPropsParent extends object = TPropsParentDefault,\n> {\n\t/**\n\t * Name your page.\n\t *\n\t * @default Descriptor key\n\t */\n\tname?: string;\n\n\t/**\n\t * Optional description of the page.\n\t */\n\tdescription?: string;\n\n\t/**\n\t * Add a pathname to the page.\n\t *\n\t * Pathname can contain parameters, like `/post/:slug`.\n\t *\n\t * @default \"\"\n\t */\n\tpath?: string;\n\n\t/**\n\t * Add an input schema to define:\n\t * - `params`: parameters from the pathname.\n\t * - `query`: query parameters from the URL.\n\t */\n\tschema?: TConfig;\n\n\t/**\n\t * Load data before rendering the page.\n\t *\n\t * This function receives\n\t * - the request context and\n\t * - the parent props (if page has a parent)\n\t *\n\t * In SSR, the returned data will be serialized and sent to the client, then reused during the client-side hydration.\n\t *\n\t * Resolve can be stopped by throwing an error, which will be handled by the `errorHandler` function.\n\t * It's common to throw a `NotFoundError` to display a 404 page.\n\t *\n\t * RedirectError can be thrown to redirect the user to another page.\n\t */\n\tresolve?: (context: PageResolve<TConfig, TPropsParent>) => Async<TProps>;\n\n\t/**\n\t * The component to render when the page is loaded.\n\t *\n\t * If `lazy` is defined, this will be ignored.\n\t * Prefer using `lazy` to improve the initial loading time.\n\t */\n\tcomponent?: FC<TProps & TPropsParent>;\n\n\t/**\n\t * Lazy load the component when the page is loaded.\n\t *\n\t * It's recommended to use this for components to improve the initial loading time\n\t * and enable code-splitting.\n\t */\n\tlazy?: () => Promise<{ default: FC<TProps & TPropsParent> }>;\n\n\t/**\n\t * Set some children pages and make the page a parent page.\n\t *\n\t * /!\\ Parent page can't be rendered directly. /!\\\n\t *\n\t * If you still want to render at this pathname, add a child page with an empty path.\n\t */\n\tchildren?: Array<{ [OPTIONS]: PageDescriptorOptions }>;\n\n\tparent?: { [OPTIONS]: PageDescriptorOptions<PageConfigSchema, TPropsParent> };\n\n\tcan?: () => boolean;\n\n\terrorHandler?: (error: Error) => ReactNode;\n\n\tprerender?:\n\t\t| boolean\n\t\t| {\n\t\t\t\tentries?: Array<Partial<PageRequestConfig<TConfig>>>;\n\t\t };\n\n\t/**\n\t * If true, the page will be rendered on the client-side.\n\t */\n\tclient?: boolean | ClientOnlyProps;\n\n\tafterHandler?: (request: ServerRequest) => any;\n\n\tcache?: ServerRouteCache;\n}\n\nexport interface PageDescriptor<\n\tTConfig extends PageConfigSchema = PageConfigSchema,\n\tTProps extends object = TPropsDefault,\n\tTPropsParent extends object = TPropsParentDefault,\n> {\n\t[KIND]: typeof KEY;\n\t[OPTIONS]: PageDescriptorOptions<TConfig, TProps, TPropsParent>;\n\n\t/**\n\t * For testing or build purposes, this will render the page (with or without the HTML layout) and return the HTML and context.\n\t * Only valid for server-side rendering, it will throw an error if called on the client-side.\n\t */\n\trender: (\n\t\toptions?: PageDescriptorRenderOptions,\n\t) => Promise<PageDescriptorRenderResult>;\n}\n\n/**\n * Main descriptor for defining a React route in the application.\n */\nexport const $page = <\n\tTConfig extends PageConfigSchema = PageConfigSchema,\n\tTProps extends object = TPropsDefault,\n\tTPropsParent extends object = TPropsParentDefault,\n>(\n\toptions: PageDescriptorOptions<TConfig, TProps, TPropsParent>,\n): PageDescriptor<TConfig, TProps, TPropsParent> => {\n\t__descriptor(KEY);\n\n\tif (options.children) {\n\t\tfor (const child of options.children) {\n\t\t\tchild[OPTIONS].parent = {\n\t\t\t\t[OPTIONS]: options as PageDescriptorOptions<any, any, any>,\n\t\t\t};\n\t\t}\n\t}\n\n\tif (options.parent) {\n\t\toptions.parent[OPTIONS].children ??= [];\n\t\toptions.parent[OPTIONS].children.push({\n\t\t\t[OPTIONS]: options as PageDescriptorOptions<any, any, any>,\n\t\t});\n\t}\n\n\treturn {\n\t\t[KIND]: KEY,\n\t\t[OPTIONS]: options,\n\t\trender: () => {\n\t\t\tthrow new NotImplementedError(KEY);\n\t\t},\n\t};\n};\n\n$page[KIND] = KEY;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface PageDescriptorRenderOptions {\n\tparams?: Record<string, string>;\n\tquery?: Record<string, string>;\n\thtml?: boolean;\n\thydration?: boolean;\n}\n\nexport interface PageDescriptorRenderResult {\n\thtml: string;\n\tcontext: PageReactContext;\n}\n\nexport interface PageRequestConfig<\n\tTConfig extends PageConfigSchema = PageConfigSchema,\n> {\n\tparams: TConfig[\"params\"] extends TSchema\n\t\t? Static<TConfig[\"params\"]>\n\t\t: Record<string, string>;\n\n\tquery: TConfig[\"query\"] extends TSchema\n\t\t? Static<TConfig[\"query\"]>\n\t\t: Record<string, string>;\n}\n\nexport type PageResolve<\n\tTConfig extends PageConfigSchema = PageConfigSchema,\n\tTPropsParent extends object = TPropsParentDefault,\n> = PageRequestConfig<TConfig> & TPropsParent & PageReactContext;\n","import {\n\ttype PropsWithChildren,\n\ttype ReactNode,\n\tuseEffect,\n\tuseState,\n} from \"react\";\n\nexport interface ClientOnlyProps {\n\tfallback?: ReactNode;\n\tdisabled?: boolean;\n}\n\n/**\n * A small utility component that renders its children only on the client side.\n *\n * Optionally, you can provide a fallback React node that will be rendered.\n *\n * You should use this component when\n * - you have code that relies on browser-specific APIs\n * - you want to avoid server-side rendering for a specific part of your application\n * - you want to prevent pre-rendering of a component\n */\nconst ClientOnly = (props: PropsWithChildren<ClientOnlyProps>) => {\n\tconst [mounted, setMounted] = useState(false);\n\n\tuseEffect(() => setMounted(true), []);\n\n\tif (props.disabled) {\n\t\treturn props.children;\n\t}\n\n\treturn mounted ? props.children : props.fallback;\n};\n\nexport default ClientOnly;\n","import type { Alepha } from \"@alepha/core\";\nimport { createContext } from \"react\";\nimport type {\n\tPageReactContext,\n\tRouterState,\n} from \"../providers/PageDescriptorProvider.ts\";\n\nexport interface RouterContextValue {\n\talepha: Alepha;\n\tstate: RouterState;\n\tcontext: PageReactContext;\n}\n\nexport const RouterContext = createContext<RouterContextValue | undefined>(\n\tundefined,\n);\n","import type { Alepha } from \"@alepha/core\";\nimport { useContext } from \"react\";\nimport { RouterContext } from \"../contexts/RouterContext.ts\";\n\nexport const useAlepha = (): Alepha => {\n\tconst routerContext = useContext(RouterContext);\n\tif (!routerContext) {\n\t\tthrow new Error(\"useAlepha must be used within a RouterProvider\");\n\t}\n\n\treturn routerContext.alepha;\n};\n","import { useState } from \"react\";\nimport { useAlepha } from \"../hooks/useAlepha.ts\";\n\ninterface ErrorViewerProps {\n\terror: Error;\n}\n\n// TODO: design this better\n\nconst ErrorViewer = ({ error }: ErrorViewerProps) => {\n\tconst [expanded, setExpanded] = useState(false);\n\tconst isProduction = useAlepha().isProduction();\n\t// const status = isHttpError(error) ? error.status : 500;\n\n\tif (isProduction) {\n\t\treturn <ErrorViewerProduction />;\n\t}\n\n\tconst stackLines = error.stack?.split(\"\\n\") ?? [];\n\tconst previewLines = stackLines.slice(0, 5);\n\tconst hiddenLineCount = stackLines.length - previewLines.length;\n\n\tconst copyToClipboard = (text: string) => {\n\t\tnavigator.clipboard.writeText(text).catch((err) => {\n\t\t\tconsole.error(\"Clipboard error:\", err);\n\t\t});\n\t};\n\n\tconst styles = {\n\t\tcontainer: {\n\t\t\tpadding: \"24px\",\n\t\t\tbackgroundColor: \"#FEF2F2\",\n\t\t\tcolor: \"#7F1D1D\",\n\t\t\tborder: \"1px solid #FECACA\",\n\t\t\tborderRadius: \"16px\",\n\t\t\tboxShadow: \"0 8px 24px rgba(0,0,0,0.05)\",\n\t\t\tfontFamily: \"monospace\",\n\t\t\tmaxWidth: \"768px\",\n\t\t\tmargin: \"40px auto\",\n\t\t},\n\t\theading: {\n\t\t\tfontSize: \"20px\",\n\t\t\tfontWeight: \"bold\",\n\t\t\tmarginBottom: \"4px\",\n\t\t},\n\t\tname: {\n\t\t\tfontSize: \"16px\",\n\t\t\tfontWeight: 600,\n\t\t},\n\t\tmessage: {\n\t\t\tfontSize: \"14px\",\n\t\t\tmarginBottom: \"16px\",\n\t\t},\n\t\tsectionHeader: {\n\t\t\tdisplay: \"flex\",\n\t\t\tjustifyContent: \"space-between\",\n\t\t\talignItems: \"center\",\n\t\t\tfontSize: \"12px\",\n\t\t\tmarginBottom: \"4px\",\n\t\t\tcolor: \"#991B1B\",\n\t\t},\n\t\tcopyButton: {\n\t\t\tfontSize: \"12px\",\n\t\t\tcolor: \"#DC2626\",\n\t\t\tbackground: \"none\",\n\t\t\tborder: \"none\",\n\t\t\tcursor: \"pointer\",\n\t\t\ttextDecoration: \"underline\",\n\t\t},\n\t\tstackContainer: {\n\t\t\tbackgroundColor: \"#FEE2E2\",\n\t\t\tpadding: \"12px\",\n\t\t\tborderRadius: \"8px\",\n\t\t\tfontSize: \"13px\",\n\t\t\tlineHeight: \"1.4\",\n\t\t\toverflowX: \"auto\" as const,\n\t\t\twhiteSpace: \"pre-wrap\" as const,\n\t\t},\n\t\texpandLine: {\n\t\t\tcolor: \"#F87171\",\n\t\t\tcursor: \"pointer\",\n\t\t\tmarginTop: \"8px\",\n\t\t},\n\t};\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<div>\n\t\t\t\t<div style={styles.heading}>🔥 Error</div>\n\t\t\t\t<div style={styles.name}>{error.name}</div>\n\t\t\t\t<div style={styles.message}>{error.message}</div>\n\t\t\t</div>\n\n\t\t\t{stackLines.length > 0 && (\n\t\t\t\t<div>\n\t\t\t\t\t<div style={styles.sectionHeader}>\n\t\t\t\t\t\t<span>Stack trace</span>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tonClick={() => copyToClipboard(error.stack!)}\n\t\t\t\t\t\t\tstyle={styles.copyButton}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tCopy all\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</div>\n\t\t\t\t\t<pre style={styles.stackContainer}>\n\t\t\t\t\t\t{(expanded ? stackLines : previewLines).map((line, i) => (\n\t\t\t\t\t\t\t<div key={i}>{line}</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t{!expanded && hiddenLineCount > 0 && (\n\t\t\t\t\t\t\t<div style={styles.expandLine} onClick={() => setExpanded(true)}>\n\t\t\t\t\t\t\t\t+ {hiddenLineCount} more lines...\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</pre>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n};\n\nexport default ErrorViewer;\n\nconst ErrorViewerProduction = () => {\n\tconst styles = {\n\t\tcontainer: {\n\t\t\tpadding: \"24px\",\n\t\t\tbackgroundColor: \"#FEF2F2\",\n\t\t\tcolor: \"#7F1D1D\",\n\t\t\tborder: \"1px solid #FECACA\",\n\t\t\tborderRadius: \"16px\",\n\t\t\tboxShadow: \"0 8px 24px rgba(0,0,0,0.05)\",\n\t\t\tfontFamily: \"monospace\",\n\t\t\tmaxWidth: \"768px\",\n\t\t\tmargin: \"40px auto\",\n\t\t\ttextAlign: \"center\" as const,\n\t\t},\n\t\theading: {\n\t\t\tfontSize: \"20px\",\n\t\t\tfontWeight: \"bold\",\n\t\t\tmarginBottom: \"8px\",\n\t\t},\n\t\tname: {\n\t\t\tfontSize: \"16px\",\n\t\t\tfontWeight: 600,\n\t\t\tmarginBottom: \"4px\",\n\t\t},\n\t\tmessage: {\n\t\t\tfontSize: \"14px\",\n\t\t\topacity: 0.85,\n\t\t},\n\t};\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<div style={styles.heading}>🚨 An error occurred</div>\n\t\t\t<div style={styles.message}>\n\t\t\t\tSomething went wrong. Please try again later.\n\t\t\t</div>\n\t\t</div>\n\t);\n};\n","import { createContext } from \"react\";\n\nexport interface RouterLayerContextValue {\n\tindex: number;\n\tpath: string;\n}\n\nexport const RouterLayerContext = createContext<\n\tRouterLayerContextValue | undefined\n>(undefined);\n","import { useContext, useEffect } from \"react\";\nimport { RouterContext } from \"../contexts/RouterContext.ts\";\nimport type { RouterState } from \"../providers/PageDescriptorProvider.ts\";\n\nexport const useRouterEvents = (\n\topts: {\n\t\tonBegin?: (ev: { state: RouterState }) => void;\n\t\tonEnd?: (ev: { state: RouterState }) => void;\n\t\tonError?: (ev: { state: RouterState; error: Error }) => void;\n\t} = {},\n\tdeps: any[] = [],\n) => {\n\tconst ctx = useContext(RouterContext);\n\tif (!ctx) {\n\t\tthrow new Error(\"useRouter must be used within a RouterProvider\");\n\t}\n\n\tuseEffect(() => {\n\t\tif (!ctx.alepha.isBrowser()) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst subs: Function[] = [];\n\t\tconst onBegin = opts.onBegin;\n\t\tconst onEnd = opts.onEnd;\n\t\tconst onError = opts.onError;\n\n\t\tif (onBegin) {\n\t\t\tsubs.push(\n\t\t\t\tctx.alepha.on(\"react:transition:begin\", {\n\t\t\t\t\tcallback: onBegin,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (onEnd) {\n\t\t\tsubs.push(\n\t\t\t\tctx.alepha.on(\"react:transition:end\", {\n\t\t\t\t\tcallback: onEnd,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (onError) {\n\t\t\tsubs.push(\n\t\t\t\tctx.alepha.on(\"react:transition:error\", {\n\t\t\t\t\tcallback: onError,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\treturn () => {\n\t\t\tfor (const sub of subs) {\n\t\t\t\tsub();\n\t\t\t}\n\t\t};\n\t}, deps);\n};\n","import React, {\n\ttype ErrorInfo,\n\ttype PropsWithChildren,\n\ttype ReactNode,\n} from \"react\";\n\n/**\n * Props for the ErrorBoundary component.\n */\nexport interface ErrorBoundaryProps {\n\t/**\n\t * Fallback React node to render when an error is caught.\n\t * If not provided, a default error message will be shown.\n\t */\n\tfallback: (error: Error) => ReactNode;\n\n\t/**\n\t * Optional callback that receives the error and error info.\n\t * Use this to log errors to a monitoring service.\n\t */\n\tonError?: (error: Error, info: ErrorInfo) => void;\n}\n\n/**\n * State of the ErrorBoundary component.\n */\ninterface ErrorBoundaryState {\n\terror?: Error;\n}\n\n/**\n * A reusable error boundary for catching rendering errors\n * in any part of the React component tree.\n */\nexport class ErrorBoundary extends React.Component<\n\tPropsWithChildren<ErrorBoundaryProps>,\n\tErrorBoundaryState\n> {\n\tconstructor(props: ErrorBoundaryProps) {\n\t\tsuper(props);\n\t\tthis.state = {};\n\t}\n\n\t/**\n\t * Update state so the next render shows the fallback UI.\n\t */\n\tstatic getDerivedStateFromError(error: Error): ErrorBoundaryState {\n\t\treturn {\n\t\t\terror,\n\t\t};\n\t}\n\n\t/**\n\t * Lifecycle method called when an error is caught.\n\t * You can log the error or perform side effects here.\n\t */\n\tcomponentDidCatch(error: Error, info: ErrorInfo): void {\n\t\tif (this.props.onError) {\n\t\t\tthis.props.onError(error, info);\n\t\t}\n\t}\n\n\trender(): ReactNode {\n\t\tif (this.state.error) {\n\t\t\treturn this.props.fallback(this.state.error);\n\t\t}\n\n\t\treturn this.props.children;\n\t}\n}\n\nexport default ErrorBoundary;\n","import type { ReactNode } from \"react\";\nimport { useContext, useState } from \"react\";\nimport { RouterContext } from \"../contexts/RouterContext.ts\";\nimport { RouterLayerContext } from \"../contexts/RouterLayerContext.ts\";\nimport { useRouterEvents } from \"../hooks/useRouterEvents.ts\";\nimport ErrorBoundary from \"./ErrorBoundary.tsx\";\n\nexport interface NestedViewProps {\n\tchildren?: ReactNode;\n}\n\n/**\n * A component that renders the current view of the nested router layer.\n *\n * To be simple, it renders the `element` of the current child page of a parent page.\n *\n * @example\n * ```tsx\n * import { NestedView } from \"@alepha/react\";\n *\n * class App {\n * parent = $page({\n * component: () => <NestedView />,\n * });\n *\n * child = $page({\n * parent: this.root,\n * component: () => <div>Child Page</div>,\n * });\n * }\n * ```\n */\nconst NestedView = (props: NestedViewProps) => {\n\tconst app = useContext(RouterContext);\n\tconst layer = useContext(RouterLayerContext);\n\tconst index = layer?.index ?? 0;\n\n\tconst [view, setView] = useState<ReactNode | undefined>(\n\t\tapp?.state.layers[index]?.element,\n\t);\n\n\tuseRouterEvents(\n\t\t{\n\t\t\tonEnd: ({ state }) => {\n\t\t\t\tsetView(state.layers[index]?.element);\n\t\t\t},\n\t\t},\n\t\t[app],\n\t);\n\n\tif (!app) {\n\t\tthrow new Error(\"NestedView must be used within a RouterContext.\");\n\t}\n\n\tconst element = view ?? props.children ?? null;\n\n\treturn (\n\t\t<ErrorBoundary fallback={app.context.onError!}>{element}</ErrorBoundary>\n\t);\n};\n\nexport default NestedView;\n","export default function NotFoundPage() {\n\treturn (\n\t\t<div\n\t\t\tstyle={{\n\t\t\t\theight: \"100vh\",\n\t\t\t\tdisplay: \"flex\",\n\t\t\t\tflexDirection: \"column\",\n\t\t\t\tjustifyContent: \"center\",\n\t\t\t\talignItems: \"center\",\n\t\t\t\ttextAlign: \"center\",\n\t\t\t\tfontFamily: \"sans-serif\",\n\t\t\t\tpadding: \"1rem\",\n\t\t\t}}\n\t\t>\n\t\t\t<h1 style={{ fontSize: \"1rem\", marginBottom: \"0.5rem\" }}>\n\t\t\t\tThis page does not exist\n\t\t\t</h1>\n\t\t</div>\n\t);\n}\n","import type { HrefLike } from \"../hooks/RouterHookApi.ts\";\n\nexport class RedirectionError extends Error {\n\tpublic readonly page: HrefLike;\n\n\tconstructor(page: HrefLike) {\n\t\tsuper(\"Redirection\");\n\t\tthis.page = page;\n\t}\n}\n","import type { Static } from \"@alepha/core\";\nimport { $hook, $inject, $logger, Alepha, OPTIONS, t } from \"@alepha/core\";\nimport type { ApiLinksResponse } from \"@alepha/server\";\nimport { createElement, type ReactNode, StrictMode } from \"react\";\nimport ClientOnly from \"../components/ClientOnly.tsx\";\nimport ErrorViewer from \"../components/ErrorViewer.tsx\";\nimport NestedView from \"../components/NestedView.tsx\";\nimport NotFoundPage from \"../components/NotFound.tsx\";\nimport { RouterContext } from \"../contexts/RouterContext.ts\";\nimport { RouterLayerContext } from \"../contexts/RouterLayerContext.ts\";\nimport { $page, type PageDescriptorOptions } from \"../descriptors/$page.ts\";\nimport { RedirectionError } from \"../errors/RedirectionError.ts\";\n\nconst envSchema = t.object({\n\tREACT_STRICT_MODE: t.boolean({ default: true }),\n});\n\ndeclare module \"@alepha/core\" {\n\texport interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport class PageDescriptorProvider {\n\tprotected readonly log = $logger();\n\tprotected readonly env = $inject(envSchema);\n\tprotected readonly alepha = $inject(Alepha);\n\tprotected readonly pages: PageRoute[] = [];\n\n\tpublic getPages(): PageRoute[] {\n\t\treturn this.pages;\n\t}\n\n\tpublic page(name: string): PageRoute {\n\t\tfor (const page of this.pages) {\n\t\t\tif (page.name === name) {\n\t\t\t\treturn page;\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(`Page ${name} not found`);\n\t}\n\n\tpublic url(\n\t\tname: string,\n\t\toptions: { params?: Record<string, string>; base?: string } = {},\n\t): URL {\n\t\tconst page = this.page(name);\n\t\tif (!page) {\n\t\t\tthrow new Error(`Page ${name} not found`);\n\t\t}\n\n\t\tlet url = page.path ?? \"\";\n\t\tlet parent = page.parent;\n\t\twhile (parent) {\n\t\t\turl = `${parent.path ?? \"\"}/${url}`;\n\t\t\tparent = parent.parent;\n\t\t}\n\n\t\turl = this.compile(url, options.params ?? {});\n\n\t\treturn new URL(\n\t\t\turl.replace(/\\/\\/+/g, \"/\") || \"/\",\n\t\t\toptions.base ?? `http://localhost`,\n\t\t);\n\t}\n\n\tpublic root(state: RouterState, context: PageReactContext): ReactNode {\n\t\tconst root = createElement(\n\t\t\tRouterContext.Provider,\n\t\t\t{\n\t\t\t\tvalue: {\n\t\t\t\t\talepha: this.alepha,\n\t\t\t\t\tstate,\n\t\t\t\t\tcontext,\n\t\t\t\t},\n\t\t\t},\n\t\t\tcreateElement(NestedView, {}, state.layers[0]?.element),\n\t\t);\n\n\t\tif (this.env.REACT_STRICT_MODE) {\n\t\t\treturn createElement(StrictMode, {}, root);\n\t\t}\n\n\t\treturn root;\n\t}\n\n\tpublic async createLayers(\n\t\troute: PageRoute,\n\t\trequest: PageRequest,\n\t): Promise<CreateLayersResult> {\n\t\tconst { pathname, search } = request.url;\n\t\tconst layers: Layer[] = []; // result layers\n\t\tlet context: Record<string, any> = {}; // all props\n\t\tconst stack: Array<RouterStackItem> = [{ route }]; // stack of routes\n\t\trequest.onError = (error) => this.renderError(error); // error handler\n\n\t\tlet parent = route.parent;\n\t\twhile (parent) {\n\t\t\tstack.unshift({ route: parent });\n\t\t\tparent = parent.parent;\n\t\t}\n\n\t\tlet forceRefresh = false;\n\n\t\tfor (let i = 0; i < stack.length; i++) {\n\t\t\tconst it = stack[i];\n\t\t\tconst route = it.route;\n\t\t\tconst config: Record<string, any> = {};\n\n\t\t\ttry {\n\t\t\t\tconfig.query = route.schema?.query\n\t\t\t\t\t? this.alepha.parse(route.schema.query, request.query)\n\t\t\t\t\t: request.query;\n\t\t\t} catch (e) {\n\t\t\t\tit.error = e as Error;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconfig.params = route.schema?.params\n\t\t\t\t\t? this.alepha.parse(route.schema.params, request.params)\n\t\t\t\t\t: request.params;\n\t\t\t} catch (e) {\n\t\t\t\tit.error = e as Error;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// save config\n\t\t\tit.config = {\n\t\t\t\t...config,\n\t\t\t};\n\n\t\t\t// no resolve, render a basic view by default\n\t\t\tif (!route.resolve) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// check if previous layer is the same, reuse if possible\n\t\t\tconst previous = request.previous;\n\t\t\tif (previous?.[i] && !forceRefresh && previous[i].name === route.name) {\n\t\t\t\tconst url = (str?: string) => (str ? str.replace(/\\/\\/+/g, \"/\") : \"/\");\n\n\t\t\t\tconst prev = JSON.stringify({\n\t\t\t\t\tpart: url(previous[i].part),\n\t\t\t\t\tparams: previous[i].config?.params ?? {},\n\t\t\t\t});\n\n\t\t\t\tconst curr = JSON.stringify({\n\t\t\t\t\tpart: url(route.path),\n\t\t\t\t\tparams: config.params ?? {},\n\t\t\t\t});\n\n\t\t\t\tif (prev === curr) {\n\t\t\t\t\t// part is the same, reuse previous layer\n\t\t\t\t\tit.props = previous[i].props;\n\t\t\t\t\tit.error = previous[i].error;\n\t\t\t\t\tcontext = {\n\t\t\t\t\t\t...context,\n\t\t\t\t\t\t...it.props,\n\t\t\t\t\t};\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// part is different, force refresh of next layers\n\t\t\t\tforceRefresh = true;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst props =\n\t\t\t\t\t(await route.resolve?.({\n\t\t\t\t\t\t...request, // request\n\t\t\t\t\t\t...config, // params, query\n\t\t\t\t\t\t...context, // previous props\n\t\t\t\t\t} as any)) ?? {};\n\n\t\t\t\t// save props\n\t\t\t\tit.props = {\n\t\t\t\t\t...props,\n\t\t\t\t};\n\n\t\t\t\t// add props to context\n\t\t\t\tcontext = {\n\t\t\t\t\t...context,\n\t\t\t\t\t...props,\n\t\t\t\t};\n\t\t\t} catch (e) {\n\t\t\t\t// check if we need to redirect\n\t\t\t\tif (e instanceof RedirectionError) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tlayers: [],\n\t\t\t\t\t\tredirect: typeof e.page === \"string\" ? e.page : this.href(e.page),\n\t\t\t\t\t\tpathname,\n\t\t\t\t\t\tsearch,\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tthis.log.error(e);\n\n\t\t\t\tit.error = e as Error;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tlet acc = \"\";\n\t\tfor (let i = 0; i < stack.length; i++) {\n\t\t\tconst it = stack[i];\n\t\t\tconst props = it.props ?? {};\n\n\t\t\tconst params = { ...it.config?.params };\n\t\t\tfor (const key of Object.keys(params)) {\n\t\t\t\tparams[key] = String(params[key]);\n\t\t\t}\n\n\t\t\t// if (it.route.head && !it.error) {\n\t\t\t// \tthis.fillHead(it.route, request, {\n\t\t\t// \t\t...props,\n\t\t\t// \t\t...context,\n\t\t\t// \t});\n\t\t\t// }\n\n\t\t\tacc += \"/\";\n\t\t\tacc += it.route.path ? this.compile(it.route.path, params) : \"\";\n\t\t\tconst path = acc.replace(/\\/+/, \"/\");\n\t\t\tconst localErrorHandler = this.getErrorHandler(it.route);\n\t\t\tif (localErrorHandler) {\n\t\t\t\trequest.onError = localErrorHandler;\n\t\t\t}\n\n\t\t\t// handler has thrown an error, render an error view\n\t\t\tif (it.error) {\n\t\t\t\tlet element: ReactNode = await request.onError(it.error);\n\t\t\t\tif (element === null) {\n\t\t\t\t\telement = this.renderError(it.error);\n\t\t\t\t}\n\n\t\t\t\tlayers.push({\n\t\t\t\t\tprops,\n\t\t\t\t\terror: it.error,\n\t\t\t\t\tname: it.route.name,\n\t\t\t\t\tpart: it.route.path,\n\t\t\t\t\tconfig: it.config,\n\t\t\t\t\telement: this.renderView(i + 1, path, element, it.route),\n\t\t\t\t\tindex: i + 1,\n\t\t\t\t\tpath,\n\t\t\t\t\troute,\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// normal use case\n\n\t\t\tconst element = await this.createElement(it.route, {\n\t\t\t\t...props,\n\t\t\t\t...context,\n\t\t\t});\n\n\t\t\tlayers.push({\n\t\t\t\tname: it.route.name,\n\t\t\t\tprops,\n\t\t\t\tpart: it.route.path,\n\t\t\t\tconfig: it.config,\n\t\t\t\telement: this.renderView(i + 1, path, element, it.route),\n\t\t\t\tindex: i + 1,\n\t\t\t\tpath,\n\t\t\t\troute,\n\t\t\t});\n\t\t}\n\n\t\treturn { layers, pathname, search };\n\t}\n\n\tprotected getErrorHandler(route: PageRoute) {\n\t\tif (route.errorHandler) return route.errorHandler;\n\t\tlet parent = route.parent;\n\t\twhile (parent) {\n\t\t\tif (parent.errorHandler) return parent.errorHandler;\n\t\t\tparent = parent.parent;\n\t\t}\n\t}\n\n\tprotected async createElement(\n\t\tpage: PageRoute,\n\t\tprops: Record<string, any>,\n\t): Promise<ReactNode> {\n\t\tif (page.lazy) {\n\t\t\tconst component = await page.lazy(); // load component\n\t\t\treturn createElement(component.default, props);\n\t\t}\n\n\t\tif (page.component) {\n\t\t\treturn createElement(page.component, props);\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tpublic renderError(error: Error): ReactNode {\n\t\treturn createElement(ErrorViewer, { error });\n\t}\n\n\tpublic renderEmptyView(): ReactNode {\n\t\treturn createElement(NestedView, {});\n\t}\n\n\tpublic href(\n\t\tpage: { options: { name?: string } },\n\t\tparams: Record<string, any> = {},\n\t): string {\n\t\tconst found = this.pages.find((it) => it.name === page.options.name);\n\t\tif (!found) {\n\t\t\tthrow new Error(`Page ${page.options.name} not found`);\n\t\t}\n\n\t\tlet url = found.path ?? \"\";\n\t\tlet parent = found.parent;\n\t\twhile (parent) {\n\t\t\turl = `${parent.path ?? \"\"}/${url}`;\n\t\t\tparent = parent.parent;\n\t\t}\n\n\t\turl = this.compile(url, params);\n\n\t\treturn url.replace(/\\/\\/+/g, \"/\") || \"/\";\n\t}\n\n\tpublic compile(path: string, params: Record<string, string> = {}) {\n\t\tfor (const [key, value] of Object.entries(params)) {\n\t\t\tpath = path.replace(`:${key}`, value);\n\t\t}\n\t\treturn path;\n\t}\n\n\tprotected renderView(\n\t\tindex: number,\n\t\tpath: string,\n\t\tview: ReactNode | undefined,\n\t\tpage: PageRoute,\n\t): ReactNode {\n\t\tview ??= this.renderEmptyView();\n\n\t\tconst element = page.client\n\t\t\t? createElement(\n\t\t\t\t\tClientOnly,\n\t\t\t\t\ttypeof page.client === \"object\" ? page.client : {},\n\t\t\t\t\tview,\n\t\t\t\t)\n\t\t\t: view;\n\n\t\treturn createElement(\n\t\t\tRouterLayerContext.Provider,\n\t\t\t{\n\t\t\t\tvalue: {\n\t\t\t\t\tindex,\n\t\t\t\t\tpath,\n\t\t\t\t},\n\t\t\t},\n\t\t\telement,\n\t\t);\n\t}\n\n\tprotected readonly configure = $hook({\n\t\tname: \"configure\",\n\t\thandler: () => {\n\t\t\tlet hasNotFoundHandler = false;\n\t\t\tconst pages = this.alepha.getDescriptorValues($page);\n\t\t\tfor (const { value, key } of pages) {\n\t\t\t\tvalue[OPTIONS].name ??= key;\n\t\t\t}\n\n\t\t\tfor (const { value } of pages) {\n\t\t\t\t// skip children, we only want root pages\n\t\t\t\tif (value[OPTIONS].parent) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (value[OPTIONS].path === \"/*\") {\n\t\t\t\t\thasNotFoundHandler = true;\n\t\t\t\t}\n\n\t\t\t\tthis.add(this.map(pages, value));\n\t\t\t}\n\n\t\t\tif (!hasNotFoundHandler && pages.length > 0) {\n\t\t\t\t// add a default 404 page if not already defined\n\t\t\t\tthis.add({\n\t\t\t\t\tpath: \"/*\",\n\t\t\t\t\tname: \"notFound\",\n\t\t\t\t\tcache: true,\n\t\t\t\t\tcomponent: NotFoundPage,\n\t\t\t\t\tafterHandler: ({ reply }) => {\n\t\t\t\t\t\treply.status = 404;\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t});\n\n\tprotected map(\n\t\tpages: Array<{ value: { [OPTIONS]: PageDescriptorOptions } }>,\n\t\ttarget: { [OPTIONS]: PageDescriptorOptions },\n\t): PageRouteEntry {\n\t\tconst children = target[OPTIONS].children ?? [];\n\n\t\treturn {\n\t\t\t...target[OPTIONS],\n\t\t\tparent: undefined,\n\t\t\tchildren: children.map((it) => this.map(pages, it)),\n\t\t} as PageRoute;\n\t}\n\n\tpublic add(entry: PageRouteEntry) {\n\t\tif (this.alepha.isReady()) {\n\t\t\tthrow new Error(\"Router is already initialized\");\n\t\t}\n\n\t\tentry.name ??= this.nextId();\n\t\tconst page = entry as PageRoute;\n\n\t\tpage.match = this.createMatch(page);\n\t\tthis.pages.push(page);\n\n\t\tif (page.children) {\n\t\t\tfor (const child of page.children) {\n\t\t\t\t(child as PageRoute).parent = page;\n\t\t\t\tthis.add(child);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected createMatch(page: PageRoute): string {\n\t\tlet url = page.path ?? \"/\";\n\t\tlet target = page.parent;\n\t\twhile (target) {\n\t\t\turl = `${target.path ?? \"\"}/${url}`;\n\t\t\ttarget = target.parent;\n\t\t}\n\n\t\tlet path = url.replace(/\\/\\/+/g, \"/\");\n\n\t\tif (path.endsWith(\"/\") && path !== \"/\") {\n\t\t\t// remove trailing slash\n\t\t\tpath = path.slice(0, -1);\n\t\t}\n\n\t\treturn path;\n\t}\n\n\tprotected _next = 0;\n\n\tprotected nextId(): string {\n\t\tthis._next += 1;\n\t\treturn `P${this._next}`;\n\t}\n}\n\nexport const isPageRoute = (it: any): it is PageRoute => {\n\treturn (\n\t\tit &&\n\t\ttypeof it === \"object\" &&\n\t\ttypeof it.path === \"string\" &&\n\t\ttypeof it.page === \"object\"\n\t);\n};\n\nexport interface PageRouteEntry\n\textends Omit<PageDescriptorOptions, \"children\" | \"parent\"> {\n\tchildren?: PageRouteEntry[];\n}\n\nexport interface PageRoute extends PageRouteEntry {\n\ttype: \"page\";\n\tname: string;\n\tparent?: PageRoute;\n\tmatch: string;\n}\n\nexport interface Layer {\n\tconfig?: {\n\t\tquery?: Record<string, any>;\n\t\tparams?: Record<string, any>;\n\t\t// stack of resolved props\n\t\tcontext?: Record<string, any>;\n\t};\n\n\tname: string;\n\tprops?: Record<string, any>;\n\terror?: Error;\n\tpart?: string;\n\telement: ReactNode;\n\tindex: number;\n\tpath: string;\n\troute?: PageRoute;\n}\n\nexport type PreviousLayerData = Omit<Layer, \"element\" | \"index\" | \"path\">;\n\nexport interface AnchorProps {\n\thref: string;\n\tonClick: (ev: any) => any;\n}\n\nexport interface RouterState {\n\tpathname: string;\n\tsearch: string;\n\tlayers: Array<Layer>;\n}\n\nexport interface TransitionOptions {\n\tstate?: RouterState;\n\tprevious?: PreviousLayerData[];\n\tcontext?: PageReactContext;\n}\n\nexport interface RouterStackItem {\n\troute: PageRoute;\n\tconfig?: Record<string, any>;\n\tprops?: Record<string, any>;\n\terror?: Error;\n}\n\nexport interface RouterRenderResult {\n\tstate: RouterState;\n\tcontext: PageReactContext;\n\tredirect?: string;\n}\n\nexport interface PageRequest extends PageReactContext {\n\tparams: Record<string, any>;\n\tquery: Record<string, string>;\n\n\t// previous layers (browser history or browser hydration, always null on server)\n\tprevious?: PreviousLayerData[];\n}\n\nexport interface CreateLayersResult extends RouterState {\n\tredirect?: string;\n}\n\n/**\n * It's like RouterState, but publicly available in React context.\n * This is where we store all plugin data!\n */\nexport interface PageReactContext {\n\turl: URL;\n\tonError: (error: Error) => ReactNode;\n\tlinks?: ApiLinksResponse;\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n\t$hook,\n\t$inject,\n\t$logger,\n\tAlepha,\n\tOPTIONS,\n\ttype Static,\n\tt,\n} from \"@alepha/core\";\nimport {\n\tapiLinksResponseSchema,\n\ttype ServerHandler,\n\tServerRouterProvider,\n\tServerTimingProvider,\n} from \"@alepha/server\";\nimport { ServerLinksProvider } from \"@alepha/server-links\";\nimport { ServerStaticProvider } from \"@alepha/server-static\";\nimport { renderToString } from \"react-dom/server\";\nimport {\n\t$page,\n\ttype PageDescriptorRenderOptions,\n} from \"../descriptors/$page.ts\";\nimport {\n\tPageDescriptorProvider,\n\ttype PageReactContext,\n\ttype PageRequest,\n\ttype PageRoute,\n\ttype RouterState,\n} from \"./PageDescriptorProvider.ts\";\nimport type { ReactHydrationState } from \"./ReactBrowserProvider.ts\";\n\nconst envSchema = t.object({\n\tREACT_SERVER_DIST: t.string({ default: \"public\" }),\n\tREACT_SERVER_PREFIX: t.string({ default: \"\" }),\n\tREACT_SSR_ENABLED: t.optional(t.boolean()),\n\tREACT_ROOT_ID: t.string({ default: \"root\" }),\n});\n\ndeclare module \"@alepha/core\" {\n\tinterface Env extends Partial<Static<typeof envSchema>> {}\n\tinterface State {\n\t\t\"ReactServerProvider.template\"?: string;\n\t\t\"ReactServerProvider.ssr\"?: boolean;\n\t}\n}\n\nexport class ReactServerProvider {\n\tprotected readonly log = $logger();\n\tprotected readonly alepha = $inject(Alepha);\n\tprotected readonly pageDescriptorProvider = $inject(PageDescriptorProvider);\n\tprotected readonly serverStaticProvider = $inject(ServerStaticProvider);\n\tprotected readonly serverRouterProvider = $inject(ServerRouterProvider);\n\tprotected readonly serverTimingProvider = $inject(ServerTimingProvider);\n\tprotected readonly env = $inject(envSchema);\n\tprotected readonly ROOT_DIV_REGEX = new RegExp(\n\t\t`<div([^>]*)\\\\s+id=[\"']${this.env.REACT_ROOT_ID}[\"']([^>]*)>(.*?)<\\\\/div>`,\n\t\t\"is\",\n\t);\n\n\tpublic readonly onConfigure = $hook({\n\t\tname: \"configure\",\n\t\thandler: async () => {\n\t\t\tconst pages = this.alepha.getDescriptorValues($page);\n\n\t\t\tconst ssrEnabled =\n\t\t\t\tpages.length > 0 && this.env.REACT_SSR_ENABLED !== false;\n\n\t\t\tthis.alepha.state(\"ReactServerProvider.ssr\", ssrEnabled);\n\n\t\t\tfor (const { key, instance, value } of pages) {\n\t\t\t\tconst name = value[OPTIONS].name ?? key;\n\n\t\t\t\tinstance[key].render = this.createRenderFunction(name);\n\t\t\t}\n\n\t\t\t// development mode\n\t\t\tif (this.alepha.isServerless() === \"vite\") {\n\t\t\t\tawait this.configureVite(ssrEnabled);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// production mode\n\t\t\tlet root = \"\";\n\n\t\t\t// non-serverless mode only -> serve static files\n\t\t\tif (!this.alepha.isServerless()) {\n\t\t\t\troot = this.getPublicDirectory();\n\t\t\t\tif (!root) {\n\t\t\t\t\tthis.log.warn(\n\t\t\t\t\t\t\"Missing static files, static file server will be disabled\",\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tthis.log.debug(`Using static files from: ${root}`);\n\t\t\t\t\tawait this.configureStaticServer(root);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (ssrEnabled) {\n\t\t\t\tawait this.registerPages(async () => this.template);\n\t\t\t\tthis.log.info(\"SSR OK\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// no SSR enabled, serve index.html for all unmatched routes\n\t\t\tthis.log.info(\"SSR is disabled, use History API fallback\");\n\t\t\tawait this.serverRouterProvider.route({\n\t\t\t\tpath: \"*\",\n\t\t\t\thandler: async ({ url, reply }) => {\n\t\t\t\t\tif (url.pathname.includes(\".\")) {\n\t\t\t\t\t\t// If the request is for a file (e.g., /style.css), do not fallback\n\t\t\t\t\t\treply.headers[\"content-type\"] = \"text/plain\";\n\t\t\t\t\t\treply.body = \"Not Found\";\n\t\t\t\t\t\treply.status = 404;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\treply.headers[\"content-type\"] = \"text/html\";\n\n\t\t\t\t\t// serve index.html for all unmatched routes\n\t\t\t\t\treturn this.template;\n\t\t\t\t},\n\t\t\t});\n\t\t},\n\t});\n\n\tpublic get template() {\n\t\treturn (\n\t\t\tthis.alepha.state(\"ReactServerProvider.template\") ??\n\t\t\t\"<!DOCTYPE html><html lang='en'><head></head><body></body></html>\"\n\t\t);\n\t}\n\n\tprotected async registerPages(templateLoader: TemplateLoader) {\n\t\tfor (const page of this.pageDescriptorProvider.getPages()) {\n\t\t\tif (page.children?.length) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tthis.log.debug(`+ ${page.match} -> ${page.name}`);\n\n\t\t\tawait this.serverRouterProvider.route({\n\t\t\t\t...page,\n\t\t\t\tschema: undefined, // schema is handled by the page descriptor provider for now (shared by browser and server)\n\t\t\t\tmethod: \"GET\",\n\t\t\t\tpath: page.match,\n\t\t\t\thandler: this.createHandler(page, templateLoader),\n\t\t\t});\n\t\t}\n\t}\n\n\tprotected getPublicDirectory(): string {\n\t\tconst maybe = [\n\t\t\tjoin(process.cwd(), `dist/${this.env.REACT_SERVER_DIST}`),\n\t\t\tjoin(process.cwd(), this.env.REACT_SERVER_DIST),\n\t\t];\n\n\t\tfor (const it of maybe) {\n\t\t\tif (existsSync(it)) {\n\t\t\t\treturn it;\n\t\t\t}\n\t\t}\n\n\t\treturn \"\";\n\t}\n\n\tprotected async configureStaticServer(root: string) {\n\t\tawait this.serverStaticProvider.serve({\n\t\t\troot,\n\t\t\tpath: this.env.REACT_SERVER_PREFIX,\n\t\t});\n\t}\n\n\tprotected async configureVite(ssrEnabled: boolean) {\n\t\tif (!ssrEnabled) {\n\t\t\t// do nothing, vite will handle everything for us\n\t\t\treturn;\n\t\t}\n\n\t\tthis.log.info(\"SSR (vite) OK\");\n\n\t\tconst url = `http://${process.env.SERVER_HOST}:${process.env.SERVER_PORT}`;\n\n\t\tawait this.registerPages(() =>\n\t\t\tfetch(`${url}/index.html`)\n\t\t\t\t.then((it) => it.text())\n\t\t\t\t.catch(() => undefined),\n\t\t);\n\t}\n\n\t/**\n\t * For testing purposes, creates a render function that can be used.\n\t */\n\tprotected createRenderFunction(name: string, withIndex = false) {\n\t\treturn async (options: PageDescriptorRenderOptions = {}) => {\n\t\t\tconst page = this.pageDescriptorProvider.page(name);\n\t\t\tconst url = new URL(this.pageDescriptorProvider.url(name, options));\n\t\t\tconst context: PageRequest = {\n\t\t\t\turl,\n\t\t\t\tparams: options.params ?? {},\n\t\t\t\tquery: options.query ?? {},\n\t\t\t\thead: {},\n\t\t\t\tonError: () => null,\n\t\t\t};\n\n\t\t\tawait this.alepha.emit(\"react:server:render:begin\", {\n\t\t\t\tcontext,\n\t\t\t});\n\n\t\t\tconst state = await this.pageDescriptorProvider.createLayers(\n\t\t\t\tpage,\n\t\t\t\tcontext,\n\t\t\t);\n\n\t\t\tif (!withIndex && !options.html) {\n\t\t\t\treturn {\n\t\t\t\t\tcontext,\n\t\t\t\t\thtml: renderToString(\n\t\t\t\t\t\tthis.pageDescriptorProvider.root(state, context),\n\t\t\t\t\t),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst html = this.renderToHtml(\n\t\t\t\tthis.template ?? \"\",\n\t\t\t\tstate,\n\t\t\t\tcontext,\n\t\t\t\toptions.hydration,\n\t\t\t);\n\n\t\t\tconst result = {\n\t\t\t\tcontext,\n\t\t\t\tstate,\n\t\t\t\thtml,\n\t\t\t};\n\n\t\t\tawait this.alepha.emit(\"react:server:render:end\", result);\n\n\t\t\treturn result;\n\t\t};\n\t}\n\n\tprotected createHandler(\n\t\tpage: PageRoute,\n\t\ttemplateLoader: TemplateLoader,\n\t): ServerHandler {\n\t\treturn async (serverRequest) => {\n\t\t\tconst { url, reply, query, params } = serverRequest;\n\t\t\tconst template = await templateLoader();\n\t\t\tif (!template) {\n\t\t\t\tthrow new Error(\"Template not found\");\n\t\t\t}\n\n\t\t\tconst context: PageRequest = {\n\t\t\t\turl,\n\t\t\t\tparams,\n\t\t\t\tquery,\n\t\t\t\t// plugins\n\t\t\t\thead: {},\n\t\t\t\tonError: () => null,\n\t\t\t};\n\n\t\t\tif (this.alepha.has(ServerLinksProvider)) {\n\t\t\t\tconst srv = this.alepha.get(ServerLinksProvider);\n\t\t\t\tconst schema = apiLinksResponseSchema as any;\n\n\t\t\t\tcontext.links = this.alepha.parse(\n\t\t\t\t\tschema,\n\t\t\t\t\tawait srv.getLinks({\n\t\t\t\t\t\tuser: serverRequest.user,\n\t\t\t\t\t\tauthorization: serverRequest.headers.authorization,\n\t\t\t\t\t}),\n\t\t\t\t) as any;\n\n\t\t\t\tthis.alepha.context.set(\"links\", context.links);\n\t\t\t}\n\n\t\t\tlet target: PageRoute | undefined = page; // TODO: move to PageDescriptorProvider\n\t\t\twhile (target) {\n\t\t\t\tif (page.can && !page.can()) {\n\t\t\t\t\t// if the page is not accessible, return 403\n\t\t\t\t\treply.status = 403;\n\t\t\t\t\treply.headers[\"content-type\"] = \"text/plain\";\n\t\t\t\t\treturn \"Forbidden\";\n\t\t\t\t}\n\t\t\t\ttarget = target.parent;\n\t\t\t}\n\n\t\t\t// TODO: SSR strategies\n\t\t\t// - only when googlebot\n\t\t\t// - only child pages\n\t\t\t// if (page.client) {\n\t\t\t// \t// if the page is a client-only page, return 404\n\t\t\t// \treply.status = 200;\n\t\t\t// \treply.headers[\"content-type\"] = \"text/html\";\n\t\t\t// \treply.body = template;\n\t\t\t// \treturn;\n\t\t\t// }\n\n\t\t\tawait this.alepha.emit(\"react:server:render:begin\", {\n\t\t\t\trequest: serverRequest,\n\t\t\t\tcontext,\n\t\t\t});\n\n\t\t\tthis.serverTimingProvider.beginTiming(\"createLayers\");\n\n\t\t\tconst state = await this.pageDescriptorProvider.createLayers(\n\t\t\t\tpage,\n\t\t\t\tcontext,\n\t\t\t);\n\n\t\t\tthis.serverTimingProvider.endTiming(\"createLayers\");\n\n\t\t\tif (state.redirect) {\n\t\t\t\treturn reply.redirect(state.redirect);\n\t\t\t}\n\n\t\t\treply.headers[\"content-type\"] = \"text/html\";\n\n\t\t\t// by default, disable caching for SSR responses\n\t\t\t// some plugins may override this\n\t\t\treply.headers[\"cache-control\"] =\n\t\t\t\t\"no-store, no-cache, must-revalidate, proxy-revalidate\";\n\t\t\treply.headers.pragma = \"no-cache\";\n\t\t\treply.headers.expires = \"0\";\n\n\t\t\t// don't cache user links\n\t\t\tif (page.cache && serverRequest.user) {\n\t\t\t\tdelete context.links;\n\t\t\t}\n\n\t\t\tconst html = this.renderToHtml(template, state, context);\n\n\t\t\tawait this.alepha.emit(\"react:server:render:end\", {\n\t\t\t\trequest: serverRequest,\n\t\t\t\tcontext,\n\t\t\t\tstate,\n\t\t\t\thtml,\n\t\t\t});\n\n\t\t\tpage.afterHandler?.(serverRequest);\n\n\t\t\treturn html;\n\t\t};\n\t}\n\n\tpublic renderToHtml(\n\t\ttemplate: string,\n\t\tstate: RouterState,\n\t\tcontext: PageReactContext,\n\t\thydration = true,\n\t) {\n\t\tconst element = this.pageDescriptorProvider.root(state, context);\n\n\t\tthis.serverTimingProvider.beginTiming(\"renderToString\");\n\n\t\tlet app = \"\";\n\t\ttry {\n\t\t\tapp = renderToString(element);\n\t\t} catch (error) {\n\t\t\tthis.log.error(\"Error during SSR\", error);\n\t\t\tapp = renderToString(context.onError(error as Error));\n\t\t}\n\n\t\tthis.serverTimingProvider.endTiming(\"renderToString\");\n\n\t\tconst response = {\n\t\t\thtml: template,\n\t\t};\n\n\t\tif (hydration) {\n\t\t\tconst hydrationData: ReactHydrationState = {\n\t\t\t\tlinks: context.links,\n\t\t\t\tlayers: state.layers.map((it) => ({\n\t\t\t\t\t...it,\n\t\t\t\t\terror: it.error\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t...it.error,\n\t\t\t\t\t\t\t\tname: it.error.name,\n\t\t\t\t\t\t\t\tmessage: it.error.message,\n\t\t\t\t\t\t\t\tstack: it.error.stack, // TODO: Hide stack in production ?\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\tindex: undefined,\n\t\t\t\t\tpath: undefined,\n\t\t\t\t\telement: undefined,\n\t\t\t\t\troute: undefined,\n\t\t\t\t})),\n\t\t\t};\n\n\t\t\t// create hydration data\n\t\t\tconst script = `<script>window.__ssr=${JSON.stringify(hydrationData)}</script>`;\n\n\t\t\t// inject app into template\n\t\t\tthis.fillTemplate(response, app, script);\n\t\t}\n\n\t\treturn response.html;\n\t}\n\n\tprotected fillTemplate(\n\t\tresponse: { html: string },\n\t\tapp: string,\n\t\tscript: string,\n\t) {\n\t\tif (this.ROOT_DIV_REGEX.test(response.html)) {\n\t\t\t// replace contents of the existing <div id=\"root\">\n\t\t\tresponse.html = response.html.replace(\n\t\t\t\tthis.ROOT_DIV_REGEX,\n\t\t\t\t(_match, beforeId, afterId) => {\n\t\t\t\t\treturn `<div${beforeId} id=\"${this.env.REACT_ROOT_ID}\"${afterId}>${app}</div>`;\n\t\t\t\t},\n\t\t\t);\n\t\t} else {\n\t\t\tconst bodyOpenTag = /<body([^>]*)>/i;\n\t\t\tif (bodyOpenTag.test(response.html)) {\n\t\t\t\tresponse.html = response.html.replace(bodyOpenTag, (match) => {\n\t\t\t\t\treturn `${match}\\n<div id=\"${this.env.REACT_ROOT_ID}\">${app}</div>`;\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tconst bodyCloseTagRegex = /<\\/body>/i;\n\t\tif (bodyCloseTagRegex.test(response.html)) {\n\t\t\tresponse.html = response.html.replace(\n\t\t\t\tbodyCloseTagRegex,\n\t\t\t\t`${script}\\n</body>`,\n\t\t\t);\n\t\t}\n\t}\n}\n\ntype TemplateLoader = () => Promise<string | undefined>;\n","import { $hook, $inject, $logger, Alepha } from \"@alepha/core\";\nimport { type Route, RouterProvider } from \"@alepha/router\";\nimport { createElement, type ReactNode } from \"react\";\nimport NotFoundPage from \"../components/NotFound.tsx\";\nimport {\n\tisPageRoute,\n\tPageDescriptorProvider,\n\ttype PageReactContext,\n\ttype PageRequest,\n\ttype PageRoute,\n\ttype PageRouteEntry,\n\ttype RouterRenderResult,\n\ttype RouterState,\n\ttype TransitionOptions,\n} from \"./PageDescriptorProvider.ts\";\n\nexport interface BrowserRoute extends Route {\n\tpage: PageRoute;\n}\n\nexport class BrowserRouterProvider extends RouterProvider<BrowserRoute> {\n\tprotected readonly log = $logger();\n\tprotected readonly alepha = $inject(Alepha);\n\tprotected readonly pageDescriptorProvider = $inject(PageDescriptorProvider);\n\n\tpublic add(entry: PageRouteEntry) {\n\t\tthis.pageDescriptorProvider.add(entry);\n\t}\n\n\tprotected readonly configure = $hook({\n\t\tname: \"configure\",\n\t\thandler: async () => {\n\t\t\tfor (const page of this.pageDescriptorProvider.getPages()) {\n\t\t\t\t// mount only if a view is provided\n\t\t\t\tif (page.component || page.lazy) {\n\t\t\t\t\tthis.push({\n\t\t\t\t\t\tpath: page.match,\n\t\t\t\t\t\tpage,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t});\n\n\tpublic async transition(\n\t\turl: URL,\n\t\toptions: TransitionOptions = {},\n\t): Promise<RouterRenderResult> {\n\t\tconst { pathname, search } = url;\n\t\tconst state: RouterState = {\n\t\t\tpathname,\n\t\t\tsearch,\n\t\t\tlayers: [],\n\t\t};\n\n\t\tconst context = {\n\t\t\turl,\n\t\t\tquery: {},\n\t\t\tparams: {},\n\t\t\tonError: () => null,\n\t\t\t...(options.context ?? {}),\n\t\t} as PageRequest;\n\n\t\tawait this.alepha.emit(\"react:transition:begin\", { state, context });\n\n\t\ttry {\n\t\t\tconst previous = options.previous;\n\t\t\tconst { route, params } = this.match(pathname);\n\n\t\t\tconst query: Record<string, string> = {};\n\t\t\tif (search) {\n\t\t\t\tfor (const [key, value] of new URLSearchParams(search).entries()) {\n\t\t\t\t\tquery[key] = String(value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcontext.query = query;\n\t\t\tcontext.params = params ?? {};\n\t\t\tcontext.previous = previous;\n\n\t\t\tif (isPageRoute(route)) {\n\t\t\t\tconst result = await this.pageDescriptorProvider.createLayers(\n\t\t\t\t\troute.page,\n\t\t\t\t\tcontext,\n\t\t\t\t);\n\n\t\t\t\tif (result.redirect) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tredirect: result.redirect,\n\t\t\t\t\t\tstate,\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tstate.layers = result.layers;\n\t\t\t}\n\n\t\t\tif (state.layers.length === 0) {\n\t\t\t\tstate.layers.push({\n\t\t\t\t\tname: \"not-found\",\n\t\t\t\t\telement: createElement(NotFoundPage),\n\t\t\t\t\tindex: 0,\n\t\t\t\t\tpath: \"/\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tawait this.alepha.emit(\"react:transition:success\", { state, context });\n\t\t} catch (e) {\n\t\t\tthis.log.error(e);\n\t\t\tstate.layers = [\n\t\t\t\t{\n\t\t\t\t\tname: \"error\",\n\t\t\t\t\telement: this.pageDescriptorProvider.renderError(e as Error),\n\t\t\t\t\tindex: 0,\n\t\t\t\t\tpath: \"/\",\n\t\t\t\t},\n\t\t\t];\n\n\t\t\tawait this.alepha.emit(\"react:transition:error\", {\n\t\t\t\terror: e as Error,\n\t\t\t\tstate,\n\t\t\t\tcontext,\n\t\t\t});\n\t\t}\n\n\t\tif (options.state) {\n\t\t\toptions.state.layers = state.layers;\n\t\t\toptions.state.pathname = state.pathname;\n\t\t\toptions.state.search = state.search;\n\t\t}\n\n\t\tawait this.alepha.emit(\"react:transition:end\", {\n\t\t\tstate: options.state,\n\t\t\tcontext,\n\t\t});\n\n\t\treturn {\n\t\t\tcontext,\n\t\t\tstate,\n\t\t};\n\t}\n\n\tpublic root(state: RouterState, context: PageReactContext): ReactNode {\n\t\treturn this.pageDescriptorProvider.root(state, context);\n\t}\n}\n","import { $hook, $inject, $logger, Alepha } from \"@alepha/core\";\nimport type { ApiLinksResponse } from \"@alepha/server\";\nimport { LinkProvider } from \"@alepha/server-links\";\nimport type { Root } from \"react-dom/client\";\nimport { BrowserRouterProvider } from \"./BrowserRouterProvider.ts\";\nimport type {\n\tPreviousLayerData,\n\tRouterRenderResult,\n\tRouterState,\n\tTransitionOptions,\n} from \"./PageDescriptorProvider.ts\";\n\nexport class ReactBrowserProvider {\n\tprotected readonly log = $logger();\n\tprotected readonly client = $inject(LinkProvider);\n\tprotected readonly alepha = $inject(Alepha);\n\tprotected readonly router = $inject(BrowserRouterProvider);\n\tprotected root!: Root;\n\n\tpublic transitioning?: {\n\t\tto: string;\n\t};\n\n\tpublic state: RouterState = {\n\t\tlayers: [],\n\t\tpathname: \"\",\n\t\tsearch: \"\",\n\t};\n\n\tpublic get document() {\n\t\treturn window.document;\n\t}\n\n\tpublic get history() {\n\t\treturn window.history;\n\t}\n\n\tpublic get url(): string {\n\t\treturn window.location.pathname + window.location.search;\n\t}\n\n\tpublic async invalidate(props?: Record<string, any>) {\n\t\tconst previous: PreviousLayerData[] = [];\n\n\t\tif (props) {\n\t\t\tconst [key] = Object.keys(props);\n\t\t\tconst value = props[key];\n\n\t\t\tfor (const layer of this.state.layers) {\n\t\t\t\tif (layer.props?.[key]) {\n\t\t\t\t\tprevious.push({\n\t\t\t\t\t\t...layer,\n\t\t\t\t\t\tprops: {\n\t\t\t\t\t\t\t...layer.props,\n\t\t\t\t\t\t\t[key]: value,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tprevious.push(layer);\n\t\t\t}\n\t\t}\n\n\t\tawait this.render({ previous });\n\t}\n\n\tpublic async go(url: string, options: RouterGoOptions = {}): Promise<void> {\n\t\tconst result = await this.render({\n\t\t\turl,\n\t\t});\n\n\t\t// when redirecting in browser\n\t\tif (result.context.url.pathname !== url) {\n\t\t\t// TODO: check if losing search params is acceptable?\n\t\t\tthis.history.replaceState({}, \"\", result.context.url.pathname);\n\t\t\treturn;\n\t\t}\n\n\t\tif (options.replace) {\n\t\t\tthis.history.replaceState({}, \"\", url);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.history.pushState({}, \"\", url);\n\t}\n\n\tprotected async render(\n\t\toptions: { url?: string; previous?: PreviousLayerData[] } = {},\n\t): Promise<RouterRenderResult> {\n\t\tconst previous = options.previous ?? this.state.layers;\n\t\tconst url = options.url ?? this.url;\n\n\t\tthis.transitioning = { to: url };\n\n\t\tconst result = await this.router.transition(\n\t\t\tnew URL(`http://localhost${url}`),\n\t\t\t{\n\t\t\t\tprevious,\n\t\t\t\tstate: this.state,\n\t\t\t},\n\t\t);\n\n\t\tif (result.redirect) {\n\t\t\treturn await this.render({ url: result.redirect });\n\t\t}\n\n\t\tthis.transitioning = undefined;\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get embedded layers from the server.\n\t */\n\tprotected getHydrationState(): ReactHydrationState | undefined {\n\t\ttry {\n\t\t\tif (\"__ssr\" in window && typeof window.__ssr === \"object\") {\n\t\t\t\treturn window.__ssr as ReactHydrationState;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(error);\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------------------------------------------------\n\n\tpublic readonly ready = $hook({\n\t\tname: \"ready\",\n\t\thandler: async () => {\n\t\t\tconst hydration = this.getHydrationState();\n\t\t\tconst previous = hydration?.layers ?? [];\n\n\t\t\tif (hydration?.links) {\n\t\t\t\tfor (const link of hydration.links.links) {\n\t\t\t\t\tthis.client.pushLink(link);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst { context } = await this.render({ previous });\n\n\t\t\tawait this.alepha.emit(\"react:browser:render\", {\n\t\t\t\tstate: this.state,\n\t\t\t\tcontext,\n\t\t\t\thydration,\n\t\t\t});\n\n\t\t\twindow.addEventListener(\"popstate\", () => {\n\t\t\t\tthis.render();\n\t\t\t});\n\t\t},\n\t});\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RouterGoOptions {\n\treplace?: boolean;\n\tmatch?: TransitionOptions;\n\tparams?: Record<string, string>;\n}\n\nexport interface ReactHydrationState {\n\tlayers?: Array<PreviousLayerData>;\n\tlinks?: ApiLinksResponse;\n}\n","import type { PageDescriptor } from \"../descriptors/$page.ts\";\nimport type {\n\tAnchorProps,\n\tPageRoute,\n\tRouterState,\n} from \"../providers/PageDescriptorProvider.ts\";\nimport type {\n\tReactBrowserProvider,\n\tRouterGoOptions,\n} from \"../providers/ReactBrowserProvider.ts\";\n\nexport class RouterHookApi {\n\tconstructor(\n\t\tprivate readonly pages: PageRoute[],\n\t\tprivate readonly state: RouterState,\n\t\tprivate readonly layer: {\n\t\t\tpath: string;\n\t\t},\n\t\tprivate readonly browser?: ReactBrowserProvider,\n\t) {}\n\n\tpublic get current(): RouterState {\n\t\treturn this.state;\n\t}\n\n\tpublic get pathname(): string {\n\t\treturn this.state.pathname;\n\t}\n\n\tpublic get query(): Record<string, string> {\n\t\tconst query: Record<string, string> = {};\n\n\t\tfor (const [key, value] of new URLSearchParams(\n\t\t\tthis.state.search,\n\t\t).entries()) {\n\t\t\tquery[key] = String(value);\n\t\t}\n\n\t\treturn query;\n\t}\n\n\tpublic async back() {\n\t\tthis.browser?.history.back();\n\t}\n\n\tpublic async forward() {\n\t\tthis.browser?.history.forward();\n\t}\n\n\tpublic async invalidate(props?: Record<string, any>) {\n\t\tawait this.browser?.invalidate(props);\n\t}\n\n\t/**\n\t * Create a valid href for the given pathname.\n\t *\n\t * @param pathname\n\t * @param layer\n\t */\n\tpublic createHref(\n\t\tpathname: HrefLike,\n\t\tlayer: { path: string } = this.layer,\n\t\toptions: { params?: Record<string, any> } = {},\n\t) {\n\t\tif (typeof pathname === \"object\") {\n\t\t\tpathname = pathname.options.path ?? \"\";\n\t\t}\n\n\t\tif (options.params) {\n\t\t\tfor (const [key, value] of Object.entries(options.params)) {\n\t\t\t\tpathname = pathname.replace(`:${key}`, String(value));\n\t\t\t}\n\t\t}\n\n\t\treturn pathname.startsWith(\"/\")\n\t\t\t? pathname\n\t\t\t: `${layer.path}/${pathname}`.replace(/\\/\\/+/g, \"/\");\n\t}\n\n\tpublic async go(path: string, options?: RouterGoOptions): Promise<void>;\n\tpublic async go<T extends object>(\n\t\tpath: keyof VirtualRouter<T>,\n\t\toptions?: RouterGoOptions,\n\t): Promise<void>;\n\tpublic async go(path: string, options?: RouterGoOptions): Promise<void> {\n\t\tfor (const page of this.pages) {\n\t\t\tif (page.name === path) {\n\t\t\t\tpath = page.path ?? \"\";\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tawait this.browser?.go(this.createHref(path, this.layer, options), options);\n\t}\n\n\tpublic anchor(\n\t\tpath: string,\n\t\toptions?: { params?: Record<string, any> },\n\t): AnchorProps;\n\tpublic anchor<T extends object>(\n\t\tpath: keyof VirtualRouter<T>,\n\t\toptions?: { params?: Record<string, any> },\n\t): AnchorProps;\n\tpublic anchor(\n\t\tpath: string,\n\t\toptions: { params?: Record<string, any> } = {},\n\t): AnchorProps {\n\t\tfor (const page of this.pages) {\n\t\t\tif (page.name === path) {\n\t\t\t\tpath = page.path ?? \"\";\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tconst href = this.createHref(path, this.layer, options);\n\t\treturn {\n\t\t\thref,\n\t\t\tonClick: (ev: any) => {\n\t\t\t\tev.stopPropagation();\n\t\t\t\tev.preventDefault();\n\n\t\t\t\tthis.go(path, options).catch(console.error);\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Set query params.\n\t *\n\t * @param record\n\t * @param options\n\t */\n\tpublic setQueryParams(\n\t\trecord:\n\t\t\t| Record<string, any>\n\t\t\t| ((queryParams: Record<string, any>) => Record<string, any>),\n\t\toptions: {\n\t\t\t/**\n\t\t\t * If true, this will add a new entry to the history stack.\n\t\t\t */\n\t\t\tpush?: boolean;\n\t\t} = {},\n\t) {\n\t\tconst func = typeof record === \"function\" ? record : () => record;\n\t\tconst search = new URLSearchParams(func(this.query)).toString();\n\t\tconst state = search ? `${this.pathname}?${search}` : this.pathname;\n\n\t\tif (options.push) {\n\t\t\twindow.history.pushState({}, \"\", state);\n\t\t} else {\n\t\t\twindow.history.replaceState({}, \"\", state);\n\t\t}\n\t}\n}\n\nexport type HrefLike = string | { options: { path?: string; name?: string } };\n\nexport type VirtualRouter<T> = {\n\t[K in keyof T as T[K] extends PageDescriptor ? K : never]: T[K];\n};\n","import { useContext, useMemo } from \"react\";\nimport { RouterContext } from \"../contexts/RouterContext.ts\";\nimport { RouterLayerContext } from \"../contexts/RouterLayerContext.ts\";\nimport { PageDescriptorProvider } from \"../providers/PageDescriptorProvider.ts\";\nimport { ReactBrowserProvider } from \"../providers/ReactBrowserProvider.ts\";\nimport { RouterHookApi } from \"./RouterHookApi.ts\";\n\nexport const useRouter = (): RouterHookApi => {\n\tconst ctx = useContext(RouterContext);\n\tconst layer = useContext(RouterLayerContext);\n\tif (!ctx || !layer) {\n\t\tthrow new Error(\"useRouter must be used within a RouterProvider\");\n\t}\n\n\tconst pages = useMemo(() => {\n\t\treturn ctx.alepha.get(PageDescriptorProvider).getPages();\n\t}, []);\n\n\treturn useMemo(\n\t\t() =>\n\t\t\tnew RouterHookApi(\n\t\t\t\tpages,\n\t\t\t\tctx.state,\n\t\t\t\tlayer,\n\t\t\t\tctx.alepha.isBrowser()\n\t\t\t\t\t? ctx.alepha.get(ReactBrowserProvider)\n\t\t\t\t\t: undefined,\n\t\t\t),\n\t\t[layer],\n\t);\n};\n","import { OPTIONS } from \"@alepha/core\";\nimport type { AnchorHTMLAttributes } from \"react\";\nimport React from \"react\";\nimport { RouterContext } from \"../contexts/RouterContext.ts\";\nimport type { PageDescriptor } from \"../descriptors/$page.ts\";\nimport { useRouter } from \"../hooks/useRouter.ts\";\n\nexport interface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {\n\tto: string | PageDescriptor;\n\tchildren?: React.ReactNode;\n}\n\nconst Link = (props: LinkProps) => {\n\tReact.useContext(RouterContext);\n\n\tconst router = useRouter();\n\n\tconst to = typeof props.to === \"string\" ? props.to : props.to[OPTIONS].path;\n\tif (!to) {\n\t\treturn null;\n\t}\n\n\tconst can = typeof props.to === \"string\" ? undefined : props.to[OPTIONS].can;\n\tif (can && !can()) {\n\t\treturn null;\n\t}\n\n\tconst name =\n\t\ttypeof props.to === \"string\" ? undefined : props.to[OPTIONS].name;\n\n\tconst anchorProps = {\n\t\t...props,\n\t\tto: undefined,\n\t};\n\n\treturn (\n\t\t<a {...router.anchor(to)} {...anchorProps}>\n\t\t\t{props.children ?? name}\n\t\t</a>\n\t);\n};\n\nexport default Link;\n","import { useContext, useMemo, useState } from \"react\";\nimport { RouterContext } from \"../contexts/RouterContext.ts\";\nimport { RouterLayerContext } from \"../contexts/RouterLayerContext.ts\";\nimport type { AnchorProps } from \"../providers/PageDescriptorProvider.ts\";\nimport type { HrefLike } from \"./RouterHookApi.ts\";\nimport { useRouter } from \"./useRouter.ts\";\nimport { useRouterEvents } from \"./useRouterEvents.ts\";\n\nexport const useActive = (path: HrefLike): UseActiveHook => {\n\tconst router = useRouter();\n\tconst ctx = useContext(RouterContext);\n\tconst layer = useContext(RouterLayerContext);\n\tif (!ctx || !layer) {\n\t\tthrow new Error(\"useRouter must be used within a RouterProvider\");\n\t}\n\n\tlet name: string | undefined;\n\tif (typeof path === \"object\" && path.options.name) {\n\t\tname = path.options.name;\n\t}\n\n\tconst [current, setCurrent] = useState(ctx.state.pathname);\n\tconst href = useMemo(() => router.createHref(path, layer), [path, layer]);\n\tconst [isPending, setPending] = useState(false);\n\tconst isActive = current === href;\n\n\tuseRouterEvents({\n\t\tonEnd: ({ state }) => setCurrent(state.pathname),\n\t});\n\n\treturn {\n\t\tname,\n\t\tisPending,\n\t\tisActive,\n\t\tanchorProps: {\n\t\t\thref,\n\t\t\tonClick: (ev: any) => {\n\t\t\t\tev.stopPropagation();\n\t\t\t\tev.preventDefault();\n\t\t\t\tif (isActive) return;\n\t\t\t\tif (isPending) return;\n\n\t\t\t\tsetPending(true);\n\t\t\t\trouter.go(href).then(() => {\n\t\t\t\t\tsetPending(false);\n\t\t\t\t});\n\t\t\t},\n\t\t},\n\t};\n};\n\nexport interface UseActiveHook {\n\tisActive: boolean;\n\tanchorProps: AnchorProps;\n\tisPending: boolean;\n\tname?: string;\n}\n","import type { Service } from \"@alepha/core\";\nimport { useContext, useMemo } from \"react\";\nimport { RouterContext } from \"../contexts/RouterContext.ts\";\n\nexport const useInject = <T extends object>(clazz: Service<T>): T => {\n\tconst ctx = useContext(RouterContext);\n\tif (!ctx) {\n\t\tthrow new Error(\"useRouter must be used within a <RouterProvider>\");\n\t}\n\n\treturn useMemo(() => ctx.alepha.get(clazz), []);\n};\n","import {\n\ttype ClientScope,\n\ttype HttpVirtualClient,\n\tLinkProvider,\n} from \"@alepha/server-links\";\nimport { useInject } from \"./useInject.ts\";\n\nexport const useClient = <T extends object>(\n\t_scope?: ClientScope,\n): HttpVirtualClient<T> => {\n\treturn useInject(LinkProvider).client<T>();\n};\n","import type { Alepha, Static, TObject } from \"@alepha/core\";\nimport { useContext, useEffect, useState } from \"react\";\nimport { RouterContext } from \"../contexts/RouterContext.ts\";\nimport { useRouter } from \"./useRouter.ts\";\n\nexport interface UseQueryParamsHookOptions {\n\tformat?: \"base64\" | \"querystring\";\n\tkey?: string;\n\tpush?: boolean;\n}\n\nexport const useQueryParams = <T extends TObject>(\n\tschema: T,\n\toptions: UseQueryParamsHookOptions = {},\n): [Static<T>, (data: Static<T>) => void] => {\n\tconst ctx = useContext(RouterContext);\n\tif (!ctx) {\n\t\tthrow new Error(\"useQueryParams must be used within a RouterProvider\");\n\t}\n\n\tconst key = options.key ?? \"q\";\n\tconst router = useRouter();\n\tconst querystring = router.query[key];\n\n\tconst [queryParams, setQueryParams] = useState(\n\t\tdecode(ctx.alepha, schema, router.query[key]),\n\t);\n\n\tuseEffect(() => {\n\t\tsetQueryParams(decode(ctx.alepha, schema, querystring));\n\t}, [querystring]);\n\n\treturn [\n\t\tqueryParams,\n\t\t(queryParams: Static<T>) => {\n\t\t\tsetQueryParams(queryParams);\n\t\t\trouter.setQueryParams((data) => {\n\t\t\t\treturn { ...data, [key]: encode(ctx.alepha, schema, queryParams) };\n\t\t\t});\n\t\t},\n\t];\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nconst encode = (alepha: Alepha, schema: TObject, data: any) => {\n\treturn btoa(JSON.stringify(alepha.parse(schema, data)));\n};\n\nconst decode = (alepha: Alepha, schema: TObject, data: any) => {\n\ttry {\n\t\treturn alepha.parse(schema, JSON.parse(atob(decodeURIComponent(data))));\n\t} catch (_error) {\n\t\treturn {};\n\t}\n};\n","import { useContext, useState } from \"react\";\nimport { RouterContext } from \"../contexts/RouterContext.ts\";\nimport { RouterLayerContext } from \"../contexts/RouterLayerContext.ts\";\nimport type { RouterState } from \"../providers/PageDescriptorProvider.ts\";\nimport { useRouterEvents } from \"./useRouterEvents.ts\";\n\nexport const useRouterState = (): RouterState => {\n\tconst ctx = useContext(RouterContext);\n\tconst layer = useContext(RouterLayerContext);\n\tif (!ctx || !layer) {\n\t\tthrow new Error(\"useRouter must be used within a RouterProvider\");\n\t}\n\n\tconst [state, setState] = useState(ctx.state);\n\n\tuseRouterEvents({\n\t\tonEnd: ({ state }) => setState({ ...state }),\n\t});\n\n\treturn state;\n};\n","import { __bind, type Alepha, type Module } from \"@alepha/core\";\nimport { AlephaServer, type ServerRequest } from \"@alepha/server\";\nimport { AlephaServerCache } from \"@alepha/server-cache\";\nimport { $page } from \"./descriptors/$page.ts\";\nimport {\n\tPageDescriptorProvider,\n\ttype PageReactContext,\n\ttype PageRequest,\n\ttype RouterState,\n} from \"./providers/PageDescriptorProvider.ts\";\nimport type { ReactHydrationState } from \"./providers/ReactBrowserProvider.ts\";\nimport { ReactServerProvider } from \"./providers/ReactServerProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./index.shared.ts\";\nexport * from \"./providers/PageDescriptorProvider.ts\";\nexport * from \"./providers/ReactBrowserProvider.ts\";\nexport * from \"./providers/ReactServerProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"@alepha/core\" {\n\tinterface Hooks {\n\t\t\"react:router:createLayers\": {\n\t\t\trequest: ServerRequest;\n\t\t\tcontext: PageRequest;\n\t\t\tlayers: PageRequest[];\n\t\t};\n\t\t\"react:server:render:begin\": {\n\t\t\trequest?: ServerRequest;\n\t\t\tcontext: PageRequest;\n\t\t};\n\t\t\"react:server:render:end\": {\n\t\t\trequest?: ServerRequest;\n\t\t\tcontext: PageRequest;\n\t\t\tstate: RouterState;\n\t\t\thtml: string;\n\t\t};\n\t\t\"react:browser:render\": {\n\t\t\tstate: RouterState;\n\t\t\tcontext: PageReactContext;\n\t\t\thydration?: ReactHydrationState;\n\t\t};\n\t\t\"react:transition:begin\": {\n\t\t\tstate: RouterState;\n\t\t\tcontext: PageReactContext;\n\t\t};\n\t\t\"react:transition:success\": {\n\t\t\tstate: RouterState;\n\t\t\tcontext: PageReactContext;\n\t\t};\n\t\t\"react:transition:error\": {\n\t\t\terror: Error;\n\t\t\tstate: RouterState;\n\t\t\tcontext: PageReactContext;\n\t\t};\n\t\t\"react:transition:end\": {\n\t\t\tstate: RouterState;\n\t\t\tcontext: PageReactContext;\n\t\t};\n\t}\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Alepha React Module\n *\n * Alepha React Module contains a router for client-side navigation and server-side rendering.\n * Routes can be defined using the `$page` descriptor.\n *\n * @see {@link $page}\n * @module alepha.react\n */\nexport class AlephaReact implements Module {\n\tpublic readonly name = \"alepha.react\";\n\tpublic readonly $services = (alepha: Alepha) =>\n\t\talepha\n\t\t\t.with(AlephaServer)\n\t\t\t.with(AlephaServerCache)\n\t\t\t.with(ReactServerProvider)\n\t\t\t.with(PageDescriptorProvider);\n}\n\n__bind($page, AlephaReact);\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alepha/react",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.7",
|
|
4
4
|
"type": "module",
|
|
5
|
+
"engines": {
|
|
6
|
+
"node": ">=22.0.0"
|
|
7
|
+
},
|
|
5
8
|
"license": "MIT",
|
|
6
9
|
"main": "./dist/index.js",
|
|
7
10
|
"types": "./dist/index.d.ts",
|
|
@@ -13,18 +16,19 @@
|
|
|
13
16
|
"src"
|
|
14
17
|
],
|
|
15
18
|
"dependencies": {
|
|
16
|
-
"@alepha/core": "0.7.
|
|
17
|
-
"@alepha/router": "0.7.
|
|
18
|
-
"@alepha/server": "0.7.
|
|
19
|
-
"@alepha/server-cache": "0.7.
|
|
20
|
-
"@alepha/server-
|
|
19
|
+
"@alepha/core": "0.7.7",
|
|
20
|
+
"@alepha/router": "0.7.7",
|
|
21
|
+
"@alepha/server": "0.7.7",
|
|
22
|
+
"@alepha/server-cache": "0.7.7",
|
|
23
|
+
"@alepha/server-links": "0.7.7",
|
|
24
|
+
"@alepha/server-static": "0.7.7",
|
|
21
25
|
"react-dom": "^19.1.0"
|
|
22
26
|
},
|
|
23
27
|
"devDependencies": {
|
|
24
28
|
"@types/react": "^19.1.8",
|
|
25
29
|
"@types/react-dom": "^19.1.6",
|
|
26
|
-
"pkgroll": "^2.13.1",
|
|
27
30
|
"react": "^19.1.0",
|
|
31
|
+
"tsdown": "^0.12.9",
|
|
28
32
|
"vitest": "^3.2.4"
|
|
29
33
|
},
|
|
30
34
|
"peerDependencies": {
|
|
@@ -33,7 +37,7 @@
|
|
|
33
37
|
},
|
|
34
38
|
"scripts": {
|
|
35
39
|
"test": "vitest run",
|
|
36
|
-
"build": "
|
|
40
|
+
"build": "tsdown -c ../../tsdown.config.ts"
|
|
37
41
|
},
|
|
38
42
|
"homepage": "https://github.com/feunard/alepha",
|
|
39
43
|
"repository": {
|
package/src/descriptors/$page.ts
CHANGED
|
@@ -102,8 +102,6 @@ export interface PageDescriptorOptions<
|
|
|
102
102
|
|
|
103
103
|
can?: () => boolean;
|
|
104
104
|
|
|
105
|
-
head?: Head | ((props: TProps, previous?: Head) => Head);
|
|
106
|
-
|
|
107
105
|
errorHandler?: (error: Error) => ReactNode;
|
|
108
106
|
|
|
109
107
|
prerender?:
|
|
@@ -182,7 +180,8 @@ $page[KIND] = KEY;
|
|
|
182
180
|
export interface PageDescriptorRenderOptions {
|
|
183
181
|
params?: Record<string, string>;
|
|
184
182
|
query?: Record<string, string>;
|
|
185
|
-
|
|
183
|
+
html?: boolean;
|
|
184
|
+
hydration?: boolean;
|
|
186
185
|
}
|
|
187
186
|
|
|
188
187
|
export interface PageDescriptorRenderResult {
|
|
@@ -190,50 +189,6 @@ export interface PageDescriptorRenderResult {
|
|
|
190
189
|
context: PageReactContext;
|
|
191
190
|
}
|
|
192
191
|
|
|
193
|
-
export interface Head {
|
|
194
|
-
title?: string;
|
|
195
|
-
description?: string;
|
|
196
|
-
titleSeparator?: string;
|
|
197
|
-
htmlAttributes?: Record<string, string>;
|
|
198
|
-
bodyAttributes?: Record<string, string>;
|
|
199
|
-
meta?: Array<{ name: string; content: string }>;
|
|
200
|
-
|
|
201
|
-
// TODO
|
|
202
|
-
keywords?: string[];
|
|
203
|
-
author?: string;
|
|
204
|
-
robots?: string;
|
|
205
|
-
themeColor?: string;
|
|
206
|
-
viewport?:
|
|
207
|
-
| string
|
|
208
|
-
| {
|
|
209
|
-
width?: string;
|
|
210
|
-
height?: string;
|
|
211
|
-
initialScale?: string;
|
|
212
|
-
maximumScale?: string;
|
|
213
|
-
userScalable?: "no" | "yes" | "0" | "1";
|
|
214
|
-
interactiveWidget?:
|
|
215
|
-
| "resizes-visual"
|
|
216
|
-
| "resizes-content"
|
|
217
|
-
| "overlays-content";
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
og?: {
|
|
221
|
-
title?: string;
|
|
222
|
-
description?: string;
|
|
223
|
-
image?: string;
|
|
224
|
-
url?: string;
|
|
225
|
-
type?: string;
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
twitter?: {
|
|
229
|
-
card?: string;
|
|
230
|
-
title?: string;
|
|
231
|
-
description?: string;
|
|
232
|
-
image?: string;
|
|
233
|
-
site?: string;
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
192
|
export interface PageRequestConfig<
|
|
238
193
|
TConfig extends PageConfigSchema = PageConfigSchema,
|
|
239
194
|
> {
|
package/src/hooks/useClient.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type ClientScope,
|
|
3
|
-
HttpClient,
|
|
4
3
|
type HttpVirtualClient,
|
|
5
|
-
|
|
4
|
+
LinkProvider,
|
|
5
|
+
} from "@alepha/server-links";
|
|
6
6
|
import { useInject } from "./useInject.ts";
|
|
7
7
|
|
|
8
8
|
export const useClient = <T extends object>(
|
|
9
9
|
_scope?: ClientScope,
|
|
10
10
|
): HttpVirtualClient<T> => {
|
|
11
|
-
return useInject(
|
|
11
|
+
return useInject(LinkProvider).client<T>();
|
|
12
12
|
};
|
package/src/index.ts
CHANGED
|
@@ -22,21 +22,33 @@ export * from "./providers/ReactServerProvider.ts";
|
|
|
22
22
|
|
|
23
23
|
declare module "@alepha/core" {
|
|
24
24
|
interface Hooks {
|
|
25
|
+
"react:router:createLayers": {
|
|
26
|
+
request: ServerRequest;
|
|
27
|
+
context: PageRequest;
|
|
28
|
+
layers: PageRequest[];
|
|
29
|
+
};
|
|
30
|
+
"react:server:render:begin": {
|
|
31
|
+
request?: ServerRequest;
|
|
32
|
+
context: PageRequest;
|
|
33
|
+
};
|
|
34
|
+
"react:server:render:end": {
|
|
35
|
+
request?: ServerRequest;
|
|
36
|
+
context: PageRequest;
|
|
37
|
+
state: RouterState;
|
|
38
|
+
html: string;
|
|
39
|
+
};
|
|
25
40
|
"react:browser:render": {
|
|
26
41
|
state: RouterState;
|
|
27
42
|
context: PageReactContext;
|
|
28
43
|
hydration?: ReactHydrationState;
|
|
29
44
|
};
|
|
30
|
-
"react:server:render": {
|
|
31
|
-
request: ServerRequest;
|
|
32
|
-
pageRequest: PageRequest;
|
|
33
|
-
};
|
|
34
45
|
"react:transition:begin": {
|
|
35
46
|
state: RouterState;
|
|
36
47
|
context: PageReactContext;
|
|
37
48
|
};
|
|
38
49
|
"react:transition:success": {
|
|
39
50
|
state: RouterState;
|
|
51
|
+
context: PageReactContext;
|
|
40
52
|
};
|
|
41
53
|
"react:transition:error": {
|
|
42
54
|
error: Error;
|
|
@@ -53,14 +53,13 @@ export class BrowserRouterProvider extends RouterProvider<BrowserRoute> {
|
|
|
53
53
|
layers: [],
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
-
const context
|
|
56
|
+
const context = {
|
|
57
57
|
url,
|
|
58
58
|
query: {},
|
|
59
59
|
params: {},
|
|
60
|
-
head: {},
|
|
61
60
|
onError: () => null,
|
|
62
61
|
...(options.context ?? {}),
|
|
63
|
-
};
|
|
62
|
+
} as PageRequest;
|
|
64
63
|
|
|
65
64
|
await this.alepha.emit("react:transition:begin", { state, context });
|
|
66
65
|
|
|
@@ -105,7 +104,7 @@ export class BrowserRouterProvider extends RouterProvider<BrowserRoute> {
|
|
|
105
104
|
});
|
|
106
105
|
}
|
|
107
106
|
|
|
108
|
-
await this.alepha.emit("react:transition:success", { state });
|
|
107
|
+
await this.alepha.emit("react:transition:success", { state, context });
|
|
109
108
|
} catch (e) {
|
|
110
109
|
this.log.error(e);
|
|
111
110
|
state.layers = [
|
|
@@ -8,11 +8,7 @@ import NestedView from "../components/NestedView.tsx";
|
|
|
8
8
|
import NotFoundPage from "../components/NotFound.tsx";
|
|
9
9
|
import { RouterContext } from "../contexts/RouterContext.ts";
|
|
10
10
|
import { RouterLayerContext } from "../contexts/RouterLayerContext.ts";
|
|
11
|
-
import {
|
|
12
|
-
$page,
|
|
13
|
-
type Head,
|
|
14
|
-
type PageDescriptorOptions,
|
|
15
|
-
} from "../descriptors/$page.ts";
|
|
11
|
+
import { $page, type PageDescriptorOptions } from "../descriptors/$page.ts";
|
|
16
12
|
import { RedirectionError } from "../errors/RedirectionError.ts";
|
|
17
13
|
|
|
18
14
|
const envSchema = t.object({
|
|
@@ -213,12 +209,12 @@ export class PageDescriptorProvider {
|
|
|
213
209
|
params[key] = String(params[key]);
|
|
214
210
|
}
|
|
215
211
|
|
|
216
|
-
if (it.route.head && !it.error) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
212
|
+
// if (it.route.head && !it.error) {
|
|
213
|
+
// this.fillHead(it.route, request, {
|
|
214
|
+
// ...props,
|
|
215
|
+
// ...context,
|
|
216
|
+
// });
|
|
217
|
+
// }
|
|
222
218
|
|
|
223
219
|
acc += "/";
|
|
224
220
|
acc += it.route.path ? this.compile(it.route.path, params) : "";
|
|
@@ -244,6 +240,7 @@ export class PageDescriptorProvider {
|
|
|
244
240
|
element: this.renderView(i + 1, path, element, it.route),
|
|
245
241
|
index: i + 1,
|
|
246
242
|
path,
|
|
243
|
+
route,
|
|
247
244
|
});
|
|
248
245
|
break;
|
|
249
246
|
}
|
|
@@ -263,6 +260,7 @@ export class PageDescriptorProvider {
|
|
|
263
260
|
element: this.renderView(i + 1, path, element, it.route),
|
|
264
261
|
index: i + 1,
|
|
265
262
|
path,
|
|
263
|
+
route,
|
|
266
264
|
});
|
|
267
265
|
}
|
|
268
266
|
|
|
@@ -294,51 +292,6 @@ export class PageDescriptorProvider {
|
|
|
294
292
|
return undefined;
|
|
295
293
|
}
|
|
296
294
|
|
|
297
|
-
protected fillHead(
|
|
298
|
-
page: PageRoute,
|
|
299
|
-
ctx: PageRequest,
|
|
300
|
-
props: Record<string, any>,
|
|
301
|
-
): void {
|
|
302
|
-
if (!page.head) {
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
ctx.head ??= {};
|
|
307
|
-
|
|
308
|
-
const head =
|
|
309
|
-
typeof page.head === "function" ? page.head(props, ctx.head) : page.head;
|
|
310
|
-
|
|
311
|
-
if (head.title) {
|
|
312
|
-
ctx.head ??= {};
|
|
313
|
-
|
|
314
|
-
if (ctx.head.titleSeparator) {
|
|
315
|
-
ctx.head.title = `${head.title}${ctx.head.titleSeparator}${ctx.head.title}`;
|
|
316
|
-
} else {
|
|
317
|
-
ctx.head.title = head.title;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
ctx.head.titleSeparator = head.titleSeparator;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
if (head.htmlAttributes) {
|
|
324
|
-
ctx.head.htmlAttributes = {
|
|
325
|
-
...ctx.head.htmlAttributes,
|
|
326
|
-
...head.htmlAttributes,
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
if (head.bodyAttributes) {
|
|
331
|
-
ctx.head.bodyAttributes = {
|
|
332
|
-
...ctx.head.bodyAttributes,
|
|
333
|
-
...head.bodyAttributes,
|
|
334
|
-
};
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if (head.meta) {
|
|
338
|
-
ctx.head.meta = [...(ctx.head.meta ?? []), ...(head.meta ?? [])];
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
295
|
public renderError(error: Error): ReactNode {
|
|
343
296
|
return createElement(ErrorViewer, { error });
|
|
344
297
|
}
|
|
@@ -534,6 +487,7 @@ export interface Layer {
|
|
|
534
487
|
element: ReactNode;
|
|
535
488
|
index: number;
|
|
536
489
|
path: string;
|
|
490
|
+
route?: PageRoute;
|
|
537
491
|
}
|
|
538
492
|
|
|
539
493
|
export type PreviousLayerData = Omit<Layer, "element" | "index" | "path">;
|
|
@@ -586,7 +540,6 @@ export interface CreateLayersResult extends RouterState {
|
|
|
586
540
|
*/
|
|
587
541
|
export interface PageReactContext {
|
|
588
542
|
url: URL;
|
|
589
|
-
head: Head;
|
|
590
543
|
onError: (error: Error) => ReactNode;
|
|
591
544
|
links?: ApiLinksResponse;
|
|
592
545
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { $hook, $inject, $logger, Alepha } from "@alepha/core";
|
|
2
|
-
import {
|
|
2
|
+
import type { ApiLinksResponse } from "@alepha/server";
|
|
3
|
+
import { LinkProvider } from "@alepha/server-links";
|
|
3
4
|
import type { Root } from "react-dom/client";
|
|
4
|
-
import { BrowserHeadProvider } from "./BrowserHeadProvider.ts";
|
|
5
5
|
import { BrowserRouterProvider } from "./BrowserRouterProvider.ts";
|
|
6
6
|
import type {
|
|
7
7
|
PreviousLayerData,
|
|
@@ -12,10 +12,9 @@ import type {
|
|
|
12
12
|
|
|
13
13
|
export class ReactBrowserProvider {
|
|
14
14
|
protected readonly log = $logger();
|
|
15
|
-
protected readonly client = $inject(
|
|
15
|
+
protected readonly client = $inject(LinkProvider);
|
|
16
16
|
protected readonly alepha = $inject(Alepha);
|
|
17
17
|
protected readonly router = $inject(BrowserRouterProvider);
|
|
18
|
-
protected readonly headProvider = $inject(BrowserHeadProvider);
|
|
19
18
|
protected root!: Root;
|
|
20
19
|
|
|
21
20
|
public transitioning?: {
|
|
@@ -138,9 +137,6 @@ export class ReactBrowserProvider {
|
|
|
138
137
|
}
|
|
139
138
|
|
|
140
139
|
const { context } = await this.render({ previous });
|
|
141
|
-
if (context.head) {
|
|
142
|
-
this.headProvider.renderHead(this.document, context.head);
|
|
143
|
-
}
|
|
144
140
|
|
|
145
141
|
await this.alepha.emit("react:browser:render", {
|
|
146
142
|
state: this.state,
|
|
@@ -153,13 +149,6 @@ export class ReactBrowserProvider {
|
|
|
153
149
|
});
|
|
154
150
|
},
|
|
155
151
|
});
|
|
156
|
-
|
|
157
|
-
public readonly onTransitionEnd = $hook({
|
|
158
|
-
name: "react:transition:end",
|
|
159
|
-
handler: async ({ context }) => {
|
|
160
|
-
this.headProvider.renderHead(this.document, context.head);
|
|
161
|
-
},
|
|
162
|
-
});
|
|
163
152
|
}
|
|
164
153
|
|
|
165
154
|
// ---------------------------------------------------------------------------------------------------------------------
|