@devlusoft/devix 0.2.2 → 0.3.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/cli/build.js +12 -5
  2. package/dist/cli/build.js.map +2 -2
  3. package/dist/cli/dev.js +33 -26
  4. package/dist/cli/dev.js.map +3 -3
  5. package/dist/cli/generate.js +12 -5
  6. package/dist/cli/generate.js.map +2 -2
  7. package/dist/cli/index.js +12 -5
  8. package/dist/cli/index.js.map +2 -2
  9. package/dist/runtime/api-context.d.ts +10 -7
  10. package/dist/runtime/api-context.js +1 -1
  11. package/dist/runtime/api-context.js.map +2 -2
  12. package/dist/runtime/create-handler.d.ts +10 -0
  13. package/dist/runtime/create-handler.js +2 -0
  14. package/dist/runtime/create-handler.js.map +7 -0
  15. package/dist/runtime/fetch.d.ts +16 -5
  16. package/dist/runtime/fetch.js +1 -1
  17. package/dist/runtime/fetch.js.map +2 -2
  18. package/dist/runtime/index.d.ts +3 -0
  19. package/dist/runtime/index.js +1 -1
  20. package/dist/runtime/index.js.map +4 -4
  21. package/dist/server/api.js +1 -1
  22. package/dist/server/api.js.map +4 -4
  23. package/dist/server/handler-store.d.ts +10 -0
  24. package/dist/server/handler-store.js +2 -0
  25. package/dist/server/handler-store.js.map +7 -0
  26. package/dist/server/public-index.d.ts +1 -0
  27. package/dist/server/public-index.js +2 -0
  28. package/dist/server/public-index.js.map +7 -0
  29. package/dist/utils/response.d.ts +1 -1
  30. package/dist/utils/response.js.map +2 -2
  31. package/dist/vite/codegen/routes-dts.js +12 -5
  32. package/dist/vite/codegen/routes-dts.js.map +2 -2
  33. package/dist/vite/codegen/scan-api.js +1 -1
  34. package/dist/vite/codegen/scan-api.js.map +1 -1
  35. package/dist/vite/index.js +10 -3
  36. package/dist/vite/index.js.map +2 -2
  37. package/package.json +5 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/runtime/router-provider.tsx", "../../src/runtime/head.tsx", "../../src/runtime/context.tsx", "../../src/runtime/error-boundary.tsx", "../../src/runtime/link.tsx", "../../src/utils/cookies.ts", "../../src/utils/response.ts", "../../src/runtime/fetch.ts"],
