litzjs 0.0.0 → 0.1.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.
package/dist/client.js CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("./chunk-8l464Juk.js");
3
3
  const require_bindings = require("./bindings-B1P6pL93.js");
4
- const require_internal_transport = require("./internal-transport-DR0r68ff.js");
4
+ const require_internal_transport = require("./internal-transport-Er_DZe-u.js");
5
5
  let react = require("react");
6
6
  react = require_chunk.__toESM(react);
7
7
  let virtual_litzjs_route_manifest = require("virtual:litzjs:route-manifest");
@@ -191,7 +191,7 @@ async function createViewResult(response, publicHeaders = createPublicResultHead
191
191
  const node = await createFromReadableStream(response.body);
192
192
  return {
193
193
  kind: "view",
194
- status: Number(response.headers.get("x-litz-status") ?? response.status),
194
+ status: Number(response.headers.get("x-litzjs-status") ?? response.status),
195
195
  headers: publicHeaders,
196
196
  stale: false,
197
197
  node,
@@ -207,7 +207,7 @@ function isRedirectSignal(value) {
207
207
  return typeof value === "object" && value !== null && "kind" in value && value.kind === "redirect";
208
208
  }
209
209
  function getRevalidateTargets(headers) {
210
- const value = headers.get("x-litz-revalidate");
210
+ const value = headers.get("x-litzjs-revalidate");
211
211
  if (!value) return [];
212
212
  return value.split(",").map((entry) => entry.trim()).filter(Boolean);
213
213
  }
@@ -232,6 +232,207 @@ function createRedirectSignal(status, headers, body) {
232
232
  };
233
233
  }
234
234
  //#endregion
235
+ //#region src/client/route-runtime.tsx
236
+ let routeLocationContext = null;
237
+ let routeStatusContext = null;
238
+ let routeDataContext = null;
239
+ let routeActionsContext = null;
240
+ const routeFormComponentCache = /* @__PURE__ */ new Map();
241
+ function createRuntimeContext$1(name) {
242
+ const createContext = react.createContext;
243
+ if (!createContext) throw new Error(`${name} is not available in this environment.`);
244
+ return createContext(null);
245
+ }
246
+ function getRouteLocationContext() {
247
+ routeLocationContext ??= createRuntimeContext$1("Litz route location");
248
+ return routeLocationContext;
249
+ }
250
+ function getRouteStatusContext() {
251
+ routeStatusContext ??= createRuntimeContext$1("Litz route status");
252
+ return routeStatusContext;
253
+ }
254
+ function getRouteDataContext() {
255
+ routeDataContext ??= createRuntimeContext$1("Litz route data");
256
+ return routeDataContext;
257
+ }
258
+ function getRouteActionsContext() {
259
+ routeActionsContext ??= createRuntimeContext$1("Litz route actions");
260
+ return routeActionsContext;
261
+ }
262
+ function requireActiveRouteSlice(routeId, value) {
263
+ if (!value) throw new Error(`Route "${routeId}" is being used outside the Litz runtime.`);
264
+ if (value.id !== routeId) throw new Error(`Route "${routeId}" is not the active route. Active route is "${value.id}".`);
265
+ return value;
266
+ }
267
+ function RouteRuntimeProvider(props) {
268
+ const RouteLocationContext = getRouteLocationContext();
269
+ const RouteStatusContext = getRouteStatusContext();
270
+ const RouteDataContext = getRouteDataContext();
271
+ const RouteActionsContext = getRouteActionsContext();
272
+ const locationValue = react.useMemo(() => ({
273
+ id: props.value.id,
274
+ params: props.value.params,
275
+ search: props.value.search,
276
+ setSearch: props.value.setSearch
277
+ }), [
278
+ props.value.id,
279
+ props.value.params,
280
+ props.value.search,
281
+ props.value.setSearch
282
+ ]);
283
+ const statusValue = react.useMemo(() => ({
284
+ id: props.value.id,
285
+ status: props.value.status,
286
+ pending: props.value.pending
287
+ }), [
288
+ props.value.id,
289
+ props.value.pending,
290
+ props.value.status
291
+ ]);
292
+ const dataValue = react.useMemo(() => ({
293
+ id: props.value.id,
294
+ loaderResult: props.value.loaderResult,
295
+ actionResult: props.value.actionResult,
296
+ data: props.value.data,
297
+ view: props.value.view
298
+ }), [
299
+ props.value.actionResult,
300
+ props.value.data,
301
+ props.value.id,
302
+ props.value.loaderResult,
303
+ props.value.view
304
+ ]);
305
+ const actionsValue = react.useMemo(() => ({
306
+ id: props.value.id,
307
+ submit: props.value.submit,
308
+ reload: props.value.reload,
309
+ retry: props.value.retry
310
+ }), [
311
+ props.value.id,
312
+ props.value.reload,
313
+ props.value.retry,
314
+ props.value.submit
315
+ ]);
316
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteLocationContext.Provider, {
317
+ value: locationValue,
318
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteStatusContext.Provider, {
319
+ value: statusValue,
320
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteDataContext.Provider, {
321
+ value: dataValue,
322
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteActionsContext.Provider, {
323
+ value: actionsValue,
324
+ children: props.children
325
+ })
326
+ })
327
+ })
328
+ });
329
+ }
330
+ function useRequiredRouteLocation(routeId) {
331
+ return requireActiveRouteSlice(routeId, react.useContext(getRouteLocationContext()));
332
+ }
333
+ function useRequiredRouteStatus(routeId) {
334
+ return requireActiveRouteSlice(routeId, react.useContext(getRouteStatusContext()));
335
+ }
336
+ function useRequiredRouteData(routeId) {
337
+ return requireActiveRouteSlice(routeId, react.useContext(getRouteDataContext()));
338
+ }
339
+ function useRequiredRouteActions(routeId) {
340
+ return requireActiveRouteSlice(routeId, react.useContext(getRouteActionsContext()));
341
+ }
342
+ function createRouteFormComponent(routeId) {
343
+ const cached = routeFormComponentCache.get(routeId);
344
+ if (cached) return cached;
345
+ const MemoizedLitzRouteForm = react.memo(function LitzRouteForm(props) {
346
+ const actions = useRequiredRouteActions(routeId);
347
+ const { children, onSubmit, replace, revalidate, ...rest } = props;
348
+ const submitRef = react.useRef((payload, options) => actions.submit(payload, options));
349
+ react.useEffect(() => {
350
+ submitRef.current = (payload, options) => actions.submit(payload, options);
351
+ }, [actions.submit]);
352
+ const action = react.useCallback(async (formData) => {
353
+ await submitRef.current(formData, {
354
+ replace,
355
+ revalidate
356
+ });
357
+ }, [replace, revalidate]);
358
+ return react.createElement("form", {
359
+ ...rest,
360
+ action,
361
+ onSubmit
362
+ }, children);
363
+ });
364
+ MemoizedLitzRouteForm.displayName = `LitzRouteForm(${routeId})`;
365
+ routeFormComponentCache.set(routeId, MemoizedLitzRouteForm);
366
+ return MemoizedLitzRouteForm;
367
+ }
368
+ //#endregion
369
+ //#region src/client/runtime.tsx
370
+ async function fetchRouteLoader(path, request, target, signal) {
371
+ return parseLoaderResponse(await fetch("/_litzjs/route", {
372
+ method: "POST",
373
+ headers: {
374
+ "content-type": "application/json",
375
+ accept: require_internal_transport.LITZ_RESULT_ACCEPT
376
+ },
377
+ body: JSON.stringify({
378
+ path,
379
+ target,
380
+ operation: "loader",
381
+ request: normalizeRequest(request)
382
+ }),
383
+ signal
384
+ }));
385
+ }
386
+ async function fetchRouteAction(path, request, payload) {
387
+ const actionRequest = require_internal_transport.createInternalActionRequestInit({
388
+ path,
389
+ operation: "action",
390
+ request: normalizeRequest(request)
391
+ }, payload);
392
+ return parseActionResponse(await fetch("/_litzjs/action", {
393
+ method: "POST",
394
+ headers: actionRequest.headers,
395
+ body: actionRequest.body
396
+ }));
397
+ }
398
+ function normalizeRequest(request) {
399
+ return {
400
+ params: request.params ?? {},
401
+ search: request.search instanceof URLSearchParams ? Object.fromEntries(request.search.entries()) : request.search ?? {}
402
+ };
403
+ }
404
+ //#endregion
405
+ //#region src/client/loader-fetch.ts
406
+ async function fetchRouteLoadersInParallel(matches, context) {
407
+ return Promise.allSettled(matches.map((match) => fetchRouteLoader(context.routePath, context.baseRequest, match.id, context.signal).then((loaderResult) => ({
408
+ match,
409
+ loaderResult
410
+ }))));
411
+ }
412
+ function processLoaderResults(settled, matches, callbacks) {
413
+ for (const [index, result] of settled.entries()) {
414
+ if (callbacks.isCancelled?.()) return;
415
+ if (result.status === "rejected") {
416
+ const error = result.reason;
417
+ if (isRedirectSignal(error)) {
418
+ callbacks.onRedirect(error.location);
419
+ return;
420
+ }
421
+ if (isRouteLikeError(error)) {
422
+ callbacks.onRouteError(matches[index].id, error);
423
+ return;
424
+ }
425
+ throw error;
426
+ }
427
+ callbacks.onResult(result.value.match, result.value.loaderResult);
428
+ }
429
+ }
430
+ //#endregion
431
+ //#region src/client/sort-record.ts
432
+ function sortRecord(record) {
433
+ return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
434
+ }
435
+ //#endregion
235
436
  //#region src/client/resources.tsx
