react-router 8.0.1 → 8.2.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 (148) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/development/dom-export.js +1 -1
  3. package/dist/development/index-react-server-client.js +1 -1
  4. package/dist/development/index-react-server.d.ts +35 -9
  5. package/dist/development/index-react-server.js +190 -157
  6. package/dist/development/index.d.ts +2 -2
  7. package/dist/development/index.js +1 -1
  8. package/dist/development/lib/actions.js +1 -1
  9. package/dist/development/lib/components.js +1 -1
  10. package/dist/development/lib/context.js +1 -1
  11. package/dist/development/lib/dom/dom.js +1 -1
  12. package/dist/development/lib/dom/lib.js +2 -2
  13. package/dist/development/lib/dom/server.js +1 -1
  14. package/dist/development/lib/dom/ssr/components.js +1 -1
  15. package/dist/development/lib/dom/ssr/data.js +1 -1
  16. package/dist/development/lib/dom/ssr/errorBoundaries.js +1 -1
  17. package/dist/development/lib/dom/ssr/fallback.js +1 -1
  18. package/dist/development/lib/dom/ssr/fog-of-war.js +1 -1
  19. package/dist/development/lib/dom/ssr/hydration.js +1 -1
  20. package/dist/development/lib/dom/ssr/invariant.js +1 -1
  21. package/dist/development/lib/dom/ssr/links.js +1 -1
  22. package/dist/development/lib/dom/ssr/markup.js +1 -1
  23. package/dist/development/lib/dom/ssr/routeModules.js +1 -1
  24. package/dist/development/lib/dom/ssr/routes-test-stub.js +1 -1
  25. package/dist/development/lib/dom/ssr/routes.js +1 -1
  26. package/dist/development/lib/dom/ssr/server.js +1 -1
  27. package/dist/development/lib/dom/ssr/single-fetch.js +1 -1
  28. package/dist/development/lib/dom-export/dom-router-provider.js +1 -1
  29. package/dist/development/lib/dom-export/hydrated-router.js +1 -1
  30. package/dist/development/lib/errors.js +1 -1
  31. package/dist/development/lib/hooks.d.ts +33 -6
  32. package/dist/development/lib/hooks.js +34 -7
  33. package/dist/development/lib/href.js +6 -3
  34. package/dist/development/lib/router/history.js +1 -1
  35. package/dist/development/lib/router/instrumentation.d.ts +33 -8
  36. package/dist/development/lib/router/instrumentation.js +176 -99
  37. package/dist/development/lib/router/router.js +20 -20
  38. package/dist/development/lib/router/url.js +1 -1
  39. package/dist/development/lib/router/utils.js +21 -4
  40. package/dist/development/lib/rsc/browser.js +1 -1
  41. package/dist/development/lib/rsc/errorBoundaries.js +1 -1
  42. package/dist/development/lib/rsc/html-stream/browser.js +1 -1
  43. package/dist/development/lib/rsc/html-stream/server.js +1 -1
  44. package/dist/development/lib/rsc/route-modules.js +1 -1
  45. package/dist/development/lib/rsc/server.rsc.d.ts +3 -2
  46. package/dist/development/lib/rsc/server.ssr.js +1 -1
  47. package/dist/development/lib/server-runtime/cookies.js +1 -1
  48. package/dist/development/lib/server-runtime/crypto.js +1 -1
  49. package/dist/development/lib/server-runtime/data.js +1 -1
  50. package/dist/development/lib/server-runtime/dev.js +1 -1
  51. package/dist/development/lib/server-runtime/entry.js +1 -1
  52. package/dist/development/lib/server-runtime/errors.js +1 -1
  53. package/dist/development/lib/server-runtime/headers.js +1 -1
  54. package/dist/development/lib/server-runtime/invariant.js +1 -1
  55. package/dist/development/lib/server-runtime/mode.js +1 -1
  56. package/dist/development/lib/server-runtime/routeMatching.js +1 -1
  57. package/dist/development/lib/server-runtime/routes.js +1 -1
  58. package/dist/development/lib/server-runtime/server.js +12 -5
  59. package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
  60. package/dist/development/lib/server-runtime/sessions/cookieStorage.js +1 -1
  61. package/dist/development/lib/server-runtime/sessions/memoryStorage.js +1 -1
  62. package/dist/development/lib/server-runtime/sessions.js +1 -1
  63. package/dist/development/lib/server-runtime/single-fetch.js +1 -1
  64. package/dist/development/lib/server-runtime/urls.js +1 -1
  65. package/dist/development/lib/server-runtime/warnings.js +1 -1
  66. package/dist/development/lib/types/internal.js +1 -1
  67. package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
  68. package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  69. package/dist/development/vendor/turbo-stream-v2/unflatten.js +1 -1
  70. package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
  71. package/dist/production/dom-export.js +1 -1
  72. package/dist/production/index-react-server-client.js +1 -1
  73. package/dist/production/index-react-server.d.ts +35 -9
  74. package/dist/production/index-react-server.js +190 -157
  75. package/dist/production/index.d.ts +2 -2
  76. package/dist/production/index.js +1 -1
  77. package/dist/production/lib/actions.js +1 -1
  78. package/dist/production/lib/components.js +1 -1
  79. package/dist/production/lib/context.js +1 -1
  80. package/dist/production/lib/dom/dom.js +1 -1
  81. package/dist/production/lib/dom/lib.js +2 -2
  82. package/dist/production/lib/dom/server.js +1 -1
  83. package/dist/production/lib/dom/ssr/components.js +1 -1
  84. package/dist/production/lib/dom/ssr/data.js +1 -1
  85. package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
  86. package/dist/production/lib/dom/ssr/fallback.js +1 -1
  87. package/dist/production/lib/dom/ssr/fog-of-war.js +1 -1
  88. package/dist/production/lib/dom/ssr/hydration.js +1 -1
  89. package/dist/production/lib/dom/ssr/invariant.js +1 -1
  90. package/dist/production/lib/dom/ssr/links.js +1 -1
  91. package/dist/production/lib/dom/ssr/markup.js +1 -1
  92. package/dist/production/lib/dom/ssr/routeModules.js +1 -1
  93. package/dist/production/lib/dom/ssr/routes-test-stub.js +1 -1
  94. package/dist/production/lib/dom/ssr/routes.js +1 -1
  95. package/dist/production/lib/dom/ssr/server.js +1 -1
  96. package/dist/production/lib/dom/ssr/single-fetch.js +1 -1
  97. package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
  98. package/dist/production/lib/dom-export/hydrated-router.js +1 -1
  99. package/dist/production/lib/errors.js +1 -1
  100. package/dist/production/lib/hooks.d.ts +33 -6
  101. package/dist/production/lib/hooks.js +34 -7
  102. package/dist/production/lib/href.js +6 -3
  103. package/dist/production/lib/router/history.js +1 -1
  104. package/dist/production/lib/router/instrumentation.d.ts +33 -8
  105. package/dist/production/lib/router/instrumentation.js +176 -99
  106. package/dist/production/lib/router/router.js +20 -20
  107. package/dist/production/lib/router/url.js +1 -1
  108. package/dist/production/lib/router/utils.js +21 -4
  109. package/dist/production/lib/rsc/browser.js +1 -1
  110. package/dist/production/lib/rsc/errorBoundaries.js +1 -1
  111. package/dist/production/lib/rsc/html-stream/browser.js +1 -1
  112. package/dist/production/lib/rsc/html-stream/server.js +1 -1
  113. package/dist/production/lib/rsc/route-modules.js +1 -1
  114. package/dist/production/lib/rsc/server.rsc.d.ts +3 -2
  115. package/dist/production/lib/rsc/server.ssr.js +1 -1
  116. package/dist/production/lib/server-runtime/cookies.js +1 -1
  117. package/dist/production/lib/server-runtime/crypto.js +1 -1
  118. package/dist/production/lib/server-runtime/data.js +1 -1
  119. package/dist/production/lib/server-runtime/dev.js +1 -1
  120. package/dist/production/lib/server-runtime/entry.js +1 -1
  121. package/dist/production/lib/server-runtime/errors.js +1 -1
  122. package/dist/production/lib/server-runtime/headers.js +1 -1
  123. package/dist/production/lib/server-runtime/invariant.js +1 -1
  124. package/dist/production/lib/server-runtime/mode.js +1 -1
  125. package/dist/production/lib/server-runtime/routeMatching.js +1 -1
  126. package/dist/production/lib/server-runtime/routes.js +1 -1
  127. package/dist/production/lib/server-runtime/server.js +12 -5
  128. package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
  129. package/dist/production/lib/server-runtime/sessions/cookieStorage.js +1 -1
  130. package/dist/production/lib/server-runtime/sessions/memoryStorage.js +1 -1
  131. package/dist/production/lib/server-runtime/sessions.js +1 -1
  132. package/dist/production/lib/server-runtime/single-fetch.js +1 -1
  133. package/dist/production/lib/server-runtime/urls.js +1 -1
  134. package/dist/production/lib/server-runtime/warnings.js +1 -1
  135. package/dist/production/lib/types/internal.js +1 -1
  136. package/dist/production/vendor/turbo-stream-v2/flatten.js +1 -1
  137. package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  138. package/dist/production/vendor/turbo-stream-v2/unflatten.js +1 -1
  139. package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
  140. package/docs/explanation/hot-module-replacement.md +1 -1
  141. package/docs/how-to/data-strategy.md +1 -1
  142. package/docs/how-to/instrumentation.md +131 -26
  143. package/docs/start/framework/deploying.md +4 -0
  144. package/docs/upgrading/component-routes.md +2 -2
  145. package/docs/upgrading/future.md +33 -0
  146. package/docs/upgrading/router-provider.md +6 -6
  147. package/docs/upgrading/v7.md +39 -22
  148. package/package.json +1 -1
