@reckona/mreact-router 0.0.202 → 0.0.204

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 (152) hide show
  1. package/README.md +58 -11
  2. package/dist/actions.d.ts +8 -3
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +125 -45
  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 +5 -2
  11. package/dist/adapters/cloudflare.d.ts.map +1 -1
  12. package/dist/adapters/cloudflare.js +35 -11
  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 +4 -1
  25. package/dist/build.d.ts.map +1 -1
  26. package/dist/build.js +185 -13
  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 +4 -2
  33. package/dist/cache.d.ts.map +1 -1
  34. package/dist/cache.js +216 -17
  35. package/dist/cache.js.map +1 -1
  36. package/dist/cli-options.d.ts +3 -0
  37. package/dist/cli-options.d.ts.map +1 -1
  38. package/dist/cli-options.js +10 -2
  39. package/dist/cli-options.js.map +1 -1
  40. package/dist/cli.js +14 -3
  41. package/dist/cli.js.map +1 -1
  42. package/dist/client.d.ts +5 -0
  43. package/dist/client.d.ts.map +1 -1
  44. package/dist/client.js +43 -3
  45. package/dist/client.js.map +1 -1
  46. package/dist/config.d.ts +3 -0
  47. package/dist/config.d.ts.map +1 -1
  48. package/dist/config.js +21 -2
  49. package/dist/config.js.map +1 -1
  50. package/dist/csp.d.ts +1 -0
  51. package/dist/csp.d.ts.map +1 -1
  52. package/dist/csp.js +9 -0
  53. package/dist/csp.js.map +1 -1
  54. package/dist/dehydrate-policy.d.ts +3 -0
  55. package/dist/dehydrate-policy.d.ts.map +1 -0
  56. package/dist/dehydrate-policy.js +16 -0
  57. package/dist/dehydrate-policy.js.map +1 -0
  58. package/dist/dev-server.d.ts +7 -2
  59. package/dist/dev-server.d.ts.map +1 -1
  60. package/dist/dev-server.js +120 -11
  61. package/dist/dev-server.js.map +1 -1
  62. package/dist/http.d.ts +1 -1
  63. package/dist/http.d.ts.map +1 -1
  64. package/dist/http.js +147 -7
  65. package/dist/http.js.map +1 -1
  66. package/dist/index.d.ts +4 -3
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +1 -0
  69. package/dist/index.js.map +1 -1
  70. package/dist/logger.d.ts +22 -1
  71. package/dist/logger.d.ts.map +1 -1
  72. package/dist/logger.js +22 -8
  73. package/dist/logger.js.map +1 -1
  74. package/dist/metadata.d.ts +8 -0
  75. package/dist/metadata.d.ts.map +1 -1
  76. package/dist/metadata.js +60 -22
  77. package/dist/metadata.js.map +1 -1
  78. package/dist/navigation-marker.d.ts +2 -0
  79. package/dist/navigation-marker.d.ts.map +1 -0
  80. package/dist/navigation-marker.js +357 -0
  81. package/dist/navigation-marker.js.map +1 -0
  82. package/dist/navigation-runtime.d.ts +1 -1
  83. package/dist/navigation-runtime.d.ts.map +1 -1
  84. package/dist/navigation-runtime.js +1 -1
  85. package/dist/navigation-runtime.js.map +1 -1
  86. package/dist/navigation.d.ts +1 -0
  87. package/dist/navigation.d.ts.map +1 -1
  88. package/dist/navigation.js +1 -1
  89. package/dist/navigation.js.map +1 -1
  90. package/dist/node-server.d.ts +5 -2
  91. package/dist/node-server.d.ts.map +1 -1
  92. package/dist/node-server.js +75 -6
  93. package/dist/node-server.js.map +1 -1
  94. package/dist/prerender-entry.d.ts +17 -1
  95. package/dist/prerender-entry.d.ts.map +1 -1
  96. package/dist/prerender-entry.js +98 -3
  97. package/dist/prerender-entry.js.map +1 -1
  98. package/dist/render.d.ts +16 -2
  99. package/dist/render.d.ts.map +1 -1
  100. package/dist/render.js +142 -135
  101. package/dist/render.js.map +1 -1
  102. package/dist/serve.d.ts +11 -2
  103. package/dist/serve.d.ts.map +1 -1
  104. package/dist/serve.js +67 -41
  105. package/dist/serve.js.map +1 -1
  106. package/dist/session.d.ts.map +1 -1
  107. package/dist/session.js +52 -19
  108. package/dist/session.js.map +1 -1
  109. package/dist/upgrade.d.ts +59 -2
  110. package/dist/upgrade.d.ts.map +1 -1
  111. package/dist/upgrade.js +282 -1
  112. package/dist/upgrade.js.map +1 -1
  113. package/dist/vite-config.d.ts +2 -0
  114. package/dist/vite-config.d.ts.map +1 -1
  115. package/dist/vite-config.js +2 -0
  116. package/dist/vite-config.js.map +1 -1
  117. package/dist/vite.d.ts +4 -1
  118. package/dist/vite.d.ts.map +1 -1
  119. package/dist/vite.js +117 -6
  120. package/dist/vite.js.map +1 -1
  121. package/package.json +11 -11
  122. package/src/actions.ts +146 -60
  123. package/src/adapters/aws-lambda.ts +426 -115
  124. package/src/adapters/cloudflare.ts +58 -12
  125. package/src/adapters/edge.ts +2 -0
  126. package/src/adapters/node.ts +8 -10
  127. package/src/adapters/static.ts +31 -2
  128. package/src/build.ts +217 -13
  129. package/src/built-runtime.ts +28 -0
  130. package/src/cache.ts +272 -19
  131. package/src/cli-options.ts +15 -2
  132. package/src/cli.ts +16 -2
  133. package/src/client.ts +47 -3
  134. package/src/config.ts +43 -6
  135. package/src/csp.ts +11 -0
  136. package/src/dehydrate-policy.ts +23 -0
  137. package/src/dev-server.ts +188 -15
  138. package/src/http.ts +172 -6
  139. package/src/index.ts +14 -1
  140. package/src/logger.ts +47 -10
  141. package/src/metadata.ts +125 -60
  142. package/src/navigation-marker.ts +423 -0
  143. package/src/navigation-runtime.ts +1 -0
  144. package/src/navigation.ts +1 -1
  145. package/src/node-server.ts +128 -22
  146. package/src/prerender-entry.ts +127 -5
  147. package/src/render.ts +859 -749
  148. package/src/serve.ts +112 -82
  149. package/src/session.ts +56 -25
  150. package/src/upgrade.ts +402 -3
  151. package/src/vite-config.ts +6 -0
  152. package/src/vite.ts +157 -5
