@rangojs/router 0.0.0-experimental.39 → 0.0.0-experimental.3b1deca8
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/bin/rango.js +8 -3
- package/dist/vite/index.js +292 -204
- package/package.json +1 -1
- package/skills/cache-guide/SKILL.md +32 -0
- package/skills/caching/SKILL.md +45 -4
- package/skills/loader/SKILL.md +53 -43
- package/skills/parallel/SKILL.md +126 -0
- package/skills/route/SKILL.md +31 -0
- package/skills/router-setup/SKILL.md +52 -2
- package/skills/typesafety/SKILL.md +10 -0
- package/src/browser/debug-channel.ts +93 -0
- package/src/browser/event-controller.ts +5 -0
- package/src/browser/navigation-bridge.ts +1 -5
- package/src/browser/navigation-client.ts +84 -27
- package/src/browser/navigation-transaction.ts +11 -9
- package/src/browser/partial-update.ts +50 -9
- package/src/browser/prefetch/cache.ts +57 -5
- package/src/browser/prefetch/fetch.ts +30 -21
- package/src/browser/prefetch/queue.ts +92 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/react/Link.tsx +9 -1
- package/src/browser/react/NavigationProvider.tsx +32 -3
- package/src/browser/rsc-router.tsx +109 -57
- package/src/browser/scroll-restoration.ts +31 -34
- package/src/browser/segment-reconciler.ts +6 -1
- package/src/browser/server-action-bridge.ts +12 -0
- package/src/browser/types.ts +17 -1
- package/src/build/route-types/router-processing.ts +12 -2
- package/src/cache/cache-runtime.ts +15 -11
- package/src/cache/cache-scope.ts +48 -7
- package/src/cache/cf/cf-cache-store.ts +453 -11
- package/src/cache/cf/index.ts +5 -1
- package/src/cache/document-cache.ts +17 -7
- package/src/cache/index.ts +1 -0
- package/src/cache/taint.ts +55 -0
- package/src/context-var.ts +72 -2
- package/src/debug.ts +2 -2
- package/src/deps/browser.ts +1 -0
- package/src/route-definition/dsl-helpers.ts +32 -7
- package/src/route-definition/helpers-types.ts +6 -5
- package/src/route-definition/redirect.ts +2 -2
- package/src/route-map-builder.ts +7 -1
- package/src/router/find-match.ts +4 -2
- package/src/router/handler-context.ts +31 -8
- package/src/router/intercept-resolution.ts +2 -0
- package/src/router/lazy-includes.ts +4 -1
- package/src/router/loader-resolution.ts +7 -1
- package/src/router/logging.ts +5 -2
- package/src/router/manifest.ts +9 -3
- package/src/router/match-middleware/background-revalidation.ts +30 -2
- package/src/router/match-middleware/cache-lookup.ts +66 -9
- package/src/router/match-middleware/cache-store.ts +53 -10
- package/src/router/match-middleware/intercept-resolution.ts +9 -7
- package/src/router/match-middleware/segment-resolution.ts +8 -5
- package/src/router/match-result.ts +22 -6
- package/src/router/metrics.ts +6 -1
- package/src/router/middleware-types.ts +6 -2
- package/src/router/middleware.ts +4 -3
- package/src/router/router-context.ts +6 -1
- package/src/router/segment-resolution/fresh.ts +130 -17
- package/src/router/segment-resolution/helpers.ts +29 -24
- package/src/router/segment-resolution/loader-cache.ts +1 -0
- package/src/router/segment-resolution/revalidation.ts +352 -290
- package/src/router/segment-wrappers.ts +2 -0
- package/src/router/types.ts +1 -0
- package/src/router.ts +6 -1
- package/src/rsc/handler.ts +28 -2
- package/src/rsc/loader-fetch.ts +7 -2
- package/src/rsc/progressive-enhancement.ts +4 -1
- package/src/rsc/rsc-rendering.ts +4 -1
- package/src/rsc/server-action.ts +2 -0
- package/src/rsc/types.ts +7 -1
- package/src/segment-system.tsx +140 -4
- package/src/server/context.ts +102 -13
- package/src/server/request-context.ts +59 -12
- package/src/ssr/index.tsx +1 -0
- package/src/types/handler-context.ts +120 -22
- package/src/types/loader-types.ts +4 -4
- package/src/types/route-entry.ts +7 -0
- package/src/types/segments.ts +2 -0
- package/src/urls/path-helper.ts +1 -1
- package/src/vite/discovery/state.ts +0 -2
- package/src/vite/plugin-types.ts +0 -83
- package/src/vite/plugins/expose-action-id.ts +1 -3
- package/src/vite/plugins/performance-tracks.ts +235 -0
- package/src/vite/plugins/version-plugin.ts +13 -1
- package/src/vite/rango.ts +148 -209
- package/src/vite/router-discovery.ts +0 -8
- package/src/vite/utils/banner.ts +3 -3
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
|
|
8
8
|
import type { ReactNode } from "react";
|
|
9
9
|
import { invariant } from "../../errors";
|
|
10
|
-
import
|
|
10
|
+
import {
|
|
11
|
+
getParallelEntries,
|
|
12
|
+
getParallelSlotEntries,
|
|
13
|
+
type EntryData,
|
|
14
|
+
} from "../../server/context";
|
|
11
15
|
import type {
|
|
12
16
|
HandlerContext,
|
|
13
17
|
InternalHandlerContext,
|
|
@@ -15,6 +19,8 @@ import type {
|
|
|
15
19
|
} from "../../types";
|
|
16
20
|
import type { SegmentResolutionDeps } from "../types.js";
|
|
17
21
|
import { resolveLoaderData } from "./loader-cache.js";
|
|
22
|
+
import { _getRequestContext } from "../../server/request-context.js";
|
|
23
|
+
import { appendMetric } from "../metrics.js";
|
|
18
24
|
import {
|
|
19
25
|
handleHandlerResult,
|
|
20
26
|
tryStaticHandler,
|
|
@@ -24,7 +30,7 @@ import {
|
|
|
24
30
|
} from "./helpers.js";
|
|
25
31
|
import { getRouterContext } from "../router-context.js";
|
|
26
32
|
import { resolveSink, safeEmit } from "../telemetry.js";
|
|
27
|
-
import { track } from "../../server/context.js";
|
|
33
|
+
import { track, RSCRouterContext } from "../../server/context.js";
|
|
28
34
|
|
|
29
35
|
// ---------------------------------------------------------------------------
|
|
30
36
|
// Streamed handler telemetry
|
|
@@ -90,9 +96,11 @@ export async function resolveLoaders<TEnv>(
|
|
|
90
96
|
const shortCode = shortCodeOverride ?? entry.shortCode;
|
|
91
97
|
const hasLoading = "loading" in entry && entry.loading !== undefined;
|
|
92
98
|
const loadingDisabled = hasLoading && entry.loading === false;
|
|
99
|
+
const ms = _getRequestContext()?._metricsStore;
|
|
93
100
|
|
|
94
101
|
if (!loadingDisabled) {
|
|
95
|
-
|
|
102
|
+
// Streaming loaders: promises kick off now, settle during RSC serialization.
|
|
103
|
+
const segments = loaderEntries.map((loaderEntry, i) => {
|
|
96
104
|
const { loader } = loaderEntry;
|
|
97
105
|
const segmentId = `${shortCode}D${i}.${loader.$$id}`;
|
|
98
106
|
return {
|
|
@@ -112,18 +120,36 @@ export async function resolveLoaders<TEnv>(
|
|
|
112
120
|
belongsToRoute,
|
|
113
121
|
};
|
|
114
122
|
});
|
|
123
|
+
|
|
124
|
+
return segments;
|
|
115
125
|
}
|
|
116
126
|
|
|
117
127
|
// Loading disabled: still start all loaders in parallel, but only emit
|
|
118
128
|
// settled promises so handlers don't stream loading placeholders.
|
|
119
|
-
const pendingLoaderData = loaderEntries.map((loaderEntry) =>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
129
|
+
const pendingLoaderData = loaderEntries.map((loaderEntry) => {
|
|
130
|
+
const start = performance.now();
|
|
131
|
+
const promise = resolveLoaderData(loaderEntry, ctx, ctx.pathname);
|
|
132
|
+
return { promise, start, loaderId: loaderEntry.loader.$$id };
|
|
133
|
+
});
|
|
134
|
+
await Promise.all(pendingLoaderData.map((p) => p.promise));
|
|
123
135
|
|
|
124
136
|
return loaderEntries.map((loaderEntry, i) => {
|
|
125
137
|
const { loader } = loaderEntry;
|
|
126
138
|
const segmentId = `${shortCode}D${i}.${loader.$$id}`;
|
|
139
|
+
const pending = pendingLoaderData[i]!;
|
|
140
|
+
if (ms && !ms.metrics.some((m) => m.label === `loader:${loader.$$id}`)) {
|
|
141
|
+
// All loaders ran in parallel via Promise.all — each span covers
|
|
142
|
+
// from its own kickoff to the batch settlement, giving a ceiling
|
|
143
|
+
// on that loader's contribution to the overall wait.
|
|
144
|
+
const batchEnd = performance.now();
|
|
145
|
+
appendMetric(
|
|
146
|
+
ms,
|
|
147
|
+
`loader:${loader.$$id}`,
|
|
148
|
+
pending.start,
|
|
149
|
+
batchEnd - pending.start,
|
|
150
|
+
2,
|
|
151
|
+
);
|
|
152
|
+
}
|
|
127
153
|
return {
|
|
128
154
|
id: segmentId,
|
|
129
155
|
namespace: entry.id,
|
|
@@ -133,7 +159,7 @@ export async function resolveLoaders<TEnv>(
|
|
|
133
159
|
params: ctx.params,
|
|
134
160
|
loaderId: loader.$$id,
|
|
135
161
|
loaderData: deps.wrapLoaderPromise(
|
|
136
|
-
|
|
162
|
+
pending.promise,
|
|
137
163
|
entry,
|
|
138
164
|
segmentId,
|
|
139
165
|
ctx.pathname,
|
|
@@ -197,7 +223,10 @@ export async function resolveSegment<TEnv>(
|
|
|
197
223
|
...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
|
|
198
224
|
});
|
|
199
225
|
|
|
200
|
-
|
|
226
|
+
const resolvedParallelEntries = new Set<string>();
|
|
227
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
228
|
+
entry.parallel,
|
|
229
|
+
)) {
|
|
201
230
|
const parallelSegments = await resolveParallelEntry(
|
|
202
231
|
parallelEntry,
|
|
203
232
|
params,
|
|
@@ -207,8 +236,11 @@ export async function resolveSegment<TEnv>(
|
|
|
207
236
|
deps,
|
|
208
237
|
options,
|
|
209
238
|
routeKey,
|
|
239
|
+
[slot],
|
|
240
|
+
!resolvedParallelEntries.has(parallelEntry.id),
|
|
210
241
|
);
|
|
211
242
|
segments.push(...parallelSegments);
|
|
243
|
+
resolvedParallelEntries.add(parallelEntry.id);
|
|
212
244
|
}
|
|
213
245
|
|
|
214
246
|
for (const orphan of entry.layout) {
|
|
@@ -286,7 +318,10 @@ export async function resolveSegment<TEnv>(
|
|
|
286
318
|
segments.push(...orphanSegments);
|
|
287
319
|
}
|
|
288
320
|
|
|
289
|
-
|
|
321
|
+
const resolvedParallelEntries = new Set<string>();
|
|
322
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
323
|
+
entry.parallel,
|
|
324
|
+
)) {
|
|
290
325
|
const parallelSegments = await resolveParallelEntry(
|
|
291
326
|
parallelEntry,
|
|
292
327
|
params,
|
|
@@ -296,8 +331,11 @@ export async function resolveSegment<TEnv>(
|
|
|
296
331
|
deps,
|
|
297
332
|
options,
|
|
298
333
|
routeKey,
|
|
334
|
+
[slot],
|
|
335
|
+
!resolvedParallelEntries.has(parallelEntry.id),
|
|
299
336
|
);
|
|
300
337
|
segments.push(...parallelSegments);
|
|
338
|
+
resolvedParallelEntries.add(parallelEntry.id);
|
|
301
339
|
}
|
|
302
340
|
|
|
303
341
|
segments.push({
|
|
@@ -305,7 +343,7 @@ export async function resolveSegment<TEnv>(
|
|
|
305
343
|
namespace: entry.id,
|
|
306
344
|
type: "route",
|
|
307
345
|
index: 0,
|
|
308
|
-
component,
|
|
346
|
+
component: component ?? null,
|
|
309
347
|
loading: entry.loading === false ? null : entry.loading,
|
|
310
348
|
transition: entry.transition,
|
|
311
349
|
params,
|
|
@@ -368,7 +406,10 @@ export async function resolveOrphanLayout<TEnv>(
|
|
|
368
406
|
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
369
407
|
});
|
|
370
408
|
|
|
371
|
-
|
|
409
|
+
const resolvedParallelEntries = new Set<string>();
|
|
410
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
411
|
+
orphan.parallel,
|
|
412
|
+
)) {
|
|
372
413
|
const parallelSegments = await resolveParallelEntry(
|
|
373
414
|
parallelEntry,
|
|
374
415
|
params,
|
|
@@ -378,8 +419,11 @@ export async function resolveOrphanLayout<TEnv>(
|
|
|
378
419
|
deps,
|
|
379
420
|
options,
|
|
380
421
|
routeKey,
|
|
422
|
+
[slot],
|
|
423
|
+
!resolvedParallelEntries.has(parallelEntry.id),
|
|
381
424
|
);
|
|
382
425
|
segments.push(...parallelSegments);
|
|
426
|
+
resolvedParallelEntries.add(parallelEntry.id);
|
|
383
427
|
}
|
|
384
428
|
|
|
385
429
|
return segments;
|
|
@@ -397,6 +441,8 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
397
441
|
deps: SegmentResolutionDeps<TEnv>,
|
|
398
442
|
options?: ResolveSegmentOptions,
|
|
399
443
|
routeKey?: string,
|
|
444
|
+
slotNames?: `@${string}`[],
|
|
445
|
+
includeLoaders: boolean = true,
|
|
400
446
|
): Promise<ResolvedSegment[]> {
|
|
401
447
|
invariant(
|
|
402
448
|
parallelEntry.type === "parallel",
|
|
@@ -411,7 +457,12 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
411
457
|
| ReactNode
|
|
412
458
|
>;
|
|
413
459
|
|
|
414
|
-
|
|
460
|
+
const slotsToResolve = slotNames ?? (Object.keys(slots) as `@${string}`[]);
|
|
461
|
+
|
|
462
|
+
for (const slot of slotsToResolve) {
|
|
463
|
+
// Try static lookup first — in production, handler bodies are evicted
|
|
464
|
+
// and replaced with stubs that have no .handler property (undefined).
|
|
465
|
+
// The static store holds the pre-rendered component for these slots.
|
|
415
466
|
let component: ReactNode | undefined = await tryStaticSlot(
|
|
416
467
|
parallelEntry,
|
|
417
468
|
slot,
|
|
@@ -419,6 +470,10 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
419
470
|
);
|
|
420
471
|
|
|
421
472
|
if (component === undefined) {
|
|
473
|
+
const handler = slots[slot];
|
|
474
|
+
if (handler === undefined) {
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
422
477
|
const doneParallelHandler = track(
|
|
423
478
|
`handler:${parallelEntry.id}.${slot}`,
|
|
424
479
|
2,
|
|
@@ -472,7 +527,7 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
472
527
|
});
|
|
473
528
|
}
|
|
474
529
|
|
|
475
|
-
if (!
|
|
530
|
+
if (!options?.skipLoaders && includeLoaders) {
|
|
476
531
|
const loaderSegments = await resolveLoaders(
|
|
477
532
|
parallelEntry,
|
|
478
533
|
context,
|
|
@@ -480,6 +535,15 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
480
535
|
deps,
|
|
481
536
|
parentShortCode,
|
|
482
537
|
);
|
|
538
|
+
// Tag parallel-owned loaders so renderSegments can stream them
|
|
539
|
+
// using the parallel's loading() instead of awaiting on the layout
|
|
540
|
+
const parallelLoading =
|
|
541
|
+
parallelEntry.loading === false ? undefined : parallelEntry.loading;
|
|
542
|
+
if (parallelLoading) {
|
|
543
|
+
for (const seg of loaderSegments) {
|
|
544
|
+
seg.parallelLoading = parallelLoading;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
483
547
|
segments.push(...loaderSegments);
|
|
484
548
|
}
|
|
485
549
|
|
|
@@ -515,6 +579,13 @@ export async function resolveAllSegments<TEnv>(
|
|
|
515
579
|
} catch {}
|
|
516
580
|
|
|
517
581
|
for (const entry of entries) {
|
|
582
|
+
// Set ALS flag when entering a cache() boundary so that ctx.get()
|
|
583
|
+
// can guard non-cacheable variable reads. Also guards response-level
|
|
584
|
+
// side effects (headers.set). Persists for all descendant entries.
|
|
585
|
+
if (entry.type === "cache") {
|
|
586
|
+
const store = RSCRouterContext.getStore();
|
|
587
|
+
if (store) store.insideCacheScope = true;
|
|
588
|
+
}
|
|
518
589
|
const doneEntry = track(`segment:${entry.id}`, 1);
|
|
519
590
|
const resolvedSegments = await resolveWithErrorBoundary(
|
|
520
591
|
entry,
|
|
@@ -559,11 +630,53 @@ export async function resolveLoadersOnly<TEnv>(
|
|
|
559
630
|
deps: SegmentResolutionDeps<TEnv>,
|
|
560
631
|
): Promise<ResolvedSegment[]> {
|
|
561
632
|
const loaderSegments: ResolvedSegment[] = [];
|
|
633
|
+
const seenIds = new Set<string>();
|
|
634
|
+
|
|
635
|
+
async function collectEntryLoaders(
|
|
636
|
+
entry: EntryData,
|
|
637
|
+
belongsToRoute: boolean,
|
|
638
|
+
shortCodeOverride?: string,
|
|
639
|
+
): Promise<void> {
|
|
640
|
+
// Skip if all loaders from this entry have already been resolved
|
|
641
|
+
// via a parent (e.g., cache boundary wrapping a layout with shared loaders).
|
|
642
|
+
const entryLoaders = entry.loader ?? [];
|
|
643
|
+
const sc = shortCodeOverride ?? entry.shortCode;
|
|
644
|
+
const allAlreadySeen =
|
|
645
|
+
entryLoaders.length > 0 &&
|
|
646
|
+
entryLoaders.every((le, i) =>
|
|
647
|
+
seenIds.has(`${sc}D${i}.${le.loader.$$id}`),
|
|
648
|
+
);
|
|
649
|
+
if (!allAlreadySeen) {
|
|
650
|
+
const segments = await resolveLoaders(
|
|
651
|
+
entry,
|
|
652
|
+
context,
|
|
653
|
+
belongsToRoute,
|
|
654
|
+
deps,
|
|
655
|
+
shortCodeOverride,
|
|
656
|
+
);
|
|
657
|
+
for (const seg of segments) {
|
|
658
|
+
if (!seenIds.has(seg.id)) {
|
|
659
|
+
seenIds.add(seg.id);
|
|
660
|
+
loaderSegments.push(seg);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
const seenParallelEntryIds = new Set<string>();
|
|
666
|
+
for (const parallelEntry of getParallelEntries(entry.parallel)) {
|
|
667
|
+
if (seenParallelEntryIds.has(parallelEntry.id)) continue;
|
|
668
|
+
seenParallelEntryIds.add(parallelEntry.id);
|
|
669
|
+
await collectEntryLoaders(parallelEntry, belongsToRoute, entry.shortCode);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const childBelongsToRoute = belongsToRoute || entry.type === "route";
|
|
673
|
+
for (const layoutEntry of entry.layout) {
|
|
674
|
+
await collectEntryLoaders(layoutEntry, childBelongsToRoute);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
562
677
|
|
|
563
678
|
for (const entry of entries) {
|
|
564
|
-
|
|
565
|
-
const segments = await resolveLoaders(entry, context, belongsToRoute, deps);
|
|
566
|
-
loaderSegments.push(...segments);
|
|
679
|
+
await collectEntryLoaders(entry, entry.type === "route");
|
|
567
680
|
}
|
|
568
681
|
|
|
569
682
|
return loaderSegments;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* - Error boundary segment creation
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type
|
|
11
|
+
import { createElement, type ReactNode } from "react";
|
|
12
12
|
import { DataNotFoundError } from "../../errors";
|
|
13
13
|
import {
|
|
14
14
|
createErrorInfo,
|
|
@@ -180,34 +180,39 @@ export function catchSegmentError<TEnv>(
|
|
|
180
180
|
|
|
181
181
|
if (error instanceof DataNotFoundError) {
|
|
182
182
|
const notFoundFallback = deps.findNearestNotFoundBoundary(entry);
|
|
183
|
+
// Fall back to router's notFound component, then a plain default
|
|
184
|
+
const notFoundOption = deps.notFoundComponent;
|
|
185
|
+
const defaultFallback =
|
|
186
|
+
typeof notFoundOption === "function"
|
|
187
|
+
? notFoundOption({ pathname: pathname ?? "" })
|
|
188
|
+
: (notFoundOption ?? createElement("h1", null, "Not Found"));
|
|
189
|
+
const effectiveNotFoundFallback = notFoundFallback ?? defaultFallback;
|
|
183
190
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
);
|
|
191
|
+
const notFoundInfo = createNotFoundInfo(
|
|
192
|
+
error,
|
|
193
|
+
entry.shortCode,
|
|
194
|
+
entry.type,
|
|
195
|
+
pathname,
|
|
196
|
+
);
|
|
191
197
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
198
|
+
reportError(true, {
|
|
199
|
+
notFound: true,
|
|
200
|
+
message: notFoundInfo.message,
|
|
201
|
+
});
|
|
196
202
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
203
|
+
debugLog("segment", "notFound boundary handled error", {
|
|
204
|
+
segmentId: entry.shortCode,
|
|
205
|
+
message: notFoundInfo.message,
|
|
206
|
+
});
|
|
201
207
|
|
|
202
|
-
|
|
208
|
+
setResponseStatus(404);
|
|
203
209
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
210
|
+
return createNotFoundSegment(
|
|
211
|
+
notFoundInfo,
|
|
212
|
+
effectiveNotFoundFallback,
|
|
213
|
+
entry,
|
|
214
|
+
params,
|
|
215
|
+
);
|
|
211
216
|
}
|
|
212
217
|
|
|
213
218
|
const fallback = deps.findNearestErrorBoundary(entry);
|
|
@@ -147,6 +147,7 @@ export function resolveLoaderData<TEnv>(
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
const loaderId = loaderEntry.loader.$$id;
|
|
150
|
+
|
|
150
151
|
const ttl = resolveTtl(options.ttl, store.defaults, DEFAULT_ROUTE_TTL);
|
|
151
152
|
const swrWindow = resolveSwrWindow(options.swr, store.defaults);
|
|
152
153
|
const swr = swrWindow || undefined;
|