@devlusoft/devix 0.4.4 → 0.5.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 (81) hide show
  1. package/README.md +64 -9
  2. package/dist/cli/build.js +27 -17
  3. package/dist/cli/build.js.map +3 -3
  4. package/dist/cli/dev-server.js +31 -21
  5. package/dist/cli/dev-server.js.map +4 -4
  6. package/dist/cli/generate.js +29 -19
  7. package/dist/cli/generate.js.map +3 -3
  8. package/dist/cli/index.js +30 -20
  9. package/dist/cli/index.js.map +4 -4
  10. package/dist/cli/start.js +1 -1
  11. package/dist/cli/start.js.map +4 -4
  12. package/dist/config.d.ts +61 -0
  13. package/dist/config.js +1 -1
  14. package/dist/config.js.map +3 -3
  15. package/dist/runtime/api-context.d.ts +9 -4
  16. package/dist/runtime/api-context.js +1 -1
  17. package/dist/runtime/api-context.js.map +3 -3
  18. package/dist/runtime/context.d.ts +1 -0
  19. package/dist/runtime/context.js.map +2 -2
  20. package/dist/runtime/create-handler.d.ts +52 -2
  21. package/dist/runtime/create-handler.js +1 -1
  22. package/dist/runtime/create-handler.js.map +3 -3
  23. package/dist/runtime/error-boundary.d.ts +7 -1
  24. package/dist/runtime/error-boundary.js +1 -1
  25. package/dist/runtime/error-boundary.js.map +3 -3
  26. package/dist/runtime/fetch.d.ts +1 -0
  27. package/dist/runtime/fetch.js +1 -1
  28. package/dist/runtime/fetch.js.map +3 -3
  29. package/dist/runtime/index.d.ts +6 -2
  30. package/dist/runtime/index.js +1 -1
  31. package/dist/runtime/index.js.map +4 -4
  32. package/dist/runtime/link.js.map +2 -2
  33. package/dist/runtime/router-provider.d.ts +24 -1
  34. package/dist/runtime/router-provider.js +1 -1
  35. package/dist/runtime/router-provider.js.map +3 -3
  36. package/dist/runtime/server-app.d.ts +2 -1
  37. package/dist/runtime/server-app.js +1 -1
  38. package/dist/runtime/server-app.js.map +3 -3
  39. package/dist/runtime/server-client.d.ts +66 -0
  40. package/dist/runtime/server-client.js +2 -0
  41. package/dist/runtime/server-client.js.map +7 -0
  42. package/dist/server/api.d.ts +2 -1
  43. package/dist/server/api.js +1 -1
  44. package/dist/server/api.js.map +4 -4
  45. package/dist/server/handler-store.d.ts +12 -0
  46. package/dist/server/handler-store.js.map +2 -2
  47. package/dist/server/public-index.js.map +2 -2
  48. package/dist/server/render.d.ts +8 -0
  49. package/dist/server/render.js +1 -1
  50. package/dist/server/render.js.map +4 -4
  51. package/dist/server/routes.d.ts +4 -2
  52. package/dist/server/routes.js +1 -1
  53. package/dist/server/routes.js.map +4 -4
  54. package/dist/server/server-bound.d.ts +11 -0
  55. package/dist/server/server-bound.js +2 -0
  56. package/dist/server/server-bound.js.map +7 -0
  57. package/dist/server/server-proxy.d.ts +15 -0
  58. package/dist/server/server-proxy.js +2 -0
  59. package/dist/server/server-proxy.js.map +7 -0
  60. package/dist/server/types.d.ts +1 -0
  61. package/dist/types.d.ts +22 -0
  62. package/dist/utils/banner.js +1 -1
  63. package/dist/utils/glob.d.ts +11 -0
  64. package/dist/utils/glob.js +2 -0
  65. package/dist/utils/glob.js.map +7 -0
  66. package/dist/utils/response.d.ts +33 -1
  67. package/dist/utils/response.js +1 -1
  68. package/dist/utils/response.js.map +3 -3
  69. package/dist/utils/standard-schema.d.ts +39 -0
  70. package/dist/utils/standard-schema.js +1 -0
  71. package/dist/utils/standard-schema.js.map +7 -0
  72. package/dist/vite/codegen/entry-client.js +5 -3
  73. package/dist/vite/codegen/entry-client.js.map +2 -2
  74. package/dist/vite/codegen/page-types.d.ts +11 -2
  75. package/dist/vite/codegen/page-types.js +3 -3
  76. package/dist/vite/codegen/page-types.js.map +3 -3
  77. package/dist/vite/codegen/server-entry.js +16 -8
  78. package/dist/vite/codegen/server-entry.js.map +2 -2
  79. package/dist/vite/index.js +26 -16
  80. package/dist/vite/index.js.map +3 -3
  81. package/package.json +5 -5
@@ -23,8 +23,40 @@ export interface RouteError {
23
23
  readonly [ERROR_BRAND]: true;
24
24
  readonly statusCode: number;
25
25
  readonly message: string;
26
+ readonly code?: string;
26
27
  readonly data?: unknown;
27
28
  }
28
- export declare function error(statusCode: number, message: string, data?: unknown): RouteError;
29
+ export interface ErrorOptions {
30
+ code?: string;
31
+ data?: unknown;
32
+ }
33
+ /**
34
+ * Crea un error tipado que funciona en loaders, guards y handlers API.
35
+ *
36
+ * En loaders/guards: retórnalo (no lo lances) y el sistema renderiza `error.tsx`.
37
+ * En handlers API: retórnalo y el sistema serializa el shape `ErrorBody` como JSON
38
+ * con el statusCode correcto.
39
+ *
40
+ * ```ts
41
+ * return error(404, 'Post no encontrado', { code: 'POST_NOT_FOUND' })
42
+ * ```
43
+ */
44
+ export declare function error(statusCode: number, message: string, options?: ErrorOptions): RouteError;
29
45
  export declare function isLoaderError(value: unknown): value is RouteError;
46
+ /**
47
+ * Shape público del body de un error API. Todos los errores emitidos por `error()`
48
+ * o `DevixError` se serializan a este shape. `FetchError.body` del cliente lo recibe.
49
+ */
50
+ export interface ErrorBody {
51
+ statusCode: number;
52
+ message: string;
53
+ code?: string;
54
+ data?: unknown;
55
+ }
56
+ export declare function errorToBody(err: {
57
+ statusCode: number;
58
+ message: string;
59
+ code?: string;
60
+ data?: unknown;
61
+ }): ErrorBody;
30
62
  export {};
@@ -1,2 +1,2 @@
1
- function a(e,n=200){return new Response(JSON.stringify(e),{status:n,headers:{"Content-Type":"application/json"}})}var u=(e,n=200)=>new Response(e,{status:n,headers:{"Content-Type":"text/plain; charset=utf-8"}}),o=Symbol.for("devix.redirect");function c(e,n){let r=typeof n=="number"?n:n?.status??302,s=typeof n=="object"?n?.replace??!1:!1;return{[o]:!0,url:e,status:r,replace:s}}function i(e){return typeof e=="object"&&e!==null&&o in e}var t=Symbol.for("devix.loaderError");function R(e,n,r){return{[t]:!0,statusCode:e,message:n,data:r}}function p(e){return typeof e=="object"&&e!==null&&t in e}export{R as error,p as isLoaderError,i as isRedirect,a as json,c as redirect,u as text};
1
+ function a(e,n=200){return new Response(JSON.stringify(e),{status:n,headers:{"Content-Type":"application/json"}})}var d=(e,n=200)=>new Response(e,{status:n,headers:{"Content-Type":"text/plain; charset=utf-8"}}),r=Symbol.for("devix.redirect");function u(e,n){let o=typeof n=="number"?n:n?.status??302,s=typeof n=="object"?n?.replace??!1:!1;return{[r]:!0,url:e,status:o,replace:s}}function c(e){return typeof e=="object"&&e!==null&&r in e}var t=Symbol.for("devix.loaderError");function i(e,n,o){return{[t]:!0,statusCode:e,message:n,code:o?.code,data:o?.data}}function p(e){return typeof e=="object"&&e!==null&&t in e}function R(e){let n={statusCode:e.statusCode,message:e.message};return e.code!==void 0&&(n.code=e.code),e.data!==void 0&&(n.data=e.data),n}export{i as error,R as errorToBody,p as isLoaderError,c as isRedirect,a as json,u as redirect,d as text};
2
2
  //# sourceMappingURL=response.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/response.ts"],