@@ -1,6 +1,10 @@
1
1
  import type { BuiltPrerenderedRoute } from "./build.js";
2
+ import { responseHeadersContainCspNonce } from "./csp.js";
3
+ import { hasNavigationRouteMarker } from "./navigation-marker.js";
2
4
 
3
- export const PRERENDERED_ROUTE_SCHEMA_VERSION = 2;
5
+ export const PRERENDERED_ROUTE_SCHEMA_VERSION = 4;
6
+
7
+ const validatedNavigationHtmlByEntry = new WeakMap<object, string | null>();
4
8
 
5
9
  export function isCurrentPrerenderedRoute(value: unknown): value is BuiltPrerenderedRoute {
6
10
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
@@ -12,6 +16,9 @@ export function isCurrentPrerenderedRoute(value: unknown): value is BuiltPrerend
12
16
  entry.schemaVersion === PRERENDERED_ROUTE_SCHEMA_VERSION &&
13
17
  isStringRecord(entry.headers) &&
14
18
  isShareableHeaderRecord(entry.headers) &&
19
+ hasRequiredNavigationVary(entry.headers) &&
20
+ !hasStoredHstsHeader(entry.headers) &&
21
+ isCanonicalHstsHeader(entry.strictTransportSecurity) &&
15
22
  typeof entry.html === "string" &&
16
23
  typeof entry.status === "number" &&
17
24
  Number.isInteger(entry.status) &&
@@ -20,10 +27,120 @@ export function isCurrentPrerenderedRoute(value: unknown): value is BuiltPrerend
20
27
  );
21
28
  }
22
29
 
