@ressjs/vite-router 0.5.2 → 0.6.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/LICENSE +69 -0
  2. package/README.md +9 -515
  3. package/dist/client/entry.d.mts +2 -0
  4. package/dist/client/entry.mjs +1 -0
  5. package/dist/client/index.d.ts +2 -0
  6. package/dist/client/index.js +8 -0
  7. package/dist/config/base-path.d.ts +44 -0
  8. package/dist/config/base-path.js +100 -0
  9. package/dist/config/codegen.d.ts +29 -0
  10. package/dist/config/codegen.js +131 -0
  11. package/dist/config/define.d.ts +8 -0
  12. package/dist/config/define.js +12 -0
  13. package/dist/config/env.d.ts +19 -0
  14. package/dist/config/env.js +40 -0
  15. package/dist/config/index.d.ts +18 -0
  16. package/dist/config/index.js +46 -0
  17. package/dist/config/load.d.ts +28 -0
  18. package/dist/config/load.js +82 -0
  19. package/dist/config/middleware.d.ts +18 -0
  20. package/dist/config/middleware.js +79 -0
  21. package/dist/config/rules.d.ts +59 -0
  22. package/dist/config/rules.js +162 -0
  23. package/dist/config/types.d.ts +189 -0
  24. package/dist/config/types.js +2 -0
  25. package/dist/config/validate.d.ts +19 -0
  26. package/dist/config/validate.js +143 -0
  27. package/dist/config/watch.d.ts +53 -0
  28. package/dist/config/watch.js +163 -0
  29. package/dist/fs/module-extensions.d.ts +105 -0
  30. package/dist/fs/module-extensions.js +213 -0
  31. package/dist/head/resolve.d.ts +5 -0
  32. package/dist/head/resolve.js +166 -0
  33. package/dist/head/types.d.ts +44 -0
  34. package/dist/head/types.js +2 -0
  35. package/dist/helpers/html-generator.d.ts +26 -6
  36. package/dist/helpers/html-generator.js +96 -138
  37. package/dist/helpers/middlewares.d.ts +32 -21
  38. package/dist/helpers/middlewares.js +181 -159
  39. package/dist/helpers/page-config-merge.d.ts +11 -0
  40. package/dist/helpers/page-config-merge.js +84 -0
  41. package/dist/helpers/request-handler.d.ts +15 -2
  42. package/dist/helpers/request-handler.js +164 -27
  43. package/dist/index.d.ts +35 -3
  44. package/dist/index.js +77 -8
  45. package/dist/isr/capture.d.ts +79 -0
  46. package/dist/isr/capture.js +222 -0
  47. package/dist/isr/handler.d.ts +49 -0
  48. package/dist/isr/handler.js +207 -0
  49. package/dist/isr/key.d.ts +26 -0
  50. package/dist/isr/key.js +59 -0
  51. package/dist/isr/page-config.d.ts +17 -0
  52. package/dist/isr/page-config.js +71 -0
  53. package/dist/isr/preview.d.ts +5 -0
  54. package/dist/isr/preview.js +38 -0
  55. package/dist/isr/public-request.d.ts +50 -0
  56. package/dist/isr/public-request.js +108 -0
  57. package/dist/isr/response.d.ts +41 -0
  58. package/dist/isr/response.js +128 -0
  59. package/dist/isr/store.d.ts +42 -0
  60. package/dist/isr/store.js +108 -0
  61. package/dist/isr/types.d.ts +62 -0
  62. package/dist/isr/types.js +2 -0
  63. package/dist/pages.d.ts +10 -9
  64. package/dist/pages.js +42 -261
  65. package/dist/platform.d.ts +11 -14
  66. package/dist/platform.js +18 -102
  67. package/dist/plugin/environments.d.ts +68 -0
  68. package/dist/plugin/environments.js +74 -0
  69. package/dist/plugin/index.d.ts +59 -0
  70. package/dist/plugin/index.js +195 -0
  71. package/dist/plugin/route-manifest.d.ts +19 -0
  72. package/dist/plugin/route-manifest.js +55 -0
  73. package/dist/plugin/virtual-entries.d.ts +26 -0
  74. package/dist/plugin/virtual-entries.js +80 -0
  75. package/dist/render.d.ts +39 -2
  76. package/dist/render.js +75 -77
  77. package/dist/router.d.ts +32 -13
  78. package/dist/router.js +180 -146
  79. package/dist/routes/dispatcher.d.ts +58 -0
  80. package/dist/routes/dispatcher.js +70 -0
  81. package/dist/routes/manifest.d.ts +17 -0
  82. package/dist/routes/manifest.js +62 -0
  83. package/dist/routes/match.d.ts +20 -0
  84. package/dist/routes/match.js +75 -0
  85. package/dist/routes/module.d.ts +14 -0
  86. package/dist/routes/module.js +30 -0
  87. package/dist/routes/parse.d.ts +31 -0
  88. package/dist/routes/parse.js +114 -0
  89. package/dist/routes/rank.d.ts +12 -0
  90. package/dist/routes/rank.js +49 -0
  91. package/dist/routes/router.d.ts +54 -0
  92. package/dist/routes/router.js +153 -0
  93. package/dist/routes/scan.d.ts +26 -0
  94. package/dist/routes/scan.js +98 -0
  95. package/dist/routes/types.d.ts +114 -0
  96. package/dist/routes/types.js +17 -0
  97. package/dist/runtime/dev-server.d.ts +64 -0
  98. package/dist/runtime/dev-server.js +94 -0
  99. package/dist/runtime/dev-styles.d.ts +13 -0
  100. package/dist/runtime/dev-styles.js +50 -0
  101. package/dist/runtime/module-loader.d.ts +55 -0
  102. package/dist/runtime/module-loader.js +122 -0
  103. package/dist/runtime/prod-server.d.ts +55 -0
  104. package/dist/runtime/prod-server.js +188 -0
  105. package/dist/runtime/template.d.ts +10 -0
  106. package/dist/runtime/template.js +33 -0
  107. package/dist/security/client-props.d.ts +25 -0
  108. package/dist/security/client-props.js +51 -0
  109. package/dist/security/config.d.ts +121 -0
  110. package/dist/security/config.js +77 -0
  111. package/dist/security/csp.d.ts +53 -0
  112. package/dist/security/csp.js +118 -0
  113. package/dist/security/dev-hardening.d.ts +46 -0
  114. package/dist/security/dev-hardening.js +65 -0
  115. package/dist/security/errors.d.ts +37 -0
  116. package/dist/security/errors.js +84 -0
  117. package/dist/security/escape.d.ts +40 -0
  118. package/dist/security/escape.js +90 -0
  119. package/dist/security/head-tags.d.ts +32 -0
  120. package/dist/security/head-tags.js +156 -0
  121. package/dist/security/headers.d.ts +76 -0
  122. package/dist/security/headers.js +278 -0
  123. package/dist/security/index.d.ts +24 -0
  124. package/dist/security/index.js +49 -0
  125. package/dist/security/serialize.d.ts +48 -0
  126. package/dist/security/serialize.js +146 -0
  127. package/dist/variants/assets.d.ts +33 -0
  128. package/dist/variants/assets.js +144 -0
  129. package/dist/variants/catalog.d.ts +33 -0
  130. package/dist/variants/catalog.js +79 -0
  131. package/dist/variants/platform-tokens.d.ts +9 -0
  132. package/dist/variants/platform-tokens.js +15 -0
  133. package/dist/variants/resolve.d.ts +40 -0
  134. package/dist/variants/resolve.js +90 -0
  135. package/dist/variants/suffix.d.ts +8 -0
  136. package/dist/variants/suffix.js +12 -0
  137. package/dist/variants/types.d.ts +72 -0
  138. package/dist/variants/types.js +2 -0
  139. package/package.json +23 -11
  140. package/dist/helpers/vite-config.d.ts +0 -10
  141. package/dist/helpers/vite-config.js +0 -90
