@reckona/mreact-router 0.0.203 → 0.0.205

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 (140) hide show
  1. package/README.md +53 -9
  2. package/dist/actions.d.ts +6 -0
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +70 -10
  5. package/dist/actions.js.map +1 -1
  6. package/dist/adapters/aws-lambda.d.ts +15 -4
  7. package/dist/adapters/aws-lambda.d.ts.map +1 -1
  8. package/dist/adapters/aws-lambda.js +301 -63
  9. package/dist/adapters/aws-lambda.js.map +1 -1
  10. package/dist/adapters/cloudflare.d.ts +2 -1
  11. package/dist/adapters/cloudflare.d.ts.map +1 -1
  12. package/dist/adapters/cloudflare.js +18 -10
  13. package/dist/adapters/cloudflare.js.map +1 -1
  14. package/dist/adapters/edge.d.ts +1 -1
  15. package/dist/adapters/edge.d.ts.map +1 -1
  16. package/dist/adapters/edge.js.map +1 -1
  17. package/dist/adapters/node.d.ts +5 -3
  18. package/dist/adapters/node.d.ts.map +1 -1
  19. package/dist/adapters/node.js +2 -1
  20. package/dist/adapters/node.js.map +1 -1
  21. package/dist/adapters/static.d.ts.map +1 -1
  22. package/dist/adapters/static.js +24 -2
  23. package/dist/adapters/static.js.map +1 -1
  24. package/dist/build.d.ts +1 -0
  25. package/dist/build.d.ts.map +1 -1
  26. package/dist/build.js +91 -5
  27. package/dist/build.js.map +1 -1
  28. package/dist/built-runtime.d.ts +2 -0
  29. package/dist/built-runtime.d.ts.map +1 -1
  30. package/dist/built-runtime.js +16 -0
  31. package/dist/built-runtime.js.map +1 -1
  32. package/dist/cache.d.ts +3 -1
  33. package/dist/cache.d.ts.map +1 -1
  34. package/dist/cache.js +211 -13
  35. package/dist/cache.js.map +1 -1
  36. package/dist/cli.js +12 -1
  37. package/dist/cli.js.map +1 -1
  38. package/dist/client.d.ts.map +1 -1
  39. package/dist/client.js +32 -1
  40. package/dist/client.js.map +1 -1
  41. package/dist/config.d.ts +3 -0
  42. package/dist/config.d.ts.map +1 -1
  43. package/dist/config.js +21 -2
  44. package/dist/config.js.map +1 -1
  45. package/dist/csp.d.ts +1 -0
  46. package/dist/csp.d.ts.map +1 -1
  47. package/dist/csp.js +11 -6
  48. package/dist/csp.js.map +1 -1
  49. package/dist/dehydrate-policy.d.ts +3 -0
  50. package/dist/dehydrate-policy.d.ts.map +1 -0
  51. package/dist/dehydrate-policy.js +16 -0
  52. package/dist/dehydrate-policy.js.map +1 -0
  53. package/dist/dev-server.d.ts +7 -2
  54. package/dist/dev-server.d.ts.map +1 -1
  55. package/dist/dev-server.js +120 -11
  56. package/dist/dev-server.js.map +1 -1
  57. package/dist/http.d.ts +1 -1
  58. package/dist/http.d.ts.map +1 -1
  59. package/dist/http.js +147 -7
  60. package/dist/http.js.map +1 -1
  61. package/dist/index.d.ts +4 -3
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +1 -0
  64. package/dist/index.js.map +1 -1
  65. package/dist/logger.d.ts +22 -1
  66. package/dist/logger.d.ts.map +1 -1
  67. package/dist/logger.js +22 -8
  68. package/dist/logger.js.map +1 -1
  69. package/dist/metadata.d.ts +8 -0
  70. package/dist/metadata.d.ts.map +1 -1
  71. package/dist/metadata.js +60 -22
  72. package/dist/metadata.js.map +1 -1
  73. package/dist/navigation-marker.d.ts.map +1 -1
  74. package/dist/navigation-marker.js +72 -9
  75. package/dist/navigation-marker.js.map +1 -1
  76. package/dist/navigation.d.ts +1 -0
  77. package/dist/navigation.d.ts.map +1 -1
  78. package/dist/navigation.js +1 -1
  79. package/dist/navigation.js.map +1 -1
  80. package/dist/node-server.d.ts +5 -2
  81. package/dist/node-server.d.ts.map +1 -1
  82. package/dist/node-server.js +75 -6
  83. package/dist/node-server.js.map +1 -1
  84. package/dist/prerender-entry.d.ts +14 -0
  85. package/dist/prerender-entry.d.ts.map +1 -1
  86. package/dist/prerender-entry.js +40 -3
  87. package/dist/prerender-entry.js.map +1 -1
  88. package/dist/render.d.ts +9 -1
  89. package/dist/render.d.ts.map +1 -1
  90. package/dist/render.js +102 -123
  91. package/dist/render.js.map +1 -1
  92. package/dist/serve.d.ts +8 -2
  93. package/dist/serve.d.ts.map +1 -1
  94. package/dist/serve.js +22 -20
  95. package/dist/serve.js.map +1 -1
  96. package/dist/session.d.ts.map +1 -1
  97. package/dist/session.js +52 -19
  98. package/dist/session.js.map +1 -1
  99. package/dist/upgrade.d.ts +59 -2
  100. package/dist/upgrade.d.ts.map +1 -1
  101. package/dist/upgrade.js +282 -1
  102. package/dist/upgrade.js.map +1 -1
  103. package/dist/vite-config.d.ts +2 -0
  104. package/dist/vite-config.d.ts.map +1 -1
  105. package/dist/vite-config.js +2 -0
  106. package/dist/vite-config.js.map +1 -1
  107. package/dist/vite.d.ts +4 -1
  108. package/dist/vite.d.ts.map +1 -1
  109. package/dist/vite.js +117 -6
  110. package/dist/vite.js.map +1 -1
  111. package/package.json +11 -11
  112. package/src/actions.ts +81 -11
  113. package/src/adapters/aws-lambda.ts +426 -115
  114. package/src/adapters/cloudflare.ts +29 -13
  115. package/src/adapters/edge.ts +2 -0
  116. package/src/adapters/node.ts +8 -10
  117. package/src/adapters/static.ts +31 -2
  118. package/src/build.ts +117 -10
  119. package/src/built-runtime.ts +28 -0
  120. package/src/cache.ts +262 -15
  121. package/src/cli.ts +14 -1
  122. package/src/client.ts +32 -1
  123. package/src/config.ts +43 -6
  124. package/src/csp.ts +14 -11
  125. package/src/dehydrate-policy.ts +23 -0
  126. package/src/dev-server.ts +188 -15
  127. package/src/http.ts +172 -6
  128. package/src/index.ts +14 -1
  129. package/src/logger.ts +47 -10
  130. package/src/metadata.ts +125 -60
  131. package/src/navigation-marker.ts +79 -9
  132. package/src/navigation.ts +1 -1
  133. package/src/node-server.ts +128 -22
  134. package/src/prerender-entry.ts +52 -3
  135. package/src/render.ts +807 -753
  136. package/src/serve.ts +49 -56
  137. package/src/session.ts +56 -25
  138. package/src/upgrade.ts +402 -3
  139. package/src/vite-config.ts +6 -0
  140. package/src/vite.ts +157 -5
@@ -12,6 +12,8 @@ const rawTextElementNames = new Set([
12
12
  ]);
