@tanstack/router-core 1.131.14 → 1.131.16

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.
@@ -705,525 +705,598 @@ class RouterCore {
705
705
  const findFn = (d) => d.id === matchId;
706
706
  return this.state.cachedMatches.find(findFn) ?? ((_a = this.state.pendingMatches) == null ? void 0 : _a.find(findFn)) ?? this.state.matches.find(findFn);
707
707
  };
708
- this.loadMatches = async ({
709
- location,
710
- matches,
711
- preload: allPreload,
712
- onReady,
713
- updateMatch = this.updateMatch,
714
- sync
715
- }) => {
716
- let firstBadMatchIndex;
717
- let rendered = false;
718
- const triggerOnReady = async () => {
719
- if (!rendered) {
720
- rendered = true;
721
- await (onReady == null ? void 0 : onReady());
708
+ this.triggerOnReady = (innerLoadContext) => {
709
+ var _a;
710
+ if (!innerLoadContext.rendered) {
711
+ innerLoadContext.rendered = true;
712
+ return (_a = innerLoadContext.onReady) == null ? void 0 : _a.call(innerLoadContext);
713
+ }
714
+ };
715
+ this.resolvePreload = (innerLoadContext, matchId) => {
716
+ return !!(innerLoadContext.preload && !this.state.matches.some((d) => d.id === matchId));
717
+ };
718
+ this.handleRedirectAndNotFound = (innerLoadContext, match, err) => {
719
+ var _a, _b, _c;
720
+ if (!redirect.isRedirect(err) && !notFound.isNotFound(err)) return;
721
+ if (redirect.isRedirect(err) && err.redirectHandled && !err.options.reloadDocument) {
722
+ throw err;
723
+ }
724
+ if (match) {
725
+ (_a = match._nonReactive.beforeLoadPromise) == null ? void 0 : _a.resolve();
726
+ (_b = match._nonReactive.loaderPromise) == null ? void 0 : _b.resolve();
727
+ match._nonReactive.beforeLoadPromise = void 0;
728
+ match._nonReactive.loaderPromise = void 0;
729
+ const status = redirect.isRedirect(err) ? "redirected" : "notFound";
730
+ innerLoadContext.updateMatch(match.id, (prev) => ({
731
+ ...prev,
732
+ status,
733
+ isFetching: false,
734
+ error: err
735
+ }));
736
+ if (notFound.isNotFound(err) && !err.routeId) {
737
+ err.routeId = match.routeId;
738
+ }
739
+ (_c = match._nonReactive.loadPromise) == null ? void 0 : _c.resolve();
740
+ }
741
+ if (redirect.isRedirect(err)) {
742
+ innerLoadContext.rendered = true;
743
+ err.options._fromLocation = innerLoadContext.location;
744
+ err.redirectHandled = true;
745
+ err = this.resolveRedirect(err);
746
+ throw err;
747
+ } else {
748
+ this._handleNotFound(innerLoadContext, err);
749
+ throw err;
750
+ }
751
+ };
752
+ this.shouldSkipLoader = (matchId) => {
753
+ const match = this.getMatch(matchId);
754
+ if (!this.isServer && match._nonReactive.dehydrated) {
755
+ return true;
756
+ }
757
+ if (this.isServer) {
758
+ if (match.ssr === false) {
759
+ return true;
760
+ }
761
+ }
762
+ return false;
763
+ };
764
+ this.handleSerialError = (innerLoadContext, index, err, routerCode) => {
765
+ var _a, _b;
766
+ const { id: matchId, routeId } = innerLoadContext.matches[index];
767
+ const route = this.looseRoutesById[routeId];
768
+ if (err instanceof Promise) {
769
+ throw err;
770
+ }
771
+ err.routerCode = routerCode;
772
+ innerLoadContext.firstBadMatchIndex ?? (innerLoadContext.firstBadMatchIndex = index);
773
+ this.handleRedirectAndNotFound(
774
+ innerLoadContext,
775
+ this.getMatch(matchId),
776
+ err
777
+ );
778
+ try {
779
+ (_b = (_a = route.options).onError) == null ? void 0 : _b.call(_a, err);
780
+ } catch (errorHandlerErr) {
781
+ err = errorHandlerErr;
782
+ this.handleRedirectAndNotFound(
783
+ innerLoadContext,
784
+ this.getMatch(matchId),
785
+ err
786
+ );
787
+ }
788
+ innerLoadContext.updateMatch(matchId, (prev) => {
789
+ var _a2, _b2;
790
+ (_a2 = prev._nonReactive.beforeLoadPromise) == null ? void 0 : _a2.resolve();
791
+ prev._nonReactive.beforeLoadPromise = void 0;
792
+ (_b2 = prev._nonReactive.loadPromise) == null ? void 0 : _b2.resolve();
793
+ return {
794
+ ...prev,
795
+ error: err,
796
+ status: "error",
797
+ isFetching: false,
798
+ updatedAt: Date.now(),
799
+ abortController: new AbortController()
800
+ };
801
+ });
802
+ };
803
+ this.isBeforeLoadSsr = (innerLoadContext, matchId, index, route) => {
804
+ var _a;
805
+ const existingMatch = this.getMatch(matchId);
806
+ const parentMatchId = (_a = innerLoadContext.matches[index - 1]) == null ? void 0 : _a.id;
807
+ const parentMatch = parentMatchId ? this.getMatch(parentMatchId) : void 0;
808
+ if (this.isShell()) {
809
+ existingMatch.ssr = matchId === root.rootRouteId;
810
+ return;
811
+ }
812
+ if ((parentMatch == null ? void 0 : parentMatch.ssr) === false) {
813
+ existingMatch.ssr = false;
814
+ return;
815
+ }
816
+ const parentOverride = (tempSsr2) => {
817
+ if (tempSsr2 === true && (parentMatch == null ? void 0 : parentMatch.ssr) === "data-only") {
818
+ return "data-only";
722
819
  }
820
+ return tempSsr2;
723
821
  };
724
- const resolvePreload = (matchId) => {
725
- return !!(allPreload && !this.state.matches.some((d) => d.id === matchId));
822
+ const defaultSsr = this.options.defaultSsr ?? true;
823
+ if (route.options.ssr === void 0) {
824
+ existingMatch.ssr = parentOverride(defaultSsr);
825
+ return;
826
+ }
827
+ if (typeof route.options.ssr !== "function") {
828
+ existingMatch.ssr = parentOverride(route.options.ssr);
829
+ return;
830
+ }
831
+ const { search, params } = this.getMatch(matchId);
832
+ const ssrFnContext = {
833
+ search: makeMaybe(search, existingMatch.searchError),
834
+ params: makeMaybe(params, existingMatch.paramsError),
835
+ location: innerLoadContext.location,
836
+ matches: innerLoadContext.matches.map((match) => ({
837
+ index: match.index,
838
+ pathname: match.pathname,
839
+ fullPath: match.fullPath,
840
+ staticData: match.staticData,
841
+ id: match.id,
842
+ routeId: match.routeId,
843
+ search: makeMaybe(match.search, match.searchError),
844
+ params: makeMaybe(match.params, match.paramsError),
845
+ ssr: match.ssr
846
+ }))
726
847
  };
727
- if (!this.isServer && this.state.matches.some((d) => d._forcePending)) {
728
- triggerOnReady();
848
+ const tempSsr = route.options.ssr(ssrFnContext);
849
+ if (utils.isPromise(tempSsr)) {
850
+ return tempSsr.then((ssr) => {
851
+ existingMatch.ssr = parentOverride(ssr ?? defaultSsr);
852
+ });
729
853
  }
730
- const handleRedirectAndNotFound = (match, err) => {
731
- var _a, _b, _c;
732
- if (!redirect.isRedirect(err) && !notFound.isNotFound(err)) return;
733
- if (redirect.isRedirect(err) && err.redirectHandled && !err.options.reloadDocument) {
734
- throw err;
854
+ existingMatch.ssr = parentOverride(tempSsr ?? defaultSsr);
855
+ return;
856
+ };
857
+ this.setupPendingTimeout = (innerLoadContext, matchId, route) => {
858
+ var _a;
859
+ const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs;
860
+ const shouldPending = !!(innerLoadContext.onReady && !this.isServer && !this.resolvePreload(innerLoadContext, matchId) && (route.options.loader || route.options.beforeLoad || routeNeedsPreload(route)) && typeof pendingMs === "number" && pendingMs !== Infinity && (route.options.pendingComponent ?? ((_a = this.options) == null ? void 0 : _a.defaultPendingComponent)));
861
+ const match = this.getMatch(matchId);
862
+ if (shouldPending && match._nonReactive.pendingTimeout === void 0) {
863
+ const pendingTimeout = setTimeout(() => {
864
+ try {
865
+ this.triggerOnReady(innerLoadContext);
866
+ } catch {
867
+ }
868
+ }, pendingMs);
869
+ match._nonReactive.pendingTimeout = pendingTimeout;
870
+ }
871
+ };
872
+ this.shouldExecuteBeforeLoad = (innerLoadContext, matchId, route) => {
873
+ const existingMatch = this.getMatch(matchId);
874
+ if (!existingMatch._nonReactive.beforeLoadPromise && !existingMatch._nonReactive.loaderPromise)
875
+ return true;
876
+ this.setupPendingTimeout(innerLoadContext, matchId, route);
877
+ const then = () => {
878
+ let shouldExecuteBeforeLoad = true;
879
+ const match = this.getMatch(matchId);
880
+ if (match.status === "error") {
881
+ shouldExecuteBeforeLoad = true;
882
+ } else if (match.preload && (match.status === "redirected" || match.status === "notFound")) {
883
+ this.handleRedirectAndNotFound(innerLoadContext, match, match.error);
735
884
  }
736
- if (match) {
737
- (_a = match._nonReactive.beforeLoadPromise) == null ? void 0 : _a.resolve();
738
- (_b = match._nonReactive.loaderPromise) == null ? void 0 : _b.resolve();
739
- match._nonReactive.beforeLoadPromise = void 0;
740
- match._nonReactive.loaderPromise = void 0;
741
- const status = redirect.isRedirect(err) ? "redirected" : "notFound";
742
- updateMatch(match.id, (prev) => ({
885
+ return shouldExecuteBeforeLoad;
886
+ };
887
+ return existingMatch._nonReactive.beforeLoadPromise ? existingMatch._nonReactive.beforeLoadPromise.then(then) : then();
888
+ };
889
+ this.executeBeforeLoad = (innerLoadContext, matchId, index, route) => {
890
+ var _a, _b, _c;
891
+ const resolve = () => {
892
+ innerLoadContext.updateMatch(matchId, (prev) => {
893
+ var _a2;
894
+ (_a2 = prev._nonReactive.beforeLoadPromise) == null ? void 0 : _a2.resolve();
895
+ prev._nonReactive.beforeLoadPromise = void 0;
896
+ return {
743
897
  ...prev,
744
- status,
745
- isFetching: false,
746
- error: err
747
- }));
748
- if (notFound.isNotFound(err) && !err.routeId) {
749
- err.routeId = match.routeId;
750
- }
751
- (_c = match._nonReactive.loadPromise) == null ? void 0 : _c.resolve();
898
+ isFetching: false
899
+ };
900
+ });
901
+ };
902
+ try {
903
+ const match = this.getMatch(matchId);
904
+ match._nonReactive.beforeLoadPromise = utils.createControlledPromise();
905
+ const prevLoadPromise = match._nonReactive.loadPromise;
906
+ match._nonReactive.loadPromise = utils.createControlledPromise(() => {
907
+ prevLoadPromise == null ? void 0 : prevLoadPromise.resolve();
908
+ });
909
+ const { paramsError, searchError } = this.getMatch(matchId);
910
+ if (paramsError) {
911
+ this.handleSerialError(
912
+ innerLoadContext,
913
+ index,
914
+ paramsError,
915
+ "PARSE_PARAMS"
916
+ );
752
917
  }
753
- if (redirect.isRedirect(err)) {
754
- rendered = true;
755
- err.options._fromLocation = location;
756
- err.redirectHandled = true;
757
- err = this.resolveRedirect(err);
758
- throw err;
918
+ if (searchError) {
919
+ this.handleSerialError(
920
+ innerLoadContext,
921
+ index,
922
+ searchError,
923
+ "VALIDATE_SEARCH"
924
+ );
925
+ }
926
+ this.setupPendingTimeout(innerLoadContext, matchId, route);
927
+ const abortController = new AbortController();
928
+ const parentMatchId = (_a = innerLoadContext.matches[index - 1]) == null ? void 0 : _a.id;
929
+ const parentMatch = parentMatchId ? this.getMatch(parentMatchId) : void 0;
930
+ const parentMatchContext = (parentMatch == null ? void 0 : parentMatch.context) ?? this.options.context ?? void 0;
931
+ innerLoadContext.updateMatch(matchId, (prev) => ({
932
+ ...prev,
933
+ isFetching: "beforeLoad",
934
+ fetchCount: prev.fetchCount + 1,
935
+ abortController,
936
+ context: {
937
+ ...parentMatchContext,
938
+ ...prev.__routeContext
939
+ }
940
+ }));
941
+ const { search, params, context, cause } = this.getMatch(matchId);
942
+ const preload = this.resolvePreload(innerLoadContext, matchId);
943
+ const beforeLoadFnContext = {
944
+ search,
945
+ abortController,
946
+ params,
947
+ preload,
948
+ context,
949
+ location: innerLoadContext.location,
950
+ navigate: (opts) => this.navigate({ ...opts, _fromLocation: innerLoadContext.location }),
951
+ buildLocation: this.buildLocation,
952
+ cause: preload ? "preload" : cause,
953
+ matches: innerLoadContext.matches
954
+ };
955
+ const updateContext = (beforeLoadContext2) => {
956
+ if (redirect.isRedirect(beforeLoadContext2) || notFound.isNotFound(beforeLoadContext2)) {
957
+ this.handleSerialError(
958
+ innerLoadContext,
959
+ index,
960
+ beforeLoadContext2,
961
+ "BEFORE_LOAD"
962
+ );
963
+ }
964
+ innerLoadContext.updateMatch(matchId, (prev) => ({
965
+ ...prev,
966
+ __beforeLoadContext: beforeLoadContext2,
967
+ context: {
968
+ ...parentMatchContext,
969
+ ...prev.__routeContext,
970
+ ...beforeLoadContext2
971
+ },
972
+ abortController
973
+ }));
974
+ };
975
+ const beforeLoadContext = (_c = (_b = route.options).beforeLoad) == null ? void 0 : _c.call(_b, beforeLoadFnContext);
976
+ if (utils.isPromise(beforeLoadContext)) {
977
+ return beforeLoadContext.then(updateContext).catch((err) => {
978
+ this.handleSerialError(innerLoadContext, index, err, "BEFORE_LOAD");
979
+ }).then(resolve);
759
980
  } else {
760
- this._handleNotFound(matches, err, updateMatch);
761
- throw err;
981
+ updateContext(beforeLoadContext);
762
982
  }
983
+ } catch (err) {
984
+ this.handleSerialError(innerLoadContext, index, err, "BEFORE_LOAD");
985
+ }
986
+ resolve();
987
+ return;
988
+ };
989
+ this.handleBeforeLoad = (innerLoadContext, index) => {
990
+ const { id: matchId, routeId } = innerLoadContext.matches[index];
991
+ const route = this.looseRoutesById[routeId];
992
+ const serverSsr = () => {
993
+ if (this.isServer) {
994
+ const maybePromise = this.isBeforeLoadSsr(
995
+ innerLoadContext,
996
+ matchId,
997
+ index,
998
+ route
999
+ );
1000
+ if (utils.isPromise(maybePromise)) return maybePromise.then(queueExecution);
1001
+ }
1002
+ return queueExecution();
763
1003
  };
764
- const shouldSkipLoader = (matchId) => {
1004
+ const queueExecution = () => {
1005
+ if (this.shouldSkipLoader(matchId)) return;
1006
+ const shouldExecuteBeforeLoadResult = this.shouldExecuteBeforeLoad(
1007
+ innerLoadContext,
1008
+ matchId,
1009
+ route
1010
+ );
1011
+ return utils.isPromise(shouldExecuteBeforeLoadResult) ? shouldExecuteBeforeLoadResult.then(execute) : execute(shouldExecuteBeforeLoadResult);
1012
+ };
1013
+ const execute = (shouldExecuteBeforeLoad) => {
1014
+ if (shouldExecuteBeforeLoad) {
1015
+ return this.executeBeforeLoad(innerLoadContext, matchId, index, route);
1016
+ }
1017
+ return;
1018
+ };
1019
+ return serverSsr();
1020
+ };
1021
+ this.executeHead = (innerLoadContext, matchId, route) => {
1022
+ var _a, _b, _c, _d, _e, _f;
1023
+ const match = this.getMatch(matchId);
1024
+ if (!match) {
1025
+ return;
1026
+ }
1027
+ if (!route.options.head && !route.options.scripts && !route.options.headers) {
1028
+ return;
1029
+ }
1030
+ const assetContext = {
1031
+ matches: innerLoadContext.matches,
1032
+ match,
1033
+ params: match.params,
1034
+ loaderData: match.loaderData
1035
+ };
1036
+ return Promise.all([
1037
+ (_b = (_a = route.options).head) == null ? void 0 : _b.call(_a, assetContext),
1038
+ (_d = (_c = route.options).scripts) == null ? void 0 : _d.call(_c, assetContext),
1039
+ (_f = (_e = route.options).headers) == null ? void 0 : _f.call(_e, assetContext)
1040
+ ]).then(([headFnContent, scripts, headers]) => {
1041
+ const meta = headFnContent == null ? void 0 : headFnContent.meta;
1042
+ const links = headFnContent == null ? void 0 : headFnContent.links;
1043
+ const headScripts = headFnContent == null ? void 0 : headFnContent.scripts;
1044
+ const styles = headFnContent == null ? void 0 : headFnContent.styles;
1045
+ return {
1046
+ meta,
1047
+ links,
1048
+ headScripts,
1049
+ headers,
1050
+ scripts,
1051
+ styles
1052
+ };
1053
+ });
1054
+ };
1055
+ this.potentialPendingMinPromise = (matchId) => {
1056
+ const latestMatch = this.getMatch(matchId);
1057
+ return latestMatch._nonReactive.minPendingPromise;
1058
+ };
1059
+ this.getLoaderContext = (innerLoadContext, matchId, index, route) => {
1060
+ const parentMatchPromise = innerLoadContext.matchPromises[index - 1];
1061
+ const { params, loaderDeps, abortController, context, cause } = this.getMatch(matchId);
1062
+ const preload = this.resolvePreload(innerLoadContext, matchId);
1063
+ return {
1064
+ params,
1065
+ deps: loaderDeps,
1066
+ preload: !!preload,
1067
+ parentMatchPromise,
1068
+ abortController,
1069
+ context,
1070
+ location: innerLoadContext.location,
1071
+ navigate: (opts) => this.navigate({ ...opts, _fromLocation: innerLoadContext.location }),
1072
+ cause: preload ? "preload" : cause,
1073
+ route
1074
+ };
1075
+ };
1076
+ this.runLoader = async (innerLoadContext, matchId, index, route) => {
1077
+ var _a, _b, _c, _d;
1078
+ try {
1079
+ try {
1080
+ if (!this.isServer || this.getMatch(matchId).ssr === true) {
1081
+ this.loadRouteChunk(route);
1082
+ }
1083
+ const loaderResult = (_b = (_a = route.options).loader) == null ? void 0 : _b.call(
1084
+ _a,
1085
+ this.getLoaderContext(innerLoadContext, matchId, index, route)
1086
+ );
1087
+ const loaderResultIsPromise = route.options.loader && utils.isPromise(loaderResult);
1088
+ const willLoadSomething = !!(loaderResultIsPromise || route._lazyPromise || route._componentsPromise || route.options.head || route.options.scripts || route.options.headers || this.getMatch(matchId)._nonReactive.minPendingPromise);
1089
+ if (willLoadSomething) {
1090
+ innerLoadContext.updateMatch(matchId, (prev) => ({
1091
+ ...prev,
1092
+ isFetching: "loader"
1093
+ }));
1094
+ }
1095
+ if (route.options.loader) {
1096
+ const loaderData = loaderResultIsPromise ? await loaderResult : loaderResult;
1097
+ this.handleRedirectAndNotFound(
1098
+ innerLoadContext,
1099
+ this.getMatch(matchId),
1100
+ loaderData
1101
+ );
1102
+ innerLoadContext.updateMatch(matchId, (prev) => ({
1103
+ ...prev,
1104
+ loaderData
1105
+ }));
1106
+ }
1107
+ if (route._lazyPromise) await route._lazyPromise;
1108
+ const headResult = this.executeHead(innerLoadContext, matchId, route);
1109
+ const head = headResult ? await headResult : void 0;
1110
+ const pendingPromise = this.potentialPendingMinPromise(matchId);
1111
+ if (pendingPromise) await pendingPromise;
1112
+ if (route._componentsPromise) await route._componentsPromise;
1113
+ innerLoadContext.updateMatch(matchId, (prev) => ({
1114
+ ...prev,
1115
+ error: void 0,
1116
+ status: "success",
1117
+ isFetching: false,
1118
+ updatedAt: Date.now(),
1119
+ ...head
1120
+ }));
1121
+ } catch (e) {
1122
+ let error = e;
1123
+ await this.potentialPendingMinPromise(matchId);
1124
+ this.handleRedirectAndNotFound(
1125
+ innerLoadContext,
1126
+ this.getMatch(matchId),
1127
+ e
1128
+ );
1129
+ try {
1130
+ (_d = (_c = route.options).onError) == null ? void 0 : _d.call(_c, e);
1131
+ } catch (onErrorError) {
1132
+ error = onErrorError;
1133
+ this.handleRedirectAndNotFound(
1134
+ innerLoadContext,
1135
+ this.getMatch(matchId),
1136
+ onErrorError
1137
+ );
1138
+ }
1139
+ const headResult = this.executeHead(innerLoadContext, matchId, route);
1140
+ const head = headResult ? await headResult : void 0;
1141
+ innerLoadContext.updateMatch(matchId, (prev) => ({
1142
+ ...prev,
1143
+ error,
1144
+ status: "error",
1145
+ isFetching: false,
1146
+ ...head
1147
+ }));
1148
+ }
1149
+ } catch (err) {
765
1150
  const match = this.getMatch(matchId);
766
- if (!this.isServer && match._nonReactive.dehydrated) {
767
- return true;
1151
+ if (match) {
1152
+ const headResult = this.executeHead(innerLoadContext, matchId, route);
1153
+ if (headResult) {
1154
+ const head = await headResult;
1155
+ innerLoadContext.updateMatch(matchId, (prev) => ({
1156
+ ...prev,
1157
+ ...head
1158
+ }));
1159
+ }
1160
+ match._nonReactive.loaderPromise = void 0;
768
1161
  }
1162
+ this.handleRedirectAndNotFound(innerLoadContext, match, err);
1163
+ }
1164
+ };
1165
+ this.loadRouteMatch = async (innerLoadContext, index) => {
1166
+ var _a, _b;
1167
+ const { id: matchId, routeId } = innerLoadContext.matches[index];
1168
+ let loaderShouldRunAsync = false;
1169
+ let loaderIsRunningAsync = false;
1170
+ const route = this.looseRoutesById[routeId];
1171
+ const prevMatch = this.getMatch(matchId);
1172
+ if (this.shouldSkipLoader(matchId)) {
769
1173
  if (this.isServer) {
770
- if (match.ssr === false) {
771
- return true;
1174
+ const headResult = this.executeHead(innerLoadContext, matchId, route);
1175
+ if (headResult) {
1176
+ const head = await headResult;
1177
+ innerLoadContext.updateMatch(matchId, (prev) => ({
1178
+ ...prev,
1179
+ ...head
1180
+ }));
772
1181
  }
1182
+ return this.getMatch(matchId);
773
1183
  }
774
- return false;
775
- };
1184
+ } else if (prevMatch._nonReactive.loaderPromise) {
1185
+ if (prevMatch.status === "success" && !innerLoadContext.sync && !prevMatch.preload) {
1186
+ return this.getMatch(matchId);
1187
+ }
1188
+ await prevMatch._nonReactive.loaderPromise;
1189
+ const match = this.getMatch(matchId);
1190
+ if (match.error) {
1191
+ this.handleRedirectAndNotFound(innerLoadContext, match, match.error);
1192
+ }
1193
+ } else {
1194
+ const age = Date.now() - this.getMatch(matchId).updatedAt;
1195
+ const preload = this.resolvePreload(innerLoadContext, matchId);
1196
+ const staleAge = preload ? route.options.preloadStaleTime ?? this.options.defaultPreloadStaleTime ?? 3e4 : route.options.staleTime ?? this.options.defaultStaleTime ?? 0;
1197
+ const shouldReloadOption = route.options.shouldReload;
1198
+ const shouldReload = typeof shouldReloadOption === "function" ? shouldReloadOption(
1199
+ this.getLoaderContext(innerLoadContext, matchId, index, route)
1200
+ ) : shouldReloadOption;
1201
+ innerLoadContext.updateMatch(matchId, (prev) => {
1202
+ prev._nonReactive.loaderPromise = utils.createControlledPromise();
1203
+ return {
1204
+ ...prev,
1205
+ preload: !!preload && !this.state.matches.some((d) => d.id === matchId)
1206
+ };
1207
+ });
1208
+ const { status, invalid } = this.getMatch(matchId);
1209
+ loaderShouldRunAsync = status === "success" && (invalid || (shouldReload ?? age > staleAge));
1210
+ if (preload && route.options.preload === false) ;
1211
+ else if (loaderShouldRunAsync && !innerLoadContext.sync) {
1212
+ loaderIsRunningAsync = true;
1213
+ (async () => {
1214
+ var _a2, _b2;
1215
+ try {
1216
+ await this.runLoader(innerLoadContext, matchId, index, route);
1217
+ const match = this.getMatch(matchId);
1218
+ (_a2 = match._nonReactive.loaderPromise) == null ? void 0 : _a2.resolve();
1219
+ (_b2 = match._nonReactive.loadPromise) == null ? void 0 : _b2.resolve();
1220
+ match._nonReactive.loaderPromise = void 0;
1221
+ } catch (err) {
1222
+ if (redirect.isRedirect(err)) {
1223
+ await this.navigate(err.options);
1224
+ }
1225
+ }
1226
+ })();
1227
+ } else if (status !== "success" || loaderShouldRunAsync && innerLoadContext.sync) {
1228
+ await this.runLoader(innerLoadContext, matchId, index, route);
1229
+ } else {
1230
+ const headResult = this.executeHead(innerLoadContext, matchId, route);
1231
+ if (headResult) {
1232
+ const head = await headResult;
1233
+ innerLoadContext.updateMatch(matchId, (prev) => ({
1234
+ ...prev,
1235
+ ...head
1236
+ }));
1237
+ }
1238
+ }
1239
+ }
1240
+ if (!loaderIsRunningAsync) {
1241
+ const match = this.getMatch(matchId);
1242
+ (_a = match._nonReactive.loaderPromise) == null ? void 0 : _a.resolve();
1243
+ (_b = match._nonReactive.loadPromise) == null ? void 0 : _b.resolve();
1244
+ }
1245
+ innerLoadContext.updateMatch(matchId, (prev) => {
1246
+ clearTimeout(prev._nonReactive.pendingTimeout);
1247
+ prev._nonReactive.pendingTimeout = void 0;
1248
+ if (!loaderIsRunningAsync) prev._nonReactive.loaderPromise = void 0;
1249
+ prev._nonReactive.dehydrated = void 0;
1250
+ return {
1251
+ ...prev,
1252
+ isFetching: loaderIsRunningAsync ? prev.isFetching : false,
1253
+ invalid: false
1254
+ };
1255
+ });
1256
+ return this.getMatch(matchId);
1257
+ };
1258
+ this.loadMatches = async (baseContext) => {
1259
+ const innerLoadContext = baseContext;
1260
+ innerLoadContext.updateMatch ?? (innerLoadContext.updateMatch = this.updateMatch);
1261
+ innerLoadContext.matchPromises = [];
1262
+ if (!this.isServer && this.state.matches.some((d) => d._forcePending)) {
1263
+ this.triggerOnReady(innerLoadContext);
1264
+ }
776
1265
  try {
777
1266
  await new Promise((resolveAll, rejectAll) => {
778
1267
  ;
779
1268
  (async () => {
780
- var _a, _b, _c, _d;
781
1269
  try {
782
- const handleSerialError = (index, err, routerCode) => {
783
- var _a2, _b2;
784
- const { id: matchId, routeId } = matches[index];
785
- const route = this.looseRoutesById[routeId];
786
- if (err instanceof Promise) {
787
- throw err;
788
- }
789
- err.routerCode = routerCode;
790
- firstBadMatchIndex = firstBadMatchIndex ?? index;
791
- handleRedirectAndNotFound(this.getMatch(matchId), err);
792
- try {
793
- (_b2 = (_a2 = route.options).onError) == null ? void 0 : _b2.call(_a2, err);
794
- } catch (errorHandlerErr) {
795
- err = errorHandlerErr;
796
- handleRedirectAndNotFound(this.getMatch(matchId), err);
797
- }
798
- updateMatch(matchId, (prev) => {
799
- var _a3, _b3;
800
- (_a3 = prev._nonReactive.beforeLoadPromise) == null ? void 0 : _a3.resolve();
801
- prev._nonReactive.beforeLoadPromise = void 0;
802
- (_b3 = prev._nonReactive.loadPromise) == null ? void 0 : _b3.resolve();
803
- return {
804
- ...prev,
805
- error: err,
806
- status: "error",
807
- isFetching: false,
808
- updatedAt: Date.now(),
809
- abortController: new AbortController()
810
- };
811
- });
812
- };
813
- for (const [index, { id: matchId, routeId }] of matches.entries()) {
814
- const existingMatch = this.getMatch(matchId);
815
- const parentMatchId = (_a = matches[index - 1]) == null ? void 0 : _a.id;
816
- const parentMatch = parentMatchId ? this.getMatch(parentMatchId) : void 0;
817
- const route = this.looseRoutesById[routeId];
818
- const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs;
819
- if (this.isServer) {
820
- let ssr;
821
- if (this.isShell()) {
822
- ssr = matchId === root.rootRouteId;
823
- } else {
824
- const defaultSsr = this.options.defaultSsr ?? true;
825
- if ((parentMatch == null ? void 0 : parentMatch.ssr) === false) {
826
- ssr = false;
827
- } else {
828
- let tempSsr;
829
- if (route.options.ssr === void 0) {
830
- tempSsr = defaultSsr;
831
- } else if (typeof route.options.ssr === "function") {
832
- let makeMaybe = function(value, error) {
833
- if (error) {
834
- return { status: "error", error };
835
- }
836
- return { status: "success", value };
837
- };
838
- const { search, params } = this.getMatch(matchId);
839
- const ssrFnContext = {
840
- search: makeMaybe(search, existingMatch.searchError),
841
- params: makeMaybe(params, existingMatch.paramsError),
842
- location,
843
- matches: matches.map((match) => ({
844
- index: match.index,
845
- pathname: match.pathname,
846
- fullPath: match.fullPath,
847
- staticData: match.staticData,
848
- id: match.id,
849
- routeId: match.routeId,
850
- search: makeMaybe(match.search, match.searchError),
851
- params: makeMaybe(match.params, match.paramsError),
852
- ssr: match.ssr
853
- }))
854
- };
855
- tempSsr = await route.options.ssr(ssrFnContext) ?? defaultSsr;
856
- } else {
857
- tempSsr = route.options.ssr;
858
- }
859
- if (tempSsr === true && (parentMatch == null ? void 0 : parentMatch.ssr) === "data-only") {
860
- ssr = "data-only";
861
- } else {
862
- ssr = tempSsr;
863
- }
864
- }
865
- }
866
- existingMatch.ssr = ssr;
867
- }
868
- if (shouldSkipLoader(matchId)) {
869
- continue;
870
- }
871
- const shouldPending = !!(onReady && !this.isServer && !resolvePreload(matchId) && (route.options.loader || route.options.beforeLoad || routeNeedsPreload(route)) && typeof pendingMs === "number" && pendingMs !== Infinity && (route.options.pendingComponent ?? ((_b = this.options) == null ? void 0 : _b.defaultPendingComponent)));
872
- let executeBeforeLoad = true;
873
- const setupPendingTimeout = () => {
874
- const match = this.getMatch(matchId);
875
- if (shouldPending && match._nonReactive.pendingTimeout === void 0) {
876
- const pendingTimeout = setTimeout(() => {
877
- try {
878
- triggerOnReady();
879
- } catch {
880
- }
881
- }, pendingMs);
882
- match._nonReactive.pendingTimeout = pendingTimeout;
883
- }
884
- };
885
- if (
886
- // If we are in the middle of a load, either of these will be present
887
- // (not to be confused with `loadPromise`, which is always defined)
888
- existingMatch._nonReactive.beforeLoadPromise || existingMatch._nonReactive.loaderPromise
889
- ) {
890
- setupPendingTimeout();
891
- await existingMatch._nonReactive.beforeLoadPromise;
892
- const match = this.getMatch(matchId);
893
- if (match.status === "error") {
894
- executeBeforeLoad = true;
895
- } else if (match.preload && (match.status === "redirected" || match.status === "notFound")) {
896
- handleRedirectAndNotFound(match, match.error);
897
- }
898
- }
899
- if (executeBeforeLoad) {
900
- try {
901
- const match = this.getMatch(matchId);
902
- match._nonReactive.beforeLoadPromise = utils.createControlledPromise();
903
- const prevLoadPromise = match._nonReactive.loadPromise;
904
- match._nonReactive.loadPromise = utils.createControlledPromise(() => {
905
- prevLoadPromise == null ? void 0 : prevLoadPromise.resolve();
906
- });
907
- const { paramsError, searchError } = this.getMatch(matchId);
908
- if (paramsError) {
909
- handleSerialError(index, paramsError, "PARSE_PARAMS");
910
- }
911
- if (searchError) {
912
- handleSerialError(index, searchError, "VALIDATE_SEARCH");
913
- }
914
- setupPendingTimeout();
915
- const abortController = new AbortController();
916
- const parentMatchContext = (parentMatch == null ? void 0 : parentMatch.context) ?? this.options.context ?? void 0;
917
- updateMatch(matchId, (prev) => ({
918
- ...prev,
919
- isFetching: "beforeLoad",
920
- fetchCount: prev.fetchCount + 1,
921
- abortController,
922
- context: {
923
- ...parentMatchContext,
924
- ...prev.__routeContext
925
- }
926
- }));
927
- const { search, params, context, cause } = this.getMatch(matchId);
928
- const preload = resolvePreload(matchId);
929
- const beforeLoadFnContext = {
930
- search,
931
- abortController,
932
- params,
933
- preload,
934
- context,
935
- location,
936
- navigate: (opts) => this.navigate({ ...opts, _fromLocation: location }),
937
- buildLocation: this.buildLocation,
938
- cause: preload ? "preload" : cause,
939
- matches
940
- };
941
- const beforeLoadContext = await ((_d = (_c = route.options).beforeLoad) == null ? void 0 : _d.call(_c, beforeLoadFnContext));
942
- if (redirect.isRedirect(beforeLoadContext) || notFound.isNotFound(beforeLoadContext)) {
943
- handleSerialError(index, beforeLoadContext, "BEFORE_LOAD");
944
- }
945
- updateMatch(matchId, (prev) => {
946
- return {
947
- ...prev,
948
- __beforeLoadContext: beforeLoadContext,
949
- context: {
950
- ...parentMatchContext,
951
- ...prev.__routeContext,
952
- ...beforeLoadContext
953
- },
954
- abortController
955
- };
956
- });
957
- } catch (err) {
958
- handleSerialError(index, err, "BEFORE_LOAD");
959
- }
960
- updateMatch(matchId, (prev) => {
961
- var _a2;
962
- (_a2 = prev._nonReactive.beforeLoadPromise) == null ? void 0 : _a2.resolve();
963
- prev._nonReactive.beforeLoadPromise = void 0;
964
- return {
965
- ...prev,
966
- isFetching: false
967
- };
968
- });
969
- }
1270
+ for (let i = 0; i < innerLoadContext.matches.length; i++) {
1271
+ const beforeLoad = this.handleBeforeLoad(innerLoadContext, i);
1272
+ if (utils.isPromise(beforeLoad)) await beforeLoad;
970
1273
  }
971
- const validResolvedMatches = matches.slice(0, firstBadMatchIndex);
972
- const matchPromises = [];
973
- validResolvedMatches.forEach(({ id: matchId, routeId }, index) => {
974
- matchPromises.push(
975
- (async () => {
976
- var _a2, _b2;
977
- let loaderShouldRunAsync = false;
978
- let loaderIsRunningAsync = false;
979
- const route = this.looseRoutesById[routeId];
980
- const executeHead = () => {
981
- var _a3, _b3, _c2, _d2, _e, _f;
982
- const match = this.getMatch(matchId);
983
- if (!match) {
984
- return;
985
- }
986
- if (!route.options.head && !route.options.scripts && !route.options.headers) {
987
- return;
988
- }
989
- const assetContext = {
990
- matches,
991
- match,
992
- params: match.params,
993
- loaderData: match.loaderData
994
- };
995
- return Promise.all([
996
- (_b3 = (_a3 = route.options).head) == null ? void 0 : _b3.call(_a3, assetContext),
997
- (_d2 = (_c2 = route.options).scripts) == null ? void 0 : _d2.call(_c2, assetContext),
998
- (_f = (_e = route.options).headers) == null ? void 0 : _f.call(_e, assetContext)
999
- ]).then(([headFnContent, scripts, headers]) => {
1000
- const meta = headFnContent == null ? void 0 : headFnContent.meta;
1001
- const links = headFnContent == null ? void 0 : headFnContent.links;
1002
- const headScripts = headFnContent == null ? void 0 : headFnContent.scripts;
1003
- const styles = headFnContent == null ? void 0 : headFnContent.styles;
1004
- return {
1005
- meta,
1006
- links,
1007
- headScripts,
1008
- headers,
1009
- scripts,
1010
- styles
1011
- };
1012
- });
1013
- };
1014
- const potentialPendingMinPromise = () => {
1015
- const latestMatch = this.getMatch(matchId);
1016
- return latestMatch._nonReactive.minPendingPromise;
1017
- };
1018
- const prevMatch = this.getMatch(matchId);
1019
- if (shouldSkipLoader(matchId)) {
1020
- if (this.isServer) {
1021
- const headResult = executeHead();
1022
- if (headResult) {
1023
- const head = await headResult;
1024
- updateMatch(matchId, (prev) => ({
1025
- ...prev,
1026
- ...head
1027
- }));
1028
- }
1029
- return this.getMatch(matchId);
1030
- }
1031
- } else if (prevMatch._nonReactive.loaderPromise) {
1032
- if (prevMatch.status === "success" && !sync && !prevMatch.preload) {
1033
- return this.getMatch(matchId);
1034
- }
1035
- await prevMatch._nonReactive.loaderPromise;
1036
- const match = this.getMatch(matchId);
1037
- if (match.error) {
1038
- handleRedirectAndNotFound(match, match.error);
1039
- }
1040
- } else {
1041
- const parentMatchPromise = matchPromises[index - 1];
1042
- const getLoaderContext = () => {
1043
- const {
1044
- params,
1045
- loaderDeps,
1046
- abortController,
1047
- context,
1048
- cause
1049
- } = this.getMatch(matchId);
1050
- const preload2 = resolvePreload(matchId);
1051
- return {
1052
- params,
1053
- deps: loaderDeps,
1054
- preload: !!preload2,
1055
- parentMatchPromise,
1056
- abortController,
1057
- context,
1058
- location,
1059
- navigate: (opts) => this.navigate({ ...opts, _fromLocation: location }),
1060
- cause: preload2 ? "preload" : cause,
1061
- route
1062
- };
1063
- };
1064
- const age = Date.now() - this.getMatch(matchId).updatedAt;
1065
- const preload = resolvePreload(matchId);
1066
- const staleAge = preload ? route.options.preloadStaleTime ?? this.options.defaultPreloadStaleTime ?? 3e4 : route.options.staleTime ?? this.options.defaultStaleTime ?? 0;
1067
- const shouldReloadOption = route.options.shouldReload;
1068
- const shouldReload = typeof shouldReloadOption === "function" ? shouldReloadOption(getLoaderContext()) : shouldReloadOption;
1069
- updateMatch(matchId, (prev) => {
1070
- prev._nonReactive.loaderPromise = utils.createControlledPromise();
1071
- return {
1072
- ...prev,
1073
- preload: !!preload && !this.state.matches.some((d) => d.id === matchId)
1074
- };
1075
- });
1076
- const runLoader = async () => {
1077
- var _a3, _b3, _c2, _d2;
1078
- try {
1079
- try {
1080
- if (!this.isServer || this.getMatch(matchId).ssr === true) {
1081
- this.loadRouteChunk(route);
1082
- }
1083
- const loaderResult = (_b3 = (_a3 = route.options).loader) == null ? void 0 : _b3.call(_a3, getLoaderContext());
1084
- const loaderResultIsPromise = route.options.loader && utils.isPromise(loaderResult);
1085
- const willLoadSomething = !!(loaderResultIsPromise || route._lazyPromise || route._componentsPromise || route.options.head || route.options.scripts || route.options.headers || this.getMatch(matchId)._nonReactive.minPendingPromise);
1086
- if (willLoadSomething) {
1087
- updateMatch(matchId, (prev) => ({
1088
- ...prev,
1089
- isFetching: "loader"
1090
- }));
1091
- }
1092
- if (route.options.loader) {
1093
- const loaderData = loaderResultIsPromise ? await loaderResult : loaderResult;
1094
- handleRedirectAndNotFound(
1095
- this.getMatch(matchId),
1096
- loaderData
1097
- );
1098
- updateMatch(matchId, (prev) => ({
1099
- ...prev,
1100
- loaderData
1101
- }));
1102
- }
1103
- if (route._lazyPromise) await route._lazyPromise;
1104
- const headResult = executeHead();
1105
- const head = headResult ? await headResult : void 0;
1106
- const pendingPromise = potentialPendingMinPromise();
1107
- if (pendingPromise) await pendingPromise;
1108
- if (route._componentsPromise)
1109
- await route._componentsPromise;
1110
- updateMatch(matchId, (prev) => ({
1111
- ...prev,
1112
- error: void 0,
1113
- status: "success",
1114
- isFetching: false,
1115
- updatedAt: Date.now(),
1116
- ...head
1117
- }));
1118
- } catch (e) {
1119
- let error = e;
1120
- await potentialPendingMinPromise();
1121
- handleRedirectAndNotFound(this.getMatch(matchId), e);
1122
- try {
1123
- (_d2 = (_c2 = route.options).onError) == null ? void 0 : _d2.call(_c2, e);
1124
- } catch (onErrorError) {
1125
- error = onErrorError;
1126
- handleRedirectAndNotFound(
1127
- this.getMatch(matchId),
1128
- onErrorError
1129
- );
1130
- }
1131
- const headResult = executeHead();
1132
- const head = headResult ? await headResult : void 0;
1133
- updateMatch(matchId, (prev) => ({
1134
- ...prev,
1135
- error,
1136
- status: "error",
1137
- isFetching: false,
1138
- ...head
1139
- }));
1140
- }
1141
- } catch (err) {
1142
- const match = this.getMatch(matchId);
1143
- if (match) {
1144
- const headResult = executeHead();
1145
- if (headResult) {
1146
- const head = await headResult;
1147
- updateMatch(matchId, (prev) => ({
1148
- ...prev,
1149
- ...head
1150
- }));
1151
- }
1152
- match._nonReactive.loaderPromise = void 0;
1153
- }
1154
- handleRedirectAndNotFound(match, err);
1155
- }
1156
- };
1157
- const { status, invalid } = this.getMatch(matchId);
1158
- loaderShouldRunAsync = status === "success" && (invalid || (shouldReload ?? age > staleAge));
1159
- if (preload && route.options.preload === false) {
1160
- } else if (loaderShouldRunAsync && !sync) {
1161
- loaderIsRunningAsync = true;
1162
- (async () => {
1163
- var _a3, _b3;
1164
- try {
1165
- await runLoader();
1166
- const match = this.getMatch(matchId);
1167
- (_a3 = match._nonReactive.loaderPromise) == null ? void 0 : _a3.resolve();
1168
- (_b3 = match._nonReactive.loadPromise) == null ? void 0 : _b3.resolve();
1169
- match._nonReactive.loaderPromise = void 0;
1170
- } catch (err) {
1171
- if (redirect.isRedirect(err)) {
1172
- await this.navigate(err.options);
1173
- }
1174
- }
1175
- })();
1176
- } else if (status !== "success" || loaderShouldRunAsync && sync) {
1177
- await runLoader();
1178
- } else {
1179
- const headResult = executeHead();
1180
- if (headResult) {
1181
- const head = await headResult;
1182
- updateMatch(matchId, (prev) => ({
1183
- ...prev,
1184
- ...head
1185
- }));
1186
- }
1187
- }
1188
- }
1189
- if (!loaderIsRunningAsync) {
1190
- const match = this.getMatch(matchId);
1191
- (_a2 = match._nonReactive.loaderPromise) == null ? void 0 : _a2.resolve();
1192
- (_b2 = match._nonReactive.loadPromise) == null ? void 0 : _b2.resolve();
1193
- }
1194
- updateMatch(matchId, (prev) => {
1195
- clearTimeout(prev._nonReactive.pendingTimeout);
1196
- prev._nonReactive.pendingTimeout = void 0;
1197
- if (!loaderIsRunningAsync)
1198
- prev._nonReactive.loaderPromise = void 0;
1199
- prev._nonReactive.dehydrated = void 0;
1200
- return {
1201
- ...prev,
1202
- isFetching: loaderIsRunningAsync ? prev.isFetching : false,
1203
- invalid: false
1204
- };
1205
- });
1206
- return this.getMatch(matchId);
1207
- })()
1274
+ const max = innerLoadContext.firstBadMatchIndex ?? innerLoadContext.matches.length;
1275
+ for (let i = 0; i < max; i++) {
1276
+ innerLoadContext.matchPromises.push(
1277
+ this.loadRouteMatch(innerLoadContext, i)
1208
1278
  );
1209
- });
1210
- await Promise.all(matchPromises);
1279
+ }
1280
+ await Promise.all(innerLoadContext.matchPromises);
1211
1281
  resolveAll();
1212
1282
  } catch (err) {
1213
1283
  rejectAll(err);
1214
1284
  }
1215
1285
  })();
1216
1286
  });
1217
- await triggerOnReady();
1287
+ const readyPromise = this.triggerOnReady(innerLoadContext);
1288
+ if (utils.isPromise(readyPromise)) await readyPromise;
1218
1289
  } catch (err) {
1219
- if (redirect.isRedirect(err) || notFound.isNotFound(err)) {
1220
- if (notFound.isNotFound(err) && !allPreload) {
1221
- await triggerOnReady();
1222
- }
1290
+ if (notFound.isNotFound(err) && !innerLoadContext.preload) {
1291
+ const readyPromise = this.triggerOnReady(innerLoadContext);
1292
+ if (utils.isPromise(readyPromise)) await readyPromise;
1293
+ throw err;
1294
+ }
1295
+ if (redirect.isRedirect(err)) {
1223
1296
  throw err;
1224
1297
  }
1225
1298
  }
1226
- return matches;
1299
+ return innerLoadContext.matches;
1227
1300
  };
1228
1301
  this.invalidate = (opts) => {
1229
1302
  const invalidate = (d) => {
@@ -1421,11 +1494,11 @@ class RouterCore {
1421
1494
  }
1422
1495
  return match;
1423
1496
  };
1424
- this._handleNotFound = (matches, err, updateMatch = this.updateMatch) => {
1497
+ this._handleNotFound = (innerLoadContext, err) => {
1425
1498
  var _a;
1426
1499
  const routeCursor = this.routesById[err.routeId ?? ""] ?? this.routeTree;
1427
1500
  const matchesByRouteId = {};
1428
- for (const match of matches) {
1501
+ for (const match of innerLoadContext.matches) {
1429
1502
  matchesByRouteId[match.routeId] = match;
1430
1503
  }
1431
1504
  if (!routeCursor.options.notFoundComponent && ((_a = this.options) == null ? void 0 : _a.defaultNotFoundComponent)) {
@@ -1440,7 +1513,7 @@ class RouterCore {
1440
1513
  matchForRoute,
1441
1514
  "Could not find match for route: " + routeCursor.id
1442
1515
  );
1443
- updateMatch(matchForRoute.id, (prev) => ({
1516
+ innerLoadContext.updateMatch(matchForRoute.id, (prev) => ({
1444
1517
  ...prev,
1445
1518
  status: "notFound",
1446
1519
  error: err,
@@ -1448,7 +1521,7 @@ class RouterCore {
1448
1521
  }));
1449
1522
  if (err.routerCode === "BEFORE_LOAD" && routeCursor.parentRoute) {
1450
1523
  err.routeId = routeCursor.parentRoute.id;
1451
- this._handleNotFound(matches, err, updateMatch);
1524
+ this._handleNotFound(innerLoadContext, err);
1452
1525
  }
1453
1526
  };
1454
1527
  this.hasNotFoundMatch = () => {
@@ -1687,6 +1760,12 @@ class SearchParamError extends Error {
1687
1760
  }
1688
1761
  class PathParamError extends Error {
1689
1762
  }
1763
+ function makeMaybe(value, error) {
1764
+ if (error) {
1765
+ return { status: "error", error };
1766
+ }
1767
+ return { status: "success", value };
1768
+ }
1690
1769
  const normalize = (str) => str.endsWith("/") && str.length > 1 ? str.slice(0, -1) : str;
1691
1770
  function comparePaths(a, b) {
1692
1771
  return normalize(a) === normalize(b);