@@ -1,85 +1,108 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
38
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.middlewareAppliesTo = middlewareAppliesTo;
7
+ exports.toMiddlewareEntry = toMiddlewareEntry;
8
+ exports.middlewaresFromManifest = middlewaresFromManifest;
39
9
  exports.getMiddlewares = getMiddlewares;
40
10
  exports.loadPageMiddlewares = loadPageMiddlewares;
41
11
  exports.loadPageModule = loadPageModule;
42
12
  exports.createGetServerSidePropsMiddleware = createGetServerSidePropsMiddleware;
43
13
  exports.createPageErrorHandler = createPageErrorHandler;
44
- exports.createDynamicPageMiddlewares = createDynamicPageMiddlewares;
45
14
  exports.getPagePrevMiddlewares = getPagePrevMiddlewares;
46
15
  exports.getServerSidePropsAsMiddleware = getServerSidePropsAsMiddleware;
47
16
  exports.getErrorMiddleware = getErrorMiddleware;
48
17
  const node_path_1 = __importDefault(require("node:path"));
18
+ // En Windows una ruta absoluta (`D:\\…`) no es un especificador válido para
19
+ // `import()`: hay que pasarla como URL `file://`. Sin esto, los middlewares no
20
+ // cargaban en producción y la ruta quedaba sin ellos.
21
+ const node_url_1 = require("node:url");
49
22
  const fast_glob_1 = __importDefault(require("fast-glob"));