236
437
  const RESOURCE_STORE_LIMIT = 200;
237
438
  const resourceStore = /* @__PURE__ */ new Map();
@@ -241,25 +442,25 @@ let resourceLocationContext = null;
241
442
  let resourceStatusContext = null;
242
443
  let resourceDataContext = null;
243
444
  let resourceActionsContext = null;
244
- function createRuntimeContext$1(name) {
445
+ function createRuntimeContext(name) {
245
446
  const createContext = react.createContext;
246
447
  if (!createContext) throw new Error(`${name} is not available in this environment.`);
247
448
  return createContext(null);
248
449
  }
249
450
  function getResourceLocationContext() {
250
- resourceLocationContext ??= createRuntimeContext$1("Litz resource location");
451
+ resourceLocationContext ??= createRuntimeContext("Litz resource location");
251
452
  return resourceLocationContext;
252
453
  }
253
454
  function getResourceStatusContext() {
254
- resourceStatusContext ??= createRuntimeContext$1("Litz resource status");
455
+ resourceStatusContext ??= createRuntimeContext("Litz resource status");
255
456
  return resourceStatusContext;
256
457
  }
257
458
  function getResourceDataContext() {
258
- resourceDataContext ??= createRuntimeContext$1("Litz resource data");
459
+ resourceDataContext ??= createRuntimeContext("Litz resource data");
259
460
  return resourceDataContext;
260
461
  }
261
462
  function getResourceActionsContext() {
262
- resourceActionsContext ??= createRuntimeContext$1("Litz resource actions");
463
+ resourceActionsContext ??= createRuntimeContext("Litz resource actions");
263
464
  return resourceActionsContext;
264
465
  }
265
466
  function requireActiveResourceSlice(resourcePath, value) {
@@ -477,7 +678,7 @@ async function performPreparedResourceRequest(resourcePath, operation, preparedR
477
678
  notify(entry);
478
679
  entry.inFlight = (async () => {
479
680
  try {
480
- const response = operation === "action" ? await fetch("/_litz/resource", {
681
+ const response = operation === "action" ? await fetch("/_litzjs/resource", {
481
682
  method: "POST",
482
683
  ...require_internal_transport.createInternalActionRequestInit({
483
684
  path: resourcePath,
@@ -487,7 +688,7 @@ async function performPreparedResourceRequest(resourcePath, operation, preparedR
487
688
  search: normalizedRequest.search
488
689
  }
489
690
  }, payload)
490
- }) : await fetch("/_litz/resource", {
691
+ }) : await fetch("/_litzjs/resource", {
491
692
  method: "POST",
492
693
  headers: {
493
694
  "content-type": "application/json",
@@ -594,19 +795,16 @@ function prepareResourceRequest(resourcePath, request) {
594
795
  function createResourceCacheKey(resourcePath, normalizedRequest) {
595
796
  return JSON.stringify({
596
797
  path: resourcePath,
597
- params: sortRecord$1(normalizedRequest.params),
598
- search: sortRecord$1(normalizedRequest.search)
798
+ params: sortRecord(normalizedRequest.params),
799
+ search: sortRecord(normalizedRequest.search)
599
800
  });
600
801
  }
601
- function sortRecord$1(value) {
602
- return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)));
603
- }
604
802
  function subscribe(key, listener) {
605
803
  const entry = getEntry(key);
606
804
  entry.listeners.add(listener);
607
805
  return () => {
608
806
  entry.listeners.delete(listener);
609
- cleanupResourceEntry(key, entry);
807
+ deferredCleanupResourceEntry(key, entry);
610
808
  };
611
809
  }
612
810
  function getInitialSnapshot() {
@@ -638,6 +836,13 @@ function cleanupResourceEntry(key, entry) {
638
836
  if (entry.listeners.size > 0 || entry.inFlight || entry.snapshot.pending) return;
639
837
  resourceStore.delete(key);
640
838
  }
839
+ function deferredCleanupResourceEntry(key, entry) {
840
+ if (entry.listeners.size > 0 || entry.inFlight || entry.snapshot.pending) return;
841
+ queueMicrotask(() => {
842
+ if (entry.listeners.size > 0 || entry.inFlight || entry.snapshot.pending) return;
843
+ if (resourceStore.get(key) === entry) resourceStore.delete(key);
844
+ });
845
+ }
641
846
  function pruneResourceStore() {
642
847
  if (resourceStore.size <= RESOURCE_STORE_LIMIT) return;
643
848
  for (const [key, entry] of resourceStore) {
@@ -758,175 +963,6 @@ function useResolvedRouteState(options) {
758
963
  };
759
964
  }
760
965
  //#endregion
761
- //#region src/client/route-runtime.tsx
762
- let routeLocationContext = null;
763
- let routeStatusContext = null;
764
- let routeDataContext = null;
765
- let routeActionsContext = null;
766
- const routeFormComponentCache = /* @__PURE__ */ new Map();
767
- function createRuntimeContext(name) {
768
- const createContext = react.createContext;
769
- if (!createContext) throw new Error(`${name} is not available in this environment.`);
770
- return createContext(null);
771
- }
772
- function getRouteLocationContext() {
773
- routeLocationContext ??= createRuntimeContext("Litz route location");
774
- return routeLocationContext;
775
- }
776
- function getRouteStatusContext() {
777
- routeStatusContext ??= createRuntimeContext("Litz route status");
778
- return routeStatusContext;
779
- }
780
- function getRouteDataContext() {
781
- routeDataContext ??= createRuntimeContext("Litz route data");
782
- return routeDataContext;
783
- }
784
- function getRouteActionsContext() {
785
- routeActionsContext ??= createRuntimeContext("Litz route actions");
786
- return routeActionsContext;
787
- }
788
- function requireActiveRouteSlice(routeId, value) {
789
- if (!value) throw new Error(`Route "${routeId}" is being used outside the Litz runtime.`);
790
- if (value.id !== routeId) throw new Error(`Route "${routeId}" is not the active route. Active route is "${value.id}".`);
791
- return value;
792
- }
793
- function RouteRuntimeProvider(props) {
794
- const RouteLocationContext = getRouteLocationContext();
795
- const RouteStatusContext = getRouteStatusContext();
796
- const RouteDataContext = getRouteDataContext();
797
- const RouteActionsContext = getRouteActionsContext();
798
- const locationValue = react.useMemo(() => ({
799
- id: props.value.id,
800
- params: props.value.params,
801
- search: props.value.search,
802
- setSearch: props.value.setSearch
803
- }), [
804
- props.value.id,
805
- props.value.params,
806
- props.value.search,
807
- props.value.setSearch
808
- ]);
809
- const statusValue = react.useMemo(() => ({
810
- id: props.value.id,
811
- status: props.value.status,
812
- pending: props.value.pending
813
- }), [
814
- props.value.id,
815
- props.value.pending,
816
- props.value.status
817
- ]);
818
- const dataValue = react.useMemo(() => ({
819
- id: props.value.id,
820
- loaderResult: props.value.loaderResult,
821
- actionResult: props.value.actionResult,
822
- data: props.value.data,
823
- view: props.value.view
824
- }), [
825
- props.value.actionResult,
826
- props.value.data,
827
- props.value.id,
828
- props.value.loaderResult,
829
- props.value.view
830
- ]);
831
- const actionsValue = react.useMemo(() => ({
832
- id: props.value.id,
833
- submit: props.value.submit,
834
- reload: props.value.reload,
835
- retry: props.value.retry
836
- }), [
837
- props.value.id,
838
- props.value.reload,
839
- props.value.retry,
840
- props.value.submit
841
- ]);
842
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteLocationContext.Provider, {
843
- value: locationValue,
844
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteStatusContext.Provider, {
845
- value: statusValue,
846
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteDataContext.Provider, {
847
- value: dataValue,
848
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteActionsContext.Provider, {
849
- value: actionsValue,
850
- children: props.children
851
- })
852
- })
853
- })
854
- });
855
- }
856
- function useRequiredRouteLocation(routeId) {
857
- return requireActiveRouteSlice(routeId, react.useContext(getRouteLocationContext()));
858
- }
859
- function useRequiredRouteStatus(routeId) {
860
- return requireActiveRouteSlice(routeId, react.useContext(getRouteStatusContext()));
861
- }
862
- function useRequiredRouteData(routeId) {
863
- return requireActiveRouteSlice(routeId, react.useContext(getRouteDataContext()));
864
- }
865
- function useRequiredRouteActions(routeId) {
866
- return requireActiveRouteSlice(routeId, react.useContext(getRouteActionsContext()));
867
- }
868
- function createRouteFormComponent(routeId) {
869
- const cached = routeFormComponentCache.get(routeId);
870
- if (cached) return cached;
871
- const MemoizedLitzRouteForm = react.memo(function LitzRouteForm(props) {
872
- const actions = useRequiredRouteActions(routeId);
873
- const { children, onSubmit, replace, revalidate, ...rest } = props;
874
- const submitRef = react.useRef((payload, options) => actions.submit(payload, options));
875
- react.useEffect(() => {
876
- submitRef.current = (payload, options) => actions.submit(payload, options);
877
- }, [actions.submit]);
878
- const action = react.useCallback(async (formData) => {
879
- await submitRef.current(formData, {
880
- replace,
881
- revalidate
882
- });
883
- }, [replace, revalidate]);
884
- return react.createElement("form", {
885
- ...rest,
886
- action,
887
- onSubmit
888
- }, children);
889
- });
890
- MemoizedLitzRouteForm.displayName = `LitzRouteForm(${routeId})`;
891
- routeFormComponentCache.set(routeId, MemoizedLitzRouteForm);
892
- return MemoizedLitzRouteForm;
893
- }
894
- //#endregion
895
- //#region src/client/runtime.tsx
896
- async function fetchRouteLoader(path, request, target) {
897
- return parseLoaderResponse(await fetch("/_litz/route", {
898
- method: "POST",
899
- headers: {
900
- "content-type": "application/json",
901
- accept: require_internal_transport.LITZ_RESULT_ACCEPT
902
- },
903
- body: JSON.stringify({
904
- path,
905
- target,
906
- operation: "loader",
907
- request: normalizeRequest(request)
908
- })
909
- }));
910
- }
911
- async function fetchRouteAction(path, request, payload) {
912
- const actionRequest = require_internal_transport.createInternalActionRequestInit({
913
- path,
914
- operation: "action",
915
- request: normalizeRequest(request)
916
- }, payload);
917
- return parseActionResponse(await fetch("/_litz/action", {
918
- method: "POST",
919
- headers: actionRequest.headers,
920
- body: actionRequest.body
921
- }));
922
- }
923
- function normalizeRequest(request) {
924
- return {
925
- params: request.params ?? {},
926
- search: request.search instanceof URLSearchParams ? Object.fromEntries(request.search.entries()) : request.search ?? {}
927
- };
928
- }
929
- //#endregion
930
966
  //#region src/client/index.ts
931
967
  require_bindings.installClientBindings({
932
968
  usePathname,
@@ -1087,12 +1123,12 @@ function RouteHost(props) {
1087
1123
  ]);
1088
1124
  react.useEffect(() => {
1089
1125
  if (!renderedRoute || !activeRouteState) return;
1090
- let cancelled = false;
1126
+ const controller = new AbortController();
1091
1127
  const { loaderMatches, baseRequest } = activeRouteState;
1092
1128
  const finalLoaderMatchId = loaderMatches[loaderMatches.length - 1]?.id;
1093
1129
  const reload = async (mode = "loading") => {
1094
1130
  if (loaderMatches.length === 0) {
1095
- if (!cancelled) setPageState((current) => ({
1131
+ if (!controller.signal.aborted) setPageState((current) => ({
1096
1132
  ...current,
1097
1133
  status: "idle",
1098
1134
  pending: false,
@@ -1102,33 +1138,33 @@ function RouteHost(props) {
1102
1138
  return;
1103
1139
  }
1104
1140
  setPageState((current) => applyCachedLoaderStateToPageState(current, loaderMatches, mode));
1105
- for (const entry of loaderMatches) try {
1106
- const loaderResult = await fetchRouteLoader(renderedRoute.path, baseRequest, entry.id);
1107
- if (cancelled) return;
1108
- setCachedLoaderResult(entry.cacheKey, withLoaderStaleState(loaderResult, false));
1109
- setPageState((current) => withMatchLoaderResult(current, entry.id, renderedRoute.id, loaderResult, entry.id === finalLoaderMatchId ? "idle" : current.status, entry.id === finalLoaderMatchId ? false : current.pending));
1110
- } catch (error) {
1111
- if (cancelled) return;
1112
- if (isRedirectSignal(error)) {
1113
- navigate(error.location, true);
1114
- return;
1115
- }
1116
- if (isRouteLikeError(error)) {
1141
+ processLoaderResults(await fetchRouteLoadersInParallel(loaderMatches, {
1142
+ routePath: renderedRoute.path,
1143
+ baseRequest,
1144
+ signal: controller.signal
1145
+ }), loaderMatches, {
1146
+ isCancelled: () => controller.signal.aborted,
1147
+ onResult(match, loaderResult) {
1148
+ setCachedLoaderResult(match.cacheKey, withLoaderStaleState(loaderResult, false));
1149
+ setPageState((current) => withMatchLoaderResult(current, match.id, renderedRoute.id, loaderResult, match.id === finalLoaderMatchId ? "idle" : current.status, match.id === finalLoaderMatchId ? false : current.pending));
1150
+ },
1151
+ onRedirect(location) {
1152
+ navigate(location, true);
1153
+ },
1154
+ onRouteError(matchId, error) {
1117
1155
  setPageState((current) => ({
1118
1156
  ...current,
1119
1157
  status: "error",
1120
1158
  pending: false,
1121
1159
  errorInfo: error,
1122
- errorTargetId: entry.id
1160
+ errorTargetId: matchId
1123
1161
  }));
1124
- return;
1125
1162
  }
1126
- throw error;
1127
- }
1163
+ });
1128
1164
  };
1129
1165
  reload();
1130
1166
  return () => {
1131
- cancelled = true;
1167
+ controller.abort();
1132
1168
  };
1133
1169
  }, [
1134
1170
  activeRouteState,
@@ -1318,27 +1354,30 @@ async function reloadCurrentRoute(options) {
1318
1354
  params: extractRouteParams(options.route.path, options.pathname) ?? {},
1319
1355
  search: options.search
1320
1356
  };
1321
- for (const entry of loaderMatches) try {
1322
- const loaderResult = await fetchRouteLoader(options.route.path, baseRequest, entry.id);
1323
- setCachedLoaderResult(entry.cacheKey, withLoaderStaleState(loaderResult, false));
1324
- options.setPageState((current) => withMatchLoaderResult(current, entry.id, options.route.id, loaderResult, entry === loaderMatches[loaderMatches.length - 1] ? "idle" : current.status, entry === loaderMatches[loaderMatches.length - 1] ? false : current.pending));
1325
- } catch (error) {
1326
- if (isRedirectSignal(error)) {
1327
- options.navigate(error.location, true);
1328
- return;
1329
- }
1330
- if (isRouteLikeError(error)) {
1357
+ const finalLoaderMatch = loaderMatches[loaderMatches.length - 1];
1358
+ processLoaderResults(await fetchRouteLoadersInParallel(loaderMatches, {
1359
+ routePath: options.route.path,
1360
+ baseRequest,
1361
+ signal: options.signal
1362
+ }), loaderMatches, {
1363
+ isCancelled: () => options.signal?.aborted === true,
1364
+ onResult(match, loaderResult) {
1365
+ setCachedLoaderResult(match.cacheKey, withLoaderStaleState(loaderResult, false));
1366
+ options.setPageState((current) => withMatchLoaderResult(current, match.id, options.route.id, loaderResult, match === finalLoaderMatch ? "idle" : current.status, match === finalLoaderMatch ? false : current.pending));
1367
+ },
1368
+ onRedirect(location) {
1369
+ options.navigate(location, true);
1370
+ },
1371
+ onRouteError(matchId, error) {
1331
1372
  options.setPageState((current) => ({
1332
1373
  ...current,
1333
1374
  status: "error",
1334
1375
  pending: false,
1335
1376
  errorInfo: error,
1336
- errorTargetId: entry.id
1377
+ errorTargetId: matchId
1337
1378
  }));
1338
- return;
1339
1379
  }
1340
- throw error;
1341
- }
1380
+ });
1342
1381
  }
1343
1382
  function renderMatchChain(route, matches, pageState, location, navigate, reloadImpl, setPageState) {
1344
1383
  const errorBoundaryIndex = findErrorBoundaryIndex(matches, pageState);
@@ -1558,9 +1597,6 @@ function getPathParamNames(pathPattern) {
1558
1597
  pathParamNamesCache.set(pathPattern, names);
1559
1598
  return names;
1560
1599
  }
1561
- function sortRecord(record) {
1562
- return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
1563
- }
1564
1600
  function getCachedLoaderResult(key) {
1565
1601
  const cached = routeCache.get(key);
1566
1602
  if (!cached) return;