@ressjs/vite-router 0.5.0 → 0.6.0-experimental.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 (110) hide show
  1. package/README.md +41 -31
  2. package/dist/client/entry.d.mts +2 -0
  3. package/dist/client/entry.mjs +1 -0
  4. package/dist/client/index.d.ts +2 -0
  5. package/dist/client/index.js +8 -0
  6. package/dist/config/base-path.d.ts +44 -0
  7. package/dist/config/base-path.js +100 -0
  8. package/dist/config/codegen.d.ts +23 -0
  9. package/dist/config/codegen.js +118 -0
  10. package/dist/config/define.d.ts +8 -0
  11. package/dist/config/define.js +12 -0
  12. package/dist/config/env.d.ts +19 -0
  13. package/dist/config/env.js +40 -0
  14. package/dist/config/index.d.ts +18 -0
  15. package/dist/config/index.js +44 -0
  16. package/dist/config/load.d.ts +26 -0
  17. package/dist/config/load.js +73 -0
  18. package/dist/config/middleware.d.ts +18 -0
  19. package/dist/config/middleware.js +79 -0
  20. package/dist/config/rules.d.ts +48 -0
  21. package/dist/config/rules.js +149 -0
  22. package/dist/config/types.d.ts +164 -0
  23. package/dist/config/types.js +2 -0
  24. package/dist/config/validate.d.ts +19 -0
  25. package/dist/config/validate.js +105 -0
  26. package/dist/config/watch.d.ts +41 -0
  27. package/dist/config/watch.js +132 -0
  28. package/dist/fs/module-extensions.d.ts +82 -0
  29. package/dist/fs/module-extensions.js +196 -0
  30. package/dist/helpers/html-generator.d.ts +22 -5
  31. package/dist/helpers/html-generator.js +89 -120
  32. package/dist/helpers/middlewares.d.ts +7 -7
  33. package/dist/helpers/middlewares.js +51 -103
  34. package/dist/helpers/request-handler.d.ts +2 -2
  35. package/dist/helpers/request-handler.js +118 -25
  36. package/dist/index.d.ts +30 -3
  37. package/dist/index.js +70 -8
  38. package/dist/pages.d.ts +10 -9
  39. package/dist/pages.js +42 -261
  40. package/dist/platform.d.ts +11 -14
  41. package/dist/platform.js +18 -102
  42. package/dist/plugin/environments.d.ts +66 -0
  43. package/dist/plugin/environments.js +72 -0
  44. package/dist/plugin/index.d.ts +54 -0
  45. package/dist/plugin/index.js +178 -0
  46. package/dist/plugin/virtual-entries.d.ts +26 -0
  47. package/dist/plugin/virtual-entries.js +80 -0
  48. package/dist/render.d.ts +23 -2
  49. package/dist/render.js +40 -53
  50. package/dist/router.d.ts +32 -13
  51. package/dist/router.js +147 -146
  52. package/dist/routes/dispatcher.d.ts +37 -0
  53. package/dist/routes/dispatcher.js +69 -0
  54. package/dist/routes/manifest.d.ts +17 -0
  55. package/dist/routes/manifest.js +56 -0
  56. package/dist/routes/match.d.ts +20 -0
  57. package/dist/routes/match.js +75 -0
  58. package/dist/routes/parse.d.ts +31 -0
  59. package/dist/routes/parse.js +114 -0
  60. package/dist/routes/rank.d.ts +12 -0
  61. package/dist/routes/rank.js +49 -0
  62. package/dist/routes/router.d.ts +44 -0
  63. package/dist/routes/router.js +81 -0
  64. package/dist/routes/scan.d.ts +16 -0
  65. package/dist/routes/scan.js +82 -0
  66. package/dist/routes/types.d.ts +85 -0
  67. package/dist/routes/types.js +13 -0
  68. package/dist/runtime/dev-server.d.ts +62 -0
  69. package/dist/runtime/dev-server.js +94 -0
  70. package/dist/runtime/module-loader.d.ts +55 -0
  71. package/dist/runtime/module-loader.js +122 -0
  72. package/dist/runtime/prod-server.d.ts +42 -0
  73. package/dist/runtime/prod-server.js +142 -0
  74. package/dist/runtime/template.d.ts +10 -0
  75. package/dist/runtime/template.js +33 -0
  76. package/dist/security/client-props.d.ts +25 -0
  77. package/dist/security/client-props.js +50 -0
  78. package/dist/security/config.d.ts +130 -0
  79. package/dist/security/config.js +73 -0
  80. package/dist/security/csp.d.ts +53 -0
  81. package/dist/security/csp.js +118 -0
  82. package/dist/security/dev-hardening.d.ts +46 -0
  83. package/dist/security/dev-hardening.js +65 -0
  84. package/dist/security/errors.d.ts +37 -0
  85. package/dist/security/errors.js +84 -0
  86. package/dist/security/escape.d.ts +40 -0
  87. package/dist/security/escape.js +90 -0
  88. package/dist/security/head-tags.d.ts +32 -0
  89. package/dist/security/head-tags.js +111 -0
  90. package/dist/security/headers.d.ts +62 -0
  91. package/dist/security/headers.js +187 -0
  92. package/dist/security/index.d.ts +24 -0
  93. package/dist/security/index.js +47 -0
  94. package/dist/security/serialize.d.ts +48 -0
  95. package/dist/security/serialize.js +146 -0
  96. package/dist/variants/assets.d.ts +23 -0
  97. package/dist/variants/assets.js +78 -0
  98. package/dist/variants/catalog.d.ts +33 -0
  99. package/dist/variants/catalog.js +79 -0
  100. package/dist/variants/platform-tokens.d.ts +9 -0
  101. package/dist/variants/platform-tokens.js +15 -0
  102. package/dist/variants/resolve.d.ts +40 -0
  103. package/dist/variants/resolve.js +90 -0
  104. package/dist/variants/suffix.d.ts +8 -0
  105. package/dist/variants/suffix.js +12 -0
  106. package/dist/variants/types.d.ts +69 -0
  107. package/dist/variants/types.js +2 -0
  108. package/package.json +22 -10
  109. package/dist/helpers/vite-config.d.ts +0 -10
  110. package/dist/helpers/vite-config.js +0 -90
