@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
@@ -0,0 +1,44 @@
1
+ import type { CollectedHeadTag, HeadAttributes } from '@ressjs/assets';
2
+ import type { PageRenderingConfig } from '../isr/types';
3
+ export type { CollectedHeadTag, HeadAttributes, HeadProps, HeadTagName } from '@ressjs/assets';
4
+ export interface PageHeadConfig {
5
+ title?: string;
6
+ robots?: RobotsConfig;
7
+ meta?: HeadAttributes[];
8
+ link?: HeadAttributes[];
9
+ script?: Array<HeadAttributes & {
10
+ content?: string;
11
+ }>;
12
+ style?: Array<HeadAttributes & {
13
+ content: string;
14
+ }>;
15
+ base?: HeadAttributes;
16
+ noscript?: Array<{
17
+ content: string;
18
+ }>;
19
+ }
20
+ export interface PageConfigVariant {
21
+ head?: PageHeadConfig;
22
+ htmlAttributes?: HeadAttributes;
23
+ bodyAttributes?: HeadAttributes;
24
+ }
25
+ export interface PageConfig extends PageConfigVariant, PageRenderingConfig {
26
+ /** Overrides selected per request with the same platform vocabulary as page assets. */
27
+ variants?: Record<string, PageConfigVariant>;
28
+ }
29
+ export type { PageRenderingConfig, RenderingMode } from '../isr/types';
30
+ export type RobotsConfig = string | {
31
+ index?: boolean;
32
+ follow?: boolean;
33
+ noarchive?: boolean;
34
+ nosnippet?: boolean;
35
+ noimageindex?: boolean;
36
+ notranslate?: boolean;
37
+ };
38
+ export interface ResolvedPageHead {
39
+ tags: CollectedHeadTag[];
40
+ htmlAttributes: HeadAttributes;
41
+ bodyAttributes: HeadAttributes;
42
+ /** Effective robots directive after pageConfig and <Head> have been merged. */
43
+ robots?: string;
44
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,14 +1,34 @@
1
- import { PlatformInfo } from '../platform';
1
+ import { type PlatformInfo } from '../platform';
2
+ import { type SecurityContext } from '../security';
3
+ import type { AssetIndex, VariantCatalog } from '../variants/types';
4
+ import type { ResolvedPageHead } from '../head/types';
5
+ export { resolvePageAssets } from '@ressjs/assets';
6
+ /**
7
+ * Qué archivos servirle a esta plataforma: elige la variante y busca sus artefactos.
8
+ *
9
+ * Son dos consultas en memoria. El catálogo ya está armado y los artefactos ya
10
+ * están indexados desde que arrancó el servidor, así que servir una página no
11
+ * toca el sistema de archivos ni escribe nada.
12
+ *
13
+ * Si la variante elegida no tiene artefactos construidos, responde los de la
14
+ * base antes que no responder nada.
15
+ */
2
16
  export interface HTMLGenerationContext {
3
17
  template: string;
4
18
  rendered: any;
5
19
  page: any;
6
- manifest: any;
7
20
  platform: string;
8
21
  isProduction: boolean;
9
22
  platformInfo: PlatformInfo | null;
10
- serverSideResult?: any;
23
+ pageHead?: ResolvedPageHead;
24
+ catalog?: VariantCatalog;
25
+ assets?: AssetIndex;
26
+ /** Request-scoped links prepared before rendering (and overridable by middleware). */
27
+ styles?: {
28
+ js: string;
29
+ css: string[];
30
+ };
31
+ /** `false`: HTML y CSS, sin script de entrada ni estado para el navegador. */
32
+ hydrate?: boolean;
11
33
  }
12
- export declare function generateClientScript(pageFile: string, platformInfo: PlatformInfo | null, manifest: any, isProduction: boolean): Promise<string | null>;
13
- export declare function generateClientCSS(pageFile: string, platformInfo: PlatformInfo | null, manifest: any, isProduction: boolean): string;
14
- export declare function generateCompleteHTML(context: HTMLGenerationContext): Promise<string>;
34
+ export declare function generateCompleteHTML(context: HTMLGenerationContext, security?: SecurityContext): Promise<string>;
@@ -1,146 +1,104 @@
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
- exports.generateClientScript = generateClientScript;
7
- exports.generateClientCSS = generateClientCSS;
3
+ exports.resolvePageAssets = void 0;
8
4
  exports.generateCompleteHTML = generateCompleteHTML;
9
- const promises_1 = __importDefault(require("node:fs/promises"));
10
- async function generateClientScript(pageFile, platformInfo, manifest, isProduction) {
11
- // Sanitizar nombre: convertir [id] a _id_ para nombres de archivo (igual que en pages.ts)
12
- const rel = pageFile
13
- .replace(/^app\/pages\//, '')
14
- .replace(/\.tsx$|\.jsx$/, '')
15
- .replace(/\//g, '__')
16
- .replace(/\[([^\]]+)\]/g, '_$1_');
17
- let entryKey = `${rel}__entry-client.tsx`;
18
- if (platformInfo && platformInfo.device !== 'desktop') {
19
- // Priority order (from highest to lowest):
20
- const priorities = [
21
- // 1. Specific: device.os.tsx
22
- `${rel}__entry-client.${platformInfo.device}.${platformInfo.os}.tsx`,
23
- // 2. DeviceType: device.tsx
24
- `${rel}__entry-client.${platformInfo.device}.tsx`,
25
- // 3. Mobile Fallback: mobile.tsx (only for phone/tablet)
26
- ...(platformInfo.device === 'phone' || platformInfo.device === 'tablet'
27
- ? [`${rel}__entry-client.mobile.tsx`]
28
- : []),
29
- // 4. OS General: os.tsx
30
- `${rel}__entry-client.${platformInfo.os}.tsx`
31
- ];
32
- // Find first existing entry following priority order
33
- for (const candidate of priorities) {
34
- if (isProduction) {
35
- // In production, check manifest
36
- const manifestKey = `dist/.entries/${candidate}`;
37
- if (manifest[manifestKey]) {
38
- entryKey = candidate;
39
- break;
40
- }
41
- }
42
- else {
43
- // In development, check if physical file exists
44
- try {
45
- await promises_1.default.access(`dist/.entries/${candidate}`);
46
- entryKey = candidate;
47
- break;
48
- }
49
- catch {
50
- // File doesn't exist, continue to next priority
51
- }
52
- }
53
- }
54
- }
55
- if (!isProduction) {
56
- return `/dist/.entries/${entryKey}`;
57
- }
58
- else {
59
- const chunk = manifest[`dist/.entries/${entryKey}`];
60
- return chunk ? `/${chunk.file}` : null;
61
- }
5
+ const platform_1 = require("../platform");
6
+ const security_1 = require("../security");
7
+ const assets_1 = require("@ressjs/assets");
8
+ var assets_2 = require("@ressjs/assets");
9
+ Object.defineProperty(exports, "resolvePageAssets", { enumerable: true, get: function () { return assets_2.resolvePageAssets; } });
10
+ /**
11
+ * Plataforma que se asume cuando la detección está desactivada.
12
+ *
13
+ * Es la misma que produce el pipeline cuando nadie declaró nada, de modo que
14
+ * apagar la detección y recibir una petición anónima den el mismo resultado.
15
+ */
16
+ function fallbackPlatform() {
17
+ return platform_1.DEFAULT_PLATFORM;
62
18
  }
63
- function generateClientCSS(pageFile, platformInfo, manifest, isProduction) {
64
- if (!isProduction || !pageFile) {
65
- return '';
66
- }
67
- // Sanitizar nombre: convertir [id] a _id_ para nombres de archivo (igual que en pages.ts)
68
- const rel = pageFile
69
- .replace(/^app\/pages\//, '')
70
- .replace(/\.tsx$|\.jsx$/, '')
71
- .replace(/\//g, '__')
72
- .replace(/\[([^\]]+)\]/g, '_$1_');
73
- let entryKey = `dist/.entries/${rel}__entry-client.tsx`;
74
- if (platformInfo && platformInfo.device !== 'desktop') {
75
- // Priority order (from highest to lowest):
76
- const priorities = [
77
- // 1. Specific: device.os.tsx
78
- `dist/.entries/${rel}__entry-client.${platformInfo.device}.${platformInfo.os}.tsx`,
79
- // 2. DeviceType: device.tsx
80
- `dist/.entries/${rel}__entry-client.${platformInfo.device}.tsx`,
81
- // 3. Mobile Fallback: mobile.tsx (only for phone/tablet)
82
- ...(platformInfo.device === 'phone' || platformInfo.device === 'tablet'
83
- ? [`dist/.entries/${rel}__entry-client.mobile.tsx`]
84
- : []),
85
- // 4. OS General: os.tsx
86
- `dist/.entries/${rel}__entry-client.${platformInfo.os}.tsx`
87
- ];
88
- // Find first existing entry following priority order
89
- for (const candidate of priorities) {
90
- if (manifest[candidate]) {
91
- entryKey = candidate;
92
- break;
93
- }
94
- }
95
- }
96
- const entryChunk = manifest[entryKey];
97
- if (entryChunk && entryChunk.css) {
98
- return entryChunk.css.map((cssFile) => `<link rel="stylesheet" href="/${cssFile}">`).join('');
19
+ async function generateCompleteHTML(context, security) {
20
+ const { template, rendered, page, platformInfo, pageHead, catalog, assets, hydrate = true } = context;
21
+ const ctx = security ?? {
22
+ isProduction: context.isProduction,
23
+ options: (0, security_1.resolveSecurityOptions)(),
24
+ route: page?.route ?? '',
25
+ };
26
+ const escapeCtx = { where: '', isProduction: ctx.isProduction, route: ctx.route };
27
+ const nonce = ctx.nonce;
28
+ const platform = platformInfo ?? fallbackPlatform();
29
+ const resolved = context.styles ?? (catalog && assets && page?.pageId
30
+ ? (0, assets_1.resolvePageAssets)(page.pageId, platform, catalog, assets)
31
+ : { js: '', css: [] });
32
+ const scriptTag = hydrate && resolved.js
33
+ ? `<script type="module"${nonceAttr(nonce)} src="${(0, security_1.escapeAttrValue)(resolved.js)}"></script>`
34
+ : '';
35
+ const cssLinks = resolved.css
36
+ .map((href) => `<link rel="stylesheet" href="${(0, security_1.escapeAttrValue)(href)}">`)
37
+ .join('');
38
+ const pageTags = pageHead?.tags.length
39
+ ? (0, security_1.renderHeadTags)(pageHead.tags.map((tag) => ({ tag: tag.type, attrs: tag.attrs, children: tag.content })), escapeCtx, {
40
+ // Page head declarations are application source, like the hydration
41
+ // script itself. CSP still controls whether inline code executes.
42
+ allowScriptTags: ctx.options.head.allowScriptTags,
43
+ extraAllowlist: ['noscript', ...ctx.options.head.extraTagAllowlist],
44
+ nonce,
45
+ })
46
+ : '';
47
+ const headContent = [cssLinks, pageTags].filter(Boolean).join('\n');
48
+ // La plataforma sólo se escribe si la página varía por algo. Si no varía, el
49
+ // documento tiene que ser idéntico para todos —es lo que lo hace cacheable— y
50
+ // el cliente la detecta por su cuenta.
51
+ const varies = (ctx.varyBy?.length ?? 0) > 0;
52
+ const platformTravels = ctx.options.serializePlatform === 'always' ||
53
+ (ctx.options.serializePlatform === 'auto' && varies);
54
+ // El estado que viaja. `__RESS_PAGE__` sólo en desarrollo: es una ruta del
55
+ // sistema de archivos del proyecto y no le aporta nada al cliente.
56
+ //
57
+ // Una página que no hidrata no lleva estado: nadie lo va a leer, y un
58
+ // documento sin JavaScript de página es justamente lo que pidió.
59
+ const blocks = !hydrate ? [] : [
60
+ ...(platformTravels ? [{ varName: '__RESS_PLATFORM__', value: platform }] : []),
61
+ { varName: '__RESS_PROPS__', value: rendered.props || {} },
62
+ { varName: '__RESS_ROUTE__', value: page?.route ?? '' },
63
+ ...(ctx.isProduction ? [] : [{ varName: '__RESS_PAGE__', value: page?.file ?? '' }]),
64
+ ];
65
+ const stateScript = blocks.length ? (0, security_1.renderStateScript)(blocks, nonce) : '';
66
+ let html = template;
67
+ if (pageHead?.htmlAttributes)
68
+ html = applyRootAttributes(html, 'html', pageHead.htmlAttributes, escapeCtx);
69
+ if (pageHead?.bodyAttributes)
70
+ html = applyRootAttributes(html, 'body', pageHead.bodyAttributes, escapeCtx);
71
+ if (pageHead?.tags.some((tag) => tag.type === 'title')) {
72
+ html = html.replace(/<title(?:\s[^>]*)?>[\s\S]*?<\/title>/i, '');
99
73
  }
100
- return '';
74
+ // Los tres valores insertados acá son contenido dinámico: si se pasaran
75
+ // como string de reemplazo, `$$`, `$&`, `` $` `` y `$'` se interpretarían
76
+ // como patrones especiales de `String.replace` y corromperían el documento.
77
+ // La forma función evita esa interpretación.
78
+ return html
79
+ .replace('<!--app-head-->', () => headContent)
80
+ .replace('<!--app-html-->', () => rendered.html)
81
+ .replace('</body>', () => `${stateScript}${scriptTag}</body>`);
101
82
  }
102
- async function generateCompleteHTML(context) {
103
- const { template, rendered, page, manifest, isProduction, platformInfo, serverSideResult } = context;
104
- const scriptSrc = await generateClientScript(page.file, platformInfo, manifest, isProduction);
105
- const scriptTag = scriptSrc ? `<script type="module" src="${scriptSrc}"></script>` : '';
106
- const cssLinks = page?.file ? generateClientCSS(page.file, platformInfo, manifest, isProduction) : '';
107
- // Construir contenido HEAD extra y atributos desde serverSideResult
108
- let headContent = cssLinks;
109
- let htmlAttributes = '';
110
- let bodyAttributes = '';
111
- let pageTitle = '';
112
- if (serverSideResult?.html) {
113
- if (serverSideResult.html.head?.attributes) {
114
- htmlAttributes = Object.entries(serverSideResult.html.head.attributes)
115
- .map(([k, v]) => `${k}="${v}"`).join(' ');
116
- }
117
- if (serverSideResult.html.body?.attributes) {
118
- bodyAttributes = Object.entries(serverSideResult.html.body.attributes)
119
- .map(([k, v]) => `${k}="${v}"`).join(' ');
120
- }
121
- if (serverSideResult.html.head?.extraTags) {
122
- headContent += '\n' + serverSideResult.html.head.extraTags.join('\n');
83
+ const nonceAttr = (nonce) => (nonce ? ` nonce="${(0, security_1.escapeAttrValue)(nonce)}"` : '');
84
+ /**
85
+ * Acepta la forma estructurada y descarta el string de HTML crudo.
86
+ *
87
+ * Un array de strings no se puede filtrar sin analizar HTML, y analizarlo para
88
+ * decidir si es seguro es exactamente el problema que la lista de etiquetas
89
+ * admitidas evita. F-004 ya produce la forma estructurada.
90
+ */
91
+ function applyRootAttributes(html, element, attrs, ctx) {
92
+ return html.replace(new RegExp('<' + element + '([^>]*)>', 'i'), (_opening, existing) => {
93
+ let merged = existing;
94
+ for (const [name, value] of Object.entries(attrs)) {
95
+ const rendered = (0, security_1.renderAttrs)({ [name]: value }, { ...ctx, where: '<' + element + '>' });
96
+ if (!rendered)
97
+ continue;
98
+ const escapedName = name.replace(/[.*+?^$()|[\]\\{}]/g, '\\$&');
99
+ const present = new RegExp('\\s' + escapedName + '(?:\\s*=\\s*(?:"[^"]*"|\'[^\']*\'|[^\\s>]+))?', 'i');
100
+ merged = present.test(merged) ? merged.replace(present, () => ' ' + rendered) : merged + ' ' + rendered;
123
101
  }
124
- if (serverSideResult.title || serverSideResult.html.head?.title) {
125
- pageTitle = `<title>${serverSideResult.title || serverSideResult.html.head?.title}</title>`;
126
- }
127
- }
128
- let html = template;
129
- if (htmlAttributes)
130
- html = html.replace('<html', `<html ${htmlAttributes}`);
131
- if (bodyAttributes)
132
- html = html.replace('<body', `<body ${bodyAttributes}`);
133
- if (pageTitle)
134
- html = html.replace(/<title>.*?<\/title>/i, pageTitle);
135
- html = html
136
- .replace('<!--app-head-->', headContent)
137
- .replace('<!--app-html-->', rendered.html)
138
- .replace('</body>', `<script>
139
- window.__INITIAL_PROPS__=${JSON.stringify(rendered.props || {})};
140
- window.__PAGE_ROUTE__=${JSON.stringify(page.route)};
141
- window.__PAGE_FILE__=${JSON.stringify(page.file)};
142
- </script>
143
- ${scriptTag}
144
- </body>`);
145
- return html;
102
+ return '<' + element + merged + '>';
103
+ });
146
104
  }
@@ -1,65 +1,76 @@
1
1
  import { Request, RequestHandler, Response } from 'express';
2
+ import type { ModuleLoader } from '../runtime/module-loader';
3
+ import type { MiddlewareEntry } from '../routes/types';
4
+ import type { PageConfig } from '../head/types';
5
+ export type { PageConfig, PageConfigVariant, PageHeadConfig, RobotsConfig } from '../head/types';
2
6
  export interface MiddlewareInfo {
3
7
  route: string;
4
8
  file: string;
5
9
  abs: string;
6
10
  type: 'page-specific' | 'folder-global';
7
11
  depth: number;
12
+ /** Chunk de servidor registrado por el build. Sólo en producción. */
13
+ buildChunk?: string;
8
14
  }
15
+ /**
16
+ * Si un middleware de carpeta con alcance `scope` aplica a `route`.
17
+ *
18
+ * Se compara por segmentos: `admin/middlewares.ts` protege `/admin` y
19
+ * `/admin/x`, pero no `/administrator`, que sólo comparte el prefijo de texto.
20
+ * La raíz aplica a todo.
21
+ */
22
+ export declare function middlewareAppliesTo(scope: string, route: string): boolean;
23
+ /** Lo que el build escribe en el manifest por cada middleware. */
24
+ export declare function toMiddlewareEntry(info: MiddlewareInfo): MiddlewareEntry;
25
+ /** Los middlewares tal como los registró el build, listos para cargar. */
26
+ export declare function middlewaresFromManifest(entries: readonly MiddlewareEntry[]): MiddlewareInfo[];
9
27
  export interface ServerSideResult {
10
28
  props?: Record<string, any>;
11
- title?: string;
12
- html?: {
13
- head?: {
14
- title?: string;
15
- attributes?: Record<string, string>;
16
- extraTags?: string[];
17
- };
18
- body?: {
19
- attributes?: Record<string, string>;
20
- };
21
- };
29
+ pageConfig?: PageConfig;
22
30
  }
23
31
  export interface PageModule {
24
32
  default?: React.ComponentType<any>;
33
+ /** Configuración estática: el router la lee al armar la ruta, antes de cualquier petición. */
34
+ pageConfig?: PageConfig;
25
35
  getServerSideProps?: (req: Request, res: Response) => Promise<ServerSideResult> | ServerSideResult;
26
36
  errorMiddleware?: (err: any, req: Request, res: Response, next: any) => void;
27
37
  }
28
38
  /**
29
39
  * Busca archivos de middlewares tanto específicos por página como globales por carpeta
30
40
  */
31
- export declare function getMiddlewares(): MiddlewareInfo[];
41
+ export declare function getMiddlewares(options?: {
42
+ cwd?: string;
43
+ }): MiddlewareInfo[];
32
44
  /**
33
45
  * Carga middlewares para una página específica y los convierte en middlewares de Express
34
46
  */
35
- export declare function loadPageMiddlewares(pageRoute: string, vite?: any): Promise<RequestHandler[]>;
47
+ export declare function loadPageMiddlewares(pageRoute: string, loader?: ModuleLoader, middlewares?: readonly MiddlewareInfo[]): Promise<RequestHandler[]>;
36
48
  /**
37
49
  * Carga el módulo completo de una página
38
50
  */
39
- export declare function loadPageModule(pageFile: string, vite?: any): Promise<PageModule | null>;
51
+ export declare function loadPageModule(pageFile: string, loader?: ModuleLoader): Promise<PageModule | null>;
40
52
  /**
41
53
  * Convierte getServerSideProps en un middleware de Express
42
54
  */
43
- export declare function createGetServerSidePropsMiddleware(pageModule: PageModule): RequestHandler;
55
+ export declare function createGetServerSidePropsMiddleware(pageModule: PageModule, file?: string): RequestHandler;
44
56
  /**
45
57
  * Creates a unified error handler for a specific page route
46
58
  * This replaces the old createPageErrorMiddleware with a more useful approach
47
59
  */
48
- export declare function createPageErrorHandler(pageFile: string, vite?: any): (err: any, req: any, res: any, next: any) => Promise<void>;
60
+ export declare function createPageErrorHandler(pageFile: string, loader?: ModuleLoader, middlewares?: readonly MiddlewareInfo[]): (err: any, req: any, res: any, next: any) => Promise<any>;
49
61
  /**
50
62
  * Creates a dynamic middleware handler that reloads middlewares on each request in development
51
63
  * This enables HMR for middlewares
52
64
  * I FUCKING HATE THIS FUNCTION BUT IT SEEMS TO WORK
53
65
  */
54
- export declare function createDynamicPageMiddlewares(pageRoute: string, pageFile: string, vite?: any): RequestHandler;
55
66
  /**
56
67
  * 1. Resuelve middlewares previos al getServerSideProps
57
68
  */
58
- export declare function getPagePrevMiddlewares(pageRoute: string, vite?: any): Promise<RequestHandler[]>;
69
+ export declare function getPagePrevMiddlewares(pageRoute: string, loader?: ModuleLoader): Promise<RequestHandler[]>;
59
70
  /**
60
71
  * 2. Convierte getServerSideProps a middleware
61
72
  */
62
- export declare function getServerSidePropsAsMiddleware(pageFile: string, vite?: any): Promise<RequestHandler>;
73
+ export declare function getServerSidePropsAsMiddleware(pageFile: string, loader?: ModuleLoader): Promise<RequestHandler>;
63
74
  /**
64
75
  * 3. Error middleware con fallback automático y jerarquía
65
76
  * Orden de prioridad (el más específico gana):
@@ -67,6 +78,6 @@ export declare function getServerSidePropsAsMiddleware(pageFile: string, vite?:
67
78
  * 2. errorMiddleware en [page].middlewares.ts
68
79
  * 3. errorMiddleware en carpeta/middlewares.ts
69
80
  * 4. errorMiddleware en root middlewares.ts (más general)
70
- * 5. defaultErrorMiddleware (fallback)
81
+ * 5. ninguno: null, para que el llamador delegue con next(err)
71
82
  */
72
- export declare function getErrorMiddleware(pageFile: string, vite?: any): Promise<any>;
83
+ export declare function getErrorMiddleware(pageFile: string, loader?: ModuleLoader, middlewares?: readonly MiddlewareInfo[]): Promise<any>;