react-router 8.4.0 → 8.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/CHANGELOG.md +51 -1
  2. package/dist/development/dom-export.d.ts +2 -1
  3. package/dist/development/dom-export.js +1 -1
  4. package/dist/development/index-react-server-client.js +2 -2
  5. package/dist/development/index-react-server.d.ts +583 -165
  6. package/dist/development/index-react-server.js +27 -19
  7. package/dist/development/index.d.ts +9 -8
  8. package/dist/development/index.js +5 -4
  9. package/dist/development/lib/actions.js +1 -1
  10. package/dist/development/lib/components.js +22 -3
  11. package/dist/development/lib/context.d.ts +6 -1
  12. package/dist/development/lib/context.js +1 -1
  13. package/dist/development/lib/dom/dom.d.ts +3 -0
  14. package/dist/development/lib/dom/dom.js +1 -1
  15. package/dist/development/lib/dom/global.d.ts +35 -0
  16. package/dist/development/lib/dom/lib.d.ts +34 -9
  17. package/dist/development/lib/dom/lib.js +17 -6
  18. package/dist/development/lib/dom/server.js +2 -2
  19. package/dist/development/lib/dom/ssr/components.js +12 -9
  20. package/dist/development/lib/dom/ssr/data.js +1 -1
  21. package/dist/development/lib/dom/ssr/entry.d.ts +9 -2
  22. package/dist/development/lib/dom/ssr/errorBoundaries.js +1 -1
  23. package/dist/development/lib/dom/ssr/fallback.js +1 -1
  24. package/dist/development/lib/dom/ssr/fog-of-war.d.ts +3 -2
  25. package/dist/development/lib/dom/ssr/fog-of-war.js +14 -11
  26. package/dist/development/lib/dom/ssr/hydration.js +1 -1
  27. package/dist/development/lib/dom/ssr/invariant.js +1 -1
  28. package/dist/development/lib/dom/ssr/links.js +1 -1
  29. package/dist/development/lib/dom/ssr/markup.js +1 -1
  30. package/dist/development/lib/dom/ssr/route-discovery.d.ts +120 -0
  31. package/dist/development/lib/dom/ssr/route-discovery.js +415 -0
  32. package/dist/development/lib/dom/ssr/routeModules.js +1 -1
  33. package/dist/development/lib/dom/ssr/routes-test-stub.js +2 -2
  34. package/dist/development/lib/dom/ssr/routes.d.ts +2 -2
  35. package/dist/development/lib/dom/ssr/routes.js +8 -8
  36. package/dist/development/lib/dom/ssr/server.js +3 -1
  37. package/dist/development/lib/dom/ssr/single-fetch.d.ts +28 -2
  38. package/dist/development/lib/dom/ssr/single-fetch.js +73 -55
  39. package/dist/development/lib/dom-export/dom-router-provider.js +1 -1
  40. package/dist/development/lib/dom-export/hydrated-router.d.ts +28 -0
  41. package/dist/development/lib/dom-export/hydrated-router.js +32 -12
  42. package/dist/development/lib/errors.js +1 -1
  43. package/dist/development/lib/hooks.d.ts +3 -3
  44. package/dist/development/lib/hooks.js +4 -4
  45. package/dist/development/lib/href.js +1 -1
  46. package/dist/development/lib/router/history.js +1 -1
  47. package/dist/development/lib/router/instrumentation.js +1 -1
  48. package/dist/development/lib/router/matcher-route-pattern.js +1 -1
  49. package/dist/development/lib/router/matcher-route-pattern.preload.js +1 -1
  50. package/dist/development/lib/router/matcher.js +1 -1
  51. package/dist/development/lib/router/navigation.js +1 -1
  52. package/dist/development/lib/router/router.d.ts +6 -0
  53. package/dist/development/lib/router/router.js +157 -65
  54. package/dist/development/lib/router/url.js +1 -1
  55. package/dist/development/lib/router/utils.d.ts +24 -4
  56. package/dist/development/lib/router/utils.js +4 -3
  57. package/dist/development/lib/rsc/browser.js +2 -2
  58. package/dist/development/lib/rsc/errorBoundaries.js +1 -1
  59. package/dist/development/lib/rsc/html-stream/browser.js +1 -1
  60. package/dist/development/lib/rsc/html-stream/server.js +24 -12
  61. package/dist/development/lib/rsc/route-modules.js +1 -1
  62. package/dist/development/lib/rsc/server.rsc.d.ts +3 -2
  63. package/dist/development/lib/rsc/server.ssr.js +1 -1
  64. package/dist/development/lib/server-runtime/build.d.ts +14 -0
  65. package/dist/development/lib/server-runtime/cookies.js +1 -1
  66. package/dist/development/lib/server-runtime/crypto.js +1 -1
  67. package/dist/development/lib/server-runtime/data.js +1 -1
  68. package/dist/development/lib/server-runtime/dev.js +1 -1
  69. package/dist/development/lib/server-runtime/entry.js +1 -1
  70. package/dist/development/lib/server-runtime/errors.js +1 -1
  71. package/dist/development/lib/server-runtime/headers.js +1 -1
  72. package/dist/development/lib/server-runtime/invariant.js +1 -1
  73. package/dist/development/lib/server-runtime/mode.js +1 -1
  74. package/dist/development/lib/server-runtime/routeMatching.js +1 -1
  75. package/dist/development/lib/server-runtime/routes.d.ts +1 -1
  76. package/dist/development/lib/server-runtime/routes.js +1 -1
  77. package/dist/development/lib/server-runtime/server.js +12 -4
  78. package/dist/development/lib/server-runtime/serverHandoff.d.ts +17 -0
  79. package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
  80. package/dist/development/lib/server-runtime/sessions/cookieStorage.js +1 -1
  81. package/dist/development/lib/server-runtime/sessions/memoryStorage.js +1 -1
  82. package/dist/development/lib/server-runtime/sessions.js +1 -1
  83. package/dist/development/lib/server-runtime/single-fetch.js +1 -1
  84. package/dist/development/lib/server-runtime/urls.js +1 -1
  85. package/dist/development/lib/server-runtime/warnings.js +1 -1
  86. package/dist/development/lib/types/internal.js +1 -1
  87. package/dist/development/lib/types/route-data.d.ts +2 -2
  88. package/dist/development/lib/types/route-module-annotations.d.ts +1 -1
  89. package/dist/development/route-pattern.js +1 -1
  90. package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
  91. package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  92. package/dist/development/vendor/turbo-stream-v2/unflatten.js +1 -1
  93. package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
  94. package/dist/production/dom-export.d.ts +2 -1
  95. package/dist/production/dom-export.js +1 -1
  96. package/dist/production/index-react-server-client.js +2 -2
  97. package/dist/production/index-react-server.d.ts +583 -165
  98. package/dist/production/index-react-server.js +27 -19
  99. package/dist/production/index.d.ts +9 -8
  100. package/dist/production/index.js +5 -4
  101. package/dist/production/lib/actions.js +1 -1
  102. package/dist/production/lib/components.js +22 -3
  103. package/dist/production/lib/context.d.ts +6 -1
  104. package/dist/production/lib/context.js +1 -1
  105. package/dist/production/lib/dom/dom.d.ts +3 -0
  106. package/dist/production/lib/dom/dom.js +1 -1
  107. package/dist/production/lib/dom/global.d.ts +35 -0
  108. package/dist/production/lib/dom/lib.d.ts +34 -9
  109. package/dist/production/lib/dom/lib.js +17 -6
  110. package/dist/production/lib/dom/server.js +2 -2
  111. package/dist/production/lib/dom/ssr/components.js +12 -9
  112. package/dist/production/lib/dom/ssr/data.js +1 -1
  113. package/dist/production/lib/dom/ssr/entry.d.ts +9 -2
  114. package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
  115. package/dist/production/lib/dom/ssr/fallback.js +1 -1
  116. package/dist/production/lib/dom/ssr/fog-of-war.d.ts +3 -2
  117. package/dist/production/lib/dom/ssr/fog-of-war.js +14 -11
  118. package/dist/production/lib/dom/ssr/hydration.js +1 -1
  119. package/dist/production/lib/dom/ssr/invariant.js +1 -1
  120. package/dist/production/lib/dom/ssr/links.js +1 -1
  121. package/dist/production/lib/dom/ssr/markup.js +1 -1
  122. package/dist/production/lib/dom/ssr/route-discovery.d.ts +120 -0
  123. package/dist/production/lib/dom/ssr/route-discovery.js +415 -0
  124. package/dist/production/lib/dom/ssr/routeModules.js +1 -1
  125. package/dist/production/lib/dom/ssr/routes-test-stub.js +2 -2
  126. package/dist/production/lib/dom/ssr/routes.d.ts +2 -2
  127. package/dist/production/lib/dom/ssr/routes.js +8 -8
  128. package/dist/production/lib/dom/ssr/server.js +3 -1
  129. package/dist/production/lib/dom/ssr/single-fetch.d.ts +28 -2
  130. package/dist/production/lib/dom/ssr/single-fetch.js +73 -55
  131. package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
  132. package/dist/production/lib/dom-export/hydrated-router.d.ts +28 -0
  133. package/dist/production/lib/dom-export/hydrated-router.js +32 -12
  134. package/dist/production/lib/errors.js +1 -1
  135. package/dist/production/lib/hooks.d.ts +3 -3
  136. package/dist/production/lib/hooks.js +3 -3
  137. package/dist/production/lib/href.js +1 -1
  138. package/dist/production/lib/router/history.js +1 -1
  139. package/dist/production/lib/router/instrumentation.js +1 -1
  140. package/dist/production/lib/router/matcher-route-pattern.js +1 -1
  141. package/dist/production/lib/router/matcher-route-pattern.preload.js +1 -1
  142. package/dist/production/lib/router/matcher.js +1 -1
  143. package/dist/production/lib/router/navigation.js +1 -1
  144. package/dist/production/lib/router/router.d.ts +6 -0
  145. package/dist/production/lib/router/router.js +157 -65
  146. package/dist/production/lib/router/url.js +1 -1
  147. package/dist/production/lib/router/utils.d.ts +24 -4
  148. package/dist/production/lib/router/utils.js +4 -3
  149. package/dist/production/lib/rsc/browser.js +2 -2
  150. package/dist/production/lib/rsc/errorBoundaries.js +1 -1
  151. package/dist/production/lib/rsc/html-stream/browser.js +1 -1
  152. package/dist/production/lib/rsc/html-stream/server.js +24 -12
  153. package/dist/production/lib/rsc/route-modules.js +1 -1
  154. package/dist/production/lib/rsc/server.rsc.d.ts +3 -2
  155. package/dist/production/lib/rsc/server.ssr.js +1 -1
  156. package/dist/production/lib/server-runtime/build.d.ts +14 -0
  157. package/dist/production/lib/server-runtime/cookies.js +1 -1
  158. package/dist/production/lib/server-runtime/crypto.js +1 -1
  159. package/dist/production/lib/server-runtime/data.js +1 -1
  160. package/dist/production/lib/server-runtime/dev.js +1 -1
  161. package/dist/production/lib/server-runtime/entry.js +1 -1
  162. package/dist/production/lib/server-runtime/errors.js +1 -1
  163. package/dist/production/lib/server-runtime/headers.js +1 -1
  164. package/dist/production/lib/server-runtime/invariant.js +1 -1
  165. package/dist/production/lib/server-runtime/mode.js +1 -1
  166. package/dist/production/lib/server-runtime/routeMatching.js +1 -1
  167. package/dist/production/lib/server-runtime/routes.d.ts +1 -1
  168. package/dist/production/lib/server-runtime/routes.js +1 -1
  169. package/dist/production/lib/server-runtime/server.js +12 -4
  170. package/dist/production/lib/server-runtime/serverHandoff.d.ts +17 -0
  171. package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
  172. package/dist/production/lib/server-runtime/sessions/cookieStorage.js +1 -1
  173. package/dist/production/lib/server-runtime/sessions/memoryStorage.js +1 -1
  174. package/dist/production/lib/server-runtime/sessions.js +1 -1
  175. package/dist/production/lib/server-runtime/single-fetch.js +1 -1
  176. package/dist/production/lib/server-runtime/urls.js +1 -1
  177. package/dist/production/lib/server-runtime/warnings.js +1 -1
  178. package/dist/production/lib/types/internal.js +1 -1
  179. package/dist/production/lib/types/route-data.d.ts +2 -2
  180. package/dist/production/lib/types/route-module-annotations.d.ts +1 -1
  181. package/dist/production/route-pattern.js +1 -1
  182. package/dist/production/vendor/turbo-stream-v2/flatten.js +1 -1
  183. package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  184. package/dist/production/vendor/turbo-stream-v2/unflatten.js +1 -1
  185. package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
  186. package/docs/explanation/lazy-route-discovery.md +4 -0
  187. package/docs/explanation/sessions-and-cookies.md +108 -4
  188. package/docs/how-to/custom-fetch.md +218 -0
  189. package/docs/how-to/data-strategy.md +23 -1
  190. package/docs/how-to/middleware.md +1 -0
  191. package/docs/how-to/optimize-revalidation.md +2 -2
  192. package/docs/how-to/pre-rendering.md +23 -1
  193. package/docs/how-to/route-discovery.md +145 -0
  194. package/docs/how-to/spa.md +2 -0
  195. package/docs/how-to/view-transitions.md +80 -13
  196. package/docs/start/framework/navigating.md +5 -9
  197. package/docs/start/framework/rendering.md +37 -0
  198. package/package.json +9 -1