4
- "sourcesContent": ["export type JsonResponse<T = unknown, S extends number = number> = Response & {\n readonly __body: T\n readonly __status: S\n}\n\nexport function json<const T>(data: T): JsonResponse<T, 200>\nexport function json<const T, const S extends number>(data: T, status: S): JsonResponse<T, S>\nexport function json<const T>(data: T, status: number = 200): JsonResponse<T, any> {\n return new Response(JSON.stringify(data), {\n status,\n headers: {'Content-Type': 'application/json'},\n }) as JsonResponse<T, any>\n}\n\nexport const text = (body: string, status = 200): Response =>\n new Response(body, {status, headers: {'Content-Type': 'text/plain; charset=utf-8'}})\n\nconst REDIRECT_BRAND = Symbol.for('devix.redirect')\n\nexport interface RedirectOptions {\n status?: number\n replace?: boolean\n}\n\nexport interface Redirect {\n readonly [REDIRECT_BRAND]: true\n readonly url: string\n readonly status: number\n readonly replace: boolean\n}\n\nexport function redirect(url: string, statusOrOptions?: number | RedirectOptions): Redirect {\n const status = typeof statusOrOptions === 'number' ? statusOrOptions : (statusOrOptions?.status ?? 302)\n const replace = typeof statusOrOptions === 'object' ? (statusOrOptions?.replace ?? false) : false\n return {[REDIRECT_BRAND]: true, url, status, replace} as Redirect\n}\n\nexport function isRedirect(value: unknown): value is Redirect {\n return typeof value === 'object' && value !== null && REDIRECT_BRAND in value\n}\n\nconst ERROR_BRAND = Symbol.for('devix.loaderError')\n\nexport interface RouteError {\n readonly [ERROR_BRAND]: true\n readonly statusCode: number\n readonly message: string\n readonly data?: unknown\n}\n\nexport function error(statusCode: number, message: string, data?: unknown): RouteError {\n return { [ERROR_BRAND]: true, statusCode, message, data } as RouteError\n}\n\nexport function isLoaderError(value: unknown): value is RouteError {\n return typeof value === 'object' && value !== null && ERROR_BRAND in value\n}\n"],
5
- "mappings": "AAOO,SAASA,EAAcC,EAASC,EAAiB,IAA2B,CAC/E,OAAO,IAAI,SAAS,KAAK,UAAUD,CAAI,EAAG,CACtC,OAAAC,EACA,QAAS,CAAC,eAAgB,kBAAkB,CAChD,CAAC,CACL,CAEO,IAAMC,EAAO,CAACC,EAAcF,EAAS,MACxC,IAAI,SAASE,EAAM,CAAC,OAAAF,EAAQ,QAAS,CAAC,eAAgB,2BAA2B,CAAC,CAAC,EAEjFG,EAAiB,OAAO,IAAI,gBAAgB,EAc3C,SAASC,EAASC,EAAaC,EAAsD,CACxF,IAAMN,EAAS,OAAOM,GAAoB,SAAWA,EAAmBA,GAAiB,QAAU,IAC7FC,EAAU,OAAOD,GAAoB,SAAYA,GAAiB,SAAW,GAAS,GAC5F,MAAO,CAAC,CAACH,CAAc,EAAG,GAAM,IAAAE,EAAK,OAAAL,EAAQ,QAAAO,CAAO,CACxD,CAEO,SAASC,EAAWC,EAAmC,CAC1D,OAAO,OAAOA,GAAU,UAAYA,IAAU,MAAQN,KAAkBM,CAC5E,CAEA,IAAMC,EAAc,OAAO,IAAI,mBAAmB,EAS3C,SAASC,EAAMC,EAAoBC,EAAiBd,EAA4B,CACnF,MAAO,CAAE,CAACW,CAAW,EAAG,GAAM,WAAAE,EAAY,QAAAC,EAAS,KAAAd,CAAK,CAC5D,CAEO,SAASe,EAAcL,EAAqC,CAC/D,OAAO,OAAOA,GAAU,UAAYA,IAAU,MAAQC,KAAeD,CACzE",
6
- "names": ["json", "data", "status", "text", "body", "REDIRECT_BRAND", "redirect", "url", "statusOrOptions", "replace", "isRedirect", "value", "ERROR_BRAND", "error", "statusCode", "message", "isLoaderError"]
4
+ "sourcesContent": ["export type JsonResponse<T = unknown, S extends number = number> = Response & {\n readonly __body: T\n readonly __status: S\n}\n\nexport function json<const T>(data: T): JsonResponse<T, 200>\nexport function json<const T, const S extends number>(data: T, status: S): JsonResponse<T, S>\nexport function json<const T>(data: T, status: number = 200): JsonResponse<T, any> {\n return new Response(JSON.stringify(data), {\n status,\n headers: {'Content-Type': 'application/json'},\n }) as JsonResponse<T, any>\n}\n\nexport const text = (body: string, status = 200): Response =>\n new Response(body, {status, headers: {'Content-Type': 'text/plain; charset=utf-8'}})\n\nconst REDIRECT_BRAND = Symbol.for('devix.redirect')\n\nexport interface RedirectOptions {\n status?: number\n replace?: boolean\n}\n\nexport interface Redirect {\n readonly [REDIRECT_BRAND]: true\n readonly url: string\n readonly status: number\n readonly replace: boolean\n}\n\nexport function redirect(url: string, statusOrOptions?: number | RedirectOptions): Redirect {\n const status = typeof statusOrOptions === 'number' ? statusOrOptions : (statusOrOptions?.status ?? 302)\n const replace = typeof statusOrOptions === 'object' ? (statusOrOptions?.replace ?? false) : false\n return {[REDIRECT_BRAND]: true, url, status, replace} as Redirect\n}\n\nexport function isRedirect(value: unknown): value is Redirect {\n return typeof value === 'object' && value !== null && REDIRECT_BRAND in value\n}\n\nconst ERROR_BRAND = Symbol.for('devix.loaderError')\n\nexport interface RouteError {\n readonly [ERROR_BRAND]: true\n readonly statusCode: number\n readonly message: string\n readonly code?: string\n readonly data?: unknown\n}\n\nexport interface ErrorOptions {\n code?: string\n data?: unknown\n}\n\n/**\n * Crea un error tipado que funciona en loaders, guards y handlers API.\n *\n * En loaders/guards: ret\u00F3rnalo (no lo lances) y el sistema renderiza `error.tsx`.\n * En handlers API: ret\u00F3rnalo y el sistema serializa el shape `ErrorBody` como JSON\n * con el statusCode correcto.\n *\n * ```ts\n * return error(404, 'Post no encontrado', { code: 'POST_NOT_FOUND' })\n * ```\n */\nexport function error(statusCode: number, message: string, options?: ErrorOptions): RouteError {\n return {\n [ERROR_BRAND]: true,\n statusCode,\n message,\n code: options?.code,\n data: options?.data,\n } as RouteError\n}\n\nexport function isLoaderError(value: unknown): value is RouteError {\n return typeof value === 'object' && value !== null && ERROR_BRAND in value\n}\n\n/**\n * Shape p\u00FAblico del body de un error API. Todos los errores emitidos por `error()`\n * o `DevixError` se serializan a este shape. `FetchError.body` del cliente lo recibe.\n */\nexport interface ErrorBody {\n statusCode: number\n message: string\n code?: string\n data?: unknown\n}\n\nexport function errorToBody(err: { statusCode: number; message: string; code?: string; data?: unknown }): ErrorBody {\n const body: ErrorBody = { statusCode: err.statusCode, message: err.message }\n if (err.code !== undefined) body.code = err.code\n if (err.data !== undefined) body.data = err.data\n return body\n}\n"],
5
+ "mappings": "AAOO,SAASA,EAAcC,EAASC,EAAiB,IAA2B,CAC/E,OAAO,IAAI,SAAS,KAAK,UAAUD,CAAI,EAAG,CACtC,OAAAC,EACA,QAAS,CAAC,eAAgB,kBAAkB,CAChD,CAAC,CACL,CAEO,IAAMC,EAAO,CAACC,EAAcF,EAAS,MACxC,IAAI,SAASE,EAAM,CAAC,OAAAF,EAAQ,QAAS,CAAC,eAAgB,2BAA2B,CAAC,CAAC,EAEjFG,EAAiB,OAAO,IAAI,gBAAgB,EAc3C,SAASC,EAASC,EAAaC,EAAsD,CACxF,IAAMN,EAAS,OAAOM,GAAoB,SAAWA,EAAmBA,GAAiB,QAAU,IAC7FC,EAAU,OAAOD,GAAoB,SAAYA,GAAiB,SAAW,GAAS,GAC5F,MAAO,CAAC,CAACH,CAAc,EAAG,GAAM,IAAAE,EAAK,OAAAL,EAAQ,QAAAO,CAAO,CACxD,CAEO,SAASC,EAAWC,EAAmC,CAC1D,OAAO,OAAOA,GAAU,UAAYA,IAAU,MAAQN,KAAkBM,CAC5E,CAEA,IAAMC,EAAc,OAAO,IAAI,mBAAmB,EA0B3C,SAASC,EAAMC,EAAoBC,EAAiBC,EAAoC,CAC3F,MAAO,CACH,CAACJ,CAAW,EAAG,GACf,WAAAE,EACA,QAAAC,EACA,KAAMC,GAAS,KACf,KAAMA,GAAS,IACnB,CACJ,CAEO,SAASC,EAAcN,EAAqC,CAC/D,OAAO,OAAOA,GAAU,UAAYA,IAAU,MAAQC,KAAeD,CACzE,CAaO,SAASO,EAAYC,EAAwF,CAChH,IAAMf,EAAkB,CAAE,WAAYe,EAAI,WAAY,QAASA,EAAI,OAAQ,EAC3E,OAAIA,EAAI,OAAS,SAAWf,EAAK,KAAOe,EAAI,MACxCA,EAAI,OAAS,SAAWf,EAAK,KAAOe,EAAI,MACrCf,CACX",
6
+ "names": ["json", "data", "status", "text", "body", "REDIRECT_BRAND", "redirect", "url", "statusOrOptions", "replace", "isRedirect", "value", "ERROR_BRAND", "error", "statusCode", "message", "options", "isLoaderError", "errorToBody", "err"]
7
7
  }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Tipos del contrato Standard Schema v1 — https://standardschema.dev