13
13
  export function hasNavigationRouteMarker(html) {
14
14
  let cursor = 0;
15
+ let foreignContentDepth = 0;
16
+ let templateDepth = 0;
15
17
  while (cursor < html.length) {
16
18
  const openingBracket = html.indexOf("<", cursor);
17
19
  if (openingBracket < 0) {
@@ -22,11 +24,26 @@ export function hasNavigationRouteMarker(html) {
22
24
  continue;
23
25
  }
24
26
  if (html.startsWith("<![CDATA[", openingBracket)) {
25
- cursor = skipCdataSection(html, openingBracket + 9);
27
+ cursor =
28
+ foreignContentDepth > 0
29
+ ? skipCdataSection(html, openingBracket + 9)
30
+ : skipBogusComment(html, openingBracket + 2);
26
31
  continue;
27
32
  }
28
33
  const discriminator = html[openingBracket + 1];
29
- if (discriminator === "!" || discriminator === "?" || discriminator === "/") {
34
+ if (discriminator === "/") {
35
+ const endTag = parseEndTag(html, openingBracket);
36
+ if (endTag.name === "template" && templateDepth > 0) {
37
+ templateDepth -= 1;
38
+ }
39
+ else if ((endTag.name === "svg" || endTag.name === "math") &&
40
+ foreignContentDepth > 0) {
41
+ foreignContentDepth -= 1;
42
+ }
43
+ cursor = endTag.end;
44
+ continue;
45
+ }
46
+ if (discriminator === "!" || discriminator === "?") {
30
47
  cursor = skipTagTail(html, openingBracket + 2);
31
48
  continue;
32
49
  }
@@ -39,16 +56,27 @@ export function hasNavigationRouteMarker(html) {
39
56
  cursor = openingBracket + 1;
40
57
  continue;
41
58
  }
42
- if (tag.hasMarker) {
59
+ if (tag.hasMarker &&
60
+ templateDepth === 0 &&
61
+ tag.name !== "html" &&
62
+ tag.name !== "head" &&
63
+ tag.name !== "body" &&
64
+ tag.name !== "frameset") {
43
65
  return true;
44
66
  }
45
67
  if (tag.name === "plaintext") {
46
68
  return false;
47
69
  }
70
+ if (!tag.selfClosing && tag.name === "template") {
71
+ templateDepth += 1;
72
+ }
73
+ else if (!tag.selfClosing && (tag.name === "svg" || tag.name === "math")) {
74
+ foreignContentDepth += 1;
75
+ }
48
76
  cursor =
49
77
  tag.name === "script"
50
78
  ? skipScriptElement(html, tag.end)
51
- : rawTextElementNames.has(tag.name)
79
+ : foreignContentDepth === 0 && rawTextElementNames.has(tag.name)
52
80
  ? skipRawTextElement(html, tag.end, tag.name)
53
81
  : tag.end;
54
82
  }
@@ -71,13 +99,23 @@ function parseStartTag(html, openingBracket) {
71
99
  for (;;) {
72
100
  cursor = skipAsciiWhitespace(html, cursor);
73
101
  if (cursor >= html.length) {
74
- return { end: html.length, hasMarker: false, name };
102
+ return { end: html.length, hasMarker: false, name, selfClosing: false };
75
103
  }
76
104
  if (html[cursor] === ">") {
77
- return { end: cursor + 1, hasMarker: markerValueIsNonEmpty === true, name };
105
+ return {
106
+ end: cursor + 1,
107
+ hasMarker: markerValueIsNonEmpty === true,
108
+ name,
109
+ selfClosing: false,
110
+ };
78
111
  }
79
112
  if (html[cursor] === "/" && html[cursor + 1] === ">") {
80
- return { end: cursor + 2, hasMarker: markerValueIsNonEmpty === true, name };
113
+ return {
114
+ end: cursor + 2,
115
+ hasMarker: markerValueIsNonEmpty === true,
116
+ name,
117
+ selfClosing: true,
118
+ };
81
119
  }
82
120
  if (html[cursor] === "/") {
83
121
  return invalidStartTag(html, cursor, name);
@@ -102,7 +140,7 @@ function parseStartTag(html, openingBracket) {
102
140
  const valueStart = cursor + 1;
103
141
  const valueEnd = html.indexOf(quote, valueStart);
104
142
  if (valueEnd < 0) {
105
- return { end: html.length, hasMarker: false, name };
143
+ return { end: html.length, hasMarker: false, name, selfClosing: false };
106
144
  }
107
145
  valueIsNonEmpty = valueEnd > valueStart;
108
146
  cursor = valueEnd + 1;
@@ -127,14 +165,39 @@ function parseStartTag(html, openingBracket) {
127
165
  }
128
166
  }
129
167
  function invalidStartTag(html, cursor, name) {
130
- return { end: skipTagTail(html, cursor), hasMarker: false, name };
168
+ return { end: skipMalformedTagTail(html, cursor), hasMarker: false, name, selfClosing: false };
131
169
  }
132
170
  function skipComment(html, cursor) {
171
+ if (html[cursor] === ">") {
172
+ return cursor + 1;
173
+ }
174
+ if (html.startsWith("->", cursor)) {
175
+ return cursor + 2;
176
+ }
133
177
  const standardEnd = html.indexOf("-->", cursor);
134
178
  const abruptEnd = html.indexOf("--!>", cursor);
135
179
  const end = standardEnd < 0 ? abruptEnd : abruptEnd < 0 ? standardEnd : Math.min(standardEnd, abruptEnd);
136
180
  return end < 0 ? html.length : end + (end === abruptEnd ? 4 : 3);
137
181
  }
182
+ function skipBogusComment(html, cursor) {
183
+ const end = html.indexOf(">", cursor);
184
+ return end < 0 ? html.length : end + 1;
185
+ }
186
+ function parseEndTag(html, openingBracket) {
187
+ let cursor = openingBracket + 2;
188
+ const nameStart = cursor;
189
+ while (isAsciiLetter(html[cursor])) {
190
+ cursor += 1;
191
+ }
192
+ return {
193
+ end: skipTagTail(html, cursor),
194
+ name: html.slice(nameStart, cursor).toLowerCase(),
195
+ };
196
+ }
197
+ function skipMalformedTagTail(html, cursor) {
198
+ const end = html.indexOf(">", cursor);
199
+ return end < 0 ? html.length : end + 1;
200
+ }
138
201
  function skipCdataSection(html, cursor) {
139
202
  const end = html.indexOf("]]>", cursor);
140
203
  return end < 0 ? html.length : end + 3;
@@ -1 +1 @@
1
- {"version":3,"file":"navigation-marker.js","sourceRoot":"","sources":["../src/navigation-marker.ts"],"names":[],"mappings":"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","sourcesContent":["import { routeHydrationContract } from \"./route-hydration-contract.js\";\n\nconst markerAttribute = routeHydrationContract.routeMarkerAttribute;\nconst rawTextElementNames = new Set([\n \"iframe\",\n \"noembed\",\n \"noframes\",\n \"noscript\",\n \"style\",\n \"textarea\",\n \"title\",\n \"xmp\",\n]);\n\ninterface ParsedStartTag {\n end: number;\n hasMarker: boolean;\n name: string;\n}\n\nexport function hasNavigationRouteMarker(html: string): boolean {\n let cursor = 0;\n\n while (cursor < html.length) {\n const openingBracket = html.indexOf(\"<\", cursor);\n if (openingBracket < 0) {\n return false;\n }\n\n if (html.startsWith(\"<!--\", openingBracket)) {\n cursor = skipComment(html, openingBracket + 4);\n continue;\n }\n\n if (html.startsWith(\"<![CDATA[\", openingBracket)) {\n cursor = skipCdataSection(html, openingBracket + 9);\n continue;\n }\n\n const discriminator = html[openingBracket + 1];\n if (discriminator === \"!\" || discriminator === \"?\" || discriminator === \"/\") {\n cursor = skipTagTail(html, openingBracket + 2);\n continue;\n }\n if (!isAsciiLetter(discriminator)) {\n cursor = openingBracket + 1;\n continue;\n }\n\n const tag = parseStartTag(html, openingBracket);\n if (tag === undefined) {\n cursor = openingBracket + 1;\n continue;\n }\n if (tag.hasMarker) {\n return true;\n }\n\n if (tag.name === \"plaintext\") {\n return false;\n }\n\n cursor =\n tag.name === \"script\"\n ? skipScriptElement(html, tag.end)\n : rawTextElementNames.has(tag.name)\n ? skipRawTextElement(html, tag.end, tag.name)\n : tag.end;\n }\n\n return false;\n}\n\nfunction parseStartTag(html: string, openingBracket: number): ParsedStartTag | undefined {\n let cursor = openingBracket + 1;\n const nameStart = cursor;\n\n while (cursor < html.length && !isTagNameTerminator(html[cursor])) {\n if (isForbiddenTagNameCharacter(html[cursor])) {\n return invalidStartTag(html, cursor, \"\");\n }\n cursor += 1;\n }\n\n if (cursor === nameStart) {\n return undefined;\n }\n\n const name = html.slice(nameStart, cursor).toLowerCase();\n let markerValueIsNonEmpty: boolean | undefined;\n\n for (;;) {\n cursor = skipAsciiWhitespace(html, cursor);\n if (cursor >= html.length) {\n return { end: html.length, hasMarker: false, name };\n }\n if (html[cursor] === \">\") {\n return { end: cursor + 1, hasMarker: markerValueIsNonEmpty === true, name };\n }\n if (html[cursor] === \"/\" && html[cursor + 1] === \">\") {\n return { end: cursor + 2, hasMarker: markerValueIsNonEmpty === true, name };\n }\n if (html[cursor] === \"/\") {\n return invalidStartTag(html, cursor, name);\n }\n\n const attributeNameStart = cursor;\n while (cursor < html.length && !isAttributeNameTerminator(html[cursor])) {\n if (isForbiddenAttributeNameCharacter(html[cursor])) {\n return invalidStartTag(html, cursor, name);\n }\n cursor += 1;\n }\n if (cursor === attributeNameStart) {\n return invalidStartTag(html, cursor, name);\n }\n\n const attributeName = html.slice(attributeNameStart, cursor).toLowerCase();\n cursor = skipAsciiWhitespace(html, cursor);\n let valueIsNonEmpty = false;\n\n if (html[cursor] === \"=\") {\n cursor = skipAsciiWhitespace(html, cursor + 1);\n const quote = html[cursor];\n\n if (quote === '\"' || quote === \"'\") {\n const valueStart = cursor + 1;\n const valueEnd = html.indexOf(quote, valueStart);\n if (valueEnd < 0) {\n return { end: html.length, hasMarker: false, name };\n }\n valueIsNonEmpty = valueEnd > valueStart;\n cursor = valueEnd + 1;\n } else {\n const valueStart = cursor;\n while (cursor < html.length && !isUnquotedValueTerminator(html[cursor])) {\n if (isForbiddenUnquotedValueCharacter(html[cursor])) {\n return invalidStartTag(html, cursor, name);\n }\n cursor += 1;\n }\n if (cursor === valueStart) {\n return invalidStartTag(html, cursor, name);\n }\n valueIsNonEmpty = true;\n }\n }\n\n if (attributeName === markerAttribute && markerValueIsNonEmpty === undefined) {\n markerValueIsNonEmpty = valueIsNonEmpty;\n }\n }\n}\n\nfunction invalidStartTag(html: string, cursor: number, name: string): ParsedStartTag {\n return { end: skipTagTail(html, cursor), hasMarker: false, name };\n}\n\nfunction skipComment(html: string, cursor: number): number {\n const standardEnd = html.indexOf(\"-->\", cursor);\n const abruptEnd = html.indexOf(\"--!>\", cursor);\n const end =\n standardEnd < 0 ? abruptEnd : abruptEnd < 0 ? standardEnd : Math.min(standardEnd, abruptEnd);\n return end < 0 ? html.length : end + (end === abruptEnd ? 4 : 3);\n}\n\nfunction skipCdataSection(html: string, cursor: number): number {\n const end = html.indexOf(\"]]>\", cursor);\n return end < 0 ? html.length : end + 3;\n}\n\nfunction skipTagTail(html: string, cursor: number): number {\n let quote: '\"' | \"'\" | undefined;\n while (cursor < html.length) {\n const character = html[cursor];\n if (quote !== undefined) {\n if (character === quote) {\n quote = undefined;\n }\n } else if (character === '\"' || character === \"'\") {\n quote = character;\n } else if (character === \">\") {\n return cursor + 1;\n }\n cursor += 1;\n }\n return html.length;\n}\n\nfunction skipRawTextElement(html: string, cursor: number, name: string): number {\n const closingPrefix = `</${name}`;\n for (;;) {\n const closingStart = indexOfAsciiCaseInsensitive(html, closingPrefix, cursor);\n if (closingStart < 0) {\n return html.length;\n }\n const delimiter = html[closingStart + closingPrefix.length];\n if (delimiter === \">\" || delimiter === \"/\" || isAsciiWhitespace(delimiter)) {\n return skipTagTail(html, closingStart + closingPrefix.length);\n }\n cursor = closingStart + 2;\n }\n}\n\nfunction skipScriptElement(html: string, cursor: number): number {\n let state: \"data\" | \"double-escaped\" | \"escaped\" = \"data\";\n\n while (cursor < html.length) {\n if (state === \"data\" && html.startsWith(\"<!--\", cursor)) {\n state = \"escaped\";\n cursor += 4;\n continue;\n }\n\n if (state !== \"data\" && html.startsWith(\"-->\", cursor)) {\n state = \"data\";\n cursor += 3;\n continue;\n }\n\n if (html[cursor] === \"<\") {\n if (isScriptTagAt(html, cursor, true)) {\n if (state === \"double-escaped\") {\n state = \"escaped\";\n cursor += 8;\n continue;\n }\n return skipTagTail(html, cursor + 8);\n }\n\n if (state === \"escaped\" && isScriptTagAt(html, cursor, false)) {\n state = \"double-escaped\";\n cursor += 7;\n continue;\n }\n }\n\n cursor += 1;\n }\n\n return html.length;\n}\n\nfunction isScriptTagAt(html: string, cursor: number, closing: boolean): boolean {\n const prefix = closing ? \"</script\" : \"<script\";\n if (!matchesAsciiCaseInsensitive(html, prefix, cursor)) {\n return false;\n }\n const delimiter = html[cursor + prefix.length];\n return delimiter === \">\" || delimiter === \"/\" || isAsciiWhitespace(delimiter);\n}\n\nfunction matchesAsciiCaseInsensitive(html: string, needle: string, start: number): boolean {\n if (start + needle.length > html.length) {\n return false;\n }\n for (let offset = 0; offset < needle.length; offset += 1) {\n if (html.charAt(start + offset).toLowerCase() !== needle.charAt(offset)) {\n return false;\n }\n }\n return true;\n}\n\nfunction indexOfAsciiCaseInsensitive(html: string, needle: string, start: number): number {\n const lastStart = html.length - needle.length;\n for (let index = start; index <= lastStart; index += 1) {\n let offset = 0;\n while (\n offset < needle.length &&\n html.charAt(index + offset).toLowerCase() === needle.charAt(offset)\n ) {\n offset += 1;\n }\n if (offset === needle.length) {\n return index;\n }\n }\n return -1;\n}\n\nfunction skipAsciiWhitespace(html: string, cursor: number): number {\n while (isAsciiWhitespace(html[cursor])) {\n cursor += 1;\n }\n return cursor;\n}\n\nfunction isAsciiWhitespace(character: string | undefined): boolean {\n return (\n character === \" \" ||\n character === \"\\t\" ||\n character === \"\\n\" ||\n character === \"\\f\" ||\n character === \"\\r\"\n );\n}\n\nfunction isAsciiLetter(character: string | undefined): boolean {\n if (character === undefined) {\n return false;\n }\n const code = character.charCodeAt(0);\n return (code >= 65 && code <= 90) || (code >= 97 && code <= 122);\n}\n\nfunction isTagNameTerminator(character: string | undefined): boolean {\n return (\n character === undefined ||\n character === \">\" ||\n character === \"/\" ||\n isAsciiWhitespace(character)\n );\n}\n\nfunction isForbiddenTagNameCharacter(character: string | undefined): boolean {\n return (\n character === \"\\0\" ||\n character === '\"' ||\n character === \"'\" ||\n character === \"<\" ||\n character === \"=\"\n );\n}\n\nfunction isAttributeNameTerminator(character: string | undefined): boolean {\n return (\n character === undefined ||\n character === \">\" ||\n character === \"/\" ||\n character === \"=\" ||\n isAsciiWhitespace(character)\n );\n}\n\nfunction isForbiddenAttributeNameCharacter(character: string | undefined): boolean {\n return character === \"\\0\" || character === '\"' || character === \"'\" || character === \"<\";\n}\n\nfunction isUnquotedValueTerminator(character: string | undefined): boolean {\n return character === undefined || character === \">\" || isAsciiWhitespace(character);\n}\n\nfunction isForbiddenUnquotedValueCharacter(character: string | undefined): boolean {\n return (\n character === \"\\0\" ||\n character === '\"' ||\n character === \"'\" ||\n character === \"`\" ||\n character === \"<\" ||\n character === \"=\"\n );\n}\n"]}
1
+ {"version":3,"file":"navigation-marker.js","sourceRoot":"","sources":["../src/navigation-marker.ts"],"names":[],"mappings":"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","sourcesContent":["import { routeHydrationContract } from \"./route-hydration-contract.js\";\n\nconst markerAttribute = routeHydrationContract.routeMarkerAttribute;\nconst rawTextElementNames = new Set([\n \"iframe\",\n \"noembed\",\n \"noframes\",\n \"noscript\",\n \"style\",\n \"textarea\",\n \"title\",\n \"xmp\",\n]);\n\ninterface ParsedStartTag {\n end: number;\n hasMarker: boolean;\n name: string;\n selfClosing: boolean;\n}\n\nexport function hasNavigationRouteMarker(html: string): boolean {\n let cursor = 0;\n let foreignContentDepth = 0;\n let templateDepth = 0;\n\n while (cursor < html.length) {\n const openingBracket = html.indexOf(\"<\", cursor);\n if (openingBracket < 0) {\n return false;\n }\n\n if (html.startsWith(\"<!--\", openingBracket)) {\n cursor = skipComment(html, openingBracket + 4);\n continue;\n }\n\n if (html.startsWith(\"<![CDATA[\", openingBracket)) {\n cursor =\n foreignContentDepth > 0\n ? skipCdataSection(html, openingBracket + 9)\n : skipBogusComment(html, openingBracket + 2);\n continue;\n }\n\n const discriminator = html[openingBracket + 1];\n if (discriminator === \"/\") {\n const endTag = parseEndTag(html, openingBracket);\n if (endTag.name === \"template\" && templateDepth > 0) {\n templateDepth -= 1;\n } else if (\n (endTag.name === \"svg\" || endTag.name === \"math\") &&\n foreignContentDepth > 0\n ) {\n foreignContentDepth -= 1;\n }\n cursor = endTag.end;\n continue;\n }\n if (discriminator === \"!\" || discriminator === \"?\") {\n cursor = skipTagTail(html, openingBracket + 2);\n continue;\n }\n if (!isAsciiLetter(discriminator)) {\n cursor = openingBracket + 1;\n continue;\n }\n\n const tag = parseStartTag(html, openingBracket);\n if (tag === undefined) {\n cursor = openingBracket + 1;\n continue;\n }\n if (\n tag.hasMarker &&\n templateDepth === 0 &&\n tag.name !== \"html\" &&\n tag.name !== \"head\" &&\n tag.name !== \"body\" &&\n tag.name !== \"frameset\"\n ) {\n return true;\n }\n\n if (tag.name === \"plaintext\") {\n return false;\n }\n\n if (!tag.selfClosing && tag.name === \"template\") {\n templateDepth += 1;\n } else if (!tag.selfClosing && (tag.name === \"svg\" || tag.name === \"math\")) {\n foreignContentDepth += 1;\n }\n\n cursor =\n tag.name === \"script\"\n ? skipScriptElement(html, tag.end)\n : foreignContentDepth === 0 && rawTextElementNames.has(tag.name)\n ? skipRawTextElement(html, tag.end, tag.name)\n : tag.end;\n }\n\n return false;\n}\n\nfunction parseStartTag(html: string, openingBracket: number): ParsedStartTag | undefined {\n let cursor = openingBracket + 1;\n const nameStart = cursor;\n\n while (cursor < html.length && !isTagNameTerminator(html[cursor])) {\n if (isForbiddenTagNameCharacter(html[cursor])) {\n return invalidStartTag(html, cursor, \"\");\n }\n cursor += 1;\n }\n\n if (cursor === nameStart) {\n return undefined;\n }\n\n const name = html.slice(nameStart, cursor).toLowerCase();\n let markerValueIsNonEmpty: boolean | undefined;\n\n for (;;) {\n cursor = skipAsciiWhitespace(html, cursor);\n if (cursor >= html.length) {\n return { end: html.length, hasMarker: false, name, selfClosing: false };\n }\n if (html[cursor] === \">\") {\n return {\n end: cursor + 1,\n hasMarker: markerValueIsNonEmpty === true,\n name,\n selfClosing: false,\n };\n }\n if (html[cursor] === \"/\" && html[cursor + 1] === \">\") {\n return {\n end: cursor + 2,\n hasMarker: markerValueIsNonEmpty === true,\n name,\n selfClosing: true,\n };\n }\n if (html[cursor] === \"/\") {\n return invalidStartTag(html, cursor, name);\n }\n\n const attributeNameStart = cursor;\n while (cursor < html.length && !isAttributeNameTerminator(html[cursor])) {\n if (isForbiddenAttributeNameCharacter(html[cursor])) {\n return invalidStartTag(html, cursor, name);\n }\n cursor += 1;\n }\n if (cursor === attributeNameStart) {\n return invalidStartTag(html, cursor, name);\n }\n\n const attributeName = html.slice(attributeNameStart, cursor).toLowerCase();\n cursor = skipAsciiWhitespace(html, cursor);\n let valueIsNonEmpty = false;\n\n if (html[cursor] === \"=\") {\n cursor = skipAsciiWhitespace(html, cursor + 1);\n const quote = html[cursor];\n\n if (quote === '\"' || quote === \"'\") {\n const valueStart = cursor + 1;\n const valueEnd = html.indexOf(quote, valueStart);\n if (valueEnd < 0) {\n return { end: html.length, hasMarker: false, name, selfClosing: false };\n }\n valueIsNonEmpty = valueEnd > valueStart;\n cursor = valueEnd + 1;\n } else {\n const valueStart = cursor;\n while (cursor < html.length && !isUnquotedValueTerminator(html[cursor])) {\n if (isForbiddenUnquotedValueCharacter(html[cursor])) {\n return invalidStartTag(html, cursor, name);\n }\n cursor += 1;\n }\n if (cursor === valueStart) {\n return invalidStartTag(html, cursor, name);\n }\n valueIsNonEmpty = true;\n }\n }\n\n if (attributeName === markerAttribute && markerValueIsNonEmpty === undefined) {\n markerValueIsNonEmpty = valueIsNonEmpty;\n }\n }\n}\n\nfunction invalidStartTag(html: string, cursor: number, name: string): ParsedStartTag {\n return { end: skipMalformedTagTail(html, cursor), hasMarker: false, name, selfClosing: false };\n}\n\nfunction skipComment(html: string, cursor: number): number {\n if (html[cursor] === \">\") {\n return cursor + 1;\n }\n if (html.startsWith(\"->\", cursor)) {\n return cursor + 2;\n }\n const standardEnd = html.indexOf(\"-->\", cursor);\n const abruptEnd = html.indexOf(\"--!>\", cursor);\n const end =\n standardEnd < 0 ? abruptEnd : abruptEnd < 0 ? standardEnd : Math.min(standardEnd, abruptEnd);\n return end < 0 ? html.length : end + (end === abruptEnd ? 4 : 3);\n}\n\nfunction skipBogusComment(html: string, cursor: number): number {\n const end = html.indexOf(\">\", cursor);\n return end < 0 ? html.length : end + 1;\n}\n\nfunction parseEndTag(html: string, openingBracket: number): { end: number; name: string } {\n let cursor = openingBracket + 2;\n const nameStart = cursor;\n while (isAsciiLetter(html[cursor])) {\n cursor += 1;\n }\n return {\n end: skipTagTail(html, cursor),\n name: html.slice(nameStart, cursor).toLowerCase(),\n };\n}\n\nfunction skipMalformedTagTail(html: string, cursor: number): number {\n const end = html.indexOf(\">\", cursor);\n return end < 0 ? html.length : end + 1;\n}\n\nfunction skipCdataSection(html: string, cursor: number): number {\n const end = html.indexOf(\"]]>\", cursor);\n return end < 0 ? html.length : end + 3;\n}\n\nfunction skipTagTail(html: string, cursor: number): number {\n let quote: '\"' | \"'\" | undefined;\n while (cursor < html.length) {\n const character = html[cursor];\n if (quote !== undefined) {\n if (character === quote) {\n quote = undefined;\n }\n } else if (character === '\"' || character === \"'\") {\n quote = character;\n } else if (character === \">\") {\n return cursor + 1;\n }\n cursor += 1;\n }\n return html.length;\n}\n\nfunction skipRawTextElement(html: string, cursor: number, name: string): number {\n const closingPrefix = `</${name}`;\n for (;;) {\n const closingStart = indexOfAsciiCaseInsensitive(html, closingPrefix, cursor);\n if (closingStart < 0) {\n return html.length;\n }\n const delimiter = html[closingStart + closingPrefix.length];\n if (delimiter === \">\" || delimiter === \"/\" || isAsciiWhitespace(delimiter)) {\n return skipTagTail(html, closingStart + closingPrefix.length);\n }\n cursor = closingStart + 2;\n }\n}\n\nfunction skipScriptElement(html: string, cursor: number): number {\n let state: \"data\" | \"double-escaped\" | \"escaped\" = \"data\";\n\n while (cursor < html.length) {\n if (state === \"data\" && html.startsWith(\"<!--\", cursor)) {\n state = \"escaped\";\n cursor += 4;\n continue;\n }\n\n if (state !== \"data\" && html.startsWith(\"-->\", cursor)) {\n state = \"data\";\n cursor += 3;\n continue;\n }\n\n if (html[cursor] === \"<\") {\n if (isScriptTagAt(html, cursor, true)) {\n if (state === \"double-escaped\") {\n state = \"escaped\";\n cursor += 8;\n continue;\n }\n return skipTagTail(html, cursor + 8);\n }\n\n if (state === \"escaped\" && isScriptTagAt(html, cursor, false)) {\n state = \"double-escaped\";\n cursor += 7;\n continue;\n }\n }\n\n cursor += 1;\n }\n\n return html.length;\n}\n\nfunction isScriptTagAt(html: string, cursor: number, closing: boolean): boolean {\n const prefix = closing ? \"</script\" : \"<script\";\n if (!matchesAsciiCaseInsensitive(html, prefix, cursor)) {\n return false;\n }\n const delimiter = html[cursor + prefix.length];\n return delimiter === \">\" || delimiter === \"/\" || isAsciiWhitespace(delimiter);\n}\n\nfunction matchesAsciiCaseInsensitive(html: string, needle: string, start: number): boolean {\n if (start + needle.length > html.length) {\n return false;\n }\n for (let offset = 0; offset < needle.length; offset += 1) {\n if (html.charAt(start + offset).toLowerCase() !== needle.charAt(offset)) {\n return false;\n }\n }\n return true;\n}\n\nfunction indexOfAsciiCaseInsensitive(html: string, needle: string, start: number): number {\n const lastStart = html.length - needle.length;\n for (let index = start; index <= lastStart; index += 1) {\n let offset = 0;\n while (\n offset < needle.length &&\n html.charAt(index + offset).toLowerCase() === needle.charAt(offset)\n ) {\n offset += 1;\n }\n if (offset === needle.length) {\n return index;\n }\n }\n return -1;\n}\n\nfunction skipAsciiWhitespace(html: string, cursor: number): number {\n while (isAsciiWhitespace(html[cursor])) {\n cursor += 1;\n }\n return cursor;\n}\n\nfunction isAsciiWhitespace(character: string | undefined): boolean {\n return (\n character === \" \" ||\n character === \"\\t\" ||\n character === \"\\n\" ||\n character === \"\\f\" ||\n character === \"\\r\"\n );\n}\n\nfunction isAsciiLetter(character: string | undefined): boolean {\n if (character === undefined) {\n return false;\n }\n const code = character.charCodeAt(0);\n return (code >= 65 && code <= 90) || (code >= 97 && code <= 122);\n}\n\nfunction isTagNameTerminator(character: string | undefined): boolean {\n return (\n character === undefined ||\n character === \">\" ||\n character === \"/\" ||\n isAsciiWhitespace(character)\n );\n}\n\nfunction isForbiddenTagNameCharacter(character: string | undefined): boolean {\n return (\n character === \"\\0\" ||\n character === '\"' ||\n character === \"'\" ||\n character === \"<\" ||\n character === \"=\"\n );\n}\n\nfunction isAttributeNameTerminator(character: string | undefined): boolean {\n return (\n character === undefined ||\n character === \">\" ||\n character === \"/\" ||\n character === \"=\" ||\n isAsciiWhitespace(character)\n );\n}\n\nfunction isForbiddenAttributeNameCharacter(character: string | undefined): boolean {\n return character === \"\\0\" || character === '\"' || character === \"'\" || character === \"<\";\n}\n\nfunction isUnquotedValueTerminator(character: string | undefined): boolean {\n return character === undefined || character === \">\" || isAsciiWhitespace(character);\n}\n\nfunction isForbiddenUnquotedValueCharacter(character: string | undefined): boolean {\n return (\n character === \"\\0\" ||\n character === '\"' ||\n character === \"'\" ||\n character === \"`\" ||\n character === \"<\" ||\n character === \"=\"\n );\n}\n"]}
@@ -8,6 +8,7 @@ export type MiddlewareNext = undefined;
8
8
  export interface ParseSchema<T> {
9
9
  parse(value: FormData): T;
10
10
  }
11
+ export declare function isSafeInternalRedirect(location: string): boolean;
11
12
  /**
12
13
  * Throws an internal redirect for loaders, middleware, route handlers, or server actions.
13
14
  *
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../src/navigation.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;CACtC;AAED,MAAM,MAAM,cAAc,GAAG,SAAS,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,KAAK,CAAC,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC;CAC3B;AA+DD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,KAAK,CAS/E;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,eAAoB,GAC5B,KAAK,CASP;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,QAAQ,CAa/E;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,QAAQ,CAY1F;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AACrE,wBAAsB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAUzF;;;;GAIG;AACH,wBAAgB,QAAQ,IAAI,KAAK,CAKhC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,KAAK,CAErC;AAED;;GAEG;AACH,wBAAgB,IAAI,IAAI,cAAc,CAErC;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,QAAQ,CAiB3E;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,QAAQ,CAElE;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,QAAQ,CAkBrE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAEjD;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACtC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAYxD;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAOtE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,KAAK,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAIrG;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEvD"}
1
+ {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../src/navigation.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;CACtC;AAED,MAAM,MAAM,cAAc,GAAG,SAAS,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,KAAK,CAAC,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC;CAC3B;AA8BD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAUhE;AAuBD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,KAAK,CAS/E;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,eAAoB,GAC5B,KAAK,CASP;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,QAAQ,CAa/E;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,QAAQ,CAY1F;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AACrE,wBAAsB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAUzF;;;;GAIG;AACH,wBAAgB,QAAQ,IAAI,KAAK,CAKhC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,KAAK,CAErC;AAED;;GAEG;AACH,wBAAgB,IAAI,IAAI,cAAc,CAErC;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,QAAQ,CAiB3E;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,QAAQ,CAElE;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,QAAQ,CAkBrE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAEjD;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACtC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAYxD;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAOtE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,KAAK,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAIrG;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEvD"}
@@ -26,7 +26,7 @@ function containsControlCharacter(value) {
26
26
  // Allowed: path-absolute (`/foo`), query-only (`?x=1`), hash-only (`#x`),
27
27
  // relative (`foo`). Rejected: protocol-relative (`//evil`), backslash variants
28
28
  // (`/\evil`, `\\evil`), and anything with a scheme like `javascript:`.
29
- function isSafeInternalRedirect(location) {
29
+ export function isSafeInternalRedirect(location) {
30
30
  if (containsControlCharacter(location))
31
31
  return false;
32
32
  const trimmed = stripLeadingControlOrWhitespace(location);
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.js","sourceRoot":"","sources":["../src/navigation.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseCookieHeader } from \"./cookies.js\";\n\nconst redirectErrorName = \"MReactRedirect\";\nconst notFoundErrorName = \"MReactNotFound\";\nconst rewriteHeaderName = \"x-mreact-rewrite\";\nconst rewriteLocationSymbol = Symbol.for(\"mreact.router.rewriteLocation\");\n\nexport interface RedirectOptions {\n status?: 301 | 302 | 303 | 307 | 308;\n}\n\nexport type MiddlewareNext = undefined;\n\n/**\n * Describes a parser used by `parseForm()` to validate submitted form data.\n */\nexport interface ParseSchema<T> {\n parse(value: FormData): T;\n}\n\n// Strip leading C0 controls + ASCII whitespace per WHATWG URL parsing.\n// Browsers ignore these characters when resolving the Location header,\n// so attacker payloads must be rejected after the same normalization.\nfunction stripLeadingControlOrWhitespace(value: string): string {\n let start = 0;\n\n while (start < value.length && value.charCodeAt(start) <= 0x20) {\n start += 1;\n }\n\n return value.slice(start);\n}\n\nfunction containsControlCharacter(value: string): boolean {\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index);\n if (code <= 0x1f || code === 0x7f) {\n return true;\n }\n }\n\n return false;\n}\n\n// Returns true if `location` is safe to use as a same-origin Location header.\n// Allowed: path-absolute (`/foo`), query-only (`?x=1`), hash-only (`#x`),\n// relative (`foo`). Rejected: protocol-relative (`//evil`), backslash variants\n// (`/\\evil`, `\\\\evil`), and anything with a scheme like `javascript:`.\nfunction isSafeInternalRedirect(location: string): boolean {\n if (containsControlCharacter(location)) return false;\n const trimmed = stripLeadingControlOrWhitespace(location);\n if (trimmed === \"\") return false;\n if (trimmed.startsWith(\"//\")) return false;\n if (trimmed.startsWith(\"/\\\\\")) return false;\n if (trimmed.startsWith(\"\\\\\")) return false;\n // Reject anything that parses as a URL with a scheme.\n if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed)) return false;\n return true;\n}\n\nfunction isSafeExternalRedirect(location: string): boolean {\n if (containsControlCharacter(location)) return false;\n const trimmed = stripLeadingControlOrWhitespace(location);\n const match = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(trimmed);\n if (match === null || match[1] === undefined) return false;\n const scheme = match[1].toLowerCase();\n return scheme === \"http\" || scheme === \"https\";\n}\n\nfunction throwUnsafeRedirect(location: string): never {\n throw new TypeError(\n `unsafe redirect target: ${JSON.stringify(location)} - use redirectExternal() for off-site destinations and ensure the URL is http(s)`,\n );\n}\n\nfunction throwUnsafeRewrite(location: string): never {\n throw new TypeError(\n `unsafe rewrite target: ${JSON.stringify(location)} - rewrite() only accepts same-origin paths, query strings, or hashes; external, protocol-relative, backslash, and control-character targets are rejected`,\n );\n}\n\n/**\n * Throws an internal redirect for loaders, middleware, route handlers, or server actions.\n *\n * The target must be same-origin or relative; use `redirectExternal()` for trusted `http` or `https` destinations. The default status is `303`.\n */\nexport function redirect(location: string, options: RedirectOptions = {}): never {\n if (!isSafeInternalRedirect(location)) {\n throwUnsafeRedirect(location);\n }\n throw Object.assign(new Error(`Redirect to ${location}`), {\n location,\n name: redirectErrorName,\n status: options.status ?? 303,\n });\n}\n\n/**\n * Throws an external redirect for trusted `http` or `https` destinations.\n *\n * This helper is intentionally separate from `redirect()` so off-site navigation is explicit. The default status is `307`.\n */\nexport function redirectExternal(\n location: string,\n options: RedirectOptions = {},\n): never {\n if (!isSafeExternalRedirect(location)) {\n throwUnsafeRedirect(location);\n }\n throw Object.assign(new Error(`Redirect to ${location}`), {\n location,\n name: redirectErrorName,\n status: options.status ?? 307,\n });\n}\n\n/**\n * Creates a same-origin `303 See Other` redirect response.\n *\n * Use this from route handlers when returning a `Response` is more convenient than throwing through the app-router control flow.\n */\nexport function redirect303(location: string, init: ResponseInit = {}): Response {\n if (!isSafeInternalRedirect(location)) {\n throwUnsafeRedirect(location);\n }\n\n const headers = new Headers(init.headers);\n headers.set(\"location\", location);\n\n return new Response(null, {\n ...init,\n headers,\n status: 303,\n });\n}\n\n/**\n * Creates a plain-text error response with a default `text/plain` content type.\n */\nexport function textError(message: string, status = 400, init: ResponseInit = {}): Response {\n const headers = new Headers(init.headers);\n\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"text/plain; charset=utf-8\");\n }\n\n return new Response(message, {\n ...init,\n headers,\n status,\n });\n}\n\n/**\n * Reads `request.formData()` and optionally validates it with a schema-like parser.\n *\n * Pass a schema object with `parse(FormData)` when a route handler or server action should receive typed form data.\n */\nexport async function parseForm(request: Request): Promise<FormData>;\nexport async function parseForm<T>(request: Request, schema: ParseSchema<T>): Promise<T>;\nexport async function parseForm<T>(\n request: Request,\n schema?: ParseSchema<T>,\n): Promise<FormData | T> {\n const form = await request.formData();\n\n return schema === undefined ? form : schema.parse(form);\n}\n\n/**\n * Throws a route-level 404 control error.\n *\n * Use this from loaders, metadata, middleware, or route handlers when the matched route is valid but the requested resource is absent.\n */\nexport function notFound(): never {\n throw Object.assign(new Error(\"Not Found\"), {\n name: notFoundErrorName,\n status: 404,\n });\n}\n\n/**\n * Alias for `notFound()` for codebases that prefer an explicit throwing helper name.\n */\nexport function throwNotFound(): never {\n return notFound();\n}\n\n/**\n * Continues middleware processing without changing the request.\n */\nexport function next(): MiddlewareNext {\n return undefined;\n}\n\n/**\n * Creates an internal rewrite response consumed by app-router middleware.\n *\n * The target must be a same-origin route path, query, hash, or relative URL; protocol-relative, external, and control-character targets are rejected.\n */\nexport function rewrite(location: string, init: ResponseInit = {}): Response {\n if (!isSafeInternalRedirect(location)) {\n throwUnsafeRewrite(location);\n }\n\n const response = new Response(null, {\n ...init,\n status: init.status ?? 200,\n });\n\n Object.defineProperty(response, rewriteLocationSymbol, {\n configurable: false,\n enumerable: false,\n value: location,\n });\n\n return response;\n}\n\n/**\n * Creates a JSON response using the platform `Response.json()` implementation.\n */\nexport function json(value: unknown, init?: ResponseInit): Response {\n return Response.json(value, init);\n}\n\n/**\n * Creates an HTML response and defaults the content type to `text/html; charset=utf-8`.\n */\nexport function html(value: string, init: ResponseInit = {}): Response {\n if (init.headers === undefined) {\n return new Response(value, {\n ...init,\n headers: { \"content-type\": \"text/html; charset=utf-8\" },\n });\n }\n\n const headers = new Headers(init.headers);\n\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"text/html; charset=utf-8\");\n }\n\n return new Response(value, {\n ...init,\n headers,\n });\n}\n\n/**\n * Returns the request headers object for route code that follows app-router helper naming.\n */\nexport function headers(request: Request): Headers {\n return request.headers;\n}\n\nexport interface RequestCookies {\n entries(): IterableIterator<[string, string]>;\n get(name: string): string | undefined;\n has(name: string): boolean;\n}\n\n/**\n * Parses request cookies into a small read-only helper.\n *\n * Values are decoded with `decodeURIComponent`; malformed encoded values are ignored rather than throwing during request handling.\n */\nexport function cookies(request: Request): RequestCookies {\n let values: ReadonlyMap<string, string> | undefined;\n const cookieValues = () => {\n values ??= parseCookieHeader(request.headers.get(\"cookie\"));\n return values;\n };\n\n return {\n entries: () => cookieValues().entries(),\n get: (name) => cookieValues().get(name),\n has: (name) => cookieValues().has(name),\n };\n}\n\nexport function rewriteLocation(response: Response): string | undefined {\n const marked = (response as { [rewriteLocationSymbol]?: unknown })[rewriteLocationSymbol];\n const candidate = typeof marked === \"string\"\n ? marked\n : response.headers.get(rewriteHeaderName) ?? undefined;\n\n return candidate !== undefined && isSafeInternalRedirect(candidate) ? candidate : undefined;\n}\n\n/**\n * Checks whether an unknown error is an app-router redirect signal.\n */\nexport function isRedirectError(error: unknown): error is Error & { location: string; status: number } {\n return error instanceof Error &&\n error.name === redirectErrorName &&\n typeof (error as { location?: unknown }).location === \"string\";\n}\n\n/**\n * Checks whether an unknown error is an app-router not-found signal.\n */\nexport function isNotFoundError(error: unknown): boolean {\n return error instanceof Error && error.name === notFoundErrorName;\n}\n"]}
1
+ {"version":3,"file":"navigation.js","sourceRoot":"","sources":["../src/navigation.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseCookieHeader } from \"./cookies.js\";\n\nconst redirectErrorName = \"MReactRedirect\";\nconst notFoundErrorName = \"MReactNotFound\";\nconst rewriteHeaderName = \"x-mreact-rewrite\";\nconst rewriteLocationSymbol = Symbol.for(\"mreact.router.rewriteLocation\");\n\nexport interface RedirectOptions {\n status?: 301 | 302 | 303 | 307 | 308;\n}\n\nexport type MiddlewareNext = undefined;\n\n/**\n * Describes a parser used by `parseForm()` to validate submitted form data.\n */\nexport interface ParseSchema<T> {\n parse(value: FormData): T;\n}\n\n// Strip leading C0 controls + ASCII whitespace per WHATWG URL parsing.\n// Browsers ignore these characters when resolving the Location header,\n// so attacker payloads must be rejected after the same normalization.\nfunction stripLeadingControlOrWhitespace(value: string): string {\n let start = 0;\n\n while (start < value.length && value.charCodeAt(start) <= 0x20) {\n start += 1;\n }\n\n return value.slice(start);\n}\n\nfunction containsControlCharacter(value: string): boolean {\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index);\n if (code <= 0x1f || code === 0x7f) {\n return true;\n }\n }\n\n return false;\n}\n\n// Returns true if `location` is safe to use as a same-origin Location header.\n// Allowed: path-absolute (`/foo`), query-only (`?x=1`), hash-only (`#x`),\n// relative (`foo`). Rejected: protocol-relative (`//evil`), backslash variants\n// (`/\\evil`, `\\\\evil`), and anything with a scheme like `javascript:`.\nexport function isSafeInternalRedirect(location: string): boolean {\n if (containsControlCharacter(location)) return false;\n const trimmed = stripLeadingControlOrWhitespace(location);\n if (trimmed === \"\") return false;\n if (trimmed.startsWith(\"//\")) return false;\n if (trimmed.startsWith(\"/\\\\\")) return false;\n if (trimmed.startsWith(\"\\\\\")) return false;\n // Reject anything that parses as a URL with a scheme.\n if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed)) return false;\n return true;\n}\n\nfunction isSafeExternalRedirect(location: string): boolean {\n if (containsControlCharacter(location)) return false;\n const trimmed = stripLeadingControlOrWhitespace(location);\n const match = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(trimmed);\n if (match === null || match[1] === undefined) return false;\n const scheme = match[1].toLowerCase();\n return scheme === \"http\" || scheme === \"https\";\n}\n\nfunction throwUnsafeRedirect(location: string): never {\n throw new TypeError(\n `unsafe redirect target: ${JSON.stringify(location)} - use redirectExternal() for off-site destinations and ensure the URL is http(s)`,\n );\n}\n\nfunction throwUnsafeRewrite(location: string): never {\n throw new TypeError(\n `unsafe rewrite target: ${JSON.stringify(location)} - rewrite() only accepts same-origin paths, query strings, or hashes; external, protocol-relative, backslash, and control-character targets are rejected`,\n );\n}\n\n/**\n * Throws an internal redirect for loaders, middleware, route handlers, or server actions.\n *\n * The target must be same-origin or relative; use `redirectExternal()` for trusted `http` or `https` destinations. The default status is `303`.\n */\nexport function redirect(location: string, options: RedirectOptions = {}): never {\n if (!isSafeInternalRedirect(location)) {\n throwUnsafeRedirect(location);\n }\n throw Object.assign(new Error(`Redirect to ${location}`), {\n location,\n name: redirectErrorName,\n status: options.status ?? 303,\n });\n}\n\n/**\n * Throws an external redirect for trusted `http` or `https` destinations.\n *\n * This helper is intentionally separate from `redirect()` so off-site navigation is explicit. The default status is `307`.\n */\nexport function redirectExternal(\n location: string,\n options: RedirectOptions = {},\n): never {\n if (!isSafeExternalRedirect(location)) {\n throwUnsafeRedirect(location);\n }\n throw Object.assign(new Error(`Redirect to ${location}`), {\n location,\n name: redirectErrorName,\n status: options.status ?? 307,\n });\n}\n\n/**\n * Creates a same-origin `303 See Other` redirect response.\n *\n * Use this from route handlers when returning a `Response` is more convenient than throwing through the app-router control flow.\n */\nexport function redirect303(location: string, init: ResponseInit = {}): Response {\n if (!isSafeInternalRedirect(location)) {\n throwUnsafeRedirect(location);\n }\n\n const headers = new Headers(init.headers);\n headers.set(\"location\", location);\n\n return new Response(null, {\n ...init,\n headers,\n status: 303,\n });\n}\n\n/**\n * Creates a plain-text error response with a default `text/plain` content type.\n */\nexport function textError(message: string, status = 400, init: ResponseInit = {}): Response {\n const headers = new Headers(init.headers);\n\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"text/plain; charset=utf-8\");\n }\n\n return new Response(message, {\n ...init,\n headers,\n status,\n });\n}\n\n/**\n * Reads `request.formData()` and optionally validates it with a schema-like parser.\n *\n * Pass a schema object with `parse(FormData)` when a route handler or server action should receive typed form data.\n */\nexport async function parseForm(request: Request): Promise<FormData>;\nexport async function parseForm<T>(request: Request, schema: ParseSchema<T>): Promise<T>;\nexport async function parseForm<T>(\n request: Request,\n schema?: ParseSchema<T>,\n): Promise<FormData | T> {\n const form = await request.formData();\n\n return schema === undefined ? form : schema.parse(form);\n}\n\n/**\n * Throws a route-level 404 control error.\n *\n * Use this from loaders, metadata, middleware, or route handlers when the matched route is valid but the requested resource is absent.\n */\nexport function notFound(): never {\n throw Object.assign(new Error(\"Not Found\"), {\n name: notFoundErrorName,\n status: 404,\n });\n}\n\n/**\n * Alias for `notFound()` for codebases that prefer an explicit throwing helper name.\n */\nexport function throwNotFound(): never {\n return notFound();\n}\n\n/**\n * Continues middleware processing without changing the request.\n */\nexport function next(): MiddlewareNext {\n return undefined;\n}\n\n/**\n * Creates an internal rewrite response consumed by app-router middleware.\n *\n * The target must be a same-origin route path, query, hash, or relative URL; protocol-relative, external, and control-character targets are rejected.\n */\nexport function rewrite(location: string, init: ResponseInit = {}): Response {\n if (!isSafeInternalRedirect(location)) {\n throwUnsafeRewrite(location);\n }\n\n const response = new Response(null, {\n ...init,\n status: init.status ?? 200,\n });\n\n Object.defineProperty(response, rewriteLocationSymbol, {\n configurable: false,\n enumerable: false,\n value: location,\n });\n\n return response;\n}\n\n/**\n * Creates a JSON response using the platform `Response.json()` implementation.\n */\nexport function json(value: unknown, init?: ResponseInit): Response {\n return Response.json(value, init);\n}\n\n/**\n * Creates an HTML response and defaults the content type to `text/html; charset=utf-8`.\n */\nexport function html(value: string, init: ResponseInit = {}): Response {\n if (init.headers === undefined) {\n return new Response(value, {\n ...init,\n headers: { \"content-type\": \"text/html; charset=utf-8\" },\n });\n }\n\n const headers = new Headers(init.headers);\n\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"text/html; charset=utf-8\");\n }\n\n return new Response(value, {\n ...init,\n headers,\n });\n}\n\n/**\n * Returns the request headers object for route code that follows app-router helper naming.\n */\nexport function headers(request: Request): Headers {\n return request.headers;\n}\n\nexport interface RequestCookies {\n entries(): IterableIterator<[string, string]>;\n get(name: string): string | undefined;\n has(name: string): boolean;\n}\n\n/**\n * Parses request cookies into a small read-only helper.\n *\n * Values are decoded with `decodeURIComponent`; malformed encoded values are ignored rather than throwing during request handling.\n */\nexport function cookies(request: Request): RequestCookies {\n let values: ReadonlyMap<string, string> | undefined;\n const cookieValues = () => {\n values ??= parseCookieHeader(request.headers.get(\"cookie\"));\n return values;\n };\n\n return {\n entries: () => cookieValues().entries(),\n get: (name) => cookieValues().get(name),\n has: (name) => cookieValues().has(name),\n };\n}\n\nexport function rewriteLocation(response: Response): string | undefined {\n const marked = (response as { [rewriteLocationSymbol]?: unknown })[rewriteLocationSymbol];\n const candidate = typeof marked === \"string\"\n ? marked\n : response.headers.get(rewriteHeaderName) ?? undefined;\n\n return candidate !== undefined && isSafeInternalRedirect(candidate) ? candidate : undefined;\n}\n\n/**\n * Checks whether an unknown error is an app-router redirect signal.\n */\nexport function isRedirectError(error: unknown): error is Error & { location: string; status: number } {\n return error instanceof Error &&\n error.name === redirectErrorName &&\n typeof (error as { location?: unknown }).location === \"string\";\n}\n\n/**\n * Checks whether an unknown error is an app-router not-found signal.\n */\nexport function isNotFoundError(error: unknown): boolean {\n return error instanceof Error && error.name === notFoundErrorName;\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { type Server } from "node:http";
2
2
  import { type AppRouterLogger } from "./logger.js";
3
- import type { HttpUpgradeHandler } from "./upgrade.js";
3
+ import { type HttpUpgradeOriginPolicy, type ManagedHttpUpgradeHandler } from "./upgrade.js";
4
4
  export interface StartNodeRequestServerOptions {
5
5
  allowedHosts?: readonly string[] | undefined;
6
6
  errorHandler?: ((error: unknown) => {
@@ -11,7 +11,7 @@ export interface StartNodeRequestServerOptions {
11
11
  hostname?: string | undefined;
12
12
  hostPolicy?: "strict" | "trusted-proxy" | undefined;
13
13
  logger?: AppRouterLogger | undefined;
14
- onUpgrade?: HttpUpgradeHandler | undefined;
14
+ onUpgrade?: ManagedHttpUpgradeHandler | undefined;
15
15
  port: number;
16
16
  render(request: Request): Promise<Response>;
17
17
  resolveHost?: ((options: {
@@ -21,6 +21,9 @@ export interface StartNodeRequestServerOptions {
21
21
  rawHost: string | undefined;
22
22
  }) => string) | undefined;
23
23
  trustForwardedProto?: boolean | undefined;
24
+ upgradeCloseTimeoutMs?: number | undefined;
25
+ upgradeDecisionTimeoutMs?: number | undefined;
26
+ upgradeOriginPolicy?: HttpUpgradeOriginPolicy | undefined;
24
27
  }
25
28
  export declare function resolveNodeRequestProtocol(options: {
26
29
  encrypted: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"node-server.d.ts","sourceRoot":"","sources":["../src/node-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAEtD,OAAO,EAOL,KAAK,eAAe,EACrB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,MAAM,WAAW,6BAA6B;IAC5C,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7C,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK;QAClC,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAClC,CAAC,GAAG,SAAS,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,UAAU,CAAC,EAAE,QAAQ,GAAG,eAAe,GAAG,SAAS,CAAC;IACpD,MAAM,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IACrC,SAAS,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC5C,WAAW,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE;QACvB,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;QAC7C,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,QAAQ,GAAG,eAAe,GAAG,SAAS,CAAC;QACpD,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;KAC7B,KAAK,MAAM,CAAC,GAAG,SAAS,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC3C;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE;IAClD,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACvD,mBAAmB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC3C,GAAG,MAAM,GAAG,OAAO,CAcnB;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC;IAAE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAmFlE"}
1
+ {"version":3,"file":"node-server.d.ts","sourceRoot":"","sources":["../src/node-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAEtD,OAAO,EAOL,KAAK,eAAe,EACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAKL,KAAK,uBAAuB,EAE5B,KAAK,yBAAyB,EAC/B,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,6BAA6B;IAC5C,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7C,YAAY,CAAC,EACT,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAClC,CAAC,GACF,SAAS,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,UAAU,CAAC,EAAE,QAAQ,GAAG,eAAe,GAAG,SAAS,CAAC;IACpD,MAAM,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IACrC,SAAS,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC5C,WAAW,CAAC,EACR,CAAC,CAAC,OAAO,EAAE;QACT,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;QAC7C,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,QAAQ,GAAG,eAAe,GAAG,SAAS,CAAC;QACpD,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;KAC7B,KAAK,MAAM,CAAC,GACb,SAAS,CAAC;IACd,mBAAmB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1C,qBAAqB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9C,mBAAmB,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;CAC3D;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE;IAClD,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACvD,mBAAmB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC3C,GAAG,MAAM,GAAG,OAAO,CAcnB;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC;IAAE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAuHlE"}
@@ -1,6 +1,7 @@
1
1
  import { createServer } from "node:http";
2
2
  import { nodeRequestToWebRequest, sendResponse } from "./http.js";
3
3
  import { emitRouterLog, logDurationMs, logError, logNow, nodeRequestPath, requestLogFields, } from "./logger.js";
4
+ import { assertValidHttpUpgradeOriginPolicy, closeServerWithUpgrades, createManagedHttpUpgradeLifecycle, validateHttpUpgradeOrigin, } from "./upgrade.js";
4
5
  export function resolveNodeRequestProtocol(options) {
5
6
  if (options.encrypted) {
6
7
  return "https";
@@ -15,6 +16,10 @@ export function resolveNodeRequestProtocol(options) {
15
16
  return first === "https" ? "https" : "http";
16
17
  }
17
18
  export async function startNodeRequestServer(options) {
19
+ const upgradeCloseTimeoutMs = finiteNonNegativeTimeout(options.upgradeCloseTimeoutMs, 1_000, "upgradeCloseTimeoutMs");
20
+ const upgradeDecisionTimeoutMs = finiteNonNegativeTimeout(options.upgradeDecisionTimeoutMs, 1_000, "upgradeDecisionTimeoutMs");
21
+ assertValidHttpUpgradeOriginPolicy(options.upgradeOriginPolicy);
22
+ let listeningPort = options.port;
18
23
  const server = createServer(async (incoming, outgoing) => {
19
24
  const startedAt = logNow();
20
25
  const fallbackRequestFields = {
@@ -23,7 +28,7 @@ export async function startNodeRequestServer(options) {
23
28
  runtime: "node",
24
29
  };
25
30
  try {
26
- const fallbackHost = `${options.hostname ?? "127.0.0.1"}:${options.port}`;
31
+ const fallbackHost = formatNodeAuthority(options.hostname ?? "127.0.0.1", options.port);
27
32
  const host = (options.resolveHost ?? defaultResolveRequestHost)({
28
33
  allowedHosts: options.allowedHosts,
29
34
  fallbackHost,
@@ -35,7 +40,7 @@ export async function startNodeRequestServer(options) {
35
40
  forwardedProto: incoming.headers["x-forwarded-proto"],
36
41
  trustForwardedProto: options.trustForwardedProto,
37
42
  });
38
- const request = nodeRequestToWebRequest(incoming, `${protocol}://${host}`);
43
+ const request = nodeRequestToWebRequest(incoming, `${protocol}://${host}`, outgoing);
39
44
  const logFields = requestLogFields(request, "node");
40
45
  emitRouterLog(options.logger, "info", {
41
46
  ...logFields,
@@ -75,18 +80,82 @@ export async function startNodeRequestServer(options) {
75
80
  outgoing.end(payload.body);
76
81
  }
77
82
  });
78
- if (options.onUpgrade !== undefined) {
79
- server.on("upgrade", options.onUpgrade);
83
+ const upgradeLifecycle = options.onUpgrade === undefined
84
+ ? undefined
85
+ : createManagedHttpUpgradeLifecycle({
86
+ decisionTimeoutMs: upgradeDecisionTimeoutMs,
87
+ handler: options.onUpgrade,
88
+ isOriginAllowed: (request) => validateNodeUpgradeOrigin(request, {
89
+ ...options,
90
+ listeningPort,
91
+ }),
92
+ logger: options.logger,
93
+ });
94
+ if (upgradeLifecycle !== undefined) {
95
+ server.on("upgrade", upgradeLifecycle.handle);
80
96
  }
81
97
  await new Promise((resolve) => server.listen(options.port, options.hostname ?? "127.0.0.1", resolve));
82
98
  const address = server.address();
83
99
  const port = typeof address === "object" && address !== null ? address.port : options.port;
100
+ listeningPort = port;
101
+ let closePromise;
84
102
  return {
85
103
  server,
86
- url: `http://${options.hostname ?? "127.0.0.1"}:${port}`,
87
- close: () => new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))),
104
+ url: `http://${formatNodeAuthority(options.hostname ?? "127.0.0.1", port)}`,
105
+ close: () => {
106
+ closePromise ??=
107
+ upgradeLifecycle === undefined
108
+ ? new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())))
109
+ : closeServerWithUpgrades({
110
+ lifecycle: upgradeLifecycle,
111
+ server,
112
+ timeoutMs: upgradeCloseTimeoutMs,
113
+ });
114
+ return closePromise;
115
+ },
88
116
  };
89
117
  }
118
+ function validateNodeUpgradeOrigin(request, options) {
119
+ const policy = options.upgradeOriginPolicy ?? "same-origin";
120
+ if (policy === "unchecked") {
121
+ return { ok: true, origin: request.headers.origin };
122
+ }
123
+ if (typeof policy === "object") {
124
+ return validateHttpUpgradeOrigin(request, policy);
125
+ }
126
+ const fallbackHost = formatNodeAuthority(options.hostname ?? "127.0.0.1", options.listeningPort);
127
+ const host = (options.resolveHost ?? defaultResolveRequestHost)({
128
+ allowedHosts: options.allowedHosts,
129
+ fallbackHost,
130
+ hostPolicy: options.hostPolicy,
131
+ rawHost: request.headers.host,
132
+ });
133
+ const protocol = resolveNodeRequestProtocol({
134
+ encrypted: request.socket.encrypted === true,
135
+ forwardedProto: request.headers["x-forwarded-proto"],
136
+ trustForwardedProto: options.trustForwardedProto,
137
+ });
138
+ try {
139
+ return validateHttpUpgradeOrigin(request, { allowedOrigins: [`${protocol}://${host}`] });
140
+ }
141
+ catch {
142
+ return { ok: false, reason: "malformed-origin" };
143
+ }
144
+ }
145
+ function formatNodeAuthority(hostname, port) {
146
+ const host = hostname.includes(":") && !hostname.startsWith("[") ? `[${hostname}]` : hostname;
147
+ return `${host}:${port}`;
148
+ }
149
+ function finiteNonNegativeTimeout(value, fallback, name) {
150
+ const timeout = value ?? fallback;
151
+ if (!Number.isFinite(timeout) ||
152
+ timeout < 0 ||
153
+ !Number.isSafeInteger(timeout) ||
154
+ timeout > 2_147_483_647) {
155
+ throw new TypeError(`${name} must be a finite non-negative safe integer no greater than 2147483647.`);
156
+ }
157
+ return timeout;
158
+ }
90
159
  function defaultResolveRequestHost(options) {
91
160
  const raw = options.rawHost;
92
161
  if (raw === undefined || raw === "")
@@ -1 +1 @@
1
- {"version":3,"file":"node-server.js","sourceRoot":"","sources":["../src/node-server.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer, type Server } from \"node:http\";\nimport { nodeRequestToWebRequest, sendResponse } from \"./http.js\";\nimport {\n emitRouterLog,\n logDurationMs,\n logError,\n logNow,\n nodeRequestPath,\n requestLogFields,\n type AppRouterLogger,\n} from \"./logger.js\";\nimport type { HttpUpgradeHandler } from \"./upgrade.js\";\n\nexport interface StartNodeRequestServerOptions {\n allowedHosts?: readonly string[] | undefined;\n errorHandler?: ((error: unknown) => {\n body: string;\n status: number;\n headers?: Record<string, string>;\n }) | undefined;\n hostname?: string | undefined;\n hostPolicy?: \"strict\" | \"trusted-proxy\" | undefined;\n logger?: AppRouterLogger | undefined;\n onUpgrade?: HttpUpgradeHandler | undefined;\n port: number;\n render(request: Request): Promise<Response>;\n resolveHost?: ((options: {\n allowedHosts?: readonly string[] | undefined;\n fallbackHost: string;\n hostPolicy?: \"strict\" | \"trusted-proxy\" | undefined;\n rawHost: string | undefined;\n }) => string) | undefined;\n trustForwardedProto?: boolean | undefined;\n}\n\nexport function resolveNodeRequestProtocol(options: {\n encrypted: boolean;\n forwardedProto: string | readonly string[] | undefined;\n trustForwardedProto?: boolean | undefined;\n}): \"http\" | \"https\" {\n if (options.encrypted) {\n return \"https\";\n }\n\n if (options.trustForwardedProto !== true) {\n return \"http\";\n }\n\n const forwarded = Array.isArray(options.forwardedProto)\n ? options.forwardedProto[0]\n : options.forwardedProto;\n const first = forwarded?.split(\",\", 1)[0]?.trim().toLowerCase();\n return first === \"https\" ? \"https\" : \"http\";\n}\n\nexport async function startNodeRequestServer(\n options: StartNodeRequestServerOptions,\n): Promise<{ close(): Promise<void>; server: Server; url: string }> {\n const server = createServer(async (incoming, outgoing) => {\n const startedAt = logNow();\n const fallbackRequestFields = {\n method: incoming.method ?? \"GET\",\n path: nodeRequestPath(incoming.url),\n runtime: \"node\" as const,\n };\n\n try {\n const fallbackHost = `${options.hostname ?? \"127.0.0.1\"}:${options.port}`;\n const host = (options.resolveHost ?? defaultResolveRequestHost)({\n allowedHosts: options.allowedHosts,\n fallbackHost,\n hostPolicy: options.hostPolicy,\n rawHost: incoming.headers.host,\n });\n const protocol = resolveNodeRequestProtocol({\n encrypted: (incoming.socket as { encrypted?: boolean }).encrypted === true,\n forwardedProto: incoming.headers[\"x-forwarded-proto\"],\n trustForwardedProto: options.trustForwardedProto,\n });\n const request = nodeRequestToWebRequest(incoming, `${protocol}://${host}`);\n const logFields = requestLogFields(request, \"node\");\n emitRouterLog(options.logger, \"info\", {\n ...logFields,\n type: \"router:request:start\",\n });\n const response = await options.render(request);\n emitRouterLog(options.logger, \"info\", {\n ...logFields,\n durationMs: logDurationMs(startedAt),\n status: response.status,\n type: \"router:request:end\",\n });\n\n await sendResponse(outgoing, response);\n } catch (error) {\n // Log the full stack to stderr for operator visibility; never put\n // it in the response body where attackers can scrape it.\n emitRouterLog(options.logger, \"error\", {\n ...fallbackRequestFields,\n durationMs: logDurationMs(startedAt),\n error: logError(error),\n type: \"router:request:error\",\n });\n if (options.logger === undefined) {\n console.error(\"[mreact] startServer request failed:\", error);\n }\n const payload = options.errorHandler\n ? options.errorHandler(error)\n : { body: \"Internal Server Error\", status: 500 };\n outgoing.statusCode = payload.status;\n outgoing.setHeader(\n \"content-type\",\n payload.headers?.[\"content-type\"] ?? \"text/plain; charset=utf-8\",\n );\n for (const [name, value] of Object.entries(payload.headers ?? {})) {\n if (name.toLowerCase() === \"content-type\") continue;\n outgoing.setHeader(name, value);\n }\n outgoing.end(payload.body);\n }\n });\n\n if (options.onUpgrade !== undefined) {\n server.on(\"upgrade\", options.onUpgrade);\n }\n\n await new Promise<void>((resolve) =>\n server.listen(options.port, options.hostname ?? \"127.0.0.1\", resolve),\n );\n const address = server.address();\n const port = typeof address === \"object\" && address !== null ? address.port : options.port;\n\n return {\n server,\n url: `http://${options.hostname ?? \"127.0.0.1\"}:${port}`,\n close: () =>\n new Promise<void>((resolve, reject) =>\n server.close((error) => (error ? reject(error) : resolve())),\n ),\n };\n}\n\nfunction defaultResolveRequestHost(options: {\n allowedHosts?: readonly string[] | undefined;\n fallbackHost: string;\n hostPolicy?: \"strict\" | \"trusted-proxy\" | undefined;\n rawHost: string | undefined;\n}): string {\n const raw = options.rawHost;\n if (raw === undefined || raw === \"\") return options.fallbackHost;\n if (options.allowedHosts === undefined) {\n return options.hostPolicy === \"strict\" ? options.fallbackHost : raw;\n }\n return options.allowedHosts.includes(raw) ? raw : options.fallbackHost;\n}\n"]}
1
+ {"version":3,"file":"node-server.js","sourceRoot":"","sources":["../src/node-server.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer, type Server } from \"node:http\";\nimport { nodeRequestToWebRequest, sendResponse } from \"./http.js\";\nimport {\n emitRouterLog,\n logDurationMs,\n logError,\n logNow,\n nodeRequestPath,\n requestLogFields,\n type AppRouterLogger,\n} from \"./logger.js\";\nimport {\n assertValidHttpUpgradeOriginPolicy,\n closeServerWithUpgrades,\n createManagedHttpUpgradeLifecycle,\n validateHttpUpgradeOrigin,\n type HttpUpgradeOriginPolicy,\n type HttpUpgradeOriginValidation,\n type ManagedHttpUpgradeHandler,\n} from \"./upgrade.js\";\n\nexport interface StartNodeRequestServerOptions {\n allowedHosts?: readonly string[] | undefined;\n errorHandler?:\n | ((error: unknown) => {\n body: string;\n status: number;\n headers?: Record<string, string>;\n })\n | undefined;\n hostname?: string | undefined;\n hostPolicy?: \"strict\" | \"trusted-proxy\" | undefined;\n logger?: AppRouterLogger | undefined;\n onUpgrade?: ManagedHttpUpgradeHandler | undefined;\n port: number;\n render(request: Request): Promise<Response>;\n resolveHost?:\n | ((options: {\n allowedHosts?: readonly string[] | undefined;\n fallbackHost: string;\n hostPolicy?: \"strict\" | \"trusted-proxy\" | undefined;\n rawHost: string | undefined;\n }) => string)\n | undefined;\n trustForwardedProto?: boolean | undefined;\n upgradeCloseTimeoutMs?: number | undefined;\n upgradeDecisionTimeoutMs?: number | undefined;\n upgradeOriginPolicy?: HttpUpgradeOriginPolicy | undefined;\n}\n\nexport function resolveNodeRequestProtocol(options: {\n encrypted: boolean;\n forwardedProto: string | readonly string[] | undefined;\n trustForwardedProto?: boolean | undefined;\n}): \"http\" | \"https\" {\n if (options.encrypted) {\n return \"https\";\n }\n\n if (options.trustForwardedProto !== true) {\n return \"http\";\n }\n\n const forwarded = Array.isArray(options.forwardedProto)\n ? options.forwardedProto[0]\n : options.forwardedProto;\n const first = forwarded?.split(\",\", 1)[0]?.trim().toLowerCase();\n return first === \"https\" ? \"https\" : \"http\";\n}\n\nexport async function startNodeRequestServer(\n options: StartNodeRequestServerOptions,\n): Promise<{ close(): Promise<void>; server: Server; url: string }> {\n const upgradeCloseTimeoutMs = finiteNonNegativeTimeout(\n options.upgradeCloseTimeoutMs,\n 1_000,\n \"upgradeCloseTimeoutMs\",\n );\n const upgradeDecisionTimeoutMs = finiteNonNegativeTimeout(\n options.upgradeDecisionTimeoutMs,\n 1_000,\n \"upgradeDecisionTimeoutMs\",\n );\n assertValidHttpUpgradeOriginPolicy(options.upgradeOriginPolicy);\n let listeningPort = options.port;\n const server = createServer(async (incoming, outgoing) => {\n const startedAt = logNow();\n const fallbackRequestFields = {\n method: incoming.method ?? \"GET\",\n path: nodeRequestPath(incoming.url),\n runtime: \"node\" as const,\n };\n\n try {\n const fallbackHost = formatNodeAuthority(options.hostname ?? \"127.0.0.1\", options.port);\n const host = (options.resolveHost ?? defaultResolveRequestHost)({\n allowedHosts: options.allowedHosts,\n fallbackHost,\n hostPolicy: options.hostPolicy,\n rawHost: incoming.headers.host,\n });\n const protocol = resolveNodeRequestProtocol({\n encrypted: (incoming.socket as { encrypted?: boolean }).encrypted === true,\n forwardedProto: incoming.headers[\"x-forwarded-proto\"],\n trustForwardedProto: options.trustForwardedProto,\n });\n const request = nodeRequestToWebRequest(incoming, `${protocol}://${host}`, outgoing);\n const logFields = requestLogFields(request, \"node\");\n emitRouterLog(options.logger, \"info\", {\n ...logFields,\n type: \"router:request:start\",\n });\n const response = await options.render(request);\n emitRouterLog(options.logger, \"info\", {\n ...logFields,\n durationMs: logDurationMs(startedAt),\n status: response.status,\n type: \"router:request:end\",\n });\n\n await sendResponse(outgoing, response);\n } catch (error) {\n // Log the full stack to stderr for operator visibility; never put\n // it in the response body where attackers can scrape it.\n emitRouterLog(options.logger, \"error\", {\n ...fallbackRequestFields,\n durationMs: logDurationMs(startedAt),\n error: logError(error),\n type: \"router:request:error\",\n });\n if (options.logger === undefined) {\n console.error(\"[mreact] startServer request failed:\", error);\n }\n const payload = options.errorHandler\n ? options.errorHandler(error)\n : { body: \"Internal Server Error\", status: 500 };\n outgoing.statusCode = payload.status;\n outgoing.setHeader(\n \"content-type\",\n payload.headers?.[\"content-type\"] ?? \"text/plain; charset=utf-8\",\n );\n for (const [name, value] of Object.entries(payload.headers ?? {})) {\n if (name.toLowerCase() === \"content-type\") continue;\n outgoing.setHeader(name, value);\n }\n outgoing.end(payload.body);\n }\n });\n\n const upgradeLifecycle =\n options.onUpgrade === undefined\n ? undefined\n : createManagedHttpUpgradeLifecycle({\n decisionTimeoutMs: upgradeDecisionTimeoutMs,\n handler: options.onUpgrade,\n isOriginAllowed: (request) =>\n validateNodeUpgradeOrigin(request, {\n ...options,\n listeningPort,\n }),\n logger: options.logger,\n });\n if (upgradeLifecycle !== undefined) {\n server.on(\"upgrade\", upgradeLifecycle.handle);\n }\n\n await new Promise<void>((resolve) =>\n server.listen(options.port, options.hostname ?? \"127.0.0.1\", resolve),\n );\n const address = server.address();\n const port = typeof address === \"object\" && address !== null ? address.port : options.port;\n listeningPort = port;\n let closePromise: Promise<void> | undefined;\n\n return {\n server,\n url: `http://${formatNodeAuthority(options.hostname ?? \"127.0.0.1\", port)}`,\n close: () => {\n closePromise ??=\n upgradeLifecycle === undefined\n ? new Promise<void>((resolve, reject) =>\n server.close((error) => (error ? reject(error) : resolve())),\n )\n : closeServerWithUpgrades({\n lifecycle: upgradeLifecycle,\n server,\n timeoutMs: upgradeCloseTimeoutMs,\n });\n return closePromise;\n },\n };\n}\n\nfunction validateNodeUpgradeOrigin(\n request: import(\"node:http\").IncomingMessage,\n options: StartNodeRequestServerOptions & { listeningPort: number },\n): HttpUpgradeOriginValidation {\n const policy = options.upgradeOriginPolicy ?? \"same-origin\";\n if (policy === \"unchecked\") {\n return { ok: true, origin: request.headers.origin };\n }\n if (typeof policy === \"object\") {\n return validateHttpUpgradeOrigin(request, policy);\n }\n\n const fallbackHost = formatNodeAuthority(options.hostname ?? \"127.0.0.1\", options.listeningPort);\n const host = (options.resolveHost ?? defaultResolveRequestHost)({\n allowedHosts: options.allowedHosts,\n fallbackHost,\n hostPolicy: options.hostPolicy,\n rawHost: request.headers.host,\n });\n const protocol = resolveNodeRequestProtocol({\n encrypted: (request.socket as { encrypted?: boolean }).encrypted === true,\n forwardedProto: request.headers[\"x-forwarded-proto\"],\n trustForwardedProto: options.trustForwardedProto,\n });\n try {\n return validateHttpUpgradeOrigin(request, { allowedOrigins: [`${protocol}://${host}`] });\n } catch {\n return { ok: false, reason: \"malformed-origin\" };\n }\n}\n\nfunction formatNodeAuthority(hostname: string, port: number): string {\n const host = hostname.includes(\":\") && !hostname.startsWith(\"[\") ? `[${hostname}]` : hostname;\n return `${host}:${port}`;\n}\n\nfunction finiteNonNegativeTimeout(\n value: number | undefined,\n fallback: number,\n name: string,\n): number {\n const timeout = value ?? fallback;\n if (\n !Number.isFinite(timeout) ||\n timeout < 0 ||\n !Number.isSafeInteger(timeout) ||\n timeout > 2_147_483_647\n ) {\n throw new TypeError(\n `${name} must be a finite non-negative safe integer no greater than 2147483647.`,\n );\n }\n return timeout;\n}\n\nfunction defaultResolveRequestHost(options: {\n allowedHosts?: readonly string[] | undefined;\n fallbackHost: string;\n hostPolicy?: \"strict\" | \"trusted-proxy\" | undefined;\n rawHost: string | undefined;\n}): string {\n const raw = options.rawHost;\n if (raw === undefined || raw === \"\") return options.fallbackHost;\n if (options.allowedHosts === undefined) {\n return options.hostPolicy === \"strict\" ? options.fallbackHost : raw;\n }\n return options.allowedHosts.includes(raw) ? raw : options.fallbackHost;\n}\n"]}
@@ -1,7 +1,21 @@
1
1
  import type { BuiltPrerenderedRoute } from "./build.js";
2
2
  export declare const PRERENDERED_ROUTE_SCHEMA_VERSION = 4;
3
3
  export declare function isCurrentPrerenderedRoute(value: unknown): value is BuiltPrerenderedRoute;
4
+ /**
5
+ * Returns a navigation variant only after validating its route marker.
6
+ *
7
+ * Stored entries are stable objects during a runtime's lifetime, so the
8
+ * marker scan is cached by identity. Document requests never call this
9
+ * function and therefore never inspect navigation HTML.
10
+ */
11
+ export declare function validatedPrerenderedNavigationHtml(entry: BuiltPrerenderedRoute): string | undefined;
12
+ /**
13
+ * Adds a regenerated navigation variant only when it satisfies the marker
14
+ * contract. Invalid navigation output must not replace a valid document.
15
+ */
16
+ export declare function mergePrerenderedNavigationHtml(entry: BuiltPrerenderedRoute, navigationHtml: string): BuiltPrerenderedRoute;
4
17
  export declare function isVisitorDependentResponse(response: Response): boolean;
18
+ export declare function protectNonceBearingResponse(response: Response): Response;
5
19
  export declare function storedPrerenderedRouteHeaders(headers: Headers): Record<string, string>;
6
20
  export declare function replayedPrerenderedRouteHeaders(entry: BuiltPrerenderedRoute, request: Request): Headers;
7
21
  //# sourceMappingURL=prerender-entry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prerender-entry.d.ts","sourceRoot":"","sources":["../src/prerender-entry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAGxD,eAAO,MAAM,gCAAgC,IAAI,CAAC;AAElD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,qBAAqB,CAsBxF;AAkBD,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAEtE;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUtF;AAED,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,qBAAqB,EAC5B,OAAO,EAAE,OAAO,GACf,OAAO,CAYT"}
1
+ {"version":3,"file":"prerender-entry.d.ts","sourceRoot":"","sources":["../src/prerender-entry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAIxD,eAAO,MAAM,gCAAgC,IAAI,CAAC;AAIlD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,qBAAqB,CAmBxF;AAED;;;;;;GAMG;AACH,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,qBAAqB,GAC3B,MAAM,GAAG,SAAS,CAWpB;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,qBAAqB,EAC5B,cAAc,EAAE,MAAM,GACrB,qBAAqB,CAEvB;AAkBD,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAEtE;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,CAaxE;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUtF;AAED,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,qBAAqB,EAC5B,OAAO,EAAE,OAAO,GACf,OAAO,CAYT"}