50
- const pages_1 = require("../pages");
23
+ const prod_server_1 = require("../runtime/prod-server");
24
+ const environments_1 = require("../plugin/environments");
25
+ const module_1 = require("../routes/module");
26
+ const module_extensions_1 = require("../fs/module-extensions");
27
+ const page_config_1 = require("../isr/page-config");
28
+ const page_config_merge_1 = require("./page-config-merge");
29
+ /**
30
+ * Si un middleware de carpeta con alcance `scope` aplica a `route`.
31
+ *
32
+ * Se compara por segmentos: `admin/middlewares.ts` protege `/admin` y
33
+ * `/admin/x`, pero no `/administrator`, que sólo comparte el prefijo de texto.
34
+ * La raíz aplica a todo.
35
+ */
36
+ function middlewareAppliesTo(scope, route) {
37
+ const scopeSegments = scope.split('/').filter(Boolean);
38
+ const routeSegments = route.split('/').filter(Boolean);
39
+ if (scopeSegments.length > routeSegments.length)
40
+ return false;
41
+ return scopeSegments.every((segment, i) => routeSegments[i] === segment);
42
+ }
43
+ /** Los middlewares que corren para una ruta: los de carpeta que la contienen y el de la página. */
44
+ function middlewaresFor(all, pageRoute) {
45
+ return all.filter((middleware) => middleware.type === 'page-specific'
46
+ ? middleware.route === pageRoute
47
+ : middlewareAppliesTo(middleware.route, pageRoute));
48
+ }
49
+ /** Lo que el build escribe en el manifest por cada middleware. */
50
+ function toMiddlewareEntry(info) {
51
+ return {
52
+ file: info.file,
53
+ route: info.route,
54
+ type: info.type,
55
+ depth: info.depth,
56
+ server: { devId: info.file },
57
+ };
58
+ }
59
+ /** Los middlewares tal como los registró el build, listos para cargar. */
60
+ function middlewaresFromManifest(entries) {
61
+ return entries.map((entry) => ({
62
+ route: entry.route,
63
+ file: entry.file,
64
+ abs: node_path_1.default.resolve(entry.file),
65
+ type: entry.type,
66
+ depth: entry.depth,
67
+ buildChunk: entry.server.buildChunk,
68
+ }));
69
+ }
51
70
  /**
52
71
  * Busca archivos de middlewares tanto específicos por página como globales por carpeta
53
72
  */