30
+ /**
31
+ * Returns a navigation variant only after validating its route marker.
32
+ *
33
+ * Stored entries are stable objects during a runtime's lifetime, so the
34
+ * marker scan is cached by identity. Document requests never call this
35
+ * function and therefore never inspect navigation HTML.
36
+ */
37
+ export function validatedPrerenderedNavigationHtml(
38
+ entry: BuiltPrerenderedRoute,
39
+ ): string | undefined {
40
+ const cached = validatedNavigationHtmlByEntry.get(entry);
41
+ if (cached !== undefined) {
42
+ return cached === null ? undefined : cached;
43
+ }
44
+
45
+ const candidate = entry.navigationHtml;
46
+ const validated =
47
+ typeof candidate === "string" && hasNavigationRouteMarker(candidate) ? candidate : null;
48
+ validatedNavigationHtmlByEntry.set(entry, validated);
49
+ return validated === null ? undefined : validated;
50
+ }
51
+
52
+ /**
53
+ * Adds a regenerated navigation variant only when it satisfies the marker
54
+ * contract. Invalid navigation output must not replace a valid document.
55
+ */
56
+ export function mergePrerenderedNavigationHtml(
57
+ entry: BuiltPrerenderedRoute,
58
+ navigationHtml: string,
59
+ ): BuiltPrerenderedRoute {
60
+ return hasNavigationRouteMarker(navigationHtml) ? { ...entry, navigationHtml } : entry;
61
+ }
62
+
63
+ function hasStoredHstsHeader(headers: Record<string, string>): boolean {
64
+ try {
65
+ return new Headers(headers).has("strict-transport-security");
66
+ } catch {
67
+ return true;
68
+ }
69
+ }
70
+
71
+ function isCanonicalHstsHeader(value: unknown): value is string | undefined {
72
+ return (
73
+ value === undefined ||
74
+ (typeof value === "string" &&
75
+ /^max-age=(?:0|[1-9][0-9]*)(?:; includeSubDomains)?(?:; preload)?$/.test(value))
76
+ );
77
+ }
78
+
23
79
  export function isVisitorDependentResponse(response: Response): boolean {
24
80
  return isVisitorDependentHeaders(response.headers);
25
81
  }
26
82
 
83
+ export function protectNonceBearingResponse(response: Response): Response {
84
+ if (!responseHeadersContainCspNonce(response.headers)) {
85
+ return response;
86
+ }
87
+
88
+ const headers = new Headers(response.headers);
89
+ headers.set("cache-control", "private, no-store");
90
+ headers.set("x-mreact-cache", "DYNAMIC");
91
+ return new Response(response.body, {
92
+ headers,
93
+ status: response.status,
94
+ statusText: response.statusText,
95
+ });
96
+ }
97
+
98
+ export function storedPrerenderedRouteHeaders(headers: Headers): Record<string, string> {
99
+ const stored: Record<string, string> = {};
100
+
101
+ headers.forEach((value, key) => {
102
+ if (key !== "strict-transport-security") {
103
+ stored[key] = value;
104
+ }
105
+ });
106
+
107
+ return stored;
108
+ }
109
+
110
+ export function replayedPrerenderedRouteHeaders(
111
+ entry: BuiltPrerenderedRoute,
112
+ request: Request,
113
+ ): Headers {
114
+ const headers = new Headers(entry.headers);
115
+
116
+ if (!hasNavigationVary(headers)) {
117
+ headers.append("vary", "x-mreact-navigation");
118
+ }
119
+
120
+ if (entry.strictTransportSecurity !== undefined && request.url.startsWith("https://")) {
121
+ headers.set("strict-transport-security", entry.strictTransportSecurity);
122
+ }
123
+
124
+ return headers;
125
+ }
126
+
127
+ function hasRequiredNavigationVary(headers: Record<string, string>): boolean {
128
+ try {
129
+ return hasNavigationVary(new Headers(headers));
130
+ } catch {
131
+ return false;
132
+ }
133
+ }
134
+
135
+ function hasNavigationVary(headers: Headers): boolean {
136
+ return (
137
+ headers
138
+ .get("vary")
139
+ ?.split(",")
140
+ .some((value) => value.trim().toLowerCase() === "x-mreact-navigation") === true
141
+ );
142
+ }
143
+
27
144
  function isShareableHeaderRecord(headers: Record<string, string>): boolean {
28
145
  try {
29
146
  return !isVisitorDependentHeaders(new Headers(headers));
@@ -34,14 +151,19 @@ function isShareableHeaderRecord(headers: Record<string, string>): boolean {
34
151
 
35
152
  function isVisitorDependentHeaders(headers: Headers): boolean {
36
153
  const cacheControl = headers.get("cache-control") ?? "";
37
- const forbidsSharedStorage = cacheControl.split(",").some((directive) =>
38
- /^(?:private|no-cache|no-store)(?:=|$)/i.test(directive.trim()),
39
- );
154
+ const vary = headers.get("vary");
155
+ const hasVisitorDependentVary =
156
+ vary !== null &&
157
+ vary.split(",").some((value) => value.trim().toLowerCase() !== "x-mreact-navigation");
158
+ const forbidsSharedStorage = cacheControl
159
+ .split(",")
160
+ .some((directive) => /^(?:private|no-cache|no-store)(?:=|$)/i.test(directive.trim()));
40
161
 
41
162
  return (
42
163
  headers.get("x-mreact-cache")?.toUpperCase() === "DYNAMIC" ||
43
164
  headers.has("set-cookie") ||
44
- headers.has("vary") ||
165
+ responseHeadersContainCspNonce(headers) ||
166
+ hasVisitorDependentVary ||
45
167
  forbidsSharedStorage
46
168
  );
47
169
  }