package/dist/router.js CHANGED
@@ -1,161 +1,162 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.getPages = void 0;
7
- exports.createAutoViteServer = createAutoViteServer;
4
+ exports.waitForViteRouter = waitForViteRouter;
8
5
  exports.createViteRouter = createViteRouter;
9
- exports.listDetectedPages = listDetectedPages;
10
6
  const express_1 = require("express");
11
- const pages_1 = require("./pages");
12
- const express_2 = __importDefault(require("express"));
13
- const vite_config_1 = require("./helpers/vite-config");
14
- const middlewares_1 = require("./helpers/middlewares");
15
- const render_1 = require("./render");
16
- async function createAutoViteServer(options = {}) {
17
- const { port = 5173, base = '/', enablePlatformDetection = true } = options;
18
- const isProduction = process.env.NODE_ENV === 'production';
19
- const app = (0, express_2.default)();
20
- const viteConfig = await (0, vite_config_1.setupVite)(isProduction, base);
21
- if (!isProduction) {
22
- app.use(viteConfig.vite.middlewares);
23
- app.use('/dist/.entries', express_2.default.static('dist/.entries'));
24
- }
25
- else {
26
- const prodFactory = (0, vite_config_1.createProdMiddleware)();
27
- const prodMiddleware = await prodFactory();
28
- app.use(prodMiddleware);
29
- }
30
- const viteRouter = createViteRouter({
31
- enablePlatformDetection,
32
- vite: viteConfig.vite,
33
- templateHtml: viteConfig.templateHtml,
34
- isProduction
35
- });
36
- app.use('/', viteRouter);
37
- app.use(express_2.default.static('public'));
38
- const start = () => {
39
- return app.listen(port, () => {
40
- if (process.env.NODE_ENV !== 'production') {
41
- const pages = (0, pages_1.getPages)();
42
- console.log(`🚀 Auto Vite Server started at http://localhost:${port}`);
43
- console.log(`📄 Auto-detected routes:`);
44
- pages.forEach(page => {
45
- const expressDisplay = page.route !== page.expressRoute ? ` (Express: ${page.expressRoute})` : '';
46
- console.log(` ${page.route}${expressDisplay} -> ${page.file}`);
47
- });
48
- }
49
- });
50
- };
51
- return { app, vite: viteConfig.vite, start };
7
+ const prod_server_1 = require("./runtime/prod-server");
8
+ const security_1 = require("./security");
9
+ const router_1 = require("./routes/router");
10
+ const manifest_1 = require("./routes/manifest");
11
+ const scan_1 = require("./routes/scan");
12
+ const config_1 = require("./config");
13
+ const routerReadiness = new WeakMap();
14
+ /** Espera a que el runtime, el manifest y las cadenas de middleware estén listos. */
15
+ function waitForViteRouter(router) {
16
+ return routerReadiness.get(router) ?? Promise.resolve();
52
17
  }
18
+ /**
19
+ * El router de ress.js.
20
+ *
21
+ * Antes había dos constructores con el 90 % del cuerpo repetido, uno para quien
22
+ * deja que el framework arme el runtime y otro para quien lo aporta. La
23
+ * diferencia real entre los dos es de dónde sale el runtime, así que eso es lo
24
+ * único que se decide acá; el resto del camino es uno solo.
25
+ */
53
26
  function createViteRouter(options = {}) {
54
- const needsAutoConfig = !options.vite && !options.templateHtml && options.isProduction !== true;
55
- if (needsAutoConfig) {
56
- return createAutoRouter(options);
57
- }
58
- return createManualRouter(options);
59
- }
60
- function createAutoRouter(options) {
61
27
  const { renderFunction, enablePlatformDetection = true } = options;
62
- const isProduction = process.env.NODE_ENV === 'production';
63
- let viteConfig = null;
64
- let initialized = false;
28
+ const security = options.security ?? (0, security_1.resolveSecurityOptions)(options.securityOptions);
29
+ const providesOwnRuntime = Boolean(options.moduleLoader || options.templateHtml);
30
+ const isProduction = options.isProduction ?? process.env.NODE_ENV === 'production';
65
31
  const mainRouter = (0, express_1.Router)();
66
- const initializeIfNeeded = async () => {
67
- if (initialized)
68
- return viteConfig;
69
- const basePath = process.env.BASE || '/';
70
- viteConfig = await (0, vite_config_1.setupVite)(isProduction, basePath);
71
- if (!isProduction) {
72
- mainRouter.use(viteConfig.vite.middlewares);
32
+ let runtime = null;
33
+ /**
34
+ * El manifest vigente.
35
+ *
36
+ * Se guarda acá y no se lee del runtime en cada rearmado: en desarrollo hay
37
+ * que volver a escanear cuando aparece o desaparece un archivo, y el runtime
38
+ * lo construyó una sola vez al arrancar.
39
+ */
40
+ let routes = null;
41
+ let state = null;
42
+ let pageRouter = null;
43
+ let assetMiddleware = null;
44
+ let configMiddleware = null;
45
+ let stale = false;
46
+ let rescan = false;
47
+ const markRescan = () => {
48
+ stale = true;
49
+ rescan = true;
50
+ };
51
+ const chainContext = () => ({
52
+ moduleLoader: runtime?.moduleLoader,
53
+ renderOptions: {
54
+ enablePlatformDetection,
55
+ renderFunction,
56
+ security,
57
+ viteConfig: () => runtime,
58
+ },
59
+ });
60
+ const rebuild = async (rescan = false) => {
61
+ stale = false;
62
+ if (rescan || !routes)
63
+ routes = (0, manifest_1.createRouteManifest)(await (0, scan_1.scanRoutes)());
64
+ state = await (0, router_1.buildRouterState)(routes, chainContext());
65
+ };
66
+ const setup = async () => {
67
+ // La configuración primero: de ella salen el prefijo de ruta, el prefijo de
68
+ // artefactos y las reglas del pipeline, y las tres se necesitan antes de
69
+ // armar el runtime.
70
+ const config = options.config ??
71
+ (await (0, config_1.loadConfig)(options.root ?? process.cwd(), options.mode ?? (isProduction ? 'production' : 'development')));
72
+ configMiddleware = (0, config_1.createConfigMiddleware)(config);
73
+ const assetPrefix = (0, config_1.assetPrefixFor)({
74
+ basePath: config.basePath,
75
+ cdnPrefix: config.assets?.cdnPrefix,
76
+ });
77
+ if (options.runtime) {
78
+ runtime = options.runtime;
79
+ routes = runtime.routes;
80
+ assetMiddleware = isProduction
81
+ ? await (0, prod_server_1.createProdAssetMiddleware)(undefined, { buildOutputs: runtime.buildOutputs })
82
+ : runtime.vite.middlewares;
73
83
  }
74
- else {
75
- const prodFactory = (0, vite_config_1.createProdMiddleware)();
76
- const prodMiddleware = await prodFactory();
77
- mainRouter.use(prodMiddleware);
84
+ else if (providesOwnRuntime) {
85
+ // Quien aporta su propio cargador de módulos no tiene runtime del
86
+ // framework, así que se arma uno mínimo con lo que declaró.
87
+ runtime = {
88
+ vite: null,
89
+ moduleLoader: options.moduleLoader,
90
+ templateHtml: options.templateHtml ?? '',
91
+ isProduction,
92
+ buildOutputs: new Set(),
93
+ close: async () => { },
94
+ };
78
95
  }
79
- initialized = true;
80
- return viteConfig;
81
- };
82
- const pages = (0, pages_1.getPages)();
83
- // Initialize Vite ONCE before setting up any routes
84
- const setupRoutes = async () => {
85
- if (!viteConfig)
86
- await initializeIfNeeded();
87
- // Process pages sequentially to avoid race conditions
88
- for (const page of pages) {
89
- // Dynamic middleware loading for HMR support
90
- const HMRMiddleware = await (0, middlewares_1.createDynamicPageMiddlewares)(page.route, page.file, viteConfig?.vite);
91
- // Productive middlewares
92
- const prevMiddlewares = await (0, middlewares_1.loadPageMiddlewares)(page.route, viteConfig?.vite);
93
- const serverSidePropsMiddleware = await (0, middlewares_1.getServerSidePropsAsMiddleware)(page.file, viteConfig?.vite);
94
- const renderMiddleware = (0, render_1.createRenderMiddleware)(page, {
95
- enablePlatformDetection,
96
- renderFunction,
97
- viteConfig: () => viteConfig // pasar por función para evitar capturar null
98
- });
99
- const pageMiddlewares = isProduction ? [
100
- ...prevMiddlewares,
101
- serverSidePropsMiddleware,
102
- renderMiddleware
103
- ] : [
104
- HMRMiddleware,
105
- renderMiddleware
106
- ];
107
- mainRouter.get(page.expressRoute, ...pageMiddlewares);
108
- // Error middleware separado
109
- mainRouter.use(page.expressRoute, (0, middlewares_1.createPageErrorHandler)(page.file, viteConfig?.vite));
96
+ else {
97
+ // El `base` de Vite se queda en `/`: el prefijo de ruta lo quita el
98
+ // pipeline de petición, y lo que se escribe en el HTML lo lleva por el
99
+ // prefijo de artefactos.
100
+ runtime = await (0, prod_server_1.createRuntime)(isProduction, options.basePath ?? process.env.BASE ?? '/', assetPrefix);
101
+ routes = runtime.routes;
102
+ // Los estáticos se atienden antes que las páginas: un pedido de artefacto
103
+ // no tiene que recorrer el manifest de rutas, y una ruta que capture todo
104
+ // no debe interceptarlos.
105
+ assetMiddleware = isProduction
106
+ ? await (0, prod_server_1.createProdAssetMiddleware)(undefined, { buildOutputs: runtime.buildOutputs })
107
+ : runtime.vite.middlewares;
110
108
  }
109
+ await rebuild();
110
+ pageRouter = (0, router_1.createPageRouter)(() => state, chainContext());
111
+ // Se marca y se rearma en la petición siguiente, no acá: el watcher avisa
112
+ // antes de que Vite termine de invalidar su grafo, y rearmar en ese instante
113
+ // volvería a leer la versión anterior del archivo.
114
+ runtime.onSourceChange?.(() => {
115
+ stale = true;
116
+ });
117
+ // Crear o borrar una página cambia el conjunto de rutas, no sólo el
118
+ // contenido de un módulo: hay que volver a escanear.
119
+ runtime.vite?.watcher?.on('add', markRescan);
120
+ runtime.vite?.watcher?.on('unlink', markRescan);
111
121
  };
112
- // Setup routes asynchronously but don't block router creation
113
- setupRoutes().catch(console.error);
122
+ // El armado es asíncrono y una petición puede llegar antes de que termine. Se
123
+ // espera la promesa en vez de responder como si la ruta no existiera.
124
+ const ready = setup();
125
+ routerReadiness.set(mainRouter, ready);
126
+ mainRouter.use((req, res, next) => {
127
+ ready.then(() => {
128
+ const serve = () => {
129
+ // Se lee acá y no antes: la marca la pone el pipeline de configuración,
130
+ // que corre después de este callback.
131
+ const outside = req.ressOutsideBasePath === true;
132
+ // Después de la configuración, los artefactos: un pedido de artefacto no
133
+ // tiene que recorrer el manifest de rutas. Se registra acá y no con
134
+ // `use()` porque los dos middlewares se resuelven dentro del armado
135
+ // asíncrono, y registrarlos después dejaría al despachador por delante.
136
+ // Fuera del prefijo no hay páginas: declarar un prefijo y seguir
137
+ // sirviendo el sitio entero por la raíz es exactamente lo que un prefijo
138
+ // existe para evitar. Los artefactos sí se intentan igual, porque las
139
+ // rutas internas del servidor de desarrollo de Vite —`/@vite/client`,
140
+ // `/@fs/`— viven en la raíz y no bajo el prefijo de la aplicación.
141
+ const after = (err) => err ? next(err) : outside ? next() : dispatch(req, res, next);
142
+ if (!assetMiddleware)
143
+ return after();
144
+ assetMiddleware(req, res, after);
145
+ };
146
+ // Barra final, prefijo de ruta, redirecciones y reescrituras: todo lo que
147
+ // decide *qué* URL se está atendiendo va antes que quien la atiende.
148
+ if (!configMiddleware)
149
+ return serve();
150
+ configMiddleware(req, res, ((err) => (err ? next(err) : serve())));
151
+ }, next);
152
+ });
153
+ function dispatch(req, res, next) {
154
+ const run = () => (pageRouter ? pageRouter(req, res, next) : next());
155
+ if (!stale)
156
+ return run();
157
+ rebuild(rescan).then(() => { rescan = false; run(); }, next);
158
+ }
114
159
  return mainRouter;
115
160
  }
116
- function createManualRouter(options) {
117
- const { vite, renderFunction, enablePlatformDetection = true } = options;
118
- const router = (0, express_1.Router)();
119
- const pages = (0, pages_1.getPages)();
120
- // Crear viteConfig para processRequest
121
- const viteConfig = {
122
- vite,
123
- templateHtml: options.templateHtml || '',
124
- isProduction: options.isProduction || false,
125
- manifest: null
126
- };
127
- // Process pages sequentially to avoid race conditions
128
- const setupRoutes = async () => {
129
- for (const page of pages) {
130
- // Dynamic middleware loading for HMR support(only in dev)
131
- const HMRMiddleware = await (0, middlewares_1.createDynamicPageMiddlewares)(page.route, page.file, vite);
132
- // Productive middlewares
133
- const prevMiddlewares = await (0, middlewares_1.loadPageMiddlewares)(page.route, vite);
134
- const serverSidePropsMiddleware = await (0, middlewares_1.getServerSidePropsAsMiddleware)(page.file, vite);
135
- const renderMiddleware = (0, render_1.createRenderMiddleware)(page, {
136
- enablePlatformDetection,
137
- renderFunction,
138
- viteConfig
139
- });
140
- const pageMiddlewares = options.isProduction ? [
141
- ...prevMiddlewares,
142
- serverSidePropsMiddleware,
143
- renderMiddleware
144
- ] : [
145
- HMRMiddleware,
146
- renderMiddleware
147
- ];
148
- router.get(page.expressRoute, ...pageMiddlewares);
149
- // Error middleware separado
150
- router.use(page.expressRoute, (0, middlewares_1.createPageErrorHandler)(page.file, vite));
151
- }
152
- };
153
- // Setup routes asynchronously but don't block router creation
154
- setupRoutes().catch(console.error);
155
- return router;
156
- }
157
- function listDetectedPages() {
158
- return (0, pages_1.getPages)();
159
- }
160
- var pages_2 = require("./pages");
161
- Object.defineProperty(exports, "getPages", { enumerable: true, get: function () { return pages_2.getPages; } });
161
+ var pages_1 = require("./pages");
162
+ Object.defineProperty(exports, "getPages", { enumerable: true, get: function () { return pages_1.getPages; } });
@@ -0,0 +1,37 @@
1
+ import type { RequestHandler } from 'express';
2
+ import type { RouteEntry, RouteManifest } from './types';
3
+ declare global {
4
+ namespace Express {
5
+ interface Request {
6
+ /** La ruta que atendió esta petición. La usa el manejador de errores. */
7
+ ressRoute?: RouteEntry;
8
+ /**
9
+ * Los parámetros de la URL.
10
+ *
11
+ * Express reasigna `req.params` al entrar a cada router anidado, así que
12
+ * los valores que el despachador extrajo se perderían en el camino. Acá
13
+ * quedan en un campo propio, y `req.params` se mantiene por compatibilidad
14
+ * con quien ya lo lee.
15
+ */
16
+ ressParams?: Record<string, string | string[]>;
17
+ }
18
+ }
19
+ }
20
+ /** Cómo se arma la cadena de middlewares de una entrada. */
21
+ export type ChainBuilder = (entry: RouteEntry) => Promise<RequestHandler[]>;
22
+ /**
23
+ * Un único punto de entrada para todas las rutas.
24
+ *
25
+ * El modelo anterior registraba una ruta de Express por página, lo que obligaba
26
+ * a traducir cada patrón a la sintaxis de Express —que no puede expresar un
27
+ * catch-all opcional ni un grupo— y dejaba la precedencia en manos del orden de
28
+ * registro. Acá el manifest decide, y Express sólo ve un middleware.
29
+ *
30
+ * Las cadenas se construyen una vez, no por petición. `chains` se reemplaza
31
+ * entero cuando el manifest cambia en desarrollo, de modo que una petición nunca
32
+ * ve una mezcla de dos versiones.
33
+ */
34
+ export declare function createDispatcher(getState: () => {
35
+ manifest: RouteManifest;
36
+ chains: Map<string, RequestHandler[]>;
37
+ }): RequestHandler;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createDispatcher = createDispatcher;
4
+ /**
5
+ * Un único punto de entrada para todas las rutas.
6
+ *
7
+ * El modelo anterior registraba una ruta de Express por página, lo que obligaba
8
+ * a traducir cada patrón a la sintaxis de Express —que no puede expresar un
9
+ * catch-all opcional ni un grupo— y dejaba la precedencia en manos del orden de
10
+ * registro. Acá el manifest decide, y Express sólo ve un middleware.
11
+ *
12
+ * Las cadenas se construyen una vez, no por petición. `chains` se reemplaza
13
+ * entero cuando el manifest cambia en desarrollo, de modo que una petición nunca
14
+ * ve una mezcla de dos versiones.
15
+ */
16
+ function createDispatcher(getState) {
17
+ return async (req, res, next) => {
18
+ const { manifest, chains } = getState();
19
+ const match = manifest.match(req.path ?? req.url ?? '/');
20
+ // Sin match no es un 404 todavía: puede haber middlewares detrás que sepan
21
+ // atenderla, y decidirlo acá les quitaría la oportunidad.
22
+ if (!match)
23
+ return next();
24
+ const chain = chains.get(match.entry.pageId);
25
+ if (!chain)
26
+ return next();
27
+ req.ressParams = match.params;
28
+ req.params = match.params;
29
+ req.ressRoute = match.entry;
30
+ runChain(chain, req, res, next);
31
+ };
32
+ }
33
+ /**
34
+ * Ejecuta la cadena respetando el contrato de Express.
35
+ *
36
+ * Cada middleware decide si sigue: `next()` avanza, `next(error)` corta y
37
+ * propaga. Un throw síncrono se convierte en `next(error)` para que no quede una
38
+ * petición colgada.
39
+ */
40
+ function runChain(chain, req, res, done) {
41
+ let index = 0;
42
+ const step = (error) => {
43
+ if (error)
44
+ return done(error);
45
+ const handler = chain[index++];
46
+ if (!handler)
47
+ return done();
48
+ // Un middleware asíncrono puede llamar a `next()` y rechazar después. Sin
49
+ // esta guarda, el rechazo volvería a entrar a la cadena sobre una respuesta
50
+ // que ya se envió, y el fallo sería un ERR_HTTP_HEADERS_SENT en vez de la
51
+ // página de error.
52
+ let advanced = false;
53
+ const once = (err) => {
54
+ if (advanced)
55
+ return;
56
+ advanced = true;
57
+ step(err);
58
+ };
59
+ try {
60
+ const result = handler(req, res, once);
61
+ if (result instanceof Promise)
62
+ result.catch(once);
63
+ }
64
+ catch (err) {
65
+ once(err);
66
+ }
67
+ };
68
+ step();
69
+ }
@@ -0,0 +1,17 @@
1
+ import { type RouteManifest, type RouteManifestData } from './types';
2
+ /**
3
+ * El manifest ya indexado.
4
+ *
5
+ * Las rutas estáticas —la mayoría de un proyecto— se resuelven con una consulta
6
+ * a un mapa. Sólo las que tienen parámetros recorren la lista, y ya viene
7
+ * ordenada por precedencia, así que la primera que matchea es la que gana.
8
+ */
9
+ export declare function createRouteManifest(data: RouteManifestData): RouteManifest;
10
+ export declare function serializeRouteManifest(data: RouteManifestData): string;
11
+ /**
12
+ * Lee un manifest emitido por el build.
13
+ *
14
+ * La versión se comprueba a propósito: un manifest de un formato anterior
15
+ * produciría fallos de ruteo difíciles de atribuir, y el mensaje dice qué hacer.
16
+ */
17
+ export declare function loadRouteManifest(raw: string): RouteManifest;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRouteManifest = createRouteManifest;
4
+ exports.serializeRouteManifest = serializeRouteManifest;
5
+ exports.loadRouteManifest = loadRouteManifest;
6
+ const match_1 = require("./match");
7
+ const types_1 = require("./types");
8
+ /**
9
+ * El manifest ya indexado.
10
+ *
11
+ * Las rutas estáticas —la mayoría de un proyecto— se resuelven con una consulta
12
+ * a un mapa. Sólo las que tienen parámetros recorren la lista, y ya viene
13
+ * ordenada por precedencia, así que la primera que matchea es la que gana.
14
+ */
15
+ function createRouteManifest(data) {
16
+ const entries = data.entries;
17
+ const byPageId = new Map(entries.map((e) => [e.pageId, e]));
18
+ const staticIndex = new Map();
19
+ for (const entry of entries) {
20
+ if (entry.segments.every((s) => s.kind === 'static')) {
21
+ staticIndex.set(`${entry.kind} ${entry.pattern}`, entry);
22
+ }
23
+ }
24
+ return {
25
+ entries,
26
+ match(pathname, options = {}) {
27
+ const kinds = options.kinds ?? ['page'];
28
+ const clean = '/' + pathname.split('?')[0].split('#')[0].split('/').filter(Boolean).join('/');
29
+ for (const kind of kinds) {
30
+ const exact = staticIndex.get(`${kind} ${clean}`);
31
+ if (exact)
32
+ return { entry: exact, params: {} };
33
+ }
34
+ return (0, match_1.matchEntries)(entries, pathname, options);
35
+ },
36
+ get: (pageId) => byPageId.get(pageId),
37
+ byKind: (kind) => entries.filter((e) => e.kind === kind),
38
+ };
39
+ }
40
+ function serializeRouteManifest(data) {
41
+ return JSON.stringify(data, null, 2);
42
+ }
43
+ /**
44
+ * Lee un manifest emitido por el build.
45
+ *
46
+ * La versión se comprueba a propósito: un manifest de un formato anterior
47
+ * produciría fallos de ruteo difíciles de atribuir, y el mensaje dice qué hacer.
48
+ */
49
+ function loadRouteManifest(raw) {
50
+ const data = JSON.parse(raw);
51
+ if (data.version !== types_1.ROUTE_MANIFEST_VERSION) {
52
+ throw new Error(`[ress] El manifest de rutas es de la versión ${data.version} y este ress.js espera la ` +
53
+ `${types_1.ROUTE_MANIFEST_VERSION}. Volvé a construir el proyecto.`);
54
+ }
55
+ return createRouteManifest(data);
56
+ }
@@ -0,0 +1,20 @@
1
+ import type { MatchOptions, RouteEntry, RouteMatch, RouteSegment } from './types';
2
+ /**
3
+ * Parte una URL en segmentos.
4
+ *
5
+ * Se descartan la consulta y el fragmento, y la barra final no distingue: `/about`
6
+ * y `/about/` son la misma ruta.
7
+ */
8
+ export declare function splitPath(pathname: string): string[];
9
+ /**
10
+ * ¿Esta ruta atiende esta URL?
11
+ *
12
+ * Sin expresiones regulares: se recorre segmento a segmento. Una regex construida
13
+ * a partir de un patrón es difícil de auditar y fácil de romper con un carácter
14
+ * que había que escapar — y el modelo anterior tenía justamente ese defecto:
15
+ * `/page1` matcheaba `/page1/cualquier/cosa` porque su regex no exigía consumir
16
+ * la URL entera.
17
+ */
18
+ export declare function matchSegments(segments: readonly RouteSegment[], parts: readonly string[]): Record<string, string | string[]> | undefined;
19
+ /** La primera entrada que atiende esta URL, recorriendo en orden de precedencia. */
20
+ export declare function matchEntries(entries: readonly RouteEntry[], pathname: string, options?: MatchOptions): RouteMatch | undefined;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.splitPath = splitPath;
4
+ exports.matchSegments = matchSegments;
5
+ exports.matchEntries = matchEntries;
6
+ /**
7
+ * Parte una URL en segmentos.
8
+ *
9
+ * Se descartan la consulta y el fragmento, y la barra final no distingue: `/about`
10
+ * y `/about/` son la misma ruta.
11
+ */
12
+ function splitPath(pathname) {
13
+ const clean = pathname.split('?')[0].split('#')[0];
14
+ return clean.split('/').filter(Boolean);
15
+ }
16
+ /**
17
+ * ¿Esta ruta atiende esta URL?
18
+ *
19
+ * Sin expresiones regulares: se recorre segmento a segmento. Una regex construida
20
+ * a partir de un patrón es difícil de auditar y fácil de romper con un carácter
21
+ * que había que escapar — y el modelo anterior tenía justamente ese defecto:
22
+ * `/page1` matcheaba `/page1/cualquier/cosa` porque su regex no exigía consumir
23
+ * la URL entera.
24
+ */
25
+ function matchSegments(segments, parts) {
26
+ const params = {};
27
+ let index = 0;
28
+ for (const segment of segments) {
29
+ if (segment.kind === 'catchAll' || segment.kind === 'optionalCatchAll') {
30
+ const rest = parts.slice(index).map(decode);
31
+ // Un catch-all obligatorio necesita al menos un segmento; el opcional
32
+ // acepta ninguno y entrega una lista vacía.
33
+ if (segment.kind === 'catchAll' && rest.length === 0)
34
+ return undefined;
35
+ params[segment.value] = rest;
36
+ return params;
37
+ }
38
+ const part = parts[index];
39
+ if (part === undefined)
40
+ return undefined;
41
+ if (segment.kind === 'static') {
42
+ if (part !== segment.value)
43
+ return undefined;
44
+ }
45
+ else {
46
+ params[segment.value] = decode(part);
47
+ }
48
+ index++;
49
+ }
50
+ // La URL tiene que quedar consumida entera: lo que sobra no lo atiende esta ruta.
51
+ return index === parts.length ? params : undefined;
52
+ }
53
+ function decode(value) {
54
+ try {
55
+ return decodeURIComponent(value);
56
+ }
57
+ catch {
58
+ // Una secuencia de escape inválida no es motivo para fallar la petición: el
59
+ // valor crudo es lo que el cliente pidió.
60
+ return value;
61
+ }
62
+ }
63
+ /** La primera entrada que atiende esta URL, recorriendo en orden de precedencia. */
64
+ function matchEntries(entries, pathname, options = {}) {
65
+ const kinds = options.kinds ?? ['page'];
66
+ const parts = splitPath(pathname);
67
+ for (const entry of entries) {
68
+ if (!kinds.includes(entry.kind))
69
+ continue;
70
+ const params = matchSegments(entry.segments, parts);
71
+ if (params)
72
+ return { entry, params };
73
+ }
74
+ return undefined;
75
+ }
@@ -0,0 +1,31 @@
1
+ import type { RouteEntryKind, RouteSegment } from './types';
2
+ /**
3
+ * Convierte la ubicación de un archivo en un patrón de ruta.
4
+ *
5
+ * Notación:
6
+ *
7
+ * | En el archivo | En la URL |
8
+ * |---|---|
9
+ * | `users/perfil.tsx` | `/users/perfil` |
10
+ * | `users/index.tsx` | `/users` |
11
+ * | `users/[id].tsx` | `/users/:id` |
12
+ * | `docs/[...slug].tsx` | `/docs/` y todo lo que siga |
13
+ * | `shop/[[...filtros]].tsx` | `/shop` y todo lo que siga |
14
+ * | `(marketing)/about.tsx` | `/about` — el grupo no aporta segmento |
15
+ */
16
+ export interface ParsedRoute {
17
+ pattern: string;
18
+ segments: RouteSegment[];
19
+ paramNames: string[];
20
+ groups: string[];
21
+ }
22
+ export declare function parseRoutePattern(sourceFile: string, pagesDir?: string): ParsedRoute;
23
+ /**
24
+ * Identificador estable de una página.
25
+ *
26
+ * Es la misma clave que usa el catálogo de variantes de F-016, para que las dos
27
+ * estructuras hablen del mismo objeto sin traducir nada entre ellas.
28
+ */
29
+ export declare function pageIdFromFile(sourceFile: string, pagesDir?: string): string;
30
+ /** Qué clase de entrada es, según dónde vive el archivo. */
31
+ export declare function classifyEntry(sourceFile: string, pagesDir?: string): RouteEntryKind;