3
+ *
4
+ * Implementado por Zod 3.24+, Valibot, ArkType, Effect Schema y otros.
5
+ * devix usa este contrato para validar bodies de handlers de forma agnóstica
6
+ * al validador.
7
+ */
8
+ export interface StandardSchemaV1<Input = unknown, Output = Input> {
9
+ readonly '~standard': StandardSchemaV1.Props<Input, Output>;
10
+ }
11
+ export declare namespace StandardSchemaV1 {
12
+ interface Props<Input = unknown, Output = Input> {
13
+ readonly version: 1;
14
+ readonly vendor: string;
15
+ readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
16
+ readonly types?: Types<Input, Output>;
17
+ }
18
+ type Result<Output> = SuccessResult<Output> | FailureResult;
19
+ interface SuccessResult<Output> {
20
+ readonly value: Output;
21
+ readonly issues?: undefined;
22
+ }
23
+ interface FailureResult {
24
+ readonly issues: readonly Issue[];
25
+ }
26
+ interface Issue {
27
+ readonly message: string;
28
+ readonly path?: readonly (PropertyKey | PathSegment)[] | undefined;
29
+ }
30
+ interface PathSegment {
31
+ readonly key: PropertyKey;
32
+ }
33
+ interface Types<Input = unknown, Output = Input> {
34
+ readonly input: Input;
35
+ readonly output: Output;
36
+ }
37
+ type InferInput<S> = S extends StandardSchemaV1<infer I, any> ? I : never;
38
+ type InferOutput<S> = S extends StandardSchemaV1<any, infer O> ? O : never;
39
+ }
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=standard-schema.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -1,5 +1,5 @@
1
1
  function o({cssUrls:t}){return`
2
- ${t.map(e=>`import '${e}'`).join(`
2
+ ${t.map(a=>`import '${a}'`).join(`
3
3
  `)}
4
4
  import "@vitejs/plugin-react/preamble"
5
5
  import React from "react"
@@ -16,11 +16,12 @@ if (!window.__DEVIX__) {
16
16
  const {metadata, viewport, clientEntry} = window.__DEVIX__
17
17
  const loaderData = window.__LOADER_DATA__
18
18
  const layoutsData = window.__LAYOUTS_DATA__ ?? []
19
+ const guardData = window.__GUARD_DATA__ ?? null
19
20
 
20
21
  const matched = matchClientRoute(window.location.pathname)
21
22
 
22
23
  if (window.__LOADER_ERROR__) {
23
- const {statusCode, message, data} = window.__LOADER_ERROR__
24
+ const {statusCode, message, code, data} = window.__LOADER_ERROR__
24
25
  const ErrorPage = await loadErrorPage() ?? getDefaultErrorPage()
25
26
  createRoot(root).render(
26
27
  React.createElement(RouterProvider, {
@@ -28,7 +29,7 @@ if (!window.__DEVIX__) {
28
29
  initialData: null,
29
30
  initialParams: {},
30
31
  initialPage: () => null,
31
- initialError: {statusCode, message, data},
32
+ initialError: {statusCode, message, code, data},
32
33
  initialErrorPage: ErrorPage,
33
34
  })
34
35
  )
@@ -46,6 +47,7 @@ if (!window.__DEVIX__) {
46
47
  initialPage: pageMod.default,
47
48
  initialLayouts: layoutMods.map(m => m.default),
48
49
  initialLayoutsData: layoutsData,
50
+ initialGuardData: guardData,
49
51
  initialMeta: metadata,
50
52
  initialViewport: viewport,
51
53
  })
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/vite/codegen/entry-client.ts"],
4
- "sourcesContent": ["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 (window.__LOADER_ERROR__) {\n const {statusCode, message, data} = window.__LOADER_ERROR__\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 initialError: {statusCode, message, data},\n initialErrorPage: ErrorPage,\n })\n )\n } else 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\n if (window.location.hash) { \n const id = window.location.hash.slice(1) \n const scrollBehavior = getComputedStyle(document.documentElement).scrollBehavior \n requestAnimationFrame(() => { \n document.getElementById(id)?.scrollIntoView({ behavior: scrollBehavior }) \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}"],
5
- "mappings": "AAIO,SAASA,EAAoB,CAAE,QAAAC,CAAQ,EAA+B,CAGzE,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CA4EZ",
4
+ "sourcesContent": ["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 const guardData = window.__GUARD_DATA__ ?? null\n\n const matched = matchClientRoute(window.location.pathname)\n\n if (window.__LOADER_ERROR__) {\n const {statusCode, message, code, data} = window.__LOADER_ERROR__\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 initialError: {statusCode, message, code, data},\n initialErrorPage: ErrorPage,\n })\n )\n } else 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 initialGuardData: guardData,\n initialMeta: metadata,\n initialViewport: viewport,\n })\n )\n\n if (window.location.hash) { \n const id = window.location.hash.slice(1) \n const scrollBehavior = getComputedStyle(document.documentElement).scrollBehavior \n requestAnimationFrame(() => { \n document.getElementById(id)?.scrollIntoView({ behavior: scrollBehavior }) \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}"],
5
+ "mappings": "AAIO,SAASA,EAAoB,CAAE,QAAAC,CAAQ,EAA+B,CAGzE,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CA8EZ",
6
6
  "names": ["generateEntryClient", "cssUrls", "u"]
7
7
  }
@@ -1,5 +1,14 @@
1
+ export interface LoaderExportInfo {
2
+ exists: boolean;
3
+ isAsync: boolean;
4
+ isReExport: boolean;
5
+ }
6
+ export declare function inspectLoaderExport(code: string, filePath: string): LoaderExportInfo;
1
7
  export declare function hasLoaderExport(code: string, filePath: string): boolean;
2
8
  export declare function generatePageTypesDts(importPath: string, withLoader: boolean): string;
3
- export declare function writePageTypes(pageRelPath: string, root: string): void;
9
+ export interface WritePageTypesResult {
10
+ warnings: string[];
11
+ }
12
+ export declare function writePageTypes(pageRelPath: string, root: string): WritePageTypesResult;
4
13
  export declare function deletePageTypes(pageRelPath: string, root: string): void;
5
- export declare function scanAndWritePageTypes(appDir: string, root: string): void;
14
+ export declare function scanAndWritePageTypes(appDir: string, root: string): WritePageTypesResult;
@@ -1,5 +1,5 @@
1
- import{existsSync as d,mkdirSync as y,readdirSync as x,readFileSync as p,rmSync as m,statSync as P,writeFileSync as v}from"node:fs";import{join as s,relative as f}from"node:path";import{parseSync as D}from"oxc-parser";function l(r,t){let n=[];for(let e of x(r)){let o=s(r,e);P(o).isDirectory()?n.push(...l(o,t)):/\.(ts|tsx)$/.test(e)&&e!=="layout.tsx"&&e!=="error.tsx"&&n.push(f(t,o).replace(/\\/g,"/"))}return n}function h(r,t){let n=D(t,r,{sourceType:"module"});for(let e of n.program.body){if(e.type!=="ExportNamedDeclaration")continue;let o=e.declaration;if(o?.type==="FunctionDeclaration"&&o.id?.name==="loader")return!0;if(o?.type==="VariableDeclaration"){for(let i of o.declarations)if(i.id.type==="Identifier"&&i.id.name==="loader")return!0}for(let i of e.specifiers??[])if(i.exported.type==="Identifier"&&i.exported.name==="loader")return!0}return!1}function S(r,t){return t?`// auto-generado por devix \u2014 no editar
2
- import type { loader } from "${r}"
1
+ import{existsSync as d,mkdirSync as m,readdirSync as P,readFileSync as f,rmSync as E,statSync as b,writeFileSync as v}from"node:fs";import{join as a,relative as l}from"node:path";import{parseSync as A}from"oxc-parser";function u(n,e){let s=[];for(let t of P(n)){let r=a(n,t);b(r).isDirectory()?s.push(...u(r,e)):/\.(ts|tsx)$/.test(t)&&t!=="layout.tsx"&&t!=="error.tsx"&&s.push(l(e,r).replace(/\\/g,"/"))}return s}function x(n,e){let s=A(e,n,{sourceType:"module"});for(let t of s.program.body){if(t.type!=="ExportNamedDeclaration")continue;let r=t.declaration;if(r?.type==="FunctionDeclaration"&&r.id?.name==="loader")return{exists:!0,isAsync:r.async,isReExport:!1};if(r?.type==="VariableDeclaration"){for(let o of r.declarations)if(o.id.type==="Identifier"&&o.id.name==="loader"){let i=o.init;return{exists:!0,isAsync:i?.type==="ArrowFunctionExpression"&&i.async||i?.type==="FunctionExpression"&&i.async,isReExport:!1}}}for(let o of t.specifiers??[])if(o.exported.type==="Identifier"&&o.exported.name==="loader")return{exists:!0,isAsync:!1,isReExport:!0}}return{exists:!1,isAsync:!1,isReExport:!1}}function S(n,e){return x(n,e).exists}function R(n,e){return e?`// auto-generado por devix \u2014 no editar
2
+ import type { loader } from "${n}"
3
3
  import type { Redirect } from "@devlusoft/devix"
4
4
 
5
5
  export type PageData = Exclude<
@@ -10,5 +10,5 @@ export type PageParams = NonNullable<Parameters<typeof loader>[0]>["params"]
10
10
  `:`// auto-generado por devix - no editar
11
11
  export type PageData = undefined
12
12
  export type PageParams = Record<string, string>
13
- `}function b(r,t){let n=s(t,r),e=p(n,"utf-8"),o=h(e,n),i=s(t,".devix","pages",r.replace(/\.(tsx?|jsx?)$/,"")),a=s(i,"$types.d.ts"),u=n.replace(/\.(tsx?|jsx?)$/,""),g=f(i,u).replace(/\\/g,"/"),c=S(g,o);d(a)&&p(a,"utf-8")===c||(y(i,{recursive:!0}),v(a,c,"utf-8"))}function w(r,t){let n=s(t,".devix","pages",r.replace(/\.(tsx?|jsx?)$/,"")),e=s(n,"$types.d.ts");d(e)&&m(e)}function j(r,t){let n=s(t,r,"pages"),e;try{e=l(n,t)}catch{return}for(let o of e)try{b(o,t)}catch{}}export{w as deletePageTypes,S as generatePageTypesDts,h as hasLoaderExport,j as scanAndWritePageTypes,b as writePageTypes};
13
+ `}function D(n,e){let s=a(e,n),t=f(s,"utf-8"),r=x(t,s),o=[];r.exists&&!r.isAsync&&!r.isReExport&&o.push(`[devix] ${n}: 'loader' must be async. Use 'export async function loader' or 'export const loader = async (...) => ...'.`);let i=a(e,".devix","pages",n.replace(/\.(tsx?|jsx?)$/,"")),c=a(i,"$types.d.ts"),y=s.replace(/\.(tsx?|jsx?)$/,""),g=l(i,y).replace(/\\/g,"/"),p=R(g,r.exists);return d(c)&&f(c,"utf-8")===p?{warnings:o}:(m(i,{recursive:!0}),v(c,p,"utf-8"),{warnings:o})}function $(n,e){let s=a(e,".devix","pages",n.replace(/\.(tsx?|jsx?)$/,"")),t=a(s,"$types.d.ts");d(t)&&E(t)}function N(n,e){let s=a(e,n,"pages"),t=[],r;try{r=u(s,e)}catch{return{warnings:t}}for(let o of r)try{let i=D(o,e);t.push(...i.warnings)}catch{}return{warnings:t}}export{$ as deletePageTypes,R as generatePageTypesDts,S as hasLoaderExport,x as inspectLoaderExport,N as scanAndWritePageTypes,D as writePageTypes};
14
14
  //# sourceMappingURL=page-types.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/vite/codegen/page-types.ts"],
4
- "sourcesContent": ["import {existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync} from \"node:fs\";\nimport {join, relative} from \"node:path\";\nimport {parseSync} from \"oxc-parser\";\n\nfunction walkPages(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(...walkPages(full, root))\n } else if (/\\.(ts|tsx)$/.test(name) && name !== 'layout.tsx' && name !== 'error.tsx') {\n entries.push(relative(root, full).replace(/\\\\/g, '/'))\n }\n }\n return entries\n}\n\nexport function hasLoaderExport(code: string, filePath: string): boolean {\n const ast = parseSync(filePath, code, {sourceType: 'module'})\n for (const node of ast.program.body) {\n if (node.type !== 'ExportNamedDeclaration') continue\n const decl = node.declaration\n if (decl?.type === 'FunctionDeclaration' && decl.id?.name === 'loader') return true\n if (decl?.type === 'VariableDeclaration') {\n for (const d of decl.declarations) {\n if (d.id.type === 'Identifier' && d.id.name === 'loader') return true\n }\n }\n for (const spec of (node.specifiers ?? [])) {\n if (spec.exported.type === 'Identifier' && spec.exported.name === 'loader') return true\n }\n }\n return false\n}\n\nexport function generatePageTypesDts(importPath: string, withLoader: boolean): string {\n if (!withLoader) {\n return '// auto-generado por devix - no editar\\nexport type PageData = undefined\\nexport type PageParams = Record<string, string>\\n'\n }\n return `// auto-generado por devix \u2014 no editar\\nimport type { loader } from \"${importPath}\"\\nimport type { Redirect } from \"@devlusoft/devix\"\\n\\nexport type PageData = Exclude<\\n Awaited<ReturnType<NonNullable<typeof loader>>>,\\n Redirect | void | undefined\\n>\\nexport type PageParams = NonNullable<Parameters<typeof loader>[0]>[\"params\"]\\n`\n}\n\nexport function writePageTypes(pageRelPath: string, root: string): void {\n const fullPath = join(root, pageRelPath)\n const code = readFileSync(fullPath, 'utf-8')\n const withLoader = hasLoaderExport(code, fullPath)\n\n const typesDir = join(root, '.devix', 'pages', pageRelPath.replace(/\\.(tsx?|jsx?)$/, ''))\n const outPath = join(typesDir, '$types.d.ts')\n\n const pageAbsNoExt = fullPath.replace(/\\.(tsx?|jsx?)$/, '')\n const importPath = relative(typesDir, pageAbsNoExt).replace(/\\\\/g, '/')\n\n const content = generatePageTypesDts(importPath, withLoader)\n\n if (existsSync(outPath) && readFileSync(outPath, 'utf-8') === content) return\n\n mkdirSync(typesDir, {recursive: true})\n writeFileSync(outPath, content, 'utf-8')\n}\n\nexport function deletePageTypes(pageRelPath: string, root: string): void {\n const typesDir = join(root, '.devix', 'pages', pageRelPath.replace(/\\.(tsx?|jsx?)$/, ''))\n const outPath = join(typesDir, '$types.d.ts')\n if (existsSync(outPath)) rmSync(outPath)\n}\n\nexport function scanAndWritePageTypes(appDir: string, root: string): void {\n const pagesDir = join(root, appDir, 'pages')\n let files: string[]\n try {\n files = walkPages(pagesDir, root)\n } catch {\n return\n }\n for (const file of files) {\n try {\n writePageTypes(file, root)\n } catch {\n /* ignorar archivos no procesables */\n }\n }\n}"],
5
- "mappings": "AAAA,OAAQ,cAAAA,EAAY,aAAAC,EAAW,eAAAC,EAAa,gBAAAC,EAAc,UAAAC,EAAQ,YAAAC,EAAU,iBAAAC,MAAoB,UAChG,OAAQ,QAAAC,EAAM,YAAAC,MAAe,YAC7B,OAAQ,aAAAC,MAAgB,aAExB,SAASC,EAAUC,EAAaC,EAAwB,CACpD,IAAMC,EAAoB,CAAC,EAC3B,QAAWC,KAAQZ,EAAYS,CAAG,EAAG,CACjC,IAAMI,EAAOR,EAAKI,EAAKG,CAAI,EACvBT,EAASU,CAAI,EAAE,YAAY,EAC3BF,EAAQ,KAAK,GAAGH,EAAUK,EAAMH,CAAI,CAAC,EAC9B,cAAc,KAAKE,CAAI,GAAKA,IAAS,cAAgBA,IAAS,aACrED,EAAQ,KAAKL,EAASI,EAAMG,CAAI,EAAE,QAAQ,MAAO,GAAG,CAAC,CAE7D,CACA,OAAOF,CACX,CAEO,SAASG,EAAgBC,EAAcC,EAA2B,CACrE,IAAMC,EAAMV,EAAUS,EAAUD,EAAM,CAAC,WAAY,QAAQ,CAAC,EAC5D,QAAWG,KAAQD,EAAI,QAAQ,KAAM,CACjC,GAAIC,EAAK,OAAS,yBAA0B,SAC5C,IAAMC,EAAOD,EAAK,YAClB,GAAIC,GAAM,OAAS,uBAAyBA,EAAK,IAAI,OAAS,SAAU,MAAO,GAC/E,GAAIA,GAAM,OAAS,uBACf,QAAWC,KAAKD,EAAK,aACjB,GAAIC,EAAE,GAAG,OAAS,cAAgBA,EAAE,GAAG,OAAS,SAAU,MAAO,GAGzE,QAAWC,KAASH,EAAK,YAAc,CAAC,EACpC,GAAIG,EAAK,SAAS,OAAS,cAAgBA,EAAK,SAAS,OAAS,SAAU,MAAO,EAE3F,CACA,MAAO,EACX,CAEO,SAASC,EAAqBC,EAAoBC,EAA6B,CAClF,OAAKA,EAGE;AAAA,+BAAwED,CAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAF9E;AAAA;AAAA;AAAA,CAGf,CAEO,SAASE,EAAeC,EAAqBhB,EAAoB,CACpE,IAAMiB,EAAWtB,EAAKK,EAAMgB,CAAW,EACjCX,EAAOd,EAAa0B,EAAU,OAAO,EACrCH,EAAaV,EAAgBC,EAAMY,CAAQ,EAE3CC,EAAWvB,EAAKK,EAAM,SAAU,QAASgB,EAAY,QAAQ,iBAAkB,EAAE,CAAC,EAClFG,EAAUxB,EAAKuB,EAAU,aAAa,EAEtCE,EAAeH,EAAS,QAAQ,iBAAkB,EAAE,EACpDJ,EAAajB,EAASsB,EAAUE,CAAY,EAAE,QAAQ,MAAO,GAAG,EAEhEC,EAAUT,EAAqBC,EAAYC,CAAU,EAEvD1B,EAAW+B,CAAO,GAAK5B,EAAa4B,EAAS,OAAO,IAAME,IAE9DhC,EAAU6B,EAAU,CAAC,UAAW,EAAI,CAAC,EACrCxB,EAAcyB,EAASE,EAAS,OAAO,EAC3C,CAEO,SAASC,EAAgBN,EAAqBhB,EAAoB,CACrE,IAAMkB,EAAWvB,EAAKK,EAAM,SAAU,QAASgB,EAAY,QAAQ,iBAAkB,EAAE,CAAC,EAClFG,EAAUxB,EAAKuB,EAAU,aAAa,EACxC9B,EAAW+B,CAAO,GAAG3B,EAAO2B,CAAO,CAC3C,CAEO,SAASI,EAAsBC,EAAgBxB,EAAoB,CACtE,IAAMyB,EAAW9B,EAAKK,EAAMwB,EAAQ,OAAO,EACvCE,EACJ,GAAI,CACAA,EAAQ5B,EAAU2B,EAAUzB,CAAI,CACpC,MAAQ,CACJ,MACJ,CACA,QAAW2B,KAAQD,EACf,GAAI,CACAX,EAAeY,EAAM3B,CAAI,CAC7B,MAAQ,CAER,CAER",
6
- "names": ["existsSync", "mkdirSync", "readdirSync", "readFileSync", "rmSync", "statSync", "writeFileSync", "join", "relative", "parseSync", "walkPages", "dir", "root", "entries", "name", "full", "hasLoaderExport", "code", "filePath", "ast", "node", "decl", "d", "spec", "generatePageTypesDts", "importPath", "withLoader", "writePageTypes", "pageRelPath", "fullPath", "typesDir", "outPath", "pageAbsNoExt", "content", "deletePageTypes", "scanAndWritePageTypes", "appDir", "pagesDir", "files", "file"]
4
+ "sourcesContent": ["import {existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync} from \"node:fs\";\nimport {join, relative} from \"node:path\";\nimport {parseSync} from \"oxc-parser\";\n\nfunction walkPages(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(...walkPages(full, root))\n } else if (/\\.(ts|tsx)$/.test(name) && name !== 'layout.tsx' && name !== 'error.tsx') {\n entries.push(relative(root, full).replace(/\\\\/g, '/'))\n }\n }\n return entries\n}\n\nexport interface LoaderExportInfo {\n exists: boolean\n isAsync: boolean\n isReExport: boolean\n}\n\nexport function inspectLoaderExport(code: string, filePath: string): LoaderExportInfo {\n const ast = parseSync(filePath, code, {sourceType: 'module'})\n for (const node of ast.program.body) {\n if (node.type !== 'ExportNamedDeclaration') continue\n const decl = node.declaration\n if (decl?.type === 'FunctionDeclaration' && decl.id?.name === 'loader') {\n return {exists: true, isAsync: decl.async, isReExport: false}\n }\n if (decl?.type === 'VariableDeclaration') {\n for (const d of decl.declarations) {\n if (d.id.type === 'Identifier' && d.id.name === 'loader') {\n const init = d.init\n const isAsync =\n (init?.type === 'ArrowFunctionExpression' && init.async) ||\n (init?.type === 'FunctionExpression' && init.async)\n return {exists: true, isAsync, isReExport: false}\n }\n }\n }\n for (const spec of (node.specifiers ?? [])) {\n if (spec.exported.type === 'Identifier' && spec.exported.name === 'loader') {\n return {exists: true, isAsync: false, isReExport: true}\n }\n }\n }\n return {exists: false, isAsync: false, isReExport: false}\n}\n\nexport function hasLoaderExport(code: string, filePath: string): boolean {\n return inspectLoaderExport(code, filePath).exists\n}\n\nexport function generatePageTypesDts(importPath: string, withLoader: boolean): string {\n if (!withLoader) {\n return '// auto-generado por devix - no editar\\nexport type PageData = undefined\\nexport type PageParams = Record<string, string>\\n'\n }\n return `// auto-generado por devix \u2014 no editar\\nimport type { loader } from \"${importPath}\"\\nimport type { Redirect } from \"@devlusoft/devix\"\\n\\nexport type PageData = Exclude<\\n Awaited<ReturnType<NonNullable<typeof loader>>>,\\n Redirect | void | undefined\\n>\\nexport type PageParams = NonNullable<Parameters<typeof loader>[0]>[\"params\"]\\n`\n}\n\nexport interface WritePageTypesResult {\n warnings: string[]\n}\n\nexport function writePageTypes(pageRelPath: string, root: string): WritePageTypesResult {\n const fullPath = join(root, pageRelPath)\n const code = readFileSync(fullPath, 'utf-8')\n const loaderInfo = inspectLoaderExport(code, fullPath)\n const warnings: string[] = []\n\n if (loaderInfo.exists && !loaderInfo.isAsync && !loaderInfo.isReExport) {\n warnings.push(\n `[devix] ${pageRelPath}: 'loader' must be async. ` +\n `Use 'export async function loader' or 'export const loader = async (...) => ...'.`\n )\n }\n\n const typesDir = join(root, '.devix', 'pages', pageRelPath.replace(/\\.(tsx?|jsx?)$/, ''))\n const outPath = join(typesDir, '$types.d.ts')\n\n const pageAbsNoExt = fullPath.replace(/\\.(tsx?|jsx?)$/, '')\n const importPath = relative(typesDir, pageAbsNoExt).replace(/\\\\/g, '/')\n\n const content = generatePageTypesDts(importPath, loaderInfo.exists)\n\n if (existsSync(outPath) && readFileSync(outPath, 'utf-8') === content) return {warnings}\n\n mkdirSync(typesDir, {recursive: true})\n writeFileSync(outPath, content, 'utf-8')\n return {warnings}\n}\n\nexport function deletePageTypes(pageRelPath: string, root: string): void {\n const typesDir = join(root, '.devix', 'pages', pageRelPath.replace(/\\.(tsx?|jsx?)$/, ''))\n const outPath = join(typesDir, '$types.d.ts')\n if (existsSync(outPath)) rmSync(outPath)\n}\n\nexport function scanAndWritePageTypes(appDir: string, root: string): WritePageTypesResult {\n const pagesDir = join(root, appDir, 'pages')\n const warnings: string[] = []\n let files: string[]\n try {\n files = walkPages(pagesDir, root)\n } catch {\n return {warnings}\n }\n for (const file of files) {\n try {\n const result = writePageTypes(file, root)\n warnings.push(...result.warnings)\n } catch {\n /* ignorar archivos no procesables */\n }\n }\n return {warnings}\n}"],
5
+ "mappings": "AAAA,OAAQ,cAAAA,EAAY,aAAAC,EAAW,eAAAC,EAAa,gBAAAC,EAAc,UAAAC,EAAQ,YAAAC,EAAU,iBAAAC,MAAoB,UAChG,OAAQ,QAAAC,EAAM,YAAAC,MAAe,YAC7B,OAAQ,aAAAC,MAAgB,aAExB,SAASC,EAAUC,EAAaC,EAAwB,CACpD,IAAMC,EAAoB,CAAC,EAC3B,QAAWC,KAAQZ,EAAYS,CAAG,EAAG,CACjC,IAAMI,EAAOR,EAAKI,EAAKG,CAAI,EACvBT,EAASU,CAAI,EAAE,YAAY,EAC3BF,EAAQ,KAAK,GAAGH,EAAUK,EAAMH,CAAI,CAAC,EAC9B,cAAc,KAAKE,CAAI,GAAKA,IAAS,cAAgBA,IAAS,aACrED,EAAQ,KAAKL,EAASI,EAAMG,CAAI,EAAE,QAAQ,MAAO,GAAG,CAAC,CAE7D,CACA,OAAOF,CACX,CAQO,SAASG,EAAoBC,EAAcC,EAAoC,CAClF,IAAMC,EAAMV,EAAUS,EAAUD,EAAM,CAAC,WAAY,QAAQ,CAAC,EAC5D,QAAWG,KAAQD,EAAI,QAAQ,KAAM,CACjC,GAAIC,EAAK,OAAS,yBAA0B,SAC5C,IAAMC,EAAOD,EAAK,YAClB,GAAIC,GAAM,OAAS,uBAAyBA,EAAK,IAAI,OAAS,SAC1D,MAAO,CAAC,OAAQ,GAAM,QAASA,EAAK,MAAO,WAAY,EAAK,EAEhE,GAAIA,GAAM,OAAS,uBACf,QAAWC,KAAKD,EAAK,aACjB,GAAIC,EAAE,GAAG,OAAS,cAAgBA,EAAE,GAAG,OAAS,SAAU,CACtD,IAAMC,EAAOD,EAAE,KAIf,MAAO,CAAC,OAAQ,GAAM,QAFjBC,GAAM,OAAS,2BAA6BA,EAAK,OACjDA,GAAM,OAAS,sBAAwBA,EAAK,MAClB,WAAY,EAAK,CACpD,EAGR,QAAWC,KAASJ,EAAK,YAAc,CAAC,EACpC,GAAII,EAAK,SAAS,OAAS,cAAgBA,EAAK,SAAS,OAAS,SAC9D,MAAO,CAAC,OAAQ,GAAM,QAAS,GAAO,WAAY,EAAI,CAGlE,CACA,MAAO,CAAC,OAAQ,GAAO,QAAS,GAAO,WAAY,EAAK,CAC5D,CAEO,SAASC,EAAgBR,EAAcC,EAA2B,CACrE,OAAOF,EAAoBC,EAAMC,CAAQ,EAAE,MAC/C,CAEO,SAASQ,EAAqBC,EAAoBC,EAA6B,CAClF,OAAKA,EAGE;AAAA,+BAAwED,CAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAF9E;AAAA;AAAA;AAAA,CAGf,CAMO,SAASE,EAAeC,EAAqBlB,EAAoC,CACpF,IAAMmB,EAAWxB,EAAKK,EAAMkB,CAAW,EACjCb,EAAOd,EAAa4B,EAAU,OAAO,EACrCC,EAAahB,EAAoBC,EAAMc,CAAQ,EAC/CE,EAAqB,CAAC,EAExBD,EAAW,QAAU,CAACA,EAAW,SAAW,CAACA,EAAW,YACxDC,EAAS,KACL,WAAWH,CAAW,6GAE1B,EAGJ,IAAMI,EAAW3B,EAAKK,EAAM,SAAU,QAASkB,EAAY,QAAQ,iBAAkB,EAAE,CAAC,EAClFK,EAAU5B,EAAK2B,EAAU,aAAa,EAEtCE,EAAeL,EAAS,QAAQ,iBAAkB,EAAE,EACpDJ,EAAanB,EAAS0B,EAAUE,CAAY,EAAE,QAAQ,MAAO,GAAG,EAEhEC,EAAUX,EAAqBC,EAAYK,EAAW,MAAM,EAElE,OAAIhC,EAAWmC,CAAO,GAAKhC,EAAagC,EAAS,OAAO,IAAME,EAAgB,CAAC,SAAAJ,CAAQ,GAEvFhC,EAAUiC,EAAU,CAAC,UAAW,EAAI,CAAC,EACrC5B,EAAc6B,EAASE,EAAS,OAAO,EAChC,CAAC,SAAAJ,CAAQ,EACpB,CAEO,SAASK,EAAgBR,EAAqBlB,EAAoB,CACrE,IAAMsB,EAAW3B,EAAKK,EAAM,SAAU,QAASkB,EAAY,QAAQ,iBAAkB,EAAE,CAAC,EAClFK,EAAU5B,EAAK2B,EAAU,aAAa,EACxClC,EAAWmC,CAAO,GAAG/B,EAAO+B,CAAO,CAC3C,CAEO,SAASI,EAAsBC,EAAgB5B,EAAoC,CACtF,IAAM6B,EAAWlC,EAAKK,EAAM4B,EAAQ,OAAO,EACrCP,EAAqB,CAAC,EACxBS,EACJ,GAAI,CACAA,EAAQhC,EAAU+B,EAAU7B,CAAI,CACpC,MAAQ,CACJ,MAAO,CAAC,SAAAqB,CAAQ,CACpB,CACA,QAAWU,KAAQD,EACf,GAAI,CACA,IAAME,EAASf,EAAec,EAAM/B,CAAI,EACxCqB,EAAS,KAAK,GAAGW,EAAO,QAAQ,CACpC,MAAQ,CAER,CAEJ,MAAO,CAAC,SAAAX,CAAQ,CACpB",
6
+ "names": ["existsSync", "mkdirSync", "readdirSync", "readFileSync", "rmSync", "statSync", "writeFileSync", "join", "relative", "parseSync", "walkPages", "dir", "root", "entries", "name", "full", "inspectLoaderExport", "code", "filePath", "ast", "node", "decl", "d", "init", "spec", "hasLoaderExport", "generatePageTypesDts", "importPath", "withLoader", "writePageTypes", "pageRelPath", "fullPath", "loaderInfo", "warnings", "typesDir", "outPath", "pageAbsNoExt", "content", "deletePageTypes", "scanAndWritePageTypes", "appDir", "pagesDir", "files", "file", "result"]
7
7
  }
@@ -1,12 +1,12 @@
1
- function i({routesPath:e,envPath:t,honoServerPath:o,honoServerStaticPath:r,honoPath:n}){return`
1
+ function n({routesPath:e,envPath:r,honoServerPath:o,honoServerStaticPath:t,honoPath:i}){return`
2
2
  import { readFileSync } from 'node:fs'
3
3
  import { serve } from '${o}'
4
- import { serveStatic } from '${r}'
5
- import { Hono } from '${n}'
4
+ import { serveStatic } from '${t}'
5
+ import { Hono } from '${i}'
6
6
  import { resolve, join, dirname } from 'node:path'
7
7
  import { pathToFileURL } from 'node:url'
8
8
  import { registerApiRoutes, registerSsrRoute } from '${e}'
9
- import { loadDotenv } from '${t}'
9
+ import { loadDotenv } from '${r}'
10
10
 
11
11
  loadDotenv('production')
12
12
 
@@ -59,9 +59,17 @@ import { readFileSync } from 'node:fs'
59
59
 
60
60
  if (runtimeConfig.output === 'static') {
61
61
  console.log('[devix] Static mode \u2014 serving pre-generated files from dist/client')
62
- } else {
63
- registerApiRoutes(app, { renderModule, apiModule, manifest })
64
- registerSsrRoute(app, { renderModule, apiModule, manifest, loaderTimeout: runtimeConfig.loaderTimeout })
62
+ } else {
63
+ let userServerConfig
64
+ try {
65
+ const userConfigMod = await import(pathToFileURL(resolve(process.cwd(), 'devix.config.ts')).href).catch(() =>
66
+ import(pathToFileURL(resolve(process.cwd(), 'devix.config.js')).href))
67
+ userServerConfig = userConfigMod?.default?.server
68
+ } catch {
69
+ /* config sin server \u2014 sigue normal */
70
+ }
71
+ registerApiRoutes(app, { renderModule, apiModule, manifest, server: userServerConfig })
72
+ registerSsrRoute(app, { renderModule, apiModule, manifest, loaderTimeout: runtimeConfig.loaderTimeout, server: userServerConfig })
65
73
  }
66
74
 
67
75
  const server = serve({ fetch: app.fetch, port, hostname: host }, (info) =>
@@ -69,5 +77,5 @@ import { readFileSync } from 'node:fs'
69
77
 
70
78
  process.on('SIGTERM', () => server.close())
71
79
  process.on('SIGINT', () => server.close())
72
- `}export{i as generateServerEntry};
80
+ `}export{n as generateServerEntry};
73
81
  //# sourceMappingURL=server-entry.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/vite/codegen/server-entry.ts"],
4
- "sourcesContent": ["interface ServerEntryOptions {\n routesPath: string\n envPath: string\n honoServerPath: string\n honoServerStaticPath: string\n honoPath: string\n}\n\nexport function generateServerEntry({ routesPath, envPath, honoServerPath, honoServerStaticPath, honoPath }: ServerEntryOptions): string {\n return `\nimport { readFileSync } from 'node:fs'\n import { serve } from '${honoServerPath}'\n import { serveStatic } from '${honoServerStaticPath}'\n import { Hono } from '${honoPath}'\n import { resolve, join, dirname } from 'node:path'\n import { pathToFileURL } from 'node:url'\n import { registerApiRoutes, registerSsrRoute } from '${routesPath}' \n import { loadDotenv } from '${envPath}'\n \n loadDotenv('production')\n \n const __dir = dirname(process.argv[1])\n\n let renderModule, apiModule, manifest, runtimeConfig \n \n try { \n runtimeConfig = JSON.parse(readFileSync(resolve(__dir, '../devix.config.json'), 'utf-8'))\n if (runtimeConfig.output !== 'static') { \n renderModule = await import(pathToFileURL(resolve(__dir, 'render.js')).href)\n apiModule = await import(pathToFileURL(resolve(__dir, 'api.js')).href) \n } \n manifest = JSON.parse(readFileSync(resolve(__dir, '../client/.vite/manifest.json'), 'utf-8')) \n } catch { \n console.error('[devix] Build not found. Run \"devix build\" first.')\n process.exit(1) \n } \n \n const port = Number(process.env.PORT) || runtimeConfig.port || 3000 \n const host = typeof runtimeConfig.host === 'string'\n ? runtimeConfig.host \n : runtimeConfig.host ? '0.0.0.0' : (process.env.HOST || '0.0.0.0') \n \n const clientRoot = resolve(__dir, '../client') \n const app = new Hono()\n \n if (runtimeConfig.output === 'static') {\n app.get('/_data/*', (c) => {\n const pathname = c.req.path.replace(/^\\\\/_data/, '') || '/' \n const filePath = pathname === '/' \n ? join(clientRoot, '_data/index.json') \n : join(clientRoot, '_data', pathname + '.json') \n try { \n return c.json(JSON.parse(readFileSync(filePath, 'utf-8')))\n } catch { \n return c.json({ error: 'not found' }, 404)\n } \n }) \n }\n\n app.use('/*', serveStatic({ \n root: clientRoot,\n onFound: (_path, c) => { \n c.header('Cache-Control', _path.includes('/assets/') \n ? 'public, immutable, max-age=31536000'\n : 'no-cache') \n } \n })) \n \n if (runtimeConfig.output === 'static') {\n console.log('[devix] Static mode \u2014 serving pre-generated files from dist/client')\n } else { \n registerApiRoutes(app, { renderModule, apiModule, manifest })\n registerSsrRoute(app, { renderModule, apiModule, manifest, loaderTimeout: runtimeConfig.loaderTimeout })\n } \n \n const server = serve({ fetch: app.fetch, port, hostname: host }, (info) => \n console.log(\\`http://\\${info.address}:\\${info.port}\\`))\n\nprocess.on('SIGTERM', () => server.close())\nprocess.on('SIGINT', () => server.close())\n`\n}"],
5
- "mappings": "AAQO,SAASA,EAAoB,CAAE,WAAAC,EAAY,QAAAC,EAAS,eAAAC,EAAgB,qBAAAC,EAAsB,SAAAC,CAAS,EAA+B,CACrI,MAAO;AAAA;AAAA,2BAEgBF,CAAc;AAAA,iCACRC,CAAoB;AAAA,0BAC3BC,CAAQ;AAAA;AAAA;AAAA,yDAGuBJ,CAAU;AAAA,gCACnCC,CAAO;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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAgEvC",
4
+ "sourcesContent": ["interface ServerEntryOptions {\n routesPath: string\n envPath: string\n honoServerPath: string\n honoServerStaticPath: string\n honoPath: string\n}\n\nexport function generateServerEntry({ routesPath, envPath, honoServerPath, honoServerStaticPath, honoPath }: ServerEntryOptions): string {\n return `\nimport { readFileSync } from 'node:fs'\n import { serve } from '${honoServerPath}'\n import { serveStatic } from '${honoServerStaticPath}'\n import { Hono } from '${honoPath}'\n import { resolve, join, dirname } from 'node:path'\n import { pathToFileURL } from 'node:url'\n import { registerApiRoutes, registerSsrRoute } from '${routesPath}' \n import { loadDotenv } from '${envPath}'\n \n loadDotenv('production')\n \n const __dir = dirname(process.argv[1])\n\n let renderModule, apiModule, manifest, runtimeConfig \n \n try { \n runtimeConfig = JSON.parse(readFileSync(resolve(__dir, '../devix.config.json'), 'utf-8'))\n if (runtimeConfig.output !== 'static') { \n renderModule = await import(pathToFileURL(resolve(__dir, 'render.js')).href)\n apiModule = await import(pathToFileURL(resolve(__dir, 'api.js')).href) \n } \n manifest = JSON.parse(readFileSync(resolve(__dir, '../client/.vite/manifest.json'), 'utf-8')) \n } catch { \n console.error('[devix] Build not found. Run \"devix build\" first.')\n process.exit(1) \n } \n \n const port = Number(process.env.PORT) || runtimeConfig.port || 3000 \n const host = typeof runtimeConfig.host === 'string'\n ? runtimeConfig.host \n : runtimeConfig.host ? '0.0.0.0' : (process.env.HOST || '0.0.0.0') \n \n const clientRoot = resolve(__dir, '../client') \n const app = new Hono()\n \n if (runtimeConfig.output === 'static') {\n app.get('/_data/*', (c) => {\n const pathname = c.req.path.replace(/^\\\\/_data/, '') || '/' \n const filePath = pathname === '/' \n ? join(clientRoot, '_data/index.json') \n : join(clientRoot, '_data', pathname + '.json') \n try { \n return c.json(JSON.parse(readFileSync(filePath, 'utf-8')))\n } catch { \n return c.json({ error: 'not found' }, 404)\n } \n }) \n }\n\n app.use('/*', serveStatic({ \n root: clientRoot,\n onFound: (_path, c) => { \n c.header('Cache-Control', _path.includes('/assets/') \n ? 'public, immutable, max-age=31536000'\n : 'no-cache') \n } \n })) \n \n if (runtimeConfig.output === 'static') {\n console.log('[devix] Static mode \u2014 serving pre-generated files from dist/client')\n } else {\n let userServerConfig\n try {\n const userConfigMod = await import(pathToFileURL(resolve(process.cwd(), 'devix.config.ts')).href).catch(() =>\n import(pathToFileURL(resolve(process.cwd(), 'devix.config.js')).href))\n userServerConfig = userConfigMod?.default?.server\n } catch {\n /* config sin server \u2014 sigue normal */\n }\n registerApiRoutes(app, { renderModule, apiModule, manifest, server: userServerConfig })\n registerSsrRoute(app, { renderModule, apiModule, manifest, loaderTimeout: runtimeConfig.loaderTimeout, server: userServerConfig })\n } \n \n const server = serve({ fetch: app.fetch, port, hostname: host }, (info) => \n console.log(\\`http://\\${info.address}:\\${info.port}\\`))\n\nprocess.on('SIGTERM', () => server.close())\nprocess.on('SIGINT', () => server.close())\n`\n}"],
5
+ "mappings": "AAQO,SAASA,EAAoB,CAAE,WAAAC,EAAY,QAAAC,EAAS,eAAAC,EAAgB,qBAAAC,EAAsB,SAAAC,CAAS,EAA+B,CACrI,MAAO;AAAA;AAAA,2BAEgBF,CAAc;AAAA,iCACRC,CAAoB;AAAA,0BAC3BC,CAAQ;AAAA;AAAA;AAAA,yDAGuBJ,CAAU;AAAA,gCACnCC,CAAO;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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAwEvC",
6
6
  "names": ["generateServerEntry", "routesPath", "envPath", "honoServerPath", "honoServerStaticPath", "honoPath"]
7
7
  }
@@ -1,4 +1,4 @@
1
- import{mergeConfig as Te}from"vite";import Se from"@vitejs/plugin-react";import{fileURLToPath as we}from"node:url";import{dirname as $e,relative as De,resolve as u}from"node:path";import{createRequire as Ae}from"node:module";function L({cssUrls:e}){return`
1
+ import{mergeConfig as we}from"vite";import Te from"@vitejs/plugin-react";import{fileURLToPath as Se}from"node:url";import{dirname as $e,relative as Ae,resolve as u}from"node:path";import{createRequire as De}from"node:module";function U({cssUrls:e}){return`
2
2
  ${e.map(r=>`import '${r}'`).join(`
3
3
  `)}
4
4
  import "@vitejs/plugin-react/preamble"
@@ -16,11 +16,12 @@ if (!window.__DEVIX__) {
16
16
  const {metadata, viewport, clientEntry} = window.__DEVIX__
17
17
  const loaderData = window.__LOADER_DATA__
18
18
  const layoutsData = window.__LAYOUTS_DATA__ ?? []
19
+ const guardData = window.__GUARD_DATA__ ?? null
19
20
 
20
21
  const matched = matchClientRoute(window.location.pathname)
21
22
 
22
23
  if (window.__LOADER_ERROR__) {
23
- const {statusCode, message, data} = window.__LOADER_ERROR__
24
+ const {statusCode, message, code, data} = window.__LOADER_ERROR__
24
25
  const ErrorPage = await loadErrorPage() ?? getDefaultErrorPage()
25
26
  createRoot(root).render(
26
27
  React.createElement(RouterProvider, {
@@ -28,7 +29,7 @@ if (!window.__DEVIX__) {
28
29
  initialData: null,
29
30
  initialParams: {},
30
31
  initialPage: () => null,
31
- initialError: {statusCode, message, data},
32
+ initialError: {statusCode, message, code, data},
32
33
  initialErrorPage: ErrorPage,
33
34
  })
34
35
  )
@@ -46,6 +47,7 @@ if (!window.__DEVIX__) {
46
47
  initialPage: pageMod.default,
47
48
  initialLayouts: layoutMods.map(m => m.default),
48
49
  initialLayoutsData: layoutsData,
50
+ initialGuardData: guardData,
49
51
  initialMeta: metadata,
50
52
  initialViewport: viewport,
51
53
  })
@@ -103,7 +105,7 @@ export function getDefaultErrorPage() {
103
105
  )
104
106
  }
105
107
  }
106
- `}function U({pagesDir:e,renderPath:t}){return`
108
+ `}function H({pagesDir:e,renderPath:t}){return`
107
109
  import { render as _render, runLoader as _runLoader, getStaticRoutes as _getStaticRoutes } from '${t}'
108
110
 
109
111
  const _pages = import.meta.glob(['/${e}/**/*.tsx', '!**/error.tsx', '!**/layout.tsx'])
@@ -126,7 +128,7 @@ export function runLoader(url, request, options) {
126
128
  export function getStaticRoutes() {
127
129
  return _getStaticRoutes(_glob)
128
130
  }
129
- `}function H({apiPath:e,appDir:t}){return`
131
+ `}function N({apiPath:e,appDir:t}){return`
130
132
  import { handleApiRequest as _handleApiRequest } from '${e}'
131
133
 
132
134
  const _routes = import.meta.glob(['/${t}/api/**/*.ts', '!**/middleware.ts'])
@@ -141,15 +143,15 @@ const _glob = {
141
143
  export function handleApiRequest(url, request) {
142
144
  return _handleApiRequest(url, request, _glob)
143
145
  }
144
- `}function N(){return`
146
+ `}function W(){return`
145
147
  export {RouterContext} from '@devlusoft/devix/runtime/context'
146
- `}import{readFileSync as ce,readdirSync as le,statSync as ue}from"node:fs";import{join as D,relative as pe}from"node:path";var ie=/export\s+(?:const|async\s+function|function)\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b/g;function se(e){return e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/\/\/.*$/gm,"")}function k(e){let t=new Set;for(let r of se(e).matchAll(ie))t.add(r[1]);return[...t]}function V(e){return e.replace(/\.(tsx|ts|jsx|js)$/,"").replace(/\(.*?\)\//g,"").replace(/^index$|\/index$/,"").replace(/\[([^\]]+)]/g,":$1")||"/"}function q(e,t){let r=e.slice(t.length+1).replace(/\\/g,"/"),n=V(r);return n==="/"?"/api":`/api/${n}`.replace("/api//","/api/")}function ae(e,t){return"_api_"+e.slice(`${t}/`.length).replace(/\.(ts|tsx)$/,"").replace(/[^a-zA-Z0-9]/g,"_")}function W(e,t,r){return{filePath:e,urlPattern:q(e,t),identifier:ae(e,t),methods:r}}function $(e,t){if(e.length===0)return`// auto-generado por devix \u2014 no editar
148
+ `}import{readFileSync as ce,readdirSync as le,statSync as pe}from"node:fs";import{join as A,relative as ue}from"node:path";var ie=/export\s+(?:const|async\s+function|function)\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b/g;function se(e){return e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/\/\/.*$/gm,"")}function k(e){let t=new Set;for(let r of se(e).matchAll(ie))t.add(r[1]);return[...t]}function V(e){return e.replace(/\.(tsx|ts|jsx|js)$/,"").replace(/\(.*?\)\//g,"").replace(/^index$|\/index$/,"").replace(/\[([^\]]+)]/g,":$1")||"/"}function q(e,t){let r=e.slice(t.length+1).replace(/\\/g,"/"),i=V(r);return i==="/"?"/api":`/api/${i}`.replace("/api//","/api/")}function ae(e,t){return"_api_"+e.slice(`${t}/`.length).replace(/\.(ts|tsx)$/,"").replace(/[^a-zA-Z0-9]/g,"_")}function B(e,t,r){return{filePath:e,urlPattern:q(e,t),identifier:ae(e,t),methods:r}}function $(e,t){if(e.length===0)return`// auto-generado por devix \u2014 no editar
147
149
  export {}
148
150
  declare module '@devlusoft/devix' {
149
151
  interface ApiRoutes {}
150
152
  }
151
153
  `;let r=e.map(o=>{let a="../"+o.filePath.replace(/\.(ts|tsx)$/,"");return`import type * as ${o.identifier} from '${a}'`}).join(`
152
- `),n=e.flatMap(o=>o.methods.map(a=>` '${a} ${o.urlPattern}': InferRoute<(typeof ${o.identifier})['${a}']>`)).join(`
154
+ `),i=e.flatMap(o=>o.methods.map(a=>` '${a} ${o.urlPattern}': InferRoute<(typeof ${o.identifier})['${a}']>`)).join(`
153
155
  `);return`// auto-generado por devix \u2014 no editar
154
156
  ${r}
155
157
 
@@ -170,13 +172,13 @@ type InferRoute<T> =
170
172
 
171
173
  declare module '@devlusoft/devix' {
172
174
  interface ApiRoutes {
173
- ${n}
175
+ ${i}
174
176
  }
175
177
  }
176
- `}function B(e,t){let r=[];for(let n of le(e)){let o=D(e,n);ue(o).isDirectory()?r.push(...B(o,t)):/\.(ts|tsx)$/.test(n)&&r.push(pe(t,o).replace(/\\/g,"/"))}return r}function A(e,t){let r=D(t,e,"api"),n;try{n=B(r,t)}catch{return[]}return n.filter(o=>!o.endsWith("middleware.ts")&&!o.endsWith("middleware.tsx")).flatMap(o=>{try{let a=ce(D(t,o),"utf-8"),m=k(a);return m.length===0?[]:[W(o,`${e}/api`,m)]}catch{return[]}})}import{mkdirSync as de,readFileSync as me,writeFileSync as fe,existsSync as ge}from"node:fs";import{join as J}from"node:path";function b(e,t){let r=J(t,".devix"),n=J(r,"routes.d.ts");return de(r,{recursive:!0}),ge(n)&&me(n,"utf-8")===e?!1:(fe(n,e,"utf-8"),!0)}import{parseSync as be}from"oxc-parser";function G({routesPath:e,envPath:t,honoServerPath:r,honoServerStaticPath:n,honoPath:o}){return`
178
+ `}function J(e,t){let r=[];for(let i of le(e)){let o=A(e,i);pe(o).isDirectory()?r.push(...J(o,t)):/\.(ts|tsx)$/.test(i)&&r.push(ue(t,o).replace(/\\/g,"/"))}return r}function D(e,t){let r=A(t,e,"api"),i;try{i=J(r,t)}catch{return[]}return i.filter(o=>!o.endsWith("middleware.ts")&&!o.endsWith("middleware.tsx")).flatMap(o=>{try{let a=ce(A(t,o),"utf-8"),p=k(a);return p.length===0?[]:[B(o,`${e}/api`,p)]}catch{return[]}})}import{mkdirSync as de,readFileSync as fe,writeFileSync as ge,existsSync as me}from"node:fs";import{join as G}from"node:path";function b(e,t){let r=G(t,".devix"),i=G(r,"routes.d.ts");return de(r,{recursive:!0}),me(i)&&fe(i,"utf-8")===e?!1:(ge(i,e,"utf-8"),!0)}import{parseSync as be}from"oxc-parser";function X({routesPath:e,envPath:t,honoServerPath:r,honoServerStaticPath:i,honoPath:o}){return`
177
179
  import { readFileSync } from 'node:fs'
178
180
  import { serve } from '${r}'
179
- import { serveStatic } from '${n}'
181
+ import { serveStatic } from '${i}'
180
182
  import { Hono } from '${o}'
181
183
  import { resolve, join, dirname } from 'node:path'
182
184
  import { pathToFileURL } from 'node:url'
@@ -234,9 +236,17 @@ import { readFileSync } from 'node:fs'
234
236
 
235
237
  if (runtimeConfig.output === 'static') {
236
238
  console.log('[devix] Static mode \u2014 serving pre-generated files from dist/client')
237
- } else {
238
- registerApiRoutes(app, { renderModule, apiModule, manifest })
239
- registerSsrRoute(app, { renderModule, apiModule, manifest, loaderTimeout: runtimeConfig.loaderTimeout })
239
+ } else {
240
+ let userServerConfig
241
+ try {
242
+ const userConfigMod = await import(pathToFileURL(resolve(process.cwd(), 'devix.config.ts')).href).catch(() =>
243
+ import(pathToFileURL(resolve(process.cwd(), 'devix.config.js')).href))
244
+ userServerConfig = userConfigMod?.default?.server
245
+ } catch {
246
+ /* config sin server \u2014 sigue normal */
247
+ }
248
+ registerApiRoutes(app, { renderModule, apiModule, manifest, server: userServerConfig })
249
+ registerSsrRoute(app, { renderModule, apiModule, manifest, loaderTimeout: runtimeConfig.loaderTimeout, server: userServerConfig })
240
250
  }
241
251
 
242
252
  const server = serve({ fetch: app.fetch, port, hostname: host }, (info) =>
@@ -244,7 +254,7 @@ import { readFileSync } from 'node:fs'
244
254
 
245
255
  process.on('SIGTERM', () => server.close())
246
256
  process.on('SIGINT', () => server.close())
247
- `}import{existsSync as Y,mkdirSync as he,readdirSync as xe,readFileSync as X,rmSync as ye,statSync as Re,writeFileSync as ve}from"node:fs";import{join as f,relative as z}from"node:path";import{parseSync as _e}from"oxc-parser";function Z(e,t){let r=[];for(let n of xe(e)){let o=f(e,n);Re(o).isDirectory()?r.push(...Z(o,t)):/\.(ts|tsx)$/.test(n)&&n!=="layout.tsx"&&n!=="error.tsx"&&r.push(z(t,o).replace(/\\/g,"/"))}return r}function Ee(e,t){let r=_e(t,e,{sourceType:"module"});for(let n of r.program.body){if(n.type!=="ExportNamedDeclaration")continue;let o=n.declaration;if(o?.type==="FunctionDeclaration"&&o.id?.name==="loader")return!0;if(o?.type==="VariableDeclaration"){for(let a of o.declarations)if(a.id.type==="Identifier"&&a.id.name==="loader")return!0}for(let a of n.specifiers??[])if(a.exported.type==="Identifier"&&a.exported.name==="loader")return!0}return!1}function Pe(e,t){return t?`// auto-generado por devix \u2014 no editar
257
+ `}import{existsSync as z,mkdirSync as xe,readdirSync as ye,readFileSync as Y,rmSync as he,statSync as Re,writeFileSync as ve}from"node:fs";import{join as x,relative as Z}from"node:path";import{parseSync as Ee}from"oxc-parser";function K(e,t){let r=[];for(let i of ye(e)){let o=x(e,i);Re(o).isDirectory()?r.push(...K(o,t)):/\.(ts|tsx)$/.test(i)&&i!=="layout.tsx"&&i!=="error.tsx"&&r.push(Z(t,o).replace(/\\/g,"/"))}return r}function _e(e,t){let r=Ee(t,e,{sourceType:"module"});for(let i of r.program.body){if(i.type!=="ExportNamedDeclaration")continue;let o=i.declaration;if(o?.type==="FunctionDeclaration"&&o.id?.name==="loader")return{exists:!0,isAsync:o.async,isReExport:!1};if(o?.type==="VariableDeclaration"){for(let a of o.declarations)if(a.id.type==="Identifier"&&a.id.name==="loader"){let p=a.init;return{exists:!0,isAsync:p?.type==="ArrowFunctionExpression"&&p.async||p?.type==="FunctionExpression"&&p.async,isReExport:!1}}}for(let a of i.specifiers??[])if(a.exported.type==="Identifier"&&a.exported.name==="loader")return{exists:!0,isAsync:!1,isReExport:!0}}return{exists:!1,isAsync:!1,isReExport:!1}}function Pe(e,t){return t?`// auto-generado por devix \u2014 no editar
248
258
  import type { loader } from "${e}"
249
259
  import type { Redirect } from "@devlusoft/devix"
250
260
 
@@ -256,5 +266,5 @@ export type PageParams = NonNullable<Parameters<typeof loader>[0]>["params"]
256
266
  `:`// auto-generado por devix - no editar
257
267
  export type PageData = undefined
258
268
  export type PageParams = Record<string, string>
259
- `}function P(e,t){let r=f(t,e),n=X(r,"utf-8"),o=Ee(n,r),a=f(t,".devix","pages",e.replace(/\.(tsx?|jsx?)$/,"")),m=f(a,"$types.d.ts"),T=r.replace(/\.(tsx?|jsx?)$/,""),S=z(a,T).replace(/\\/g,"/"),g=Pe(S,o);Y(m)&&X(m,"utf-8")===g||(he(a,{recursive:!0}),ve(m,g,"utf-8"))}function K(e,t){let r=f(t,".devix","pages",e.replace(/\.(tsx?|jsx?)$/,"")),n=f(r,"$types.d.ts");Y(n)&&ye(n)}function C(e,t){let r=f(t,e,"pages"),n;try{n=Z(r,t)}catch{return}for(let o of n)try{P(o,t)}catch{}}var R=$e(we(import.meta.url)),I="virtual:devix/entry-client",M="virtual:devix/client-routes",v="virtual:devix/render",_="virtual:devix/api",O="virtual:devix/context",F="virtual:devix/server-entry",Q=new Set(["loader","guard","generateStaticParams","headers"]);function Rt(e){let t=e.appDir??"app",r=`${t}/pages`,n=(e.css??[]).map(i=>i.startsWith("/")?i:`/${i.replace(/^\.\//,"")}`),o=u(R,"../server/render.js").replace(/\\/g,"/"),a=u(R,"../server/api.js").replace(/\\/g,"/"),m=u(R,"../runtime/client-router.js").replace(/\\/g,"/"),T=u(R,"../server/routes.js").replace(/\\/g,"/"),S=u(R,"../utils/env.js").replace(/\\/g,"/"),g=Ae(import.meta.url),ee=g.resolve("@hono/node-server").replace(/\\/g,"/"),te=g.resolve("@hono/node-server/serve-static").replace(/\\/g,"/"),re=g.resolve("hono").replace(/\\/g,"/"),ne={name:"devix",enforce:"pre",resolveId(i){if(i===I)return`\0${I}`;if(i===M)return`\0${M}`;if(i===v)return`\0${v}`;if(i===_)return`\0${_}`;if(i===O)return`\0${O}`;if(i===F)return`\0${F}`},load(i){if(i===`\0${I}`)return L({cssUrls:n});if(i===`\0${M}`)return j({pagesDir:r,matcherPath:m});if(i===`\0${v}`)return U({pagesDir:r,renderPath:o});if(i===`\0${_}`)return H({apiPath:a,appDir:t});if(i===`\0${O}`)return N();if(i===`\0${F}`)return G({routesPath:T,envPath:S,honoServerPath:ee,honoServerStaticPath:te,honoPath:re})},transform(i,c,h){if(h?.ssr)return;let x=u(process.cwd(),r);if(!c.startsWith(x))return;let y=be(c,i,{sourceType:"module"}),p=[];for(let l of y.program.body){if(l.type!=="ExportNamedDeclaration"||!l.declaration)continue;let d=l.declaration;if(d.type==="FunctionDeclaration"&&d.id&&Q.has(d.id.name)&&p.push({start:l.start,end:l.end,name:d.id.name}),d.type==="VariableDeclaration"){let E=new Set;for(let w of d.declarations)w.id.type==="Identifier"&&Q.has(w.id.name)&&(E.has(l.start)||(E.add(l.start),p.push({start:l.start,end:l.end,name:w.id.name})))}}if(p.length===0)return;p.sort((l,d)=>d.start-l.start);let s=i;for(let{start:l,end:d,name:E}of p)s=s.slice(0,l)+`export const ${E} = undefined`+s.slice(d);return{code:s,map:null}},buildStart(){let i=process.cwd(),c=A(t,i);b($(c,`${t}/api`),i),C(t,i)},configureServer(i){let c=process.cwd();C(t,c);let h=()=>{let s=A(t,c);b($(s,`${t}/api`),c)},x=s=>s.startsWith(u(c,r))&&!s.endsWith("layout.tsx")&&!s.endsWith("error.tsx"),y=s=>De(c,s).replace(/\\/g,"/"),p=s=>{let l=i.moduleGraph.getModuleById(`\0${s}`);l&&i.moduleGraph.invalidateModule(l)};i.watcher.add(u(c,"devix.config.ts")),i.watcher.on("change",s=>{s===u(c,"devix.config.ts")&&(console.log("[devix] Config changed, restarting..."),process.exit(75))}),i.watcher.on("add",s=>{s.startsWith(u(c,r))&&p(v),x(s)&&P(y(s),c),s.includes(`${t}/api`)&&(p(_),h())}),i.watcher.on("unlink",s=>{s.startsWith(u(c,r))&&p(v),x(s)&&K(y(s),c),s.includes(`${t}/api`)&&(p(_),h())}),i.watcher.on("change",s=>{x(s)&&P(y(s),c),s.includes(`${t}/api`)&&!s.endsWith("middleware.ts")&&h()})}},oe={plugins:[Se(),ne],publicDir:u(process.cwd(),e.publicDir??"public"),ssr:{noExternal:["@devlusoft/devix"]},...e.envPrefix?{envPrefix:e.envPrefix}:{}};return Te(oe,e.vite??{})}export{Rt as devix};
269
+ `}function C(e,t){let r=x(t,e),i=Y(r,"utf-8"),o=_e(i,r),a=[];o.exists&&!o.isAsync&&!o.isReExport&&a.push(`[devix] ${e}: 'loader' must be async. Use 'export async function loader' or 'export const loader = async (...) => ...'.`);let p=x(t,".devix","pages",e.replace(/\.(tsx?|jsx?)$/,"")),y=x(p,"$types.d.ts"),S=r.replace(/\.(tsx?|jsx?)$/,""),h=Z(p,S).replace(/\\/g,"/"),T=Pe(h,o.exists);return z(y)&&Y(y,"utf-8")===T?{warnings:a}:(xe(p,{recursive:!0}),ve(y,T,"utf-8"),{warnings:a})}function Q(e,t){let r=x(t,".devix","pages",e.replace(/\.(tsx?|jsx?)$/,"")),i=x(r,"$types.d.ts");z(i)&&he(i)}function I(e,t){let r=x(t,e,"pages"),i=[],o;try{o=K(r,t)}catch{return{warnings:i}}for(let a of o)try{let p=C(a,t);i.push(...p.warnings)}catch{}return{warnings:i}}var _=$e(Se(import.meta.url)),M="virtual:devix/entry-client",F="virtual:devix/client-routes",P="virtual:devix/render",w="virtual:devix/api",O="virtual:devix/context",L="virtual:devix/server-entry",ee=new Set(["loader","guard","generateStaticParams","headers"]);function Rt(e){let t=e.appDir??"app",r=`${t}/pages`,i=(e.css??[]).map(s=>s.startsWith("/")?s:`/${s.replace(/^\.\//,"")}`),o=u(_,"../server/render.js").replace(/\\/g,"/"),a=u(_,"../server/api.js").replace(/\\/g,"/"),p=u(_,"../runtime/client-router.js").replace(/\\/g,"/"),y=u(_,"../server/routes.js").replace(/\\/g,"/"),S=u(_,"../utils/env.js").replace(/\\/g,"/"),h=De(import.meta.url),T=h.resolve("@hono/node-server").replace(/\\/g,"/"),te=h.resolve("@hono/node-server/serve-static").replace(/\\/g,"/"),re=h.resolve("hono").replace(/\\/g,"/"),ne={name:"devix",enforce:"pre",resolveId(s){if(s===M)return`\0${M}`;if(s===F)return`\0${F}`;if(s===P)return`\0${P}`;if(s===w)return`\0${w}`;if(s===O)return`\0${O}`;if(s===L)return`\0${L}`},load(s){if(s===`\0${M}`)return U({cssUrls:i});if(s===`\0${F}`)return j({pagesDir:r,matcherPath:p});if(s===`\0${P}`)return H({pagesDir:r,renderPath:o});if(s===`\0${w}`)return N({apiPath:a,appDir:t});if(s===`\0${O}`)return W();if(s===`\0${L}`)return X({routesPath:y,envPath:S,honoServerPath:T,honoServerStaticPath:te,honoPath:re})},transform(s,c,R){if(R?.ssr)return;let m=u(process.cwd(),r);if(!c.startsWith(m))return;let v=be(c,s,{sourceType:"module"}),g=[];for(let l of v.program.body){if(l.type!=="ExportNamedDeclaration"||!l.declaration)continue;let n=l.declaration;if(n.type==="FunctionDeclaration"&&n.id&&ee.has(n.id.name)&&g.push({start:l.start,end:l.end,name:n.id.name}),n.type==="VariableDeclaration"){let f=new Set;for(let E of n.declarations)E.id.type==="Identifier"&&ee.has(E.id.name)&&(f.has(l.start)||(f.add(l.start),g.push({start:l.start,end:l.end,name:E.id.name})))}}if(g.length===0)return;g.sort((l,n)=>n.start-l.start);let d=s;for(let{start:l,end:n,name:f}of g)d=d.slice(0,l)+`export const ${f} = undefined`+d.slice(n);return{code:d,map:null}},buildStart(){let s=process.cwd(),c=D(t,s);b($(c,`${t}/api`),s);let{warnings:R}=I(t,s);for(let m of R)console.warn(m)},configureServer(s){let c=process.cwd(),R=I(t,c);for(let n of R.warnings)console.warn(n);let m=()=>{let n=D(t,c);b($(n,`${t}/api`),c)},v=n=>n.startsWith(u(c,r))&&!n.endsWith("layout.tsx")&&!n.endsWith("error.tsx"),g=n=>Ae(c,n).replace(/\\/g,"/"),d=n=>{let f=s.moduleGraph.getModuleById(`\0${n}`);f&&s.moduleGraph.invalidateModule(f)};s.watcher.add(u(c,"devix.config.ts")),s.watcher.on("change",n=>{n===u(c,"devix.config.ts")&&(console.log("[devix] Config changed, restarting..."),process.exit(75))});let l=n=>{try{let{warnings:f}=C(g(n),c);for(let E of f)console.warn(E)}catch{}};s.watcher.on("add",n=>{n.startsWith(u(c,r))&&d(P),v(n)&&l(n),n.includes(`${t}/api`)&&(d(w),m())}),s.watcher.on("unlink",n=>{n.startsWith(u(c,r))&&d(P),v(n)&&Q(g(n),c),n.includes(`${t}/api`)&&(d(w),m())}),s.watcher.on("change",n=>{v(n)&&l(n),n.includes(`${t}/api`)&&!n.endsWith("middleware.ts")&&m()})}},oe={plugins:[Te(),ne],publicDir:u(process.cwd(),e.publicDir??"public"),ssr:{noExternal:["@devlusoft/devix"]},...e.envPrefix?{envPrefix:e.envPrefix}:{}};return we(oe,e.vite??{})}export{Rt as devix};
260
270
  //# sourceMappingURL=index.js.map