54
- function getMiddlewares() {
55
- // Busca archivos .middlewares.ts y middlewares.ts
56
- const files = fast_glob_1.default.sync('app/pages/**/*.middlewares.{ts,js}');
57
- const globalFiles = fast_glob_1.default.sync('app/pages/**/middlewares.{ts,js}');
73
+ function getMiddlewares(options = {}) {
74
+ const cwd = options.cwd ?? process.cwd();
75
+ // Las dos formas: `algo.middlewares.<ext>` para una página, `middlewares.<ext>`
76
+ // para una carpeta. Qué extensiones cuentan lo declara la política, no este archivo.
77
+ const [globalGlob, pageGlobPattern] = (0, module_extensions_1.middlewareGlobs)((0, module_extensions_1.getExtensionPolicy)());
78
+ const files = fast_glob_1.default.sync(pageGlobPattern, { cwd }).sort();
79
+ const globalFiles = fast_glob_1.default.sync(globalGlob, { cwd }).sort();
58
80
  const middlewares = [];
59
81
  // Procesar archivos específicos de página: page.middlewares.ts
60
82
  for (const file of files) {
61
- let route = file.replace(/^app\/pages/, '').replace(/\.middlewares\.(ts|js)$/, '');
62
- route = route.replace(/\/index$/, '') || '/';
83
+ // La derivación recorta cualquier extensión reconocida: antes sólo quitaba
84
+ // `.ts` y `.js`, así que un `.middlewares.tsx` producía una ruta con la
85
+ // extensión adentro, no coincidía con ninguna página y no corría nunca.
86
+ let route = (0, module_extensions_1.middlewareRoute)(file).replace(/\/index$/, '') || '/';
63
87
  // Calcular depth: contar slashes en la ruta del archivo
64
88
  const depth = (file.match(/\//g) || []).length;
65
89
  middlewares.push({
66
90
  route,
67
91
  file,
68
- abs: node_path_1.default.resolve(file),
92
+ abs: node_path_1.default.resolve(cwd, file),
69
93
  type: 'page-specific',
70
94
  depth
71
95
  });
72
96
  }
73
97
  // Procesar archivos globales: middlewares.ts
74
98
  for (const file of globalFiles) {
75
- let route = file.replace(/^app\/pages/, '').replace(/\/middlewares\.(ts|js)$/, '');
76
- route = route || '/';
99
+ let route = (0, module_extensions_1.middlewareRoute)(file) || '/';
77
100
  // Calcular depth: contar slashes en la ruta del archivo
78
101
  const depth = (file.match(/\//g) || []).length;
79
102
  middlewares.push({
80
103
  route,
81
104
  file,
82
- abs: node_path_1.default.resolve(file),
105
+ abs: node_path_1.default.resolve(cwd, file),
83
106
  type: 'folder-global',
84
107
  depth
85
108
  });
@@ -89,21 +112,9 @@ function getMiddlewares() {
89
112
  /**
90
113
  * Carga middlewares para una página específica y los convierte en middlewares de Express
91
114
  */
92
- async function loadPageMiddlewares(pageRoute, vite) {
93
- const allMiddlewares = getMiddlewares();
94
- // Filtrar middlewares relevantes para esta página usando jerarquía
95
- const relevantMiddlewares = allMiddlewares.filter(middleware => {
96
- if (middleware.type === 'folder-global') {
97
- // Un middleware global aplica si la página está dentro de su carpeta
98
- // ej: middleware en "/pokedex" aplica a "/pokedex" y "/pokedex/detail"
99
- return pageRoute.startsWith(middleware.route) || middleware.route === '/';
100
- }
101
- if (middleware.type === 'page-specific') {
102
- // Un middleware específico aplica solo a esa página exacta por ruta
103
- return middleware.route === pageRoute;
104
- }
105
- return false;
106
- });
115
+ async function loadPageMiddlewares(pageRoute, loader, middlewares) {
116
+ // Los que registró el build, si están; si no (desarrollo), el sistema de archivos.
117
+ const relevantMiddlewares = middlewaresFor(middlewares ?? getMiddlewares(), pageRoute);
107
118
  // Ordenar por jerarquía: depth menor primero, luego por tipo
108
119
  relevantMiddlewares.sort((a, b) => {
109
120
  // Primero por depth (carpetas más arriba en la jerarquía van primero)
@@ -121,7 +132,7 @@ async function loadPageMiddlewares(pageRoute, vite) {
121
132
  // Cargar middlewares de archivos .middlewares.ts
122
133
  for (const middlewareFile of relevantMiddlewares) {
123
134
  try {
124
- const moduleToLoad = await loadMiddlewareModule(middlewareFile, vite);
135
+ const moduleToLoad = await loadMiddlewareModule(middlewareFile, loader);
125
136
  const handlers = moduleToLoad.default || [];
126
137
  for (const handler of handlers) {
127
138
  if (typeof handler === 'function') {
@@ -130,7 +141,15 @@ async function loadPageMiddlewares(pageRoute, vite) {
130
141
  }
131
142
  }
132
143
  catch (error) {
133
- console.error(`Error loading middleware ${middlewareFile.file}:`, error);
144
+ // Fallo cerrado: un middleware que no carga no desaparece de la cadena,
145
+ // porque si protegía la ruta la dejaría abierta. En producción no se
146
+ // arranca; en desarrollo la ruta responde con error hasta que cargue.
147
+ const failure = new Error(`[ress] No se pudo cargar el middleware "${middlewareFile.file}": ` +
148
+ (error instanceof Error ? error.message : String(error)), { cause: error });
149
+ if (process.env.NODE_ENV === 'production')
150
+ throw failure;
151
+ console.error(failure.message);
152
+ expressMiddlewares.push((_req, _res, next) => next(failure));
134
153
  }
135
154
  }
136
155
  return expressMiddlewares;
@@ -138,7 +157,7 @@ async function loadPageMiddlewares(pageRoute, vite) {
138
157
  /**
139
158
  * Carga el módulo completo de una página
140
159
  */
141
- async function loadPageModule(pageFile, vite) {
160
+ async function loadPageModule(pageFile, loader) {
142
161
  const isProduction = process.env.NODE_ENV === 'production';
143
162
  const cacheKey = `${pageFile}-${process.env.NODE_ENV || 'development'}`;
144
163
  // Only use cache in production mode to allow HMR in development
@@ -147,19 +166,12 @@ async function loadPageModule(pageFile, vite) {
147
166
  }
148
167
  try {
149
168
  let pageModule;
150
- if (vite) {
151
- const modulePath = `/${pageFile}`;
152
- pageModule = await vite.ssrLoadModule(modulePath);
169
+ if (loader) {
170
+ pageModule = await loader.load(`/${pageFile}`);
153
171
  }
154
172
  else {
155
- if (pageFile.endsWith('.tsx')) {
156
- const moduleUrl = (0, pages_1.getCompiledProdPage)(pageFile);
157
- pageModule = await Promise.resolve(`${moduleUrl}`).then(s => __importStar(require(s)));
158
- }
159
- else {
160
- const moduleUrl = node_path_1.default.resolve(pageFile);
161
- pageModule = await Promise.resolve(`${moduleUrl}`).then(s => __importStar(require(s)));
162
- }
173
+ const moduleUrl = (0, prod_server_1.resolveServerModulePath)(pageFile);
174
+ pageModule = await import(/* @vite-ignore */ (0, node_url_1.pathToFileURL)(moduleUrl).href);
163
175
  }
164
176
  const result = pageModule;
165
177
  // Only cache in production mode
@@ -180,7 +192,10 @@ async function loadPageModule(pageFile, vite) {
180
192
  /**
181
193
  * Convierte getServerSideProps en un middleware de Express
182
194
  */
183
- function createGetServerSidePropsMiddleware(pageModule) {
195
+ function createGetServerSidePropsMiddleware(pageModule, file = 'la página') {
196
+ // Se declara acá afuera, no dentro del handler: tiene que sobrevivir entre
197
+ // requests para que el aviso salga una sola vez por página y no en cada hit.
198
+ let warnedAboutLegacyHeadApi = false;
184
199
  return async (req, res, next) => {
185
200
  if (!pageModule.getServerSideProps) {
186
201
  next();
@@ -190,13 +205,30 @@ function createGetServerSidePropsMiddleware(pageModule) {
190
205
  const result = await pageModule.getServerSideProps(req, res);
191
206
  // Manejar ServerSideResult
192
207
  if (result && typeof result === 'object') {
193
- // SEPARAR: Configuración HTML se queda en servidor, props van al cliente
194
- // Configuración HTML → solo para el servidor (NO va al cliente)
195
- if (result.title || result.html) {
196
- res.locals.htmlConfig = {
197
- title: result.title,
198
- html: result.html
199
- };
208
+ // `title` y `html` eran la forma en la que F-004 devolvía el head antes
209
+ // de `pageConfig` (ver PR #44). Se reemplazaron sin dejar rastro: una
210
+ // página que todavía las devuelve no rompe, simplemente deja de tener
211
+ // efecto en silencio. Un aviso en desarrollo es la única señal de que
212
+ // hace falta migrar.
213
+ if (!warnedAboutLegacyHeadApi && ('title' in result || 'html' in result) && process.env.NODE_ENV !== 'production') {
214
+ warnedAboutLegacyHeadApi = true;
215
+ console.warn(`[ress] getServerSideProps de "${req.path}" devuelve "title" y/o "html", ` +
216
+ 'una API de F-004 anterior a pageConfig que ya no se interpreta. ' +
217
+ 'Usá pageConfig.head para title/meta/link/script, y htmlAttributes / ' +
218
+ 'bodyAttributes para los atributos de <html> y <body>.');
219
+ }
220
+ if (result.pageConfig) {
221
+ const { document, rendering } = splitPageConfig(result.pageConfig);
222
+ // La configuración del documento sólo existe durante este request y no
223
+ // se serializa junto con las props de hidratación.
224
+ res.locals.pageConfig = document;
225
+ // La de render tiene la misma firma que el export estático y se
226
+ // combina con él: lo devuelto acá gana. Viaja en la petición, que
227
+ // nunca se serializa, y la leen el render (hidratación) y la caché
228
+ // (plazos de esta entrada y modo de la ruta).
229
+ if (Object.keys(rendering).length) {
230
+ req.ressPageRendering = (0, page_config_1.resolvePageRendering)({ ...renderingOf(pageModule.pageConfig), ...rendering }, file);
231
+ }
200
232
  }
201
233
  // Props → solo en un bucket claro (serverSideProps)
202
234
  if (result.props) {
@@ -213,67 +245,54 @@ function createGetServerSidePropsMiddleware(pageModule) {
213
245
  }
214
246
  };
215
247
  }
248
+ /** Separa lo que describe el documento de lo que decide cómo se renderiza. */
249
+ function splitPageConfig(config) {
250
+ const document = { ...config };
251
+ for (const field of page_config_merge_1.RENDERING_FIELDS)
252
+ delete document[field];
253
+ return { document, rendering: renderingOf(config) };
254
+ }
255
+ function renderingOf(config) {
256
+ const rendering = {};
257
+ for (const field of page_config_merge_1.RENDERING_FIELDS) {
258
+ if (config?.[field] !== undefined)
259
+ rendering[field] = config[field];
260
+ }
261
+ return rendering;
262
+ }
216
263
  /**
217
264
  * Creates a unified error handler for a specific page route
218
265
  * This replaces the old createPageErrorMiddleware with a more useful approach
219
266
  */
220
- function createPageErrorHandler(pageFile, vite) {
267
+ function createPageErrorHandler(pageFile, loader, middlewares) {
221
268
  return async (err, req, res, next) => {
222
- const errorMiddleware = await getErrorMiddleware(pageFile, vite);
269
+ const errorMiddleware = await getErrorMiddleware(pageFile, loader, middlewares);
270
+ // Sin errorMiddleware propio (ni en la página ni en la jerarquía de
271
+ // middlewares), la responsabilidad sigue hasta Express —o hasta un error
272
+ // handler que la aplicación haya montado después del router—, que no
273
+ // reproduce err.message en producción.
274
+ if (!errorMiddleware)
275
+ return next(err);
223
276
  errorMiddleware(err, req, res, next);
224
277
  };
225
278
  }
226
- /**
227
- * Error middleware por defecto
228
- */
229
- const defaultErrorMiddleware = (err, _req, res, _next) => {
230
- console.error(err);
231
- if (process.env.NODE_ENV !== 'production') {
232
- console.warn('No custom error handler configured. Add errorMiddleware export to your page or middleware file.');
233
- }
234
- res.status(500).send(err.message || 'Internal Server Error');
235
- };
236
279
  /**
237
280
  * Creates a dynamic middleware handler that reloads middlewares on each request in development
238
281
  * This enables HMR for middlewares
239
282
  * I FUCKING HATE THIS FUNCTION BUT IT SEEMS TO WORK
240
283
  */
241
- function createDynamicPageMiddlewares(pageRoute, pageFile, vite) {
242
- return async (req, res, next) => {
243
- try {
244
- const middlewares = [
245
- ...(await loadPageMiddlewares(pageRoute, vite)),
246
- await getServerSidePropsAsMiddleware(pageFile, vite)
247
- ];
248
- // Execute middlewares sequentially
249
- let currentIndex = 0;
250
- const executeNext = (err) => {
251
- if (err)
252
- return next(err);
253
- const currentMiddleware = middlewares[currentIndex++];
254
- if (!currentMiddleware)
255
- return next();
256
- currentMiddleware(req, res, executeNext);
257
- };
258
- executeNext();
259
- }
260
- catch (error) {
261
- next(error);
262
- }
263
- };
264
- }
265
284
  /**
266
285
  * 1. Resuelve middlewares previos al getServerSideProps
267
286
  */
268
- async function getPagePrevMiddlewares(pageRoute, vite) {
269
- return await loadPageMiddlewares(pageRoute, vite);
287
+ async function getPagePrevMiddlewares(pageRoute, loader) {
288
+ return await loadPageMiddlewares(pageRoute, loader);
270
289
  }
271
290
  /**
272
291
  * 2. Convierte getServerSideProps a middleware
273
292
  */
274
- async function getServerSidePropsAsMiddleware(pageFile, vite) {
275
- const pageModule = await loadPageModule(pageFile, vite);
276
- return pageModule ? createGetServerSidePropsMiddleware(pageModule) : (_req, _res, next) => next();
293
+ async function getServerSidePropsAsMiddleware(pageFile, loader) {
294
+ const pageModule = await loadPageModule(pageFile, loader);
295
+ return pageModule ? createGetServerSidePropsMiddleware(pageModule, pageFile) : (_req, _res, next) => next();
277
296
  }
278
297
  /**
279
298
  * 3. Error middleware con fallback automático y jerarquía
@@ -282,46 +301,44 @@ async function getServerSidePropsAsMiddleware(pageFile, vite) {
282
301
  * 2. errorMiddleware en [page].middlewares.ts
283
302
  * 3. errorMiddleware en carpeta/middlewares.ts
284
303
  * 4. errorMiddleware en root middlewares.ts (más general)
285
- * 5. defaultErrorMiddleware (fallback)
304
+ * 5. ninguno: null, para que el llamador delegue con next(err)
286
305
  */
287
- async function getErrorMiddleware(pageFile, vite) {
288
- const pageModule = await loadPageModule(pageFile, vite);
306
+ async function getErrorMiddleware(pageFile, loader, middlewares) {
307
+ const pageModule = await loadPageModule(pageFile, loader);
289
308
  // 1. Prioridad máxima: errorMiddleware en la página
290
309
  if (pageModule?.errorMiddleware) {
291
310
  return pageModule.errorMiddleware;
292
311
  }
293
312
  // 2-4. Buscar errorMiddleware en middlewares siguiendo jerarquía
294
- const errorMiddleware = await findErrorMiddlewareInHierarchy(pageFile, vite);
313
+ const errorMiddleware = await findErrorMiddlewareInHierarchy(pageFile, loader, middlewares);
295
314
  if (errorMiddleware) {
296
315
  return errorMiddleware;
297
316
  }
298
- // 5. Fallback al default
299
- return defaultErrorMiddleware;
317
+ // 5. No hay ninguno declarado: que siga hasta Express
318
+ return null;
300
319
  }
301
320
  /**
302
321
  * Busca errorMiddleware en la jerarquía de middlewares
303
322
  * Desde el más específico (página) hasta el más general (root)
304
323
  */
305
- async function findErrorMiddlewareInHierarchy(pageFile, vite) {
324
+ async function findErrorMiddlewareInHierarchy(pageFile, loader, middlewares) {
306
325
  // Obtener todos los middlewares que podrían aplicar a esta página
307
- const allMiddlewares = getMiddlewares();
326
+ const allMiddlewares = middlewares ?? getMiddlewares();
308
327
  // Convertir pageFile a route para matching
309
- let pageRoute = pageFile.replace(/^app\/pages/, '').replace(/\.(tsx|ts|jsx|js)$/, '');
328
+ // Sin `try` esto lanzaría desde el camino de error —el que atiende cuando algo
329
+ // ya falló—, y una página que no se pudo renderizar terminaría en una
330
+ // excepción sin manejar en vez de en la página de error.
331
+ let pageRoute;
332
+ try {
333
+ pageRoute = '/' + (0, module_extensions_1.pageIdOf)(pageFile);
334
+ }
335
+ catch {
336
+ pageRoute = '/' + pageFile.replace(/^app\/pages\//, '').replace(/\.[^./]+$/, '');
337
+ }
310
338
  pageRoute = pageRoute.replace(/\/index$/, '') || '/';
311
339
  // Filtrar middlewares relevantes para esta página
312
- const relevantMiddlewares = allMiddlewares
313
- .filter(mw => {
314
- // Para page-specific middlewares: debe coincidir exactamente con la ruta
315
- if (mw.type === 'page-specific') {
316
- return mw.route === pageRoute;
317
- }
318
- // Para folder-global middlewares: la página debe estar en esa carpeta o subcarpeta
319
- if (mw.route === '/')
320
- return true; // root middleware applies to all
321
- // Si el middleware está en una carpeta, debe aplicar a todas las páginas de esa carpeta
322
- // Ejemplo: /error-test/middlewares.ts aplica a /error-test/index, /error-test/sub, etc.
323
- return pageRoute.startsWith(mw.route + '/') || pageRoute === mw.route;
324
- })
340
+ // El mismo alcance que la cadena: por segmentos, la raíz aplica a todo.
341
+ const relevantMiddlewares = middlewaresFor(allMiddlewares, pageRoute)
325
342
  .sort((a, b) => {
326
343
  // Ordenar por especificidad: mayor depth = más específico
327
344
  if (a.depth !== b.depth)
@@ -336,7 +353,7 @@ async function findErrorMiddlewareInHierarchy(pageFile, vite) {
336
353
  // Buscar errorMiddleware en orden de especificidad
337
354
  for (const middlewareInfo of relevantMiddlewares) {
338
355
  try {
339
- const middlewareModule = await loadMiddlewareModule(middlewareInfo, vite);
356
+ const middlewareModule = await loadMiddlewareModule(middlewareInfo, loader);
340
357
  if (middlewareModule?.errorMiddleware) {
341
358
  return middlewareModule.errorMiddleware;
342
359
  }
@@ -347,14 +364,15 @@ async function findErrorMiddlewareInHierarchy(pageFile, vite) {
347
364
  }
348
365
  return null;
349
366
  }
350
- // Cache for loaded middleware modules to avoid multiple ssrLoadModule calls
367
+ // Caché de módulos de middleware. Sólo activa en producción: en desarrollo
368
+ // cada rearmado del router debe volver a leerlos para reflejar los cambios.
351
369
  const middlewareModuleCache = new Map();
352
- // Cache for loaded page modules to avoid multiple ssrLoadModule calls
370
+ // Caché de módulos de página. Sólo activa en producción, por el mismo motivo.
353
371
  const pageModuleCache = new Map();
354
372
  /**
355
373
  * Cargar un módulo de middleware específico
356
374
  */
357
- async function loadMiddlewareModule(middlewareInfo, vite) {
375
+ async function loadMiddlewareModule(middlewareInfo, loader) {
358
376
  const isProduction = process.env.NODE_ENV === 'production';
359
377
  const cacheKey = `${middlewareInfo.file}-${process.env.NODE_ENV || 'development'}`;
360
378
  // Only use cache in production mode to allow HMR in development
@@ -362,18 +380,22 @@ async function loadMiddlewareModule(middlewareInfo, vite) {
362
380
  return middlewareModuleCache.get(cacheKey);
363
381
  }
364
382
  let moduleToLoad;
365
- if (!isProduction && vite) {
366
- moduleToLoad = await vite.ssrLoadModule(middlewareInfo.file);
383
+ if (isProduction && middlewareInfo.buildChunk) {
384
+ // El chunk que registró el build, no un nombre derivado de la fuente.
385
+ const moduleUrl = (0, module_1.resolveServerModule)({ pageId: middlewareInfo.file, server: { devId: middlewareInfo.file, buildChunk: middlewareInfo.buildChunk } }, { root: process.cwd(), isProduction: true, serverOutDir: environments_1.OUT_DIR.ssr });
386
+ // `resolveServerModule` devuelve una ruta absoluta: en Windows sólo se
387
+ // puede importar como URL `file://`.
388
+ moduleToLoad = await import(/* @vite-ignore */ (0, node_url_1.pathToFileURL)(moduleUrl).href);
389
+ }
390
+ else if (!isProduction && loader) {
391
+ moduleToLoad = await loader.load(middlewareInfo.file);
367
392
  }
368
393
  else {
369
- // En producción, usar archivos compilados
370
- // Vite transforms [param] to _param_ in compiled filenames
371
- const compiledPath = middlewareInfo.file
372
- .replace(/^app\/pages\//, 'dist/server/')
373
- .replace(/\.ts$/, '.js')
374
- .replace(/\[(\w+)\]/g, '_$1_'); // [photocardId] -> _photocardId_
375
- const moduleUrl = node_path_1.default.resolve(compiledPath);
376
- moduleToLoad = await Promise.resolve(`${moduleUrl}`).then(s => __importStar(require(s)));
394
+ // En producción se importa el artefacto compilado. La traducción del nombre
395
+ // es la misma que usa el resto del framework: antes esta copia sólo recortaba
396
+ // `.ts`, así que un middleware `.js` cargaba un archivo inexistente.
397
+ const moduleUrl = (0, prod_server_1.resolveServerModulePath)(middlewareInfo.file);
398
+ moduleToLoad = await import(/* @vite-ignore */ (0, node_url_1.pathToFileURL)(moduleUrl).href);
377
399
  }
378
400
  // Only cache in production mode
379
401
  if (isProduction) {
@@ -0,0 +1,11 @@
1
+ import type { PageConfig } from '../head/types';
2
+ /**
3
+ * El documento de una petición: el `pageConfig` estático de la página como base
4
+ * y lo que devolvió `getServerSideProps` encima.
5
+ *
6
+ * Los campos de render no participan: se decidieron al armar la ruta y no son
7
+ * parte del documento.
8
+ */
9
+ export declare function mergePageConfig(staticConfig: PageConfig | undefined, dynamicConfig: PageConfig | undefined): PageConfig | undefined;
10
+ /** Los campos de render que alguien intentó fijar por petición. */
11
+ export declare const RENDERING_FIELDS: readonly ["renderingMode", "revalidate", "expire", "hydrate", "searchParams"];
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RENDERING_FIELDS = void 0;
4
+ exports.mergePageConfig = mergePageConfig;
5
+ /** Qué hace que dos `<meta>` sean la misma declaración. */
6
+ function metaIdentity(meta) {
7
+ for (const key of ['name', 'property', 'httpEquiv', 'http-equiv', 'charset', 'itemProp']) {
8
+ const value = meta[key];
9
+ if (value !== undefined)
10
+ return `${key === 'http-equiv' ? 'httpEquiv' : key}:${String(value).toLowerCase()}`;
11
+ }
12
+ return undefined;
13
+ }
14
+ function mergeHead(base, over) {
15
+ if (!base)
16
+ return over;
17
+ if (!over)
18
+ return base;
19
+ // Una `description` estática y otra calculada no son dos descripciones: la
20
+ // calculada reemplaza a la estática.
21
+ const replaced = new Set((over.meta ?? []).map(metaIdentity).filter(Boolean));
22
+ const concat = (a, b) => (a || b ? [...(a ?? []), ...(b ?? [])] : undefined);
23
+ const merged = {
24
+ ...base,
25
+ ...over,
26
+ meta: concat((base.meta ?? []).filter((m) => !replaced.has(metaIdentity(m))), over.meta),
27
+ link: concat(base.link, over.link),
28
+ script: concat(base.script, over.script),
29
+ style: concat(base.style, over.style),
30
+ noscript: concat(base.noscript, over.noscript),
31
+ };
32
+ for (const key of Object.keys(merged)) {
33
+ if (merged[key] === undefined)
34
+ delete merged[key];
35
+ }
36
+ return merged;
37
+ }
38
+ function mergeVariant(base, over) {
39
+ const merged = { ...base, ...over };
40
+ const head = mergeHead(base.head, over.head);
41
+ if (head)
42
+ merged.head = head;
43
+ if (base.htmlAttributes || over.htmlAttributes) {
44
+ merged.htmlAttributes = { ...base.htmlAttributes, ...over.htmlAttributes };
45
+ }
46
+ if (base.bodyAttributes || over.bodyAttributes) {
47
+ merged.bodyAttributes = { ...base.bodyAttributes, ...over.bodyAttributes };
48
+ }
49
+ return merged;
50
+ }
51
+ /**
52
+ * El documento de una petición: el `pageConfig` estático de la página como base
53
+ * y lo que devolvió `getServerSideProps` encima.
54
+ *
55
+ * Los campos de render no participan: se decidieron al armar la ruta y no son
56
+ * parte del documento.
57
+ */
58
+ function mergePageConfig(staticConfig, dynamicConfig) {
59
+ const base = documentOf(staticConfig);
60
+ const over = documentOf(dynamicConfig);
61
+ if (!base)
62
+ return over;
63
+ if (!over)
64
+ return base;
65
+ const merged = mergeVariant(base, over);
66
+ if (base.variants || over.variants) {
67
+ const variants = { ...base.variants };
68
+ for (const [name, variant] of Object.entries(over.variants ?? {})) {
69
+ variants[name] = variants[name] ? mergeVariant(variants[name], variant) : variant;
70
+ }
71
+ merged.variants = variants;
72
+ }
73
+ return merged;
74
+ }
75
+ /** Los campos de render que alguien intentó fijar por petición. */
76
+ exports.RENDERING_FIELDS = ['renderingMode', 'revalidate', 'expire', 'hydrate', 'searchParams'];
77
+ function documentOf(config) {
78
+ if (!config)
79
+ return undefined;
80
+ const document = { ...config };
81
+ for (const field of exports.RENDERING_FIELDS)
82
+ delete document[field];
83
+ return document;
84
+ }
@@ -1,4 +1,5 @@
1
- import { ViteConfig } from './vite-config';
1
+ import type { RessRuntime } from '../runtime/dev-server';
2
+ import type { RequestPlatform } from '../isr/key';
2
3
  export interface RequestContext {
3
4
  req: any;
4
5
  res: any;
@@ -12,6 +13,18 @@ export interface RouterConfig {
12
13
  platform: string;
13
14
  req: any;
14
15
  }) => Promise<string>;
15
- viteConfig: ViteConfig;
16
+ viteConfig: RessRuntime;
16
17
  }
18
+ type PlatformContext = RequestPlatform;
19
+ export declare function resolveRequestPlatform(req: any, enablePlatformDetection?: boolean): PlatformContext;
20
+ /**
21
+ * Arma el render server a partir de la config de la ruta.
22
+ *
23
+ * `viteConfig` puede llegar como thunk (modo automático: el runtime se
24
+ * resuelve después de crear el router), por eso se resuelve acá y no al
25
+ * armar `RouterConfig`. Se llama una sola vez por middleware: quien la usa
26
+ * cachea el resultado en su propio closure.
27
+ */
28
+ export declare function buildRenderServer(config: RouterConfig): any;
17
29
  export declare function processRequest(context: RequestContext, config: RouterConfig, renderServer?: any): Promise<void>;
30
+ export {};