next-leak 0.13.0 → 0.13.1
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/README.md +19 -4
- package/dist/{chunk-UKWFONEI.js → chunk-GBRBWUZZ.js} +28 -11
- package/dist/{chunk-QUFL2FXU.js → chunk-GU7IJUNY.js} +14 -0
- package/dist/{chunk-UOAW5UA5.js → chunk-QNNVRQ4L.js} +1 -1
- package/dist/cli.js +5 -11
- package/dist/{html-report-ZXDEJ6X5.js → html-report-VNTPHLO3.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/manifests.d.ts +3 -0
- package/dist/route-guidance.d.ts +2 -0
- package/dist/runner.d.ts +6 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -193,7 +193,10 @@ entry, so the route reads as flat whatever it retains, and that false negative
|
|
|
193
193
|
lands on exactly the leaks being reported now (`use cache`, `cacheComponents`
|
|
194
194
|
and ISR all key on the params). If your app answers 404 for params it never
|
|
195
195
|
prerendered, the run says so through its non-2xx count; drop the marker then.
|
|
196
|
-
When
|
|
196
|
+
When the build prerendered nothing for a route, the value is `REPLACE-ME-{n}`:
|
|
197
|
+
put the shape of a real value in place of `REPLACE-ME` and keep the `-{n}`, or
|
|
198
|
+
the run measures a single cache entry. When a run skips a route it prints the
|
|
199
|
+
same fragment.
|
|
197
200
|
|
|
198
201
|
```json
|
|
199
202
|
```
|
|
@@ -240,7 +243,7 @@ separates them, because each one has a different fix:
|
|
|
240
243
|
| One-time warm-up growth (JIT, lazy caches) | `stable` | The first cycle is excluded from the verdict; warm-up flattens, leaks keep climbing |
|
|
241
244
|
| A route that is expensive, not leaky | `failed` under load it cannot sustain, flat once concurrency fits | Real leaks survive forced GC at any concurrency; saturation disappears when load drops |
|
|
242
245
|
| Growth that pauses and resumes (stepwise) | `leak` | A healthy route gives back 20-30% of its growth; a stepwise leak gives back nothing |
|
|
243
|
-
| A cache filling up under the load that measures it | `saturating` | A bounded store grows by less each cycle as new keys get rarer; a leak does not decelerate |
|
|
246
|
+
| A cache filling up under the load that measures it | `saturating` | A bounded store grows by less each cycle as new keys get rarer; a leak does not decelerate. A bend that lands on a steady rate above the gate is a leak under the fill, and stays `leak` |
|
|
244
247
|
| Memory a forced GC reclaims that production never reclaims in time | `pressure` | Every verdict sample is post-GC; the peaks are sampled under load, and a ceiling every settled cycle comes back to is a regime, not an episode |
|
|
245
248
|
| Native/buffer memory with a flat JS heap | `leak (external)` or an explicit RSS note | Heap, `external` and RSS are sampled and judged separately |
|
|
246
249
|
| A leak in your code vs a dependency vs Next itself | `culprit: src/app/x/page.tsx (your code)` — or the package, or framework internals | Retainer chains mapped through the build's source maps |
|
|
@@ -271,7 +274,15 @@ separates them, because each one has a different fix:
|
|
|
271
274
|
alternative was calling it a leak: a `use cache` route measured on Next
|
|
272
275
|
16.3.3 came out at +603 MB per 1000 requests that was entirely the cache
|
|
273
276
|
storing what it had been asked to store — the same route dropped to +88 MB
|
|
274
|
-
once the payload was removed.
|
|
277
|
+
once the payload was removed. The bend has to head for zero, though: a store
|
|
278
|
+
filling on top of a leak bends too, and then holds the leak's rate for as
|
|
279
|
+
long as the load runs. So when the last third of the window (at least three
|
|
280
|
+
cycles) sits at one steady rate at or above the gate, within 1.5x of itself,
|
|
281
|
+
the route stays `leak`. Measured on the reproduction for
|
|
282
|
+
[#99335](https://github.com/vercel/next.js/issues/99335), an ISR route
|
|
283
|
+
keeping every new path's cache control in an unbounded map: it closed on four
|
|
284
|
+
cycles of ~2.2 MB, twice the gate, and used to be called `saturating`, with
|
|
285
|
+
no issue draft. Because the shape requires every cycle to clear
|
|
275
286
|
the growth gate, a decelerating curve always ends the window still growing,
|
|
276
287
|
so where it settles is outside what was measured: these routes are
|
|
277
288
|
**measured again** with twice the cycles, like `inconclusive` ones. No issue
|
|
@@ -388,7 +399,11 @@ A route with a revalidation period serves its cache unless the request carries
|
|
|
388
399
|
the build's own `x-prerender-revalidate` header. Measured on that same app: the
|
|
389
400
|
identical run reports `leak` with the header and `stable` without it. next-leak
|
|
390
401
|
reads `previewModeId` from `.next/prerender-manifest.json` and drives those
|
|
391
|
-
routes itself; a header you set in `next-leak.config.json` wins untouched.
|
|
402
|
+
routes itself; a header you set in `next-leak.config.json` wins untouched. A
|
|
403
|
+
dynamic template with nothing prerendered (`generateStaticParams()` returning
|
|
404
|
+
`[]`) counts too: the build records no period for it, but its blocking fallback
|
|
405
|
+
caches every new param on first request, so `{n}` fills that cache while it is
|
|
406
|
+
measured and the report says so. When
|
|
392
407
|
the manifest cannot supply one, the route is reported `not exercised` with no
|
|
393
408
|
verdict, because a flat curve measured against a static cache says nothing about
|
|
394
409
|
the app.
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
resolveCycles,
|
|
12
12
|
warrantsIssueDraft,
|
|
13
13
|
withdrawnByDisagreement
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-GU7IJUNY.js";
|
|
15
15
|
import {
|
|
16
16
|
assessPeakPressure,
|
|
17
17
|
assessPressureVerdict,
|
|
@@ -93235,6 +93235,10 @@ var prerenderManifestSchema = z2.looseObject({
|
|
|
93235
93235
|
srcRoute: z2.string().nullable().optional()
|
|
93236
93236
|
})
|
|
93237
93237
|
).optional(),
|
|
93238
|
+
// Read only for `fallback`, and left untyped: its shape has changed across
|
|
93239
|
+
// Next versions (`null`, `false`, an HTML path, a PPR shell), and a template
|
|
93240
|
+
// the tool cannot classify must not fail the whole manifest.
|
|
93241
|
+
dynamicRoutes: z2.record(z2.string(), z2.looseObject({ fallback: z2.unknown().optional() })).optional(),
|
|
93238
93242
|
preview: z2.looseObject({ previewModeId: z2.string() }).optional()
|
|
93239
93243
|
});
|
|
93240
93244
|
var INTERNAL_PATHS = /* @__PURE__ */ new Set(["/_global-error", "/_not-found"]);
|
|
@@ -94892,7 +94896,7 @@ function renderConfigSkeleton(routeTemplates, manifest = void 0) {
|
|
|
94892
94896
|
const values = {};
|
|
94893
94897
|
for (const segment of segments) {
|
|
94894
94898
|
const prerendered = sampled?.[segment.name];
|
|
94895
|
-
values[segment.name] = prerendered
|
|
94899
|
+
values[segment.name] = varyingValueFrom(prerendered ?? PLACEHOLDER);
|
|
94896
94900
|
}
|
|
94897
94901
|
routes[template] = values;
|
|
94898
94902
|
}
|
|
@@ -94901,6 +94905,12 @@ function renderConfigSkeleton(routeTemplates, manifest = void 0) {
|
|
|
94901
94905
|
function hasPlaceholders(skeleton) {
|
|
94902
94906
|
return skeleton.includes(PLACEHOLDER);
|
|
94903
94907
|
}
|
|
94908
|
+
function writtenConfigNote(skeleton) {
|
|
94909
|
+
if (hasPlaceholders(skeleton)) {
|
|
94910
|
+
return `replace each ${PLACEHOLDER} with the shape of a value that exists in your app, and keep the \`-{n}\` after it: a fixed value measures one cache entry and reads as flat whatever the route retains`;
|
|
94911
|
+
}
|
|
94912
|
+
return "`{n}` gives every request a different value: a prerendered one serves the warm cache and reads as flat whatever the route retains. Use `{n%200}` to revisit a fixed set of keys, and drop the marker only if the app 404s on params it never prerendered.";
|
|
94913
|
+
}
|
|
94904
94914
|
|
|
94905
94915
|
// src/unreclaimed-retention.ts
|
|
94906
94916
|
var MB = 1024 * 1024;
|
|
@@ -94998,10 +95008,10 @@ function ownerLabel(attribution) {
|
|
|
94998
95008
|
}
|
|
94999
95009
|
}
|
|
95000
95010
|
function revalidationLines(route) {
|
|
95001
|
-
if (route.
|
|
95011
|
+
if (route.servedFromIsrCache !== true) {
|
|
95002
95012
|
return [];
|
|
95003
95013
|
}
|
|
95004
|
-
const every = `revalidates every ${route.revalidatedEverySeconds}s`;
|
|
95014
|
+
const every = route.revalidatedEverySeconds === void 0 ? "caches each new key on its first request" : `revalidates every ${route.revalidatedEverySeconds}s`;
|
|
95005
95015
|
return route.revalidationDriven === true ? [
|
|
95006
95016
|
` driven through ISR revalidation (${every}; without it the load would serve the cache) \u2014 this is Next's revalidation path, so a leak that only happens on the path your users take is not in this number`
|
|
95007
95017
|
] : [
|
|
@@ -95032,7 +95042,7 @@ function cacheLines(route) {
|
|
|
95032
95042
|
lines.push(
|
|
95033
95043
|
` the load served keys this route had never cached, so some of this growth is cache residency; bound it with {n%N} in next-leak.config.json`
|
|
95034
95044
|
);
|
|
95035
|
-
if (route.
|
|
95045
|
+
if (route.servedFromIsrCache === true) {
|
|
95036
95046
|
lines.push(
|
|
95037
95047
|
` on this route {n%N} does not isolate that: a bounded key set is served from the ISR cache, so the run would drive revalidation and measure a different path. Compare the two runs rather than trusting either number alone`
|
|
95038
95048
|
);
|
|
@@ -95173,7 +95183,7 @@ function skippedGuidanceLines(report) {
|
|
|
95173
95183
|
if (skeleton === null) {
|
|
95174
95184
|
return [];
|
|
95175
95185
|
}
|
|
95176
|
-
const editing = hasPlaceholders(skeleton) ? ` Replace each ${"REPLACE-ME"} with a value that exists in your app.` : ` The shapes come from paths your build already prerendered; \`{n}\` makes every request use a different one, because reusing a prerendered value serves the warm cache and reads as flat whatever the route retains. Use \`{n%200}\` instead to revisit a fixed set of 200 keys, and drop the marker only if the app 404s on params it never prerendered.`;
|
|
95186
|
+
const editing = hasPlaceholders(skeleton) ? ` Replace each ${"REPLACE-ME"} with the shape of a value that exists in your app, and keep the \`-{n}\` after it: a fixed value measures one cache entry and reads as flat whatever the route retains.` : ` The shapes come from paths your build already prerendered; \`{n}\` makes every request use a different one, because reusing a prerendered value serves the warm cache and reads as flat whatever the route retains. Use \`{n%200}\` instead to revisit a fixed set of 200 keys, and drop the marker only if the app 404s on params it never prerendered.`;
|
|
95177
95187
|
return [
|
|
95178
95188
|
"",
|
|
95179
95189
|
`${needConfig.length} route(s) need sample params. Write this to next-leak.config.json in the app directory:${editing}`,
|
|
@@ -95401,8 +95411,11 @@ function revalidateSecondsFor(manifest, route) {
|
|
|
95401
95411
|
}
|
|
95402
95412
|
return null;
|
|
95403
95413
|
}
|
|
95414
|
+
function cachesOnDemand(manifest, route) {
|
|
95415
|
+
return manifest?.dynamicRoutes?.[route]?.fallback === null;
|
|
95416
|
+
}
|
|
95404
95417
|
function revalidates(manifest, route) {
|
|
95405
|
-
return revalidateSecondsFor(manifest, route) !== null;
|
|
95418
|
+
return revalidateSecondsFor(manifest, route) !== null || cachesOnDemand(manifest, route);
|
|
95406
95419
|
}
|
|
95407
95420
|
function planRevalidation(manifest, route, userHeaders, requestPath) {
|
|
95408
95421
|
const userSupplied = Object.keys(userHeaders ?? {}).some(
|
|
@@ -95419,9 +95432,11 @@ function planRevalidation(manifest, route, userHeaders, requestPath) {
|
|
|
95419
95432
|
}
|
|
95420
95433
|
const previewModeId = manifest?.preview?.previewModeId;
|
|
95421
95434
|
if (previewModeId === void 0 || previewModeId === "") {
|
|
95435
|
+
const seconds = revalidateSecondsFor(manifest, route);
|
|
95436
|
+
const cached = seconds === null ? "is served from the ISR cache" : `revalidates every ${seconds}s`;
|
|
95422
95437
|
return {
|
|
95423
95438
|
kind: "cannot-drive",
|
|
95424
|
-
reason:
|
|
95439
|
+
reason: `${cached}, but the build's prerender-manifest.json carries no previewModeId \u2014 load would serve the cache and measure nothing. Set "${REVALIDATE_HEADER}" in next-leak.config.json to drive it.`
|
|
95425
95440
|
};
|
|
95426
95441
|
}
|
|
95427
95442
|
return { kind: "drive", headers: { [REVALIDATE_HEADER]: previewModeId } };
|
|
@@ -95539,6 +95554,7 @@ async function measureRoute(context, route, requestPath, index, pass = void 0) {
|
|
|
95539
95554
|
const { deps, options, target, workDir, routeConfig, registry, nextVersion, progress } = context;
|
|
95540
95555
|
const plan = planRevalidation(target.prerender, route.path, routeConfig.headers, requestPath);
|
|
95541
95556
|
const revalidateSeconds = revalidateSecondsFor(target.prerender, route.path);
|
|
95557
|
+
const servedFromIsrCache = revalidates(target.prerender, route.path);
|
|
95542
95558
|
const bounded = boundedMarkerOf(requestPath);
|
|
95543
95559
|
const driven = plan.kind === "drive" ? plan.headers : {};
|
|
95544
95560
|
const cacheDriven = plan.kind === "no-cache-to-drive" || plan.kind === "drive" && bounded === null;
|
|
@@ -95581,7 +95597,7 @@ async function measureRoute(context, route, requestPath, index, pass = void 0) {
|
|
|
95581
95597
|
warmupRequests: options.warmupRequests ?? RITUAL_DEFAULTS.warmupRequests,
|
|
95582
95598
|
// Decides whether the cache-residency remedy exists on this route: bounding
|
|
95583
95599
|
// the keys of an ISR route hands the requests back to the cache.
|
|
95584
|
-
revalidatesFromCache:
|
|
95600
|
+
revalidatesFromCache: servedFromIsrCache,
|
|
95585
95601
|
...routeConfig.abandonAfterMs !== void 0 && {
|
|
95586
95602
|
abandonAfterMs: routeConfig.abandonAfterMs
|
|
95587
95603
|
},
|
|
@@ -95624,6 +95640,7 @@ async function measureRoute(context, route, requestPath, index, pass = void 0) {
|
|
|
95624
95640
|
memorySamples: result.memorySamples,
|
|
95625
95641
|
peaks: result.peaks,
|
|
95626
95642
|
...revalidateSeconds !== null && { revalidatedEverySeconds: revalidateSeconds },
|
|
95643
|
+
...servedFromIsrCache && { servedFromIsrCache: true },
|
|
95627
95644
|
...plan.kind === "drive" && { revalidationDriven: true },
|
|
95628
95645
|
...bounded !== null && { keyCardinality: bounded.bound },
|
|
95629
95646
|
unreclaimedSamples: result.unreclaimedSamples,
|
|
@@ -95651,7 +95668,7 @@ async function measureRoute(context, route, requestPath, index, pass = void 0) {
|
|
|
95651
95668
|
};
|
|
95652
95669
|
}
|
|
95653
95670
|
async function writeEvidenceBundle(report, workDir) {
|
|
95654
|
-
const { renderHtmlReport } = await import("./html-report-
|
|
95671
|
+
const { renderHtmlReport } = await import("./html-report-VNTPHLO3.js");
|
|
95655
95672
|
const { renderIssueMarkdown } = await import("./issue-report-4RW5YOT3.js");
|
|
95656
95673
|
for (const route of report.routes) {
|
|
95657
95674
|
if (route.status === "measured" && warrantsIssueDraft(route)) {
|
|
@@ -95948,7 +95965,7 @@ export {
|
|
|
95948
95965
|
runRitual,
|
|
95949
95966
|
runSelfCheck,
|
|
95950
95967
|
renderConfigSkeleton,
|
|
95951
|
-
|
|
95968
|
+
writtenConfigNote,
|
|
95952
95969
|
formatReport,
|
|
95953
95970
|
captureEnvironment,
|
|
95954
95971
|
loadSignatures,
|
|
@@ -11,6 +11,8 @@ var STEPWISE_MAX_DRAWDOWN_RATIO = 0.1;
|
|
|
11
11
|
var ACQUITTAL_MAX_GROWTH_MULTIPLE = 8;
|
|
12
12
|
var SATURATION_MIN_CYCLES = 3;
|
|
13
13
|
var SATURATION_MAX_FINAL_RATIO = 0.5;
|
|
14
|
+
var SETTLED_TAIL_MIN_CYCLES = 3;
|
|
15
|
+
var SETTLED_TAIL_MAX_SPREAD = 1.5;
|
|
14
16
|
function maxDrawdown(samples) {
|
|
15
17
|
let peak = samples[1] ?? 0;
|
|
16
18
|
let worst = 0;
|
|
@@ -46,8 +48,20 @@ function isSaturating(deltas, minGrowth) {
|
|
|
46
48
|
if (last > first * SATURATION_MAX_FINAL_RATIO) {
|
|
47
49
|
return false;
|
|
48
50
|
}
|
|
51
|
+
if (hasSettledAboveGate(deltas, minGrowth)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
49
54
|
return isHeadingDown(deltas);
|
|
50
55
|
}
|
|
56
|
+
function hasSettledAboveGate(deltas, minGrowth) {
|
|
57
|
+
const size = Math.max(SETTLED_TAIL_MIN_CYCLES, Math.ceil(deltas.length / 3));
|
|
58
|
+
const tail = deltas.slice(-size);
|
|
59
|
+
const smallest = Math.min(...tail);
|
|
60
|
+
if (smallest < minGrowth) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return Math.max(...tail) <= smallest * SETTLED_TAIL_MAX_SPREAD;
|
|
64
|
+
}
|
|
51
65
|
function hasArrivedWithoutResuming(deltas, minGrowth) {
|
|
52
66
|
const stop = deltas.findIndex((delta) => delta <= 0);
|
|
53
67
|
if (stop === -1) {
|
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
extractModuleRegistry,
|
|
13
13
|
formatReport,
|
|
14
14
|
freePort,
|
|
15
|
-
hasPlaceholders,
|
|
16
15
|
helpText,
|
|
17
16
|
killActiveChildren,
|
|
18
17
|
parseCliArgs,
|
|
@@ -22,12 +21,13 @@ import {
|
|
|
22
21
|
runSelfCheck,
|
|
23
22
|
terminalSafe,
|
|
24
23
|
unregisterChild,
|
|
25
|
-
validateTarget
|
|
26
|
-
|
|
24
|
+
validateTarget,
|
|
25
|
+
writtenConfigNote
|
|
26
|
+
} from "./chunk-GBRBWUZZ.js";
|
|
27
27
|
import "./chunk-IKWF7SSB.js";
|
|
28
28
|
import {
|
|
29
29
|
classifyTrend
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-GU7IJUNY.js";
|
|
31
31
|
import "./chunk-VTKOO3YJ.js";
|
|
32
32
|
import "./chunk-6XYFBOL2.js";
|
|
33
33
|
|
|
@@ -907,13 +907,7 @@ ${skeleton}`
|
|
|
907
907
|
return;
|
|
908
908
|
}
|
|
909
909
|
console.log(`wrote ${file}`);
|
|
910
|
-
|
|
911
|
-
console.log("replace each REPLACE-ME with a value that exists in your app");
|
|
912
|
-
} else {
|
|
913
|
-
console.log(
|
|
914
|
-
"`{n}` gives every request a different value: a prerendered one serves the warm cache and reads as flat whatever the route retains. Use `{n%200}` to revisit a fixed set of keys, and drop the marker only if the app 404s on params it never prerendered."
|
|
915
|
-
);
|
|
916
|
-
}
|
|
910
|
+
console.log(writtenConfigNote(skeleton));
|
|
917
911
|
}
|
|
918
912
|
async function main() {
|
|
919
913
|
const parsed = parseCliArgs(process.argv.slice(2));
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createRequire as __nextLeakCreateRequire } from 'node:module';import { fileURLToPath as __nextLeakFileURLToPath } from 'node:url';import { dirname as __nextLeakDirname } from 'node:path';const require = __nextLeakCreateRequire(import.meta.url);const __filename = __nextLeakFileURLToPath(import.meta.url);const __dirname = __nextLeakDirname(__filename);
|
|
2
2
|
import {
|
|
3
3
|
renderHtmlReport
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-QNNVRQ4L.js";
|
|
5
|
+
import "./chunk-GU7IJUNY.js";
|
|
6
6
|
import "./chunk-VTKOO3YJ.js";
|
|
7
7
|
import "./chunk-6XYFBOL2.js";
|
|
8
8
|
export {
|
package/dist/index.js
CHANGED
|
@@ -41,14 +41,14 @@ import {
|
|
|
41
41
|
sourceIndexAt,
|
|
42
42
|
summarizeBaseline,
|
|
43
43
|
validateTarget
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-GBRBWUZZ.js";
|
|
45
45
|
import "./chunk-IKWF7SSB.js";
|
|
46
46
|
import {
|
|
47
47
|
renderHtmlReport
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-QNNVRQ4L.js";
|
|
49
49
|
import {
|
|
50
50
|
classifyTrend
|
|
51
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-GU7IJUNY.js";
|
|
52
52
|
import {
|
|
53
53
|
renderIssueMarkdown
|
|
54
54
|
} from "./chunk-FJVY5MKV.js";
|
package/dist/manifests.d.ts
CHANGED
|
@@ -34,6 +34,9 @@ export declare const prerenderManifestSchema: z.ZodObject<{
|
|
|
34
34
|
initialRevalidateSeconds: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodLiteral<false>]>>;
|
|
35
35
|
srcRoute: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
36
36
|
}, z.core.$loose>>>;
|
|
37
|
+
dynamicRoutes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
38
|
+
fallback: z.ZodOptional<z.ZodUnknown>;
|
|
39
|
+
}, z.core.$loose>>>;
|
|
37
40
|
preview: z.ZodOptional<z.ZodObject<{
|
|
38
41
|
previewModeId: z.ZodString;
|
|
39
42
|
}, z.core.$loose>>;
|
package/dist/route-guidance.d.ts
CHANGED
|
@@ -38,3 +38,5 @@ export declare function sampleValuesFromManifest(manifest: PrerenderManifest | u
|
|
|
38
38
|
export declare function renderConfigSkeleton(routeTemplates: readonly string[], manifest?: PrerenderManifest | undefined): string | null;
|
|
39
39
|
/** Whether a rendered skeleton still needs the user to fill anything in. */
|
|
40
40
|
export declare function hasPlaceholders(skeleton: string): boolean;
|
|
41
|
+
/** What `--write-config` prints after writing a skeleton: how to edit it. */
|
|
42
|
+
export declare function writtenConfigNote(skeleton: string): string;
|
package/dist/runner.d.ts
CHANGED
|
@@ -113,6 +113,12 @@ export type RouteReport = {
|
|
|
113
113
|
* cache and one measured against a re-render are different experiments.
|
|
114
114
|
*/
|
|
115
115
|
revalidatedEverySeconds?: number;
|
|
116
|
+
/**
|
|
117
|
+
* Set on every route served from the ISR cache, including a dynamic
|
|
118
|
+
* template that caches each new key with no prerendered entry to carry
|
|
119
|
+
* its period — the period is then absent, but the cache is in play.
|
|
120
|
+
*/
|
|
121
|
+
servedFromIsrCache?: true;
|
|
116
122
|
/**
|
|
117
123
|
* Whether the load carried the build's own revalidation header. Set apart
|
|
118
124
|
* from the period because the two answer different questions: the period
|