@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
package/dist/cli/build.js CHANGED
@@ -1,4 +1,4 @@
1
- import{writeFileSync as it}from"node:fs";import{resolve as st}from"node:path";import{build as H}from"vite";import{mergeConfig as et}from"vite";import rt from"@vitejs/plugin-react";import{fileURLToPath as nt}from"node:url";import{dirname as ot,resolve as l}from"node:path";function T({cssUrls:t}){return`
1
+ import{writeFileSync as it}from"node:fs";import{resolve as st}from"node:path";import{build as k}from"vite";import{mergeConfig as et}from"vite";import rt from"@vitejs/plugin-react";import{fileURLToPath as nt}from"node:url";import{dirname as ot,resolve as l}from"node:path";function $({cssUrls:t}){return`
2
2
  ${t.map(r=>`import '${r}'`).join(`
3
3
  `)}
4
4
  import "@vitejs/plugin-react/preamble"
@@ -120,9 +120,9 @@ const _glob = {
120
120
  export function handleApiRequest(url, request) {
121
121
  return _handleApiRequest(url, request, _glob)
122
122
  }
123
- `}function d(t){return t.replace(/\.(tsx|ts|jsx|js)$/,"").replace(/\(.*?\)\//g,"").replace(/^index$|\/index$/,"").replace(/\[([^\]]+)]/g,":$1")||"/"}var N=null;function m(){N=null}function b(t,e){let r=t.slice(e.length+1).replace(/\\/g,"/"),i=d(r);return i==="/"?"/api":`/api/${i}`.replace("/api//","/api/")}var W=null;function f(){W=null}function S(){return`
123
+ `}function g(t){return t.replace(/\.(tsx|ts|jsx|js)$/,"").replace(/\(.*?\)\//g,"").replace(/^index$|\/index$/,"").replace(/\[([^\]]+)]/g,":$1")||"/"}var N=null;function m(){N=null}function b(t,e){let r=t.slice(e.length+1).replace(/\\/g,"/"),i=g(r);return i==="/"?"/api":`/api/${i}`.replace("/api//","/api/")}var W=null;function f(){W=null}function S(){return`
124
124
  export {RouterContext} from '@devlusoft/devix/runtime/context'
125
- `}import{readFileSync as z,readdirSync as G,statSync as Y}from"node:fs";import{join as x,relative as B}from"node:path";var V=/export\s+(?:const|async\s+function|function)\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b/g;function J(t){return t.replace(/\/\*[\s\S]*?\*\//g,"").replace(/\/\/.*$/gm,"")}function L(t){let e=new Set;for(let r of J(t).matchAll(V))e.add(r[1]);return[...e]}function X(t,e){return"_api_"+t.slice(`${e}/`.length).replace(/\.(ts|tsx)$/,"").replace(/[^a-zA-Z0-9]/g,"_")}function M(t,e,r){return{filePath:t,urlPattern:b(t,e),identifier:X(t,e),methods:r}}function h(t,e){if(t.length===0)return`// auto-generado por devix \u2014 no editar
125
+ `}import{readFileSync as X,readdirSync as z,statSync as G}from"node:fs";import{join as x,relative as Y}from"node:path";var V=/export\s+(?:const|async\s+function|function)\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b/g;function J(t){return t.replace(/\/\*[\s\S]*?\*\//g,"").replace(/\/\/.*$/gm,"")}function I(t){let e=new Set;for(let r of J(t).matchAll(V))e.add(r[1]);return[...e]}function B(t,e){return"_api_"+t.slice(`${e}/`.length).replace(/\.(ts|tsx)$/,"").replace(/[^a-zA-Z0-9]/g,"_")}function L(t,e,r){return{filePath:t,urlPattern:b(t,e),identifier:B(t,e),methods:r}}function h(t,e){if(t.length===0)return`// auto-generado por devix \u2014 no editar
126
126
  declare module '@devlusoft/devix' {
127
127
  interface ApiRoutes {}
128
128
  }
@@ -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 O(t,e){let r=[];for(let i of G(t)){let o=x(t,i);Y(o).isDirectory()?r.push(...O(o,e)):/\.(ts|tsx)$/.test(i)&&r.push(B(e,o).replace(/\\/g,"/"))}return r}function y(t,e){let r=x(e,t,"api"),i;try{i=O(r,e)}catch{return[]}return i.filter(o=>!o.endsWith("middleware.ts")&&!o.endsWith("middleware.tsx")).flatMap(o=>{try{let a=z(x(e,o),"utf-8"),p=L(a);return p.length===0?[]:[M(o,`${t}/api`,p)]}catch{return[]}})}import{mkdirSync as Z,readFileSync as K,writeFileSync as Q,existsSync as tt}from"node:fs";import{join as I}from"node:path";function R(t,e){let r=I(e,".devix"),i=I(r,"routes.d.ts");return Z(r,{recursive:!0}),tt(i)&&K(i,"utf-8")===t?!1:(Q(i,t,"utf-8"),!0)}var v=ot(nt(import.meta.url)),E="virtual:devix/entry-client",P="virtual:devix/client-routes",_="virtual:devix/render",w="virtual:devix/api",$="virtual:devix/context";function U(t){let e=t.appDir??"app",r=`${e}/pages`,i=(t.css??[]).map(n=>n.startsWith("/")?n:`/${n.replace(/^\.\//,"")}`),o=l(v,"../server/render.js").replace(/\\/g,"/"),a=l(v,"../server/api.js").replace(/\\/g,"/"),p=l(v,"../runtime/client-router.js").replace(/\\/g,"/"),j={name:"devix",enforce:"pre",resolveId(n){if(n===E)return`\0${E}`;if(n===P)return`\0${P}`;if(n===_)return`\0${_}`;if(n===w)return`\0${w}`;if(n===$)return`\0${$}`},load(n){if(n===`\0${E}`)return T({cssUrls:i});if(n===`\0${P}`)return A({pagesDir:r,matcherPath:p});if(n===`\0${_}`)return D({pagesDir:r,renderPath:o});if(n===`\0${w}`)return C({apiPath:a,appDir:e});if(n===`\0${$}`)return S()},buildStart(){let n=process.cwd(),u=y(e,n);R(h(u,`${e}/api`),n)},configureServer(n){let u=process.cwd(),g=()=>{let s=y(e,u);R(h(s,`${e}/api`),u)};n.watcher.on("add",s=>{s.startsWith(l(u,r))&&m(),s.includes(`${e}/api`)&&(f(),g())}),n.watcher.on("unlink",s=>{s.startsWith(l(u,r))&&m(),s.includes(`${e}/api`)&&(f(),g())}),n.watcher.on("change",s=>{s.includes(`${e}/api`)&&!s.endsWith("middleware.ts")&&g()})}},q={plugins:[rt(),j],ssr:{noExternal:["@devlusoft/devix"]},...t.envPrefix?{envPrefix:t.envPrefix}:{}};return et(q,t.vite??{})}function k(t){if(typeof t=="number")return t;let e=t.trim().match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h)?$/);if(!e)throw new Error(`[devix] Invalid duration: "${t}". Use a number (ms) or a string like "5s", "2m", "500ms".`);let r=parseFloat(e[1]);switch(e[2]){case"h":return r*36e5;case"m":return r*6e4;case"s":return r*1e3;default:return r}}var c=(await import(`${process.cwd()}/devix.config.ts`)).default,F=U(c);await H({...F,configFile:!1,build:{outDir:"dist/client",manifest:!0,rolldownOptions:{input:"virtual:devix/entry-client"}}});await H({...F,configFile:!1,build:{ssr:!0,outDir:"dist/server",rolldownOptions:{input:{render:"virtual:devix/render",api:"virtual:devix/api"}}}});var at={port:c.port??3e3,host:c.host??!1,loaderTimeout:k(c.loaderTimeout??1e4),output:c.output??"server"};it(st(process.cwd(),"dist/devix.config.json"),JSON.stringify(at,null,2),"utf-8");
154
+ `}function M(t,e){let r=[];for(let i of z(t)){let o=x(t,i);G(o).isDirectory()?r.push(...M(o,e)):/\.(ts|tsx)$/.test(i)&&r.push(Y(e,o).replace(/\\/g,"/"))}return r}function y(t,e){let r=x(e,t,"api"),i;try{i=M(r,e)}catch{return[]}return i.filter(o=>!o.endsWith("middleware.ts")&&!o.endsWith("middleware.tsx")).flatMap(o=>{try{let a=X(x(e,o),"utf-8"),p=I(a);return p.length===0?[]:[L(o,`${t}/api`,p)]}catch{return[]}})}import{mkdirSync as Z,readFileSync as K,writeFileSync as Q,existsSync as tt}from"node:fs";import{join as O}from"node:path";function R(t,e){let r=O(e,".devix"),i=O(r,"routes.d.ts");return Z(r,{recursive:!0}),tt(i)&&K(i,"utf-8")===t?!1:(Q(i,t,"utf-8"),!0)}var v=ot(nt(import.meta.url)),_="virtual:devix/entry-client",E="virtual:devix/client-routes",P="virtual:devix/render",T="virtual:devix/api",w="virtual:devix/context";function U(t){let e=t.appDir??"app",r=`${e}/pages`,i=(t.css??[]).map(n=>n.startsWith("/")?n:`/${n.replace(/^\.\//,"")}`),o=l(v,"../server/render.js").replace(/\\/g,"/"),a=l(v,"../server/api.js").replace(/\\/g,"/"),p=l(v,"../runtime/client-router.js").replace(/\\/g,"/"),j={name:"devix",enforce:"pre",resolveId(n){if(n===_)return`\0${_}`;if(n===E)return`\0${E}`;if(n===P)return`\0${P}`;if(n===T)return`\0${T}`;if(n===w)return`\0${w}`},load(n){if(n===`\0${_}`)return $({cssUrls:i});if(n===`\0${E}`)return A({pagesDir:r,matcherPath:p});if(n===`\0${P}`)return D({pagesDir:r,renderPath:o});if(n===`\0${T}`)return C({apiPath:a,appDir:e});if(n===`\0${w}`)return S()},buildStart(){let n=process.cwd(),u=y(e,n);R(h(u,`${e}/api`),n)},configureServer(n){let u=process.cwd(),d=()=>{let s=y(e,u);R(h(s,`${e}/api`),u)};n.watcher.on("add",s=>{s.startsWith(l(u,r))&&m(),s.includes(`${e}/api`)&&(f(),d())}),n.watcher.on("unlink",s=>{s.startsWith(l(u,r))&&m(),s.includes(`${e}/api`)&&(f(),d())}),n.watcher.on("change",s=>{s.includes(`${e}/api`)&&!s.endsWith("middleware.ts")&&d()})}},q={plugins:[rt(),j],ssr:{noExternal:["@devlusoft/devix"]},...t.envPrefix?{envPrefix:t.envPrefix}:{}};return et(q,t.vite??{})}function F(t){if(typeof t=="number")return t;let e=t.trim().match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h)?$/);if(!e)throw new Error(`[devix] Invalid duration: "${t}". Use a number (ms) or a string like "5s", "2m", "500ms".`);let r=parseFloat(e[1]);switch(e[2]){case"h":return r*36e5;case"m":return r*6e4;case"s":return r*1e3;default:return r}}var c=(await import(`${process.cwd()}/devix.config.ts`)).default,H=U(c);await k({...H,configFile:!1,build:{outDir:"dist/client",manifest:!0,rolldownOptions:{input:"virtual:devix/entry-client"}}});await k({...H,configFile:!1,build:{ssr:!0,outDir:"dist/server",rolldownOptions:{input:{render:"virtual:devix/render",api:"virtual:devix/api"}}}});var at={port:c.port??3e3,host:c.host??!1,loaderTimeout:F(c.loaderTimeout??1e4),output:c.output??"server"};it(st(process.cwd(),"dist/devix.config.json"),JSON.stringify(at,null,2),"utf-8");
148
155
  //# sourceMappingURL=build.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/cli/build.ts", "../../src/vite/index.ts", "../../src/vite/codegen/entry-client.ts", "../../src/vite/codegen/client-routes.ts", "../../src/vite/codegen/render.ts", "../../src/vite/codegen/api.ts", "../../src/utils/patterns.ts", "../../src/server/pages-router.ts", "../../src/server/api-router.ts", "../../src/vite/codegen/context.ts", "../../src/vite/codegen/scan-api.ts", "../../src/vite/codegen/extract-methods.ts", "../../src/vite/codegen/routes-dts.ts", "../../src/vite/codegen/write-routes-dts.ts", "../../src/utils/duration.ts"],
4
- "sourcesContent": ["import {writeFileSync} from 'node:fs'\nimport {resolve} from 'node:path'\nimport {build} from 'vite'\nimport type {DevixConfig} from '../config'\nimport {devix} from '../vite'\nimport {parseDuration} from '../utils/duration'\n\nconst config: DevixConfig = (await import(`${process.cwd()}/devix.config.ts`)).default\nconst baseConfig = devix(config)\n\nawait build({\n ...baseConfig,\n configFile: false,\n build: {\n outDir: 'dist/client',\n manifest: true,\n rolldownOptions: {\n input: 'virtual:devix/entry-client',\n },\n },\n})\n\nawait build({\n ...baseConfig,\n configFile: false,\n build: {\n ssr: true,\n outDir: 'dist/server',\n rolldownOptions: {\n input: {\n render: 'virtual:devix/render',\n api: 'virtual:devix/api',\n },\n },\n },\n})\n\nconst runtimeConfig = {\n port: config.port ?? 3000,\n host: config.host ?? false,\n loaderTimeout: parseDuration(config.loaderTimeout ?? 10_000),\n output: config.output ?? 'server',\n}\n\nwriteFileSync(\n resolve(process.cwd(), 'dist/devix.config.json'),\n JSON.stringify(runtimeConfig, null, 2),\n 'utf-8'\n)\n\n\nexport {}", "import {UserConfig, Plugin, mergeConfig} from 'vite'\nimport type {DevixConfig} from '../config'\nimport react from '@vitejs/plugin-react'\nimport {fileURLToPath} from 'node:url'\nimport {dirname, resolve} from 'node:path'\nimport {generateEntryClient} from './codegen/entry-client'\nimport {generateClientRoutes} from './codegen/client-routes'\nimport {generateRender} from './codegen/render'\nimport {generateApi} from './codegen/api'\nimport {invalidatePagesCache} from \"../server/pages-router\";\nimport {invalidateApiCache} from \"../server/api-router\";\nimport {generateContext} from \"./codegen/context\";\nimport {scanApiFiles} from \"./codegen/scan-api\";\nimport {generateRoutesDts} from \"./codegen/routes-dts\";\nimport {writeRoutesDts} from \"./codegen/write-routes-dts\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nconst VIRTUAL_ENTRY_CLIENT = 'virtual:devix/entry-client'\nconst VIRTUAL_CLIENT_ROUTES = 'virtual:devix/client-routes'\nconst VIRTUAL_RENDER = 'virtual:devix/render'\nconst VIRTUAL_API = 'virtual:devix/api'\nconst VIRTUAL_CONTEXT = 'virtual:devix/context'\n\nexport function devix(config: DevixConfig): UserConfig {\n const appDir = config.appDir ?? 'app'\n const pagesDir = `${appDir}/pages`\n const cssUrls = (config.css ?? []).map(u => u.startsWith('/') ? u : `/${u.replace(/^\\.\\//, '')}`)\n\n const renderPath = resolve(__dirname, '../server/render.js').replace(/\\\\/g, '/')\n const apiPath = resolve(__dirname, '../server/api.js').replace(/\\\\/g, '/')\n const matcherPath = resolve(__dirname, '../runtime/client-router.js').replace(/\\\\/g, '/')\n\n const virtualPlugin: Plugin = {\n name: 'devix',\n enforce: 'pre',\n\n resolveId(id) {\n if (id === VIRTUAL_ENTRY_CLIENT) return `\\0${VIRTUAL_ENTRY_CLIENT}`\n if (id === VIRTUAL_CLIENT_ROUTES) return `\\0${VIRTUAL_CLIENT_ROUTES}`\n if (id === VIRTUAL_RENDER) return `\\0${VIRTUAL_RENDER}`\n if (id === VIRTUAL_API) return `\\0${VIRTUAL_API}`\n if (id === VIRTUAL_CONTEXT) return `\\0${VIRTUAL_CONTEXT}`\n },\n\n load(id) {\n if (id === `\\0${VIRTUAL_ENTRY_CLIENT}`)\n return generateEntryClient({cssUrls})\n if (id === `\\0${VIRTUAL_CLIENT_ROUTES}`)\n return generateClientRoutes({pagesDir, matcherPath})\n if (id === `\\0${VIRTUAL_RENDER}`)\n return generateRender({pagesDir, renderPath})\n if (id === `\\0${VIRTUAL_API}`)\n return generateApi({apiPath, appDir})\n if (id === `\\0${VIRTUAL_CONTEXT}`)\n return generateContext()\n },\n\n buildStart() {\n const root = process.cwd()\n const entries = scanApiFiles(appDir, root)\n writeRoutesDts(generateRoutesDts(entries, `${appDir}/api`), root)\n },\n\n configureServer(server) {\n const root = process.cwd()\n\n const regenerateDts = () => {\n const entries = scanApiFiles(appDir, root)\n writeRoutesDts(generateRoutesDts(entries, `${appDir}/api`), root)\n }\n\n server.watcher.on('add', (file) => {\n if (file.startsWith(resolve(root, pagesDir))) invalidatePagesCache()\n if (file.includes(`${appDir}/api`)) { invalidateApiCache(); regenerateDts() }\n })\n server.watcher.on('unlink', (file) => {\n if (file.startsWith(resolve(root, pagesDir))) invalidatePagesCache()\n if (file.includes(`${appDir}/api`)) { invalidateApiCache(); regenerateDts() }\n })\n server.watcher.on('change', (file) => {\n if (file.includes(`${appDir}/api`) && !file.endsWith('middleware.ts')) {\n regenerateDts()\n }\n })\n },\n }\n\n const base: UserConfig = {\n plugins: [react(), virtualPlugin],\n ssr: {noExternal: ['@devlusoft/devix']},\n ...(config.envPrefix ? {envPrefix: config.envPrefix} : {}),\n }\n\n return mergeConfig(base, config.vite ?? {})\n}", "interface EntryClientOptions {\n cssUrls: string[]\n}\n\nexport function generateEntryClient({cssUrls}: EntryClientOptions): string {\n const cssImports = cssUrls.map(u => `import '${u}'`).join('\\n')\n\n return `\n${cssImports}\nimport \"@vitejs/plugin-react/preamble\"\nimport React from \"react\"\nimport {hydrateRoot, createRoot} from 'react-dom/client'\nimport {matchClientRoute, loadErrorPage, getDefaultErrorPage} from 'virtual:devix/client-routes'\nimport {RouterProvider} from '@devlusoft/devix'\n\nconst root = document.getElementById('devix-root')\n\nif (!window.__DEVIX__) {\n const ErrorPage = getDefaultErrorPage()\n createRoot(root).render(React.createElement(ErrorPage, {statusCode: 500, message: 'Server error'}))\n} else {\n const {metadata, viewport, clientEntry} = window.__DEVIX__\n const loaderData = window.__LOADER_DATA__\n const layoutsData = window.__LAYOUTS_DATA__ ?? []\n\n const matched = matchClientRoute(window.location.pathname)\n\n if (matched) {\n const [pageMod, ...layoutMods] = await Promise.all([\n matched.load(),\n ...matched.loadLayouts.map(l => l()),\n ])\n hydrateRoot(\n root,\n React.createElement(RouterProvider, {\n clientEntry,\n initialData: loaderData,\n initialParams: matched.params,\n initialPage: pageMod.default,\n initialLayouts: layoutMods.map(m => m.default),\n initialLayoutsData: layoutsData,\n initialMeta: metadata,\n initialViewport: viewport,\n })\n )\n } else {\n const ErrorPage = await loadErrorPage() ?? getDefaultErrorPage()\n createRoot(root).render(\n React.createElement(RouterProvider, {\n clientEntry,\n initialData: null,\n initialParams: {},\n initialPage: () => null,\n initialLayouts: [],\n initialLayoutsData: [],\n initialMeta: null,\n initialError: {statusCode: 404, message: 'Not found'},\n initialErrorPage: ErrorPage,\n })\n )\n }\n}\n`\n}", "interface ClientRoutesOptions {\n pagesDir: string\n matcherPath: string\n}\n\nexport function generateClientRoutes({pagesDir, matcherPath}: ClientRoutesOptions) {\n return `\nimport React from 'react'\nimport { createMatcher } from '${matcherPath}'\nconst pageFiles = import.meta.glob(['/${pagesDir}/**/*.tsx', '!**/error.tsx', '!**/layout.tsx'])\nconst layoutFiles = import.meta.glob('/${pagesDir}/**/layout.tsx')\nconst errorFiles = import.meta.glob('/${pagesDir}/**/error.tsx')\n\nexport const matchClientRoute = createMatcher(pageFiles, layoutFiles)\n\nexport async function loadErrorPage() {\n const key = Object.keys(errorFiles)[0]\n if (!key) return null\n const mod = await errorFiles[key]()\n return mod?.default ?? null\n}\n\nexport function getDefaultErrorPage() {\n return function DefaultError({ statusCode, message }) {\n return React.createElement('main', {\n style: { minHeight: '100dvh', display: 'flex', flexDirection: 'column', \n alignItems: 'center', justifyContent: 'center', gap: '8px',\n fontFamily: 'system-ui, sans-serif' }\n },\n React.createElement('h1', {style: {fontSize: '4rem', fontWeight: 700}}, statusCode),\n React.createElement('p', {style: {color: '#666'}}, message ?? 'An unexpected error occurred'),\n )\n }\n}\n`\n}", "interface RenderOptions {\n pagesDir: string\n renderPath: string\n}\n\nexport function generateRender({pagesDir, renderPath}: RenderOptions): string {\n return `\nimport { render as _render, runLoader as _runLoader, getStaticRoutes as _getStaticRoutes } from '${renderPath}'\n\nconst _pages = import.meta.glob(['/${pagesDir}/**/*.tsx', '!**/error.tsx', '!**/layout.tsx'])\nconst _layouts = import.meta.glob('/${pagesDir}/**/layout.tsx')\n\nconst _glob = {\n pages: _pages,\n layouts: _layouts,\n pagesDir: '/${pagesDir}',\n}\n\nexport function render(url, request, options) {\n return _render(url, request, _glob, options)\n}\n\nexport function runLoader(url, request, options) {\n return _runLoader(url, request, _glob, options)\n}\n\nexport function getStaticRoutes() {\n return _getStaticRoutes(_glob)\n}\n`\n}\n", "interface ApiOptions {\n apiPath: string\n appDir: string\n}\n\nexport function generateApi({apiPath, appDir}: ApiOptions): string {\n return `\nimport { handleApiRequest as _handleApiRequest } from '${apiPath}'\n\nconst _routes = import.meta.glob(['/${appDir}/api/**/*.ts', '!**/middleware.ts'])\nconst _middlewares = import.meta.glob('/${appDir}/api/**/middleware.ts')\n\nconst _glob = {\n routes: _routes,\n middlewares: _middlewares,\n apiDir: '/${appDir}/api',\n}\n\nexport function handleApiRequest(url, request) {\n return _handleApiRequest(url, request, _glob)\n}\n`\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 Page {\n path: string\n key: string\n params: string[]\n regex: RegExp\n}\n\nexport interface Layout {\n dir: string\n key: string\n}\n\nexport interface PagesResult {\n pages: Page[]\n layouts: Layout[]\n}\n\nfunction keyToRoutePattern(key: string, pagesDir: string): string {\n const rel = key.slice(pagesDir.length + 1).replace(/\\\\/g, '/')\n const pattern = routePattern(rel)\n return pattern === \"/\" ? \"/\" : `/${pattern}`\n}\n\nfunction keyToDir(key: string): string {\n return key.slice(0, key.lastIndexOf('/'))\n}\n\nlet cache: PagesResult | null = null\n\nexport function invalidatePagesCache() {\n cache = null\n}\n\nexport function buildPages(pageKeys: string[], layoutKeys: string[], pagesDir: string): PagesResult {\n if (cache) return cache\n\n const pages: Page[] = []\n const layouts: Layout[] = []\n\n for (const key of layoutKeys) {\n layouts.push({dir: keyToDir(key), key})\n }\n\n for (const key of pageKeys) {\n const pattern = keyToRoutePattern(key, pagesDir)\n const params = [...pattern.matchAll(/:([^/]+)/g)].map(m => m[1])\n const regexStr = pattern\n .replace(/:[^/]+/g, '([^/]+)')\n .replace(/\\//g, '\\\\/')\n pages.push({path: pattern, key, params, regex: new RegExp(`^${regexStr}$`)})\n }\n\n pages.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 = {pages, layouts}\n return cache\n}\n\nexport function collectLayoutChain(pageKey: string, layouts: Layout[]): Layout[] {\n const pageDir = keyToDir(pageKey)\n\n return layouts\n .filter(layout => pageDir.startsWith(layout.dir))\n .sort((a, b) => a.dir.split('/').length - b.dir.split('/').length)\n}\n\nexport function matchPage(pathname: string, pages: Page[]): {\n page: Page\n params: Record<string, string>\n} | null {\n for (const page of pages) {\n const match = pathname.match(page.regex)\n if (match) {\n const params: Record<string, string> = {}\n page.params.forEach((name, i) => {\n params[name] = decodeURIComponent(match[i + 1])\n })\n return {page, params}\n }\n }\n return null\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 function generateContext(): string {\n return `\nexport {RouterContext} from '@devlusoft/devix/runtime/context'\n`\n}", "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", "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", "import {mkdirSync, readFileSync, writeFileSync, existsSync} from 'node:fs'\nimport {join} from 'node:path'\n\nexport function writeRoutesDts(content: string, projectRoot: string): boolean {\n const devixDir = join(projectRoot, '.devix')\n const outPath = join(devixDir, 'routes.d.ts')\n\n mkdirSync(devixDir, {recursive: true})\n\n if (existsSync(outPath) && readFileSync(outPath, 'utf-8') === content) {\n return false\n }\n\n writeFileSync(outPath, content, 'utf-8')\n return true\n}\n", "export function parseDuration(value: number | string): number {\n if (typeof value === 'number') return value\n const match = value.trim().match(/^(\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h)?$/)\n if (!match) throw new Error(`[devix] Invalid duration: \"${value}\". Use a number (ms) or a string like \"5s\", \"2m\", \"500ms\".`)\n const n = parseFloat(match[1])\n switch (match[2]) {\n case 'h': return n * 3_600_000\n case 'm': return n * 60_000\n case 's': return n * 1_000\n case 'ms':\n default: return n\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {writeFileSync} from 'node:fs'\nimport {resolve} from 'node:path'\nimport {build} from 'vite'\nimport type {DevixConfig} from '../config'\nimport {devix} from '../vite'\nimport {parseDuration} from '../utils/duration'\n\nconst config: DevixConfig = (await import(`${process.cwd()}/devix.config.ts`)).default\nconst baseConfig = devix(config)\n\nawait build({\n ...baseConfig,\n configFile: false,\n build: {\n outDir: 'dist/client',\n manifest: true,\n rolldownOptions: {\n input: 'virtual:devix/entry-client',\n },\n },\n})\n\nawait build({\n ...baseConfig,\n configFile: false,\n build: {\n ssr: true,\n outDir: 'dist/server',\n rolldownOptions: {\n input: {\n render: 'virtual:devix/render',\n api: 'virtual:devix/api',\n },\n },\n },\n})\n\nconst runtimeConfig = {\n port: config.port ?? 3000,\n host: config.host ?? false,\n loaderTimeout: parseDuration(config.loaderTimeout ?? 10_000),\n output: config.output ?? 'server',\n}\n\nwriteFileSync(\n resolve(process.cwd(), 'dist/devix.config.json'),\n JSON.stringify(runtimeConfig, null, 2),\n 'utf-8'\n)\n\n\nexport {}", "import {UserConfig, Plugin, mergeConfig} from 'vite'\nimport type {DevixConfig} from '../config'\nimport react from '@vitejs/plugin-react'\nimport {fileURLToPath} from 'node:url'\nimport {dirname, resolve} from 'node:path'\nimport {generateEntryClient} from './codegen/entry-client'\nimport {generateClientRoutes} from './codegen/client-routes'\nimport {generateRender} from './codegen/render'\nimport {generateApi} from './codegen/api'\nimport {invalidatePagesCache} from \"../server/pages-router\";\nimport {invalidateApiCache} from \"../server/api-router\";\nimport {generateContext} from \"./codegen/context\";\nimport {scanApiFiles} from \"./codegen/scan-api\";\nimport {generateRoutesDts} from \"./codegen/routes-dts\";\nimport {writeRoutesDts} from \"./codegen/write-routes-dts\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nconst VIRTUAL_ENTRY_CLIENT = 'virtual:devix/entry-client'\nconst VIRTUAL_CLIENT_ROUTES = 'virtual:devix/client-routes'\nconst VIRTUAL_RENDER = 'virtual:devix/render'\nconst VIRTUAL_API = 'virtual:devix/api'\nconst VIRTUAL_CONTEXT = 'virtual:devix/context'\n\nexport function devix(config: DevixConfig): UserConfig {\n const appDir = config.appDir ?? 'app'\n const pagesDir = `${appDir}/pages`\n const cssUrls = (config.css ?? []).map(u => u.startsWith('/') ? u : `/${u.replace(/^\\.\\//, '')}`)\n\n const renderPath = resolve(__dirname, '../server/render.js').replace(/\\\\/g, '/')\n const apiPath = resolve(__dirname, '../server/api.js').replace(/\\\\/g, '/')\n const matcherPath = resolve(__dirname, '../runtime/client-router.js').replace(/\\\\/g, '/')\n\n const virtualPlugin: Plugin = {\n name: 'devix',\n enforce: 'pre',\n\n resolveId(id) {\n if (id === VIRTUAL_ENTRY_CLIENT) return `\\0${VIRTUAL_ENTRY_CLIENT}`\n if (id === VIRTUAL_CLIENT_ROUTES) return `\\0${VIRTUAL_CLIENT_ROUTES}`\n if (id === VIRTUAL_RENDER) return `\\0${VIRTUAL_RENDER}`\n if (id === VIRTUAL_API) return `\\0${VIRTUAL_API}`\n if (id === VIRTUAL_CONTEXT) return `\\0${VIRTUAL_CONTEXT}`\n },\n\n load(id) {\n if (id === `\\0${VIRTUAL_ENTRY_CLIENT}`)\n return generateEntryClient({cssUrls})\n if (id === `\\0${VIRTUAL_CLIENT_ROUTES}`)\n return generateClientRoutes({pagesDir, matcherPath})\n if (id === `\\0${VIRTUAL_RENDER}`)\n return generateRender({pagesDir, renderPath})\n if (id === `\\0${VIRTUAL_API}`)\n return generateApi({apiPath, appDir})\n if (id === `\\0${VIRTUAL_CONTEXT}`)\n return generateContext()\n },\n\n buildStart() {\n const root = process.cwd()\n const entries = scanApiFiles(appDir, root)\n writeRoutesDts(generateRoutesDts(entries, `${appDir}/api`), root)\n },\n\n configureServer(server) {\n const root = process.cwd()\n\n const regenerateDts = () => {\n const entries = scanApiFiles(appDir, root)\n writeRoutesDts(generateRoutesDts(entries, `${appDir}/api`), root)\n }\n\n server.watcher.on('add', (file) => {\n if (file.startsWith(resolve(root, pagesDir))) invalidatePagesCache()\n if (file.includes(`${appDir}/api`)) { invalidateApiCache(); regenerateDts() }\n })\n server.watcher.on('unlink', (file) => {\n if (file.startsWith(resolve(root, pagesDir))) invalidatePagesCache()\n if (file.includes(`${appDir}/api`)) { invalidateApiCache(); regenerateDts() }\n })\n server.watcher.on('change', (file) => {\n if (file.includes(`${appDir}/api`) && !file.endsWith('middleware.ts')) {\n regenerateDts()\n }\n })\n },\n }\n\n const base: UserConfig = {\n plugins: [react(), virtualPlugin],\n ssr: {noExternal: ['@devlusoft/devix']},\n ...(config.envPrefix ? {envPrefix: config.envPrefix} : {}),\n }\n\n return mergeConfig(base, config.vite ?? {})\n}", "interface EntryClientOptions {\n cssUrls: string[]\n}\n\nexport function generateEntryClient({cssUrls}: EntryClientOptions): string {\n const cssImports = cssUrls.map(u => `import '${u}'`).join('\\n')\n\n return `\n${cssImports}\nimport \"@vitejs/plugin-react/preamble\"\nimport React from \"react\"\nimport {hydrateRoot, createRoot} from 'react-dom/client'\nimport {matchClientRoute, loadErrorPage, getDefaultErrorPage} from 'virtual:devix/client-routes'\nimport {RouterProvider} from '@devlusoft/devix'\n\nconst root = document.getElementById('devix-root')\n\nif (!window.__DEVIX__) {\n const ErrorPage = getDefaultErrorPage()\n createRoot(root).render(React.createElement(ErrorPage, {statusCode: 500, message: 'Server error'}))\n} else {\n const {metadata, viewport, clientEntry} = window.__DEVIX__\n const loaderData = window.__LOADER_DATA__\n const layoutsData = window.__LAYOUTS_DATA__ ?? []\n\n const matched = matchClientRoute(window.location.pathname)\n\n if (matched) {\n const [pageMod, ...layoutMods] = await Promise.all([\n matched.load(),\n ...matched.loadLayouts.map(l => l()),\n ])\n hydrateRoot(\n root,\n React.createElement(RouterProvider, {\n clientEntry,\n initialData: loaderData,\n initialParams: matched.params,\n initialPage: pageMod.default,\n initialLayouts: layoutMods.map(m => m.default),\n initialLayoutsData: layoutsData,\n initialMeta: metadata,\n initialViewport: viewport,\n })\n )\n } else {\n const ErrorPage = await loadErrorPage() ?? getDefaultErrorPage()\n createRoot(root).render(\n React.createElement(RouterProvider, {\n clientEntry,\n initialData: null,\n initialParams: {},\n initialPage: () => null,\n initialLayouts: [],\n initialLayoutsData: [],\n initialMeta: null,\n initialError: {statusCode: 404, message: 'Not found'},\n initialErrorPage: ErrorPage,\n })\n )\n }\n}\n`\n}", "interface ClientRoutesOptions {\n pagesDir: string\n matcherPath: string\n}\n\nexport function generateClientRoutes({pagesDir, matcherPath}: ClientRoutesOptions) {\n return `\nimport React from 'react'\nimport { createMatcher } from '${matcherPath}'\nconst pageFiles = import.meta.glob(['/${pagesDir}/**/*.tsx', '!**/error.tsx', '!**/layout.tsx'])\nconst layoutFiles = import.meta.glob('/${pagesDir}/**/layout.tsx')\nconst errorFiles = import.meta.glob('/${pagesDir}/**/error.tsx')\n\nexport const matchClientRoute = createMatcher(pageFiles, layoutFiles)\n\nexport async function loadErrorPage() {\n const key = Object.keys(errorFiles)[0]\n if (!key) return null\n const mod = await errorFiles[key]()\n return mod?.default ?? null\n}\n\nexport function getDefaultErrorPage() {\n return function DefaultError({ statusCode, message }) {\n return React.createElement('main', {\n style: { minHeight: '100dvh', display: 'flex', flexDirection: 'column', \n alignItems: 'center', justifyContent: 'center', gap: '8px',\n fontFamily: 'system-ui, sans-serif' }\n },\n React.createElement('h1', {style: {fontSize: '4rem', fontWeight: 700}}, statusCode),\n React.createElement('p', {style: {color: '#666'}}, message ?? 'An unexpected error occurred'),\n )\n }\n}\n`\n}", "interface RenderOptions {\n pagesDir: string\n renderPath: string\n}\n\nexport function generateRender({pagesDir, renderPath}: RenderOptions): string {\n return `\nimport { render as _render, runLoader as _runLoader, getStaticRoutes as _getStaticRoutes } from '${renderPath}'\n\nconst _pages = import.meta.glob(['/${pagesDir}/**/*.tsx', '!**/error.tsx', '!**/layout.tsx'])\nconst _layouts = import.meta.glob('/${pagesDir}/**/layout.tsx')\n\nconst _glob = {\n pages: _pages,\n layouts: _layouts,\n pagesDir: '/${pagesDir}',\n}\n\nexport function render(url, request, options) {\n return _render(url, request, _glob, options)\n}\n\nexport function runLoader(url, request, options) {\n return _runLoader(url, request, _glob, options)\n}\n\nexport function getStaticRoutes() {\n return _getStaticRoutes(_glob)\n}\n`\n}\n", "interface ApiOptions {\n apiPath: string\n appDir: string\n}\n\nexport function generateApi({apiPath, appDir}: ApiOptions): string {\n return `\nimport { handleApiRequest as _handleApiRequest } from '${apiPath}'\n\nconst _routes = import.meta.glob(['/${appDir}/api/**/*.ts', '!**/middleware.ts'])\nconst _middlewares = import.meta.glob('/${appDir}/api/**/middleware.ts')\n\nconst _glob = {\n routes: _routes,\n middlewares: _middlewares,\n apiDir: '/${appDir}/api',\n}\n\nexport function handleApiRequest(url, request) {\n return _handleApiRequest(url, request, _glob)\n}\n`\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 Page {\n path: string\n key: string\n params: string[]\n regex: RegExp\n}\n\nexport interface Layout {\n dir: string\n key: string\n}\n\nexport interface PagesResult {\n pages: Page[]\n layouts: Layout[]\n}\n\nfunction keyToRoutePattern(key: string, pagesDir: string): string {\n const rel = key.slice(pagesDir.length + 1).replace(/\\\\/g, '/')\n const pattern = routePattern(rel)\n return pattern === \"/\" ? \"/\" : `/${pattern}`\n}\n\nfunction keyToDir(key: string): string {\n return key.slice(0, key.lastIndexOf('/'))\n}\n\nlet cache: PagesResult | null = null\n\nexport function invalidatePagesCache() {\n cache = null\n}\n\nexport function buildPages(pageKeys: string[], layoutKeys: string[], pagesDir: string): PagesResult {\n if (cache) return cache\n\n const pages: Page[] = []\n const layouts: Layout[] = []\n\n for (const key of layoutKeys) {\n layouts.push({dir: keyToDir(key), key})\n }\n\n for (const key of pageKeys) {\n const pattern = keyToRoutePattern(key, pagesDir)\n const params = [...pattern.matchAll(/:([^/]+)/g)].map(m => m[1])\n const regexStr = pattern\n .replace(/:[^/]+/g, '([^/]+)')\n .replace(/\\//g, '\\\\/')\n pages.push({path: pattern, key, params, regex: new RegExp(`^${regexStr}$`)})\n }\n\n pages.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 = {pages, layouts}\n return cache\n}\n\nexport function collectLayoutChain(pageKey: string, layouts: Layout[]): Layout[] {\n const pageDir = keyToDir(pageKey)\n\n return layouts\n .filter(layout => pageDir.startsWith(layout.dir))\n .sort((a, b) => a.dir.split('/').length - b.dir.split('/').length)\n}\n\nexport function matchPage(pathname: string, pages: Page[]): {\n page: Page\n params: Record<string, string>\n} | null {\n for (const page of pages) {\n const match = pathname.match(page.regex)\n if (match) {\n const params: Record<string, string> = {}\n page.params.forEach((name, i) => {\n params[name] = decodeURIComponent(match[i + 1])\n })\n return {page, params}\n }\n }\n return null\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 function generateContext(): string {\n return `\nexport {RouterContext} from '@devlusoft/devix/runtime/context'\n`\n}", "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", "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", "import {mkdirSync, readFileSync, writeFileSync, existsSync} from 'node:fs'\nimport {join} from 'node:path'\n\nexport function writeRoutesDts(content: string, projectRoot: string): boolean {\n const devixDir = join(projectRoot, '.devix')\n const outPath = join(devixDir, 'routes.d.ts')\n\n mkdirSync(devixDir, {recursive: true})\n\n if (existsSync(outPath) && readFileSync(outPath, 'utf-8') === content) {\n return false\n }\n\n writeFileSync(outPath, content, 'utf-8')\n return true\n}\n", "export function parseDuration(value: number | string): number {\n if (typeof value === 'number') return value\n const match = value.trim().match(/^(\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h)?$/)\n if (!match) throw new Error(`[devix] Invalid duration: \"${value}\". Use a number (ms) or a string like \"5s\", \"2m\", \"500ms\".`)\n const n = parseFloat(match[1])\n switch (match[2]) {\n case 'h': return n * 3_600_000\n case 'm': return n * 60_000\n case 's': return n * 1_000\n case 'ms':\n default: return n\n }\n}\n"],
5
+ "mappings": "AAAA,OAAQ,iBAAAA,OAAoB,UAC5B,OAAQ,WAAAC,OAAc,YACtB,OAAQ,SAAAC,MAAY,OCFpB,OAA4B,eAAAC,OAAkB,OAE9C,OAAOC,OAAW,uBAClB,OAAQ,iBAAAC,OAAoB,WAC5B,OAAQ,WAAAC,GAAS,WAAAC,MAAc,YCAxB,SAASC,EAAoB,CAAC,QAAAC,CAAO,EAA+B,CAGvE,MAAO;AAAA,EAFYA,EAAQ,IAAIC,GAAK,WAAWA,CAAC,GAAG,EAAE,KAAK;AAAA,CAAI,CAGtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAuDZ,CC1DO,SAASC,EAAqB,CAAC,SAAAC,EAAU,YAAAC,CAAW,EAAwB,CAC/E,MAAO;AAAA;AAAA,iCAEsBA,CAAW;AAAA,wCACJD,CAAQ;AAAA,yCACPA,CAAQ;AAAA,wCACTA,CAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAwBhD,CC9BO,SAASE,EAAe,CAAC,SAAAC,EAAU,WAAAC,CAAU,EAA0B,CAC1E,MAAO;AAAA,mGACwFA,CAAU;AAAA;AAAA,qCAExED,CAAQ;AAAA,sCACPA,CAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,kBAK5BA,CAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAe1B,CCzBO,SAASE,EAAY,CAAC,QAAAC,EAAS,OAAAC,CAAM,EAAuB,CAC/D,MAAO;AAAA,yDAC8CD,CAAO;AAAA;AAAA,sCAE1BC,CAAM;AAAA,0CACFA,CAAM;AAAA;AAAA;AAAA;AAAA;AAAA,gBAKhCA,CAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAOtB,CCtBO,SAASC,EAAaC,EAAqB,CAC9C,OAAOA,EACE,QAAQ,qBAAsB,EAAE,EAChC,QAAQ,aAAc,EAAE,EACxB,QAAQ,mBAAoB,EAAE,EAC9B,QAAQ,eAAgB,KAAK,GAC/B,GACX,CCsBA,IAAIC,EAA4B,KAEzB,SAASC,GAAuB,CACnCD,EAAQ,IACZ,CCdO,SAASE,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,CAMA,IAAIE,EAA0B,KAEvB,SAASC,GAAqB,CACjCD,EAAQ,IACZ,CCjCO,SAASE,GAA0B,CACtC,MAAO;AAAA;AAAA,CAGX,CCJA,OAAQ,gBAAAC,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,CCPO,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,CF9DA,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,CGzCA,OAAQ,aAAAG,EAAW,gBAAAC,EAAc,iBAAAC,EAAe,cAAAC,OAAiB,UACjE,OAAQ,QAAAC,MAAW,YAEZ,SAASC,EAAeC,EAAiBC,EAA8B,CAC1E,IAAMC,EAAWJ,EAAKG,EAAa,QAAQ,EACrCE,EAAUL,EAAKI,EAAU,aAAa,EAI5C,OAFAR,EAAUQ,EAAU,CAAC,UAAW,EAAI,CAAC,EAEjCL,GAAWM,CAAO,GAAKR,EAAaQ,EAAS,OAAO,IAAMH,EACnD,IAGXJ,EAAcO,EAASH,EAAS,OAAO,EAChC,GACX,CZCA,IAAMI,EAAYC,GAAQC,GAAc,YAAY,GAAG,CAAC,EAElDC,EAAuB,6BACvBC,EAAwB,8BACxBC,EAAiB,uBACjBC,EAAc,oBACdC,EAAkB,wBAEjB,SAASC,EAAMC,EAAiC,CACnD,IAAMC,EAASD,EAAO,QAAU,MAC1BE,EAAW,GAAGD,CAAM,SACpBE,GAAWH,EAAO,KAAO,CAAC,GAAG,IAAII,GAAKA,EAAE,WAAW,GAAG,EAAIA,EAAI,IAAIA,EAAE,QAAQ,QAAS,EAAE,CAAC,EAAE,EAE1FC,EAAaC,EAAQf,EAAW,qBAAqB,EAAE,QAAQ,MAAO,GAAG,EACzEgB,EAAUD,EAAQf,EAAW,kBAAkB,EAAE,QAAQ,MAAO,GAAG,EACnEiB,EAAcF,EAAQf,EAAW,6BAA6B,EAAE,QAAQ,MAAO,GAAG,EAElFkB,EAAwB,CAC1B,KAAM,QACN,QAAS,MAET,UAAUC,EAAI,CACV,GAAIA,IAAOhB,EAAsB,MAAO,KAAKA,CAAoB,GACjE,GAAIgB,IAAOf,EAAuB,MAAO,KAAKA,CAAqB,GACnE,GAAIe,IAAOd,EAAgB,MAAO,KAAKA,CAAc,GACrD,GAAIc,IAAOb,EAAa,MAAO,KAAKA,CAAW,GAC/C,GAAIa,IAAOZ,EAAiB,MAAO,KAAKA,CAAe,EAC3D,EAEA,KAAKY,EAAI,CACL,GAAIA,IAAO,KAAKhB,CAAoB,GAChC,OAAOiB,EAAoB,CAAC,QAAAR,CAAO,CAAC,EACxC,GAAIO,IAAO,KAAKf,CAAqB,GACjC,OAAOiB,EAAqB,CAAC,SAAAV,EAAU,YAAAM,CAAW,CAAC,EACvD,GAAIE,IAAO,KAAKd,CAAc,GAC1B,OAAOiB,EAAe,CAAC,SAAAX,EAAU,WAAAG,CAAU,CAAC,EAChD,GAAIK,IAAO,KAAKb,CAAW,GACvB,OAAOiB,EAAY,CAAC,QAAAP,EAAS,OAAAN,CAAM,CAAC,EACxC,GAAIS,IAAO,KAAKZ,CAAe,GAC3B,OAAOiB,EAAgB,CAC/B,EAEA,YAAa,CACT,IAAMC,EAAO,QAAQ,IAAI,EACnBC,EAAUC,EAAajB,EAAQe,CAAI,EACzCG,EAAeC,EAAkBH,EAAS,GAAGhB,CAAM,MAAM,EAAGe,CAAI,CACpE,EAEA,gBAAgBK,EAAQ,CACpB,IAAML,EAAO,QAAQ,IAAI,EAEnBM,EAAgB,IAAM,CACxB,IAAML,EAAUC,EAAajB,EAAQe,CAAI,EACzCG,EAAeC,EAAkBH,EAAS,GAAGhB,CAAM,MAAM,EAAGe,CAAI,CACpE,EAEAK,EAAO,QAAQ,GAAG,MAAQE,GAAS,CAC3BA,EAAK,WAAWjB,EAAQU,EAAMd,CAAQ,CAAC,GAAGsB,EAAqB,EAC/DD,EAAK,SAAS,GAAGtB,CAAM,MAAM,IAAKwB,EAAmB,EAAGH,EAAc,EAC9E,CAAC,EACDD,EAAO,QAAQ,GAAG,SAAWE,GAAS,CAC9BA,EAAK,WAAWjB,EAAQU,EAAMd,CAAQ,CAAC,GAAGsB,EAAqB,EAC/DD,EAAK,SAAS,GAAGtB,CAAM,MAAM,IAAKwB,EAAmB,EAAGH,EAAc,EAC9E,CAAC,EACDD,EAAO,QAAQ,GAAG,SAAWE,GAAS,CAC9BA,EAAK,SAAS,GAAGtB,CAAM,MAAM,GAAK,CAACsB,EAAK,SAAS,eAAe,GAChED,EAAc,CAEtB,CAAC,CACL,CACJ,EAEMI,EAAmB,CACrB,QAAS,CAACC,GAAM,EAAGlB,CAAa,EAChC,IAAK,CAAC,WAAY,CAAC,kBAAkB,CAAC,EACtC,GAAIT,EAAO,UAAY,CAAC,UAAWA,EAAO,SAAS,EAAI,CAAC,CAC5D,EAEA,OAAO4B,GAAYF,EAAM1B,EAAO,MAAQ,CAAC,CAAC,CAC9C,Ca/FO,SAAS6B,EAAcC,EAAgC,CAC1D,GAAI,OAAOA,GAAU,SAAU,OAAOA,EACtC,IAAMC,EAAQD,EAAM,KAAK,EAAE,MAAM,iCAAiC,EAClE,GAAI,CAACC,EAAO,MAAM,IAAI,MAAM,8BAA8BD,CAAK,4DAA4D,EAC3H,IAAME,EAAI,WAAWD,EAAM,CAAC,CAAC,EAC7B,OAAQA,EAAM,CAAC,EAAG,CACd,IAAK,IAAM,OAAOC,EAAI,KACtB,IAAK,IAAM,OAAOA,EAAI,IACtB,IAAK,IAAM,OAAOA,EAAI,IAEtB,QAAW,OAAOA,CACtB,CACJ,CdLA,IAAMC,GAAuB,MAAM,OAAO,GAAG,QAAQ,IAAI,CAAC,qBAAqB,QACzEC,EAAaC,EAAMF,CAAM,EAE/B,MAAMG,EAAM,CACR,GAAGF,EACH,WAAY,GACZ,MAAO,CACH,OAAQ,cACR,SAAU,GACV,gBAAiB,CACb,MAAO,4BACX,CACJ,CACJ,CAAC,EAED,MAAME,EAAM,CACR,GAAGF,EACH,WAAY,GACZ,MAAO,CACH,IAAK,GACL,OAAQ,cACR,gBAAiB,CACb,MAAO,CACH,OAAQ,uBACR,IAAK,mBACT,CACJ,CACJ,CACJ,CAAC,EAED,IAAMG,GAAgB,CAClB,KAAMJ,EAAO,MAAQ,IACrB,KAAMA,EAAO,MAAQ,GACrB,cAAeK,EAAcL,EAAO,eAAiB,GAAM,EAC3D,OAAQA,EAAO,QAAU,QAC7B,EAEAM,GACIC,GAAQ,QAAQ,IAAI,EAAG,wBAAwB,EAC/C,KAAK,UAAUH,GAAe,KAAM,CAAC,EACrC,OACJ",
6
6
  "names": ["writeFileSync", "resolve", "build", "mergeConfig", "react", "fileURLToPath", "dirname", "resolve", "generateEntryClient", "cssUrls", "u", "generateClientRoutes", "pagesDir", "matcherPath", "generateRender", "pagesDir", "renderPath", "generateApi", "apiPath", "appDir", "routePattern", "rel", "cache", "invalidatePagesCache", "keyToRoutePattern", "key", "apiDir", "rel", "pattern", "routePattern", "cache", "invalidateApiCache", "generateContext", "readFileSync", "readdirSync", "statSync", "join", "relative", "METHOD_EXPORT_RE", "stripComments", "content", "extractHttpMethods", "found", "match", "filePathToIdentifier", "filePath", "apiDir", "buildRouteEntry", "methods", "keyToRoutePattern", "generateRoutesDts", "entries", "imports", "e", "importPath", "routeLines", "m", "walkDir", "dir", "root", "entries", "name", "readdirSync", "full", "join", "statSync", "relative", "scanApiFiles", "appDir", "projectRoot", "apiDir", "files", "f", "filePath", "content", "readFileSync", "methods", "extractHttpMethods", "buildRouteEntry", "mkdirSync", "readFileSync", "writeFileSync", "existsSync", "join", "writeRoutesDts", "content", "projectRoot", "devixDir", "outPath", "__dirname", "dirname", "fileURLToPath", "VIRTUAL_ENTRY_CLIENT", "VIRTUAL_CLIENT_ROUTES", "VIRTUAL_RENDER", "VIRTUAL_API", "VIRTUAL_CONTEXT", "devix", "config", "appDir", "pagesDir", "cssUrls", "u", "renderPath", "resolve", "apiPath", "matcherPath", "virtualPlugin", "id", "generateEntryClient", "generateClientRoutes", "generateRender", "generateApi", "generateContext", "root", "entries", "scanApiFiles", "writeRoutesDts", "generateRoutesDts", "server", "regenerateDts", "file", "invalidatePagesCache", "invalidateApiCache", "base", "react", "mergeConfig", "parseDuration", "value", "match", "n", "config", "baseConfig", "devix", "build", "runtimeConfig", "parseDuration", "writeFileSync", "resolve"]
7
7
  }
package/dist/cli/dev.js CHANGED
@@ -1,5 +1,5 @@
1
- import{createServer as xt}from"node:http";import{createServer as yt}from"vite";import{getRequestListener as Rt}from"@hono/node-server";import{Hono as vt}from"hono";import{mergeConfig as lt}from"vite";import ut from"@vitejs/plugin-react";import{fileURLToPath as pt}from"node:url";import{dirname as dt,resolve as f}from"node:path";function b({cssUrls:t}){return`
2
- ${t.map(r=>`import '${r}'`).join(`
1
+ import{createServer as ye}from"node:http";import{createServer as xe}from"vite";import{getRequestListener as Re}from"@hono/node-server";import{Hono as ve}from"hono";import{mergeConfig as le}from"vite";import ue from"@vitejs/plugin-react";import{fileURLToPath as pe}from"node:url";import{dirname as de,resolve as g}from"node:path";function S({cssUrls:e}){return`
2
+ ${e.map(r=>`import '${r}'`).join(`
3
3
  `)}
4
4
  import "@vitejs/plugin-react/preamble"
5
5
  import React from "react"
@@ -54,12 +54,12 @@ if (!window.__DEVIX__) {
54
54
  )
55
55
  }
56
56
  }
57
- `}function L({pagesDir:t,matcherPath:e}){return`
57
+ `}function L({pagesDir:e,matcherPath:t}){return`
58
58
  import React from 'react'
59
- import { createMatcher } from '${e}'
60
- const pageFiles = import.meta.glob(['/${t}/**/*.tsx', '!**/error.tsx', '!**/layout.tsx'])
61
- const layoutFiles = import.meta.glob('/${t}/**/layout.tsx')
62
- const errorFiles = import.meta.glob('/${t}/**/error.tsx')
59
+ import { createMatcher } from '${t}'
60
+ const pageFiles = import.meta.glob(['/${e}/**/*.tsx', '!**/error.tsx', '!**/layout.tsx'])
61
+ const layoutFiles = import.meta.glob('/${e}/**/layout.tsx')
62
+ const errorFiles = import.meta.glob('/${e}/**/error.tsx')
63
63
 
64
64
  export const matchClientRoute = createMatcher(pageFiles, layoutFiles)
65
65
 
@@ -82,16 +82,16 @@ export function getDefaultErrorPage() {
82
82
  )
83
83
  }
84
84
  }
85
- `}function M({pagesDir:t,renderPath:e}){return`
86
- import { render as _render, runLoader as _runLoader, getStaticRoutes as _getStaticRoutes } from '${e}'
85
+ `}function M({pagesDir:e,renderPath:t}){return`
86
+ import { render as _render, runLoader as _runLoader, getStaticRoutes as _getStaticRoutes } from '${t}'
87
87
 
88
- const _pages = import.meta.glob(['/${t}/**/*.tsx', '!**/error.tsx', '!**/layout.tsx'])
89
- const _layouts = import.meta.glob('/${t}/**/layout.tsx')
88
+ const _pages = import.meta.glob(['/${e}/**/*.tsx', '!**/error.tsx', '!**/layout.tsx'])
89
+ const _layouts = import.meta.glob('/${e}/**/layout.tsx')
90
90
 
91
91
  const _glob = {
92
92
  pages: _pages,
93
93
  layouts: _layouts,
94
- pagesDir: '/${t}',
94
+ pagesDir: '/${e}',
95
95
  }
96
96
 
97
97
  export function render(url, request, options) {
@@ -105,46 +105,53 @@ export function runLoader(url, request, options) {
105
105
  export function getStaticRoutes() {
106
106
  return _getStaticRoutes(_glob)
107
107
  }
108
- `}function O({apiPath:t,appDir:e}){return`
109
- import { handleApiRequest as _handleApiRequest } from '${t}'
108
+ `}function I({apiPath:e,appDir:t}){return`
109
+ import { handleApiRequest as _handleApiRequest } from '${e}'
110
110
 
111
- const _routes = import.meta.glob(['/${e}/api/**/*.ts', '!**/middleware.ts'])
112
- const _middlewares = import.meta.glob('/${e}/api/**/middleware.ts')
111
+ const _routes = import.meta.glob(['/${t}/api/**/*.ts', '!**/middleware.ts'])
112
+ const _middlewares = import.meta.glob('/${t}/api/**/middleware.ts')
113
113
 
114
114
  const _glob = {
115
115
  routes: _routes,
116
116
  middlewares: _middlewares,
117
- apiDir: '/${e}/api',
117
+ apiDir: '/${t}/api',
118
118
  }
119
119
 
120
120
  export function handleApiRequest(url, request) {
121
121
  return _handleApiRequest(url, request, _glob)
122
122
  }
123
- `}function x(t){return t.replace(/\.(tsx|ts|jsx|js)$/,"").replace(/\(.*?\)\//g,"").replace(/^index$|\/index$/,"").replace(/\[([^\]]+)]/g,":$1")||"/"}var z=null;function y(){z=null}function I(t,e){let r=t.slice(e.length+1).replace(/\\/g,"/"),o=x(r);return o==="/"?"/api":`/api/${o}`.replace("/api//","/api/")}var Z=null;function R(){Z=null}function k(){return`
123
+ `}function y(e){return e.replace(/\.(tsx|ts|jsx|js)$/,"").replace(/\(.*?\)\//g,"").replace(/^index$|\/index$/,"").replace(/\[([^\]]+)]/g,":$1")||"/"}var z=null;function x(){z=null}function O(e,t){let r=e.slice(t.length+1).replace(/\\/g,"/"),n=y(r);return n==="/"?"/api":`/api/${n}`.replace("/api//","/api/")}var Z=null;function R(){Z=null}function k(){return`
124
124
  export {RouterContext} from '@devlusoft/devix/runtime/context'
125
- `}import{readFileSync as et,readdirSync as rt,statSync as ot}from"node:fs";import{join as w,relative as nt}from"node:path";var K=/export\s+(?:const|async\s+function|function)\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b/g;function Q(t){return t.replace(/\/\*[\s\S]*?\*\//g,"").replace(/\/\/.*$/gm,"")}function U(t){let e=new Set;for(let r of Q(t).matchAll(K))e.add(r[1]);return[...e]}function tt(t,e){return"_api_"+t.slice(`${e}/`.length).replace(/\.(ts|tsx)$/,"").replace(/[^a-zA-Z0-9]/g,"_")}function q(t,e,r){return{filePath:t,urlPattern:I(t,e),identifier:tt(t,e),methods:r}}function v(t,e){if(t.length===0)return`// auto-generado por devix \u2014 no editar
125
+ `}import{readFileSync as te,readdirSync as re,statSync as ne}from"node:fs";import{join as w,relative as oe}from"node:path";var K=/export\s+(?:const|async\s+function|function)\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b/g;function Q(e){return e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/\/\/.*$/gm,"")}function U(e){let t=new Set;for(let r of Q(e).matchAll(K))t.add(r[1]);return[...t]}function ee(e,t){return"_api_"+e.slice(`${t}/`.length).replace(/\.(ts|tsx)$/,"").replace(/[^a-zA-Z0-9]/g,"_")}function q(e,t,r){return{filePath:e,urlPattern:O(e,t),identifier:ee(e,t),methods:r}}function v(e,t){if(e.length===0)return`// auto-generado por devix \u2014 no editar
126
126
  declare module '@devlusoft/devix' {
127
127
  interface ApiRoutes {}
128
128
  }
129
- `;let r=t.map(n=>{let s="../"+n.filePath.replace(/\.(ts|tsx)$/,"");return`import type * as ${n.identifier} from '${s}'`}).join(`
130
- `),o=t.flatMap(n=>n.methods.map(s=>` '${s} ${n.urlPattern}': InferRoute<(typeof ${n.identifier})['${s}']>`)).join(`
129
+ `;let r=e.map(o=>{let s="../"+o.filePath.replace(/\.(ts|tsx)$/,"");return`import type * as ${o.identifier} from '${s}'`}).join(`
130
+ `),n=e.flatMap(o=>o.methods.map(s=>` '${s} ${o.urlPattern}': InferRoute<(typeof ${o.identifier})['${s}']>`)).join(`
131
131
  `);return`// auto-generado por devix \u2014 no editar
132
132
  ${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
- ${o}
151
+ ${n}
145
152
  }
146
153
  }
147
- `}function H(t,e){let r=[];for(let o of rt(t)){let n=w(t,o);ot(n).isDirectory()?r.push(...H(n,e)):/\.(ts|tsx)$/.test(o)&&r.push(nt(e,n).replace(/\\/g,"/"))}return r}function E(t,e){let r=w(e,t,"api"),o;try{o=H(r,e)}catch{return[]}return o.filter(n=>!n.endsWith("middleware.ts")&&!n.endsWith("middleware.tsx")).flatMap(n=>{try{let s=et(w(e,n),"utf-8"),l=U(s);return l.length===0?[]:[q(n,`${t}/api`,l)]}catch{return[]}})}import{mkdirSync as it,readFileSync as st,writeFileSync as at,existsSync as ct}from"node:fs";import{join as j}from"node:path";function $(t,e){let r=j(e,".devix"),o=j(r,"routes.d.ts");return it(r,{recursive:!0}),ct(o)&&st(o,"utf-8")===t?!1:(at(o,t,"utf-8"),!0)}var P=dt(pt(import.meta.url)),_="virtual:devix/entry-client",T="virtual:devix/client-routes",A="virtual:devix/render",D="virtual:devix/api",C="virtual:devix/context";function F(t){let e=t.appDir??"app",r=`${e}/pages`,o=(t.css??[]).map(i=>i.startsWith("/")?i:`/${i.replace(/^\.\//,"")}`),n=f(P,"../server/render.js").replace(/\\/g,"/"),s=f(P,"../server/api.js").replace(/\\/g,"/"),l=f(P,"../runtime/client-router.js").replace(/\\/g,"/"),g={name:"devix",enforce:"pre",resolveId(i){if(i===_)return`\0${_}`;if(i===T)return`\0${T}`;if(i===A)return`\0${A}`;if(i===D)return`\0${D}`;if(i===C)return`\0${C}`},load(i){if(i===`\0${_}`)return b({cssUrls:o});if(i===`\0${T}`)return L({pagesDir:r,matcherPath:l});if(i===`\0${A}`)return M({pagesDir:r,renderPath:n});if(i===`\0${D}`)return O({apiPath:s,appDir:e});if(i===`\0${C}`)return k()},buildStart(){let i=process.cwd(),u=E(e,i);$(v(u,`${e}/api`),i)},configureServer(i){let u=process.cwd(),h=()=>{let c=E(e,u);$(v(c,`${e}/api`),u)};i.watcher.on("add",c=>{c.startsWith(f(u,r))&&y(),c.includes(`${e}/api`)&&(R(),h())}),i.watcher.on("unlink",c=>{c.startsWith(f(u,r))&&y(),c.includes(`${e}/api`)&&(R(),h())}),i.watcher.on("change",c=>{c.includes(`${e}/api`)&&!c.endsWith("middleware.ts")&&h()})}},d={plugins:[ut(),g],ssr:{noExternal:["@devlusoft/devix"]},...t.envPrefix?{envPrefix:t.envPrefix}:{}};return lt(d,t.vite??{})}function N(t,{apiModule:e,renderModule:r,loaderTimeout:o}){t.all("/api/*",async n=>{try{return await e.handleApiRequest(n.req.url,n.req.raw)}catch(s){return console.error(s),n.json({error:"internal error"},500)}}),t.get("/_data/*",async n=>{try{let{pathname:s,search:l}=new URL(n.req.url,"http://localhost"),g=s.replace(/^\/_data/,"")+l,d=await r.runLoader(g,n.req.raw,{loaderTimeout:o});return n.json(d)}catch(s){return console.error(s),n.json({error:"internal error"},500)}})}import a from"picocolors";import{networkInterfaces as mt}from"node:os";import{createRequire as gt}from"node:module";function ft(t){let e=mt();for(let r of Object.values(e))for(let o of r??[])if(o.family==="IPv4"&&!o.internal)return`http://${o.address}:${t}/`;return null}function V(t){let r=gt(import.meta.url)("../../package.json").version,o=ft(t);console.log(),console.log(` ${a.bold(a.yellow("devix"))} ${a.dim(`v${r}`)}`),console.log(),console.log(` ${a.green("\u279C")} ${a.bold("Local:")} ${a.cyan(`http://localhost:${t}/`)}`),console.log(o?` ${a.green("\u279C")} ${a.bold("Network:")} ${a.cyan(o)}`:` ${a.green("\u279C")} ${a.bold("Network:")} ${a.dim("use --host to expose")}`),console.log()}async function W(t){let e=new Set;for(let[,r]of t.moduleGraph.idToModuleMap)r.id&&(r.id.endsWith(".css")||r.id.includes(".css?"))&&e.add(r.url);return[...e]}function J(t){if(typeof t=="number")return t;let e=t.trim().match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h)?$/);if(!e)throw new Error(`[devix] Invalid duration: "${t}". Use a number (ms) or a string like "5s", "2m", "500ms".`);let r=parseFloat(e[1]);switch(e[2]){case"h":return r*36e5;case"m":return r*6e4;case"s":return r*1e3;default:return r}}import{loadEnv as ht}from"vite";function X(t){let e=ht(t,process.cwd(),"");for(let[r,o]of Object.entries(e))process.env[r]===void 0&&(process.env[r]=o)}X("development");var Y="virtual:devix/render",wt="virtual:devix/api",m=(await import(`${process.cwd()}/devix.config.ts`)).default,B=Number(process.env.PORT)||m.port||3e3,Et=typeof m.host=="string"?m.host:m.host?"0.0.0.0":"localhost",p=await yt({...F(m),configFile:!1,appType:"custom",server:{middlewareMode:!0}}),G={render:async(...t)=>(await p.ssrLoadModule(Y)).render(...t),runLoader:async(...t)=>(await p.ssrLoadModule(Y)).runLoader(...t)},$t={handleApiRequest:async(...t)=>(await p.ssrLoadModule(wt)).handleApiRequest(...t)},S=new vt;N(S,{renderModule:G,apiModule:$t});S.get("*",async t=>{try{let{html:e,statusCode:r,headers:o}=await G.render(t.req.url,t.req.raw,{loaderTimeout:J(m.loaderTimeout??1e4)}),s=(await W(p)).map(i=>`<link rel="stylesheet" href="${i}">`).join(`
148
- `),l=s?e.replace("</head>",`${s}
149
- </head>`):e,g=await p.transformIndexHtml(t.req.url,`<!DOCTYPE html>${l}`),d=t.html(g,r);for(let[i,u]of Object.entries(o))d.headers.set(i,u);return d}catch(e){return p.ssrFixStacktrace(e),console.error(e),t.text("Internal Server Error",500)}});var Pt=Rt(S.fetch);xt(async(t,e)=>{await new Promise(r=>p.middlewares(t,e,r)),e.writableEnded||await Pt(t,e)}).listen(B,Et,()=>{V(B)});
154
+ `}function H(e,t){let r=[];for(let n of re(e)){let o=w(e,n);ne(o).isDirectory()?r.push(...H(o,t)):/\.(ts|tsx)$/.test(n)&&r.push(oe(t,o).replace(/\\/g,"/"))}return r}function E(e,t){let r=w(t,e,"api"),n;try{n=H(r,t)}catch{return[]}return n.filter(o=>!o.endsWith("middleware.ts")&&!o.endsWith("middleware.tsx")).flatMap(o=>{try{let s=te(w(t,o),"utf-8"),l=U(s);return l.length===0?[]:[q(o,`${e}/api`,l)]}catch{return[]}})}import{mkdirSync as ie,readFileSync as se,writeFileSync as ae,existsSync as ce}from"node:fs";import{join as j}from"node:path";function $(e,t){let r=j(t,".devix"),n=j(r,"routes.d.ts");return ie(r,{recursive:!0}),ce(n)&&se(n,"utf-8")===e?!1:(ae(n,e,"utf-8"),!0)}var P=de(pe(import.meta.url)),_="virtual:devix/entry-client",T="virtual:devix/client-routes",A="virtual:devix/render",D="virtual:devix/api",C="virtual:devix/context";function F(e){let t=e.appDir??"app",r=`${t}/pages`,n=(e.css??[]).map(i=>i.startsWith("/")?i:`/${i.replace(/^\.\//,"")}`),o=g(P,"../server/render.js").replace(/\\/g,"/"),s=g(P,"../server/api.js").replace(/\\/g,"/"),l=g(P,"../runtime/client-router.js").replace(/\\/g,"/"),f={name:"devix",enforce:"pre",resolveId(i){if(i===_)return`\0${_}`;if(i===T)return`\0${T}`;if(i===A)return`\0${A}`;if(i===D)return`\0${D}`;if(i===C)return`\0${C}`},load(i){if(i===`\0${_}`)return S({cssUrls:n});if(i===`\0${T}`)return L({pagesDir:r,matcherPath:l});if(i===`\0${A}`)return M({pagesDir:r,renderPath:o});if(i===`\0${D}`)return I({apiPath:s,appDir:t});if(i===`\0${C}`)return k()},buildStart(){let i=process.cwd(),u=E(t,i);$(v(u,`${t}/api`),i)},configureServer(i){let u=process.cwd(),h=()=>{let c=E(t,u);$(v(c,`${t}/api`),u)};i.watcher.on("add",c=>{c.startsWith(g(u,r))&&x(),c.includes(`${t}/api`)&&(R(),h())}),i.watcher.on("unlink",c=>{c.startsWith(g(u,r))&&x(),c.includes(`${t}/api`)&&(R(),h())}),i.watcher.on("change",c=>{c.includes(`${t}/api`)&&!c.endsWith("middleware.ts")&&h()})}},d={plugins:[ue(),f],ssr:{noExternal:["@devlusoft/devix"]},...e.envPrefix?{envPrefix:e.envPrefix}:{}};return le(d,e.vite??{})}function N(e,{apiModule:t,renderModule:r,loaderTimeout:n}){e.all("/api/*",async o=>{try{return await t.handleApiRequest(o.req.url,o.req.raw)}catch(s){return console.error(s),o.json({error:"internal error"},500)}}),e.get("/_data/*",async o=>{try{let{pathname:s,search:l}=new URL(o.req.url,"http://localhost"),f=s.replace(/^\/_data/,"")+l,d=await r.runLoader(f,o.req.raw,{loaderTimeout:n});return o.json(d)}catch(s){return console.error(s),o.json({error:"internal error"},500)}})}import a from"picocolors";import{networkInterfaces as me}from"node:os";import{createRequire as fe}from"node:module";function ge(e){let t=me();for(let r of Object.values(t))for(let n of r??[])if(n.family==="IPv4"&&!n.internal)return`http://${n.address}:${e}/`;return null}function V(e){let r=fe(import.meta.url)("../../package.json").version,n=ge(e);console.log(),console.log(` ${a.bold(a.yellow("devix"))} ${a.dim(`v${r}`)}`),console.log(),console.log(` ${a.green("\u279C")} ${a.bold("Local:")} ${a.cyan(`http://localhost:${e}/`)}`),console.log(n?` ${a.green("\u279C")} ${a.bold("Network:")} ${a.cyan(n)}`:` ${a.green("\u279C")} ${a.bold("Network:")} ${a.dim("use --host to expose")}`),console.log()}async function W(e){let t=new Set;for(let[,r]of e.moduleGraph.idToModuleMap)r.id&&(r.id.endsWith(".css")||r.id.includes(".css?"))&&t.add(r.url);return[...t]}function B(e){if(typeof e=="number")return e;let t=e.trim().match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h)?$/);if(!t)throw new Error(`[devix] Invalid duration: "${e}". Use a number (ms) or a string like "5s", "2m", "500ms".`);let r=parseFloat(t[1]);switch(t[2]){case"h":return r*36e5;case"m":return r*6e4;case"s":return r*1e3;default:return r}}import{loadEnv as he}from"vite";function J(e){let t=he(e,process.cwd(),"");for(let[r,n]of Object.entries(t))process.env[r]===void 0&&(process.env[r]=n)}J("development");var X="virtual:devix/render",we="virtual:devix/api",m=(await import(`${process.cwd()}/devix.config.ts`)).default,Y=Number(process.env.PORT)||m.port||3e3,Ee=typeof m.host=="string"?m.host:m.host?"0.0.0.0":"localhost",p=await xe({...F(m),configFile:!1,appType:"custom",server:{middlewareMode:!0}}),G={render:async(...e)=>(await p.ssrLoadModule(X)).render(...e),runLoader:async(...e)=>(await p.ssrLoadModule(X)).runLoader(...e)},$e={handleApiRequest:async(...e)=>(await p.ssrLoadModule(we)).handleApiRequest(...e)},b=new ve;N(b,{renderModule:G,apiModule:$e});b.get("*",async e=>{try{let{html:t,statusCode:r,headers:n}=await G.render(e.req.url,e.req.raw,{loaderTimeout:B(m.loaderTimeout??1e4)}),s=(await W(p)).map(i=>`<link rel="stylesheet" href="${i}">`).join(`
155
+ `),l=s?t.replace("</head>",`${s}
156
+ </head>`):t,f=await p.transformIndexHtml(e.req.url,`<!DOCTYPE html>${l}`),d=e.html(f,r);for(let[i,u]of Object.entries(n))d.headers.set(i,u);return d}catch(t){return p.ssrFixStacktrace(t),console.error(t),e.text("Internal Server Error",500)}});var Pe=Re(b.fetch);ye(async(e,t)=>{await new Promise(r=>p.middlewares(e,t,r)),t.writableEnded||await Pe(e,t)}).listen(Y,Ee,()=>{V(Y)});
150
157
  //# sourceMappingURL=dev.js.map