4
- "sourcesContent": ["import {ComponentType, ReactNode, useCallback, useContext, useEffect, useRef, useState} from \"react\";\nimport {RouterContext} from 'virtual:devix/context'\nimport {ErrorProps, LayoutProps, PageProps} from \"../server/types\";\nimport {Metadata, Viewport} from \"../types\";\nimport {getDefaultErrorPage, loadErrorPage, matchClientRoute} from \"virtual:devix/client-routes\";\nimport {buildHeadNodes} from \"./head\";\nimport {PageMetaContext, RouteDataContext} from \"./context\";\nimport {DevixErrorBoundary} from \"./error-boundary\";\n\ninterface RouteState {\n pathname: string\n params: Record<string, string>\n loaderData: unknown\n layoutsData: unknown[]\n Page: ComponentType<PageProps>\n layouts: ComponentType<LayoutProps>[]\n metadata: Metadata | null\n viewport?: Viewport\n pendingError?: ErrorProps\n ErrorPage?: ComponentType<ErrorProps>\n}\n\nexport function useRouter() {\n return useContext(RouterContext)\n}\n\nexport function useNavigate() {\n const ctx = useContext(RouterContext)\n if (!ctx) throw new Error(\"useNavigate must be used within a RouterProvider\")\n return ctx.navigate\n}\n\nexport function useParams<T extends Record<string, string>>() {\n const ctx = useContext(RouteDataContext)\n if (!ctx) throw new Error(\"useParams must be used within a route or layout\")\n return ctx.params as T\n}\n\ntype LoaderReturnType<T> = T extends (...args: any[]) => Promise<infer R>\n ? R\n : T extends (...args: any[]) => infer R\n ? R\n : T\n\nexport function useLoaderData<T>() {\n const ctx = useContext(RouteDataContext)\n if (!ctx) throw new Error(\"useLoaderData must be used within a route or layout\")\n return ctx.loaderData as LoaderReturnType<T>\n}\n\n\ninterface RouterProviderProps {\n initialData: unknown\n initialParams: Record<string, string>\n initialPage: ComponentType<PageProps>\n initialLayouts?: ComponentType<LayoutProps>[]\n initialLayoutsData?: unknown[]\n initialMeta?: Metadata | null\n initialViewport?: Viewport\n initialError?: ErrorProps\n initialErrorPage?: ComponentType<ErrorProps>\n clientEntry: string\n}\n\nexport function RouterProvider({\n initialData,\n initialParams,\n initialPage,\n initialLayouts = [],\n initialLayoutsData = [],\n initialMeta,\n initialViewport,\n initialError,\n initialErrorPage,\n clientEntry,\n }: RouterProviderProps) {\n\n const [state, setState] = useState<RouteState>({\n pathname: window.location.pathname,\n params: initialParams,\n loaderData: initialData,\n layoutsData: initialLayoutsData,\n Page: initialPage,\n layouts: initialLayouts,\n metadata: initialMeta ?? null,\n viewport: initialViewport,\n pendingError: initialError,\n ErrorPage: initialErrorPage,\n })\n\n const navigatingRef = useRef<AbortController | null>(null)\n const [isNavigating, setIsNavigating] = useState(false)\n\n const loadRoute = useCallback(async (to: string, controller: AbortController) => {\n const pathname = to.split('?')[0]\n const matched = matchClientRoute(pathname)\n if (!matched) {\n const ErrorPage = await loadErrorPage() ?? getDefaultErrorPage()\n setState(prev => ({\n ...prev,\n pathname: pathname,\n pendingError: {statusCode: 404, message: 'Not found'},\n ErrorPage: ErrorPage ?? undefined,\n }))\n return\n }\n\n const [pageMod, ...layoutMods] = await Promise.all([\n matched.load(),\n ...matched.loadLayouts.map(l => l()),\n ])\n\n if (controller.signal.aborted) return\n if (!pageMod.default) return\n\n const dataRes = await fetch(`/_data${to}`, {\n headers: {Accept: 'application/json'},\n signal: controller.signal,\n })\n\n if (controller.signal.aborted) return\n\n if (!dataRes.ok) {\n if (dataRes.status === 404) {\n window.location.href = to\n return\n }\n console.error(`/_data${to} returned ${dataRes.status}`)\n return\n }\n\n const data = await dataRes.json()\n\n window.scrollTo(0, 0)\n setState({\n pathname,\n params: data.params ?? {},\n loaderData: data.loaderData,\n layoutsData: (data.layouts ?? []).map((l: any) => l.loaderData),\n Page: pageMod.default,\n layouts: layoutMods.map(m => m.default),\n metadata: data.metadata ?? null,\n viewport: data.viewport,\n })\n }, [])\n\n const navigate = useCallback(async (to: string) => {\n navigatingRef.current?.abort()\n const controller = new AbortController()\n navigatingRef.current = controller\n\n setIsNavigating(true)\n try {\n window.history.pushState(null, \"\", to)\n await loadRoute(to, controller)\n } finally {\n if (!controller.signal.aborted) setIsNavigating(false)\n }\n }, [loadRoute])\n\n useEffect(() => {\n const handlePop = () => {\n navigatingRef.current?.abort()\n const controller = new AbortController()\n navigatingRef.current = controller\n\n const to = window.location.pathname + window.location.search\n loadRoute(to, controller).catch(err => {\n if (err.name !== 'AbortError') console.error('[router] popstate error:', err)\n })\n }\n window.addEventListener(\"popstate\", handlePop)\n return () => window.removeEventListener(\"popstate\", handlePop)\n }, [loadRoute])\n\n let content: ReactNode\n\n if (state.pendingError) {\n content = state.ErrorPage\n ? <state.ErrorPage {...state.pendingError} />\n : <h1>{state.pendingError.statusCode}</h1>\n } else {\n let tree: ReactNode = (\n <RouteDataContext value={{loaderData: state.loaderData, params: state.params}}>\n <state.Page data={state.loaderData} params={state.params} url={state.pathname}/>\n </RouteDataContext>\n )\n\n for (let i = state.layouts.length - 1; i >= 0; i--) {\n const Layout = state.layouts[i]\n const layoutData = state.layoutsData[i]\n tree = (\n <RouteDataContext value={{loaderData: layoutData, params: state.params}}>\n <Layout data={layoutData} params={state.params}>{tree}</Layout>\n </RouteDataContext>\n )\n }\n\n content = (\n <DevixErrorBoundary key={state.pathname} ErrorPage={state.ErrorPage}>\n {tree}\n </DevixErrorBoundary>\n )\n }\n\n return (\n <PageMetaContext value={{\n metadata: state.metadata,\n viewport: state.viewport,\n clientEntry,\n }}>\n {state.metadata && buildHeadNodes(state.metadata, state.viewport)}\n <RouterContext value={{...state, isNavigating, navigate}}>\n {content}\n </RouterContext>\n </PageMetaContext>\n )\n}", "import {Metadata, Viewport} from \"../types\";\nimport {ReactNode} from \"react\";\n\ntype MetaTag =\n | { tag: 'title'; children: string }\n | { tag: 'meta'; name?: string; property?: string; content: string }\n | { tag: 'link'; rel: string; href: string; hrefLang?: string }\n\nfunction collectTags(metadata: Metadata, viewport?: Viewport): MetaTag[] {\n const tags: MetaTag[] = []\n\n if (metadata.title)\n tags.push({tag: 'title', children: metadata.title})\n if (metadata.description)\n tags.push({tag: 'meta', name: 'description', content: metadata.description})\n if (metadata.keywords?.length)\n tags.push({tag: 'meta', name: 'keywords', content: metadata.keywords.join(', ')})\n\n const ogTitle = metadata.og?.title ?? metadata.title\n if (ogTitle) tags.push({tag: 'meta', property: 'og:title', content: ogTitle})\n const ogDesc = metadata.og?.description ?? metadata.description\n if (ogDesc) tags.push({tag: 'meta', property: 'og:description', content: ogDesc})\n if (metadata.og?.image) tags.push({tag: 'meta', property: 'og:image', content: metadata.og.image})\n if (metadata.og?.type) tags.push({tag: 'meta', property: 'og:type', content: metadata.og.type})\n if (metadata.og?.url) tags.push({tag: 'meta', property: 'og:url', content: metadata.og.url})\n\n const twTitle = metadata.twitter?.title ?? metadata.title\n if (twTitle) tags.push({tag: 'meta', name: 'twitter:title', content: twTitle})\n const twDesc = metadata.twitter?.description ?? metadata.description\n if (twDesc) tags.push({tag: 'meta', name: 'twitter:description', content: twDesc})\n if (metadata.twitter?.card) tags.push({\n tag: 'meta', name: 'twitter:card', content:\n metadata.twitter.card\n })\n if (metadata.twitter?.image) tags.push({\n tag: 'meta', name: 'twitter:image', content:\n metadata.twitter.image\n })\n if (metadata.twitter?.creator) tags.push({\n tag: 'meta', name: 'twitter:creator', content:\n metadata.twitter.creator\n })\n\n if (metadata.canonical) tags.push({tag: 'link', rel: 'canonical', href: metadata.canonical})\n if (metadata.robots) tags.push({tag: 'meta', name: 'robots', content: metadata.robots})\n if (metadata.alternates) {\n for (const [lang, href] of Object.entries(metadata.alternates))\n tags.push({tag: 'link', rel: 'alternate', href, hrefLang: lang})\n }\n\n if (viewport) {\n const parts: string[] = []\n if (viewport.width !== undefined) parts.push(`width=${viewport.width}`)\n if (viewport.initialScale !== undefined) parts.push(`initial-scale=${viewport.initialScale}`)\n if (viewport.maximumScale !== undefined) parts.push(`maximum-scale=${viewport.maximumScale}`)\n if (viewport.userScalable !== undefined) parts.push(`user-scalable=${viewport.userScalable ? 'yes' :\n 'no'}`)\n if (parts.length) tags.push({tag: 'meta', name: 'viewport', content: parts.join(', ')})\n if (viewport.themeColor) tags.push({\n tag: 'meta', name: 'theme-color', content: viewport.themeColor\n })\n }\n\n return tags\n}\n\nexport function buildHeadNodes(metadata: Metadata, viewport?: Viewport): ReactNode {\n const tags = collectTags(metadata, viewport)\n\n return <>\n {tags.map((t, i) => {\n if (t.tag === 'title') return <title key={i}>{t.children}</title>\n if (t.tag === 'link') return <link key={i} rel={t.rel} href={t.href} hrefLang={t.hrefLang}/>\n return <meta key={i} name={t.name} property={t.property} content={t.content}/>\n })}\n </>\n}", "import {createContext, Context, ComponentType} from \"react\";\nimport {Metadata, Viewport} from \"../types\";\nimport {LayoutProps, PageProps} from \"../server/types\";\n\nexport interface RouterContextValue {\n pathname: string\n params: Record<string, string>\n loaderData: unknown\n layoutsData: unknown[]\n Page: ComponentType<PageProps>\n layouts: ComponentType<LayoutProps>[]\n metadata: Metadata | null\n viewport?: Viewport\n navigate: (to: string) => void\n isNavigating: boolean\n}\n\nexport interface PageMetaContextValue {\n metadata: Metadata | null\n viewport?: Viewport\n clientEntry?: string\n}\n\nexport interface RouteDataContextValue {\n loaderData: unknown\n params: Record<string, string>\n}\n\nconst g = globalThis as any\n\ng.__devix_RouterContext__ ??= createContext<RouterContextValue | null>(null)\nexport const RouterContext: Context<RouterContextValue | null> = g.__devix_RouterContext__\n\ng.__devix_PageMetaContext__ ??= createContext<PageMetaContextValue | null>(null)\ng.__devix_RouteDataContext__ ??= createContext<RouteDataContextValue | null>(null)\n\nexport const PageMetaContext: Context<PageMetaContextValue | null> = g.__devix_PageMetaContext__\nexport const RouteDataContext: Context<RouteDataContextValue | null> = g.__devix_RouteDataContext__\n\n", "import {Component, ComponentType, ReactNode} from \"react\";\nimport {ErrorProps} from \"../server/types\";\n\ninterface Props {\n ErrorPage?: ComponentType<ErrorProps>\n children: ReactNode\n}\n\ninterface State {\n error: ErrorProps | null\n}\n\nexport class DevixErrorBoundary extends Component<Props, State> {\n state: State = { error: null }\n\n static getDerivedStateFromError(err: unknown): State {\n if (err instanceof DevixError) {\n return {\n error: {statusCode: err.statusCode, message: err.message}\n }\n }\n return {\n error: {statusCode: 500, message: err instanceof Error ? err.message : 'Unknown error'}\n }\n }\n\n render() {\n if (this.state.error && this.props.ErrorPage) {\n return <this.props.ErrorPage {...this.state.error} />\n }\n if (this.state.error) {\n return <h1>{this.state.error.statusCode}</h1>\n }\n return this.props.children\n }\n}\n\nexport class DevixError extends Error {\n statusCode: number\n constructor(statusCode: number, message: string) {\n super(message)\n this.statusCode = statusCode\n }\n}\n", "import {AnchorHTMLAttributes, MouseEventHandler, useCallback, useContext} from \"react\";\nimport {matchClientRoute} from \"virtual:devix/client-routes\";\nimport {RouterContext} from 'virtual:devix/context'\n\ninterface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {\n href: string\n prefetch?: boolean\n viewTransition?: boolean\n}\n\nfunction resolveHref(href: string): string {\n if (href.startsWith('/') || href.startsWith('http')) return href\n const base = window.location.pathname.endsWith('/')\n ? window.location.href\n : window.location.href + '/'\n const resolved = new URL(href, base).pathname\n return resolved.length > 1 ? resolved.replace(/\\/$/, '') : resolved\n}\n\nexport function Link({ href, prefetch = false, viewTransition = false, children, ...props }: LinkProps) {\n const router = useContext(RouterContext)\n\n const handleMouseEnter = useCallback(() => {\n if (!prefetch) return\n const resolved = resolveHref(href)\n const pathname = resolved.split('?')[0]\n const matched = matchClientRoute(pathname)\n if (matched) {\n matched.load().catch(() => {})\n fetch(`/_data${resolved}`, { headers: { Accept: 'application/json' } }).catch(() => {})\n }\n }, [href, prefetch])\n\n const handleClick: MouseEventHandler<HTMLAnchorElement> = (e) => {\n if (!router) return\n if (!e.ctrlKey && !e.metaKey && !e.shiftKey && e.button === 0) {\n e.preventDefault()\n const resolved = resolveHref(href)\n if (viewTransition && typeof document.startViewTransition === 'function') {\n document.startViewTransition(() => router.navigate(resolved))\n } else {\n router.navigate(resolved)\n }\n }\n }\n\n return (\n <a href={href} onClick={handleClick} onMouseEnter={handleMouseEnter} {...props}>\n {children}\n </a>\n )\n}", "export interface CookieOptions {\n httpOnly?: boolean\n secure?: boolean\n sameSite?: 'Strict' | 'Lax' | 'None'\n maxAge?: number\n expires?: Date\n path?: string\n domain?: string\n}\n\nexport function getCookie(req: Request, name: string): string | undefined {\n const header = req.headers.get('cookie')\n if (!header) return undefined\n for (const part of header.split(';')) {\n const [key, ...rest] = part.trim().split('=')\n if (key.trim() === name) return decodeURIComponent(rest.join('='))\n }\n return undefined\n}\n\nexport function setCookie(headers: Headers, name: string, value: string, options: CookieOptions = {}): void {\n let cookie = `${name}=${encodeURIComponent(value)}; Path=${options.path ?? '/'}`\n if (options.domain) cookie += `; Domain=${options.domain}`\n if (options.maxAge !== undefined) cookie += `; Max-Age=${options.maxAge}`\n if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`\n if (options.httpOnly) cookie += `; HttpOnly`\n if (options.secure) cookie += `; Secure`\n if (options.sameSite) cookie += `; SameSite=${options.sameSite}`\n headers.append('Set-Cookie', cookie)\n}\n\nexport function deleteCookie(headers: Headers, name: string, options: Pick<CookieOptions, 'path' | 'domain'> = {}): void {\n setCookie(headers, name, '', {...options, maxAge: 0, expires: new Date(0)})\n}\n", "export type JsonResponse<T = unknown> = Response & { readonly __body: T }\n\nexport const json = <T>(data: T, status = 200): JsonResponse<T> =>\n Response.json(data, {status}) as JsonResponse<T>\n\nexport const text = (body: string, status = 200): Response =>\n new Response(body, {status, headers: {'Content-Type': 'text/plain; charset=utf-8'}})\n\nexport const redirect = (url: string, status = 302): Response =>\n new Response(null, {status, headers: {Location: url}})\n", "export interface ApiRoutes {}\n\ntype HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'\n\nexport interface FetchOptions<M extends HttpMethod = 'GET'> {\n method?: M\n body?: unknown\n headers?: HeadersInit\n signal?: AbortSignal\n}\n\ntype ApiKey<M extends HttpMethod, P extends string> = `${M} ${P}`\n\ntype InferResult<M extends HttpMethod, P extends string> =\n ApiKey<M, P> extends keyof ApiRoutes\n ? ApiRoutes[ApiKey<M, P>]\n : unknown\n\nexport class FetchError extends Error {\n constructor(\n public readonly status: number,\n public readonly statusText: string,\n public readonly response: Response,\n ) {\n super(`HTTP ${status}: ${statusText}`)\n this.name = 'FetchError'\n }\n}\n\nexport async function $fetch<\n P extends string,\n M extends HttpMethod = 'GET',\n>(path: P, options?: FetchOptions<M>): Promise<InferResult<M, P>> {\n const method = (options?.method ?? 'GET') as string\n const headers = new Headers(options?.headers)\n\n let body: BodyInit | undefined\n if (options?.body !== undefined) {\n body = JSON.stringify(options.body)\n if (!headers.has('Content-Type')) {\n headers.set('Content-Type', 'application/json')\n }\n }\n\n const response = await fetch(path, {method, headers, body, signal: options?.signal})\n\n if (!response.ok) {\n throw new FetchError(response.status, response.statusText, response)\n }\n\n const contentType = response.headers.get('Content-Type') ?? ''\n if (contentType.includes('application/json')) {\n return response.json() as Promise<InferResult<M, P>>\n }\n\n return response.text() as unknown as Promise<InferResult<M, P>>\n}\n"],
5
- "mappings": "AAAA,OAAkC,eAAAA,EAAa,cAAAC,EAAY,aAAAC,EAAW,UAAAC,EAAQ,YAAAC,MAAe,QAC7F,OAAQ,iBAAAC,MAAoB,wBAG5B,OAAQ,uBAAAC,EAAqB,iBAAAC,EAAe,oBAAAC,MAAuB,8BCiExD,mBAAAC,EAE+B,OAAAC,MAF/B,oBA7DX,SAASC,EAAYC,EAAoBC,EAAgC,CACrE,IAAMC,EAAkB,CAAC,EAErBF,EAAS,OACTE,EAAK,KAAK,CAAC,IAAK,QAAS,SAAUF,EAAS,KAAK,CAAC,EAClDA,EAAS,aACTE,EAAK,KAAK,CAAC,IAAK,OAAQ,KAAM,cAAe,QAASF,EAAS,WAAW,CAAC,EAC3EA,EAAS,UAAU,QACnBE,EAAK,KAAK,CAAC,IAAK,OAAQ,KAAM,WAAY,QAASF,EAAS,SAAS,KAAK,IAAI,CAAC,CAAC,EAEpF,IAAMG,EAAUH,EAAS,IAAI,OAASA,EAAS,MAC3CG,GAASD,EAAK,KAAK,CAAC,IAAK,OAAQ,SAAU,WAAY,QAASC,CAAO,CAAC,EAC5E,IAAMC,EAASJ,EAAS,IAAI,aAAeA,EAAS,YAChDI,GAAQF,EAAK,KAAK,CAAC,IAAK,OAAQ,SAAU,iBAAkB,QAASE,CAAM,CAAC,EAC5EJ,EAAS,IAAI,OAAOE,EAAK,KAAK,CAAC,IAAK,OAAQ,SAAU,WAAY,QAASF,EAAS,GAAG,KAAK,CAAC,EAC7FA,EAAS,IAAI,MAAME,EAAK,KAAK,CAAC,IAAK,OAAQ,SAAU,UAAW,QAASF,EAAS,GAAG,IAAI,CAAC,EAC1FA,EAAS,IAAI,KAAKE,EAAK,KAAK,CAAC,IAAK,OAAQ,SAAU,SAAU,QAASF,EAAS,GAAG,GAAG,CAAC,EAE3F,IAAMK,EAAUL,EAAS,SAAS,OAASA,EAAS,MAChDK,GAASH,EAAK,KAAK,CAAC,IAAK,OAAQ,KAAM,gBAAiB,QAASG,CAAO,CAAC,EAC7E,IAAMC,EAASN,EAAS,SAAS,aAAeA,EAAS,YAiBzD,GAhBIM,GAAQJ,EAAK,KAAK,CAAC,IAAK,OAAQ,KAAM,sBAAuB,QAASI,CAAM,CAAC,EAC7EN,EAAS,SAAS,MAAME,EAAK,KAAK,CAClC,IAAK,OAAQ,KAAM,eAAgB,QACnCF,EAAS,QAAQ,IACrB,CAAC,EACGA,EAAS,SAAS,OAAOE,EAAK,KAAK,CACnC,IAAK,OAAQ,KAAM,gBAAiB,QACpCF,EAAS,QAAQ,KACrB,CAAC,EACGA,EAAS,SAAS,SAASE,EAAK,KAAK,CACrC,IAAK,OAAQ,KAAM,kBAAmB,QACtCF,EAAS,QAAQ,OACrB,CAAC,EAEGA,EAAS,WAAWE,EAAK,KAAK,CAAC,IAAK,OAAQ,IAAK,YAAa,KAAMF,EAAS,SAAS,CAAC,EACvFA,EAAS,QAAQE,EAAK,KAAK,CAAC,IAAK,OAAQ,KAAM,SAAU,QAASF,EAAS,MAAM,CAAC,EAClFA,EAAS,WACT,OAAW,CAACO,EAAMC,CAAI,IAAK,OAAO,QAAQR,EAAS,UAAU,EACzDE,EAAK,KAAK,CAAC,IAAK,OAAQ,IAAK,YAAa,KAAAM,EAAM,SAAUD,CAAI,CAAC,EAGvE,GAAIN,EAAU,CACV,IAAMQ,EAAkB,CAAC,EACrBR,EAAS,QAAU,QAAWQ,EAAM,KAAK,SAASR,EAAS,KAAK,EAAE,EAClEA,EAAS,eAAiB,QAAWQ,EAAM,KAAK,iBAAiBR,EAAS,YAAY,EAAE,EACxFA,EAAS,eAAiB,QAAWQ,EAAM,KAAK,iBAAiBR,EAAS,YAAY,EAAE,EACxFA,EAAS,eAAiB,QAAWQ,EAAM,KAAK,iBAAiBR,EAAS,aAAe,MACzF,IAAI,EAAE,EACNQ,EAAM,QAAQP,EAAK,KAAK,CAAC,IAAK,OAAQ,KAAM,WAAY,QAASO,EAAM,KAAK,IAAI,CAAC,CAAC,EAClFR,EAAS,YAAYC,EAAK,KAAK,CAC/B,IAAK,OAAQ,KAAM,cAAe,QAASD,EAAS,UACxD,CAAC,CACL,CAEA,OAAOC,CACX,CAEO,SAASQ,EAAeV,EAAoBC,EAAgC,CAC/E,IAAMC,EAAOH,EAAYC,EAAUC,CAAQ,EAE3C,OAAOH,EAAAD,EAAA,CACF,SAAAK,EAAK,IAAI,CAACS,EAAGC,IACND,EAAE,MAAQ,QAAgBb,EAAC,SAAe,SAAAa,EAAE,UAANC,CAAe,EACrDD,EAAE,MAAQ,OAAeb,EAAC,QAAa,IAAKa,EAAE,IAAK,KAAMA,EAAE,KAAM,SAAUA,EAAE,UAAzCC,CAAkD,EACnFd,EAAC,QAAa,KAAMa,EAAE,KAAM,SAAUA,EAAE,SAAU,QAASA,EAAE,SAAlDC,CAA0D,CAC/E,EACL,CACJ,CC5EA,OAAQ,iBAAAC,MAA4C,QA4BpD,IAAMC,EAAI,WAEVA,EAAE,0BAA4BD,EAAyC,IAAI,EACpE,IAAME,GAAoDD,EAAE,wBAEnEA,EAAE,4BAA8BD,EAA2C,IAAI,EAC/EC,EAAE,6BAA+BD,EAA4C,IAAI,EAE1E,IAAMG,EAAwDF,EAAE,0BAC1DG,EAA0DH,EAAE,2BCrCzE,OAAQ,aAAAI,MAA0C,QA4B/B,cAAAC,MAAA,oBAhBZ,IAAMC,EAAN,cAAiCF,CAAwB,CAC5D,MAAe,CAAE,MAAO,IAAK,EAE7B,OAAO,yBAAyBG,EAAqB,CACjD,OAAIA,aAAeC,EACR,CACH,MAAO,CAAC,WAAYD,EAAI,WAAY,QAASA,EAAI,OAAO,CAC5D,EAEI,CACJ,MAAO,CAAC,WAAY,IAAK,QAASA,aAAe,MAAQA,EAAI,QAAU,eAAe,CAC1F,CACJ,CAEA,QAAS,CACL,OAAI,KAAK,MAAM,OAAS,KAAK,MAAM,UACxBF,EAAC,KAAK,MAAM,UAAX,CAAsB,GAAG,KAAK,MAAM,MAAO,EAEnD,KAAK,MAAM,MACJA,EAAC,MAAI,cAAK,MAAM,MAAM,WAAW,EAErC,KAAK,MAAM,QACtB,CACJ,EAEaG,EAAN,cAAyB,KAAM,CAClC,WACA,YAAYC,EAAoBC,EAAiB,CAC7C,MAAMA,CAAO,EACb,KAAK,WAAaD,CACtB,CACJ,EHwIc,cAAAE,EA2BN,QAAAC,OA3BM,oBA7JP,SAASC,GAAY,CACxB,OAAOC,EAAWC,CAAa,CACnC,CAEO,SAASC,IAAc,CAC1B,IAAMC,EAAMH,EAAWC,CAAa,EACpC,GAAI,CAACE,EAAK,MAAM,IAAI,MAAM,kDAAkD,EAC5E,OAAOA,EAAI,QACf,CAEO,SAASC,IAA8C,CAC1D,IAAMD,EAAMH,EAAWK,CAAgB,EACvC,GAAI,CAACF,EAAK,MAAM,IAAI,MAAM,iDAAiD,EAC3E,OAAOA,EAAI,MACf,CAQO,SAASG,IAAmB,CAC/B,IAAMH,EAAMH,EAAWK,CAAgB,EACvC,GAAI,CAACF,EAAK,MAAM,IAAI,MAAM,qDAAqD,EAC/E,OAAOA,EAAI,UACf,CAgBO,SAASI,GAAe,CACI,YAAAC,EACA,cAAAC,EACA,YAAAC,EACA,eAAAC,EAAiB,CAAC,EAClB,mBAAAC,EAAqB,CAAC,EACtB,YAAAC,EACA,gBAAAC,EACA,aAAAC,EACA,iBAAAC,EACA,YAAAC,CACJ,EAAwB,CAEnD,GAAM,CAACC,EAAOC,CAAQ,EAAIC,EAAqB,CAC3C,SAAU,OAAO,SAAS,SAC1B,OAAQX,EACR,WAAYD,EACZ,YAAaI,EACb,KAAMF,EACN,QAASC,EACT,SAAUE,GAAe,KACzB,SAAUC,EACV,aAAcC,EACd,UAAWC,CACf,CAAC,EAEKK,EAAgBC,EAA+B,IAAI,EACnD,CAACC,EAAcC,CAAe,EAAIJ,EAAS,EAAK,EAEhDK,EAAYC,EAAY,MAAOC,EAAYC,IAAgC,CAC7E,IAAMC,EAAWF,EAAG,MAAM,GAAG,EAAE,CAAC,EAC1BG,EAAUC,EAAiBF,CAAQ,EACzC,GAAI,CAACC,EAAS,CACV,IAAME,EAAY,MAAMC,EAAc,GAAKC,EAAoB,EAC/Df,EAASgB,IAAS,CACd,GAAGA,EACH,SAAUN,EACV,aAAc,CAAC,WAAY,IAAK,QAAS,WAAW,EACpD,UAAWG,GAAa,MAC5B,EAAE,EACF,MACJ,CAEA,GAAM,CAACI,EAAS,GAAGC,CAAU,EAAI,MAAM,QAAQ,IAAI,CAC/CP,EAAQ,KAAK,EACb,GAAGA,EAAQ,YAAY,IAAIQ,GAAKA,EAAE,CAAC,CACvC,CAAC,EAGD,GADIV,EAAW,OAAO,SAClB,CAACQ,EAAQ,QAAS,OAEtB,IAAMG,EAAU,MAAM,MAAM,SAASZ,CAAE,GAAI,CACvC,QAAS,CAAC,OAAQ,kBAAkB,EACpC,OAAQC,EAAW,MACvB,CAAC,EAED,GAAIA,EAAW,OAAO,QAAS,OAE/B,GAAI,CAACW,EAAQ,GAAI,CACb,GAAIA,EAAQ,SAAW,IAAK,CACxB,OAAO,SAAS,KAAOZ,EACvB,MACJ,CACA,QAAQ,MAAM,SAASA,CAAE,aAAaY,EAAQ,MAAM,EAAE,EACtD,MACJ,CAEA,IAAMC,EAAO,MAAMD,EAAQ,KAAK,EAEhC,OAAO,SAAS,EAAG,CAAC,EACpBpB,EAAS,CACL,SAAAU,EACA,OAAQW,EAAK,QAAU,CAAC,EACxB,WAAYA,EAAK,WACjB,aAAcA,EAAK,SAAW,CAAC,GAAG,IAAKF,GAAWA,EAAE,UAAU,EAC9D,KAAMF,EAAQ,QACd,QAASC,EAAW,IAAI,GAAK,EAAE,OAAO,EACtC,SAAUG,EAAK,UAAY,KAC3B,SAAUA,EAAK,QACnB,CAAC,CACL,EAAG,CAAC,CAAC,EAECC,EAAWf,EAAY,MAAOC,GAAe,CAC/CN,EAAc,SAAS,MAAM,EAC7B,IAAMO,EAAa,IAAI,gBACvBP,EAAc,QAAUO,EAExBJ,EAAgB,EAAI,EACpB,GAAI,CACA,OAAO,QAAQ,UAAU,KAAM,GAAIG,CAAE,EACrC,MAAMF,EAAUE,EAAIC,CAAU,CAClC,QAAE,CACOA,EAAW,OAAO,SAASJ,EAAgB,EAAK,CACzD,CACJ,EAAG,CAACC,CAAS,CAAC,EAEdiB,EAAU,IAAM,CACZ,IAAMC,EAAY,IAAM,CACpBtB,EAAc,SAAS,MAAM,EAC7B,IAAMO,EAAa,IAAI,gBACvBP,EAAc,QAAUO,EAExB,IAAMD,EAAK,OAAO,SAAS,SAAW,OAAO,SAAS,OACtDF,EAAUE,EAAIC,CAAU,EAAE,MAAMgB,GAAO,CAC/BA,EAAI,OAAS,cAAc,QAAQ,MAAM,2BAA4BA,CAAG,CAChF,CAAC,CACL,EACA,cAAO,iBAAiB,WAAYD,CAAS,EACtC,IAAM,OAAO,oBAAoB,WAAYA,CAAS,CACjE,EAAG,CAAClB,CAAS,CAAC,EAEd,IAAIoB,EAEJ,GAAI3B,EAAM,aACN2B,EAAU3B,EAAM,UACVrB,EAACqB,EAAM,UAAN,CAAiB,GAAGA,EAAM,aAAc,EACzCrB,EAAC,MAAI,SAAAqB,EAAM,aAAa,WAAW,MACtC,CACH,IAAI4B,EACAjD,EAACQ,EAAA,CAAiB,MAAO,CAAC,WAAYa,EAAM,WAAY,OAAQA,EAAM,MAAM,EACxE,SAAArB,EAACqB,EAAM,KAAN,CAAW,KAAMA,EAAM,WAAY,OAAQA,EAAM,OAAQ,IAAKA,EAAM,SAAS,EAClF,EAGJ,QAAS,EAAIA,EAAM,QAAQ,OAAS,EAAG,GAAK,EAAG,IAAK,CAChD,IAAM6B,EAAS7B,EAAM,QAAQ,CAAC,EACxB8B,EAAa9B,EAAM,YAAY,CAAC,EACtC4B,EACIjD,EAACQ,EAAA,CAAiB,MAAO,CAAC,WAAY2C,EAAY,OAAQ9B,EAAM,MAAM,EAClE,SAAArB,EAACkD,EAAA,CAAO,KAAMC,EAAY,OAAQ9B,EAAM,OAAS,SAAA4B,EAAK,EAC1D,CAER,CAEAD,EACIhD,EAACoD,EAAA,CAAwC,UAAW/B,EAAM,UACrD,SAAA4B,GADoB5B,EAAM,QAE/B,CAER,CAEA,OACIpB,GAACoD,EAAA,CAAgB,MAAO,CACpB,SAAUhC,EAAM,SAChB,SAAUA,EAAM,SAChB,YAAAD,CACJ,EACK,UAAAC,EAAM,UAAYiC,EAAejC,EAAM,SAAUA,EAAM,QAAQ,EAChErB,EAACI,EAAA,CAAc,MAAO,CAAC,GAAGiB,EAAO,aAAAK,EAAc,SAAAkB,CAAQ,EAClD,SAAAI,EACL,GACJ,CAER,CIzNA,OAAiD,eAAAO,GAAa,cAAAC,OAAiB,QAC/E,OAAQ,oBAAAC,OAAuB,8BAC/B,OAAQ,iBAAAC,OAAoB,wBA6CpB,cAAAC,OAAA,oBArCR,SAASC,EAAYC,EAAsB,CACvC,GAAIA,EAAK,WAAW,GAAG,GAAKA,EAAK,WAAW,MAAM,EAAG,OAAOA,EAC5D,IAAMC,EAAO,OAAO,SAAS,SAAS,SAAS,GAAG,EAC5C,OAAO,SAAS,KAChB,OAAO,SAAS,KAAO,IACvBC,EAAW,IAAI,IAAIF,EAAMC,CAAI,EAAE,SACrC,OAAOC,EAAS,OAAS,EAAIA,EAAS,QAAQ,MAAO,EAAE,EAAIA,CAC/D,CAEO,SAASC,GAAK,CAAE,KAAAH,EAAM,SAAAI,EAAW,GAAO,eAAAC,EAAiB,GAAO,SAAAC,EAAU,GAAGC,CAAM,EAAc,CACpG,IAAMC,EAASb,GAAWE,EAAa,EAEjCY,EAAmBf,GAAY,IAAM,CACvC,GAAI,CAACU,EAAU,OACf,IAAMF,EAAWH,EAAYC,CAAI,EAC3BU,EAAWR,EAAS,MAAM,GAAG,EAAE,CAAC,EAChCS,EAAUf,GAAiBc,CAAQ,EACrCC,IACAA,EAAQ,KAAK,EAAE,MAAM,IAAM,CAAC,CAAC,EAC7B,MAAM,SAAST,CAAQ,GAAI,CAAE,QAAS,CAAE,OAAQ,kBAAmB,CAAE,CAAC,EAAE,MAAM,IAAM,CAAC,CAAC,EAE9F,EAAG,CAACF,EAAMI,CAAQ,CAAC,EAenB,OACIN,GAAC,KAAE,KAAME,EAAM,QAdwCY,GAAM,CAC7D,GAAKJ,GACD,CAACI,EAAE,SAAW,CAACA,EAAE,SAAW,CAACA,EAAE,UAAYA,EAAE,SAAW,EAAG,CAC3DA,EAAE,eAAe,EACjB,IAAMV,EAAWH,EAAYC,CAAI,EAC7BK,GAAkB,OAAO,SAAS,qBAAwB,WAC1D,SAAS,oBAAoB,IAAMG,EAAO,SAASN,CAAQ,CAAC,EAE5DM,EAAO,SAASN,CAAQ,CAEhC,CACJ,EAGyC,aAAcO,EAAmB,GAAGF,EACpE,SAAAD,EACL,CAER,CCzCO,SAASO,GAAUC,EAAcC,EAAkC,CACtE,IAAMC,EAASF,EAAI,QAAQ,IAAI,QAAQ,EACvC,GAAKE,EACL,QAAWC,KAAQD,EAAO,MAAM,GAAG,EAAG,CAClC,GAAM,CAACE,EAAK,GAAGC,CAAI,EAAIF,EAAK,KAAK,EAAE,MAAM,GAAG,EAC5C,GAAIC,EAAI,KAAK,IAAMH,EAAM,OAAO,mBAAmBI,EAAK,KAAK,GAAG,CAAC,CACrE,CAEJ,CAEO,SAASC,EAAUC,EAAkBN,EAAcO,EAAeC,EAAyB,CAAC,EAAS,CACxG,IAAIC,EAAS,GAAGT,CAAI,IAAI,mBAAmBO,CAAK,CAAC,UAAUC,EAAQ,MAAQ,GAAG,GAC1EA,EAAQ,SAAoBC,GAAU,YAAYD,EAAQ,MAAM,IAChEA,EAAQ,SAAW,SAAWC,GAAU,aAAaD,EAAQ,MAAM,IACnEA,EAAQ,UAAoBC,GAAU,aAAaD,EAAQ,QAAQ,YAAY,CAAC,IAChFA,EAAQ,WAAoBC,GAAU,cACtCD,EAAQ,SAAoBC,GAAU,YACtCD,EAAQ,WAAoBC,GAAU,cAAcD,EAAQ,QAAQ,IACxEF,EAAQ,OAAO,aAAcG,CAAM,CACvC,CAEO,SAASC,GAAaJ,EAAkBN,EAAcQ,EAAkD,CAAC,EAAS,CACrHH,EAAUC,EAASN,EAAM,GAAI,CAAC,GAAGQ,EAAS,OAAQ,EAAG,QAAS,IAAI,KAAK,CAAC,CAAC,CAAC,CAC9E,CC/BO,IAAMG,GAAO,CAAIC,EAASC,EAAS,MACtC,SAAS,KAAKD,EAAM,CAAC,OAAAC,CAAM,CAAC,EAEnBC,GAAO,CAACC,EAAcF,EAAS,MACxC,IAAI,SAASE,EAAM,CAAC,OAAAF,EAAQ,QAAS,CAAC,eAAgB,2BAA2B,CAAC,CAAC,EAE1EG,GAAW,CAACC,EAAaJ,EAAS,MAC3C,IAAI,SAAS,KAAM,CAAC,OAAAA,EAAQ,QAAS,CAAC,SAAUI,CAAG,CAAC,CAAC,ECSlD,IAAMC,EAAN,cAAyB,KAAM,CAClC,YACoBC,EACAC,EACAC,EAClB,CACE,MAAM,QAAQF,CAAM,KAAKC,CAAU,EAAE,EAJrB,YAAAD,EACA,gBAAAC,EACA,cAAAC,EAGhB,KAAK,KAAO,YAChB,CACJ,EAEA,eAAsBC,GAGpBC,EAASC,EAAuD,CAC9D,IAAMC,EAAUD,GAAS,QAAU,MAC7BE,EAAU,IAAI,QAAQF,GAAS,OAAO,EAExCG,EACAH,GAAS,OAAS,SAClBG,EAAO,KAAK,UAAUH,EAAQ,IAAI,EAC7BE,EAAQ,IAAI,cAAc,GAC3BA,EAAQ,IAAI,eAAgB,kBAAkB,GAItD,IAAML,EAAW,MAAM,MAAME,EAAM,CAAC,OAAAE,EAAQ,QAAAC,EAAS,KAAAC,EAAM,OAAQH,GAAS,MAAM,CAAC,EAEnF,GAAI,CAACH,EAAS,GACV,MAAM,IAAIH,EAAWG,EAAS,OAAQA,EAAS,WAAYA,CAAQ,EAIvE,OADoBA,EAAS,QAAQ,IAAI,cAAc,GAAK,IAC5C,SAAS,kBAAkB,EAChCA,EAAS,KAAK,EAGlBA,EAAS,KAAK,CACzB",
6
- "names": ["useCallback", "useContext", "useEffect", "useRef", "useState", "RouterContext", "getDefaultErrorPage", "loadErrorPage", "matchClientRoute", "Fragment", "jsx", "collectTags", "metadata", "viewport", "tags", "ogTitle", "ogDesc", "twTitle", "twDesc", "lang", "href", "parts", "buildHeadNodes", "t", "i", "createContext", "g", "RouterContext", "PageMetaContext", "RouteDataContext", "Component", "jsx", "DevixErrorBoundary", "err", "DevixError", "statusCode", "message", "jsx", "jsxs", "useRouter", "useContext", "RouterContext", "useNavigate", "ctx", "useParams", "RouteDataContext", "useLoaderData", "RouterProvider", "initialData", "initialParams", "initialPage", "initialLayouts", "initialLayoutsData", "initialMeta", "initialViewport", "initialError", "initialErrorPage", "clientEntry", "state", "setState", "useState", "navigatingRef", "useRef", "isNavigating", "setIsNavigating", "loadRoute", "useCallback", "to", "controller", "pathname", "matched", "matchClientRoute", "ErrorPage", "loadErrorPage", "getDefaultErrorPage", "prev", "pageMod", "layoutMods", "l", "dataRes", "data", "navigate", "useEffect", "handlePop", "err", "content", "tree", "Layout", "layoutData", "DevixErrorBoundary", "PageMetaContext", "buildHeadNodes", "useCallback", "useContext", "matchClientRoute", "RouterContext", "jsx", "resolveHref", "href", "base", "resolved", "Link", "prefetch", "viewTransition", "children", "props", "router", "handleMouseEnter", "pathname", "matched", "e", "getCookie", "req", "name", "header", "part", "key", "rest", "setCookie", "headers", "value", "options", "cookie", "deleteCookie", "json", "data", "status", "text", "body", "redirect", "url", "FetchError", "status", "statusText", "response", "$fetch", "path", "options", "method", "headers", "body"]
3
+ "sources": ["../../src/runtime/router-provider.tsx", "../../src/runtime/head.tsx", "../../src/runtime/context.tsx", "../../src/runtime/error-boundary.tsx", "../../src/runtime/link.tsx", "../../src/utils/cookies.ts", "../../src/utils/response.ts", "../../src/runtime/fetch.ts", "../../src/runtime/create-handler.ts", "../../src/server/handler-store.ts"],
4
+ "sourcesContent": ["import {ComponentType, ReactNode, useCallback, useContext, useEffect, useRef, useState} from \"react\";\nimport {RouterContext} from 'virtual:devix/context'\nimport {ErrorProps, LayoutProps, PageProps} from \"../server/types\";\nimport {Metadata, Viewport} from \"../types\";\nimport {getDefaultErrorPage, loadErrorPage, matchClientRoute} from \"virtual:devix/client-routes\";\nimport {buildHeadNodes} from \"./head\";\nimport {PageMetaContext, RouteDataContext} from \"./context\";\nimport {DevixErrorBoundary} from \"./error-boundary\";\n\ninterface RouteState {\n pathname: string\n params: Record<string, string>\n loaderData: unknown\n layoutsData: unknown[]\n Page: ComponentType<PageProps>\n layouts: ComponentType<LayoutProps>[]\n metadata: Metadata | null\n viewport?: Viewport\n pendingError?: ErrorProps\n ErrorPage?: ComponentType<ErrorProps>\n}\n\nexport function useRouter() {\n return useContext(RouterContext)\n}\n\nexport function useNavigate() {\n const ctx = useContext(RouterContext)\n if (!ctx) throw new Error(\"useNavigate must be used within a RouterProvider\")\n return ctx.navigate\n}\n\nexport function useParams<T extends Record<string, string>>() {\n const ctx = useContext(RouteDataContext)\n if (!ctx) throw new Error(\"useParams must be used within a route or layout\")\n return ctx.params as T\n}\n\ntype LoaderReturnType<T> = T extends (...args: any[]) => Promise<infer R>\n ? R\n : T extends (...args: any[]) => infer R\n ? R\n : T\n\nexport function useLoaderData<T>() {\n const ctx = useContext(RouteDataContext)\n if (!ctx) throw new Error(\"useLoaderData must be used within a route or layout\")\n return ctx.loaderData as LoaderReturnType<T>\n}\n\n\ninterface RouterProviderProps {\n initialData: unknown\n initialParams: Record<string, string>\n initialPage: ComponentType<PageProps>\n initialLayouts?: ComponentType<LayoutProps>[]\n initialLayoutsData?: unknown[]\n initialMeta?: Metadata | null\n initialViewport?: Viewport\n initialError?: ErrorProps\n initialErrorPage?: ComponentType<ErrorProps>\n clientEntry: string\n}\n\nexport function RouterProvider({\n initialData,\n initialParams,\n initialPage,\n initialLayouts = [],\n initialLayoutsData = [],\n initialMeta,\n initialViewport,\n initialError,\n initialErrorPage,\n clientEntry,\n }: RouterProviderProps) {\n\n const [state, setState] = useState<RouteState>({\n pathname: window.location.pathname,\n params: initialParams,\n loaderData: initialData,\n layoutsData: initialLayoutsData,\n Page: initialPage,\n layouts: initialLayouts,\n metadata: initialMeta ?? null,\n viewport: initialViewport,\n pendingError: initialError,\n ErrorPage: initialErrorPage,\n })\n\n const navigatingRef = useRef<AbortController | null>(null)\n const [isNavigating, setIsNavigating] = useState(false)\n\n const loadRoute = useCallback(async (to: string, controller: AbortController) => {\n const pathname = to.split('?')[0]\n const matched = matchClientRoute(pathname)\n if (!matched) {\n const ErrorPage = await loadErrorPage() ?? getDefaultErrorPage()\n setState(prev => ({\n ...prev,\n pathname: pathname,\n pendingError: {statusCode: 404, message: 'Not found'},\n ErrorPage: ErrorPage ?? undefined,\n }))\n return\n }\n\n const [pageMod, ...layoutMods] = await Promise.all([\n matched.load(),\n ...matched.loadLayouts.map(l => l()),\n ])\n\n if (controller.signal.aborted) return\n if (!pageMod.default) return\n\n const dataRes = await fetch(`/_data${to}`, {\n headers: {Accept: 'application/json'},\n signal: controller.signal,\n })\n\n if (controller.signal.aborted) return\n\n if (!dataRes.ok) {\n if (dataRes.status === 404) {\n window.location.href = to\n return\n }\n console.error(`/_data${to} returned ${dataRes.status}`)\n return\n }\n\n const data = await dataRes.json()\n\n window.scrollTo(0, 0)\n setState({\n pathname,\n params: data.params ?? {},\n loaderData: data.loaderData,\n layoutsData: (data.layouts ?? []).map((l: any) => l.loaderData),\n Page: pageMod.default,\n layouts: layoutMods.map(m => m.default),\n metadata: data.metadata ?? null,\n viewport: data.viewport,\n })\n }, [])\n\n const navigate = useCallback(async (to: string) => {\n navigatingRef.current?.abort()\n const controller = new AbortController()\n navigatingRef.current = controller\n\n setIsNavigating(true)\n try {\n window.history.pushState(null, \"\", to)\n await loadRoute(to, controller)\n } finally {\n if (!controller.signal.aborted) setIsNavigating(false)\n }\n }, [loadRoute])\n\n useEffect(() => {\n const handlePop = () => {\n navigatingRef.current?.abort()\n const controller = new AbortController()\n navigatingRef.current = controller\n\n const to = window.location.pathname + window.location.search\n loadRoute(to, controller).catch(err => {\n if (err.name !== 'AbortError') console.error('[router] popstate error:', err)\n })\n }\n window.addEventListener(\"popstate\", handlePop)\n return () => window.removeEventListener(\"popstate\", handlePop)\n }, [loadRoute])\n\n let content: ReactNode\n\n if (state.pendingError) {\n content = state.ErrorPage\n ? <state.ErrorPage {...state.pendingError} />\n : <h1>{state.pendingError.statusCode}</h1>\n } else {\n let tree: ReactNode = (\n <RouteDataContext value={{loaderData: state.loaderData, params: state.params}}>\n <state.Page data={state.loaderData} params={state.params} url={state.pathname}/>\n </RouteDataContext>\n )\n\n for (let i = state.layouts.length - 1; i >= 0; i--) {\n const Layout = state.layouts[i]\n const layoutData = state.layoutsData[i]\n tree = (\n <RouteDataContext value={{loaderData: layoutData, params: state.params}}>\n <Layout data={layoutData} params={state.params}>{tree}</Layout>\n </RouteDataContext>\n )\n }\n\n content = (\n <DevixErrorBoundary key={state.pathname} ErrorPage={state.ErrorPage}>\n {tree}\n </DevixErrorBoundary>\n )\n }\n\n return (\n <PageMetaContext value={{\n metadata: state.metadata,\n viewport: state.viewport,\n clientEntry,\n }}>\n {state.metadata && buildHeadNodes(state.metadata, state.viewport)}\n <RouterContext value={{...state, isNavigating, navigate}}>\n {content}\n </RouterContext>\n </PageMetaContext>\n )\n}", "import {Metadata, Viewport} from \"../types\";\nimport {ReactNode} from \"react\";\n\ntype MetaTag =\n | { tag: 'title'; children: string }\n | { tag: 'meta'; name?: string; property?: string; content: string }\n | { tag: 'link'; rel: string; href: string; hrefLang?: string }\n\nfunction collectTags(metadata: Metadata, viewport?: Viewport): MetaTag[] {\n const tags: MetaTag[] = []\n\n if (metadata.title)\n tags.push({tag: 'title', children: metadata.title})\n if (metadata.description)\n tags.push({tag: 'meta', name: 'description', content: metadata.description})\n if (metadata.keywords?.length)\n tags.push({tag: 'meta', name: 'keywords', content: metadata.keywords.join(', ')})\n\n const ogTitle = metadata.og?.title ?? metadata.title\n if (ogTitle) tags.push({tag: 'meta', property: 'og:title', content: ogTitle})\n const ogDesc = metadata.og?.description ?? metadata.description\n if (ogDesc) tags.push({tag: 'meta', property: 'og:description', content: ogDesc})\n if (metadata.og?.image) tags.push({tag: 'meta', property: 'og:image', content: metadata.og.image})\n if (metadata.og?.type) tags.push({tag: 'meta', property: 'og:type', content: metadata.og.type})\n if (metadata.og?.url) tags.push({tag: 'meta', property: 'og:url', content: metadata.og.url})\n\n const twTitle = metadata.twitter?.title ?? metadata.title\n if (twTitle) tags.push({tag: 'meta', name: 'twitter:title', content: twTitle})\n const twDesc = metadata.twitter?.description ?? metadata.description\n if (twDesc) tags.push({tag: 'meta', name: 'twitter:description', content: twDesc})\n if (metadata.twitter?.card) tags.push({\n tag: 'meta', name: 'twitter:card', content:\n metadata.twitter.card\n })\n if (metadata.twitter?.image) tags.push({\n tag: 'meta', name: 'twitter:image', content:\n metadata.twitter.image\n })\n if (metadata.twitter?.creator) tags.push({\n tag: 'meta', name: 'twitter:creator', content:\n metadata.twitter.creator\n })\n\n if (metadata.canonical) tags.push({tag: 'link', rel: 'canonical', href: metadata.canonical})\n if (metadata.robots) tags.push({tag: 'meta', name: 'robots', content: metadata.robots})\n if (metadata.alternates) {\n for (const [lang, href] of Object.entries(metadata.alternates))\n tags.push({tag: 'link', rel: 'alternate', href, hrefLang: lang})\n }\n\n if (viewport) {\n const parts: string[] = []\n if (viewport.width !== undefined) parts.push(`width=${viewport.width}`)\n if (viewport.initialScale !== undefined) parts.push(`initial-scale=${viewport.initialScale}`)\n if (viewport.maximumScale !== undefined) parts.push(`maximum-scale=${viewport.maximumScale}`)\n if (viewport.userScalable !== undefined) parts.push(`user-scalable=${viewport.userScalable ? 'yes' :\n 'no'}`)\n if (parts.length) tags.push({tag: 'meta', name: 'viewport', content: parts.join(', ')})\n if (viewport.themeColor) tags.push({\n tag: 'meta', name: 'theme-color', content: viewport.themeColor\n })\n }\n\n return tags\n}\n\nexport function buildHeadNodes(metadata: Metadata, viewport?: Viewport): ReactNode {\n const tags = collectTags(metadata, viewport)\n\n return <>\n {tags.map((t, i) => {\n if (t.tag === 'title') return <title key={i}>{t.children}</title>\n if (t.tag === 'link') return <link key={i} rel={t.rel} href={t.href} hrefLang={t.hrefLang}/>\n return <meta key={i} name={t.name} property={t.property} content={t.content}/>\n })}\n </>\n}", "import {createContext, Context, ComponentType} from \"react\";\nimport {Metadata, Viewport} from \"../types\";\nimport {LayoutProps, PageProps} from \"../server/types\";\n\nexport interface RouterContextValue {\n pathname: string\n params: Record<string, string>\n loaderData: unknown\n layoutsData: unknown[]\n Page: ComponentType<PageProps>\n layouts: ComponentType<LayoutProps>[]\n metadata: Metadata | null\n viewport?: Viewport\n navigate: (to: string) => void\n isNavigating: boolean\n}\n\nexport interface PageMetaContextValue {\n metadata: Metadata | null\n viewport?: Viewport\n clientEntry?: string\n}\n\nexport interface RouteDataContextValue {\n loaderData: unknown\n params: Record<string, string>\n}\n\nconst g = globalThis as any\n\ng.__devix_RouterContext__ ??= createContext<RouterContextValue | null>(null)\nexport const RouterContext: Context<RouterContextValue | null> = g.__devix_RouterContext__\n\ng.__devix_PageMetaContext__ ??= createContext<PageMetaContextValue | null>(null)\ng.__devix_RouteDataContext__ ??= createContext<RouteDataContextValue | null>(null)\n\nexport const PageMetaContext: Context<PageMetaContextValue | null> = g.__devix_PageMetaContext__\nexport const RouteDataContext: Context<RouteDataContextValue | null> = g.__devix_RouteDataContext__\n\n", "import {Component, ComponentType, ReactNode} from \"react\";\nimport {ErrorProps} from \"../server/types\";\n\ninterface Props {\n ErrorPage?: ComponentType<ErrorProps>\n children: ReactNode\n}\n\ninterface State {\n error: ErrorProps | null\n}\n\nexport class DevixErrorBoundary extends Component<Props, State> {\n state: State = { error: null }\n\n static getDerivedStateFromError(err: unknown): State {\n if (err instanceof DevixError) {\n return {\n error: {statusCode: err.statusCode, message: err.message}\n }\n }\n return {\n error: {statusCode: 500, message: err instanceof Error ? err.message : 'Unknown error'}\n }\n }\n\n render() {\n if (this.state.error && this.props.ErrorPage) {\n return <this.props.ErrorPage {...this.state.error} />\n }\n if (this.state.error) {\n return <h1>{this.state.error.statusCode}</h1>\n }\n return this.props.children\n }\n}\n\nexport class DevixError extends Error {\n statusCode: number\n constructor(statusCode: number, message: string) {\n super(message)\n this.statusCode = statusCode\n }\n}\n", "import {AnchorHTMLAttributes, MouseEventHandler, useCallback, useContext} from \"react\";\nimport {matchClientRoute} from \"virtual:devix/client-routes\";\nimport {RouterContext} from 'virtual:devix/context'\n\ninterface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {\n href: string\n prefetch?: boolean\n viewTransition?: boolean\n}\n\nfunction resolveHref(href: string): string {\n if (href.startsWith('/') || href.startsWith('http')) return href\n const base = window.location.pathname.endsWith('/')\n ? window.location.href\n : window.location.href + '/'\n const resolved = new URL(href, base).pathname\n return resolved.length > 1 ? resolved.replace(/\\/$/, '') : resolved\n}\n\nexport function Link({ href, prefetch = false, viewTransition = false, children, ...props }: LinkProps) {\n const router = useContext(RouterContext)\n\n const handleMouseEnter = useCallback(() => {\n if (!prefetch) return\n const resolved = resolveHref(href)\n const pathname = resolved.split('?')[0]\n const matched = matchClientRoute(pathname)\n if (matched) {\n matched.load().catch(() => {})\n fetch(`/_data${resolved}`, { headers: { Accept: 'application/json' } }).catch(() => {})\n }\n }, [href, prefetch])\n\n const handleClick: MouseEventHandler<HTMLAnchorElement> = (e) => {\n if (!router) return\n if (!e.ctrlKey && !e.metaKey && !e.shiftKey && e.button === 0) {\n e.preventDefault()\n const resolved = resolveHref(href)\n if (viewTransition && typeof document.startViewTransition === 'function') {\n document.startViewTransition(() => router.navigate(resolved))\n } else {\n router.navigate(resolved)\n }\n }\n }\n\n return (\n <a href={href} onClick={handleClick} onMouseEnter={handleMouseEnter} {...props}>\n {children}\n </a>\n )\n}", "export interface CookieOptions {\n httpOnly?: boolean\n secure?: boolean\n sameSite?: 'Strict' | 'Lax' | 'None'\n maxAge?: number\n expires?: Date\n path?: string\n domain?: string\n}\n\nexport function getCookie(req: Request, name: string): string | undefined {\n const header = req.headers.get('cookie')\n if (!header) return undefined\n for (const part of header.split(';')) {\n const [key, ...rest] = part.trim().split('=')\n if (key.trim() === name) return decodeURIComponent(rest.join('='))\n }\n return undefined\n}\n\nexport function setCookie(headers: Headers, name: string, value: string, options: CookieOptions = {}): void {\n let cookie = `${name}=${encodeURIComponent(value)}; Path=${options.path ?? '/'}`\n if (options.domain) cookie += `; Domain=${options.domain}`\n if (options.maxAge !== undefined) cookie += `; Max-Age=${options.maxAge}`\n if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`\n if (options.httpOnly) cookie += `; HttpOnly`\n if (options.secure) cookie += `; Secure`\n if (options.sameSite) cookie += `; SameSite=${options.sameSite}`\n headers.append('Set-Cookie', cookie)\n}\n\nexport function deleteCookie(headers: Headers, name: string, options: Pick<CookieOptions, 'path' | 'domain'> = {}): void {\n setCookie(headers, name, '', {...options, maxAge: 0, expires: new Date(0)})\n}\n", "export type JsonResponse<T = unknown> = Response & { readonly __body: T }\n\nexport const json = <const T>(data: T, status = 200): JsonResponse<T> =>\n Response.json(data, {status}) as JsonResponse<T>\n\nexport const text = (body: string, status = 200): Response =>\n new Response(body, {status, headers: {'Content-Type': 'text/plain; charset=utf-8'}})\n\nexport const redirect = (url: string, status = 302): Response =>\n new Response(null, {status, headers: {Location: url}})\n", "export interface ApiRoutes {}\n\ntype HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'\n\ntype ApiKey<M extends HttpMethod, P extends string> = `${M} ${P}`\n\ntype RouteData<M extends HttpMethod, P extends string> =\n ApiKey<M, P> extends keyof ApiRoutes ? ApiRoutes[ApiKey<M, P>] : unknown\n\ntype ExtractBody<D> = D extends {__body: infer B} ? B : never\ntype ExtractResponse<D> = D extends {__response: infer R} ? R : D\n\ntype InferBody<M extends HttpMethod, P extends string> = ExtractBody<RouteData<M, P>>\ntype InferResult<M extends HttpMethod, P extends string> = ExtractResponse<RouteData<M, P>>\n\ntype BodyOption<M extends HttpMethod, P extends string> =\n [InferBody<M, P>] extends [never] ? unknown : InferBody<M, P>\n\nexport interface FetchOptions<M extends HttpMethod = 'GET', P extends string = string> {\n method?: M\n body?: BodyOption<M, P>\n headers?: HeadersInit\n signal?: AbortSignal\n}\n\nexport class FetchError extends Error {\n constructor(\n public readonly status: number,\n public readonly statusText: string,\n public readonly response: Response,\n ) {\n super(`HTTP ${status}: ${statusText}`)\n this.name = 'FetchError'\n }\n}\n\nexport async function $fetch<\n P extends string,\n M extends HttpMethod = 'GET',\n>(path: P, options?: FetchOptions<M, P>): Promise<InferResult<M, P>> {\n const method = (options?.method ?? 'GET') as string\n const headers = new Headers(options?.headers)\n\n let body: BodyInit | undefined\n if (options?.body !== undefined) {\n body = JSON.stringify(options.body)\n if (!headers.has('Content-Type')) {\n headers.set('Content-Type', 'application/json')\n }\n }\n\n const response = await fetch(path, {method, headers, body, signal: options?.signal})\n\n if (!response.ok) {\n throw new FetchError(response.status, response.statusText, response)\n }\n\n const contentType = response.headers.get('Content-Type') ?? ''\n if (contentType.includes('application/json')) {\n return response.json() as Promise<InferResult<M, P>>\n }\n\n return response.text() as unknown as Promise<InferResult<M, P>>\n}\n", "import type {RouteResult} from './api-context'\n\nexport const HANDLER_BRAND = '__devix_handler__' as const\n\nexport interface DevixHandler<TBody = undefined, TReturn = RouteResult> {\n readonly [HANDLER_BRAND]: true\n readonly fn: (...args: any[]) => any\n readonly __body?: TBody\n readonly __return?: TReturn\n}\n\nexport function createHandler<TReturn extends RouteResult = RouteResult>(\n fn: () => Promise<TReturn> | TReturn,\n): DevixHandler<undefined, TReturn>\n\nexport function createHandler<TBody, TReturn extends RouteResult = RouteResult>(\n fn: (body: TBody) => Promise<TReturn> | TReturn,\n): DevixHandler<TBody, TReturn>\n\nexport function createHandler(fn: (...args: any[]) => any): DevixHandler<any, any> {\n return {[HANDLER_BRAND]: true, fn}\n}\n", "import {AsyncLocalStorage} from 'node:async_hooks'\nimport type {RouteContext} from '../runtime/api-context'\n\ninterface HandlerStore {\n request: Request\n ctx: RouteContext\n}\n\nconst storage = new AsyncLocalStorage<HandlerStore>()\n\nexport function withHandlerStore<T>(store: HandlerStore, fn: () => T): T {\n return storage.run(store, fn)\n}\n\nfunction getStore(hookName: string): HandlerStore {\n const store = storage.getStore()\n if (!store) throw new Error(`[devix] ${hookName}() called outside of a request handler`)\n return store\n}\n\nexport function useRequest(): Request {\n return getStore('useRequest').request\n}\n\nexport function useCtx(): RouteContext {\n return getStore('useCtx').ctx\n}\n\nexport function useParams(): Record<string, string> {\n return getStore('useParams').ctx.params\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["useCallback", "useContext", "useEffect", "useRef", "useState", "RouterContext", "getDefaultErrorPage", "loadErrorPage", "matchClientRoute", "Fragment", "jsx", "collectTags", "metadata", "viewport", "tags", "ogTitle", "ogDesc", "twTitle", "twDesc", "lang", "href", "parts", "buildHeadNodes", "t", "i", "createContext", "g", "RouterContext", "PageMetaContext", "RouteDataContext", "Component", "jsx", "DevixErrorBoundary", "err", "DevixError", "statusCode", "message", "jsx", "jsxs", "useRouter", "useContext", "RouterContext", "useNavigate", "ctx", "useParams", "RouteDataContext", "useLoaderData", "RouterProvider", "initialData", "initialParams", "initialPage", "initialLayouts", "initialLayoutsData", "initialMeta", "initialViewport", "initialError", "initialErrorPage", "clientEntry", "state", "setState", "useState", "navigatingRef", "useRef", "isNavigating", "setIsNavigating", "loadRoute", "useCallback", "to", "controller", "pathname", "matched", "matchClientRoute", "ErrorPage", "loadErrorPage", "getDefaultErrorPage", "prev", "pageMod", "layoutMods", "l", "dataRes", "data", "navigate", "useEffect", "handlePop", "err", "content", "tree", "Layout", "layoutData", "DevixErrorBoundary", "PageMetaContext", "buildHeadNodes", "useCallback", "useContext", "matchClientRoute", "RouterContext", "jsx", "resolveHref", "href", "base", "resolved", "Link", "prefetch", "viewTransition", "children", "props", "router", "handleMouseEnter", "pathname", "matched", "e", "getCookie", "req", "name", "header", "part", "key", "rest", "setCookie", "headers", "value", "options", "cookie", "deleteCookie", "json", "data", "status", "text", "body", "redirect", "url", "FetchError", "status", "statusText", "response", "$fetch", "path", "options", "method", "headers", "body", "HANDLER_BRAND", "createHandler", "fn", "AsyncLocalStorage", "storage", "getStore", "hookName", "store", "storage", "useRequest", "useCtx"]
7
7
  }