@@ -143,12 +143,16 @@ interface NavigateFunction {
143
143
  * All properties are optional.
144
144
  *
145
145
  * ```tsx
146
- * navigate({
147
- * pathname: "/some/route",
148
- * search: "?search=param",
149
- * hash: "#hash",
150
- * state: { some: "state" },
151
- * });
146
+ * navigate(
147
+ * {
148
+ * pathname: "/some/route",
149
+ * search: "?search=param",
150
+ * hash: "#hash",
151
+ * },
152
+ * {
153
+ * state: { some: "state" },
154
+ * },
155
+ * );
152
156
  * ```
153
157
  *
154
158
  * If you use `state`, that will be available on the {@link Location} object on
@@ -576,6 +580,29 @@ declare function useRevalidator(): {
576
580
  * parent/child routes or the route [`handle`](../../start/framework/route-module#handle)
577
581
  * property
578
582
  *
583
+ * Pairing the route `handle` with `useMatches` gets very powerful since you can put
584
+ * whatever you want on a route handle and have access to `useMatches` anywhere.
585
+ * Please see the [handle](../../how-to/using-handle) documentation for an example
586
+ * of breadcrumbs via `useMatches`/`handle`.
587
+ *
588
+ * ```tsx
589
+ * import { useMatches } from "react-router";
590
+ *
591
+ * function SomeComponent() {
592
+ * const matches = useMatches();
593
+ * // matches[i].id // route id
594
+ * // matches[i].pathname // the portion of the URL the route matched
595
+ * // matches[i].params // the parsed params from the URL
596
+ * // matches[i].loaderData // the data from the loader
597
+ * // matches[i].handle // the route handle with any app specific data
598
+ * }
599
+ * ```
600
+ *
601
+ * <docs-info>useMatches only works with a data router like `createBrowserRouter`,
602
+ * since they know the full route tree up front and can provide all of the current
603
+ * matches. Additionally, `useMatches` will not match down into any descendant route
604
+ * trees since the router isn't aware of the descendant routes.</docs-info>
605
+ *
579
606
  * @public
580
607
  * @category Hooks
581
608
  * @mode framework
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.0.1
2
+ * react-router v8.2.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -162,12 +162,16 @@ const navigateEffectWarning = "You should call navigate() in a React.useEffect()
162
162
  * All properties are optional.
163
163
  *
164
164
  * ```tsx
165
- * navigate({
166
- * pathname: "/some/route",
167
- * search: "?search=param",
168
- * hash: "#hash",
169
- * state: { some: "state" },
170
- * });
165
+ * navigate(
166
+ * {
167
+ * pathname: "/some/route",
168
+ * search: "?search=param",
169
+ * hash: "#hash",
170
+ * },
171
+ * {
172
+ * state: { some: "state" },
173
+ * },
174
+ * );
171
175
  * ```
172
176
  *
173
177
  * If you use `state`, that will be available on the {@link Location} object on
@@ -904,6 +908,29 @@ function useRevalidator() {
904
908
  * parent/child routes or the route [`handle`](../../start/framework/route-module#handle)
905
909
  * property
906
910
  *
911
+ * Pairing the route `handle` with `useMatches` gets very powerful since you can put
912
+ * whatever you want on a route handle and have access to `useMatches` anywhere.
913
+ * Please see the [handle](../../how-to/using-handle) documentation for an example
914
+ * of breadcrumbs via `useMatches`/`handle`.
915
+ *
916
+ * ```tsx
917
+ * import { useMatches } from "react-router";
918
+ *
919
+ * function SomeComponent() {
920
+ * const matches = useMatches();
921
+ * // matches[i].id // route id
922
+ * // matches[i].pathname // the portion of the URL the route matched
923
+ * // matches[i].params // the parsed params from the URL
924
+ * // matches[i].loaderData // the data from the loader
925
+ * // matches[i].handle // the route handle with any app specific data
926
+ * }
927
+ * ```
928
+ *
929
+ * <docs-info>useMatches only works with a data router like `createBrowserRouter`,
930
+ * since they know the full route tree up front and can provide all of the current
931
+ * matches. Additionally, `useMatches` will not match down into any descendant route
932
+ * trees since the router isn't aware of the descendant routes.</docs-info>
933
+ *
907
934
  * @public
908
935
  * @category Hooks
909
936
  * @mode framework
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.0.1
2
+ * react-router v8.2.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -9,6 +9,9 @@
9
9
  * @license MIT
10
10
  */
11
11
  //#region lib/href.ts
12
+ function stringify(p) {
13
+ return p == null ? "" : typeof p === "string" ? p : String(p);
14
+ }
12
15
  /**
13
16
  Returns a resolved URL path for the specified route.
14
17
 
@@ -25,11 +28,11 @@ function href(path, ...args) {
25
28
  const isRequired = questionMark === void 0;
26
29
  const value = params?.[param];
27
30
  if (isRequired && value === void 0) throw new Error(`Path '${path}' requires param '${param}' but it was not provided`);
28
- return value === void 0 ? "" : "/" + value;
31
+ return value == null ? "" : "/" + encodeURIComponent(stringify(value));
29
32
  });
30
33
  if (path.endsWith("*")) {
31
34
  const value = params?.["*"];
32
- if (value !== void 0) result += "/" + value;
35
+ if (value !== void 0) result += "/" + stringify(value).split("/").map(encodeURIComponent).join("/");
33
36
  }
34
37
  return result || "/";
35
38
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.0.1
2
+ * react-router v8.2.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -12,6 +12,19 @@ type ClientInstrumentation = {
12
12
  type InstrumentRequestHandlerFunction = (handler: InstrumentableRequestHandler) => void;
13
13
  type InstrumentRouterFunction = (router: InstrumentableRouter) => void;
14
14
  type InstrumentRouteFunction = (route: InstrumentableRoute) => void;
15
+ /**
16
+ * Route metadata available after React Router has matched an instrumented
17
+ * request, navigation, or fetcher call.
18
+ */
19
+ type InstrumentationResultMeta = {
20
+ url: LoaderFunctionArgs["url"];
21
+ pattern: string;
22
+ params: LoaderFunctionArgs["params"];
23
+ };
24
+ /**
25
+ * Result returned by route-level instrumented handler calls, such as
26
+ * instrumented loaders, actions, middleware, and lazy route functions.
27
+ */
15
28
  type InstrumentationHandlerResult = {
16
29
  status: "success";
17
30
  error: undefined;
@@ -19,7 +32,21 @@ type InstrumentationHandlerResult = {
19
32
  status: "error";
20
33
  error: Error;
21
34
  };
22
- type InstrumentFunction<T> = (handler: () => Promise<InstrumentationHandlerResult>, info: T) => Promise<void>;
35
+ /**
36
+ * Result returned by client-side router instrumented navigation and fetcher
37
+ * calls.
38
+ */
39
+ type InstrumentationClientRouterResult = InstrumentationHandlerResult & {
40
+ meta: InstrumentationResultMeta | undefined;
41
+ };
42
+ /**
43
+ * Result returned by server request handler instrumentation.
44
+ */
45
+ type InstrumentationServerHandlerResult = InstrumentationHandlerResult & {
46
+ statusCode: number;
47
+ meta: InstrumentationResultMeta | undefined;
48
+ };
49
+ type InstrumentFunction<T, TInnerResult = InstrumentationHandlerResult> = (handler: () => Promise<TInnerResult>, info: T) => Promise<void>;
23
50
  type ReadonlyRequest = {
24
51
  method: string;
25
52
  url: string;
@@ -42,18 +69,16 @@ type RouteInstrumentations = {
42
69
  action?: InstrumentFunction<RouteHandlerInstrumentationInfo>;
43
70
  };
44
71
  type RouteLazyInstrumentationInfo = undefined;
45
- type RouteHandlerInstrumentationInfo = Readonly<{
72
+ type RouteHandlerInstrumentationInfo = Readonly<Omit<LoaderFunctionArgs, "request" | "context"> & {
46
73
  request: ReadonlyRequest;
47
- params: LoaderFunctionArgs["params"];
48
- pattern: string;
49
74
  context: ReadonlyContext;
50
75
  }>;
51
76
  type InstrumentableRouter = {
52
77
  instrument(instrumentations: RouterInstrumentations): void;
53
78
  };
54
79
  type RouterInstrumentations = {
55
- navigate?: InstrumentFunction<RouterNavigationInstrumentationInfo>;
56
- fetch?: InstrumentFunction<RouterFetchInstrumentationInfo>;
80
+ navigate?: InstrumentFunction<RouterNavigationInstrumentationInfo, InstrumentationClientRouterResult>;
81
+ fetch?: InstrumentFunction<RouterFetchInstrumentationInfo, InstrumentationClientRouterResult>;
57
82
  };
58
83
  type RouterNavigationInstrumentationInfo = Readonly<{
59
84
  to: string | number;
@@ -76,11 +101,11 @@ type InstrumentableRequestHandler = {
76
101
  instrument(instrumentations: RequestHandlerInstrumentations): void;
77
102
  };
78
103
  type RequestHandlerInstrumentations = {
79
- request?: InstrumentFunction<RequestHandlerInstrumentationInfo>;
104
+ request?: InstrumentFunction<RequestHandlerInstrumentationInfo, InstrumentationServerHandlerResult>;
80
105
  };
81
106
  type RequestHandlerInstrumentationInfo = Readonly<{
82
107
  request: ReadonlyRequest;
83
108
  context: ReadonlyContext | undefined;
84
109
  }>;
85
110
  //#endregion
86
- export { ClientInstrumentation, InstrumentRequestHandlerFunction, InstrumentRouteFunction, InstrumentRouterFunction, InstrumentationHandlerResult, ServerInstrumentation };
111
+ export { ClientInstrumentation, InstrumentRequestHandlerFunction, InstrumentRouteFunction, InstrumentRouterFunction, InstrumentationClientRouterResult, InstrumentationHandlerResult, InstrumentationServerHandlerResult, ServerInstrumentation };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.0.1
2
+ * react-router v8.2.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -9,8 +9,11 @@
9
9
  * @license MIT
10
10
  */
11
11
  import { createPath, invariant } from "./history.js";
12
+ import { RouterContextProvider, createContext } from "./utils.js";
12
13
  //#region lib/router/instrumentation.ts
13
14
  const UninstrumentedSymbol = Symbol("Uninstrumented");
15
+ const instrumentationResultMetaContext = createContext();
16
+ let instrumentationClientResultMetaReceivers = /* @__PURE__ */ new WeakMap();
14
17
  function getRouteInstrumentationUpdates(fns, route) {
15
18
  let aggregated = {
16
19
  lazy: [],
@@ -26,50 +29,67 @@ function getRouteInstrumentationUpdates(fns, route) {
26
29
  index: route.index,
27
30
  path: route.path,
28
31
  instrument(i) {
29
- let keys = Object.keys(aggregated);
30
- for (let key of keys) if (i[key]) aggregated[key].push(i[key]);
32
+ if (i.lazy != null) aggregated.lazy.push(i.lazy);
33
+ if (i["lazy.loader"] != null) aggregated["lazy.loader"].push(i["lazy.loader"]);
34
+ if (i["lazy.action"] != null) aggregated["lazy.action"].push(i["lazy.action"]);
35
+ if (i["lazy.middleware"] != null) aggregated["lazy.middleware"].push(i["lazy.middleware"]);
36
+ if (i.middleware != null) aggregated.middleware.push(i.middleware);
37
+ if (i.loader != null) aggregated.loader.push(i.loader);
38
+ if (i.action != null) aggregated.action.push(i.action);
31
39
  }
32
40
  }));
33
41
  let updates = {};
34
42
  if (typeof route.lazy === "function" && aggregated.lazy.length > 0) {
35
- let instrumented = wrapImpl(aggregated.lazy, route.lazy, () => void 0);
36
- if (instrumented) updates.lazy = instrumented;
43
+ let lazy = route.lazy;
44
+ updates.lazy = async (...args) => {
45
+ return throwOrReturnResult(await recurseRight(aggregated.lazy, void 0, () => lazy(...args), getInstrumentationInnerResult));
46
+ };
37
47
  }
38
48
  if (typeof route.lazy === "object") {
39
49
  let lazyObject = route.lazy;
40
- [
41
- "middleware",
42
- "loader",
43
- "action"
44
- ].forEach((key) => {
45
- let lazyFn = lazyObject[key];
46
- let instrumentations = aggregated[`lazy.${key}`];
47
- if (typeof lazyFn === "function" && instrumentations.length > 0) {
48
- let instrumented = wrapImpl(instrumentations, lazyFn, () => void 0);
49
- if (instrumented) updates.lazy = Object.assign(updates.lazy || {}, { [key]: instrumented });
50
- }
51
- });
52
- }
53
- ["loader", "action"].forEach((key) => {
54
- let handler = route[key];
55
- if (typeof handler === "function" && aggregated[key].length > 0) {
56
- let original = handler[UninstrumentedSymbol] ?? handler;
57
- let instrumented = wrapImpl(aggregated[key], original, (...args) => getHandlerInfo(args[0]));
58
- if (instrumented) {
59
- if (key === "loader" && original.hydrate === true) instrumented.hydrate = true;
60
- instrumented[UninstrumentedSymbol] = original;
61
- updates[key] = instrumented;
62
- }
50
+ if (typeof lazyObject.middleware === "function" && aggregated["lazy.middleware"].length > 0) {
51
+ let middleware = lazyObject.middleware;
52
+ updates.lazy = Object.assign(updates.lazy || {}, { middleware: async (...args) => {
53
+ return throwOrReturnResult(await recurseRight(aggregated["lazy.middleware"], void 0, () => middleware(...args), getInstrumentationInnerResult));
54
+ } });
63
55
  }
64
- });
65
- if (route.middleware && route.middleware.length > 0 && aggregated.middleware.length > 0) updates.middleware = route.middleware.map((middleware) => {
66
- let original = middleware[UninstrumentedSymbol] ?? middleware;
67
- let instrumented = wrapImpl(aggregated.middleware, original, (...args) => getHandlerInfo(args[0]));
68
- if (instrumented) {
69
- instrumented[UninstrumentedSymbol] = original;
70
- return instrumented;
56
+ if (typeof lazyObject.loader === "function" && aggregated["lazy.loader"].length > 0) {
57
+ let loader = lazyObject.loader;
58
+ updates.lazy = Object.assign(updates.lazy || {}, { loader: async (...args) => {
59
+ return throwOrReturnResult(await recurseRight(aggregated["lazy.loader"], void 0, () => loader(...args), getInstrumentationInnerResult));
60
+ } });
61
+ }
62
+ if (typeof lazyObject.action === "function" && aggregated["lazy.action"].length > 0) {
63
+ let action = lazyObject.action;
64
+ updates.lazy = Object.assign(updates.lazy || {}, { action: async (...args) => {
65
+ return throwOrReturnResult(await recurseRight(aggregated["lazy.action"], void 0, () => action(...args), getInstrumentationInnerResult));
66
+ } });
71
67
  }
72
- return middleware;
68
+ }
69
+ if (typeof route.loader === "function" && aggregated.loader.length > 0) {
70
+ let original = getUninstrumentedHandler(route.loader);
71
+ let instrumented = async (...args) => {
72
+ return throwOrReturnResult(await recurseRight(aggregated.loader, getHandlerInfo(args[0]), () => original(...args), getInstrumentationInnerResult));
73
+ };
74
+ if (original.hydrate === true) instrumented.hydrate = true;
75
+ setUninstrumentedHandler(instrumented, original);
76
+ updates.loader = instrumented;
77
+ }
78
+ if (typeof route.action === "function" && aggregated.action.length > 0) {
79
+ let original = getUninstrumentedHandler(route.action);
80
+ let instrumented = async (...args) => {
81
+ return throwOrReturnResult(await recurseRight(aggregated.action, getHandlerInfo(args[0]), () => original(...args), getInstrumentationInnerResult));
82
+ };
83
+ setUninstrumentedHandler(instrumented, original);
84
+ updates.action = instrumented;
85
+ }
86
+ if (route.middleware && route.middleware.length > 0 && aggregated.middleware.length > 0) updates.middleware = route.middleware.map((middleware) => {
87
+ let original = getUninstrumentedHandler(middleware);
88
+ let instrumented = async (...args) => {
89
+ return throwOrReturnResult(await recurseRight(aggregated.middleware, getHandlerInfo(args[0]), () => original(...args), getInstrumentationInnerResult));
90
+ };
91
+ setUninstrumentedHandler(instrumented, original);
92
+ return instrumented;
73
93
  });
74
94
  return updates;
75
95
  }
@@ -79,93 +99,138 @@ function instrumentClientSideRouter(router, fns) {
79
99
  fetch: []
80
100
  };
81
101
  fns.forEach((fn) => fn({ instrument(i) {
82
- let keys = Object.keys(i);
83
- for (let key of keys) if (i[key]) aggregated[key].push(i[key]);
102
+ if (i.navigate != null) aggregated.navigate.push(i.navigate);
103
+ if (i.fetch != null) aggregated.fetch.push(i.fetch);
84
104
  } }));
85
105
  if (aggregated.navigate.length > 0) {
86
- let navigate = router.navigate[UninstrumentedSymbol] ?? router.navigate;
87
- let instrumentedNavigate = wrapImpl(aggregated.navigate, navigate, (...args) => {
106
+ let navigate = getUninstrumentedHandler(router.navigate);
107
+ let instrumentedNavigate = async (...args) => {
88
108
  let [to, opts] = args;
89
- return {
109
+ let meta;
110
+ let info = {
90
111
  to: typeof to === "number" || typeof to === "string" ? to : to ? createPath(to) : ".",
91
112
  ...getRouterInfo(router, opts ?? {})
92
113
  };
93
- });
94
- if (instrumentedNavigate) {
95
- instrumentedNavigate[UninstrumentedSymbol] = navigate;
96
- router.navigate = instrumentedNavigate;
97
- }
114
+ return throwOrReturnResult(await recurseRight(aggregated.navigate, info, async () => {
115
+ if (typeof to === "number") return await navigate(...args);
116
+ let cleanup = setInstrumentationClientResultMetaReceiver(router, (value) => {
117
+ meta = value;
118
+ });
119
+ try {
120
+ return await navigate(...args);
121
+ } finally {
122
+ cleanup();
123
+ }
124
+ }, (result) => ({
125
+ ...getInstrumentationInnerResult(result),
126
+ meta
127
+ })));
128
+ };
129
+ setUninstrumentedHandler(instrumentedNavigate, navigate);
130
+ router.navigate = instrumentedNavigate;
98
131
  }
99
132
  if (aggregated.fetch.length > 0) {
100
- let fetch = router.fetch[UninstrumentedSymbol] ?? router.fetch;
101
- let instrumentedFetch = wrapImpl(aggregated.fetch, fetch, (...args) => {
102
- let [key, , href, opts] = args;
103
- return {
133
+ let fetch = getUninstrumentedHandler(router.fetch);
134
+ let instrumentedFetch = async (...args) => {
135
+ let [key, _, href, opts] = args;
136
+ let meta;
137
+ return throwOrReturnResult(await recurseRight(aggregated.fetch, {
104
138
  href: href ?? ".",
105
139
  fetcherKey: key,
106
140
  ...getRouterInfo(router, opts ?? {})
107
- };
108
- });
109
- if (instrumentedFetch) {
110
- instrumentedFetch[UninstrumentedSymbol] = fetch;
111
- router.fetch = instrumentedFetch;
112
- }
141
+ }, async () => {
142
+ let cleanup = setInstrumentationClientResultMetaReceiver(router, (value) => {
143
+ meta = value;
144
+ });
145
+ try {
146
+ return await fetch(...args);
147
+ } finally {
148
+ cleanup();
149
+ }
150
+ }, (result) => ({
151
+ ...getInstrumentationInnerResult(result),
152
+ meta
153
+ })));
154
+ };
155
+ setUninstrumentedHandler(instrumentedFetch, fetch);
156
+ router.fetch = instrumentedFetch;
113
157
  }
114
158
  return router;
115
159
  }
116
160
  function instrumentHandler(handler, fns) {
117
161
  let aggregated = { request: [] };
118
162
  fns.forEach((fn) => fn({ instrument(i) {
119
- let keys = Object.keys(i);
120
- for (let key of keys) if (i[key]) aggregated[key].push(i[key]);
163
+ if (i.request != null) aggregated.request.push(i.request);
121
164
  } }));
122
165
  let instrumentedHandler = handler;
123
- if (aggregated.request.length > 0) instrumentedHandler = wrapImpl(aggregated.request, handler, (...args) => {
166
+ if (aggregated.request.length > 0) instrumentedHandler = async (...args) => {
124
167
  let [request, context] = args;
125
- return {
168
+ let instrumentationContext = context ?? new RouterContextProvider();
169
+ return throwOrReturnResult(await recurseRight(aggregated.request, {
126
170
  request: getReadonlyRequest(request),
127
- context: context != null ? getReadonlyContext(context) : context
128
- };
129
- });
171
+ context: getReadonlyContext(instrumentationContext)
172
+ }, () => handler(request, instrumentationContext), (result, info) => {
173
+ let meta;
174
+ try {
175
+ meta = info.context?.get(instrumentationResultMetaContext);
176
+ } catch {}
177
+ invariant(result.value instanceof Response, "Expected a Response from the request handler");
178
+ return {
179
+ ...getInstrumentationInnerResult(result),
180
+ statusCode: result.value.status,
181
+ meta
182
+ };
183
+ }));
184
+ };
130
185
  return instrumentedHandler;
131
186
  }
132
- function wrapImpl(impls, handler, getInfo) {
133
- if (impls.length === 0) return null;
134
- return async (...args) => {
135
- let result = await recurseRight(impls, getInfo(...args), () => handler(...args), impls.length - 1);
136
- if (result.type === "error") throw result.value;
137
- return result.value;
187
+ function getUninstrumentedHandler(handler) {
188
+ return handler[UninstrumentedSymbol] ?? handler;
189
+ }
190
+ function setUninstrumentedHandler(handler, uninstrumentedHandler) {
191
+ handler[UninstrumentedSymbol] = uninstrumentedHandler;
192
+ }
193
+ function setInstrumentationClientResultMetaReceiver(router, receiver) {
194
+ instrumentationClientResultMetaReceivers.set(router, receiver);
195
+ return () => {
196
+ if (instrumentationClientResultMetaReceivers.get(router) === receiver) instrumentationClientResultMetaReceivers.delete(router);
138
197
  };
139
198
  }
140
- async function recurseRight(impls, info, handler, index) {
199
+ function consumeInstrumentationClientResultMetaReceiver(router) {
200
+ let receiver = instrumentationClientResultMetaReceivers.get(router);
201
+ instrumentationClientResultMetaReceivers.delete(router);
202
+ return receiver;
203
+ }
204
+ function throwOrReturnResult(result) {
205
+ if (result.type === "error") throw result.value;
206
+ return result.value;
207
+ }
208
+ async function recurseRight(impls, info, handler, getInnerResult, state = {
209
+ result: null,
210
+ innerResult: null
211
+ }, index = impls.length - 1) {
141
212
  let impl = impls[index];
142
- let result;
143
- if (!impl) try {
144
- result = {
145
- type: "success",
146
- value: await handler()
147
- };
148
- } catch (e) {
149
- result = {
150
- type: "error",
151
- value: e
152
- };
153
- }
154
- else {
213
+ if (!impl) {
214
+ try {
215
+ state.result = {
216
+ type: "success",
217
+ value: await handler()
218
+ };
219
+ } catch (e) {
220
+ state.result = {
221
+ type: "error",
222
+ value: e
223
+ };
224
+ }
225
+ state.innerResult = getInnerResult(state.result, info);
226
+ } else {
155
227
  let handlerPromise = void 0;
156
228
  let callHandler = async () => {
157
229
  if (handlerPromise) console.error("You cannot call instrumented handlers more than once");
158
- else handlerPromise = recurseRight(impls, info, handler, index - 1);
159
- result = await handlerPromise;
160
- invariant(result, "Expected a result");
161
- if (result.type === "error" && result.value instanceof Error) return {
162
- status: "error",
163
- error: result.value
164
- };
165
- return {
166
- status: "success",
167
- error: void 0
168
- };
230
+ else handlerPromise = recurseRight(impls, info, handler, getInnerResult, state, index - 1);
231
+ await handlerPromise;
232
+ invariant(state.innerResult, "Expected an inner result");
233
+ return state.innerResult;
169
234
  };
170
235
  try {
171
236
  await impl(callHandler, info);
@@ -175,18 +240,30 @@ async function recurseRight(impls, info, handler, index) {
175
240
  if (!handlerPromise) await callHandler();
176
241
  await handlerPromise;
177
242
  }
178
- if (result) return result;
179
- return {
243
+ if (state.result) return state.result;
244
+ state.result = {
180
245
  type: "error",
181
246
  value: /* @__PURE__ */ new Error("No result assigned in instrumentation chain.")
182
247
  };
248
+ state.innerResult = getInnerResult(state.result, info);
249
+ return state.result;
250
+ }
251
+ function getInstrumentationInnerResult(result) {
252
+ if (result.type === "error" && result.value instanceof Error) return {
253
+ status: "error",
254
+ error: result.value
255
+ };
256
+ return {
257
+ status: "success",
258
+ error: void 0
259
+ };
183
260
  }
184
261
  function getHandlerInfo(args) {
185
- let { request, context, params, pattern } = args;
262
+ let { request, context, params } = args;
186
263
  return {
264
+ ...args,
187
265
  request: getReadonlyRequest(request),
188
266
  params: { ...params },
189
- pattern,
190
267
  context: getReadonlyContext(context)
191
268
  };
192
269
  }
@@ -210,4 +287,4 @@ function getReadonlyContext(context) {
210
287
  return { get: (ctx) => context.get(ctx) };
211
288
  }
212
289
  //#endregion
213
- export { getRouteInstrumentationUpdates, instrumentClientSideRouter, instrumentHandler };
290
+ export { consumeInstrumentationClientResultMetaReceiver, getRouteInstrumentationUpdates, instrumentClientSideRouter, instrumentHandler, instrumentationResultMetaContext };