@@ -0,0 +1,415 @@
1
+ /**
2
+ * react-router v8.5.0
3
+ *
4
+ * Copyright (c) Remix Software Inc.
5
+ *
6
+ * This source code is licensed under the MIT license found in the
7
+ * LICENSE.md file in the root directory of this source tree.
8
+ *
9
+ * @license MIT
10
+ */
11
+ import { createPath, invariant } from "../../router/history.js";
12
+ import { DiscoveryCancelledError, getRoutePattern, prependBasename } from "../../router/utils.js";
13
+ import { createClientRoutes } from "./routes.js";
14
+ import { getManifestPath, getPathsWithAncestors, handleClientVersionMismatch, isFogOfWarEnabled } from "./fog-of-war.js";
15
+ import { useFrameworkContext } from "./components.js";
16
+ import * as React$1 from "react";
17
+ //#region lib/dom/ssr/route-discovery.ts
18
+ var RouteDiscoveryRuntime = class {
19
+ ssrInfo;
20
+ enabled;
21
+ discoveredPaths = /* @__PURE__ */ new Set();
22
+ nextPaths = /* @__PURE__ */ new Set();
23
+ state;
24
+ ready = false;
25
+ registrations = /* @__PURE__ */ new Set();
26
+ listeners = /* @__PURE__ */ new Set();
27
+ fullLoadErrorListeners = /* @__PURE__ */ new Set();
28
+ fullLoad;
29
+ loadedManifest;
30
+ mismatch;
31
+ fetchImplementation;
32
+ loadManifest;
33
+ onError;
34
+ constructor(ssrInfo, { fetch: fetchImplementation = (request) => fetch(request), loadManifest } = {}) {
35
+ this.ssrInfo = ssrInfo;
36
+ this.fetchImplementation = fetchImplementation;
37
+ this.loadManifest = loadManifest ?? (() => this.loadManifestAsset());
38
+ let { manifest, context } = ssrInfo;
39
+ this.enabled = isFogOfWarEnabled(context.routeDiscovery, context.ssr);
40
+ this.state = this.enabled && !manifest.hmr ? "partial" : "complete";
41
+ }
42
+ get router() {
43
+ invariant(this.ssrInfo.router, "Router unavailable for route discovery");
44
+ return this.ssrInfo.router;
45
+ }
46
+ async loadManifestAsset() {
47
+ let { manifest } = this.ssrInfo;
48
+ if (this.loadedManifest) return this.loadedManifest;
49
+ try {
50
+ await import(
51
+ /* @vite-ignore */
52
+ /* webpackIgnore: true */
53
+ manifest.url
54
+ );
55
+ invariant(window.__reactRouterManifest, "Unable to load the client manifest");
56
+ invariant(window.__reactRouterManifest !== manifest, "The full route manifest asset did not provide a manifest.");
57
+ this.loadedManifest = window.__reactRouterManifest;
58
+ return this.loadedManifest;
59
+ } finally {
60
+ window.__reactRouterManifest = manifest;
61
+ }
62
+ }
63
+ subscribe = (listener) => {
64
+ this.listeners.add(listener);
65
+ return () => {
66
+ this.listeners.delete(listener);
67
+ };
68
+ };
69
+ update(state, ready = this.ready) {
70
+ this.state = state;
71
+ this.ready = ready;
72
+ this.listeners.forEach((listener) => listener());
73
+ }
74
+ markReady = () => {
75
+ if (!this.ready) this.update(this.state, true);
76
+ };
77
+ register(registration) {
78
+ this.registrations.add(registration);
79
+ return () => {
80
+ this.registrations.delete(registration);
81
+ };
82
+ }
83
+ isComplete() {
84
+ return this.state === "complete";
85
+ }
86
+ loadAllRoutes = () => {
87
+ if (this.isComplete()) return Promise.resolve();
88
+ if (this.fullLoad) return this.fullLoad;
89
+ let { manifest, routeModules, context } = this.ssrInfo;
90
+ this.fullLoad = Promise.resolve().then(async () => {
91
+ try {
92
+ let fullManifest = await this.loadManifest();
93
+ invariant(fullManifest.version === manifest.version, "The full route manifest does not match the running client version.");
94
+ applyManifestPatches(fullManifest.routes, manifest, routeModules, context.ssr, context.isSpaMode, this.router.patchRoutes, context.unstable_apiOnly === true);
95
+ this.nextPaths.clear();
96
+ this.update("complete");
97
+ } catch (error) {
98
+ this.fullLoadErrorListeners.forEach((listener) => listener(error));
99
+ this.update("partial");
100
+ throw error;
101
+ } finally {
102
+ this.fullLoad = void 0;
103
+ }
104
+ });
105
+ this.update("loading");
106
+ return this.fullLoad;
107
+ };
108
+ reportError(error) {
109
+ if (this.onError) {
110
+ let { location, matches } = this.router.state;
111
+ this.onError(error, {
112
+ location,
113
+ params: matches[0]?.params || {},
114
+ pattern: getRoutePattern(matches)
115
+ });
116
+ } else console.error(error);
117
+ }
118
+ async dispatchMismatch(source, reloadUrl) {
119
+ let { version } = this.ssrInfo.manifest;
120
+ let stopped = false;
121
+ let defaultBehavior;
122
+ let event = {
123
+ source,
124
+ reloadUrl,
125
+ version,
126
+ stopPropagation() {
127
+ stopped = true;
128
+ },
129
+ loadAllRoutes: this.loadAllRoutes,
130
+ defaultBehavior: () => {
131
+ defaultBehavior ??= Promise.resolve().then(async () => {
132
+ if (source !== "imperative" && !this.isComplete()) await handleClientVersionMismatch(true, version, reloadUrl);
133
+ });
134
+ return defaultBehavior;
135
+ }
136
+ };
137
+ let registrations = [...this.registrations].reverse().filter((registration) => registration.current.onManifestMismatch);
138
+ if (registrations.length === 0) return event.defaultBehavior();
139
+ let loadErrors = /* @__PURE__ */ new Set();
140
+ let onLoadError = (error) => loadErrors.add(error);
141
+ this.fullLoadErrorListeners.add(onLoadError);
142
+ try {
143
+ for (let registration of registrations) {
144
+ if (!this.registrations.has(registration)) continue;
145
+ try {
146
+ await registration.current.onManifestMismatch?.(event);
147
+ } catch (error) {
148
+ if (!loadErrors.has(error)) throw error;
149
+ this.reportError(error);
150
+ break;
151
+ }
152
+ if (stopped) break;
153
+ }
154
+ } finally {
155
+ this.fullLoadErrorListeners.delete(onLoadError);
156
+ }
157
+ }
158
+ requestUrl(paths) {
159
+ let { manifest, context } = this.ssrInfo;
160
+ let params = new URLSearchParams();
161
+ params.set("paths", paths.slice().sort().join(","));
162
+ params.set("version", manifest.version);
163
+ let url = new URL(getManifestPath(context.routeDiscovery.manifestPath, context.basename), context.unstable_apiServerOrigin ?? window.location.origin);
164
+ url.search = params.toString();
165
+ return url;
166
+ }
167
+ discoverRoutes = async (paths, options = {}) => {
168
+ if (options.signal?.aborted) return { type: "aborted" };
169
+ if (this.isComplete()) return { type: "success" };
170
+ let pathnames = paths.map((path) => {
171
+ invariant(path.startsWith("/") && !path.startsWith("//") && !path.includes("\\"), "discoverRoutes expects application-root paths beginning with a single '/'.");
172
+ return prependBasename({
173
+ pathname: new URL(path, window.location.origin).pathname,
174
+ basename: this.ssrInfo.context.basename || "/"
175
+ });
176
+ });
177
+ return this.discover(pathnames, "imperative", null, options.signal);
178
+ };
179
+ async discover(paths, source, reloadUrl, signal, patch = this.router.patchRoutes) {
180
+ let { manifest, routeModules, context } = this.ssrInfo;
181
+ if (signal?.aborted) return { type: "aborted" };
182
+ if (this.isComplete()) return { type: "success" };
183
+ let pending = getPathsWithAncestors(paths.filter((path) => !this.discoveredPaths.has(path)));
184
+ if (!pending.length) return { type: "success" };
185
+ let batches = [];
186
+ if (source !== "imperative") {
187
+ if (this.requestUrl(pending).href.length > 7680) {
188
+ this.nextPaths.clear();
189
+ return { type: "success" };
190
+ }
191
+ batches.push(pending);
192
+ } else {
193
+ let batch = [];
194
+ for (let path of pending) {
195
+ if (this.discoveredPaths.has(path)) continue;
196
+ if (this.requestUrl([path]).href.length > 7680) {
197
+ console.warn(`Unable to discover path exceeding the manifest request URL limit: ${path}`);
198
+ continue;
199
+ }
200
+ if (this.requestUrl([...batch, path]).href.length > 7680) {
201
+ batches.push(batch);
202
+ batch = [];
203
+ }
204
+ batch.push(path);
205
+ }
206
+ if (batch.length) batches.push(batch);
207
+ }
208
+ for (let batch of batches) try {
209
+ if (signal?.aborted) return { type: "aborted" };
210
+ if (this.isComplete()) return { type: "success" };
211
+ let response;
212
+ try {
213
+ response = await this.fetchImplementation(new Request(this.requestUrl(batch), { signal }), { type: "manifest" });
214
+ } catch (error) {
215
+ if (signal?.aborted) return { type: "aborted" };
216
+ if (this.isComplete()) return { type: "success" };
217
+ throw error;
218
+ }
219
+ if (signal?.aborted) return { type: "aborted" };
220
+ if (this.isComplete()) return { type: "success" };
221
+ if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);
222
+ if (response.status === 204 && response.headers.has("X-Remix-Reload-Document")) {
223
+ if (this.mismatch || [...this.registrations].some((r) => r.current.onManifestMismatch)) {
224
+ if (!this.mismatch) this.mismatch = Promise.resolve().then(() => this.dispatchMismatch(source, reloadUrl)).finally(() => {
225
+ this.mismatch = void 0;
226
+ });
227
+ await waitForDiscovery(this.mismatch, signal);
228
+ } else await waitForDiscovery(this.dispatchMismatch(source, reloadUrl), signal);
229
+ if (signal?.aborted) return { type: "aborted" };
230
+ return { type: this.isComplete() ? "success" : "version-mismatch" };
231
+ }
232
+ await handleClientVersionMismatch(false, manifest.version, reloadUrl);
233
+ let patches;
234
+ try {
235
+ patches = await response.json();
236
+ } catch (error) {
237
+ if (signal?.aborted) return { type: "aborted" };
238
+ if (this.isComplete()) return { type: "success" };
239
+ throw error;
240
+ }
241
+ if (signal?.aborted) return { type: "aborted" };
242
+ if (this.isComplete()) return { type: "success" };
243
+ applyManifestPatches(patches, manifest, routeModules, context.ssr, context.isSpaMode, patch, context.unstable_apiOnly === true);
244
+ batch.forEach((path) => {
245
+ if (this.discoveredPaths.size >= 1e3) this.discoveredPaths.delete(this.discoveredPaths.values().next().value);
246
+ this.discoveredPaths.add(path);
247
+ });
248
+ } catch (error) {
249
+ if (signal?.aborted) return { type: "aborted" };
250
+ throw error;
251
+ }
252
+ return { type: "success" };
253
+ }
254
+ };
255
+ function waitForDiscovery(promise, signal) {
256
+ return new Promise((resolve, reject) => {
257
+ let abort = () => resolve();
258
+ signal?.addEventListener("abort", abort, { once: true });
259
+ if (signal?.aborted) abort();
260
+ promise.then(() => resolve(), reject).finally(() => signal?.removeEventListener("abort", abort));
261
+ });
262
+ }
263
+ const serverLoad = () => Promise.resolve();
264
+ const serverDiscover = async () => ({ type: "success" });
265
+ /**
266
+ * Control lazy route discovery in Framework Mode with `future.unstable_customRouteDiscovery`
267
+ * enabled. This allows you to imperatively discover routes as well as add custom behavior
268
+ * when a manifest version mismatch is detected. Calling `loadAllRoutes` will load the entire
269
+ * route manifest for the current (potentially stale) client version - this requires that prior
270
+ * client artifacts are still available on your CDN _and_ you have not made breaking changes to
271
+ * the API contracts of your loaders and actions.
272
+ *
273
+ * See the [Route Discovery guide](../../how-to/route-discovery) for more information and examples.
274
+ *
275
+ * @example
276
+ * let { state, discoverRoutes, loadAllRoutes } = unstable_useRouteDiscovery({
277
+ * onManifestMismatch: async (event) => {
278
+ * // Eagerly load the complete route tree for this client without requiring a hard reload
279
+ * await event.loadAllRoutes();
280
+ * },
281
+ * });
282
+ *
283
+ * // Eagerly discover specific routes
284
+ * discoverRoutes(["/a", "/b"]);
285
+ *
286
+ * @name unstable_useRouteDiscovery
287
+ * @public
288
+ * @category Hooks
289
+ * @mode framework
290
+ * @param options An awaited manifest-mismatch handler.
291
+ * @returns The client version, full-manifest loading state, and discovery operations.
292
+ */
293
+ function useRouteDiscovery(options = {}) {
294
+ let context = useFrameworkContext();
295
+ invariant(context.future.unstable_customRouteDiscovery, "unstable_useRouteDiscovery requires future.unstable_customRouteDiscovery to be enabled in react-router.config.ts.");
296
+ invariant(context.manifest.url, "unstable_useRouteDiscovery is not supported in RSC mode.");
297
+ let runtime = context.routeDiscoveryRuntime;
298
+ let registration = React$1.useRef({ current: options });
299
+ let hasHandlers = Boolean(options.onManifestMismatch);
300
+ React$1.useLayoutEffect(() => {
301
+ registration.current.current = options;
302
+ });
303
+ React$1.useLayoutEffect(() => {
304
+ if (hasHandlers) return runtime?.register(registration.current);
305
+ }, [runtime, hasHandlers]);
306
+ let state = context.routeDiscoveryState ?? (isFogOfWarEnabled(context.routeDiscovery, context.ssr) && !context.manifest.hmr ? "partial" : "complete");
307
+ return {
308
+ version: context.manifest.version,
309
+ state,
310
+ loadAllRoutes: runtime?.loadAllRoutes || serverLoad,
311
+ discoverRoutes: runtime?.discoverRoutes || serverDiscover
312
+ };
313
+ }
314
+ function getCustomPatchRoutesOnNavigationFunction(getRouter, runtime) {
315
+ if (!runtime.enabled) return;
316
+ return async ({ path, patch, signal, fetcherKey }) => {
317
+ let { state } = getRouter();
318
+ if ((await runtime.discover([path], fetcherKey != null ? "fetcher" : "navigation", fetcherKey != null ? window.location.href : createPath(state.navigation.location || state.location), signal, patch)).type === "version-mismatch") throw new DiscoveryCancelledError();
319
+ };
320
+ }
321
+ function useCustomRouteDiscovery(runtime, onError) {
322
+ React$1.useLayoutEffect(() => {
323
+ runtime.onError = onError;
324
+ }, [runtime, onError]);
325
+ let [state, setState] = React$1.useState(runtime.state);
326
+ React$1.useLayoutEffect(() => {
327
+ let updateState = () => {
328
+ let nextState = runtime.state;
329
+ React$1.startTransition(() => setState(nextState));
330
+ };
331
+ let unsubscribe = runtime.subscribe(updateState);
332
+ updateState();
333
+ return unsubscribe;
334
+ }, [runtime]);
335
+ React$1.useEffect(() => {
336
+ if (!runtime.enabled || window.navigator?.connection?.saveData === true) return;
337
+ let { discoveredPaths, nextPaths } = runtime;
338
+ function registerElement(el) {
339
+ let path = el.tagName === "FORM" ? el.getAttribute("action") : el.getAttribute("href");
340
+ if (!path) return;
341
+ let pathname = el.tagName === "A" ? el.pathname : new URL(path, window.location.origin).pathname;
342
+ if (!discoveredPaths.has(pathname)) nextPaths.add(pathname);
343
+ }
344
+ let active = false;
345
+ async function fetchPatches() {
346
+ if (!active || runtime.state === "complete") return;
347
+ document.querySelectorAll("a[data-discover], form[data-discover]").forEach(registerElement);
348
+ let lazyPaths = Array.from(nextPaths.keys()).filter((path) => {
349
+ if (discoveredPaths.has(path)) {
350
+ nextPaths.delete(path);
351
+ return false;
352
+ }
353
+ return true;
354
+ });
355
+ if (lazyPaths.length === 0) return;
356
+ try {
357
+ await runtime.discover(lazyPaths, "eager", null);
358
+ } catch (e) {
359
+ runtime.reportError(e);
360
+ }
361
+ }
362
+ let debouncedFetchPatches = debounce(fetchPatches, 100);
363
+ let observer = new MutationObserver(() => debouncedFetchPatches());
364
+ function updateDiscovery() {
365
+ let { ready, state } = runtime;
366
+ if (!ready || state === "complete") {
367
+ active = false;
368
+ observer.disconnect();
369
+ } else if (!active) {
370
+ active = true;
371
+ observer.observe(document.documentElement, {
372
+ subtree: true,
373
+ childList: true,
374
+ attributes: true,
375
+ attributeFilter: [
376
+ "data-discover",
377
+ "href",
378
+ "action"
379
+ ]
380
+ });
381
+ fetchPatches();
382
+ }
383
+ }
384
+ let unsubscribe = runtime.subscribe(updateDiscovery);
385
+ updateDiscovery();
386
+ return () => {
387
+ active = false;
388
+ unsubscribe();
389
+ observer.disconnect();
390
+ };
391
+ }, [runtime]);
392
+ return state;
393
+ }
394
+ function applyManifestPatches(serverPatches, manifest, routeModules, ssr, isSpaMode, patchRoutes, isApiOnly) {
395
+ let knownRoutes = new Set(Object.keys(manifest.routes));
396
+ let patches = Object.values(serverPatches).reduce((acc, route) => {
397
+ if (route && !knownRoutes.has(route.id)) acc[route.id] = route;
398
+ return acc;
399
+ }, {});
400
+ let parentIds = /* @__PURE__ */ new Set();
401
+ Object.values(patches).forEach((patch) => {
402
+ if (patch && (!patch.parentId || !patches[patch.parentId])) parentIds.add(patch.parentId);
403
+ });
404
+ parentIds.forEach((parentId) => patchRoutes(parentId || null, createClientRoutes(patches, routeModules, null, ssr, isSpaMode, parentId, void 0, void 0, isApiOnly)));
405
+ Object.assign(manifest.routes, patches);
406
+ }
407
+ function debounce(callback, wait) {
408
+ let timeoutId;
409
+ return (...args) => {
410
+ window.clearTimeout(timeoutId);
411
+ timeoutId = window.setTimeout(() => callback(...args), wait);
412
+ };
413
+ }
414
+ //#endregion
415
+ export { RouteDiscoveryRuntime, getCustomPatchRoutesOnNavigationFunction, useCustomRouteDiscovery, useRouteDiscovery };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.4.0
2
+ * react-router v8.5.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.4.0
2
+ * react-router v8.5.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -9,8 +9,8 @@
9
9
  * @license MIT