@@ -1,2 +1,2 @@
1
- function h(o){return o.replace(/\.(tsx|ts|jsx|js)$/,"").replace(/\(.*?\)\//g,"").replace(/^index$|\/index$/,"").replace(/\[([^\]]+)]/g,":$1")||"/"}function k(o,t){let r=o.slice(t.length+1).replace(/\\/g,"/"),e=h(r);return e==="/"?"/api":`/api/${e}`.replace("/api//","/api/")}function w(o){return o.slice(0,o.lastIndexOf("/"))}var c=null;function x(o,t,r){if(c)return c;let e=[],i=[];for(let n of t)i.push({dir:w(n),key:n});for(let n of o){let s=k(n,r),a=[...s.matchAll(/:([^/]+)/g)].map(p=>p[1]),u=s.replace(/:[^/]+/g,"([^/]+)").replace(/\//g,"\\/");e.push({path:s,key:n,params:a,regex:new RegExp(`^${u}$`)})}return e.sort((n,s)=>{let a=(n.path.match(/:/g)||[]).length,u=(s.path.match(/:/g)||[]).length;return a!==u?a-u:s.path.length-n.path.length}),c={routes:e,middlewares:i},c}function y(o,t){let r=w(o);return t.filter(e=>r.startsWith(e.dir)).sort((e,i)=>e.dir.split("/").length-i.dir.split("/").length)}function A(o,t){for(let r of t){let e=o.match(r.regex);if(e){let i={};return r.params.forEach((n,s)=>{i[n]=decodeURIComponent(e[s+1])}),{route:r,params:i}}}return null}var d=class{params;_state=new Map;constructor(t={}){this.params=t}set(t,r){this._state.set(t,r)}get(t){return this._state.get(t)}};import{Component as v}from"react";import{jsx as b}from"react/jsx-runtime";var m=class extends Error{statusCode;constructor(t,r){super(r),this.statusCode=t}};async function _(o,t,r){try{let{pathname:e}=new URL(o,"http://localhost"),{routes:i,middlewares:n}=x(Object.keys(r.routes),Object.keys(r.middlewares),r.apiDir),s=A(e,i);if(!s)return new Response("Not Found",{status:404});let{route:a,params:u}=s,p=new d(u),C=y(a.key,n);for(let E of C){let R=await r.middlewares[E.key]();if(R.middleware){let f=await R.middleware(p,t);if(f instanceof Response)return f}}let M=await r.routes[a.key](),P=t.method.toUpperCase(),g=M[P];if(!g)return new Response("Method Not Allowed",{status:405});let l=await g(p,t);return l instanceof Response?l:l==null?new Response(null,{status:204}):new Response(JSON.stringify(l),{headers:{"Content-Type":"application/json"}})}catch(e){return console.error("[devix] api error:",e),e instanceof m?new Response(e.message,{status:e.statusCode}):new Response("Internal Server Error",{status:500})}}export{_ as handleApiRequest};
1
+ function x(e){return e.replace(/\.(tsx|ts|jsx|js)$/,"").replace(/\(.*?\)\//g,"").replace(/^index$|\/index$/,"").replace(/\[([^\]]+)]/g,":$1")||"/"}function S(e,t){let n=e.slice(t.length+1).replace(/\\/g,"/"),r=x(n);return r==="/"?"/api":`/api/${r}`.replace("/api//","/api/")}function y(e){return e.slice(0,e.lastIndexOf("/"))}var d=null;function h(e,t,n){if(d)return d;let r=[],a=[];for(let o of t)a.push({dir:y(o),key:o});for(let o of e){let s=S(o,n),i=[...s.matchAll(/:([^/]+)/g)].map(p=>p[1]),u=s.replace(/:[^/]+/g,"([^/]+)").replace(/\//g,"\\/");r.push({path:s,key:o,params:i,regex:new RegExp(`^${u}$`)})}return r.sort((o,s)=>{let i=(o.path.match(/:/g)||[]).length,u=(s.path.match(/:/g)||[]).length;return i!==u?i-u:s.path.length-o.path.length}),d={routes:r,middlewares:a},d}function w(e,t){let n=y(e);return t.filter(r=>n.startsWith(r.dir)).sort((r,a)=>r.dir.split("/").length-a.dir.split("/").length)}function A(e,t){for(let n of t){let r=e.match(n.regex);if(r){let a={};return n.params.forEach((o,s)=>{a[o]=decodeURIComponent(r[s+1])}),{route:n,params:a}}}return null}var c=class{params;_state=new Map;constructor(t={}){this.params=t}set(t,n){this._state.set(t,n)}get(t){return this._state.get(t)}};import{Component as O}from"react";import{jsx as G}from"react/jsx-runtime";var R=class extends Error{statusCode;constructor(t,n){super(n),this.statusCode=t}};var T="__devix_handler__";import{AsyncLocalStorage as M}from"node:async_hooks";var k=new M;function H(e,t){return k.run(e,t)}function v(e){return typeof e=="object"&&e!==null&&T in e}async function _(e){let t=e.headers.get("Content-Type")??"";return t.includes("application/json")?e.json():t.includes("multipart/form-data")||t.includes("application/x-www-form-urlencoded")?e.formData():e.text()}function N(e){return e instanceof Response?e:e==null?new Response(null,{status:204}):new Response(JSON.stringify(e),{headers:{"Content-Type":"application/json"}})}async function Z(e,t,n){try{let{pathname:r}=new URL(e,"http://localhost"),{routes:a,middlewares:o}=h(Object.keys(n.routes),Object.keys(n.middlewares),n.apiDir),s=A(r,a);if(!s)return new Response("Not Found",{status:404});let{route:i,params:u}=s,p=new c(u),C=await H({request:t,ctx:p},async()=>{let P=w(i.key,o);for(let f of P){let m=await n.middlewares[f.key]();if(m.middleware){let g=await m.middleware(p,t);if(g instanceof Response)return g}}let D=await n.routes[i.key](),E=t.method.toUpperCase(),l=D[E];if(!l)return new Response("Method Not Allowed",{status:405});if(v(l)){if(l.fn.length===0)return l.fn();let f=await _(t);return l.fn(f)}return l(p,t)});return N(C)}catch(r){return console.error("[devix] api error:",r),r instanceof R?new Response(r.message,{status:r.statusCode}):new Response("Internal Server Error",{status:500})}}export{Z as handleApiRequest};
2
2
  //# sourceMappingURL=api.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/utils/patterns.ts", "../../src/server/api-router.ts", "../../src/runtime/api-context.ts", "../../src/runtime/error-boundary.tsx", "../../src/server/api.ts"],
4
- "sourcesContent": ["export function routePattern(rel: string): string {\n return rel\n .replace(/\\.(tsx|ts|jsx|js)$/, '')\n .replace(/\\(.*?\\)\\//g, '')\n .replace(/^index$|\\/index$/, '')\n .replace(/\\[([^\\]]+)]/g, ':$1')\n || '/'\n}", "import {routePattern} from \"../utils/patterns\";\n\nexport interface ApiRoute {\n path: string\n key: string\n params: string[]\n regex: RegExp\n}\n\nexport interface ApiMiddleware {\n dir: string\n key: string\n}\n\nexport interface ApiResult {\n routes: ApiRoute[]\n middlewares: ApiMiddleware[]\n}\n\nexport function keyToRoutePattern(key: string, apiDir: string): string {\n const rel = key.slice(apiDir.length + 1).replace(/\\\\/g, '/')\n const pattern = routePattern(rel)\n return pattern === '/' ? '/api' : `/api/${pattern}`.replace('/api//', '/api/')\n}\n\nfunction keyToDir(key: string): string {\n return key.slice(0, key.lastIndexOf('/'))\n}\n\nlet cache: ApiResult | null = null\n\nexport function invalidateApiCache() {\n cache = null\n}\n\nexport function buildRoutes(routeKeys: string[], middlewareKeys: string[], apiDir: string): ApiResult {\n if (cache) return cache\n\n const routes: ApiRoute[] = []\n const middlewares: ApiMiddleware[] = []\n\n for (const key of middlewareKeys) {\n middlewares.push({dir: keyToDir(key), key})\n }\n\n for (const key of routeKeys) {\n const pattern = keyToRoutePattern(key, apiDir)\n const params = [...pattern.matchAll(/:([^/]+)/g)].map(m => m[1])\n const regexStr = pattern\n .replace(/:[^/]+/g, '([^/]+)')\n .replace(/\\//g, '\\\\/')\n routes.push({path: pattern, key, params, regex: new RegExp(`^${regexStr}$`)})\n }\n routes.sort((a, b) => {\n const aScore = (a.path.match(/:/g) || []).length\n const bScore = (b.path.match(/:/g) || []).length\n if (aScore !== bScore) return aScore - bScore\n return b.path.length - a.path.length\n })\n\n cache = {routes, middlewares}\n return cache\n}\n\nexport function collectMiddlewareChain(routeKey: string, middlewares: ApiMiddleware[]): ApiMiddleware[] {\n const routeDir = keyToDir(routeKey)\n\n return middlewares\n .filter(mw => routeDir.startsWith(mw.dir))\n .sort((a, b) => a.dir.split('/').length - b.dir.split('/').length)\n}\n\nexport function matchRoute(\n pathname: string,\n routes: ApiRoute[]\n): {route: ApiRoute; params: Record<string, string>} | null {\n for (const route of routes) {\n const match = pathname.match(route.regex)\n if (match) {\n const params: Record<string, string> = {}\n route.params.forEach((name, i) => {\n params[name] = decodeURIComponent(match[i + 1])\n })\n return {route, params}\n }\n }\n return null\n}\n", "export class RouteContext {\n readonly params: Record<string, string>\n private _state = new Map<string, unknown>()\n\n constructor(params: Record<string, string> = {}) {\n this.params = params\n }\n\n set<T>(key: string, value: T): void {\n this._state.set(key, value)\n }\n\n get<T>(key: string): T | undefined {\n return this._state.get(key) as T\n }\n}\n\nexport type RouteResult = Response | Record<string, unknown> | unknown[] | null | void\n\nexport type RouteHandler = (ctx: RouteContext, req: Request) => Promise<RouteResult> | RouteResult\n\nexport interface MiddlewareModule {\n middleware: (ctx: RouteContext, req: Request) => Promise<Response | null> | Response | null\n}\n\nexport interface RouteModule {\n GET?: RouteHandler\n POST?: RouteHandler\n PUT?: RouteHandler\n PATCH?: RouteHandler\n DELETE?: RouteHandler\n HEAD?: RouteHandler\n OPTIONS?: RouteHandler\n}\n", "import {Component, ComponentType, ReactNode} from \"react\";\nimport {ErrorProps} from \"../server/types\";\n\ninterface Props {\n ErrorPage?: ComponentType<ErrorProps>\n children: ReactNode\n}\n\ninterface State {\n error: ErrorProps | null\n}\n\nexport class DevixErrorBoundary extends Component<Props, State> {\n state: State = { error: null }\n\n static getDerivedStateFromError(err: unknown): State {\n if (err instanceof DevixError) {\n return {\n error: {statusCode: err.statusCode, message: err.message}\n }\n }\n return {\n error: {statusCode: 500, message: err instanceof Error ? err.message : 'Unknown error'}\n }\n }\n\n render() {\n if (this.state.error && this.props.ErrorPage) {\n return <this.props.ErrorPage {...this.state.error} />\n }\n if (this.state.error) {\n return <h1>{this.state.error.statusCode}</h1>\n }\n return this.props.children\n }\n}\n\nexport class DevixError extends Error {\n statusCode: number\n constructor(statusCode: number, message: string) {\n super(message)\n this.statusCode = statusCode\n }\n}\n", "import {buildRoutes, matchRoute, collectMiddlewareChain} from './api-router'\nimport {RouteContext} from '../runtime/api-context'\nimport type {RouteModule, MiddlewareModule} from '../runtime/api-context'\nimport type {ApiGlob} from './types'\nimport {DevixError} from '../runtime/error-boundary'\n\nexport async function handleApiRequest(\n url: string,\n request: Request,\n glob: ApiGlob,\n): Promise<Response> {\n try {\n const {pathname} = new URL(url, 'http://localhost')\n const {routes, middlewares} = buildRoutes(\n Object.keys(glob.routes),\n Object.keys(glob.middlewares),\n glob.apiDir,\n )\n const matched = matchRoute(pathname, routes)\n\n if (!matched) return new Response('Not Found', {status: 404})\n\n const {route, params} = matched\n const ctx = new RouteContext(params)\n\n const middlewareChain = collectMiddlewareChain(route.key, middlewares)\n for (const mw of middlewareChain) {\n const mod = await glob.middlewares[mw.key]() as MiddlewareModule\n if (mod.middleware) {\n const result = await mod.middleware(ctx, request)\n if (result instanceof Response) return result\n }\n }\n\n const mod = await glob.routes[route.key]() as RouteModule\n const method = request.method.toUpperCase() as keyof RouteModule\n const handler = mod[method]\n\n if (!handler) return new Response('Method Not Allowed', {status: 405})\n\n const result = await handler(ctx, request)\n if (result instanceof Response) return result\n if (result == null) return new Response(null, {status: 204})\n\n return new Response(JSON.stringify(result), {\n headers: {'Content-Type': 'application/json'},\n })\n } catch (err) {\n console.error('[devix] api error:', err)\n if (err instanceof DevixError) {\n return new Response(err.message, {status: err.statusCode})\n }\n return new Response('Internal Server Error', {status: 500})\n }\n}"],
5
- "mappings": "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",
6
- "names": ["routePattern", "rel", "keyToRoutePattern", "key", "apiDir", "rel", "pattern", "routePattern", "keyToDir", "cache", "buildRoutes", "routeKeys", "middlewareKeys", "apiDir", "cache", "routes", "middlewares", "key", "keyToDir", "pattern", "keyToRoutePattern", "params", "m", "regexStr", "a", "b", "aScore", "bScore", "collectMiddlewareChain", "routeKey", "routeDir", "mw", "matchRoute", "pathname", "route", "match", "name", "i", "RouteContext", "params", "key", "value", "Component", "jsx", "DevixError", "statusCode", "message", "handleApiRequest", "url", "request", "glob", "pathname", "routes", "middlewares", "buildRoutes", "matched", "matchRoute", "route", "params", "ctx", "RouteContext", "middlewareChain", "collectMiddlewareChain", "mw", "mod", "result", "method", "handler", "err", "DevixError"]
3
+ "sources": ["../../src/utils/patterns.ts", "../../src/server/api-router.ts", "../../src/runtime/api-context.ts", "../../src/runtime/error-boundary.tsx", "../../src/runtime/create-handler.ts", "../../src/server/handler-store.ts", "../../src/server/api.ts"],
4
+ "sourcesContent": ["export function routePattern(rel: string): string {\n return rel\n .replace(/\\.(tsx|ts|jsx|js)$/, '')\n .replace(/\\(.*?\\)\\//g, '')\n .replace(/^index$|\\/index$/, '')\n .replace(/\\[([^\\]]+)]/g, ':$1')\n || '/'\n}", "import {routePattern} from \"../utils/patterns\";\n\nexport interface ApiRoute {\n path: string\n key: string\n params: string[]\n regex: RegExp\n}\n\nexport interface ApiMiddleware {\n dir: string\n key: string\n}\n\nexport interface ApiResult {\n routes: ApiRoute[]\n middlewares: ApiMiddleware[]\n}\n\nexport function keyToRoutePattern(key: string, apiDir: string): string {\n const rel = key.slice(apiDir.length + 1).replace(/\\\\/g, '/')\n const pattern = routePattern(rel)\n return pattern === '/' ? '/api' : `/api/${pattern}`.replace('/api//', '/api/')\n}\n\nfunction keyToDir(key: string): string {\n return key.slice(0, key.lastIndexOf('/'))\n}\n\nlet cache: ApiResult | null = null\n\nexport function invalidateApiCache() {\n cache = null\n}\n\nexport function buildRoutes(routeKeys: string[], middlewareKeys: string[], apiDir: string): ApiResult {\n if (cache) return cache\n\n const routes: ApiRoute[] = []\n const middlewares: ApiMiddleware[] = []\n\n for (const key of middlewareKeys) {\n middlewares.push({dir: keyToDir(key), key})\n }\n\n for (const key of routeKeys) {\n const pattern = keyToRoutePattern(key, apiDir)\n const params = [...pattern.matchAll(/:([^/]+)/g)].map(m => m[1])\n const regexStr = pattern\n .replace(/:[^/]+/g, '([^/]+)')\n .replace(/\\//g, '\\\\/')\n routes.push({path: pattern, key, params, regex: new RegExp(`^${regexStr}$`)})\n }\n routes.sort((a, b) => {\n const aScore = (a.path.match(/:/g) || []).length\n const bScore = (b.path.match(/:/g) || []).length\n if (aScore !== bScore) return aScore - bScore\n return b.path.length - a.path.length\n })\n\n cache = {routes, middlewares}\n return cache\n}\n\nexport function collectMiddlewareChain(routeKey: string, middlewares: ApiMiddleware[]): ApiMiddleware[] {\n const routeDir = keyToDir(routeKey)\n\n return middlewares\n .filter(mw => routeDir.startsWith(mw.dir))\n .sort((a, b) => a.dir.split('/').length - b.dir.split('/').length)\n}\n\nexport function matchRoute(\n pathname: string,\n routes: ApiRoute[]\n): {route: ApiRoute; params: Record<string, string>} | null {\n for (const route of routes) {\n const match = pathname.match(route.regex)\n if (match) {\n const params: Record<string, string> = {}\n route.params.forEach((name, i) => {\n params[name] = decodeURIComponent(match[i + 1])\n })\n return {route, params}\n }\n }\n return null\n}\n", "import type {DevixHandler} from './create-handler'\n\nexport class RouteContext {\n readonly params: Record<string, string>\n private _state = new Map<string, unknown>()\n\n constructor(params: Record<string, string> = {}) {\n this.params = params\n }\n\n set<T>(key: string, value: T): void {\n this._state.set(key, value)\n }\n\n get<T>(key: string): T | undefined {\n return this._state.get(key) as T\n }\n}\n\nexport type RouteResult = Response | Record<string, unknown> | unknown[] | null | void\n\nexport type RouteHandler = (ctx: RouteContext, req: Request) => Promise<RouteResult> | RouteResult\n\nexport interface MiddlewareModule {\n middleware: (ctx: RouteContext, req: Request) => Promise<Response | null> | Response | null\n}\n\ntype AnyHandler = RouteHandler | DevixHandler<any, any>\n\nexport interface RouteModule {\n GET?: AnyHandler\n POST?: AnyHandler\n PUT?: AnyHandler\n PATCH?: AnyHandler\n DELETE?: AnyHandler\n HEAD?: AnyHandler\n OPTIONS?: AnyHandler\n}\n", "import {Component, ComponentType, ReactNode} from \"react\";\nimport {ErrorProps} from \"../server/types\";\n\ninterface Props {\n ErrorPage?: ComponentType<ErrorProps>\n children: ReactNode\n}\n\ninterface State {\n error: ErrorProps | null\n}\n\nexport class DevixErrorBoundary extends Component<Props, State> {\n state: State = { error: null }\n\n static getDerivedStateFromError(err: unknown): State {\n if (err instanceof DevixError) {\n return {\n error: {statusCode: err.statusCode, message: err.message}\n }\n }\n return {\n error: {statusCode: 500, message: err instanceof Error ? err.message : 'Unknown error'}\n }\n }\n\n render() {\n if (this.state.error && this.props.ErrorPage) {\n return <this.props.ErrorPage {...this.state.error} />\n }\n if (this.state.error) {\n return <h1>{this.state.error.statusCode}</h1>\n }\n return this.props.children\n }\n}\n\nexport class DevixError extends Error {\n statusCode: number\n constructor(statusCode: number, message: string) {\n super(message)\n this.statusCode = statusCode\n }\n}\n", "import type {RouteResult} from './api-context'\n\nexport const HANDLER_BRAND = '__devix_handler__' as const\n\nexport interface DevixHandler<TBody = undefined, TReturn = RouteResult> {\n readonly [HANDLER_BRAND]: true\n readonly fn: (...args: any[]) => any\n readonly __body?: TBody\n readonly __return?: TReturn\n}\n\nexport function createHandler<TReturn extends RouteResult = RouteResult>(\n fn: () => Promise<TReturn> | TReturn,\n): DevixHandler<undefined, TReturn>\n\nexport function createHandler<TBody, TReturn extends RouteResult = RouteResult>(\n fn: (body: TBody) => Promise<TReturn> | TReturn,\n): DevixHandler<TBody, TReturn>\n\nexport function createHandler(fn: (...args: any[]) => any): DevixHandler<any, any> {\n return {[HANDLER_BRAND]: true, fn}\n}\n", "import {AsyncLocalStorage} from 'node:async_hooks'\nimport type {RouteContext} from '../runtime/api-context'\n\ninterface HandlerStore {\n request: Request\n ctx: RouteContext\n}\n\nconst storage = new AsyncLocalStorage<HandlerStore>()\n\nexport function withHandlerStore<T>(store: HandlerStore, fn: () => T): T {\n return storage.run(store, fn)\n}\n\nfunction getStore(hookName: string): HandlerStore {\n const store = storage.getStore()\n if (!store) throw new Error(`[devix] ${hookName}() called outside of a request handler`)\n return store\n}\n\nexport function useRequest(): Request {\n return getStore('useRequest').request\n}\n\nexport function useCtx(): RouteContext {\n return getStore('useCtx').ctx\n}\n\nexport function useParams(): Record<string, string> {\n return getStore('useParams').ctx.params\n}\n", "import {buildRoutes, matchRoute, collectMiddlewareChain} from './api-router'\nimport {RouteContext} from '../runtime/api-context'\nimport type {RouteModule, MiddlewareModule, RouteResult} from '../runtime/api-context'\nimport type {ApiGlob} from './types'\nimport {DevixError} from '../runtime/error-boundary'\nimport {HANDLER_BRAND, type DevixHandler} from '../runtime/create-handler'\nimport {withHandlerStore} from './handler-store'\n\nfunction isDevixHandler(h: unknown): h is DevixHandler<any, any> {\n return typeof h === 'object' && h !== null && HANDLER_BRAND in h\n}\n\nasync function parseBody(request: Request): Promise<unknown> {\n const ct = request.headers.get('Content-Type') ?? ''\n if (ct.includes('application/json')) return request.json()\n if (ct.includes('multipart/form-data') || ct.includes('application/x-www-form-urlencoded')) {\n return request.formData()\n }\n return request.text()\n}\n\nfunction resultToResponse(result: RouteResult): Response {\n if (result instanceof Response) return result\n if (result == null) return new Response(null, {status: 204})\n return new Response(JSON.stringify(result), {\n headers: {'Content-Type': 'application/json'},\n })\n}\n\nexport async function handleApiRequest(\n url: string,\n request: Request,\n glob: ApiGlob,\n): Promise<Response> {\n try {\n const {pathname} = new URL(url, 'http://localhost')\n const {routes, middlewares} = buildRoutes(\n Object.keys(glob.routes),\n Object.keys(glob.middlewares),\n glob.apiDir,\n )\n const matched = matchRoute(pathname, routes)\n\n if (!matched) return new Response('Not Found', {status: 404})\n\n const {route, params} = matched\n const ctx = new RouteContext(params)\n\n const result = await withHandlerStore({request, ctx}, async () => {\n const middlewareChain = collectMiddlewareChain(route.key, middlewares)\n for (const mw of middlewareChain) {\n const mod = await glob.middlewares[mw.key]() as MiddlewareModule\n if (mod.middleware) {\n const mwResult = await mod.middleware(ctx, request)\n if (mwResult instanceof Response) return mwResult\n }\n }\n\n const mod = await glob.routes[route.key]() as RouteModule\n const method = request.method.toUpperCase() as keyof RouteModule\n const handler = mod[method]\n\n if (!handler) return new Response('Method Not Allowed', {status: 405})\n\n if (isDevixHandler(handler)) {\n if (handler.fn.length === 0) {\n return handler.fn() as Promise<RouteResult>\n }\n const body = await parseBody(request)\n return handler.fn(body) as Promise<RouteResult>\n }\n\n return handler(ctx, request)\n })\n\n return resultToResponse(result)\n } catch (err) {\n console.error('[devix] api error:', err)\n if (err instanceof DevixError) {\n return new Response(err.message, {status: err.statusCode})\n }\n return new Response('Internal Server Error', {status: 500})\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["routePattern", "rel", "keyToRoutePattern", "key", "apiDir", "rel", "pattern", "routePattern", "keyToDir", "cache", "buildRoutes", "routeKeys", "middlewareKeys", "apiDir", "cache", "routes", "middlewares", "key", "keyToDir", "pattern", "keyToRoutePattern", "params", "m", "regexStr", "a", "b", "aScore", "bScore", "collectMiddlewareChain", "routeKey", "routeDir", "mw", "matchRoute", "pathname", "route", "match", "name", "i", "RouteContext", "params", "key", "value", "Component", "jsx", "DevixError", "statusCode", "message", "HANDLER_BRAND", "AsyncLocalStorage", "storage", "withHandlerStore", "store", "fn", "isDevixHandler", "h", "HANDLER_BRAND", "parseBody", "request", "ct", "resultToResponse", "result", "handleApiRequest", "url", "glob", "pathname", "routes", "middlewares", "buildRoutes", "matched", "matchRoute", "route", "params", "ctx", "RouteContext", "withHandlerStore", "middlewareChain", "collectMiddlewareChain", "mw", "mod", "mwResult", "method", "handler", "body", "err", "DevixError"]
7
7
  }
@@ -0,0 +1,10 @@
1
+ import type { RouteContext } from '../runtime/api-context';
2
+ interface HandlerStore {
3
+ request: Request;
4
+ ctx: RouteContext;
5
+ }
6
+ export declare function withHandlerStore<T>(store: HandlerStore, fn: () => T): T;
7
+ export declare function useRequest(): Request;
8
+ export declare function useCtx(): RouteContext;
9
+ export declare function useParams(): Record<string, string>;
10
+ export {};
@@ -0,0 +1,2 @@
1
+ import{AsyncLocalStorage as n}from"node:async_hooks";var o=new n;function s(t,e){return o.run(t,e)}function r(t){let e=o.getStore();if(!e)throw new Error(`[devix] ${t}() called outside of a request handler`);return e}function a(){return r("useRequest").request}function i(){return r("useCtx").ctx}function c(){return r("useParams").ctx.params}export{i as useCtx,c as useParams,a as useRequest,s as withHandlerStore};
2
+ //# sourceMappingURL=handler-store.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/server/handler-store.ts"],
4
+ "sourcesContent": ["import {AsyncLocalStorage} from 'node:async_hooks'\nimport type {RouteContext} from '../runtime/api-context'\n\ninterface HandlerStore {\n request: Request\n ctx: RouteContext\n}\n\nconst storage = new AsyncLocalStorage<HandlerStore>()\n\nexport function withHandlerStore<T>(store: HandlerStore, fn: () => T): T {\n return storage.run(store, fn)\n}\n\nfunction getStore(hookName: string): HandlerStore {\n const store = storage.getStore()\n if (!store) throw new Error(`[devix] ${hookName}() called outside of a request handler`)\n return store\n}\n\nexport function useRequest(): Request {\n return getStore('useRequest').request\n}\n\nexport function useCtx(): RouteContext {\n return getStore('useCtx').ctx\n}\n\nexport function useParams(): Record<string, string> {\n return getStore('useParams').ctx.params\n}\n"],
5
+ "mappings": "AAAA,OAAQ,qBAAAA,MAAwB,mBAQhC,IAAMC,EAAU,IAAID,EAEb,SAASE,EAAoBC,EAAqBC,EAAgB,CACrE,OAAOH,EAAQ,IAAIE,EAAOC,CAAE,CAChC,CAEA,SAASC,EAASC,EAAgC,CAC9C,IAAMH,EAAQF,EAAQ,SAAS,EAC/B,GAAI,CAACE,EAAO,MAAM,IAAI,MAAM,WAAWG,CAAQ,wCAAwC,EACvF,OAAOH,CACX,CAEO,SAASI,GAAsB,CAClC,OAAOF,EAAS,YAAY,EAAE,OAClC,CAEO,SAASG,GAAuB,CACnC,OAAOH,EAAS,QAAQ,EAAE,GAC9B,CAEO,SAASI,GAAoC,CAChD,OAAOJ,EAAS,WAAW,EAAE,IAAI,MACrC",
6
+ "names": ["AsyncLocalStorage", "storage", "withHandlerStore", "store", "fn", "getStore", "hookName", "useRequest", "useCtx", "useParams"]
7
+ }
@@ -0,0 +1 @@
1
+ export { useRequest, useCtx, useParams } from './handler-store';
@@ -0,0 +1,2 @@
1
+ import{AsyncLocalStorage as o}from"node:async_hooks";var n=new o;function e(r){let t=n.getStore();if(!t)throw new Error(`[devix] ${r}() called outside of a request handler`);return t}function u(){return e("useRequest").request}function s(){return e("useCtx").ctx}function a(){return e("useParams").ctx.params}export{s as useCtx,a as useParams,u as useRequest};
2
+ //# sourceMappingURL=public-index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/server/handler-store.ts"],
4
+ "sourcesContent": ["import {AsyncLocalStorage} from 'node:async_hooks'\nimport type {RouteContext} from '../runtime/api-context'\n\ninterface HandlerStore {\n request: Request\n ctx: RouteContext\n}\n\nconst storage = new AsyncLocalStorage<HandlerStore>()\n\nexport function withHandlerStore<T>(store: HandlerStore, fn: () => T): T {\n return storage.run(store, fn)\n}\n\nfunction getStore(hookName: string): HandlerStore {\n const store = storage.getStore()\n if (!store) throw new Error(`[devix] ${hookName}() called outside of a request handler`)\n return store\n}\n\nexport function useRequest(): Request {\n return getStore('useRequest').request\n}\n\nexport function useCtx(): RouteContext {\n return getStore('useCtx').ctx\n}\n\nexport function useParams(): Record<string, string> {\n return getStore('useParams').ctx.params\n}\n"],
5
+ "mappings": "AAAA,OAAQ,qBAAAA,MAAwB,mBAQhC,IAAMC,EAAU,IAAID,EAMpB,SAASE,EAASC,EAAgC,CAC9C,IAAMC,EAAQC,EAAQ,SAAS,EAC/B,GAAI,CAACD,EAAO,MAAM,IAAI,MAAM,WAAWD,CAAQ,wCAAwC,EACvF,OAAOC,CACX,CAEO,SAASE,GAAsB,CAClC,OAAOJ,EAAS,YAAY,EAAE,OAClC,CAEO,SAASK,GAAuB,CACnC,OAAOL,EAAS,QAAQ,EAAE,GAC9B,CAEO,SAASM,GAAoC,CAChD,OAAON,EAAS,WAAW,EAAE,IAAI,MACrC",
6
+ "names": ["AsyncLocalStorage", "storage", "getStore", "hookName", "store", "storage", "useRequest", "useCtx", "useParams"]
7
+ }
@@ -1,6 +1,6 @@
1
1
  export type JsonResponse<T = unknown> = Response & {
2
2
  readonly __body: T;
3
3
  };
4
- export declare const json: <T>(data: T, status?: number) => JsonResponse<T>;
4
+ export declare const json: <const T>(data: T, status?: number) => JsonResponse<T>;
5
5
  export declare const text: (body: string, status?: number) => Response;
6
6
  export declare const redirect: (url: string, status?: number) => Response;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/response.ts"],
4
- "sourcesContent": ["export type JsonResponse<T = unknown> = Response & { readonly __body: T }\n\nexport const json = <T>(data: T, status = 200): JsonResponse<T> =>\n Response.json(data, {status}) as JsonResponse<T>\n\nexport const text = (body: string, status = 200): Response =>\n new Response(body, {status, headers: {'Content-Type': 'text/plain; charset=utf-8'}})\n\nexport const redirect = (url: string, status = 302): Response =>\n new Response(null, {status, headers: {Location: url}})\n"],
5
- "mappings": "AAEO,IAAMA,EAAO,CAAIC,EAASC,EAAS,MACtC,SAAS,KAAKD,EAAM,CAAC,OAAAC,CAAM,CAAC,EAEnBC,EAAO,CAACC,EAAcF,EAAS,MACxC,IAAI,SAASE,EAAM,CAAC,OAAAF,EAAQ,QAAS,CAAC,eAAgB,2BAA2B,CAAC,CAAC,EAE1EG,EAAW,CAACC,EAAaJ,EAAS,MAC3C,IAAI,SAAS,KAAM,CAAC,OAAAA,EAAQ,QAAS,CAAC,SAAUI,CAAG,CAAC,CAAC",
4
+ "sourcesContent": ["export type JsonResponse<T = unknown> = Response & { readonly __body: T }\n\nexport const json = <const T>(data: T, status = 200): JsonResponse<T> =>\n Response.json(data, {status}) as JsonResponse<T>\n\nexport const text = (body: string, status = 200): Response =>\n new Response(body, {status, headers: {'Content-Type': 'text/plain; charset=utf-8'}})\n\nexport const redirect = (url: string, status = 302): Response =>\n new Response(null, {status, headers: {Location: url}})\n"],
5
+ "mappings": "AAEO,IAAMA,EAAO,CAAUC,EAASC,EAAS,MAC5C,SAAS,KAAKD,EAAM,CAAC,OAAAC,CAAM,CAAC,EAEnBC,EAAO,CAACC,EAAcF,EAAS,MACxC,IAAI,SAASE,EAAM,CAAC,OAAAF,EAAQ,QAAS,CAAC,eAAgB,2BAA2B,CAAC,CAAC,EAE1EG,EAAW,CAACC,EAAaJ,EAAS,MAC3C,IAAI,SAAS,KAAM,CAAC,OAAAA,EAAQ,QAAS,CAAC,SAAUI,CAAG,CAAC,CAAC",
6
6
  "names": ["json", "data", "status", "text", "body", "redirect", "url"]
7
7
  }
@@ -1,23 +1,30 @@
1
- function s(e){return e.replace(/\.(tsx|ts|jsx|js)$/,"").replace(/\(.*?\)\//g,"").replace(/^index$|\/index$/,"").replace(/\[([^\]]+)]/g,":$1")||"/"}function a(e,t){let n=e.slice(t.length+1).replace(/\\/g,"/"),i=s(n);return i==="/"?"/api":`/api/${i}`.replace("/api//","/api/")}function p(e,t){return"_api_"+e.slice(`${t}/`.length).replace(/\.(ts|tsx)$/,"").replace(/[^a-zA-Z0-9]/g,"_")}function g(e,t,n){return{filePath:e,urlPattern:a(e,t),identifier:p(e,t),methods:n}}function f(e,t){if(e.length===0)return`// auto-generado por devix \u2014 no editar
1
+ function s(e){return e.replace(/\.(tsx|ts|jsx|js)$/,"").replace(/\(.*?\)\//g,"").replace(/^index$|\/index$/,"").replace(/\[([^\]]+)]/g,":$1")||"/"}function a(e,t){let n=e.slice(t.length+1).replace(/\\/g,"/"),o=s(n);return o==="/"?"/api":`/api/${o}`.replace("/api//","/api/")}function p(e,t){return"_api_"+e.slice(`${t}/`.length).replace(/\.(ts|tsx)$/,"").replace(/[^a-zA-Z0-9]/g,"_")}function g(e,t,n){return{filePath:e,urlPattern:a(e,t),identifier:p(e,t),methods:n}}function f(e,t){if(e.length===0)return`// auto-generado por devix \u2014 no editar
2
2
  declare module '@devlusoft/devix' {
3
3
  interface ApiRoutes {}
4
4
  }
5
- `;let n=e.map(r=>{let o="../"+r.filePath.replace(/\.(ts|tsx)$/,"");return`import type * as ${r.identifier} from '${o}'`}).join(`
6
- `),i=e.flatMap(r=>r.methods.map(o=>` '${o} ${r.urlPattern}': InferRoute<(typeof ${r.identifier})['${o}']>`)).join(`
5
+ `;let n=e.map(r=>{let i="../"+r.filePath.replace(/\.(ts|tsx)$/,"");return`import type * as ${r.identifier} from '${i}'`}).join(`
6
+ `),o=e.flatMap(r=>r.methods.map(i=>` '${i} ${r.urlPattern}': InferRoute<(typeof ${r.identifier})['${i}']>`)).join(`
7
7
  `);return`// auto-generado por devix \u2014 no editar
8
8
  ${n}
9
9
 
10
10
  type JsonResponse<T> = Response & { readonly __body: T }
11
11
  type UnwrapJson<T> = T extends JsonResponse<infer U> ? U : never
12
- type InferRoute<T> = T extends (...args: any[]) => any
12
+ type InferFnReturn<T> = T extends (...args: any[]) => any
13
13
  ? [UnwrapJson<Awaited<ReturnType<T>>>] extends [never]
14
14
  ? Exclude<Awaited<ReturnType<T>>, Response | null | void>
15
15
  : UnwrapJson<Awaited<ReturnType<T>>>
16
16
  : never
17
+ type InferRoute<T> =
18
+ T extends { readonly __return?: infer TReturn; readonly __body?: infer TBody }
19
+ ? {
20
+ __body: [TBody] extends [undefined] ? never : Exclude<TBody, undefined>
21
+ __response: InferFnReturn<() => TReturn>
22
+ }
23
+ : InferFnReturn<T>
17
24
 
18
25
  declare module '@devlusoft/devix' {
19
26
  interface ApiRoutes {
20
- ${i}
27
+ ${o}
21
28
  }
22
29
  }
23
30
  `}export{g as buildRouteEntry,p as filePathToIdentifier,f as generateRoutesDts};
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/utils/patterns.ts", "../../../src/server/api-router.ts", "../../../src/vite/codegen/routes-dts.ts"],
4
- "sourcesContent": ["export function routePattern(rel: string): string {\n return rel\n .replace(/\\.(tsx|ts|jsx|js)$/, '')\n .replace(/\\(.*?\\)\\//g, '')\n .replace(/^index$|\\/index$/, '')\n .replace(/\\[([^\\]]+)]/g, ':$1')\n || '/'\n}", "import {routePattern} from \"../utils/patterns\";\n\nexport interface ApiRoute {\n path: string\n key: string\n params: string[]\n regex: RegExp\n}\n\nexport interface ApiMiddleware {\n dir: string\n key: string\n}\n\nexport interface ApiResult {\n routes: ApiRoute[]\n middlewares: ApiMiddleware[]\n}\n\nexport function keyToRoutePattern(key: string, apiDir: string): string {\n const rel = key.slice(apiDir.length + 1).replace(/\\\\/g, '/')\n const pattern = routePattern(rel)\n return pattern === '/' ? '/api' : `/api/${pattern}`.replace('/api//', '/api/')\n}\n\nfunction keyToDir(key: string): string {\n return key.slice(0, key.lastIndexOf('/'))\n}\n\nlet cache: ApiResult | null = null\n\nexport function invalidateApiCache() {\n cache = null\n}\n\nexport function buildRoutes(routeKeys: string[], middlewareKeys: string[], apiDir: string): ApiResult {\n if (cache) return cache\n\n const routes: ApiRoute[] = []\n const middlewares: ApiMiddleware[] = []\n\n for (const key of middlewareKeys) {\n middlewares.push({dir: keyToDir(key), key})\n }\n\n for (const key of routeKeys) {\n const pattern = keyToRoutePattern(key, apiDir)\n const params = [...pattern.matchAll(/:([^/]+)/g)].map(m => m[1])\n const regexStr = pattern\n .replace(/:[^/]+/g, '([^/]+)')\n .replace(/\\//g, '\\\\/')\n routes.push({path: pattern, key, params, regex: new RegExp(`^${regexStr}$`)})\n }\n routes.sort((a, b) => {\n const aScore = (a.path.match(/:/g) || []).length\n const bScore = (b.path.match(/:/g) || []).length\n if (aScore !== bScore) return aScore - bScore\n return b.path.length - a.path.length\n })\n\n cache = {routes, middlewares}\n return cache\n}\n\nexport function collectMiddlewareChain(routeKey: string, middlewares: ApiMiddleware[]): ApiMiddleware[] {\n const routeDir = keyToDir(routeKey)\n\n return middlewares\n .filter(mw => routeDir.startsWith(mw.dir))\n .sort((a, b) => a.dir.split('/').length - b.dir.split('/').length)\n}\n\nexport function matchRoute(\n pathname: string,\n routes: ApiRoute[]\n): {route: ApiRoute; params: Record<string, string>} | null {\n for (const route of routes) {\n const match = pathname.match(route.regex)\n if (match) {\n const params: Record<string, string> = {}\n route.params.forEach((name, i) => {\n params[name] = decodeURIComponent(match[i + 1])\n })\n return {route, params}\n }\n }\n return null\n}\n", "import { keyToRoutePattern } from '../../server/api-router'\nimport type { HttpMethod } from './extract-methods'\n\nexport interface RouteEntry {\n filePath: string\n urlPattern: string\n identifier: string\n methods: HttpMethod[]\n}\n\nexport function filePathToIdentifier(filePath: string, apiDir: string): string {\n return '_api_' + filePath\n .slice(`${apiDir}/`.length)\n .replace(/\\.(ts|tsx)$/, '')\n .replace(/[^a-zA-Z0-9]/g, '_')\n}\n\nexport function buildRouteEntry(filePath: string, apiDir: string, methods: HttpMethod[]): RouteEntry {\n return {\n filePath,\n urlPattern: keyToRoutePattern(filePath, apiDir),\n identifier: filePathToIdentifier(filePath, apiDir),\n methods,\n }\n}\n\nexport function generateRoutesDts(entries: RouteEntry[], apiDir: string): string {\n if (entries.length === 0) {\n return `// auto-generado por devix \u2014 no editar\\ndeclare module '@devlusoft/devix' {\\n interface ApiRoutes {}\\n}\\n`\n }\n\n const imports = entries\n .map(e => {\n const importPath = '../' + e.filePath.replace(/\\.(ts|tsx)$/, '')\n return `import type * as ${e.identifier} from '${importPath}'`\n })\n .join('\\n')\n\n const routeLines = entries.flatMap(e =>\n e.methods.map(m =>\n ` '${m} ${e.urlPattern}': InferRoute<(typeof ${e.identifier})['${m}']>`\n )\n ).join('\\n')\n\n return `// auto-generado por devix \u2014 no editar\n${imports}\n\ntype JsonResponse<T> = Response & { readonly __body: T }\ntype UnwrapJson<T> = T extends JsonResponse<infer U> ? U : never\ntype InferRoute<T> = T extends (...args: any[]) => any\n ? [UnwrapJson<Awaited<ReturnType<T>>>] extends [never]\n ? Exclude<Awaited<ReturnType<T>>, Response | null | void>\n : UnwrapJson<Awaited<ReturnType<T>>>\n : never\n\ndeclare module '@devlusoft/devix' {\n interface ApiRoutes {\n${routeLines}\n }\n}\n`\n}\n"],
5
- "mappings": "AAAO,SAASA,EAAaC,EAAqB,CAC9C,OAAOA,EACE,QAAQ,qBAAsB,EAAE,EAChC,QAAQ,aAAc,EAAE,EACxB,QAAQ,mBAAoB,EAAE,EAC9B,QAAQ,eAAgB,KAAK,GAC/B,GACX,CCYO,SAASC,EAAkBC,EAAaC,EAAwB,CACnE,IAAMC,EAAMF,EAAI,MAAMC,EAAO,OAAS,CAAC,EAAE,QAAQ,MAAO,GAAG,EACrDE,EAAUC,EAAaF,CAAG,EAChC,OAAOC,IAAY,IAAM,OAAS,QAAQA,CAAO,GAAG,QAAQ,SAAU,OAAO,CACjF,CCbO,SAASE,EAAqBC,EAAkBC,EAAwB,CAC3E,MAAO,QAAUD,EACZ,MAAM,GAAGC,CAAM,IAAI,MAAM,EACzB,QAAQ,cAAe,EAAE,EACzB,QAAQ,gBAAiB,GAAG,CACrC,CAEO,SAASC,EAAgBF,EAAkBC,EAAgBE,EAAmC,CACjG,MAAO,CACH,SAAAH,EACA,WAAYI,EAAkBJ,EAAUC,CAAM,EAC9C,WAAYF,EAAqBC,EAAUC,CAAM,EACjD,QAAAE,CACJ,CACJ,CAEO,SAASE,EAAkBC,EAAuBL,EAAwB,CAC7E,GAAIK,EAAQ,SAAW,EACnB,MAAO;AAAA;AAAA;AAAA;AAAA,EAGX,IAAMC,EAAUD,EACX,IAAIE,GAAK,CACN,IAAMC,EAAa,MAAQD,EAAE,SAAS,QAAQ,cAAe,EAAE,EAC/D,MAAO,oBAAoBA,EAAE,UAAU,UAAUC,CAAU,GAC/D,CAAC,EACA,KAAK;AAAA,CAAI,EAERC,EAAaJ,EAAQ,QAAQE,GAC/BA,EAAE,QAAQ,IAAIG,GACV,QAAQA,CAAC,IAAIH,EAAE,UAAU,yBAAyBA,EAAE,UAAU,MAAMG,CAAC,KACzE,CACJ,EAAE,KAAK;AAAA,CAAI,EAEX,MAAO;AAAA,EACTJ,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYPG,CAAU;AAAA;AAAA;AAAA,CAIZ",
4
+ "sourcesContent": ["export function routePattern(rel: string): string {\n return rel\n .replace(/\\.(tsx|ts|jsx|js)$/, '')\n .replace(/\\(.*?\\)\\//g, '')\n .replace(/^index$|\\/index$/, '')\n .replace(/\\[([^\\]]+)]/g, ':$1')\n || '/'\n}", "import {routePattern} from \"../utils/patterns\";\n\nexport interface ApiRoute {\n path: string\n key: string\n params: string[]\n regex: RegExp\n}\n\nexport interface ApiMiddleware {\n dir: string\n key: string\n}\n\nexport interface ApiResult {\n routes: ApiRoute[]\n middlewares: ApiMiddleware[]\n}\n\nexport function keyToRoutePattern(key: string, apiDir: string): string {\n const rel = key.slice(apiDir.length + 1).replace(/\\\\/g, '/')\n const pattern = routePattern(rel)\n return pattern === '/' ? '/api' : `/api/${pattern}`.replace('/api//', '/api/')\n}\n\nfunction keyToDir(key: string): string {\n return key.slice(0, key.lastIndexOf('/'))\n}\n\nlet cache: ApiResult | null = null\n\nexport function invalidateApiCache() {\n cache = null\n}\n\nexport function buildRoutes(routeKeys: string[], middlewareKeys: string[], apiDir: string): ApiResult {\n if (cache) return cache\n\n const routes: ApiRoute[] = []\n const middlewares: ApiMiddleware[] = []\n\n for (const key of middlewareKeys) {\n middlewares.push({dir: keyToDir(key), key})\n }\n\n for (const key of routeKeys) {\n const pattern = keyToRoutePattern(key, apiDir)\n const params = [...pattern.matchAll(/:([^/]+)/g)].map(m => m[1])\n const regexStr = pattern\n .replace(/:[^/]+/g, '([^/]+)')\n .replace(/\\//g, '\\\\/')\n routes.push({path: pattern, key, params, regex: new RegExp(`^${regexStr}$`)})\n }\n routes.sort((a, b) => {\n const aScore = (a.path.match(/:/g) || []).length\n const bScore = (b.path.match(/:/g) || []).length\n if (aScore !== bScore) return aScore - bScore\n return b.path.length - a.path.length\n })\n\n cache = {routes, middlewares}\n return cache\n}\n\nexport function collectMiddlewareChain(routeKey: string, middlewares: ApiMiddleware[]): ApiMiddleware[] {\n const routeDir = keyToDir(routeKey)\n\n return middlewares\n .filter(mw => routeDir.startsWith(mw.dir))\n .sort((a, b) => a.dir.split('/').length - b.dir.split('/').length)\n}\n\nexport function matchRoute(\n pathname: string,\n routes: ApiRoute[]\n): {route: ApiRoute; params: Record<string, string>} | null {\n for (const route of routes) {\n const match = pathname.match(route.regex)\n if (match) {\n const params: Record<string, string> = {}\n route.params.forEach((name, i) => {\n params[name] = decodeURIComponent(match[i + 1])\n })\n return {route, params}\n }\n }\n return null\n}\n", "import { keyToRoutePattern } from '../../server/api-router'\nimport type { HttpMethod } from './extract-methods'\n\nexport interface RouteEntry {\n filePath: string\n urlPattern: string\n identifier: string\n methods: HttpMethod[]\n}\n\nexport function filePathToIdentifier(filePath: string, apiDir: string): string {\n return '_api_' + filePath\n .slice(`${apiDir}/`.length)\n .replace(/\\.(ts|tsx)$/, '')\n .replace(/[^a-zA-Z0-9]/g, '_')\n}\n\nexport function buildRouteEntry(filePath: string, apiDir: string, methods: HttpMethod[]): RouteEntry {\n return {\n filePath,\n urlPattern: keyToRoutePattern(filePath, apiDir),\n identifier: filePathToIdentifier(filePath, apiDir),\n methods,\n }\n}\n\nexport function generateRoutesDts(entries: RouteEntry[], apiDir: string): string {\n if (entries.length === 0) {\n return `// auto-generado por devix \u2014 no editar\\ndeclare module '@devlusoft/devix' {\\n interface ApiRoutes {}\\n}\\n`\n }\n\n const imports = entries\n .map(e => {\n const importPath = '../' + e.filePath.replace(/\\.(ts|tsx)$/, '')\n return `import type * as ${e.identifier} from '${importPath}'`\n })\n .join('\\n')\n\n const routeLines = entries.flatMap(e =>\n e.methods.map(m =>\n ` '${m} ${e.urlPattern}': InferRoute<(typeof ${e.identifier})['${m}']>`\n )\n ).join('\\n')\n\n return `// auto-generado por devix \u2014 no editar\n${imports}\n\ntype JsonResponse<T> = Response & { readonly __body: T }\ntype UnwrapJson<T> = T extends JsonResponse<infer U> ? U : never\ntype InferFnReturn<T> = T extends (...args: any[]) => any\n ? [UnwrapJson<Awaited<ReturnType<T>>>] extends [never]\n ? Exclude<Awaited<ReturnType<T>>, Response | null | void>\n : UnwrapJson<Awaited<ReturnType<T>>>\n : never\ntype InferRoute<T> =\n T extends { readonly __return?: infer TReturn; readonly __body?: infer TBody }\n ? {\n __body: [TBody] extends [undefined] ? never : Exclude<TBody, undefined>\n __response: InferFnReturn<() => TReturn>\n }\n : InferFnReturn<T>\n\ndeclare module '@devlusoft/devix' {\n interface ApiRoutes {\n${routeLines}\n }\n}\n`\n}\n"],
5
+ "mappings": "AAAO,SAASA,EAAaC,EAAqB,CAC9C,OAAOA,EACE,QAAQ,qBAAsB,EAAE,EAChC,QAAQ,aAAc,EAAE,EACxB,QAAQ,mBAAoB,EAAE,EAC9B,QAAQ,eAAgB,KAAK,GAC/B,GACX,CCYO,SAASC,EAAkBC,EAAaC,EAAwB,CACnE,IAAMC,EAAMF,EAAI,MAAMC,EAAO,OAAS,CAAC,EAAE,QAAQ,MAAO,GAAG,EACrDE,EAAUC,EAAaF,CAAG,EAChC,OAAOC,IAAY,IAAM,OAAS,QAAQA,CAAO,GAAG,QAAQ,SAAU,OAAO,CACjF,CCbO,SAASE,EAAqBC,EAAkBC,EAAwB,CAC3E,MAAO,QAAUD,EACZ,MAAM,GAAGC,CAAM,IAAI,MAAM,EACzB,QAAQ,cAAe,EAAE,EACzB,QAAQ,gBAAiB,GAAG,CACrC,CAEO,SAASC,EAAgBF,EAAkBC,EAAgBE,EAAmC,CACjG,MAAO,CACH,SAAAH,EACA,WAAYI,EAAkBJ,EAAUC,CAAM,EAC9C,WAAYF,EAAqBC,EAAUC,CAAM,EACjD,QAAAE,CACJ,CACJ,CAEO,SAASE,EAAkBC,EAAuBL,EAAwB,CAC7E,GAAIK,EAAQ,SAAW,EACnB,MAAO;AAAA;AAAA;AAAA;AAAA,EAGX,IAAMC,EAAUD,EACX,IAAIE,GAAK,CACN,IAAMC,EAAa,MAAQD,EAAE,SAAS,QAAQ,cAAe,EAAE,EAC/D,MAAO,oBAAoBA,EAAE,UAAU,UAAUC,CAAU,GAC/D,CAAC,EACA,KAAK;AAAA,CAAI,EAERC,EAAaJ,EAAQ,QAAQE,GAC/BA,EAAE,QAAQ,IAAIG,GACV,QAAQA,CAAC,IAAIH,EAAE,UAAU,yBAAyBA,EAAE,UAAU,MAAMG,CAAC,KACzE,CACJ,EAAE,KAAK;AAAA,CAAI,EAEX,MAAO;AAAA,EACTJ,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBPG,CAAU;AAAA;AAAA;AAAA,CAIZ",
6
6
  "names": ["routePattern", "rel", "keyToRoutePattern", "key", "apiDir", "rel", "pattern", "routePattern", "filePathToIdentifier", "filePath", "apiDir", "buildRouteEntry", "methods", "keyToRoutePattern", "generateRoutesDts", "entries", "imports", "e", "importPath", "routeLines", "m"]
7
7
  }
@@ -1,2 +1,2 @@
1
- import{readFileSync as m,readdirSync as x,statSync as y}from"node:fs";import{join as o,relative as R}from"node:path";var g=/export\s+(?:const|async\s+function|function)\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b/g;function f(t){return t.replace(/\/\*[\s\S]*?\*\//g,"").replace(/\/\/.*$/gm,"")}function a(t){let e=new Set;for(let r of f(t).matchAll(g))e.add(r[1]);return[...e]}function p(t){return t.replace(/\.(tsx|ts|jsx|js)$/,"").replace(/\(.*?\)\//g,"").replace(/^index$|\/index$/,"").replace(/\[([^\]]+)]/g,":$1")||"/"}function c(t,e){let r=t.slice(e.length+1).replace(/\\/g,"/"),i=p(r);return i==="/"?"/api":`/api/${i}`.replace("/api//","/api/")}function h(t,e){return"_api_"+t.slice(`${e}/`.length).replace(/\.(ts|tsx)$/,"").replace(/[^a-zA-Z0-9]/g,"_")}function u(t,e,r){return{filePath:t,urlPattern:c(t,e),identifier:h(t,e),methods:r}}function l(t,e){let r=[];for(let i of x(t)){let n=o(t,i);y(n).isDirectory()?r.push(...l(n,e)):/\.(ts|tsx)$/.test(i)&&r.push(R(e,n).replace(/\\/g,"/"))}return r}function D(t,e){let r=o(e,t,"api"),i;try{i=l(r,e)}catch{return[]}return i.filter(n=>!n.endsWith("middleware.ts")&&!n.endsWith("middleware.tsx")).flatMap(n=>{try{let d=m(o(e,n),"utf-8"),s=a(d);return s.length===0?[]:[u(n,`${t}/api`,s)]}catch{return[]}})}export{D as scanApiFiles};
1
+ import{readFileSync as m,readdirSync as x,statSync as y}from"node:fs";import{join as i,relative as T}from"node:path";var f=/export\s+(?:const|async\s+function|function)\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b/g;function g(t){return t.replace(/\/\*[\s\S]*?\*\//g,"").replace(/\/\/.*$/gm,"")}function a(t){let e=new Set;for(let r of g(t).matchAll(f))e.add(r[1]);return[...e]}function p(t){return t.replace(/\.(tsx|ts|jsx|js)$/,"").replace(/\(.*?\)\//g,"").replace(/^index$|\/index$/,"").replace(/\[([^\]]+)]/g,":$1")||"/"}function u(t,e){let r=t.slice(e.length+1).replace(/\\/g,"/"),o=p(r);return o==="/"?"/api":`/api/${o}`.replace("/api//","/api/")}function h(t,e){return"_api_"+t.slice(`${e}/`.length).replace(/\.(ts|tsx)$/,"").replace(/[^a-zA-Z0-9]/g,"_")}function c(t,e,r){return{filePath:t,urlPattern:u(t,e),identifier:h(t,e),methods:r}}function d(t,e){let r=[];for(let o of x(t)){let n=i(t,o);y(n).isDirectory()?r.push(...d(n,e)):/\.(ts|tsx)$/.test(o)&&r.push(T(e,n).replace(/\\/g,"/"))}return r}function v(t,e){let r=i(e,t,"api"),o;try{o=d(r,e)}catch{return[]}return o.filter(n=>!n.endsWith("middleware.ts")&&!n.endsWith("middleware.tsx")).flatMap(n=>{try{let l=m(i(e,n),"utf-8"),s=a(l);return s.length===0?[]:[c(n,`${t}/api`,s)]}catch{return[]}})}export{v as scanApiFiles};
2
2
  //# sourceMappingURL=scan-api.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/vite/codegen/scan-api.ts", "../../../src/vite/codegen/extract-methods.ts", "../../../src/utils/patterns.ts", "../../../src/server/api-router.ts", "../../../src/vite/codegen/routes-dts.ts"],
4
- "sourcesContent": ["import {readFileSync, readdirSync, statSync} from 'node:fs'\nimport {join, relative} from 'node:path'\nimport {extractHttpMethods} from './extract-methods'\nimport {buildRouteEntry} from './routes-dts'\nimport type {RouteEntry} from './routes-dts'\n\nfunction walkDir(dir: string, root: string): string[] {\n const entries: string[] = []\n for (const name of readdirSync(dir)) {\n const full = join(dir, name)\n if (statSync(full).isDirectory()) {\n entries.push(...walkDir(full, root))\n } else if (/\\.(ts|tsx)$/.test(name)) {\n entries.push(relative(root, full).replace(/\\\\/g, '/'))\n }\n }\n return entries\n}\n\nexport function scanApiFiles(appDir: string, projectRoot: string): RouteEntry[] {\n const apiDir = join(projectRoot, appDir, 'api')\n\n let files: string[]\n try {\n files = walkDir(apiDir, projectRoot)\n } catch {\n return []\n }\n\n return files\n .filter(f => !f.endsWith('middleware.ts') && !f.endsWith('middleware.tsx'))\n .flatMap(filePath => {\n try {\n const content = readFileSync(join(projectRoot, filePath), 'utf-8')\n const methods = extractHttpMethods(content)\n if (methods.length === 0) return []\n return [buildRouteEntry(filePath, `${appDir}/api`, methods)]\n } catch {\n return []\n }\n })\n}\n", "const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'] as const\nexport type HttpMethod = (typeof HTTP_METHODS)[number]\n\nconst METHOD_EXPORT_RE = /export\\s+(?:const|async\\s+function|function)\\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\\b/g\n\nfunction stripComments(content: string): string {\n return content\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n .replace(/\\/\\/.*$/gm, '')\n}\n\nexport function extractHttpMethods(content: string): HttpMethod[] {\n const found = new Set<HttpMethod>()\n for (const match of stripComments(content).matchAll(METHOD_EXPORT_RE)) {\n found.add(match[1] as HttpMethod)\n }\n return [...found]\n}\n", "export function routePattern(rel: string): string {\n return rel\n .replace(/\\.(tsx|ts|jsx|js)$/, '')\n .replace(/\\(.*?\\)\\//g, '')\n .replace(/^index$|\\/index$/, '')\n .replace(/\\[([^\\]]+)]/g, ':$1')\n || '/'\n}", "import {routePattern} from \"../utils/patterns\";\n\nexport interface ApiRoute {\n path: string\n key: string\n params: string[]\n regex: RegExp\n}\n\nexport interface ApiMiddleware {\n dir: string\n key: string\n}\n\nexport interface ApiResult {\n routes: ApiRoute[]\n middlewares: ApiMiddleware[]\n}\n\nexport function keyToRoutePattern(key: string, apiDir: string): string {\n const rel = key.slice(apiDir.length + 1).replace(/\\\\/g, '/')\n const pattern = routePattern(rel)\n return pattern === '/' ? '/api' : `/api/${pattern}`.replace('/api//', '/api/')\n}\n\nfunction keyToDir(key: string): string {\n return key.slice(0, key.lastIndexOf('/'))\n}\n\nlet cache: ApiResult | null = null\n\nexport function invalidateApiCache() {\n cache = null\n}\n\nexport function buildRoutes(routeKeys: string[], middlewareKeys: string[], apiDir: string): ApiResult {\n if (cache) return cache\n\n const routes: ApiRoute[] = []\n const middlewares: ApiMiddleware[] = []\n\n for (const key of middlewareKeys) {\n middlewares.push({dir: keyToDir(key), key})\n }\n\n for (const key of routeKeys) {\n const pattern = keyToRoutePattern(key, apiDir)\n const params = [...pattern.matchAll(/:([^/]+)/g)].map(m => m[1])\n const regexStr = pattern\n .replace(/:[^/]+/g, '([^/]+)')\n .replace(/\\//g, '\\\\/')\n routes.push({path: pattern, key, params, regex: new RegExp(`^${regexStr}$`)})\n }\n routes.sort((a, b) => {\n const aScore = (a.path.match(/:/g) || []).length\n const bScore = (b.path.match(/:/g) || []).length\n if (aScore !== bScore) return aScore - bScore\n return b.path.length - a.path.length\n })\n\n cache = {routes, middlewares}\n return cache\n}\n\nexport function collectMiddlewareChain(routeKey: string, middlewares: ApiMiddleware[]): ApiMiddleware[] {\n const routeDir = keyToDir(routeKey)\n\n return middlewares\n .filter(mw => routeDir.startsWith(mw.dir))\n .sort((a, b) => a.dir.split('/').length - b.dir.split('/').length)\n}\n\nexport function matchRoute(\n pathname: string,\n routes: ApiRoute[]\n): {route: ApiRoute; params: Record<string, string>} | null {\n for (const route of routes) {\n const match = pathname.match(route.regex)\n if (match) {\n const params: Record<string, string> = {}\n route.params.forEach((name, i) => {\n params[name] = decodeURIComponent(match[i + 1])\n })\n return {route, params}\n }\n }\n return null\n}\n", "import { keyToRoutePattern } from '../../server/api-router'\nimport type { HttpMethod } from './extract-methods'\n\nexport interface RouteEntry {\n filePath: string\n urlPattern: string\n identifier: string\n methods: HttpMethod[]\n}\n\nexport function filePathToIdentifier(filePath: string, apiDir: string): string {\n return '_api_' + filePath\n .slice(`${apiDir}/`.length)\n .replace(/\\.(ts|tsx)$/, '')\n .replace(/[^a-zA-Z0-9]/g, '_')\n}\n\nexport function buildRouteEntry(filePath: string, apiDir: string, methods: HttpMethod[]): RouteEntry {\n return {\n filePath,\n urlPattern: keyToRoutePattern(filePath, apiDir),\n identifier: filePathToIdentifier(filePath, apiDir),\n methods,\n }\n}\n\nexport function generateRoutesDts(entries: RouteEntry[], apiDir: string): string {\n if (entries.length === 0) {\n return `// auto-generado por devix \u2014 no editar\\ndeclare module '@devlusoft/devix' {\\n interface ApiRoutes {}\\n}\\n`\n }\n\n const imports = entries\n .map(e => {\n const importPath = '../' + e.filePath.replace(/\\.(ts|tsx)$/, '')\n return `import type * as ${e.identifier} from '${importPath}'`\n })\n .join('\\n')\n\n const routeLines = entries.flatMap(e =>\n e.methods.map(m =>\n ` '${m} ${e.urlPattern}': InferRoute<(typeof ${e.identifier})['${m}']>`\n )\n ).join('\\n')\n\n return `// auto-generado por devix \u2014 no editar\n${imports}\n\ntype JsonResponse<T> = Response & { readonly __body: T }\ntype UnwrapJson<T> = T extends JsonResponse<infer U> ? U : never\ntype InferRoute<T> = T extends (...args: any[]) => any\n ? [UnwrapJson<Awaited<ReturnType<T>>>] extends [never]\n ? Exclude<Awaited<ReturnType<T>>, Response | null | void>\n : UnwrapJson<Awaited<ReturnType<T>>>\n : never\n\ndeclare module '@devlusoft/devix' {\n interface ApiRoutes {\n${routeLines}\n }\n}\n`\n}\n"],
4
+ "sourcesContent": ["import {readFileSync, readdirSync, statSync} from 'node:fs'\nimport {join, relative} from 'node:path'\nimport {extractHttpMethods} from './extract-methods'\nimport {buildRouteEntry} from './routes-dts'\nimport type {RouteEntry} from './routes-dts'\n\nfunction walkDir(dir: string, root: string): string[] {\n const entries: string[] = []\n for (const name of readdirSync(dir)) {\n const full = join(dir, name)\n if (statSync(full).isDirectory()) {\n entries.push(...walkDir(full, root))\n } else if (/\\.(ts|tsx)$/.test(name)) {\n entries.push(relative(root, full).replace(/\\\\/g, '/'))\n }\n }\n return entries\n}\n\nexport function scanApiFiles(appDir: string, projectRoot: string): RouteEntry[] {\n const apiDir = join(projectRoot, appDir, 'api')\n\n let files: string[]\n try {\n files = walkDir(apiDir, projectRoot)\n } catch {\n return []\n }\n\n return files\n .filter(f => !f.endsWith('middleware.ts') && !f.endsWith('middleware.tsx'))\n .flatMap(filePath => {\n try {\n const content = readFileSync(join(projectRoot, filePath), 'utf-8')\n const methods = extractHttpMethods(content)\n if (methods.length === 0) return []\n return [buildRouteEntry(filePath, `${appDir}/api`, methods)]\n } catch {\n return []\n }\n })\n}\n", "const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'] as const\nexport type HttpMethod = (typeof HTTP_METHODS)[number]\n\nconst METHOD_EXPORT_RE = /export\\s+(?:const|async\\s+function|function)\\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\\b/g\n\nfunction stripComments(content: string): string {\n return content\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n .replace(/\\/\\/.*$/gm, '')\n}\n\nexport function extractHttpMethods(content: string): HttpMethod[] {\n const found = new Set<HttpMethod>()\n for (const match of stripComments(content).matchAll(METHOD_EXPORT_RE)) {\n found.add(match[1] as HttpMethod)\n }\n return [...found]\n}\n", "export function routePattern(rel: string): string {\n return rel\n .replace(/\\.(tsx|ts|jsx|js)$/, '')\n .replace(/\\(.*?\\)\\//g, '')\n .replace(/^index$|\\/index$/, '')\n .replace(/\\[([^\\]]+)]/g, ':$1')\n || '/'\n}", "import {routePattern} from \"../utils/patterns\";\n\nexport interface ApiRoute {\n path: string\n key: string\n params: string[]\n regex: RegExp\n}\n\nexport interface ApiMiddleware {\n dir: string\n key: string\n}\n\nexport interface ApiResult {\n routes: ApiRoute[]\n middlewares: ApiMiddleware[]\n}\n\nexport function keyToRoutePattern(key: string, apiDir: string): string {\n const rel = key.slice(apiDir.length + 1).replace(/\\\\/g, '/')\n const pattern = routePattern(rel)\n return pattern === '/' ? '/api' : `/api/${pattern}`.replace('/api//', '/api/')\n}\n\nfunction keyToDir(key: string): string {\n return key.slice(0, key.lastIndexOf('/'))\n}\n\nlet cache: ApiResult | null = null\n\nexport function invalidateApiCache() {\n cache = null\n}\n\nexport function buildRoutes(routeKeys: string[], middlewareKeys: string[], apiDir: string): ApiResult {\n if (cache) return cache\n\n const routes: ApiRoute[] = []\n const middlewares: ApiMiddleware[] = []\n\n for (const key of middlewareKeys) {\n middlewares.push({dir: keyToDir(key), key})\n }\n\n for (const key of routeKeys) {\n const pattern = keyToRoutePattern(key, apiDir)\n const params = [...pattern.matchAll(/:([^/]+)/g)].map(m => m[1])\n const regexStr = pattern\n .replace(/:[^/]+/g, '([^/]+)')\n .replace(/\\//g, '\\\\/')\n routes.push({path: pattern, key, params, regex: new RegExp(`^${regexStr}$`)})\n }\n routes.sort((a, b) => {\n const aScore = (a.path.match(/:/g) || []).length\n const bScore = (b.path.match(/:/g) || []).length\n if (aScore !== bScore) return aScore - bScore\n return b.path.length - a.path.length\n })\n\n cache = {routes, middlewares}\n return cache\n}\n\nexport function collectMiddlewareChain(routeKey: string, middlewares: ApiMiddleware[]): ApiMiddleware[] {\n const routeDir = keyToDir(routeKey)\n\n return middlewares\n .filter(mw => routeDir.startsWith(mw.dir))\n .sort((a, b) => a.dir.split('/').length - b.dir.split('/').length)\n}\n\nexport function matchRoute(\n pathname: string,\n routes: ApiRoute[]\n): {route: ApiRoute; params: Record<string, string>} | null {\n for (const route of routes) {\n const match = pathname.match(route.regex)\n if (match) {\n const params: Record<string, string> = {}\n route.params.forEach((name, i) => {\n params[name] = decodeURIComponent(match[i + 1])\n })\n return {route, params}\n }\n }\n return null\n}\n", "import { keyToRoutePattern } from '../../server/api-router'\nimport type { HttpMethod } from './extract-methods'\n\nexport interface RouteEntry {\n filePath: string\n urlPattern: string\n identifier: string\n methods: HttpMethod[]\n}\n\nexport function filePathToIdentifier(filePath: string, apiDir: string): string {\n return '_api_' + filePath\n .slice(`${apiDir}/`.length)\n .replace(/\\.(ts|tsx)$/, '')\n .replace(/[^a-zA-Z0-9]/g, '_')\n}\n\nexport function buildRouteEntry(filePath: string, apiDir: string, methods: HttpMethod[]): RouteEntry {\n return {\n filePath,\n urlPattern: keyToRoutePattern(filePath, apiDir),\n identifier: filePathToIdentifier(filePath, apiDir),\n methods,\n }\n}\n\nexport function generateRoutesDts(entries: RouteEntry[], apiDir: string): string {\n if (entries.length === 0) {\n return `// auto-generado por devix \u2014 no editar\\ndeclare module '@devlusoft/devix' {\\n interface ApiRoutes {}\\n}\\n`\n }\n\n const imports = entries\n .map(e => {\n const importPath = '../' + e.filePath.replace(/\\.(ts|tsx)$/, '')\n return `import type * as ${e.identifier} from '${importPath}'`\n })\n .join('\\n')\n\n const routeLines = entries.flatMap(e =>\n e.methods.map(m =>\n ` '${m} ${e.urlPattern}': InferRoute<(typeof ${e.identifier})['${m}']>`\n )\n ).join('\\n')\n\n return `// auto-generado por devix \u2014 no editar\n${imports}\n\ntype JsonResponse<T> = Response & { readonly __body: T }\ntype UnwrapJson<T> = T extends JsonResponse<infer U> ? U : never\ntype InferFnReturn<T> = T extends (...args: any[]) => any\n ? [UnwrapJson<Awaited<ReturnType<T>>>] extends [never]\n ? Exclude<Awaited<ReturnType<T>>, Response | null | void>\n : UnwrapJson<Awaited<ReturnType<T>>>\n : never\ntype InferRoute<T> =\n T extends { readonly __return?: infer TReturn; readonly __body?: infer TBody }\n ? {\n __body: [TBody] extends [undefined] ? never : Exclude<TBody, undefined>\n __response: InferFnReturn<() => TReturn>\n }\n : InferFnReturn<T>\n\ndeclare module '@devlusoft/devix' {\n interface ApiRoutes {\n${routeLines}\n }\n}\n`\n}\n"],
5
5
  "mappings": "AAAA,OAAQ,gBAAAA,EAAc,eAAAC,EAAa,YAAAC,MAAe,UAClD,OAAQ,QAAAC,EAAM,YAAAC,MAAe,YCE7B,IAAMC,EAAmB,6FAEzB,SAASC,EAAcC,EAAyB,CAC5C,OAAOA,EACF,QAAQ,oBAAqB,EAAE,EAC/B,QAAQ,YAAa,EAAE,CAChC,CAEO,SAASC,EAAmBD,EAA+B,CAC9D,IAAME,EAAQ,IAAI,IAClB,QAAWC,KAASJ,EAAcC,CAAO,EAAE,SAASF,CAAgB,EAChEI,EAAM,IAAIC,EAAM,CAAC,CAAe,EAEpC,MAAO,CAAC,GAAGD,CAAK,CACpB,CCjBO,SAASE,EAAaC,EAAqB,CAC9C,OAAOA,EACE,QAAQ,qBAAsB,EAAE,EAChC,QAAQ,aAAc,EAAE,EACxB,QAAQ,mBAAoB,EAAE,EAC9B,QAAQ,eAAgB,KAAK,GAC/B,GACX,CCYO,SAASC,EAAkBC,EAAaC,EAAwB,CACnE,IAAMC,EAAMF,EAAI,MAAMC,EAAO,OAAS,CAAC,EAAE,QAAQ,MAAO,GAAG,EACrDE,EAAUC,EAAaF,CAAG,EAChC,OAAOC,IAAY,IAAM,OAAS,QAAQA,CAAO,GAAG,QAAQ,SAAU,OAAO,CACjF,CCbO,SAASE,EAAqBC,EAAkBC,EAAwB,CAC3E,MAAO,QAAUD,EACZ,MAAM,GAAGC,CAAM,IAAI,MAAM,EACzB,QAAQ,cAAe,EAAE,EACzB,QAAQ,gBAAiB,GAAG,CACrC,CAEO,SAASC,EAAgBF,EAAkBC,EAAgBE,EAAmC,CACjG,MAAO,CACH,SAAAH,EACA,WAAYI,EAAkBJ,EAAUC,CAAM,EAC9C,WAAYF,EAAqBC,EAAUC,CAAM,EACjD,QAAAE,CACJ,CACJ,CJlBA,SAASE,EAAQC,EAAaC,EAAwB,CAClD,IAAMC,EAAoB,CAAC,EAC3B,QAAWC,KAAQC,EAAYJ,CAAG,EAAG,CACjC,IAAMK,EAAOC,EAAKN,EAAKG,CAAI,EACvBI,EAASF,CAAI,EAAE,YAAY,EAC3BH,EAAQ,KAAK,GAAGH,EAAQM,EAAMJ,CAAI,CAAC,EAC5B,cAAc,KAAKE,CAAI,GAC9BD,EAAQ,KAAKM,EAASP,EAAMI,CAAI,EAAE,QAAQ,MAAO,GAAG,CAAC,CAE7D,CACA,OAAOH,CACX,CAEO,SAASO,EAAaC,EAAgBC,EAAmC,CAC5E,IAAMC,EAASN,EAAKK,EAAaD,EAAQ,KAAK,EAE1CG,EACJ,GAAI,CACAA,EAAQd,EAAQa,EAAQD,CAAW,CACvC,MAAQ,CACJ,MAAO,CAAC,CACZ,CAEA,OAAOE,EACF,OAAOC,GAAK,CAACA,EAAE,SAAS,eAAe,GAAK,CAACA,EAAE,SAAS,gBAAgB,CAAC,EACzE,QAAQC,GAAY,CACjB,GAAI,CACA,IAAMC,EAAUC,EAAaX,EAAKK,EAAaI,CAAQ,EAAG,OAAO,EAC3DG,EAAUC,EAAmBH,CAAO,EAC1C,OAAIE,EAAQ,SAAW,EAAU,CAAC,EAC3B,CAACE,EAAgBL,EAAU,GAAGL,CAAM,OAAQQ,CAAO,CAAC,CAC/D,MAAQ,CACJ,MAAO,CAAC,CACZ,CACJ,CAAC,CACT",
6
6
  "names": ["readFileSync", "readdirSync", "statSync", "join", "relative", "METHOD_EXPORT_RE", "stripComments", "content", "extractHttpMethods", "found", "match", "routePattern", "rel", "keyToRoutePattern", "key", "apiDir", "rel", "pattern", "routePattern", "filePathToIdentifier", "filePath", "apiDir", "buildRouteEntry", "methods", "keyToRoutePattern", "walkDir", "dir", "root", "entries", "name", "readdirSync", "full", "join", "statSync", "relative", "scanApiFiles", "appDir", "projectRoot", "apiDir", "files", "f", "filePath", "content", "readFileSync", "methods", "extractHttpMethods", "buildRouteEntry"]
7
7
  }
@@ -1,4 +1,4 @@
1
- import{mergeConfig as B}from"vite";import Z from"@vitejs/plugin-react";import{fileURLToPath as K}from"node:url";import{dirname as Q,resolve as c}from"node:path";function T({cssUrls:t}){return`
1
+ import{mergeConfig as Y}from"vite";import Z from"@vitejs/plugin-react";import{fileURLToPath as K}from"node:url";import{dirname as Q,resolve as u}from"node:path";function $({cssUrls:t}){return`
2
2
  ${t.map(r=>`import '${r}'`).join(`
3
3
  `)}
4
4
  import "@vitejs/plugin-react/preamble"
@@ -133,16 +133,23 @@ ${r}
133
133
 
134
134
  type JsonResponse<T> = Response & { readonly __body: T }
135
135
  type UnwrapJson<T> = T extends JsonResponse<infer U> ? U : never
136
- type InferRoute<T> = T extends (...args: any[]) => any
136
+ type InferFnReturn<T> = T extends (...args: any[]) => any
137
137
  ? [UnwrapJson<Awaited<ReturnType<T>>>] extends [never]
138
138
  ? Exclude<Awaited<ReturnType<T>>, Response | null | void>
139
139
  : UnwrapJson<Awaited<ReturnType<T>>>
140
140
  : never
141
+ type InferRoute<T> =
142
+ T extends { readonly __return?: infer TReturn; readonly __body?: infer TBody }
143
+ ? {
144
+ __body: [TBody] extends [undefined] ? never : Exclude<TBody, undefined>
145
+ __response: InferFnReturn<() => TReturn>
146
+ }
147
+ : InferFnReturn<T>
141
148
 
142
149
  declare module '@devlusoft/devix' {
143
150
  interface ApiRoutes {
144
151
  ${i}
145
152
  }
146
153
  }
147
- `}function b(t,e){let r=[];for(let i of N(t)){let o=h(t,i);V(o).isDirectory()?r.push(...b(o,e)):/\.(ts|tsx)$/.test(i)&&r.push(J(e,o).replace(/\\/g,"/"))}return r}function x(t,e){let r=h(e,t,"api"),i;try{i=b(r,e)}catch{return[]}return i.filter(o=>!o.endsWith("middleware.ts")&&!o.endsWith("middleware.tsx")).flatMap(o=>{try{let s=W(h(e,o),"utf-8"),u=L(s);return u.length===0?[]:[M(o,`${t}/api`,u)]}catch{return[]}})}import{mkdirSync as X,readFileSync as z,writeFileSync as G,existsSync as Y}from"node:fs";import{join as O}from"node:path";function y(t,e){let r=O(e,".devix"),i=O(r,"routes.d.ts");return X(r,{recursive:!0}),Y(i)&&z(i,"utf-8")===t?!1:(G(i,t,"utf-8"),!0)}var R=Q(K(import.meta.url)),E="virtual:devix/entry-client",P="virtual:devix/client-routes",_="virtual:devix/render",v="virtual:devix/api",$="virtual:devix/context";function Ht(t){let e=t.appDir??"app",r=`${e}/pages`,i=(t.css??[]).map(n=>n.startsWith("/")?n:`/${n.replace(/^\.\//,"")}`),o=c(R,"../server/render.js").replace(/\\/g,"/"),s=c(R,"../server/api.js").replace(/\\/g,"/"),u=c(R,"../runtime/client-router.js").replace(/\\/g,"/"),I={name:"devix",enforce:"pre",resolveId(n){if(n===E)return`\0${E}`;if(n===P)return`\0${P}`;if(n===_)return`\0${_}`;if(n===v)return`\0${v}`;if(n===$)return`\0${$}`},load(n){if(n===`\0${E}`)return T({cssUrls:i});if(n===`\0${P}`)return A({pagesDir:r,matcherPath:u});if(n===`\0${_}`)return w({pagesDir:r,renderPath:o});if(n===`\0${v}`)return D({apiPath:s,appDir:e});if(n===`\0${$}`)return S()},buildStart(){let n=process.cwd(),l=x(e,n);y(f(l,`${e}/api`),n)},configureServer(n){let l=process.cwd(),p=()=>{let a=x(e,l);y(f(a,`${e}/api`),l)};n.watcher.on("add",a=>{a.startsWith(c(l,r))&&d(),a.includes(`${e}/api`)&&(m(),p())}),n.watcher.on("unlink",a=>{a.startsWith(c(l,r))&&d(),a.includes(`${e}/api`)&&(m(),p())}),n.watcher.on("change",a=>{a.includes(`${e}/api`)&&!a.endsWith("middleware.ts")&&p()})}},U={plugins:[Z(),I],ssr:{noExternal:["@devlusoft/devix"]},...t.envPrefix?{envPrefix:t.envPrefix}:{}};return B(U,t.vite??{})}export{Ht as devix};
154
+ `}function b(t,e){let r=[];for(let i of N(t)){let o=h(t,i);V(o).isDirectory()?r.push(...b(o,e)):/\.(ts|tsx)$/.test(i)&&r.push(J(e,o).replace(/\\/g,"/"))}return r}function x(t,e){let r=h(e,t,"api"),i;try{i=b(r,e)}catch{return[]}return i.filter(o=>!o.endsWith("middleware.ts")&&!o.endsWith("middleware.tsx")).flatMap(o=>{try{let s=W(h(e,o),"utf-8"),c=L(s);return c.length===0?[]:[M(o,`${t}/api`,c)]}catch{return[]}})}import{mkdirSync as B,readFileSync as X,writeFileSync as z,existsSync as G}from"node:fs";import{join as I}from"node:path";function y(t,e){let r=I(e,".devix"),i=I(r,"routes.d.ts");return B(r,{recursive:!0}),G(i)&&X(i,"utf-8")===t?!1:(z(i,t,"utf-8"),!0)}var R=Q(K(import.meta.url)),E="virtual:devix/entry-client",P="virtual:devix/client-routes",_="virtual:devix/render",T="virtual:devix/api",v="virtual:devix/context";function Ht(t){let e=t.appDir??"app",r=`${e}/pages`,i=(t.css??[]).map(n=>n.startsWith("/")?n:`/${n.replace(/^\.\//,"")}`),o=u(R,"../server/render.js").replace(/\\/g,"/"),s=u(R,"../server/api.js").replace(/\\/g,"/"),c=u(R,"../runtime/client-router.js").replace(/\\/g,"/"),O={name:"devix",enforce:"pre",resolveId(n){if(n===E)return`\0${E}`;if(n===P)return`\0${P}`;if(n===_)return`\0${_}`;if(n===T)return`\0${T}`;if(n===v)return`\0${v}`},load(n){if(n===`\0${E}`)return $({cssUrls:i});if(n===`\0${P}`)return A({pagesDir:r,matcherPath:c});if(n===`\0${_}`)return w({pagesDir:r,renderPath:o});if(n===`\0${T}`)return D({apiPath:s,appDir:e});if(n===`\0${v}`)return S()},buildStart(){let n=process.cwd(),l=x(e,n);y(f(l,`${e}/api`),n)},configureServer(n){let l=process.cwd(),p=()=>{let a=x(e,l);y(f(a,`${e}/api`),l)};n.watcher.on("add",a=>{a.startsWith(u(l,r))&&d(),a.includes(`${e}/api`)&&(m(),p())}),n.watcher.on("unlink",a=>{a.startsWith(u(l,r))&&d(),a.includes(`${e}/api`)&&(m(),p())}),n.watcher.on("change",a=>{a.includes(`${e}/api`)&&!a.endsWith("middleware.ts")&&p()})}},U={plugins:[Z(),O],ssr:{noExternal:["@devlusoft/devix"]},...t.envPrefix?{envPrefix:t.envPrefix}:{}};return Y(U,t.vite??{})}export{Ht as devix};
148
155
  //# sourceMappingURL=index.js.map