10
10
  */
11
11
  import { RouterContextProvider, convertRoutesToDataRoutes } from "../../router/utils.js";
12
- import { Outlet, RouterProvider, createMemoryRouter, withComponentProps, withErrorBoundaryProps, withHydrateFallbackProps } from "../../components.js";
13
12
  import { FrameworkContext } from "./components.js";
13
+ import { Outlet, RouterProvider, createMemoryRouter, withComponentProps, withErrorBoundaryProps, withHydrateFallbackProps } from "../../components.js";
14
14
  import * as React$1 from "react";
15
15
  //#region lib/dom/ssr/routes-test-stub.tsx
16
16
  /**
@@ -27,7 +27,7 @@ interface EntryRoute extends Route {
27
27
  parentId?: string;
28
28
  }
29
29
  declare function createClientRoutesWithHMRRevalidationOptOut(needsRevalidation: Set<string>, manifest: RouteManifest<EntryRoute>, routeModulesCache: RouteModules, initialState: HydrationState, ssr: boolean, isSpaMode: boolean): DataRouteObject[];
30
- declare function createClientRoutes(manifest: RouteManifest<EntryRoute>, routeModulesCache: RouteModules, initialState: HydrationState | null, ssr: boolean, isSpaMode: boolean, parentId?: string, routesByParentId?: Record<string, Omit<EntryRoute, "children">[]>, needsRevalidation?: Set<string>): DataRouteObject[];
31
- declare function shouldHydrateRouteLoader(routeId: string, clientLoader: ClientLoaderFunction | undefined, hasLoader: boolean, isSpaMode: boolean): boolean;
30
+ declare function createClientRoutes(manifest: RouteManifest<EntryRoute>, routeModulesCache: RouteModules, initialState: HydrationState | null, ssr: boolean, isSpaMode: boolean, parentId?: string, routesByParentId?: Record<string, Omit<EntryRoute, "children">[]>, needsRevalidation?: Set<string>, isApiOnly?: boolean): DataRouteObject[];
31
+ declare function shouldHydrateRouteLoader(routeId: string, clientLoader: ClientLoaderFunction | undefined, hasLoader: boolean, isSpaMode: boolean, isApiOnly?: boolean): boolean;
32
32
  //#endregion
33
33
  export { EntryRoute, Route, createClientRoutes, createClientRoutesWithHMRRevalidationOptOut, shouldHydrateRouteLoader };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.4.0
2
+ * react-router v8.5.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -78,7 +78,7 @@ function noActionDefinedError(type, routeId) {
78
78
  console.error(msg);
79
79
  throw new ErrorResponseImpl(405, "Method Not Allowed", new Error(msg), true);
80
80
  }
81
- function createClientRoutes(manifest, routeModulesCache, initialState, ssr, isSpaMode, parentId = "", routesByParentId = groupRoutesByParentId(manifest), needsRevalidation) {
81
+ function createClientRoutes(manifest, routeModulesCache, initialState, ssr, isSpaMode, parentId = "", routesByParentId = groupRoutesByParentId(manifest), needsRevalidation, isApiOnly = false) {
82
82
  return (routesByParentId[parentId] || []).map((route) => {
83
83
  let routeModule = routeModulesCache[route.id];
84
84
  function fetchServerHandler(singleFetch) {
@@ -154,12 +154,12 @@ function createClientRoutes(manifest, routeModulesCache, initialState, ssr, isSp
154
154
  });
155
155
  });
156
156
  };
157
- dataRoute.loader.hydrate = shouldHydrateRouteLoader(route.id, routeModule.clientLoader, route.hasLoader, isSpaMode);
157
+ dataRoute.loader.hydrate = shouldHydrateRouteLoader(route.id, routeModule.clientLoader, route.hasLoader, isSpaMode, isApiOnly);
158
158
  dataRoute.action = ({ request, params, context, pattern, url }, singleFetch) => {
159
159
  return prefetchStylesAndCallHandler(async () => {
160
160
  invariant(routeModule, "No `routeModule` available for critical-route action");
161
161
  if (!routeModule.clientAction) {
162
- if (isSpaMode) throw noActionDefinedError("clientAction", route.id);
162
+ if (isSpaMode && !isApiOnly) throw noActionDefinedError("clientAction", route.id);
163
163
  return fetchServerAction(singleFetch);
164
164
  }
165
165
  return routeModule.clientAction({
@@ -180,7 +180,7 @@ function createClientRoutes(manifest, routeModulesCache, initialState, ssr, isSp
180
180
  return fetchServerLoader(singleFetch);
181
181
  });
182
182
  if (!route.hasClientAction) dataRoute.action = (_, singleFetch) => prefetchStylesAndCallHandler(() => {
183
- if (isSpaMode) throw noActionDefinedError("clientAction", route.id);
183
+ if (isSpaMode && !isApiOnly) throw noActionDefinedError("clientAction", route.id);
184
184
  return fetchServerAction(singleFetch);
185
185
  });
186
186
  let lazyRoutePromise;
@@ -245,7 +245,7 @@ function createClientRoutes(manifest, routeModulesCache, initialState, ssr, isSp
245
245
  ErrorBoundary: route.hasErrorBoundary ? async () => (await getLazyRoute()).ErrorBoundary : void 0
246
246
  };
247
247
  }
248
- let children = createClientRoutes(manifest, routeModulesCache, initialState, ssr, isSpaMode, route.id, routesByParentId, needsRevalidation);
248
+ let children = createClientRoutes(manifest, routeModulesCache, initialState, ssr, isSpaMode, route.id, routesByParentId, needsRevalidation, isApiOnly);
249
249
  if (children.length > 0) dataRoute.children = children;
250
250
  return dataRoute;
251
251
  });
@@ -296,8 +296,8 @@ function getRouteModuleComponent(routeModule) {
296
296
  if (routeModule.default == null) return void 0;
297
297
  if (!(typeof routeModule.default === "object" && Object.keys(routeModule.default).length === 0)) return routeModule.default;
298
298
  }
299
- function shouldHydrateRouteLoader(routeId, clientLoader, hasLoader, isSpaMode) {
300
- return isSpaMode && routeId !== "root" || clientLoader != null && (clientLoader.hydrate === true || hasLoader !== true);
299
+ function shouldHydrateRouteLoader(routeId, clientLoader, hasLoader, isSpaMode, isApiOnly = false) {
300
+ return isSpaMode && routeId !== "root" || isApiOnly && isSpaMode && hasLoader || clientLoader != null && (clientLoader.hydrate === true || hasLoader !== true);
301
301
  }
302
302
  //#endregion
303
303
  export { createClientRoutes, createClientRoutesWithHMRRevalidationOptOut, createServerRoutes, noActionDefinedError, shouldHydrateRouteLoader };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.4.0
2
+ * react-router v8.5.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -49,7 +49,9 @@ function ServerRouter({ context, url, nonce }) {
49
49
  future: context.future,
50
50
  ssr: context.ssr,
51
51
  isSpaMode: context.isSpaMode,
52
+ unstable_apiOnly: context.unstable_apiOnly,
52
53
  routeDiscovery: context.routeDiscovery,
54
+ unstable_apiServerOrigin: context.unstable_apiServerOrigin,
53
55
  nonce,
54
56
  serializeError: context.serializeError,
55
57
  renderMeta: context.renderMeta
@@ -1,14 +1,40 @@
1
1
 
2
+ import { Action } from "../../router/history.js";
2
3
  import { DataStrategyFunction } from "../../router/utils.js";
3
4
  import { Router } from "../../router/router.js";
4
5
  import { RouteModules } from "./routeModules.js";
5
6
  import { AssetsManifest } from "./entry.js";
6
7
  //#region lib/dom/ssr/single-fetch.d.ts
7
8
  declare const SingleFetchRedirectSymbol: unique symbol;
8
- declare function getTurboStreamSingleFetchDataStrategy(getRouter: () => Router, manifest: AssetsManifest, routeModules: RouteModules, ssr: boolean): DataStrategyFunction;
9
+ /**
10
+ * Describes the operation that initiated an internal router request.
11
+ *
12
+ * Requests that reload fetchers as part of a navigation, revalidation, or
13
+ * initialization redirect keep that initiating `type`. `fetcherKey` identifies
14
+ * the fetcher being loaded, or is `null` when the request targets route loaders
15
+ * or actions.
16
+ *
17
+ * `navigationType` is the history action (`"PUSH"`, `"REPLACE"`, or `"POP"`)
18
+ * for navigation-initiated data requests, including reloaded fetchers. It is
19
+ * `null` for initialization, fetcher, and revalidation requests. Manifest
20
+ * requests have no navigation type.
21
+ */
22
+ type RouterFetchContext = {
23
+ type: "manifest";
24
+ } | {
25
+ type: "navigation";
26
+ fetcherKey: string | null;
27
+ navigationType: Action;
28
+ } | {
29
+ type: "initialization" | "fetcher" | "revalidation";
30
+ fetcherKey: string | null;
31
+ navigationType: null;
32
+ };
33
+ type RouterFetch = (request: Request, context: RouterFetchContext) => Promise<Response>;
34
+ declare function getTurboStreamSingleFetchDataStrategy(getRouter: () => Router, manifest: AssetsManifest, routeModules: RouteModules, ssr: boolean, fetchImplementation?: RouterFetch, isApiOnly?: boolean, serverOrigin?: string): DataStrategyFunction;
9
35
  declare function decodeViaTurboStream(body: ReadableStream<Uint8Array>, global: Window | typeof globalThis): Promise<{
10
36
  done: Promise<void>;
11
37
  value: unknown;
12
38
  }>;
13
39
  //#endregion
14
- export { SingleFetchRedirectSymbol, decodeViaTurboStream, getTurboStreamSingleFetchDataStrategy };
40
+ export { RouterFetch, RouterFetchContext, SingleFetchRedirectSymbol, decodeViaTurboStream, getTurboStreamSingleFetchDataStrategy };